#!/usr/bin/env python3
"""Extract "The Beginning" — The 1966 New York Journal from others-before-1977.html.
Each diary day entry becomes its own chapter file.
Chapters are grouped hierarchically:
- Introduction (standalone)
- Chapter 1: The Upper West Side (summary + day entries grouped by month)
- Chapter 2: The Bowery (summary + day entries grouped by month)
- Chapter 3: 26 Second Avenue (summary + day entries grouped by month + Notice)
- Memoranda and Accounts (standalone)
- Epilogue (standalone)
- Excerpts (each excerpt as its own chapter)
Output:
lib/jsons/journals/the-beginning/intro.json
lib/jsons/journals/the-beginning/chapters/introduction.json
lib/jsons/journals/the-beginning/chapters/upper-west-side-summary.json
lib/jsons/journals/the-beginning/chapters/jan-19.json
...
lib/jsons/journals/the-beginning/chapters/memoranda.json
lib/jsons/journals/the-beginning/chapters/epilogue.json
lib/jsons/journals/the-beginning/chapters/excerpt-*.json
Usage:
python3 scripts/the_beginning_extract.py
"""
import json
import os
import re
import sys
from bs4 import BeautifulSoup, Tag
# ---------------------------------------------------------------------------
# ScaGoudy → IAST Unicode mapping
# ---------------------------------------------------------------------------
SCAGOUDY_TO_IAST = {
"ä": "ā",
"Ä": "Ā",
"å": "ṛ",
"Å": "Ṛ",
"ñ": "ṣ",
"Ñ": "Ṣ",
"ö": "ṭ",
"Ö": "Ṭ",
"ë": "ṇ",
"Ë": "Ṇ",
"ò": "ḍ",
"Ò": "Ḍ",
"ç": "ś",
"Ç": "Ś",
"ì": "ṅ",
"Ì": "Ṅ",
"ï": "ñ",
"Ï": "Ñ",
"ü": "ū",
"Ü": "Ū",
"é": "ī",
"É": "Ī",
"à": "ṁ",
"ù": "ḥ",
"è": "ṝ",
"ÿ": "ḷ",
}
def decode_scagoudy(text: str) -> str:
for src, dst in SCAGOUDY_TO_IAST.items():
text = text.replace(src, dst)
return text
def get_css_class(tag: Tag) -> str:
classes = tag.get("class", [])
return classes[0] if classes else ""
def inner_html(tag: Tag) -> str:
"""Get inner HTML of a tag, decode ScaGoudy, clean up spans."""
html = "".join(str(child) for child in tag.children)
html = decode_scagoudy(html)
# text → text
html = re.sub(
r']*class="s3"[^>]*>(.*?)', r"\1", html, flags=re.DOTALL
)
# ... → keep inner text
html = re.sub(
r']*class="s5"[^>]*>(.*?)', r"\1", html, flags=re.DOTALL
)
# ... → keep inner text
html = re.sub(
r']*class="s7"[^>]*>(.*?)', r"\1", html, flags=re.DOTALL
)
# ... → keep inner text
html = re.sub(
r']*class="s\d+"[^>]*>(.*?)', r"\1", html, flags=re.DOTALL
)
# Remove Apple-tab-span
html = re.sub(r'\t', " ", html)
# Remove any remaining spans but keep content
html = re.sub(r"]*>(.*?)", r"\1", html, flags=re.DOTALL)
# Clean up tab characters
html = html.replace("\t", " ")
# Collapse multiple spaces
html = re.sub(r"[ ]+", " ", html)
html = html.strip()
# Remove leading
tags
html = re.sub(r"^(
[\s]*)+", "", html)
return html
def write_json(data: dict, path: str):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n")
# ---------------------------------------------------------------------------
# Month helpers
# ---------------------------------------------------------------------------
MONTH_MAP = {
"january": "jan",
"february": "feb",
"march": "mar",
"april": "apr",
"may": "may",
"june": "jun",
"july": "jul",
"august": "aug",
"september": "sep",
"october": "oct",
"november": "nov",
"december": "dec",
}
MONTH_FULL = {
"jan": "January 1966",
"feb": "February 1966",
"mar": "March 1966",
"apr": "April 1966",
"may": "May 1966",
"jun": "June 1966",
"jul": "July 1966",
"aug": "August 1966",
"sep": "September 1966",
"oct": "October 1966",
"nov": "November 1966",
}
DAY_NAMES = {
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
}
def parse_day_month(text: str):
"""Parse 'Wednesday 19 January' → ('Wednesday', 19, 'jan')
Also handles 'Notice' and other non-date entries.
Returns (day_name, date_num, month_abbr) or (None, None, None).
"""
text = text.strip()
# Try "DayName Number MonthName" pattern
m = re.match(r"([A-Za-z]+)\s+(\d+)\s+([A-Za-z]+)", text)
if m:
day = m.group(1).capitalize()
num = int(m.group(2))
month = m.group(3).lower()
if day.lower() in DAY_NAMES and month in MONTH_MAP:
return (day, num, MONTH_MAP[month])
return (None, None, None)
def slugify_excerpt(title: str) -> str:
"""Create a URL-friendly slug from an excerpt title."""
# Remove HTML tags
title = re.sub(r"<[^>]+>", "", title)
title = decode_scagoudy(title)
# Take first 60 chars
title = title[:80].lower().strip()
# Replace non-alnum with hyphens
title = re.sub(r"[^a-z0-9]+", "-", title)
title = title.strip("-")
# Truncate to reasonable length
if len(title) > 60:
title = title[:60].rsplit("-", 1)[0]
return f"excerpt-{title}"
# ---------------------------------------------------------------------------
# Section boundaries detector
# ---------------------------------------------------------------------------
def find_section_boundaries(all_tags):
"""Find the start/end tag indices for each major section."""
sections = {}
beg_start = None
beg_end = None
for i, tag in enumerate(all_tags):
cls = get_css_class(tag)
text = tag.get_text().strip()
# Find "The Beginning" start
if cls == "p170" and "The Beginning" in text:
beg_start = i
if beg_start is not None and i > beg_start:
# Find section markers by p4 bookmarks
if cls == "p4" and text.startswith("BEG"):
if "Introduction" in text:
sections["introduction_start"] = i
elif "BEG 1" in text:
sections["ch1_start"] = i
elif "BEG 2" in text:
sections["ch2_start"] = i
elif "BEG 3" in text:
sections["ch3_start"] = i
elif "BEG 4" in text:
sections["ch4_start"] = i
elif "BEG 5" in text:
sections["ch5_start"] = i
elif "BEG 6" in text:
sections["ch6_start"] = i
# Detect end of The Beginning (next non-BEG p4 or end of file)
if (
cls == "p4"
and not text.startswith("BEG")
and beg_start
and "ch6_start" in sections
and i > sections["ch6_start"] + 5
):
beg_end = i
break
# If no explicit end found, use end of conversation content
if beg_end is None:
# Search for the last p74 conversation line
for i in range(len(all_tags) - 1, sections.get("ch6_start", 0), -1):
cls = get_css_class(all_tags[i])
if cls in ("p74", "p6", "p77"):
beg_end = i + 1
break
sections["beg_start"] = beg_start
sections["beg_end"] = beg_end
return sections
# ---------------------------------------------------------------------------
# Extract prose section (Introduction, Epilogue, etc.)
# ---------------------------------------------------------------------------
def extract_prose_section(tags, start_idx, end_idx, skip_classes=None):
"""Extract paragraphs from a prose section."""
if skip_classes is None:
skip_classes = {"p4", "p11", "p3", "p5", "p39", "p24", "p153"}
paragraphs = []
for i in range(start_idx, end_idx):
tag = tags[i]
cls = get_css_class(tag)
if cls in skip_classes:
continue
html = inner_html(tag)
if html and html != "
":
paragraphs.append(html)
return paragraphs
# ---------------------------------------------------------------------------
# Extract day entries from a chapter section
# ---------------------------------------------------------------------------
def extract_day_entries(tags, start_idx, end_idx):
"""Extract day entries from a chapter section.
Returns (summary_paragraphs, day_entries_list).
"""
summary_paragraphs = []
day_entries = []
current_entry = None
in_summary = True # Start in summary mode until first p39 day entry
skip_classes = {"p4", "p11", "p3", "p5", "p153"}
def finalize_entry():
nonlocal current_entry
if current_entry and current_entry["paragraphs"]:
day_entries.append(current_entry)
current_entry = None
for i in range(start_idx, end_idx):
tag = tags[i]
cls = get_css_class(tag)
text = tag.get_text().strip()
if cls in skip_classes:
continue
# p39 = day bookmark
if cls == "p39":
finalize_entry()
in_summary = False
# Check if it's a special entry like "Notice" or "Memoranda"
if text.lower() == "notice":
current_entry = {
"day_name": None,
"date": None,
"month": None,
"slug": "notice",
"title": "Notice",
"paragraphs": [],
}
continue
elif text.lower() == "memoranda":
current_entry = {
"day_name": None,
"date": None,
"month": None,
"slug": "memoranda",
"title": "Memoranda",
"paragraphs": [],
}
continue
day_name, date_num, month_abbr = parse_day_month(text)
if day_name and date_num and month_abbr:
slug = f"{month_abbr}-{date_num}"
title = f"{day_name} {date_num}"
current_entry = {
"day_name": day_name,
"date": date_num,
"month": month_abbr,
"slug": slug,
"title": title,
"paragraphs": [],
}
else:
# Can't parse — might be an excerpt title or special entry
# Treat as a titled entry
slug_text = re.sub(r"[^a-z0-9]+", "-", text.lower().strip())[:50].strip(
"-"
)
current_entry = {
"day_name": None,
"date": None,
"month": None,
"slug": slug_text or "unknown",
"title": text,
"paragraphs": [],
}
continue
# p8 = bold day header (display version of p39)
if cls == "p8":
# This is the bold display version — skip if we already have the entry from p39
if current_entry is not None:
continue
# If we're in summary mode, this might be the chapter date range or summary header
if in_summary:
html = inner_html(tag)
if html:
summary_paragraphs.append(html)
continue
continue
# p24 = empty line (skip)
if cls == "p24":
continue
# p29 = bold subheading (like "NOTICE")
if cls == "p29":
if current_entry is not None:
html = inner_html(tag)
if html:
current_entry["paragraphs"].append(html)
continue
# Collect body paragraphs
html = inner_html(tag)
if not html or html == "
":
continue
if in_summary:
summary_paragraphs.append(html)
elif current_entry is not None:
current_entry["paragraphs"].append(html)
finalize_entry()
return summary_paragraphs, day_entries
# ---------------------------------------------------------------------------
# Extract excerpts
# ---------------------------------------------------------------------------
def extract_excerpts(tags, start_idx, end_idx):
"""Extract individual excerpts from the Excerpts section.
Each excerpt starts with a p39 bookmark.
Returns a list of { slug, title, paragraphs }.
"""
excerpts = []
current_excerpt = None
in_preamble = True # Before first p39 excerpt
preamble_paragraphs = []
skip_classes = {"p4", "p11", "p3", "p5", "p153"}
seen_slugs = set()
def finalize_excerpt():
nonlocal current_excerpt
if current_excerpt and current_excerpt["paragraphs"]:
excerpts.append(current_excerpt)
current_excerpt = None
for i in range(start_idx, end_idx):
tag = tags[i]
cls = get_css_class(tag)
text = tag.get_text().strip()
if cls in skip_classes:
continue
# p39 = excerpt title bookmark
if cls == "p39":
finalize_excerpt()
in_preamble = False
title = decode_scagoudy(text)
slug = slugify_excerpt(title)
# Ensure unique slug
if slug in seen_slugs:
suffix = 2
while f"{slug}-{suffix}" in seen_slugs:
suffix += 1
slug = f"{slug}-{suffix}"
seen_slugs.add(slug)
current_excerpt = {
"slug": slug,
"title": title,
"paragraphs": [],
}
continue
# p8 = bold excerpt header (display version) — skip if we have the excerpt
if cls == "p8":
if current_excerpt is not None:
# Skip the bold header — we already have the title from p39
continue
if in_preamble:
html = inner_html(tag)
if html:
preamble_paragraphs.append(html)
continue
if cls == "p24":
continue
html = inner_html(tag)
if not html or html == "
":
continue
if in_preamble:
preamble_paragraphs.append(html)
elif current_excerpt is not None:
# For conversation lines (p74), format with bold speaker
# The inner_html already preserves tags
current_excerpt["paragraphs"].append(html)
finalize_excerpt()
return preamble_paragraphs, excerpts
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
html_path = os.path.join(base_dir, "public", "html", "others-before-1977.html")
output_dir = os.path.join(base_dir, "lib", "jsons", "journals", "the-beginning")
chapters_dir = os.path.join(output_dir, "chapters")
if not os.path.exists(html_path):
print(f"ERROR: {html_path} not found!")
sys.exit(1)
print(f"Reading {html_path}...")
with open(html_path, "r", encoding="utf-8") as f:
content = f.read()
soup = BeautifulSoup(content, "html.parser")
all_tags = soup.find_all("p")
print(f" Parsed {len(all_tags)}
tags total") # Find section boundaries sections = find_section_boundaries(all_tags) print( f" The Beginning: tags[{sections['beg_start']}] → tags[{sections['beg_end']}]" ) for key, val in sorted(sections.items()): print(f" {key}: {val}") # --------------------------------------------------------------------------- # Extract Introduction # --------------------------------------------------------------------------- intro_start = sections["introduction_start"] intro_end = sections["ch1_start"] intro_paragraphs = extract_prose_section(all_tags, intro_start, intro_end) print(f"\n Introduction: {len(intro_paragraphs)} paragraphs") # --------------------------------------------------------------------------- # Extract Chapter 1: The Upper West Side # --------------------------------------------------------------------------- ch1_start = sections["ch1_start"] ch1_end = sections["ch2_start"] ch1_summary, ch1_entries = extract_day_entries(all_tags, ch1_start, ch1_end) print( f" Ch 1 (Upper West Side): {len(ch1_summary)} summary paras, {len(ch1_entries)} day entries" ) # --------------------------------------------------------------------------- # Extract Chapter 2: The Bowery # --------------------------------------------------------------------------- ch2_start = sections["ch2_start"] ch2_end = sections["ch3_start"] ch2_summary, ch2_entries = extract_day_entries(all_tags, ch2_start, ch2_end) print( f" Ch 2 (The Bowery): {len(ch2_summary)} summary paras, {len(ch2_entries)} day entries" ) # --------------------------------------------------------------------------- # Extract Chapter 3: 26 Second Avenue # --------------------------------------------------------------------------- ch3_start = sections["ch3_start"] ch3_end = sections["ch4_start"] ch3_summary, ch3_entries = extract_day_entries(all_tags, ch3_start, ch3_end) print( f" Ch 3 (26 Second Ave): {len(ch3_summary)} summary paras, {len(ch3_entries)} day entries" ) # --------------------------------------------------------------------------- # Extract Memoranda and Accounts # --------------------------------------------------------------------------- ch4_start = sections["ch4_start"] ch4_end = sections["ch5_start"] memo_paragraphs = extract_prose_section(all_tags, ch4_start, ch4_end) print(f" Memoranda: {len(memo_paragraphs)} paragraphs") # --------------------------------------------------------------------------- # Extract Epilogue # --------------------------------------------------------------------------- ch5_start = sections["ch5_start"] ch5_end = sections["ch6_start"] epilogue_paragraphs = extract_prose_section( all_tags, ch5_start, ch5_end, skip_classes={"p4", "p11", "p3", "p5", "p153", "p24"}, ) print(f" Epilogue: {len(epilogue_paragraphs)} paragraphs") # --------------------------------------------------------------------------- # Extract Excerpts # --------------------------------------------------------------------------- ch6_start = sections["ch6_start"] ch6_end = sections["beg_end"] excerpt_preamble, excerpt_list = extract_excerpts(all_tags, ch6_start, ch6_end) print( f" Excerpts: {len(excerpt_preamble)} preamble paras, {len(excerpt_list)} excerpts" ) for ex in excerpt_list: print( f" - {ex['slug']}: {ex['title'][:60]}... ({len(ex['paragraphs'])} paras)" ) # --------------------------------------------------------------------------- # Group day entries by month within each chapter # --------------------------------------------------------------------------- def group_by_month(entries): """Group day entries by month, preserving order.""" groups = [] # List of (month_abbr, month_full, entries_list) current_month = None current_group = None for entry in entries: m = entry.get("month") if m and m != current_month: current_month = m current_group = { "month": m, "month_full": MONTH_FULL.get(m, m), "entries": [], } groups.append(current_group) if current_group is not None: current_group["entries"].append(entry) else: # Entry without month (like "Notice") — create special group current_group = { "month": None, "month_full": "Other", "entries": [entry], } groups.append(current_group) current_group = None current_month = None return groups ch1_months = group_by_month(ch1_entries) ch2_months = group_by_month(ch2_entries) ch3_months = group_by_month(ch3_entries) # --------------------------------------------------------------------------- # Ensure unique slugs across all entries # --------------------------------------------------------------------------- all_slugs = set() def ensure_unique_slug(slug): if slug in all_slugs: suffix = 2 while f"{slug}-{suffix}" in all_slugs: suffix += 1 slug = f"{slug}-{suffix}" all_slugs.add(slug) return slug # Reserve standalone slugs for s in [ "introduction", "upper-west-side-summary", "bowery-summary", "second-avenue-summary", "memoranda", "epilogue", "excerpts-preamble", ]: all_slugs.add(s) # Ensure unique slugs for day entries for entries in [ch1_entries, ch2_entries, ch3_entries]: for entry in entries: entry["slug"] = ensure_unique_slug(entry["slug"]) # Ensure unique slugs for excerpts for ex in excerpt_list: ex["slug"] = ensure_unique_slug(ex["slug"]) # --------------------------------------------------------------------------- # Build intro.json with hierarchical chapters # --------------------------------------------------------------------------- intro_chapters = [] # Introduction (standalone) intro_chapters.append({"slug": "introduction", "title": "Introduction"}) # Chapter 1: The Upper West Side ch1_sub = [{"slug": "upper-west-side-summary", "title": "Summary"}] for mg in ch1_months: ch1_sub.append( { "title": mg["month_full"], "subChapters": [ {"slug": e["slug"], "title": e["title"]} for e in mg["entries"] ], } ) intro_chapters.append( { "title": "The Upper West Side (Jan–Mar 1966)", "subChapters": ch1_sub, } ) # Chapter 2: The Bowery ch2_sub = [{"slug": "bowery-summary", "title": "Summary"}] for mg in ch2_months: ch2_sub.append( { "title": mg["month_full"], "subChapters": [ {"slug": e["slug"], "title": e["title"]} for e in mg["entries"] ], } ) intro_chapters.append( { "title": "The Bowery (Apr–Jun 1966)", "subChapters": ch2_sub, } ) # Chapter 3: 26 Second Avenue ch3_sub = [{"slug": "second-avenue-summary", "title": "Summary"}] for mg in ch3_months: if mg["month"] is not None: ch3_sub.append( { "title": mg["month_full"], "subChapters": [ {"slug": e["slug"], "title": e["title"]} for e in mg["entries"] ], } ) else: # Non-month entries like "Notice" for e in mg["entries"]: ch3_sub.append({"slug": e["slug"], "title": e["title"]}) intro_chapters.append( { "title": "26 Second Avenue (Jul–Nov 1966)", "subChapters": ch3_sub, } ) # Memoranda intro_chapters.append({"slug": "memoranda", "title": "Memoranda and Accounts"}) # Epilogue intro_chapters.append({"slug": "epilogue", "title": "Epilogue"}) # Excerpts — group under a parent heading excerpt_sub = [] if excerpt_preamble: excerpt_sub.append({"slug": "excerpts-preamble", "title": "Preamble"}) for ex in excerpt_list: # Shorten title for sidebar short_title = ex["title"] if len(short_title) > 80: short_title = short_title[:77] + "..." excerpt_sub.append({"slug": ex["slug"], "title": short_title}) intro_chapters.append( { "title": "Excerpts", "subChapters": excerpt_sub, } ) # Count total chapters total_chapters = 0 for ch in intro_chapters: if "slug" in ch: total_chapters += 1 if "subChapters" in ch: for sc in ch["subChapters"]: if "slug" in sc: total_chapters += 1 if "subChapters" in sc: total_chapters += len(sc["subChapters"]) intro_json = { "book": "the-beginning", "title": "The Beginning — The 1966 New York Journal", "author": "His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda", "year": 1966, "description": "Śrīla Prabhupāda's personal journal documenting the founding of the Hare Kṛṣṇa movement in New York City, from January to November 1966.", "totalChapters": total_chapters, "chapters": intro_chapters, } # --------------------------------------------------------------------------- # Write all JSON files # --------------------------------------------------------------------------- # Clean output directory if os.path.exists(chapters_dir): for f in os.listdir(chapters_dir): os.remove(os.path.join(chapters_dir, f)) # intro.json write_json(intro_json, os.path.join(output_dir, "intro.json")) print(f"\n Written: intro.json ({total_chapters} total chapters)") chapter_num = 1 files_written = 0 # Introduction write_json( { "book": "the-beginning", "chapter": chapter_num, "title": "Introduction", "type": "introduction", "slug": "introduction", "paragraphs": intro_paragraphs, }, os.path.join(chapters_dir, "introduction.json"), ) print(f" Written: introduction.json ({len(intro_paragraphs)} paras)") chapter_num += 1 files_written += 1 # Chapter 1 summary write_json( { "book": "the-beginning", "chapter": chapter_num, "title": "The Upper West Side — Summary", "type": "summary", "slug": "upper-west-side-summary", "paragraphs": ch1_summary, }, os.path.join(chapters_dir, "upper-west-side-summary.json"), ) print(f" Written: upper-west-side-summary.json ({len(ch1_summary)} paras)") chapter_num += 1 files_written += 1 # Chapter 1 day entries for entry in ch1_entries: write_json( { "book": "the-beginning", "chapter": chapter_num, "title": entry["title"], "type": "chapter", "slug": entry["slug"], "paragraphs": entry["paragraphs"], }, os.path.join(chapters_dir, f"{entry['slug']}.json"), ) chapter_num += 1 files_written += 1 print(f" Written: {len(ch1_entries)} Chapter 1 day entry files") # Chapter 2 summary write_json( { "book": "the-beginning", "chapter": chapter_num, "title": "The Bowery — Summary", "type": "summary", "slug": "bowery-summary", "paragraphs": ch2_summary, }, os.path.join(chapters_dir, "bowery-summary.json"), ) print(f" Written: bowery-summary.json ({len(ch2_summary)} paras)") chapter_num += 1 files_written += 1 # Chapter 2 day entries for entry in ch2_entries: write_json( { "book": "the-beginning", "chapter": chapter_num, "title": entry["title"], "type": "chapter", "slug": entry["slug"], "paragraphs": entry["paragraphs"], }, os.path.join(chapters_dir, f"{entry['slug']}.json"), ) chapter_num += 1 files_written += 1 print(f" Written: {len(ch2_entries)} Chapter 2 day entry files") # Chapter 3 summary write_json( { "book": "the-beginning", "chapter": chapter_num, "title": "26 Second Avenue — Summary", "type": "summary", "slug": "second-avenue-summary", "paragraphs": ch3_summary, }, os.path.join(chapters_dir, "second-avenue-summary.json"), ) print(f" Written: second-avenue-summary.json ({len(ch3_summary)} paras)") chapter_num += 1 files_written += 1 # Chapter 3 day entries for entry in ch3_entries: write_json( { "book": "the-beginning", "chapter": chapter_num, "title": entry["title"], "type": "chapter", "slug": entry["slug"], "paragraphs": entry["paragraphs"], }, os.path.join(chapters_dir, f"{entry['slug']}.json"), ) chapter_num += 1 files_written += 1 print(f" Written: {len(ch3_entries)} Chapter 3 day entry files") # Memoranda write_json( { "book": "the-beginning", "chapter": chapter_num, "title": "Memoranda and Accounts", "type": "chapter", "slug": "memoranda", "paragraphs": memo_paragraphs, }, os.path.join(chapters_dir, "memoranda.json"), ) print(f" Written: memoranda.json ({len(memo_paragraphs)} paras)") chapter_num += 1 files_written += 1 # Epilogue write_json( { "book": "the-beginning", "chapter": chapter_num, "title": "Epilogue", "type": "chapter", "slug": "epilogue", "paragraphs": epilogue_paragraphs, }, os.path.join(chapters_dir, "epilogue.json"), ) print(f" Written: epilogue.json ({len(epilogue_paragraphs)} paras)") chapter_num += 1 files_written += 1 # Excerpts preamble if excerpt_preamble: write_json( { "book": "the-beginning", "chapter": chapter_num, "title": "Excerpts — Preamble", "type": "chapter", "slug": "excerpts-preamble", "paragraphs": excerpt_preamble, }, os.path.join(chapters_dir, "excerpts-preamble.json"), ) print(f" Written: excerpts-preamble.json ({len(excerpt_preamble)} paras)") chapter_num += 1 files_written += 1 # Excerpt entries for ex in excerpt_list: write_json( { "book": "the-beginning", "chapter": chapter_num, "title": ex["title"], "type": "excerpt", "slug": ex["slug"], "paragraphs": ex["paragraphs"], }, os.path.join(chapters_dir, f"{ex['slug']}.json"), ) chapter_num += 1 files_written += 1 print(f" Written: {len(excerpt_list)} excerpt files") # --------------------------------------------------------------------------- # Summary # --------------------------------------------------------------------------- print(f"\n{'=' * 60}") print(f" EXTRACTION SUMMARY") print(f"{'=' * 60}") print(f" Introduction: {len(intro_paragraphs)} paragraphs") print( f" Ch 1 (Upper West Side): {len(ch1_summary)} summary + {len(ch1_entries)} days" ) for mg in ch1_months: print(f" {mg['month_full']}: {len(mg['entries'])} entries") print(f" Ch 2 (The Bowery): {len(ch2_summary)} summary + {len(ch2_entries)} days") for mg in ch2_months: print(f" {mg['month_full']}: {len(mg['entries'])} entries") print( f" Ch 3 (26 Second Ave): {len(ch3_summary)} summary + {len(ch3_entries)} days" ) for mg in ch3_months: mname = mg["month_full"] if mg["month"] else "Other" print(f" {mname}: {len(mg['entries'])} entries") print(f" Memoranda: {len(memo_paragraphs)} paragraphs") print(f" Epilogue: {len(epilogue_paragraphs)} paragraphs") print(f" Excerpts: {len(excerpt_list)} excerpts") print(f" Total chapter files: {files_written}") print(f" Total chapters (for intro.json): {total_chapters}") print(f"{'=' * 60}") print(f"\nDone! ✨") if __name__ == "__main__": main()