"""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 ` 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)