mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
d200030361
Move routes from monolithic server.py (2716 lines) to dedicated route modules (788 lines remaining): - routes/main.py: Homepage, books browser, resources page - routes/misc.py: Search, interlinear landing, random-verse, verse-of-the-day - routes/timeline.py: Biblical timeline page and PDF - routes/about.py: Stats, cross-references index, about page - routes/reading_plans.py: Reading plans index, detail, PDF - routes/topics.py: Topics index and detail - routes/strongs.py: Strong's concordance search and entries This reduces server.py by 71% and improves code organization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
226 lines
7.3 KiB
Python
226 lines
7.3 KiB
Python
"""Miscellaneous routes - search, interlinear, random verse, verse of the day."""
|
|
import hashlib
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
|
|
from fastapi import APIRouter, Query, Request
|
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
from ..kjv import bible
|
|
from ..utils.search import perform_full_text_search
|
|
|
|
router = APIRouter()
|
|
templates = None
|
|
|
|
# Will be set by init_search_family_tree()
|
|
_search_family_tree_fn = None
|
|
|
|
|
|
def init_templates(t: Jinja2Templates):
|
|
"""Initialize templates for misc routes."""
|
|
global templates
|
|
templates = t
|
|
|
|
|
|
def init_search_family_tree(fn):
|
|
"""Initialize the search_family_tree function from server.py."""
|
|
global _search_family_tree_fn
|
|
_search_family_tree_fn = fn
|
|
|
|
|
|
# =============================================================================
|
|
# Helper Functions
|
|
# =============================================================================
|
|
|
|
def get_daily_verse(date_str=None):
|
|
"""Get the verse of the day based on a specific date (or current date if not provided)"""
|
|
# Use date as seed for consistent daily verse
|
|
if date_str is None:
|
|
date_str = datetime.now().strftime("%Y-%m-%d")
|
|
seed = int(hashlib.md5(date_str.encode()).hexdigest(), 16) % 1000000
|
|
|
|
# Featured verses for rotation
|
|
featured_verses = [
|
|
("John", 3, 16),
|
|
("Jeremiah", 29, 11),
|
|
("Philippians", 4, 13),
|
|
("Romans", 8, 28),
|
|
("Proverbs", 3, 5),
|
|
("Isaiah", 41, 10),
|
|
("Matthew", 11, 28),
|
|
("1 John", 4, 19),
|
|
("Psalms", 23, 1),
|
|
("2 Corinthians", 5, 17),
|
|
("Ephesians", 2, 8),
|
|
("Romans", 10, 9),
|
|
("1 Peter", 5, 7),
|
|
("James", 1, 5),
|
|
("Philippians", 4, 19),
|
|
("Psalms", 119, 105),
|
|
("Matthew", 6, 33),
|
|
("Romans", 12, 2),
|
|
("1 Corinthians", 13, 13),
|
|
("Galatians", 5, 22),
|
|
("Hebrews", 11, 1),
|
|
("1 Thessalonians", 5, 18),
|
|
("Psalms", 46, 1),
|
|
("Isaiah", 40, 31),
|
|
("Matthew", 5, 16),
|
|
("Romans", 15, 13),
|
|
("Colossians", 3, 23),
|
|
("1 John", 1, 9),
|
|
("Psalms", 37, 4),
|
|
("Proverbs", 27, 17)
|
|
]
|
|
|
|
# Select verse based on seed
|
|
verse_index = seed % len(featured_verses)
|
|
book, chapter, verse = featured_verses[verse_index]
|
|
|
|
verse_text = bible.get_verse_text(book, chapter, verse)
|
|
if not verse_text:
|
|
# Fallback to John 3:16
|
|
book, chapter, verse = "John", 3, 16
|
|
verse_text = bible.get_verse_text(book, chapter, verse)
|
|
|
|
return {
|
|
"book": book,
|
|
"chapter": chapter,
|
|
"verse": verse,
|
|
"text": verse_text,
|
|
"reference": f"{book} {chapter}:{verse}",
|
|
"date": date_str
|
|
}
|
|
|
|
|
|
# =============================================================================
|
|
# Routes
|
|
# =============================================================================
|
|
|
|
@router.get("/search", response_class=HTMLResponse)
|
|
async def search_page(request: Request, q: str = Query(None, description="Search query")):
|
|
"""Search page with results (includes Bible verses and family tree)"""
|
|
books = bible.get_books()
|
|
search_results = []
|
|
family_tree_results = []
|
|
is_direct_verse = False
|
|
|
|
if q and len(q.strip()) >= 2:
|
|
# Search Bible verses
|
|
search_results = perform_full_text_search(q.strip())
|
|
# Check if this was a direct verse reference match
|
|
if search_results and len(search_results) == 1 and search_results[0].get("score") == 100.0:
|
|
is_direct_verse = True
|
|
|
|
# Also search family tree (limit to 5 results)
|
|
if _search_family_tree_fn:
|
|
family_tree_results = _search_family_tree_fn(q.strip(), limit=5)
|
|
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"search.html",
|
|
{
|
|
"query": q or "",
|
|
"results": search_results,
|
|
"family_tree_results": family_tree_results,
|
|
"books": books,
|
|
"total_results": len(search_results) + len(family_tree_results),
|
|
"is_direct_verse": is_direct_verse
|
|
}
|
|
)
|
|
|
|
|
|
@router.get("/interlinear", response_class=HTMLResponse)
|
|
async def interlinear_landing_page(request: Request):
|
|
"""Landing page explaining interlinear Bible study"""
|
|
books = bible.get_books()
|
|
|
|
# Featured verses with interlinear data
|
|
featured_verses = [
|
|
{"reference": "John 3:16", "url": "/book/John/chapter/3/verse/16", "note": "God's love for the world"},
|
|
{"reference": "Genesis 1:1", "url": "/book/Genesis/chapter/1/verse/1", "note": "In the beginning"},
|
|
{"reference": "Psalm 23:1", "url": "/book/Psalms/chapter/23/verse/1", "note": "The Lord is my shepherd"},
|
|
{"reference": "Romans 8:28", "url": "/book/Romans/chapter/8/verse/28", "note": "All things work together for good"},
|
|
{"reference": "Matthew 28:19", "url": "/book/Matthew/chapter/28/verse/19", "note": "The Great Commission"},
|
|
{"reference": "1 Corinthians 13:4", "url": "/book/1 Corinthians/chapter/13/verse/4", "note": "Love is patient"},
|
|
]
|
|
|
|
# Build breadcrumbs
|
|
breadcrumbs = [
|
|
{"text": "Home", "url": "/"},
|
|
{"text": "Interlinear", "url": None}
|
|
]
|
|
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"interlinear_landing.html",
|
|
{
|
|
"books": books,
|
|
"featured_verses": featured_verses,
|
|
"breadcrumbs": breadcrumbs
|
|
}
|
|
)
|
|
|
|
|
|
@router.get("/random-verse")
|
|
async def random_verse(request: Request):
|
|
"""Redirect to a random Bible verse"""
|
|
# Get all books
|
|
all_books = bible.get_books()
|
|
|
|
# Pick a random book
|
|
book = random.choice(all_books)
|
|
|
|
# Get all chapters for this book
|
|
chapters = bible.get_chapters_for_book(book)
|
|
|
|
# Pick a random chapter
|
|
chapter = random.choice(chapters)
|
|
|
|
# Get all verses for this chapter
|
|
verses = bible.get_verses_by_book_chapter(book, chapter)
|
|
|
|
# Pick a random verse
|
|
verse = random.choice(verses)
|
|
|
|
# Redirect to the verse page with cache control headers to ensure fresh random verse each time
|
|
response = RedirectResponse(url=f"/book/{book}/chapter/{chapter}/verse/{verse.verse}", status_code=302)
|
|
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
|
|
response.headers["Pragma"] = "no-cache"
|
|
response.headers["Expires"] = "0"
|
|
return response
|
|
|
|
|
|
@router.get("/verse-of-the-day", response_class=HTMLResponse)
|
|
async def verse_of_the_day_page(request: Request):
|
|
"""Verse of the day page"""
|
|
books = bible.get_books()
|
|
daily_verse = get_daily_verse()
|
|
|
|
# Generate past 30 days of verses
|
|
past_verses = []
|
|
today = datetime.now()
|
|
for i in range(1, 31): # Past 30 days (not including today)
|
|
past_date = today - timedelta(days=i)
|
|
date_str = past_date.strftime("%Y-%m-%d")
|
|
verse = get_daily_verse(date_str)
|
|
past_verses.append(verse)
|
|
|
|
# Build breadcrumbs
|
|
breadcrumbs = [
|
|
{"text": "Home", "url": "/"},
|
|
{"text": "Verse of the Day", "url": "/verse-of-the-day"}
|
|
]
|
|
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"verse_of_the_day.html",
|
|
{
|
|
"books": books,
|
|
"daily_verse": daily_verse,
|
|
"past_verses": past_verses,
|
|
"breadcrumbs": breadcrumbs
|
|
}
|
|
)
|