Files
kjvstudy.org/kjvstudy_org/stories.py
T
kennethreitz 91b2e2cab0 Add JSON caching to stories and timeline modules
Previously, stories.py loaded all story JSON files on every request
and timeline.py loaded biblical_timeline.json on every request.
Now both use module-level caching for better performance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 19:58:14 -05:00

141 lines
4.0 KiB
Python

"""Bible Stories data loader.
Loads all story JSON files and provides access to stories by category and slug.
"""
import json
from functools import lru_cache
from pathlib import Path
from typing import Optional
# Path to stories directory
STORIES_DIR = Path(__file__).parent / "data" / "stories"
def _load_all_stories_uncached() -> list[dict]:
"""Load all story categories from JSON files (internal, uncached)."""
categories = []
if not STORIES_DIR.exists():
return categories
# Load JSON files in order (they're numbered)
json_files = sorted(STORIES_DIR.glob("*.json"))
for json_file in json_files:
try:
with open(json_file, "r", encoding="utf-8") as f:
category_data = json.load(f)
categories.append(category_data)
except (json.JSONDecodeError, IOError) as e:
print(f"Error loading {json_file}: {e}")
continue
return categories
# Load stories once at module import time
_STORIES_CACHE: list[dict] | None = None
def load_all_stories() -> list[dict]:
"""Load all story categories from JSON files (cached)."""
global _STORIES_CACHE
if _STORIES_CACHE is None:
_STORIES_CACHE = _load_all_stories_uncached()
return _STORIES_CACHE
def get_all_stories_flat() -> list[dict]:
"""Get all stories as a flat list with category info attached."""
categories = load_all_stories()
all_stories = []
for category in categories:
category_name = category.get("category", "Unknown")
category_slug = category.get("slug", "unknown")
for story in category.get("stories", []):
story_with_category = story.copy()
story_with_category["category_name"] = category_name
story_with_category["category_slug"] = category_slug
all_stories.append(story_with_category)
return all_stories
def get_story_by_slug(slug: str) -> Optional[dict]:
"""Find a story by its slug."""
for story in get_all_stories_flat():
if story.get("slug") == slug:
return story
return None
def get_stories_by_category(category_slug: str) -> list[dict]:
"""Get all stories in a specific category."""
categories = load_all_stories()
for category in categories:
if category.get("slug") == category_slug:
stories = []
for story in category.get("stories", []):
story_with_category = story.copy()
story_with_category["category_name"] = category.get("category", "Unknown")
story_with_category["category_slug"] = category_slug
stories.append(story_with_category)
return stories
return []
def get_category_by_slug(category_slug: str) -> Optional[dict]:
"""Get a category by its slug."""
categories = load_all_stories()
for category in categories:
if category.get("slug") == category_slug:
return category
return None
# Cache story counts
_CACHED_STORY_COUNT = None
_CACHED_CATEGORY_COUNT = None
def get_story_count() -> int:
"""Get total number of stories (cached)."""
global _CACHED_STORY_COUNT
if _CACHED_STORY_COUNT is None:
_CACHED_STORY_COUNT = len(get_all_stories_flat())
return _CACHED_STORY_COUNT
def get_category_count() -> int:
"""Get total number of categories (cached)."""
global _CACHED_CATEGORY_COUNT
if _CACHED_CATEGORY_COUNT is None:
_CACHED_CATEGORY_COUNT = len(load_all_stories())
return _CACHED_CATEGORY_COUNT
# Pre-load categories on module import for faster access
STORY_CATEGORIES = None
def get_categories() -> list[dict]:
"""Get all categories (cached)."""
global STORY_CATEGORIES
if STORY_CATEGORIES is None:
STORY_CATEGORIES = load_all_stories()
return STORY_CATEGORIES
def refresh_stories():
"""Refresh the cached stories (useful after adding new files)."""
global STORY_CATEGORIES, _CACHED_STORY_COUNT, _CACHED_CATEGORY_COUNT
STORY_CATEGORIES = load_all_stories()
_CACHED_STORY_COUNT = None
_CACHED_CATEGORY_COUNT = None