feat: add journals

This commit is contained in:
gosangam
2026-05-03 21:02:41 +05:30
parent 89106efc98
commit 35d1d14f37
47 changed files with 11365 additions and 37 deletions
+303
View File
@@ -0,0 +1,303 @@
#!/usr/bin/env python3
"""Extract "An Analysis of Bhagwat Geeta" from others-before-1977.html.
This document is a structured analysis organized into three numbered sections
(topics) with numbered points and verse references. It is plain English text
(no ScaGoudy decoding needed).
Source: public/html/others-before-1977.html
- Title: <p class="p116"><b>An Analysis of Bhagwat Geeta</b></p> (line ~15182)
- Section 1: "Mans eternal relation with Godhead" — 45 <p class="p6"> items
- Section 2: "Man's Duty Toward Godhead" — 34 <p class="p6"> items
- Section 3: "Man's ultimate necessity and perfection of life" — 3 <p class="p6"> items
- End boundary: <p class="p11">Solution of Present Crisises... (line ~15276)
CSS classes:
p116 — Title (bold)
p39 — Section headers ("1: Mans eternal relation with Godhead")
p5 — Duplicate section title (bold, with <span class="s3">) — SKIP
p6 — Body paragraphs (plain English)
p34 — Math calculation at the end — SKIP
p11 — Next document title (end boundary)
Output (prose book structure):
lib/jsons/other-documents/analysis-of-bhagavad-gita/
intro.json # metadata
chapters/
01.json # Chapter 1
02.json # Chapter 2
03.json # Chapter 3
Usage:
python3 scripts/od_analysis_bg_extract.py
"""
import json
import os
import re
import sys
from bs4 import BeautifulSoup, Tag
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
BOOK_CODE = "od-analysis-bg"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def get_css_class(tag: Tag) -> str:
"""Get the first CSS class of a tag, or empty string."""
classes = tag.get("class", [])
return classes[0] if classes else ""
def is_empty(tag: Tag) -> bool:
"""Check if a tag is empty or whitespace-only."""
text = tag.get_text().strip()
return not text or text == "\xa0"
def clean_text(tag: Tag) -> str:
"""Extract inner HTML from a tag, strip span class artifacts, normalize whitespace."""
inner = "".join(str(child) for child in tag.children)
# Remove span tags with CSS classes (RTF conversion artifacts) but keep their content
inner = re.sub(
r'<span[^>]*class="[^"]*"[^>]*>(.*?)</span>', r"\1", inner, flags=re.DOTALL
)
# Remove any remaining bare <span> or </span> tags
inner = re.sub(r"</?span[^>]*>", "", inner)
# Remove <b> and </b> tags (not needed for body paragraphs)
inner = re.sub(r"</?b>", "", inner)
# Clean up Apple-tab-span leftovers (tab characters)
inner = inner.replace("\t", " ")
# Strip <br> tags
inner = re.sub(r"<br\s*/?>", " ", inner)
# Normalize whitespace
inner = re.sub(r"\s+", " ", inner).strip()
# Fix HTML entities
inner = inner.replace("&amp;", "&")
return inner
def write_json(data, filepath):
"""Write data as formatted JSON to filepath, creating directories as needed."""
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f" ✅ Wrote {filepath}")
# ---------------------------------------------------------------------------
# Extraction
# ---------------------------------------------------------------------------
def extract_chapters(soup: BeautifulSoup) -> list[dict]:
"""Extract the chapter data from 'An Analysis of Bhagwat Geeta'."""
# Find the title tag: <p class="p116"><b>An Analysis of Bhagwat Geeta</b></p>
title_tag = None
for p in soup.find_all("p", class_="p116"):
text = p.get_text().strip()
if "Analysis of Bhagwat Geeta" in text:
title_tag = p
break
if not title_tag:
print("❌ Could not find title 'An Analysis of Bhagwat Geeta'")
sys.exit(1)
print(f" 📍 Found title: {title_tag.get_text().strip()}")
# Collect all <p> tags after the title until we hit the end boundary
chapters = []
current_chapter = None
tag = title_tag.find_next_sibling()
while tag:
if not isinstance(tag, Tag) or tag.name != "p":
tag = tag.find_next_sibling()
continue
css = get_css_class(tag)
text = tag.get_text().strip()
# End boundary: next document starts
if css == "p11" and "Solution of Present Crisises" in text:
break
# Skip empty tags
if is_empty(tag):
tag = tag.find_next_sibling()
continue
# Section header: <p class="p39">1: Mans eternal relation with Godhead</p>
if css == "p39":
# Parse section number and title
match = re.match(r"(\d+):\s*(.+)", text)
if match:
num = int(match.group(1))
title = match.group(2).strip()
current_chapter = {
"number": num,
"title": title,
"paragraphs": [],
}
chapters.append(current_chapter)
print(f" 📖 Section {num}: {title}")
tag = tag.find_next_sibling()
continue
# Skip p5 (duplicate section title in bold with span)
if css == "p5":
tag = tag.find_next_sibling()
continue
# Skip p34 (math calculation at the end)
if css == "p34":
tag = tag.find_next_sibling()
continue
# Body paragraph: <p class="p6">
if css == "p6" and current_chapter is not None:
cleaned = clean_text(tag)
if cleaned:
current_chapter["paragraphs"].append(cleaned)
tag = tag.find_next_sibling()
continue
# Any other class — skip
tag = tag.find_next_sibling()
return chapters
# ---------------------------------------------------------------------------
# Quality check
# ---------------------------------------------------------------------------
def quality_check(chapters: list[dict]):
"""Validate the extracted chapters."""
print("\n🔍 Quality check:\n")
print(f" Chapters: {len(chapters)}")
assert len(chapters) == 3, f"Expected 3 chapters, got {len(chapters)}"
total_paragraphs = 0
for ch in chapters:
count = len(ch["paragraphs"])
total_paragraphs += count
print(f' Chapter {ch["number"]}: "{ch["title"]}" — {count} paragraphs')
# Spot-check first paragraph
if ch["paragraphs"]:
first = ch["paragraphs"][0]
print(f" First: {first[:80]}...")
# Spot-check last paragraph
if len(ch["paragraphs"]) > 1:
last = ch["paragraphs"][-1]
print(f" Last: {last[:80]}...")
print(f"\n Total paragraphs: {total_paragraphs}")
# Expected counts (from manual inspection):
# Section 1: 45 items (numbered 1-45)
# Section 2: 34 items (numbered 1-34)
# Section 3: 3 items (numbered 1-3)
expected_total = 82 # 45 + 34 + 3
if total_paragraphs == expected_total:
print(f" ✅ Paragraph count matches expected: {expected_total}")
else:
print(f" ⚠️ Expected {expected_total} paragraphs, got {total_paragraphs}")
# This is a warning, not a hard failure — some items may have sub-items
# or the HTML structure may slightly differ
# Check that paragraphs are not empty
for ch in chapters:
for i, p in enumerate(ch["paragraphs"]):
assert p.strip(), f"Empty paragraph in chapter {ch['number']}, index {i}"
print("\n ✅ All checks passed!")
# ---------------------------------------------------------------------------
# Output
# ---------------------------------------------------------------------------
def write_output(chapters: list[dict], output_dir: str):
"""Write intro.json and per-chapter JSON files."""
# Build intro.json
intro = {
"book": BOOK_CODE,
"title": "An Analysis of Bhagwat Geeta",
"author": "His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda",
"year": None,
"description": "A structured analysis of Bhagavad-gītā organized into three topics with numbered points and verse references.",
"totalChapters": len(chapters),
"chapters": [{"number": ch["number"], "title": ch["title"]} for ch in chapters],
}
write_json(intro, os.path.join(output_dir, "intro.json"))
# Write per-chapter files
chapters_dir = os.path.join(output_dir, "chapters")
for ch in chapters:
chapter_data = {
"book": BOOK_CODE,
"chapter": ch["number"],
"title": ch["title"],
"type": "chapter",
"paragraphs": ch["paragraphs"],
}
filename = f"{ch['number']:02d}.json"
write_json(chapter_data, os.path.join(chapters_dir, filename))
# ---------------------------------------------------------------------------
# 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", "other-documents", "analysis-of-bhagavad-gita"
)
if not os.path.exists(html_path):
print(f"❌ HTML file not found: {html_path}")
sys.exit(1)
# Clean up old single-file JSON if it exists
old_json = os.path.join(
base_dir, "lib", "jsons", "other-documents", "analysis-of-bhagavad-gita.json"
)
if os.path.exists(old_json):
os.remove(old_json)
print(f" 🗑️ Removed old single-file JSON: {old_json}")
print(f"📂 Reading {html_path}...")
with open(html_path, "r", encoding="utf-8") as f:
soup = BeautifulSoup(f, "html.parser")
print("\n📝 Extracting 'An Analysis of Bhagwat Geeta'...\n")
chapters = extract_chapters(soup)
quality_check(chapters)
write_output(chapters, output_dir)
print(f"\n🎉 Done! Output directory: {output_dir}")
if __name__ == "__main__":
main()