story-to-pack: add the corpora and the catalogue v2/v3
Fourteen corpora split and classified: aesop, bierce, chekhov, holmes, keefe, lawson, lorimer, maupassant, nobody, plaintales, poe, torchy, wallingford and winesburg, each with its splitter and the hand-written groups and pages; catalogue v2 and v3; and the shared splitters gutenberg_chunks.py, se_split.py and se_build.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rwKTmug58sEsJ72AuWsEi
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e5617b86ba
commit
3a4758172d
@@ -0,0 +1,45 @@
|
||||
"""Split three Project Gutenberg volumes of Sewell Ford's Torchy stories into one corpus, one story per
|
||||
chapter (each chapter is a self-contained episode): *Torchy* (#20626), *On With Torchy* (#17301) and
|
||||
*Torchy, Private Sec.* (#20627), in that order, which is the order of his rise from office boy.
|
||||
Chapter titles come from each volume's contents; the body heads each chapter with a bare
|
||||
`CHAPTER <roman>` line, and those must number 1..N with N equal to the contents count.
|
||||
|
||||
KNOWN DEFECT, kept on purpose: volumes 1 and 3 end in the publisher's advertisements, which this script
|
||||
does not cut, so they ran into the last chapter of each. Chunks 245, 736 and 737 are advertising only;
|
||||
chunks 244 and 735 are the real end of a story followed by the start of the advertising. They are listed in
|
||||
NOT_STORY.json and excluded from grouping. The script was not changed afterwards, because readers had
|
||||
already written records against these chunk numbers and cutting the advertising in volume 1 would shift
|
||||
every later id. Fix it (stop each volume at its GROSSET & DUNLAP line) only together with a renumbering
|
||||
of claude/batches."""
|
||||
import re, sys
|
||||
sys.path.insert(0, '..')
|
||||
from gutenberg_chunks import gutenberg_body, paragraphs, write
|
||||
|
||||
VOLUMES = [('corpus/torchy.txt', 'Torchy (Sewell Ford, 1911)'),
|
||||
('corpus/on-with-torchy.txt', 'On With Torchy (Sewell Ford, 1914)'),
|
||||
('corpus/torchy-private-sec.txt', 'Torchy, Private Sec. (Sewell Ford, 1917)')]
|
||||
ROMAN = {'I': 1, 'V': 5, 'X': 10, 'L': 50}
|
||||
def roman(s): return sum(-ROMAN[a] if ROMAN[a] < ROMAN.get(b, 0) else ROMAN[a] for a, b in zip(s, s[1:] + ' '))
|
||||
|
||||
stories = []
|
||||
for path, volume in VOLUMES:
|
||||
lines = gutenberg_body(path)
|
||||
c0 = next(i for i, l in enumerate(lines) if l.strip() == 'CONTENTS')
|
||||
contents = []
|
||||
for l in lines[c0 + 1:]:
|
||||
m = re.fullmatch(r'\s*([IVXL]+)\.\s+(.+?)(\s+\d+)?\s*', l)
|
||||
if m: contents.append(m.group(2).strip())
|
||||
elif contents and not l.strip(): continue
|
||||
elif contents and l.strip(): break
|
||||
heads = [i for i, l in enumerate(lines) if re.fullmatch(r'CHAPTER [IVXL]+', l.strip())]
|
||||
nums = [roman(lines[i].split()[1]) for i in heads]
|
||||
assert nums == list(range(1, len(contents) + 1)), (path, nums, len(contents))
|
||||
ends = heads[1:] + [len(lines)]
|
||||
for n, (a, b) in enumerate(zip(heads, ends)):
|
||||
body = lines[a + 1:b]
|
||||
k = next(j for j, l in enumerate(body) if l.strip()) # the chapter title line(s) under the heading
|
||||
while k < len(body) and body[k].strip() and body[k].strip().upper() == body[k].strip(): k += 1
|
||||
paras = paragraphs('\n'.join(body[k:]), drop=(r'THE END\.?', r'\[Illustration[^\]]*\]'))
|
||||
stories.append({'volume': volume, 'title': contents[n].title() if contents[n].isupper() else contents[n],
|
||||
'words': sum(len(p.split()) for p in paras), 'text': '\n\n'.join(paras)})
|
||||
write('.', stories)
|
||||
Reference in New Issue
Block a user