forked from witten/novel-stats
Tests! And code formatting.
This commit is contained in:
parent
8da5f81f5e
commit
41bedb182b
|
@ -3,12 +3,13 @@
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
CHAPTER_MARKER = '## '
|
CHAPTER_MARKER = '## '
|
||||||
STATUS_MARKER = '[status]: # '
|
STATUS_MARKER = '[status]: # '
|
||||||
ACT_MARKER = '[act]: # '
|
ACT_MARKER = '[act]: # '
|
||||||
COMMENT_MARKER = '[//]: # ' # Strandard markdown comment marker, supported by pandoc and calibre's ebook-convert
|
# Standard markdown comment marker, supported by Pandoc and Calibre's ebook-convert.
|
||||||
|
COMMENT_MARKER = '[//]: # '
|
||||||
|
|
||||||
|
|
||||||
def count_words(line):
|
def count_words(line):
|
||||||
|
@ -31,7 +32,8 @@ def main():
|
||||||
if '-pp' in arguments:
|
if '-pp' in arguments:
|
||||||
# -pp flag to allow Markdown Preprocessing primarily to allow multi-file novel formatting
|
# -pp flag to allow Markdown Preprocessing primarily to allow multi-file novel formatting
|
||||||
# this is implemented using a temporary file created using python's buit-in tempfile library
|
# this is implemented using a temporary file created using python's buit-in tempfile library
|
||||||
import MarkdownPP, tempfile
|
import MarkdownPP
|
||||||
|
|
||||||
mdfile = tempfile.TemporaryFile(mode='w+')
|
mdfile = tempfile.TemporaryFile(mode='w+')
|
||||||
MarkdownPP.MarkdownPP(input=open(filename), output=mdfile, modules=list(MarkdownPP.modules))
|
MarkdownPP.MarkdownPP(input=open(filename), output=mdfile, modules=list(MarkdownPP.modules))
|
||||||
mdfile.seek(0)
|
mdfile.seek(0)
|
||||||
|
@ -52,22 +54,24 @@ def main():
|
||||||
word_count_by_act[act_heading] += word_count_by_chapter[chapter_heading]
|
word_count_by_act[act_heading] += word_count_by_chapter[chapter_heading]
|
||||||
total_word_count += word_count_by_chapter[chapter_heading]
|
total_word_count += word_count_by_chapter[chapter_heading]
|
||||||
|
|
||||||
chapter_heading = line[len(CHAPTER_MARKER):].strip('()\n')
|
chapter_heading = line[len(CHAPTER_MARKER) :].strip('()\n')
|
||||||
|
|
||||||
word_count_by_chapter[chapter_heading] = count_words(chapter_heading) # Count the words in chapter heading, because the chapter number and title count as words.
|
# Count the words in chapter heading, because the chapter number and title count as words.
|
||||||
|
word_count_by_chapter[chapter_heading] = count_words(chapter_heading)
|
||||||
|
|
||||||
status_by_chapter[chapter_heading] = collections.defaultdict(int)
|
status_by_chapter[chapter_heading] = collections.defaultdict(int)
|
||||||
current_status = None
|
current_status = None
|
||||||
elif line.startswith(STATUS_MARKER): # Modified to allow multiple statuses in a single chapter, can swap back and forth.
|
# Modified to allow multiple statuses in a single chapter, can swap back and forth.
|
||||||
if current_status == None:
|
elif line.startswith(STATUS_MARKER):
|
||||||
current_status = line[len(STATUS_MARKER):].strip('()\n')
|
if current_status is None:
|
||||||
|
current_status = line[len(STATUS_MARKER) :].strip('()\n')
|
||||||
status_by_chapter[chapter_heading][current_status] = count_words(chapter_heading)
|
status_by_chapter[chapter_heading][current_status] = count_words(chapter_heading)
|
||||||
else:
|
else:
|
||||||
current_status = line[len(STATUS_MARKER):].strip('()\n')
|
current_status = line[len(STATUS_MARKER) :].strip('()\n')
|
||||||
elif line.startswith(ACT_MARKER):
|
elif line.startswith(ACT_MARKER):
|
||||||
act_heading = line[len(ACT_MARKER):].strip('()\n')
|
act_heading = line[len(ACT_MARKER) :].strip('()\n')
|
||||||
word_count_by_act[act_heading] = count_words(act_heading)
|
word_count_by_act[act_heading] = count_words(act_heading)
|
||||||
elif line.startswith(COMMENT_MARKER): # don't count the words in a comment
|
elif line.startswith(COMMENT_MARKER): # Don't count the words in a comment.
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
line_word_count = count_words(line)
|
line_word_count = count_words(line)
|
||||||
|
@ -83,7 +87,8 @@ def main():
|
||||||
word_count_by_act[act_heading] += word_count_by_chapter[chapter_heading]
|
word_count_by_act[act_heading] += word_count_by_chapter[chapter_heading]
|
||||||
total_word_count += word_count_by_chapter[chapter_heading]
|
total_word_count += word_count_by_chapter[chapter_heading]
|
||||||
|
|
||||||
if '-c' in arguments or '--chapter' in arguments: # -c or --chapter to give a chapter-by-chapter word count summary
|
# -c or --chapter to give a chapter-by-chapter word count summary.
|
||||||
|
if '-c' in arguments or '--chapter' in arguments:
|
||||||
for chapter_heading, chapter_word_count in word_count_by_chapter.items():
|
for chapter_heading, chapter_word_count in word_count_by_chapter.items():
|
||||||
if chapter_heading is None:
|
if chapter_heading is None:
|
||||||
continue
|
continue
|
||||||
|
@ -102,17 +107,24 @@ def main():
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
if '-a' in arguments or '--act' in arguments: # -a or --act to give an act-by-act word count summary
|
# -a or --act to give an act-by-act word count summary.
|
||||||
|
if '-a' in arguments or '--act' in arguments:
|
||||||
for act_heading, act_word_count in word_count_by_act.items():
|
for act_heading, act_word_count in word_count_by_act.items():
|
||||||
if act_heading is None:
|
if act_heading is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print('act {}: {:,} words (~{}%)'.format(act_heading, act_word_count, act_word_count * 100 // total_word_count))
|
print(
|
||||||
|
'act {}: {:,} words (~{}%)'.format(
|
||||||
|
act_heading, act_word_count, act_word_count * 100 // total_word_count
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
for status, status_word_count in word_count_by_status.items():
|
for status, status_word_count in word_count_by_status.items():
|
||||||
print(f'{status}: {status_word_count:,} words (~{status_word_count * 100 // total_word_count}%)')
|
print(
|
||||||
|
f'{status}: {status_word_count:,} words (~{status_word_count * 100 // total_word_count}%)'
|
||||||
|
)
|
||||||
|
|
||||||
print(f'total: {total_word_count:,} words')
|
print(f'total: {total_word_count:,} words')
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[tool.black]
|
||||||
|
line-length = 100
|
||||||
|
skip-string-normalization = true
|
|
@ -0,0 +1,20 @@
|
||||||
|
[metadata]
|
||||||
|
description_file=README.md
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
testpaths = tests
|
||||||
|
addopts = --cov-report term-missing:skip-covered --cov=novel_stats
|
||||||
|
filterwarnings =
|
||||||
|
ignore:Coverage disabled.*:pytest.PytestWarning
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
ignore = E203,E501,W503
|
||||||
|
exclude = *.*/*
|
||||||
|
|
||||||
|
[tool:isort]
|
||||||
|
force_single_line = False
|
||||||
|
include_trailing_comma = True
|
||||||
|
known_first_party = novel_stats
|
||||||
|
line_length = 100
|
||||||
|
multi_line_output = 3
|
||||||
|
skip = .tox
|
38
setup.py
38
setup.py
|
@ -1,33 +1,27 @@
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
VERSION = '0.1.0'
|
VERSION = "0.1.0"
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='novel-stats',
|
name="novel-stats",
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
description='Produce word count statistics for novels written in Markdown format.',
|
description="Produce word count statistics for novels written in Markdown format.",
|
||||||
author='Dan Helfman',
|
author="Dan Helfman",
|
||||||
author_email='witten@torsion.org',
|
author_email="witten@torsion.org",
|
||||||
url='https://projects.torsion.org/witten/novel-stats',
|
url="https://projects.torsion.org/witten/novel-stats",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 4 - Beta',
|
"Development Status :: 4 - Beta",
|
||||||
'Environment :: Console',
|
"Environment :: Console",
|
||||||
'Intended Audience :: Other Audience',
|
"Intended Audience :: Other Audience",
|
||||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
'Programming Language :: Python',
|
"Programming Language :: Python",
|
||||||
'Topic :: Office/Business',
|
"Topic :: Office/Business",
|
||||||
'Topic :: Text Processing :: Markup',
|
"Topic :: Text Processing :: Markup",
|
||||||
],
|
],
|
||||||
packages=find_packages(exclude=['tests*']),
|
packages=find_packages(exclude=["tests*"]),
|
||||||
entry_points={
|
entry_points={"console_scripts": ["novel-stats = novel_stats.novel_stats:main",]},
|
||||||
'console_scripts': [
|
|
||||||
'novel-stats = novel_stats.novel_stats:main',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
install_requires=(),
|
install_requires=(),
|
||||||
extras_require = {
|
extras_require={"multi_file": ["MarkdownPP"],},
|
||||||
'multi_file': ['MarkdownPP'],
|
|
||||||
},
|
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
appdirs==1.4.4; python_version >= '3.8'
|
||||||
|
attrs==20.3.0; python_version >= '3.8'
|
||||||
|
black==19.10b0; python_version >= '3.8'
|
||||||
|
click==7.1.2; python_version >= '3.8'
|
||||||
|
coverage==5.3
|
||||||
|
flake8==3.8.4
|
||||||
|
flexmock==0.10.4
|
||||||
|
isort==5.9.1
|
||||||
|
mccabe==0.6.1
|
||||||
|
pluggy==0.13.1
|
||||||
|
pathspec==0.8.1; python_version >= '3.8'
|
||||||
|
py==1.10.0
|
||||||
|
pycodestyle==2.6.0
|
||||||
|
pyflakes==2.2.0
|
||||||
|
pytest==6.1.2
|
||||||
|
pytest-cov==2.10.1
|
||||||
|
regex; python_version >= '3.8'
|
||||||
|
typed-ast==1.4.2; python_version >= '3.8'
|
|
@ -0,0 +1,17 @@
|
||||||
|
from novel_stats import novel_stats as module
|
||||||
|
|
||||||
|
|
||||||
|
def test_count_words_tallies_basic_sentence():
|
||||||
|
assert module.count_words('This sentence is five words.') == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_count_words_em_dash_does_not_split_words():
|
||||||
|
assert module.count_words('This is only six—or is it?') == 6
|
||||||
|
|
||||||
|
|
||||||
|
def test_count_words_skips_chapter_marker():
|
||||||
|
assert module.count_words('## Chapter 1') == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_count_words_skips_scene_break():
|
||||||
|
assert module.count_words('* * *') == 0
|
|
@ -0,0 +1,28 @@
|
||||||
|
[tox]
|
||||||
|
envlist = py36,py37,py38,py39
|
||||||
|
skip_missing_interpreters = True
|
||||||
|
skipsdist = True
|
||||||
|
minversion = 3.14.1
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
usedevelop = True
|
||||||
|
deps = -rtest_requirements.txt
|
||||||
|
passenv = COVERAGE_FILE
|
||||||
|
commands =
|
||||||
|
pytest {posargs}
|
||||||
|
py38,py39: black --check .
|
||||||
|
isort --check-only --settings-path setup.cfg .
|
||||||
|
flake8 novel_stats tests
|
||||||
|
|
||||||
|
[testenv:black]
|
||||||
|
commands =
|
||||||
|
black {posargs} .
|
||||||
|
|
||||||
|
[testenv:test]
|
||||||
|
commands =
|
||||||
|
pytest {posargs}
|
||||||
|
|
||||||
|
[testenv:isort]
|
||||||
|
deps = {[testenv]deps}
|
||||||
|
commands =
|
||||||
|
isort --settings-path setup.cfg .
|
Loading…
Reference in New Issue