Compare commits

..

2 Commits

Author SHA1 Message Date
deroshkin 357eceabad Added emdash 0x2014 as a word separator 2021-11-22 21:25:08 +01:00
Dmytro Yeroshkin 1c53c94b9c Add title to the output 2021-11-01 10:01:31 +01:00
8 changed files with 90 additions and 53 deletions

View File

@ -1,12 +1,39 @@
---
kind: pipeline kind: pipeline
name: python-3-11-alpine-3-18 name: python-3-6-alpine-3-9
clone: clone:
skip_verify: true skip_verify: true
steps: steps:
- name: build - name: build
image: alpine:3.18 image: alpine:3.9
pull: always
commands:
- tests/run-ci-tests
---
kind: pipeline
name: python-3-7-alpine-3-10
clone:
skip_verify: true
steps:
- name: build
image: alpine:3.10
pull: always
commands:
- tests/run-ci-tests
---
kind: pipeline
name: python-3-8-alpine-3-13
clone:
skip_verify: true
steps:
- name: build
image: alpine:3.13
pull: always pull: always
commands: commands:
- tests/run-ci-tests - tests/run-ci-tests

View File

@ -57,11 +57,11 @@ total: 946 words
## Installation ## Installation
Start by cloning the project with git. Then install it with Start by cloning the project with git. Then install it with Python's `pip`.
[pipx](https://pypa.github.io/pipx/). Example: Example:
```bash ```bash
pipx install /path/to/novel-stats pip3 install /path/to/novel-stats
``` ```
## Usage ## Usage
@ -226,9 +226,17 @@ make sure your changes work.
```bash ```bash
cd novel-stats/ cd novel-stats/
pipx install --editable . pip3 install --editable --user .
``` ```
Note that this will typically install the novel-stats commands into
`~/.local/bin`, which may or may not be on your PATH. There are other ways to
install novel-stats editable as well, for instance into the system Python
install (so without `--user`, as root), or even into a
[virtualenv](https://virtualenv.pypa.io/en/stable/). How or where you install
novel-stats is up to you, but generally an editable install makes development
and testing easier.
### Automated tests ### Automated tests
@ -237,7 +245,7 @@ you're in the `novel-stats/` working copy, install tox, which is used for
setting up testing environments: setting up testing environments:
```bash ```bash
pipx install tox pip3 install --user tox
``` ```
Finally, to actually run tests, run: Finally, to actually run tests, run:

View File

@ -4,18 +4,21 @@
import argparse import argparse
import collections import collections
import tempfile import tempfile
import re
CHAPTER_MARKER = '## ' CHAPTER_MARKER = '## '
STATUS_MARKER = '[status]: # ' STATUS_MARKER = '[status]: # '
ACT_MARKER = '[act]: # ' ACT_MARKER = '[act]: # '
# Standard 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 = '[//]: # ' COMMENT_MARKER = '[//]: # '
TITLE_MARKER = '# '
WORD_SEPS = [' ','—']
def count_words(line): def count_words(line):
count = 0 count = 0
for word in line.strip().split(' '): for word in re.split('|'.join(WORD_SEPS), line.strip()):
if not word.strip() or word == '*' or word.startswith('#'): if not word.strip() or word == '*' or word.startswith('#'):
continue continue
@ -72,8 +75,9 @@ def main():
word_count_by_chapter = collections.defaultdict(int) word_count_by_chapter = collections.defaultdict(int)
word_count_by_status = collections.defaultdict(int) word_count_by_status = collections.defaultdict(int)
word_count_by_act = collections.defaultdict(int) word_count_by_act = collections.defaultdict(int)
status_by_chapter = collections.defaultdict(lambda: collections.defaultdict(int)) status_by_chapter = {}
current_status = None current_status = None
title = None
for line in mdfile.readlines(): for line in mdfile.readlines():
if line.startswith(CHAPTER_MARKER): if line.startswith(CHAPTER_MARKER):
@ -83,22 +87,24 @@ def main():
chapter_heading = line[len(CHAPTER_MARKER) :].strip('()\n') chapter_heading = line[len(CHAPTER_MARKER) :].strip('()\n')
# 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.
if chapter_heading: word_count_by_chapter[chapter_heading] = count_words(chapter_heading)
word_count_by_chapter[chapter_heading] = count_words(chapter_heading)
current_status = None status_by_chapter[chapter_heading] = collections.defaultdict(int)
current_status = None
# 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.
elif line.startswith(STATUS_MARKER): elif line.startswith(STATUS_MARKER):
if current_status is None: if current_status is None:
current_status = line[len(STATUS_MARKER) :].strip('()\n') current_status = line[len(STATUS_MARKER) :].strip('()\n')
if chapter_heading: 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(TITLE_MARKER):
title = line[len(TITLE_MARKER):].strip()
line_word_count = count_words(line)
word_count_by_chapter[chapter_heading] += line_word_count
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:
@ -115,6 +121,9 @@ 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 title:
print(f'Novel Stats for {title.upper()}')
# -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 arguments.chapter: if arguments.chapter:
for chapter_heading, chapter_word_count in word_count_by_chapter.items(): for chapter_heading, chapter_word_count in word_count_by_chapter.items():

View File

@ -20,15 +20,8 @@ setup(
"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,
python_requires='>3.7.0',
) )

View File

@ -1,18 +1,18 @@
appdirs==1.4.4 appdirs==1.4.4; python_version >= '3.8'
attrs==23.1.0 attrs==20.3.0; python_version >= '3.8'
black==23.10.1 black==19.10b0; python_version >= '3.8'
click==8.1.7 click==7.1.2; python_version >= '3.8'
coverage==7.3.2 coverage==5.3
flake8==6.1.0 flake8==3.8.4
flexmock==0.11.3 flexmock==0.10.4
isort==5.12.0 isort==5.9.1
mccabe==0.7.0 mccabe==0.6.1
pluggy==1.3.0 pluggy==0.13.1
pathspec==0.11.2 pathspec==0.8.1; python_version >= '3.8'
py==1.11.0 py==1.10.0
pycodestyle==2.11.1 pycodestyle==2.6.0
pyflakes==3.1.0 pyflakes==2.2.0
pytest==7.4.2 pytest==6.1.2
pytest-cov==4.1.0 pytest-cov==2.10.1
regex regex; python_version >= '3.8'
typed-ast typed-ast==1.4.2; python_version >= '3.8'

View File

View File

@ -9,6 +9,7 @@ set -e
apk add --no-cache python3 py3-pip apk add --no-cache python3 py3-pip
# If certain dependencies of black are available in this version of Alpine, install them. # If certain dependencies of black are available in this version of Alpine, install them.
apk add --no-cache py3-typed-ast py3-regex || true apk add --no-cache py3-typed-ast py3-regex || true
pip3 install tox==4.11.3 python3 -m pip install --upgrade pip==21.3.1 setuptools==58.2.0
pip3 install tox==3.24.4
export COVERAGE_FILE=/tmp/.coverage export COVERAGE_FILE=/tmp/.coverage
tox --workdir /tmp/.tox --sitepackages tox --workdir /tmp/.tox --sitepackages

17
tox.ini
View File

@ -1,16 +1,16 @@
[tox] [tox]
env_list = py38,py39,py310,py311 envlist = py36,py37,py38,py39
skip_missing_interpreters = True skip_missing_interpreters = True
package = editable skipsdist = True
minversion = 4.0 minversion = 3.14.1
[testenv] [testenv]
deps = usedevelop = True
-r test_requirements.txt deps = -rtest_requirements.txt
pass_env = COVERAGE_FILE passenv = COVERAGE_FILE
commands = commands =
pytest {posargs} pytest {posargs}
black --check . py38,py39: black --check .
isort --check-only --settings-path setup.cfg . isort --check-only --settings-path setup.cfg .
flake8 novel_stats tests flake8 novel_stats tests
@ -23,7 +23,6 @@ commands =
pytest {posargs} pytest {posargs}
[testenv:isort] [testenv:isort]
deps = deps = {[testenv]deps}
{[testenv]deps}
commands = commands =
isort --settings-path setup.cfg . isort --settings-path setup.cfg .