Tests! And code formatting.
This commit is contained in:
parent
8da5f81f5e
commit
41bedb182b
@ -3,12 +3,13 @@
|
||||
|
||||
import collections
|
||||
import sys
|
||||
|
||||
import tempfile
|
||||
|
||||
CHAPTER_MARKER = '## '
|
||||
STATUS_MARKER = '[status]: # '
|
||||
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):
|
||||
@ -31,7 +32,8 @@ def main():
|
||||
if '-pp' in arguments:
|
||||
# -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
|
||||
import MarkdownPP, tempfile
|
||||
import MarkdownPP
|
||||
|
||||
mdfile = tempfile.TemporaryFile(mode='w+')
|
||||
MarkdownPP.MarkdownPP(input=open(filename), output=mdfile, modules=list(MarkdownPP.modules))
|
||||
mdfile.seek(0)
|
||||
@ -52,22 +54,24 @@ def main():
|
||||
word_count_by_act[act_heading] += 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)
|
||||
current_status = None
|
||||
elif line.startswith(STATUS_MARKER): # Modified to allow multiple statuses in a single chapter, can swap back and forth.
|
||||
if current_status == None:
|
||||
current_status = line[len(STATUS_MARKER):].strip('()\n')
|
||||
# Modified to allow multiple statuses in a single chapter, can swap back and forth.
|
||||
elif line.startswith(STATUS_MARKER):
|
||||
if current_status is None:
|
||||
current_status = line[len(STATUS_MARKER) :].strip('()\n')
|
||||
status_by_chapter[chapter_heading][current_status] = count_words(chapter_heading)
|
||||
else:
|
||||
current_status = line[len(STATUS_MARKER):].strip('()\n')
|
||||
current_status = line[len(STATUS_MARKER) :].strip('()\n')
|
||||
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)
|
||||
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
|
||||
else:
|
||||
line_word_count = count_words(line)
|
||||
@ -83,7 +87,8 @@ def main():
|
||||
word_count_by_act[act_heading] += 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():
|
||||
if chapter_heading is None:
|
||||
continue
|
||||
@ -102,17 +107,24 @@ def main():
|
||||
|
||||
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():
|
||||
if act_heading is None:
|
||||
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()
|
||||
|
||||
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')
|
||||
|
||||
|
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[tool.black]
|
||||
line-length = 100
|
||||
skip-string-normalization = true
|
20
setup.cfg
Normal file
20
setup.cfg
Normal file
@ -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
|
||||
|
||||
VERSION = '0.1.0'
|
||||
VERSION = "0.1.0"
|
||||
|
||||
|
||||
setup(
|
||||
name='novel-stats',
|
||||
name="novel-stats",
|
||||
version=VERSION,
|
||||
description='Produce word count statistics for novels written in Markdown format.',
|
||||
author='Dan Helfman',
|
||||
author_email='witten@torsion.org',
|
||||
url='https://projects.torsion.org/witten/novel-stats',
|
||||
description="Produce word count statistics for novels written in Markdown format.",
|
||||
author="Dan Helfman",
|
||||
author_email="witten@torsion.org",
|
||||
url="https://projects.torsion.org/witten/novel-stats",
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Console',
|
||||
'Intended Audience :: Other Audience',
|
||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Office/Business',
|
||||
'Topic :: Text Processing :: Markup',
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Other Audience",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Programming Language :: Python",
|
||||
"Topic :: Office/Business",
|
||||
"Topic :: Text Processing :: Markup",
|
||||
],
|
||||
packages=find_packages(exclude=['tests*']),
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'novel-stats = novel_stats.novel_stats:main',
|
||||
]
|
||||
},
|
||||
packages=find_packages(exclude=["tests*"]),
|
||||
entry_points={"console_scripts": ["novel-stats = novel_stats.novel_stats:main",]},
|
||||
install_requires=(),
|
||||
extras_require = {
|
||||
'multi_file': ['MarkdownPP'],
|
||||
},
|
||||
extras_require={"multi_file": ["MarkdownPP"],},
|
||||
include_package_data=True,
|
||||
)
|
||||
|
18
test_requirements.txt
Normal file
18
test_requirements.txt
Normal file
@ -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'
|
17
tests/test_novel_stats.py
Normal file
17
tests/test_novel_stats.py
Normal file
@ -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
|
28
tox.ini
Normal file
28
tox.ini
Normal file
@ -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
Block a user