mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
115acd784c
- Replace bare except with specific exceptions in helpers.py - Use context managers for all JSON file operations in about.py and api.py to ensure proper file handle cleanup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
483 lines
23 KiB
Python
483 lines
23 KiB
Python
"""Helper utilities for KJV Study."""
|
|
import re
|
|
import json
|
|
import hashlib
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, List
|
|
from functools import lru_cache
|
|
|
|
from ..kjv import bible, VerseReference
|
|
from ..topics import get_all_topics
|
|
from ..red_letter import get_christ_words
|
|
from ..stories import get_all_stories_flat
|
|
from .books import normalize_book_name
|
|
|
|
# Paths to data files
|
|
_DATA_DIR = Path(__file__).parent.parent / "data"
|
|
_CHAPTER_EXPLANATIONS_PATH = _DATA_DIR / "chapter_explanations.json"
|
|
_POPULAR_CHAPTERS_PATH = _DATA_DIR / "popular_chapters.json"
|
|
_FEATURED_VERSES_PATH = _DATA_DIR / "featured_verses.json"
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _load_chapter_explanations() -> dict:
|
|
"""Load chapter explanations from JSON file. Cached since data never changes."""
|
|
with open(_CHAPTER_EXPLANATIONS_PATH, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
# Convert string keys to integers for chapter numbers
|
|
return {
|
|
book: {int(chapter): explanation for chapter, explanation in chapters.items()}
|
|
for book, chapters in data.items()
|
|
}
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _load_popular_chapters() -> dict:
|
|
"""Load popular chapters from JSON file. Cached since data never changes."""
|
|
with open(_POPULAR_CHAPTERS_PATH, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
# Convert string keys to integers for chapter numbers
|
|
popular = {
|
|
book: {int(chapter): score for chapter, score in chapters.items()}
|
|
for book, chapters in data["popular_chapters"].items()
|
|
}
|
|
return {
|
|
"popular_chapters": popular,
|
|
"high_readership_books": data["high_readership_books"]
|
|
}
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _load_featured_verses() -> list:
|
|
"""Load featured verses from JSON file. Cached since data never changes."""
|
|
with open(_FEATURED_VERSES_PATH, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
# Convert dict format to tuple format for compatibility
|
|
return [
|
|
(verse["book"], verse["chapter"], verse["verse"])
|
|
for verse in data["verses"]
|
|
]
|
|
|
|
|
|
@lru_cache(maxsize=512)
|
|
def create_slug(text: str) -> str:
|
|
"""
|
|
Convert text to URL-friendly slug.
|
|
Cached since same inputs generate same outputs and called frequently.
|
|
"""
|
|
slug = re.sub(r'[^\w\s-]', '', text.lower())
|
|
slug = re.sub(r'[-\s]+', '-', slug)
|
|
return slug.strip('-')
|
|
|
|
|
|
def get_verse_text(book: str, chapter: int, verse: int) -> str:
|
|
"""Get the actual text of a specific verse."""
|
|
try:
|
|
text = bible.get_verse_text(book, chapter, verse)
|
|
if text:
|
|
return text
|
|
return f"{book} {chapter}:{verse} text not found"
|
|
except (KeyError, ValueError, TypeError, AttributeError):
|
|
return f"{book} {chapter}:{verse}"
|
|
|
|
|
|
def is_verse_reference(query: str) -> bool:
|
|
"""Check if query looks like a verse reference."""
|
|
# Pattern for verse references like "John 3:16", "1 John 4:8", "Genesis 1:1", etc.
|
|
# Also supports space format like "Rev 22 20" (without colon)
|
|
colon_pattern = r'^(I{1,3}|1|2|3)?\s*[A-Za-z]+(\s+[A-Za-z]+)?\s+\d+:\d+$'
|
|
space_pattern = r'^(I{1,3}|1|2|3)?\s*[A-Za-z]+(\s+[A-Za-z]+)?\s+\d+\s+\d+$'
|
|
query = query.strip()
|
|
return bool(re.match(colon_pattern, query) or re.match(space_pattern, query))
|
|
|
|
|
|
def parse_verse_reference(query: str) -> Optional[Dict]:
|
|
"""Parse a verse reference string and return verse info if found."""
|
|
try:
|
|
cleaned_query = query.strip()
|
|
verse_ref = VerseReference.from_string(cleaned_query)
|
|
|
|
# Try to normalize the book name (handles abbreviations like "Rev" -> "Revelation")
|
|
book = verse_ref.book
|
|
normalized = normalize_book_name(book)
|
|
if normalized:
|
|
book = normalized
|
|
|
|
verse_text = bible.get_verse_text(book, verse_ref.chapter, verse_ref.verse)
|
|
|
|
if verse_text:
|
|
return {
|
|
"book": book,
|
|
"chapter": verse_ref.chapter,
|
|
"verse": verse_ref.verse,
|
|
"text": verse_text,
|
|
"reference": f"{book} {verse_ref.chapter}:{verse_ref.verse}",
|
|
"url": f"/book/{book}/chapter/{verse_ref.chapter}#verse-{verse_ref.verse}",
|
|
"score": 100.0,
|
|
"highlighted_text": verse_text
|
|
}
|
|
except Exception as e:
|
|
print(f"Error parsing verse reference '{query}': {e}")
|
|
|
|
# Try alternative book name formats (Roman numerals to numbers)
|
|
try:
|
|
alternative_query = query.strip()
|
|
alternative_query = re.sub(r'^I\s+', '1 ', alternative_query)
|
|
alternative_query = re.sub(r'^II\s+', '2 ', alternative_query)
|
|
alternative_query = re.sub(r'^III\s+', '3 ', alternative_query)
|
|
|
|
if alternative_query != query.strip():
|
|
verse_ref = VerseReference.from_string(alternative_query)
|
|
|
|
# Try to normalize the book name
|
|
book = verse_ref.book
|
|
normalized = normalize_book_name(book)
|
|
if normalized:
|
|
book = normalized
|
|
|
|
verse_text = bible.get_verse_text(book, verse_ref.chapter, verse_ref.verse)
|
|
|
|
if verse_text:
|
|
return {
|
|
"book": book,
|
|
"chapter": verse_ref.chapter,
|
|
"verse": verse_ref.verse,
|
|
"text": verse_text,
|
|
"reference": f"{book} {verse_ref.chapter}:{verse_ref.verse}",
|
|
"url": f"/book/{book}/chapter/{verse_ref.chapter}#verse-{verse_ref.verse}",
|
|
"score": 100.0,
|
|
"highlighted_text": verse_text
|
|
}
|
|
except Exception as e2:
|
|
print(f"Alternative parsing also failed for '{query}': {e2}")
|
|
|
|
return None
|
|
|
|
|
|
@lru_cache(maxsize=256)
|
|
def get_related_content(book: str, chapter: int = None, verse: int = None) -> Dict:
|
|
"""
|
|
Get related study guides, topics, and resources for a given passage.
|
|
Cached since this does expensive dictionary lookups and topic searches.
|
|
"""
|
|
related = {
|
|
"study_guides": [],
|
|
"topics": [],
|
|
"people": [],
|
|
"resources": [],
|
|
"stories": []
|
|
}
|
|
|
|
# Find related Bible stories based on verse references
|
|
all_stories = get_all_stories_flat()
|
|
for story in all_stories:
|
|
story_verses = story.get("verses", [])
|
|
for verse_ref in story_verses:
|
|
# Parse verse references like "Genesis 1:1-31" or "Genesis 2:1-3"
|
|
if verse_ref.startswith(book + " "):
|
|
# Extract chapter from reference
|
|
ref_part = verse_ref[len(book) + 1:] # e.g., "1:1-31" or "2:1-3"
|
|
if ":" in ref_part:
|
|
ref_chapter = ref_part.split(":")[0]
|
|
try:
|
|
ref_chapter_num = int(ref_chapter)
|
|
# If chapter matches (or no chapter specified), include this story
|
|
if chapter is None or ref_chapter_num == chapter:
|
|
story_entry = {
|
|
"name": story.get("title", ""),
|
|
"url": f"/stories/{story.get('slug', '')}",
|
|
"description": story.get("description", "")[:100] + "..." if len(story.get("description", "")) > 100 else story.get("description", "")
|
|
}
|
|
# Avoid duplicates
|
|
if story_entry not in related["stories"]:
|
|
related["stories"].append(story_entry)
|
|
break # Only add each story once per book/chapter match
|
|
except ValueError:
|
|
pass
|
|
|
|
# Map books to related people
|
|
book_people_map = {
|
|
"Genesis": [{"name": "Abraham", "url": "/family-tree/person/i60"}, {"name": "Jacob", "url": "/family-tree/person/i58"}],
|
|
"Exodus": [{"name": "Moses", "url": "/biblical-prophets/moses"}],
|
|
"1 Samuel": [{"name": "Samuel", "url": "/biblical-prophets"}],
|
|
"2 Samuel": [{"name": "David", "url": "/family-tree/person/i113"}],
|
|
"1 Kings": [{"name": "Elijah", "url": "/biblical-prophets/elijah"}],
|
|
"2 Kings": [{"name": "Elijah", "url": "/biblical-prophets/elijah"}, {"name": "Elisha", "url": "/biblical-prophets"}],
|
|
"Isaiah": [{"name": "Isaiah", "url": "/biblical-prophets/isaiah"}],
|
|
"Jeremiah": [{"name": "Jeremiah", "url": "/biblical-prophets/jeremiah"}],
|
|
"Ezekiel": [{"name": "Ezekiel", "url": "/biblical-prophets/ezekiel"}],
|
|
"Daniel": [{"name": "Daniel", "url": "/biblical-prophets/daniel"}],
|
|
"Jonah": [{"name": "Jonah", "url": "/biblical-prophets/jonah"}],
|
|
"Matthew": [{"name": "The Twelve Apostles", "url": "/the-twelve-apostles"}],
|
|
"Mark": [{"name": "The Twelve Apostles", "url": "/the-twelve-apostles"}],
|
|
"Luke": [{"name": "The Twelve Apostles", "url": "/the-twelve-apostles"}, {"name": "John the Baptist", "url": "/biblical-prophets"}],
|
|
"John": [{"name": "John", "url": "/the-twelve-apostles/john"}],
|
|
"Acts": [{"name": "Peter", "url": "/the-twelve-apostles/simon-peter"}, {"name": "Paul", "url": "/the-twelve-apostles"}],
|
|
"Ruth": [{"name": "Ruth", "url": "/women-of-the-bible/ruth"}],
|
|
"Esther": [{"name": "Esther", "url": "/women-of-the-bible/esther"}],
|
|
}
|
|
|
|
if book in book_people_map:
|
|
related["people"] = book_people_map[book]
|
|
|
|
# Map books/passages to special resources
|
|
if book in ["Exodus", "Leviticus", "Numbers", "Deuteronomy"]:
|
|
related["resources"].append({"name": "Biblical Festivals", "url": "/biblical-festivals"})
|
|
related["resources"].append({"name": "Biblical Covenants", "url": "/biblical-covenants"})
|
|
|
|
if book in ["Genesis", "Exodus", "Numbers"]:
|
|
related["resources"].append({"name": "Biblical Timeline", "url": "/biblical-timeline"})
|
|
|
|
if book in ["Joshua", "Judges", "1 Samuel", "2 Samuel", "1 Kings", "2 Kings"]:
|
|
related["resources"].append({"name": "Biblical Maps", "url": "/biblical-maps"})
|
|
|
|
if book in ["Matthew", "Mark", "Luke", "John"]:
|
|
related["resources"].append({"name": "Parables of Jesus", "url": "/parables"})
|
|
|
|
# Add topic links based on verse references in topic data
|
|
topics_data = get_all_topics()
|
|
for topic_name, topic_data in topics_data.items():
|
|
topic_matched = False
|
|
# Check subtopics for verse references
|
|
subtopics = topic_data.get("subtopics", {})
|
|
for subtopic_name, subtopic_data in subtopics.items():
|
|
if topic_matched:
|
|
break
|
|
verses = subtopic_data.get("verses", [])
|
|
for verse_entry in verses:
|
|
ref = verse_entry.get("ref", "")
|
|
# Check if this reference matches our book/chapter
|
|
if ref.startswith(book + " "):
|
|
ref_part = ref[len(book) + 1:]
|
|
if ":" in ref_part:
|
|
ref_chapter = ref_part.split(":")[0]
|
|
try:
|
|
ref_chapter_num = int(ref_chapter)
|
|
if chapter is None or ref_chapter_num == chapter:
|
|
related["topics"].append({
|
|
"name": topic_name,
|
|
"url": f"/topics/{topic_name}"
|
|
})
|
|
topic_matched = True
|
|
break
|
|
except ValueError:
|
|
pass
|
|
|
|
# Add specific resource pages based on book/chapter
|
|
specific_resources = {
|
|
# Sermon on the Mount / Beatitudes
|
|
("Matthew", 5): [{"name": "The Beatitudes", "url": "/beatitudes"}],
|
|
("Matthew", 6): [{"name": "Prayers of the Bible", "url": "/prayers-of-the-bible"}],
|
|
("Matthew", 7): [{"name": "The Beatitudes", "url": "/beatitudes"}],
|
|
# Ten Commandments
|
|
("Exodus", 20): [{"name": "Ten Commandments", "url": "/ten-commandments"}],
|
|
("Deuteronomy", 5): [{"name": "Ten Commandments", "url": "/ten-commandments"}],
|
|
# Armor of God & Spiritual Warfare
|
|
("Ephesians", 6): [{"name": "Armor of God", "url": "/armor-of-god"}, {"name": "Spirits and Demons", "url": "/spirits-and-demons"}],
|
|
# Fruits of the Spirit
|
|
("Galatians", 5): [{"name": "Fruits of the Spirit", "url": "/fruits-of-the-spirit"}],
|
|
# I Am Statements (John's Gospel)
|
|
("John", 6): [{"name": "I Am Statements of Jesus", "url": "/i-am-statements"}],
|
|
("John", 8): [{"name": "I Am Statements of Jesus", "url": "/i-am-statements"}],
|
|
("John", 10): [{"name": "I Am Statements of Jesus", "url": "/i-am-statements"}],
|
|
("John", 11): [{"name": "I Am Statements of Jesus", "url": "/i-am-statements"}],
|
|
("John", 14): [{"name": "I Am Statements of Jesus", "url": "/i-am-statements"}, {"name": "The Trinity", "url": "/trinity"}],
|
|
("John", 15): [{"name": "I Am Statements of Jesus", "url": "/i-am-statements"}],
|
|
# John 1 - Christology, Names of Christ
|
|
("John", 1): [{"name": "Names of Christ", "url": "/names-of-christ"}, {"name": "Christology", "url": "/christology"}],
|
|
# Love chapter
|
|
("1 Corinthians", 13): [{"name": "Biblical Love", "url": "/topics/Love"}],
|
|
# Faith chapter
|
|
("Hebrews", 11): [{"name": "Heroes of Faith", "url": "/topics/Faith"}],
|
|
# Messianic prophecies in key OT chapters
|
|
("Isaiah", 7): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Isaiah", 9): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Isaiah", 11): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Isaiah", 53): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Micah", 5): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Zechariah", 9): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Zechariah", 12): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Psalm", 22): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Psalms", 22): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Psalms", 110): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Daniel", 7): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
("Daniel", 9): [{"name": "Messianic Prophecies", "url": "/messianic-prophecies"}],
|
|
# Names of God
|
|
("Exodus", 3): [{"name": "Names of God", "url": "/names-of-god"}, {"name": "Tetragrammaton", "url": "/tetragrammaton"}],
|
|
("Exodus", 6): [{"name": "Names of God", "url": "/names-of-god"}],
|
|
("Genesis", 14): [{"name": "Names of God", "url": "/names-of-god"}],
|
|
("Genesis", 22): [{"name": "Names of God", "url": "/names-of-god"}],
|
|
# Biblical covenants
|
|
("Genesis", 9): [{"name": "Biblical Covenants", "url": "/biblical-covenants"}],
|
|
("Genesis", 15): [{"name": "Biblical Covenants", "url": "/biblical-covenants"}],
|
|
("Genesis", 17): [{"name": "Biblical Covenants", "url": "/biblical-covenants"}],
|
|
("2 Samuel", 7): [{"name": "Biblical Covenants", "url": "/biblical-covenants"}],
|
|
("Jeremiah", 31): [{"name": "Biblical Covenants", "url": "/biblical-covenants"}],
|
|
("Hebrews", 8): [{"name": "Biblical Covenants", "url": "/biblical-covenants"}],
|
|
("Hebrews", 9): [{"name": "Biblical Covenants", "url": "/biblical-covenants"}, {"name": "Blood in Scripture", "url": "/blood-in-scripture"}],
|
|
# Angels
|
|
("Genesis", 18): [{"name": "Biblical Angels", "url": "/biblical-angels"}],
|
|
("Genesis", 19): [{"name": "Biblical Angels", "url": "/biblical-angels"}],
|
|
("Daniel", 10): [{"name": "Biblical Angels", "url": "/biblical-angels"}],
|
|
("Luke", 1): [{"name": "Biblical Angels", "url": "/biblical-angels"}],
|
|
("Luke", 2): [{"name": "Biblical Angels", "url": "/biblical-angels"}],
|
|
("Revelation", 4): [{"name": "Biblical Angels", "url": "/biblical-angels"}],
|
|
("Revelation", 5): [{"name": "Biblical Angels", "url": "/biblical-angels"}],
|
|
# Trinity
|
|
("Matthew", 3): [{"name": "The Trinity", "url": "/trinity"}],
|
|
("Matthew", 28): [{"name": "The Trinity", "url": "/trinity"}],
|
|
("2 Corinthians", 13): [{"name": "The Trinity", "url": "/trinity"}],
|
|
# Holy Spirit / Pneumatology
|
|
("Acts", 2): [{"name": "Pneumatology", "url": "/pneumatology"}],
|
|
("John", 16): [{"name": "Pneumatology", "url": "/pneumatology"}],
|
|
("Romans", 8): [{"name": "Pneumatology", "url": "/pneumatology"}, {"name": "Providence", "url": "/providence"}],
|
|
# Eschatology
|
|
("Matthew", 24): [{"name": "Eschatology", "url": "/eschatology"}],
|
|
("Matthew", 25): [{"name": "Eschatology", "url": "/eschatology"}],
|
|
("1 Thessalonians", 4): [{"name": "Eschatology", "url": "/eschatology"}],
|
|
("2 Thessalonians", 2): [{"name": "Eschatology", "url": "/eschatology"}],
|
|
("Revelation", 20): [{"name": "Eschatology", "url": "/eschatology"}],
|
|
("Revelation", 21): [{"name": "Eschatology", "url": "/eschatology"}],
|
|
("Revelation", 22): [{"name": "Eschatology", "url": "/eschatology"}],
|
|
# Ecclesiology (Church)
|
|
("Matthew", 16): [{"name": "Ecclesiology", "url": "/ecclesiology"}],
|
|
("Matthew", 18): [{"name": "Ecclesiology", "url": "/ecclesiology"}],
|
|
("1 Corinthians", 12): [{"name": "Ecclesiology", "url": "/ecclesiology"}],
|
|
("Ephesians", 4): [{"name": "Ecclesiology", "url": "/ecclesiology"}],
|
|
# Soteriology (Salvation)
|
|
("Romans", 3): [{"name": "Soteriology", "url": "/soteriology"}],
|
|
("Romans", 5): [{"name": "Soteriology", "url": "/soteriology"}, {"name": "Justification", "url": "/justification"}],
|
|
("Romans", 6): [{"name": "Soteriology", "url": "/soteriology"}, {"name": "Sanctification", "url": "/sanctification"}],
|
|
("Ephesians", 2): [{"name": "Soteriology", "url": "/soteriology"}, {"name": "Grace", "url": "/grace"}],
|
|
("Titus", 3): [{"name": "Soteriology", "url": "/soteriology"}],
|
|
# Types and Shadows
|
|
("Hebrews", 10): [{"name": "Types and Shadows", "url": "/types-and-shadows"}],
|
|
# Blood in Scripture
|
|
("Leviticus", 17): [{"name": "Blood in Scripture", "url": "/blood-in-scripture"}],
|
|
# Kingdom of God
|
|
("Matthew", 13): [{"name": "Kingdom of God", "url": "/kingdom-of-god"}],
|
|
("Mark", 4): [{"name": "Kingdom of God", "url": "/kingdom-of-god"}],
|
|
("Luke", 17): [{"name": "Kingdom of God", "url": "/kingdom-of-god"}],
|
|
# Miracles of Jesus
|
|
("Matthew", 8): [{"name": "Miracles of Jesus", "url": "/miracles-of-jesus"}],
|
|
("Matthew", 9): [{"name": "Miracles of Jesus", "url": "/miracles-of-jesus"}],
|
|
("Mark", 5): [{"name": "Miracles of Jesus", "url": "/miracles-of-jesus"}],
|
|
("John", 2): [{"name": "Miracles of Jesus", "url": "/miracles-of-jesus"}],
|
|
("John", 9): [{"name": "Miracles of Jesus", "url": "/miracles-of-jesus"}],
|
|
# Christology
|
|
("Colossians", 1): [{"name": "Christology", "url": "/christology"}],
|
|
("Philippians", 2): [{"name": "Christology", "url": "/christology"}],
|
|
("Hebrews", 1): [{"name": "Christology", "url": "/christology"}],
|
|
# Law and Gospel
|
|
("Romans", 7): [{"name": "Law and Gospel", "url": "/law-and-gospel"}],
|
|
("Galatians", 3): [{"name": "Law and Gospel", "url": "/law-and-gospel"}],
|
|
# Hamartiology (Sin)
|
|
("Genesis", 3): [{"name": "Hamartiology", "url": "/hamartiology"}],
|
|
("Romans", 1): [{"name": "Hamartiology", "url": "/hamartiology"}],
|
|
# Providence
|
|
("Genesis", 50): [{"name": "Providence", "url": "/providence"}],
|
|
# Worship
|
|
("Psalms", 100): [{"name": "Worship", "url": "/worship"}],
|
|
("Psalms", 150): [{"name": "Worship", "url": "/worship"}],
|
|
("John", 4): [{"name": "Worship", "url": "/worship"}],
|
|
# Spirits and Demons
|
|
("1 Peter", 5): [{"name": "Spirits and Demons", "url": "/spirits-and-demons"}],
|
|
}
|
|
|
|
if chapter is not None:
|
|
key = (book, chapter)
|
|
if key in specific_resources:
|
|
for resource in specific_resources[key]:
|
|
if resource not in related["resources"]:
|
|
related["resources"].append(resource)
|
|
|
|
return related
|
|
|
|
|
|
# Load popular chapters data from JSON
|
|
_popular_data = _load_popular_chapters()
|
|
POPULAR_CHAPTERS = _popular_data["popular_chapters"]
|
|
HIGH_READERSHIP_BOOKS = _popular_data["high_readership_books"]
|
|
|
|
|
|
@lru_cache(maxsize=512)
|
|
def get_chapter_popularity_score(book: str, chapter: int) -> int:
|
|
"""
|
|
Calculate popularity score for a chapter (1-10 scale) based on well-known verses.
|
|
Cached since calculation is deterministic and called frequently.
|
|
"""
|
|
if book in POPULAR_CHAPTERS and chapter in POPULAR_CHAPTERS[book]:
|
|
return POPULAR_CHAPTERS[book][chapter]
|
|
|
|
default_score = 4
|
|
|
|
if chapter == 1:
|
|
default_score += 1
|
|
|
|
if book in HIGH_READERSHIP_BOOKS:
|
|
default_score += 1
|
|
|
|
total_chapters = len(bible.get_chapters_for_book(book))
|
|
if total_chapters <= 5:
|
|
default_score += 1
|
|
|
|
return min(default_score, 6)
|
|
|
|
|
|
# Load chapter explanations from JSON
|
|
CHAPTER_EXPLANATIONS = _load_chapter_explanations()
|
|
|
|
|
|
def get_chapter_popularity_explanation(book: str, chapter: int) -> str:
|
|
"""Get explanation for why a chapter is popular or what it contains."""
|
|
if book in CHAPTER_EXPLANATIONS and chapter in CHAPTER_EXPLANATIONS[book]:
|
|
return CHAPTER_EXPLANATIONS[book][chapter]
|
|
|
|
if chapter == 1:
|
|
return f"Opening chapter of {book} - introduces key themes and characters"
|
|
|
|
if book in ["Matthew", "Mark", "Luke", "John"]:
|
|
return "Gospel account of Jesus' life and ministry"
|
|
elif book in ["Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy"]:
|
|
return "Torah/Pentateuch - foundational law and history of Israel"
|
|
elif book in ["Psalms", "Proverbs", "Ecclesiastes", "Song of Solomon"]:
|
|
return "Wisdom literature - poetry and practical life guidance"
|
|
elif book in ["Isaiah", "Jeremiah", "Ezekiel", "Daniel"]:
|
|
return "Major prophet - messages of judgment and hope"
|
|
elif book in ["Romans", "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians",
|
|
"Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians",
|
|
"1 Timothy", "2 Timothy", "Titus", "Philemon"]:
|
|
return "Pauline epistle - apostolic teaching for the early church"
|
|
elif book == "Acts":
|
|
return "History of the early church and spread of the gospel"
|
|
elif book == "Revelation":
|
|
return "Apocalyptic vision of the end times and Christ's victory"
|
|
else:
|
|
return f"Part of {book} - explore this chapter to discover its significance"
|
|
|
|
|
|
# Load featured verses from JSON
|
|
FEATURED_VERSES = _load_featured_verses()
|
|
|
|
|
|
def get_daily_verse() -> Dict:
|
|
"""Get the verse of the day based on the current date."""
|
|
today = datetime.now()
|
|
day_of_year = today.timetuple().tm_yday
|
|
verse_index = day_of_year % len(FEATURED_VERSES)
|
|
|
|
book, chapter, verse = FEATURED_VERSES[verse_index]
|
|
verse_text = bible.get_verse_text(book, chapter, verse)
|
|
christ_words = get_christ_words(book, chapter, verse)
|
|
|
|
return {
|
|
"book": book,
|
|
"chapter": chapter,
|
|
"verse": verse,
|
|
"text": verse_text,
|
|
"reference": f"{book} {chapter}:{verse}",
|
|
"url": f"/book/{book}/chapter/{chapter}#verse-{verse}",
|
|
"red_letter": christ_words
|
|
}
|