diff --git a/kjvstudy_org/data/bible_metadata.json b/kjvstudy_org/data/bible_metadata.json
new file mode 100644
index 0000000..53e2283
--- /dev/null
+++ b/kjvstudy_org/data/bible_metadata.json
@@ -0,0 +1,259 @@
+{
+ "old_testament_books": [
+ "Genesis",
+ "Exodus",
+ "Leviticus",
+ "Numbers",
+ "Deuteronomy",
+ "Joshua",
+ "Judges",
+ "Ruth",
+ "1 Samuel",
+ "2 Samuel",
+ "1 Kings",
+ "2 Kings",
+ "1 Chronicles",
+ "2 Chronicles",
+ "Ezra",
+ "Nehemiah",
+ "Esther",
+ "Job",
+ "Psalms",
+ "Proverbs",
+ "Ecclesiastes",
+ "Solomon's Song",
+ "Isaiah",
+ "Jeremiah",
+ "Lamentations",
+ "Ezekiel",
+ "Daniel",
+ "Hosea",
+ "Joel",
+ "Amos",
+ "Obadiah",
+ "Jonah",
+ "Micah",
+ "Nahum",
+ "Habakkuk",
+ "Zephaniah",
+ "Haggai",
+ "Zechariah",
+ "Malachi"
+ ],
+ "new_testament_books": [
+ "Matthew",
+ "Mark",
+ "Luke",
+ "John",
+ "Acts",
+ "Romans",
+ "1 Corinthians",
+ "2 Corinthians",
+ "Galatians",
+ "Ephesians",
+ "Philippians",
+ "Colossians",
+ "1 Thessalonians",
+ "2 Thessalonians",
+ "1 Timothy",
+ "2 Timothy",
+ "Titus",
+ "Philemon",
+ "Hebrews",
+ "James",
+ "1 Peter",
+ "2 Peter",
+ "1 John",
+ "2 John",
+ "3 John",
+ "Jude",
+ "Revelation"
+ ],
+ "book_abbreviations": {
+ "Psalm": "Psalms",
+ "I Samuel": "1 Samuel",
+ "II Samuel": "2 Samuel",
+ "I Kings": "1 Kings",
+ "II Kings": "2 Kings",
+ "I Chronicles": "1 Chronicles",
+ "II Chronicles": "2 Chronicles",
+ "I Corinthians": "1 Corinthians",
+ "II Corinthians": "2 Corinthians",
+ "I Thessalonians": "1 Thessalonians",
+ "II Thessalonians": "2 Thessalonians",
+ "I Timothy": "1 Timothy",
+ "II Timothy": "2 Timothy",
+ "I Peter": "1 Peter",
+ "II Peter": "2 Peter",
+ "I John": "1 John",
+ "II John": "2 John",
+ "III John": "3 John",
+ "First Samuel": "1 Samuel",
+ "Second Samuel": "2 Samuel",
+ "First Kings": "1 Kings",
+ "Second Kings": "2 Kings",
+ "First Chronicles": "1 Chronicles",
+ "Second Chronicles": "2 Chronicles",
+ "First Corinthians": "1 Corinthians",
+ "Second Corinthians": "2 Corinthians",
+ "First Thessalonians": "1 Thessalonians",
+ "Second Thessalonians": "2 Thessalonians",
+ "First Timothy": "1 Timothy",
+ "Second Timothy": "2 Timothy",
+ "First Peter": "1 Peter",
+ "Second Peter": "2 Peter",
+ "First John": "1 John",
+ "Second John": "2 John",
+ "Third John": "3 John",
+ "Song of Solomon": "Solomon's Song",
+ "Song of Songs": "Solomon's Song",
+ "Canticles": "Solomon's Song",
+ "Gen": "Genesis",
+ "Ge": "Genesis",
+ "Exo": "Exodus",
+ "Ex": "Exodus",
+ "Lev": "Leviticus",
+ "Le": "Leviticus",
+ "Num": "Numbers",
+ "Nu": "Numbers",
+ "Deut": "Deuteronomy",
+ "Dt": "Deuteronomy",
+ "Josh": "Joshua",
+ "Jos": "Joshua",
+ "Judg": "Judges",
+ "Jdg": "Judges",
+ "Ru": "Ruth",
+ "1Sam": "1 Samuel",
+ "1 Sam": "1 Samuel",
+ "1S": "1 Samuel",
+ "2Sam": "2 Samuel",
+ "2 Sam": "2 Samuel",
+ "2S": "2 Samuel",
+ "1Ki": "1 Kings",
+ "1 Ki": "1 Kings",
+ "1K": "1 Kings",
+ "2Ki": "2 Kings",
+ "2 Ki": "2 Kings",
+ "2K": "2 Kings",
+ "1Chr": "1 Chronicles",
+ "1 Chr": "1 Chronicles",
+ "1Ch": "1 Chronicles",
+ "2Chr": "2 Chronicles",
+ "2 Chr": "2 Chronicles",
+ "2Ch": "2 Chronicles",
+ "Ezr": "Ezra",
+ "Neh": "Nehemiah",
+ "Ne": "Nehemiah",
+ "Est": "Esther",
+ "Ps": "Psalms",
+ "Psa": "Psalms",
+ "Prov": "Proverbs",
+ "Pr": "Proverbs",
+ "Eccl": "Ecclesiastes",
+ "Ec": "Ecclesiastes",
+ "Song": "Song of Solomon",
+ "Sos": "Song of Solomon",
+ "SS": "Song of Solomon",
+ "Isa": "Isaiah",
+ "Is": "Isaiah",
+ "Jer": "Jeremiah",
+ "Je": "Jeremiah",
+ "Lam": "Lamentations",
+ "La": "Lamentations",
+ "Ezek": "Ezekiel",
+ "Eze": "Ezekiel",
+ "Ezk": "Ezekiel",
+ "Dan": "Daniel",
+ "Da": "Daniel",
+ "Hos": "Hosea",
+ "Ho": "Hosea",
+ "Joe": "Joel",
+ "Jl": "Joel",
+ "Am": "Amos",
+ "Ob": "Obadiah",
+ "Jon": "Jonah",
+ "Mic": "Micah",
+ "Mi": "Micah",
+ "Nah": "Nahum",
+ "Na": "Nahum",
+ "Hab": "Habakkuk",
+ "Hb": "Habakkuk",
+ "Zep": "Zephaniah",
+ "Zph": "Zephaniah",
+ "Hag": "Haggai",
+ "Hg": "Haggai",
+ "Zech": "Zechariah",
+ "Zec": "Zechariah",
+ "Zch": "Zechariah",
+ "Mal": "Malachi",
+ "Mat": "Matthew",
+ "Mt": "Matthew",
+ "Mar": "Mark",
+ "Mk": "Mark",
+ "Mrk": "Mark",
+ "Luk": "Luke",
+ "Lk": "Luke",
+ "Joh": "John",
+ "Jn": "John",
+ "Act": "Acts",
+ "Ac": "Acts",
+ "Rom": "Romans",
+ "Ro": "Romans",
+ "1Cor": "1 Corinthians",
+ "1 Cor": "1 Corinthians",
+ "1Co": "1 Corinthians",
+ "2Cor": "2 Corinthians",
+ "2 Cor": "2 Corinthians",
+ "2Co": "2 Corinthians",
+ "Gal": "Galatians",
+ "Ga": "Galatians",
+ "Eph": "Ephesians",
+ "Ep": "Ephesians",
+ "Phil": "Philippians",
+ "Php": "Philippians",
+ "Ph": "Philippians",
+ "Col": "Colossians",
+ "Co": "Colossians",
+ "1Thess": "1 Thessalonians",
+ "1 Thess": "1 Thessalonians",
+ "1Th": "1 Thessalonians",
+ "2Thess": "2 Thessalonians",
+ "2 Thess": "2 Thessalonians",
+ "2Th": "2 Thessalonians",
+ "1Tim": "1 Timothy",
+ "1 Tim": "1 Timothy",
+ "1Ti": "1 Timothy",
+ "2Tim": "2 Timothy",
+ "2 Tim": "2 Timothy",
+ "2Ti": "2 Timothy",
+ "Tit": "Titus",
+ "Ti": "Titus",
+ "Phm": "Philemon",
+ "Pm": "Philemon",
+ "Heb": "Hebrews",
+ "He": "Hebrews",
+ "Jam": "James",
+ "Jas": "James",
+ "Jm": "James",
+ "1Pet": "1 Peter",
+ "1 Pet": "1 Peter",
+ "1Pe": "1 Peter",
+ "1P": "1 Peter",
+ "2Pet": "2 Peter",
+ "2 Pet": "2 Peter",
+ "2Pe": "2 Peter",
+ "2P": "2 Peter",
+ "1Joh": "1 John",
+ "1 Joh": "1 John",
+ "1Jn": "1 John",
+ "2Joh": "2 John",
+ "2 Joh": "2 John",
+ "2Jn": "2 John",
+ "3Joh": "3 John",
+ "3 Joh": "3 John",
+ "3Jn": "3 John",
+ "Jud": "Jude",
+ "Rev": "Revelation",
+ "Re": "Revelation"
+ }
+}
diff --git a/kjvstudy_org/data/chapter_explanations.json b/kjvstudy_org/data/chapter_explanations.json
new file mode 100644
index 0000000..c0fcaef
--- /dev/null
+++ b/kjvstudy_org/data/chapter_explanations.json
@@ -0,0 +1,34 @@
+{
+ "John": {
+ "3": "Contains John 3:16 - 'For God so loved the world' - the most quoted verse in Christianity",
+ "1": "The Word became flesh - Jesus as the eternal Logos and the calling of the first disciples"
+ },
+ "1 Corinthians": {
+ "13": "The famous 'Love Chapter' - 'Love is patient, love is kind' - essential reading for weddings and Christian living"
+ },
+ "Psalms": {
+ "23": "The beloved Shepherd Psalm - 'The Lord is my shepherd, I shall not want' - comfort in times of trouble",
+ "91": "Psalm of protection - 'He who dwells in the shelter of the Most High' - promises of God's care",
+ "1": "The blessed man - contrasts the righteous and wicked, foundation of wisdom literature",
+ "139": "God's omniscience and omnipresence - 'You have searched me and known me' - intimate knowledge of God"
+ },
+ "Romans": {
+ "8": "No condemnation in Christ - 'All things work together for good' - assurance of salvation",
+ "3": "All have sinned - universal need for salvation and justification by faith",
+ "12": "Living sacrifice - practical Christian living and spiritual gifts"
+ },
+ "Matthew": {
+ "5": "The Beatitudes - 'Blessed are the poor in spirit' - foundation of Christian ethics",
+ "6": "The Lord's Prayer and teachings on worry - 'Give us this day our daily bread'",
+ "7": "Golden Rule and narrow gate - 'Do unto others as you would have them do unto you'"
+ },
+ "Genesis": {
+ "1": "Creation account - 'In the beginning God created the heavens and the earth'",
+ "3": "The Fall - Adam and Eve's disobedience and the first promise of redemption",
+ "22": "Abraham's ultimate test - the near-sacrifice of Isaac, foreshadowing Christ"
+ },
+ "Isaiah": {
+ "53": "The Suffering Servant - 'He was wounded for our transgressions' - prophecy of Christ's crucifixion",
+ "40": "Comfort my people - 'Every valley shall be exalted' - hope and restoration"
+ }
+}
diff --git a/kjvstudy_org/data/featured_verses.json b/kjvstudy_org/data/featured_verses.json
new file mode 100644
index 0000000..e7b0404
--- /dev/null
+++ b/kjvstudy_org/data/featured_verses.json
@@ -0,0 +1,35 @@
+{
+ "verses": [
+ {"book": "John", "chapter": 3, "verse": 16},
+ {"book": "Psalms", "chapter": 23, "verse": 1},
+ {"book": "Proverbs", "chapter": 3, "verse": 5},
+ {"book": "Philippians", "chapter": 4, "verse": 13},
+ {"book": "Romans", "chapter": 8, "verse": 28},
+ {"book": "Isaiah", "chapter": 40, "verse": 31},
+ {"book": "Jeremiah", "chapter": 29, "verse": 11},
+ {"book": "Joshua", "chapter": 1, "verse": 9},
+ {"book": "Matthew", "chapter": 11, "verse": 28},
+ {"book": "Psalms", "chapter": 46, "verse": 10},
+ {"book": "Romans", "chapter": 12, "verse": 2},
+ {"book": "2 Timothy", "chapter": 1, "verse": 7},
+ {"book": "Proverbs", "chapter": 22, "verse": 6},
+ {"book": "1 Corinthians", "chapter": 13, "verse": 4},
+ {"book": "Galatians", "chapter": 5, "verse": 22},
+ {"book": "Hebrews", "chapter": 11, "verse": 1},
+ {"book": "James", "chapter": 1, "verse": 2},
+ {"book": "1 Peter", "chapter": 5, "verse": 7},
+ {"book": "Psalms", "chapter": 119, "verse": 105},
+ {"book": "Matthew", "chapter": 6, "verse": 33},
+ {"book": "John", "chapter": 14, "verse": 6},
+ {"book": "Romans", "chapter": 5, "verse": 8},
+ {"book": "Ephesians", "chapter": 2, "verse": 8},
+ {"book": "Psalms", "chapter": 27, "verse": 1},
+ {"book": "Isaiah", "chapter": 41, "verse": 10},
+ {"book": "Matthew", "chapter": 28, "verse": 19},
+ {"book": "John", "chapter": 1, "verse": 1},
+ {"book": "Psalms", "chapter": 51, "verse": 10},
+ {"book": "Proverbs", "chapter": 18, "verse": 10},
+ {"book": "2 Corinthians", "chapter": 5, "verse": 17},
+ {"book": "Colossians", "chapter": 3, "verse": 23}
+ ]
+}
diff --git a/kjvstudy_org/data/popular_chapters.json b/kjvstudy_org/data/popular_chapters.json
new file mode 100644
index 0000000..1197e5d
--- /dev/null
+++ b/kjvstudy_org/data/popular_chapters.json
@@ -0,0 +1,145 @@
+{
+ "popular_chapters": {
+ "John": {
+ "3": 10
+ },
+ "1 Corinthians": {
+ "13": 10
+ },
+ "Psalms": {
+ "23": 10,
+ "91": 9,
+ "1": 8,
+ "139": 8
+ },
+ "Romans": {
+ "8": 9,
+ "3": 8,
+ "12": 8
+ },
+ "Matthew": {
+ "5": 9,
+ "6": 8,
+ "7": 8
+ },
+ "Ephesians": {
+ "2": 8,
+ "6": 8
+ },
+ "Philippians": {
+ "4": 8
+ },
+ "Genesis": {
+ "1": 9,
+ "3": 8,
+ "22": 7
+ },
+ "Exodus": {
+ "20": 8,
+ "14": 7
+ },
+ "Isaiah": {
+ "53": 9,
+ "40": 8
+ },
+ "Jeremiah": {
+ "29": 7
+ },
+ "Proverbs": {
+ "31": 7,
+ "3": 7
+ },
+ "Ecclesiastes": {
+ "3": 8
+ },
+ "1 Peter": {
+ "5": 7
+ },
+ "James": {
+ "1": 7
+ },
+ "Hebrews": {
+ "11": 8,
+ "12": 7
+ },
+ "Revelation": {
+ "21": 8,
+ "22": 7
+ },
+ "Luke": {
+ "2": 9,
+ "15": 8
+ },
+ "2 Timothy": {
+ "3": 7
+ },
+ "Joshua": {
+ "1": 7
+ },
+ "Daniel": {
+ "3": 7,
+ "6": 7
+ },
+ "1 John": {
+ "4": 8
+ },
+ "Galatians": {
+ "5": 7
+ },
+ "Colossians": {
+ "3": 7
+ },
+ "1 Thessalonians": {
+ "4": 7
+ },
+ "Mark": {
+ "16": 7
+ },
+ "Acts": {
+ "2": 8
+ },
+ "1 Samuel": {
+ "17": 7
+ },
+ "Job": {
+ "19": 7
+ },
+ "2 Corinthians": {
+ "5": 7
+ },
+ "1 Kings": {
+ "3": 6,
+ "18": 6
+ },
+ "Malachi": {
+ "3": 6
+ },
+ "Joel": {
+ "2": 6
+ },
+ "Micah": {
+ "6": 6
+ },
+ "Habakkuk": {
+ "2": 6
+ }
+ },
+ "high_readership_books": [
+ "Matthew",
+ "Mark",
+ "Luke",
+ "John",
+ "Acts",
+ "Romans",
+ "1 Corinthians",
+ "2 Corinthians",
+ "Galatians",
+ "Ephesians",
+ "Philippians",
+ "Colossians",
+ "Genesis",
+ "Exodus",
+ "Psalms",
+ "Proverbs"
+ ]
+}
diff --git a/kjvstudy_org/data/resource_slugs.json b/kjvstudy_org/data/resource_slugs.json
new file mode 100644
index 0000000..ee22824
--- /dev/null
+++ b/kjvstudy_org/data/resource_slugs.json
@@ -0,0 +1,113 @@
+{
+ "study_guides": [
+ "sermon-on-the-mount",
+ "lords-prayer",
+ "beatitudes",
+ "fruit-of-spirit",
+ "armor-of-god",
+ "ten-commandments",
+ "parables-of-jesus",
+ "miracles-of-jesus",
+ "names-of-god",
+ "attributes-of-god",
+ "trinity",
+ "holy-spirit",
+ "salvation",
+ "justification",
+ "sanctification",
+ "redemption"
+ ],
+ "angels": [
+ "michael",
+ "gabriel",
+ "lucifer",
+ "abaddon"
+ ],
+ "prophets": [
+ "moses",
+ "elijah",
+ "isaiah",
+ "jeremiah",
+ "ezekiel",
+ "daniel",
+ "jonah",
+ "john-the-baptist"
+ ],
+ "names_of_god": [
+ "elohim",
+ "yahweh",
+ "adonai",
+ "el-shaddai",
+ "yahweh-jireh",
+ "yahweh-rapha",
+ "yahweh-nissi",
+ "yahweh-shalom",
+ "yahweh-tsidkenu",
+ "yahweh-shammah"
+ ],
+ "parables": [
+ "sower",
+ "wheat-tares",
+ "mustard-seed",
+ "good-samaritan",
+ "prodigal-son",
+ "lost-sheep",
+ "talents",
+ "wise-foolish-builders"
+ ],
+ "covenants": [
+ "noahic",
+ "abrahamic",
+ "mosaic",
+ "davidic",
+ "new-covenant"
+ ],
+ "apostles": [
+ "peter",
+ "andrew",
+ "james-son-of-zebedee",
+ "john",
+ "philip",
+ "bartholomew",
+ "thomas",
+ "matthew",
+ "james-son-of-alphaeus",
+ "thaddaeus",
+ "simon-zealot",
+ "judas-iscariot"
+ ],
+ "women": [
+ "eve",
+ "sarah",
+ "rebekah",
+ "rachel",
+ "miriam",
+ "deborah",
+ "ruth",
+ "hannah",
+ "esther",
+ "mary-mother-of-jesus",
+ "mary-magdalene",
+ "martha"
+ ],
+ "festivals": [
+ "passover",
+ "unleavened-bread",
+ "firstfruits",
+ "pentecost",
+ "trumpets",
+ "atonement",
+ "tabernacles"
+ ],
+ "fruits_of_spirit": [
+ "love",
+ "joy",
+ "peace",
+ "longsuffering",
+ "gentleness",
+ "goodness",
+ "faith",
+ "meekness",
+ "temperance"
+ ]
+}
diff --git a/kjvstudy_org/routes/utility.py b/kjvstudy_org/routes/utility.py
index d0730c0..c2ecfc8 100644
--- a/kjvstudy_org/routes/utility.py
+++ b/kjvstudy_org/routes/utility.py
@@ -1,5 +1,8 @@
"""Utility routes for KJV Study - sitemap, robots.txt, health checks."""
+import json
from datetime import datetime
+from pathlib import Path
+from functools import lru_cache
from fastapi import APIRouter
from fastapi.responses import Response
@@ -13,13 +16,20 @@ router = APIRouter(tags=["Utility"])
_sitemap_cache = None
_sitemap_cache_date = None
-# Study guide slugs for sitemap (extracted from study_guides module)
-STUDY_GUIDE_SLUGS = [
- "sermon-on-the-mount", "lords-prayer", "beatitudes", "fruit-of-spirit",
- "armor-of-god", "ten-commandments", "parables-of-jesus", "miracles-of-jesus",
- "names-of-god", "attributes-of-god", "trinity", "holy-spirit",
- "salvation", "justification", "sanctification", "redemption",
-]
+# Path to resource slugs JSON file
+_RESOURCE_SLUGS_PATH = Path(__file__).parent.parent / "data" / "resource_slugs.json"
+
+
+@lru_cache(maxsize=1)
+def _load_resource_slugs() -> dict:
+ """Load resource slugs from JSON file. Cached since data never changes."""
+ with open(_RESOURCE_SLUGS_PATH, "r", encoding="utf-8") as f:
+ return json.load(f)
+
+
+# Load slugs from JSON
+_slugs = _load_resource_slugs()
+STUDY_GUIDE_SLUGS = _slugs["study_guides"]
@router.get("/health")
@@ -236,8 +246,7 @@ def sitemap():
"""
# Biblical angels, prophets, names of God, parables, covenants, apostles, women, festivals slugs
- angel_slugs = ["michael", "gabriel", "lucifer", "abaddon"]
- for slug in angel_slugs:
+ for slug in _slugs["angels"]:
sitemap_xml += f"""
{base_url}/biblical-angels/{slug}
{current_date}
@@ -246,8 +255,7 @@ def sitemap():
"""
- prophet_slugs = ["moses", "elijah", "isaiah", "jeremiah", "ezekiel", "daniel", "jonah", "john-the-baptist"]
- for slug in prophet_slugs:
+ for slug in _slugs["prophets"]:
sitemap_xml += f"""
{base_url}/biblical-prophets/{slug}
{current_date}
@@ -256,8 +264,7 @@ def sitemap():
"""
- god_name_slugs = ["elohim", "yahweh", "adonai", "el-shaddai", "yahweh-jireh", "yahweh-rapha", "yahweh-nissi", "yahweh-shalom", "yahweh-tsidkenu", "yahweh-shammah"]
- for slug in god_name_slugs:
+ for slug in _slugs["names_of_god"]:
sitemap_xml += f"""
{base_url}/names-of-god/{slug}
{current_date}
@@ -266,8 +273,7 @@ def sitemap():
"""
- parable_slugs = ["sower", "wheat-tares", "mustard-seed", "good-samaritan", "prodigal-son", "lost-sheep", "talents", "wise-foolish-builders"]
- for slug in parable_slugs:
+ for slug in _slugs["parables"]:
sitemap_xml += f"""
{base_url}/parables/{slug}
{current_date}
@@ -276,8 +282,7 @@ def sitemap():
"""
- covenant_slugs = ["noahic", "abrahamic", "mosaic", "davidic", "new-covenant"]
- for slug in covenant_slugs:
+ for slug in _slugs["covenants"]:
sitemap_xml += f"""
{base_url}/biblical-covenants/{slug}
{current_date}
@@ -286,8 +291,7 @@ def sitemap():
"""
- apostle_slugs = ["peter", "andrew", "james-son-of-zebedee", "john", "philip", "bartholomew", "thomas", "matthew", "james-son-of-alphaeus", "thaddaeus", "simon-zealot", "judas-iscariot"]
- for slug in apostle_slugs:
+ for slug in _slugs["apostles"]:
sitemap_xml += f"""
{base_url}/the-twelve-apostles/{slug}
{current_date}
@@ -296,8 +300,7 @@ def sitemap():
"""
- women_slugs = ["eve", "sarah", "rebekah", "rachel", "miriam", "deborah", "ruth", "hannah", "esther", "mary-mother-of-jesus", "mary-magdalene", "martha"]
- for slug in women_slugs:
+ for slug in _slugs["women"]:
sitemap_xml += f"""
{base_url}/women-of-the-bible/{slug}
{current_date}
@@ -306,8 +309,7 @@ def sitemap():
"""
- festival_slugs = ["passover", "unleavened-bread", "firstfruits", "pentecost", "trumpets", "atonement", "tabernacles"]
- for slug in festival_slugs:
+ for slug in _slugs["festivals"]:
sitemap_xml += f"""
{base_url}/biblical-festivals/{slug}
{current_date}
@@ -316,8 +318,7 @@ def sitemap():
"""
- fruit_slugs = ["love", "joy", "peace", "longsuffering", "gentleness", "goodness", "faith", "meekness", "temperance"]
- for slug in fruit_slugs:
+ for slug in _slugs["fruits_of_spirit"]:
sitemap_xml += f"""
{base_url}/fruits-of-the-spirit/{slug}
{current_date}
diff --git a/kjvstudy_org/utils/books.py b/kjvstudy_org/utils/books.py
index ccfbc23..8e6d83a 100644
--- a/kjvstudy_org/utils/books.py
+++ b/kjvstudy_org/utils/books.py
@@ -1,227 +1,28 @@
"""Book name normalization and categorization utilities."""
+import json
+from pathlib import Path
+from functools import lru_cache
from typing import Optional
-# Old Testament books
-OT_BOOKS = [
- 'Genesis', 'Exodus', 'Leviticus', 'Numbers', 'Deuteronomy',
- 'Joshua', 'Judges', 'Ruth', '1 Samuel', '2 Samuel',
- '1 Kings', '2 Kings', '1 Chronicles', '2 Chronicles',
- 'Ezra', 'Nehemiah', 'Esther', 'Job', 'Psalms', 'Proverbs',
- 'Ecclesiastes', "Solomon's Song", 'Isaiah', 'Jeremiah',
- 'Lamentations', 'Ezekiel', 'Daniel', 'Hosea', 'Joel', 'Amos',
- 'Obadiah', 'Jonah', 'Micah', 'Nahum', 'Habakkuk', 'Zephaniah',
- 'Haggai', 'Zechariah', 'Malachi'
-]
+# Path to bible metadata JSON file
+_METADATA_PATH = Path(__file__).parent.parent / "data" / "bible_metadata.json"
-# New Testament books
-NT_BOOKS = [
- 'Matthew', 'Mark', 'Luke', 'John', 'Acts',
- 'Romans', '1 Corinthians', '2 Corinthians', 'Galatians', 'Ephesians',
- 'Philippians', 'Colossians', '1 Thessalonians', '2 Thessalonians',
- '1 Timothy', '2 Timothy', 'Titus', 'Philemon',
- 'Hebrews', 'James', '1 Peter', '2 Peter',
- '1 John', '2 John', '3 John', 'Jude', 'Revelation'
-]
-# Comprehensive book name abbreviations and variations
-BOOK_ABBREVIATIONS = {
- # Psalm/Psalms
- "Psalm": "Psalms",
+@lru_cache(maxsize=1)
+def _load_bible_metadata() -> dict:
+ """
+ Load bible metadata from JSON file.
+ Cached since this data never changes and is accessed frequently.
+ """
+ with open(_METADATA_PATH, "r", encoding="utf-8") as f:
+ return json.load(f)
- # Roman numerals to Arabic numerals
- "I Samuel": "1 Samuel",
- "II Samuel": "2 Samuel",
- "I Kings": "1 Kings",
- "II Kings": "2 Kings",
- "I Chronicles": "1 Chronicles",
- "II Chronicles": "2 Chronicles",
- "I Corinthians": "1 Corinthians",
- "II Corinthians": "2 Corinthians",
- "I Thessalonians": "1 Thessalonians",
- "II Thessalonians": "2 Thessalonians",
- "I Timothy": "1 Timothy",
- "II Timothy": "2 Timothy",
- "I Peter": "1 Peter",
- "II Peter": "2 Peter",
- "I John": "1 John",
- "II John": "2 John",
- "III John": "3 John",
- # Full word numbers to Arabic numerals
- "First Samuel": "1 Samuel",
- "Second Samuel": "2 Samuel",
- "First Kings": "1 Kings",
- "Second Kings": "2 Kings",
- "First Chronicles": "1 Chronicles",
- "Second Chronicles": "2 Chronicles",
- "First Corinthians": "1 Corinthians",
- "Second Corinthians": "2 Corinthians",
- "First Thessalonians": "1 Thessalonians",
- "Second Thessalonians": "2 Thessalonians",
- "First Timothy": "1 Timothy",
- "Second Timothy": "2 Timothy",
- "First Peter": "1 Peter",
- "Second Peter": "2 Peter",
- "First John": "1 John",
- "Second John": "2 John",
- "Third John": "3 John",
-
- # Alternative names (map to the name used in verses-1769.json)
- "Song of Solomon": "Solomon's Song",
- "Song of Songs": "Solomon's Song",
- "Canticles": "Solomon's Song",
-
- # Common abbreviations - Old Testament
- "Gen": "Genesis",
- "Ge": "Genesis",
- "Exo": "Exodus",
- "Ex": "Exodus",
- "Lev": "Leviticus",
- "Le": "Leviticus",
- "Num": "Numbers",
- "Nu": "Numbers",
- "Deut": "Deuteronomy",
- "Dt": "Deuteronomy",
- "Josh": "Joshua",
- "Jos": "Joshua",
- "Judg": "Judges",
- "Jdg": "Judges",
- "Ru": "Ruth",
- "1Sam": "1 Samuel",
- "1 Sam": "1 Samuel",
- "1S": "1 Samuel",
- "2Sam": "2 Samuel",
- "2 Sam": "2 Samuel",
- "2S": "2 Samuel",
- "1Ki": "1 Kings",
- "1 Ki": "1 Kings",
- "1K": "1 Kings",
- "2Ki": "2 Kings",
- "2 Ki": "2 Kings",
- "2K": "2 Kings",
- "1Chr": "1 Chronicles",
- "1 Chr": "1 Chronicles",
- "1Ch": "1 Chronicles",
- "2Chr": "2 Chronicles",
- "2 Chr": "2 Chronicles",
- "2Ch": "2 Chronicles",
- "Ezr": "Ezra",
- "Neh": "Nehemiah",
- "Ne": "Nehemiah",
- "Est": "Esther",
- "Ps": "Psalms",
- "Psa": "Psalms",
- "Prov": "Proverbs",
- "Pr": "Proverbs",
- "Eccl": "Ecclesiastes",
- "Ec": "Ecclesiastes",
- "Song": "Song of Solomon",
- "Sos": "Song of Solomon",
- "SS": "Song of Solomon",
- "Isa": "Isaiah",
- "Is": "Isaiah",
- "Jer": "Jeremiah",
- "Je": "Jeremiah",
- "Lam": "Lamentations",
- "La": "Lamentations",
- "Ezek": "Ezekiel",
- "Eze": "Ezekiel",
- "Ezk": "Ezekiel",
- "Dan": "Daniel",
- "Da": "Daniel",
- "Hos": "Hosea",
- "Ho": "Hosea",
- "Joe": "Joel",
- "Jl": "Joel",
- "Am": "Amos",
- "Ob": "Obadiah",
- "Jon": "Jonah",
- "Mic": "Micah",
- "Mi": "Micah",
- "Nah": "Nahum",
- "Na": "Nahum",
- "Hab": "Habakkuk",
- "Hb": "Habakkuk",
- "Zep": "Zephaniah",
- "Zph": "Zephaniah",
- "Hag": "Haggai",
- "Hg": "Haggai",
- "Zech": "Zechariah",
- "Zec": "Zechariah",
- "Zch": "Zechariah",
- "Mal": "Malachi",
-
- # Common abbreviations - New Testament
- "Mat": "Matthew",
- "Mt": "Matthew",
- "Mar": "Mark",
- "Mk": "Mark",
- "Mrk": "Mark",
- "Luk": "Luke",
- "Lk": "Luke",
- "Joh": "John",
- "Jn": "John",
- "Act": "Acts",
- "Ac": "Acts",
- "Rom": "Romans",
- "Ro": "Romans",
- "1Cor": "1 Corinthians",
- "1 Cor": "1 Corinthians",
- "1Co": "1 Corinthians",
- "2Cor": "2 Corinthians",
- "2 Cor": "2 Corinthians",
- "2Co": "2 Corinthians",
- "Gal": "Galatians",
- "Ga": "Galatians",
- "Eph": "Ephesians",
- "Ep": "Ephesians",
- "Phil": "Philippians",
- "Php": "Philippians",
- "Ph": "Philippians",
- "Col": "Colossians",
- "Co": "Colossians",
- "1Thess": "1 Thessalonians",
- "1 Thess": "1 Thessalonians",
- "1Th": "1 Thessalonians",
- "2Thess": "2 Thessalonians",
- "2 Thess": "2 Thessalonians",
- "2Th": "2 Thessalonians",
- "1Tim": "1 Timothy",
- "1 Tim": "1 Timothy",
- "1Ti": "1 Timothy",
- "2Tim": "2 Timothy",
- "2 Tim": "2 Timothy",
- "2Ti": "2 Timothy",
- "Tit": "Titus",
- "Ti": "Titus",
- "Phm": "Philemon",
- "Pm": "Philemon",
- "Heb": "Hebrews",
- "He": "Hebrews",
- "Jam": "James",
- "Jas": "James",
- "Jm": "James",
- "1Pet": "1 Peter",
- "1 Pet": "1 Peter",
- "1Pe": "1 Peter",
- "1P": "1 Peter",
- "2Pet": "2 Peter",
- "2 Pet": "2 Peter",
- "2Pe": "2 Peter",
- "2P": "2 Peter",
- "1Joh": "1 John",
- "1 Joh": "1 John",
- "1Jn": "1 John",
- "2Joh": "2 John",
- "2 Joh": "2 John",
- "2Jn": "2 John",
- "3Joh": "3 John",
- "3 Joh": "3 John",
- "3Jn": "3 John",
- "Jud": "Jude",
- "Rev": "Revelation",
- "Re": "Revelation",
-}
+# Load data from JSON
+_metadata = _load_bible_metadata()
+OT_BOOKS = _metadata["old_testament_books"]
+NT_BOOKS = _metadata["new_testament_books"]
+BOOK_ABBREVIATIONS = _metadata["book_abbreviations"]
def normalize_book_name(book: str) -> Optional[str]:
diff --git a/kjvstudy_org/utils/helpers.py b/kjvstudy_org/utils/helpers.py
index 061a06b..c46c125 100644
--- a/kjvstudy_org/utils/helpers.py
+++ b/kjvstudy_org/utils/helpers.py
@@ -1,13 +1,61 @@
"""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
+# 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:
@@ -158,50 +206,10 @@ def get_related_content(book: str, chapter: int = None, verse: int = None) -> Di
return related
-# Popular/well-known chapters with their scores (1-10 scale)
-POPULAR_CHAPTERS = {
- "John": {3: 10},
- "1 Corinthians": {13: 10},
- "Psalms": {23: 10, 91: 9, 1: 8, 139: 8},
- "Romans": {8: 9, 3: 8, 12: 8},
- "Matthew": {5: 9, 6: 8, 7: 8},
- "Ephesians": {2: 8, 6: 8},
- "Philippians": {4: 8},
- "Genesis": {1: 9, 3: 8, 22: 7},
- "Exodus": {20: 8, 14: 7},
- "Isaiah": {53: 9, 40: 8},
- "Jeremiah": {29: 7},
- "Proverbs": {31: 7, 3: 7},
- "Ecclesiastes": {3: 8},
- "1 Peter": {5: 7},
- "James": {1: 7},
- "Hebrews": {11: 8, 12: 7},
- "Revelation": {21: 8, 22: 7},
- "Luke": {2: 9, 15: 8},
- "2 Timothy": {3: 7},
- "Joshua": {1: 7},
- "Daniel": {3: 7, 6: 7},
- "1 John": {4: 8},
- "Galatians": {5: 7},
- "Colossians": {3: 7},
- "1 Thessalonians": {4: 7},
- "Mark": {16: 7},
- "Acts": {2: 8},
- "1 Samuel": {17: 7},
- "Job": {19: 7},
- "2 Corinthians": {5: 7},
- "1 Kings": {3: 6, 18: 6},
- "Malachi": {3: 6},
- "Joel": {2: 6},
- "Micah": {6: 6},
- "Habakkuk": {2: 6},
-}
-
-HIGH_READERSHIP_BOOKS = [
- "Matthew", "Mark", "Luke", "John", "Acts", "Romans",
- "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians",
- "Philippians", "Colossians", "Genesis", "Exodus", "Psalms", "Proverbs"
-]
+# 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)
@@ -228,41 +236,8 @@ def get_chapter_popularity_score(book: str, chapter: int) -> int:
return min(default_score, 6)
-# Chapter explanations for popular chapters
-CHAPTER_EXPLANATIONS = {
- "John": {
- 3: "Contains John 3:16 - 'For God so loved the world' - the most quoted verse in Christianity",
- 1: "The Word became flesh - Jesus as the eternal Logos and the calling of the first disciples",
- },
- "1 Corinthians": {
- 13: "The famous 'Love Chapter' - 'Love is patient, love is kind' - essential reading for weddings and Christian living",
- },
- "Psalms": {
- 23: "The beloved Shepherd Psalm - 'The Lord is my shepherd, I shall not want' - comfort in times of trouble",
- 91: "Psalm of protection - 'He who dwells in the shelter of the Most High' - promises of God's care",
- 1: "The blessed man - contrasts the righteous and wicked, foundation of wisdom literature",
- 139: "God's omniscience and omnipresence - 'You have searched me and known me' - intimate knowledge of God",
- },
- "Romans": {
- 8: "No condemnation in Christ - 'All things work together for good' - assurance of salvation",
- 3: "All have sinned - universal need for salvation and justification by faith",
- 12: "Living sacrifice - practical Christian living and spiritual gifts",
- },
- "Matthew": {
- 5: "The Beatitudes - 'Blessed are the poor in spirit' - foundation of Christian ethics",
- 6: "The Lord's Prayer and teachings on worry - 'Give us this day our daily bread'",
- 7: "Golden Rule and narrow gate - 'Do unto others as you would have them do unto you'",
- },
- "Genesis": {
- 1: "Creation account - 'In the beginning God created the heavens and the earth'",
- 3: "The Fall - Adam and Eve's disobedience and the first promise of redemption",
- 22: "Abraham's ultimate test - the near-sacrifice of Isaac, foreshadowing Christ",
- },
- "Isaiah": {
- 53: "The Suffering Servant - 'He was wounded for our transgressions' - prophecy of Christ's crucifixion",
- 40: "Comfort my people - 'Every valley shall be exalted' - hope and restoration",
- },
-}
+# Load chapter explanations from JSON
+CHAPTER_EXPLANATIONS = _load_chapter_explanations()
def get_chapter_popularity_explanation(book: str, chapter: int) -> str:
@@ -293,40 +268,8 @@ def get_chapter_popularity_explanation(book: str, chapter: int) -> str:
return f"Part of {book} - explore this chapter to discover its significance"
-# Featured verses for verse of the day
-FEATURED_VERSES = [
- ("John", 3, 16),
- ("Psalms", 23, 1),
- ("Proverbs", 3, 5),
- ("Philippians", 4, 13),
- ("Romans", 8, 28),
- ("Isaiah", 40, 31),
- ("Jeremiah", 29, 11),
- ("Joshua", 1, 9),
- ("Matthew", 11, 28),
- ("Psalms", 46, 10),
- ("Romans", 12, 2),
- ("2 Timothy", 1, 7),
- ("Proverbs", 22, 6),
- ("1 Corinthians", 13, 4),
- ("Galatians", 5, 22),
- ("Hebrews", 11, 1),
- ("James", 1, 2),
- ("1 Peter", 5, 7),
- ("Psalms", 119, 105),
- ("Matthew", 6, 33),
- ("John", 14, 6),
- ("Romans", 5, 8),
- ("Ephesians", 2, 8),
- ("Psalms", 27, 1),
- ("Isaiah", 41, 10),
- ("Matthew", 28, 19),
- ("John", 1, 1),
- ("Psalms", 51, 10),
- ("Proverbs", 18, 10),
- ("2 Corinthians", 5, 17),
- ("Colossians", 3, 23),
-]
+# Load featured verses from JSON
+FEATURED_VERSES = _load_featured_verses()
def get_daily_verse() -> Dict: