diff --git a/app.py b/app.py index af917e5..111b837 100644 --- a/app.py +++ b/app.py @@ -97,6 +97,26 @@ if not kjv_cur: if not sermon_cur: print("WARN: Error connecting to Sermon Database") +# Build grammatical variants lookup from word groups +variant_groups = json.load(open("data/grammatical_variants.json", "r")) +grammatical_variants_map = {} +for group in variant_groups: + for word in group: + # Map each word to all other words in its group + grammatical_variants_map[word] = [w for w in group if w != word] + +# Build theological synonyms lookup from word groups +theological_synonym_groups = json.load(open("data/theological_synonyms.json", "r")) +theological_synonyms_map = {} +for group in theological_synonym_groups: + for word in group: + # Map each word to other words in its group + theological_synonyms_map[word] = [w for w in group if w != word] + +# Load concept mappings for common theological terms not in KJV +concept_mappings = json.load(open("data/concept_mappings.json", "r")) +concept_mappings_dict = {mapping["query"].lower(): mapping for mapping in concept_mappings} + cached: dict = { "verse_ids": [i[0] for i in kjv_cur.execute("SELECT id FROM fts_kjv;").fetchall()], "book_names": [ @@ -105,13 +125,21 @@ cached: dict = { "chapter_list": [], "books_chapter_lengths": {}, "json_dictionary": json.load(open("data/1828_Webster_KJV.json", "r")), + "grammatical_variants": grammatical_variants_map, + "theological_synonyms": theological_synonyms_map, + "concept_mappings": concept_mappings_dict, "html_response": open("html/response.html", "r").read(), "html_navbar": open("html/navbar.html", "r").read(), "css_navbar": open("styles/header.css", "r").read(), "css_response": open("styles/human_readable.css", "r").read(), "css_collapsible": open("styles/collapsible.css", "r").read(), + "css_page": open("styles/page.css", "r").read(), "js_collapsible": open("js/collapsible.js", "r").read(), "js_shortcode": open("js/shortcode.js", "r").read(), + "favicon_svg": open("static/favicon.svg", "r").read(), + "book_info": kjv_cur.execute( + 'SELECT "order", title_short, category, otnt, chapters FROM book_info ORDER BY "order"' + ).fetchall(), } # Fill the chapter list with unique chapters for seeking back and forward by index. @@ -126,17 +154,52 @@ for book in range(1, 67): "SELECT c FROM fts_kjv where b = ? ORDER BY id DESC LIMIT 1;", (book,) ).fetchone()[0] +# Cache book_id to bookname mapping for fast lookups +cached["book_id_to_name"] = {} +for row in kjv_cur.execute("SELECT b, n FROM key_english").fetchall(): + cached["book_id_to_name"][row[0]] = row[1] + +# Organize books by testament and category for index page +from collections import defaultdict +testament_data = {"OT": defaultdict(list), "NT": defaultdict(list)} +for book in cached["book_info"]: + book_order, title, category, testament, num_chapters = book + testament_data[testament][category].append({ + "title": title, + "chapters": num_chapters + }) +cached["book_info_for_index"] = dict(testament_data) + +# Render the index page once and cache it +with app.app_context(): + from flask import render_template_string + template_content = open("html/index.html", "r").read() + cached["html_index"] = render_template_string(template_content, testament_data=cached["book_info_for_index"]) + @app.get("/js/shortcode.js") def serve_shortcode_js(): - return Response(cached.get("js_shortcode"), mimetype='application/javascript') + response = Response(cached.get("js_shortcode"), mimetype='application/javascript') + response.headers['Cache-Control'] = 'public, max-age=31536000' + return response @app.get("/js/collapsible.js") def serve_collapsible_js(): - return Response(cached.get("js_collapsible"), mimetype='application/javascript') + response = Response(cached.get("js_collapsible"), mimetype='application/javascript') + response.headers['Cache-Control'] = 'public, max-age=31536000' + return response @app.get("/styles/page.css") def serve_page_css(): - return send_file("styles/page.css", mimetype='text/css') + response = Response(cached.get("css_page"), mimetype='text/css') + response.headers['Cache-Control'] = 'public, max-age=31536000' + return response + +@app.get("/favicon.ico") +@app.get("/favicon.svg") +def serve_favicon(): + response = Response(cached.get("favicon_svg"), mimetype='image/svg+xml') + response.headers['Cache-Control'] = 'public, max-age=31536000' + return response def generate_short_id(): remain = random.randrange(614_656, 17_210_367) # resolves to length of 5 @@ -151,11 +214,10 @@ def generate_short_id(): def lookup_bookname(book_id): - result = kjv_cur.execute( - "SELECT n FROM key_english WHERE b = ?;",(book_id,)).fetchone() - if result: - result = result[0] - return result + # Ensure book_id is an integer for cache lookup + if isinstance(book_id, str): + book_id = int(book_id) + return cached["book_id_to_name"].get(book_id) def lookup_book_id(bookname) -> int: @@ -452,34 +514,55 @@ def parse_query(query_override: str = None): return send_file("html/find.html") if query.endswith(","): query = query[:-1] - result = show(kw=query, human_readable=human_readable) - if not hasattr(result, "__len__"): - logging.debug(f"parse_query.show = '{result}'") - return result - if len(result) > 17: - return result - result = search() + # Check if query contains both letters and digits (required for reference parsing) + # If only numbers or only letters, skip reference parsing and go to keyword search + has_letters = any(char.isalpha() for char in query) + has_digits = any(char.isdigit() for char in query) + + if not (has_letters and has_digits): + result = search() + else: + result = show(kw=query, human_readable=human_readable) + if not hasattr(result, "__len__"): + logging.debug(f"parse_query.show = '{result}'") + return result + + if len(result) > 17: + return result + # Reference parsing failed, fall back to keyword search + result = search() # Remove the closing from search() so we can add dictionary content inside .responses if result.endswith(""): result = result[:-6] # Remove last - result += "" - result += "
Click on the word to get the definition.
Click on the word to get the definition.
We couldn't find any verses matching your search. Here are some spelling suggestions:
" + for suggestion in suggestions[:5]: # Limit to 5 suggestions + search_link = f"/search?kw={suggestion}&view=rich" + no_results_html += f"" + no_results_html += "In the KJV, these terms often refer to similar theological concepts. Consider also searching for related verses.
" + for suggestion in theological_suggestions: + search_link = f"/search?kw={suggestion}&view=rich" + suggestions_html += f"