mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
Add direct verse lookup with formatted results
This commit is contained in:
+107
-4
@@ -14,12 +14,104 @@ from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
|
||||
from .kjv import bible
|
||||
from .kjv import bible, VerseReference
|
||||
|
||||
|
||||
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", "I Corinthians 13:4", etc.
|
||||
verse_pattern = r'^(I{1,3}|1|2|3)?\s*[A-Za-z]+(\s+[A-Za-z]+)?\s+\d+:\d+$'
|
||||
return bool(re.match(verse_pattern, query.strip()))
|
||||
|
||||
def parse_verse_reference(query: str) -> Optional[Dict]:
|
||||
"""Parse a verse reference string and return verse info if found"""
|
||||
try:
|
||||
# Clean up the query
|
||||
cleaned_query = query.strip()
|
||||
|
||||
# Handle common variations in book names
|
||||
# Replace common numeric prefixes
|
||||
cleaned_query = re.sub(r'^1\s+', 'I ', cleaned_query)
|
||||
cleaned_query = re.sub(r'^2\s+', 'II ', cleaned_query)
|
||||
cleaned_query = re.sub(r'^3\s+', 'III ', cleaned_query)
|
||||
|
||||
# Try to parse using the existing VerseReference.from_string method
|
||||
verse_ref = VerseReference.from_string(cleaned_query)
|
||||
|
||||
# Get the actual verse text
|
||||
verse_text = bible.get_verse_text(verse_ref.book, verse_ref.chapter, verse_ref.verse)
|
||||
|
||||
if verse_text:
|
||||
return {
|
||||
"book": verse_ref.book,
|
||||
"chapter": verse_ref.chapter,
|
||||
"verse": verse_ref.verse,
|
||||
"text": verse_text,
|
||||
"reference": f"{verse_ref.book} {verse_ref.chapter}:{verse_ref.verse}",
|
||||
"url": f"/book/{verse_ref.book}/chapter/{verse_ref.chapter}#verse-{verse_ref.verse}",
|
||||
"score": 100.0, # High score for exact verse matches
|
||||
"highlighted_text": verse_text
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Error parsing verse reference '{query}': {e}")
|
||||
|
||||
# Try alternative book name formats if the first attempt failed
|
||||
try:
|
||||
# Try with different book name mappings
|
||||
book_mappings = {
|
||||
'1 john': 'I John',
|
||||
'2 john': 'II John',
|
||||
'3 john': 'III John',
|
||||
'1 corinthians': 'I Corinthians',
|
||||
'2 corinthians': 'II Corinthians',
|
||||
'1 thessalonians': 'I Thessalonians',
|
||||
'2 thessalonians': 'II Thessalonians',
|
||||
'1 timothy': 'I Timothy',
|
||||
'2 timothy': 'II Timothy',
|
||||
'1 peter': 'I Peter',
|
||||
'2 peter': 'II Peter',
|
||||
'1 kings': 'I Kings',
|
||||
'2 kings': 'II Kings',
|
||||
'1 chronicles': 'I Chronicles',
|
||||
'2 chronicles': 'II Chronicles',
|
||||
'1 samuel': 'I Samuel',
|
||||
'2 samuel': 'II Samuel'
|
||||
}
|
||||
|
||||
query_lower = cleaned_query.lower()
|
||||
for old_name, new_name in book_mappings.items():
|
||||
if query_lower.startswith(old_name):
|
||||
alternative_query = query_lower.replace(old_name, new_name, 1)
|
||||
verse_ref = VerseReference.from_string(alternative_query)
|
||||
verse_text = bible.get_verse_text(verse_ref.book, verse_ref.chapter, verse_ref.verse)
|
||||
|
||||
if verse_text:
|
||||
return {
|
||||
"book": verse_ref.book,
|
||||
"chapter": verse_ref.chapter,
|
||||
"verse": verse_ref.verse,
|
||||
"text": verse_text,
|
||||
"reference": f"{verse_ref.book} {verse_ref.chapter}:{verse_ref.verse}",
|
||||
"url": f"/book/{verse_ref.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
|
||||
|
||||
def perform_full_text_search(query: str, limit: int = 50) -> List[Dict]:
|
||||
"""Perform full text search across all Bible verses"""
|
||||
"""Perform full text search across all Bible verses or find specific verse references"""
|
||||
results = []
|
||||
|
||||
# First, check if this looks like a verse reference
|
||||
if is_verse_reference(query):
|
||||
verse_result = parse_verse_reference(query)
|
||||
if verse_result:
|
||||
return [verse_result]
|
||||
|
||||
# If not a verse reference or verse not found, perform regular text search
|
||||
search_terms = query.lower().split()
|
||||
|
||||
# Search through all verses using the iter_verses method
|
||||
@@ -124,9 +216,13 @@ def search_page(request: Request, q: str = Query(None, description="Search query
|
||||
"""Search page with results"""
|
||||
books = list(bible.iter_books())
|
||||
search_results = []
|
||||
is_direct_verse = False
|
||||
|
||||
if q and len(q.strip()) >= 2:
|
||||
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
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"search.html",
|
||||
@@ -135,7 +231,8 @@ def search_page(request: Request, q: str = Query(None, description="Search query
|
||||
"query": q or "",
|
||||
"results": search_results,
|
||||
"books": books,
|
||||
"total_results": len(search_results)
|
||||
"total_results": len(search_results),
|
||||
"is_direct_verse": is_direct_verse
|
||||
}
|
||||
)
|
||||
|
||||
@@ -146,11 +243,17 @@ def search_api(q: str = Query(..., description="Search query"), limit: int = Que
|
||||
return {"query": q, "results": [], "total": 0}
|
||||
|
||||
search_results = perform_full_text_search(q.strip(), limit)
|
||||
is_direct_verse = False
|
||||
|
||||
# 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
|
||||
|
||||
return {
|
||||
"query": q,
|
||||
"results": search_results,
|
||||
"total": len(search_results)
|
||||
"total": len(search_results),
|
||||
"is_direct_verse": is_direct_verse
|
||||
}
|
||||
|
||||
@app.get("/study-guides", response_class=HTMLResponse)
|
||||
|
||||
@@ -255,6 +255,16 @@
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.direct-verse-result {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
background: linear-gradient(135deg, rgba(75, 46, 131, 0.03), rgba(65, 105, 225, 0.03));
|
||||
}
|
||||
|
||||
.direct-verse-result .result-reference {
|
||||
color: var(--primary-color);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.search-container {
|
||||
padding: 1rem 0.5rem;
|
||||
@@ -318,17 +328,40 @@
|
||||
|
||||
{% if query %}
|
||||
{% if total_results > 0 %}
|
||||
{% if is_direct_verse %}
|
||||
<div class="search-stats">
|
||||
<span style="color: var(--primary-color); font-weight: 600;">📖 Found exact verse: </span><strong>{{ query }}</strong>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="search-stats">
|
||||
Found <strong>{{ total_results }}</strong> result{{ 's' if total_results != 1 else '' }} for "<strong>{{ query }}</strong>"
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="search-results">
|
||||
{% for result in results %}
|
||||
<div class="search-result">
|
||||
<div class="search-result{% if is_direct_verse %} direct-verse-result{% endif %}">
|
||||
<a href="{{ result.url }}" class="result-reference">
|
||||
{{ result.reference }}
|
||||
{% if is_direct_verse %}📖 {% endif %}{{ result.reference }}
|
||||
</a>
|
||||
<p class="result-text">{{ result.highlighted_text|safe }}</p>
|
||||
{% if is_direct_verse %}
|
||||
<div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border-light);">
|
||||
<a href="{{ result.url }}" style="
|
||||
display: inline-block;
|
||||
background: var(--primary-light);
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
text-decoration: none;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.9rem;
|
||||
transition: background-color 0.2s ease;
|
||||
" onmouseover="this.style.backgroundColor='var(--primary-color)'"
|
||||
onmouseout="this.style.backgroundColor='var(--primary-light)'">
|
||||
Read {{ result.reference }} in Context →
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -348,6 +381,7 @@
|
||||
<h3>Search Tips</h3>
|
||||
<ul>
|
||||
<li>Search for words or phrases that appear in Bible verses</li>
|
||||
<li>Enter specific verse references like "John 3:16" or "1 John 4:8"</li>
|
||||
<li>Use multiple words to find verses containing all terms</li>
|
||||
<li>Try different word forms (e.g., "love" vs "loveth")</li>
|
||||
<li>Search for names, places, or key themes</li>
|
||||
|
||||
Reference in New Issue
Block a user