mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
"""Shared loader for verse commentary data split into per-book JSON files."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
from typing import Dict
|
|
|
|
DATA_DIR = Path(__file__).parent.parent / "data"
|
|
COMMENTARY_DIR = DATA_DIR / "verse_commentary"
|
|
|
|
|
|
def _normalize_entry(entry: Dict) -> Dict[str, object]:
|
|
"""Normalize commentary entry keys and defaults."""
|
|
if not isinstance(entry, dict):
|
|
return {"analysis": "", "historical": "", "questions": []}
|
|
|
|
return {
|
|
"analysis": entry.get("analysis", ""),
|
|
"historical": entry.get("historical") or entry.get("historical_context", ""),
|
|
"questions": entry.get("questions", []),
|
|
}
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def load_commentary() -> Dict[str, Dict[int, Dict[int, Dict[str, object]]]]:
|
|
"""
|
|
Load verse commentary from the per-book directory.
|
|
|
|
Returns:
|
|
Nested mapping of Book -> Chapter -> Verse -> commentary entry.
|
|
"""
|
|
aggregated: Dict[str, Dict[int, Dict[int, Dict[str, object]]]] = {}
|
|
if not COMMENTARY_DIR.exists():
|
|
return aggregated
|
|
|
|
for path in sorted(COMMENTARY_DIR.glob("*.json")):
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
payload = json.load(f)
|
|
|
|
# Handle both wrapped ({book, commentary}) and direct chapter dictionaries
|
|
book_name = payload.get("book") if isinstance(payload, dict) else None
|
|
chapters = payload.get("commentary") if isinstance(payload, dict) else payload
|
|
|
|
if not isinstance(chapters, dict):
|
|
continue
|
|
|
|
if not book_name:
|
|
book_name = path.stem.replace("_", " ").title()
|
|
|
|
book_data = aggregated.setdefault(book_name, {})
|
|
|
|
for chapter_key, verses in chapters.items():
|
|
try:
|
|
chapter = int(chapter_key)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
if not isinstance(verses, dict):
|
|
continue
|
|
|
|
chapter_data = book_data.setdefault(chapter, {})
|
|
for verse_key, entry in verses.items():
|
|
try:
|
|
verse = int(verse_key)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
chapter_data[verse] = _normalize_entry(entry)
|
|
|
|
return aggregated
|
|
|
|
|
|
def load_commentary_flat() -> Dict[str, Dict[str, object]]:
|
|
"""
|
|
Load commentary as a flat dictionary keyed by "Book Chapter:Verse".
|
|
|
|
Returns:
|
|
Dictionary of verse reference strings to commentary entries.
|
|
"""
|
|
flat: Dict[str, Dict[str, object]] = {}
|
|
for book, chapters in load_commentary().items():
|
|
for chapter, verses in chapters.items():
|
|
for verse, entry in verses.items():
|
|
flat[f"{book} {chapter}:{verse}"] = entry
|
|
return flat
|
|
|
|
|
|
def _slugify(book: str) -> str:
|
|
"""Create filesystem-friendly file name for a book."""
|
|
slug = re.sub(r"[^a-z0-9]+", "_", book.lower())
|
|
slug = re.sub(r"_+", "_", slug).strip("_")
|
|
return slug or "book"
|
|
|
|
|
|
def merge_commentary_entries(new_entries: Dict[str, Dict[str, Dict[str, dict]]]) -> None:
|
|
"""Merge new commentary entries into per-book JSON files."""
|
|
COMMENTARY_DIR.mkdir(exist_ok=True)
|
|
|
|
for book, chapters in new_entries.items():
|
|
book_file = COMMENTARY_DIR / f"{_slugify(book)}.json"
|
|
|
|
if book_file.exists():
|
|
with open(book_file, "r", encoding="utf-8") as f:
|
|
payload = json.load(f)
|
|
else:
|
|
payload = {"book": book, "commentary": {}}
|
|
|
|
commentary = payload.get("commentary", {})
|
|
|
|
for chapter, verses in chapters.items():
|
|
chapter_key = str(chapter)
|
|
chapter_block = commentary.setdefault(chapter_key, {})
|
|
for verse, entry in verses.items():
|
|
chapter_block[str(verse)] = entry
|
|
|
|
payload["book"] = payload.get("book") or book
|
|
payload["commentary"] = commentary
|
|
|
|
with open(book_file, "w", encoding="utf-8") as f:
|
|
json.dump(payload, f, ensure_ascii=False, indent=2)
|
|
|
|
# Clear cached data so future loads pick up updates
|
|
load_commentary.cache_clear()
|