diff --git a/.gitignore b/.gitignore index d39c5ca..c27414f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ logs/*.log *__pycache__* +data/shortcodes.db +data/seq.db +data/sermons.db diff --git a/app.py b/app.py index c43454c..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,13 +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(): + 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 @@ -147,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: @@ -448,37 +514,56 @@ 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 = "" - result += search() - result += "" - result += ( - "

1828 Webster Definitions

Click on the word to get the definition." - ) + # 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 + + # Check if we have any dictionary definitions before showing the section split_query = query.replace(",", " ").split(" ") if len(split_query) > 20: return "Your Query is too long. 20 Words or less." + + definitions_html = "" + has_definitions = False for word in split_query: definition = define(word) if not definition: continue - result += ( + has_definitions = True + definitions_html += ( "" ) - result += "
" + definition + "
" + definitions_html += "
" + definition + "
" logging.debug(f"parse_query.define = '{word}'") - result += "" - result += "
" - # result += search() - #logging.debug(f"parse_query.search = '{result}'") + + # Only add the dictionary section if we found at least one definition + if has_definitions: + result += "" + result += "

1828 Webster Definitions

Click on the word to get the definition.

" + result += definitions_html + result += "" + + result += "" return result @@ -604,7 +689,7 @@ def show(kw=None, get_start_verse_id=False, human_readable=False): if arg_view == "plain": response = "
".join(response_list) elif arg_view == "rich" or human_readable: - response = "
".join(response_list) + response = "".join(response_list) else: response = jsonify(response_list) return response @@ -643,7 +728,7 @@ def response_rich(scripture): def scripture_response(scriptures, human_readable=False): response = [] - arg_view = request.args.get("view", "json") + arg_view = request.args.get("view", "rich") logging.debug(f"search ->requested_format = {arg_view}") if arg_view == "plain": for scripture in scriptures: @@ -670,8 +755,8 @@ def scripture_response(scriptures, human_readable=False): ) response.append(cached["css_navbar"]) - response.append("
") + response.append(f"
{len(scriptures)} Results
") for scripture in scriptures: response.append(response_rich(scripture)) response.append("
") @@ -698,7 +783,7 @@ def idx(shortcode=""): logging.info(f"/{shortcode}") return resolve_shortcode(shortcode) logging.info(f"/{shortcode}") - return send_file("html/index.html") + return cached["html_index"] @app.get("/fe") def frontend(): @@ -720,7 +805,7 @@ def random_verse(): if arg_view == "plain": response = "
".join(response_list) elif arg_view == "rich": - response = "
".join(response_list) + response = "".join(response_list) else: response = jsonify(response_list) #logging.debug(f"Search Response: {response}") @@ -741,7 +826,7 @@ def verse_of_the_day(): if arg_view == "plain": response = "
".join(response_list) elif arg_view == "rich": - response = "
".join(response_list) + response = "".join(response_list) else: response = jsonify(response_list) #logging.debug(f"Search Response: {response}") @@ -769,19 +854,287 @@ def search(): return send_file("html/search.html") else: return send_file("html/search.html") + + # Check if this is a common theological concept not in KJV + concept_mapping = cached["concept_mappings"].get(cleaned_keywords.lower(), None) + concept_notice = None + if concept_mapping: + # Build hyperlinks for all search terms + original_query = cleaned_keywords + search_links = [] + for term in concept_mapping["search_terms"]: + search_link = f"/search?kw={term.replace(' ', '+')}&view=rich" + search_links.append(f"{term}") + + links_html = ", ".join(search_links) + concept_notice = f"
{original_query.capitalize()}: {concept_mapping['explanation']}
You may find what you are looking for with these search terms: {links_html}
" + + # Use the first search term for the actual search + cleaned_keywords = concept_mapping["search_terms"][0] + + def score_result_by_proximity(verse_text, query_words): + """ + Score a verse based on how close together the query words appear. + Higher score = words appear closer together. + """ + verse_lower = verse_text.lower() + + # Find all positions of each query word + word_positions = {} + for word in query_words: + word_lower = word.lower() + positions = [] + start = 0 + while True: + pos = verse_lower.find(word_lower, start) + if pos == -1: + break + positions.append(pos) + start = pos + 1 + word_positions[word] = positions + + # If any word is missing, score is 0 + if any(len(positions) == 0 for positions in word_positions.values()): + return 0 + + # Calculate minimum distance between all query words + # For each combination of positions, find the span (max_pos - min_pos) + # Smaller span = higher score + import itertools + + # Get all combinations of positions for all words + all_position_combos = list(itertools.product(*word_positions.values())) + + if not all_position_combos: + return 0 + + # Find the combination with minimum span + min_span = float('inf') + for combo in all_position_combos: + span = max(combo) - min(combo) + if span < min_span: + min_span = span + + # Convert span to score (smaller span = higher score) + # Use inverse: 1000 / (span + 1) so adjacent words score ~1000, far apart score low + score = 1000.0 / (min_span + 1) + + return score + results = lookup_fts(cleaned_keywords) + + # Sort results by proximity score if we have multiple query words + if results and len(cleaned_keywords.split()) > 1: + query_words = cleaned_keywords.split() + scored_results = [] + for result in results: + verse_text = result[4] # The text is at index 4 + score = score_result_by_proximity(verse_text, query_words) + scored_results.append((score, result)) + + # Sort by score descending, then keep just the results + scored_results.sort(key=lambda x: x[0], reverse=True) + results = [result for score, result in scored_results] + if not results: + # PRIORITY 1: Try grammatical variant expansion first + words = cleaned_keywords.split() + + def sort_variants_by_similarity(original, variants): + """Sort variants by morphological similarity: forwards (longer/more specific) then backwards (shorter/simpler)""" + def get_common_prefix_length(w1, w2): + """Find how many characters match from the start""" + prefix_len = 0 + for i in range(min(len(w1), len(w2))): + if w1[i].lower() == w2[i].lower(): + prefix_len += 1 + else: + break + return prefix_len + + scored_variants = [] + for variant in variants: + prefix_len = get_common_prefix_length(original, variant) + + # Reject variants that don't share the stem (beginning doesn't match) + if prefix_len < min(2, len(original) // 2): + continue + + # Score: prioritize common prefix length (stem preservation) heavily, + # then prefer longer variants (forward/more specific) over shorter (backward/simpler) + # Prefix is weighted 1000x to ensure stem-preservation is primary + score = (prefix_len * 1000) + len(variant) + scored_variants.append((score, variant)) + + # Sort by score descending: highest score = most similar with longest form first + return [v for _, v in sorted(scored_variants, key=lambda x: x[0], reverse=True)] + + for word in words: + variants = cached["grammatical_variants"].get(word.lower(), []) + if variants: + # Sort variants: forwards in likeness (longer/specific) then backwards (shorter/simple) + sorted_variants = sort_variants_by_similarity(word.lower(), variants) + + # Try each variant in order of similarity + for variant in sorted_variants: + variant_query = cleaned_keywords.replace(word, variant, 1) + results = lookup_fts(variant_query) + if results: + response_list = scripture_response(results) + arg_view = request.args.get("view", None) + if arg_view == "plain": + response = f"Showing results for '{variant_query}' (grammatical variant of '{cleaned_keywords}')\n
" + "
".join(response_list) + elif arg_view == "rich": + variant_notice = f"
Showing results for '{variant_query}' (grammatical variant of '{cleaned_keywords}')
" + response_str = "".join(response_list) + response = response_str.replace("
", f"
{variant_notice}", 1) + else: + response = jsonify({"original": cleaned_keywords, "variant": variant_query, "results": response_list}) + return response + + # PRIORITY 2: If grammatical variants didn't help, try spell-check h = hunspell.HunSpell("data/kjv.dic", "data/kjv.aff") + corrected_words = [] + had_corrections = False + + for word in words: + if h.spell(word): + # Word is spelled correctly + corrected_words.append(word) + else: + # Word is misspelled, get suggestions + word_suggestions = h.suggest(word) + if word_suggestions: + corrected_words.append(word_suggestions[0]) + had_corrections = True + else: + # No suggestions, keep original + corrected_words.append(word) + + if had_corrections: + corrected_keyword = " ".join(corrected_words) + results = lookup_fts(corrected_keyword) + if results: + response_list = scripture_response(results) + arg_view = request.args.get("view", None) + if arg_view == "plain": + response = f"Showing results for '{corrected_keyword}' (corrected from '{cleaned_keywords}')\n
" + "
".join(response_list) + elif arg_view == "rich": + correction_notice = f"
Showing results for '{corrected_keyword}' (corrected from '{cleaned_keywords}')
" + response_str = "".join(response_list) + response = response_str.replace("
", f"
{correction_notice}", 1) + else: + response = jsonify({"corrected_from": cleaned_keywords, "corrected_to": corrected_keyword, "results": response_list}) + return response + + # PRIORITY 3: Try theological synonym expansion + for word in words: + synonyms = cached["theological_synonyms"].get(word.lower(), None) + if synonyms: + for synonym in synonyms: + synonym_query = cleaned_keywords.replace(word, synonym, 1) + results = lookup_fts(synonym_query) + if results: + response_list = scripture_response(results) + arg_view = request.args.get("view", None) + explanation = f"In the KJV, '{word}' and '{synonym}' often refer to similar theological concepts. Consider also searching for related verses." + if arg_view == "plain": + response = f"Showing results for '{synonym_query}' (theological equivalent of '{cleaned_keywords}')\n{explanation}\n
" + "
".join(response_list) + elif arg_view == "rich": + theological_notice = f"
Showing results for '{synonym_query}' (theological equivalent of '{cleaned_keywords}')
{explanation}
" + response_str = "".join(response_list) + response = response_str.replace("
", f"
{theological_notice}", 1) + else: + response = jsonify({"original": cleaned_keywords, "theological_equivalent": synonym_query, "explanation": explanation, "results": response_list}) + return response + + # PRIORITY 4: If we still have no results, show spelling suggestions suggestions = h.suggest(cleaned_keywords) - return "{} not found, try one of the following: {}".format( - cleaned_keywords, ", ".join(suggestions) - ) + arg_view = request.args.get("view", "rich") + + if arg_view == "rich": + # Build a nice no-results page with header + no_results_html = cached["html_navbar"].format( + query=cleaned_keywords, + current_chapter_name="", + next_chapter_name="", + next_chapter_link="", + prev_chapter_name="", + prev_chapter_link="" + ) + no_results_html += cached["css_navbar"] + no_results_html += "
" + no_results_html += f"
No results found for '{cleaned_keywords}'.
" + + if suggestions: + no_results_html += "

Did you mean?

" + no_results_html += "

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 += "
" + else: + no_results_html += "

Search Tips

" + no_results_html += "

We couldn't find any verses matching your search. Try:

" + no_results_html += "
    " + no_results_html += "
  • Checking your spelling
  • " + no_results_html += "
  • Using different keywords
  • " + no_results_html += "
  • Searching for love, faith, or hope
  • " + no_results_html += "
" + + no_results_html += "
" + return no_results_html + else: + # Plain text fallback + return "{} not found, try one of the following: {}".format( + cleaned_keywords, ", ".join(suggestions) if suggestions else "No suggestions available" + ) response_list = scripture_response(results) - arg_view = request.args.get("view", None) + + # Check if any words in the search have theological synonyms to suggest + theological_suggestions = [] + words = cleaned_keywords.split() + for word in words: + synonyms = cached["theological_synonyms"].get(word.lower(), None) + if synonyms: + for synonym in synonyms: + theological_suggestions.append(synonym) + + arg_view = request.args.get("view", "rich") if arg_view == "plain": response = "
".join(response_list) + if concept_notice: + response = concept_notice + "
" + response + if theological_suggestions: + response += "

Also consider searching: " + for suggestion in theological_suggestions: + response += f"{suggestion}
" elif arg_view == "rich": - response = "
".join(response_list) + response = "".join(response_list) + # Insert concept notice at the top if it exists + if concept_notice: + # Find the first
after the opening
and insert after it + responses_start = response.find("
") + if responses_start != -1: + # Find the results-count div closing + insert_pos = response.find("
", responses_start + len("
")) + if insert_pos != -1: + insert_pos += len("
") + response = response[:insert_pos] + concept_notice + response[insert_pos:] + if theological_suggestions: + # Add suggestions before the LAST closing
which closes the responses container + suggestions_html = "

Related Theological Concepts

" + suggestions_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"
" + suggestions_html += f"{suggestion.capitalize()}" + suggestions_html += "
" + suggestions_html += "
" + # Find the last
and insert before it + last_div_pos = response.rfind("
") + if last_div_pos != -1: + response = response[:last_div_pos] + suggestions_html + response[last_div_pos:] else: response = jsonify(response_list) #logging.debug(f"Search Response: {response}") @@ -935,8 +1288,10 @@ def sequential_read(start=0, num=1, uid=None, getuid=False): # only look at a small set of the oldest unused entries. seq_conn.commit() arg_view = request.args.get("view", None) - if arg_view == "plain" or arg_view == "rich": + if arg_view == "plain": response = "
".join(response_list) + elif arg_view == "rich": + response = "".join(response_list) else: response = jsonify(response_list) diff --git a/data/concept_mappings.json b/data/concept_mappings.json new file mode 100644 index 0000000..8001fa3 --- /dev/null +++ b/data/concept_mappings.json @@ -0,0 +1,142 @@ +[ + { + "query": "rapture", + "explanation": "The word 'rapture' does not appear in the KJV, but is usually a reference to believers being caught up to meet Christ in the air.", + "search_terms": ["caught up", "meet the lord in the air"] + }, + { + "query": "trinity", + "explanation": "The word 'trinity' does not appear in the KJV, but is usually a reference to the doctrine of the triune God (Father, Son, and Holy Spirit).", + "search_terms": ["father son holy ghost", "three that bear record"] + }, + { + "query": "missionary", + "explanation": "The word 'missionary' does not appear in the KJV, but is usually a reference to being sent to preach the gospel.", + "search_terms": ["go ye therefore", "preach the gospel", "sent"] + }, + { + "query": "bible", + "explanation": "The word 'bible' does not appear in the KJV, but is usually a reference to the scriptures and God's word.", + "search_terms": ["scripture", "word of god", "holy scriptures"] + }, + { + "query": "inerrancy", + "explanation": "The word 'inerrancy' does not appear in the KJV, but is usually a reference to the perfection and preservation of God's word.", + "search_terms": ["word of god", "scripture", "thou hast magnified thy word"] + }, + { + "query": "antichrist system", + "explanation": "The phrase 'antichrist system' is usually a reference to the antichrist and end times deception.", + "search_terms": ["antichrist", "man of sin", "son of perdition", "beast"] + }, + { + "query": "pre-tribulation", + "explanation": "The phrase 'pre-tribulation' does not appear in the KJV, but is usually a reference to Christ's return before the tribulation.", + "search_terms": ["caught up", "great tribulation", "day of the lord"] + }, + { + "query": "soul winning", + "explanation": "The phrase 'soul winning' does not appear in the KJV, but is usually a reference to winning souls and converting sinners.", + "search_terms": ["winneth souls", "save souls", "convert"] + }, + { + "query": "guardian angel", + "explanation": "The phrase 'guardian angel' does not appear in the KJV, but is usually a reference to angels and their protective ministry.", + "search_terms": ["angel", "ministering spirits", "charge over thee"] + }, + { + "query": "angel wings", + "explanation": "The phrase 'angel wings' does not appear in the KJV, but is usually a reference to biblical descriptions of angels.", + "search_terms": ["angel", "seraphim", "cherubim", "wings"] + }, + { + "query": "7 deadly sins", + "explanation": "The phrase '7 deadly sins' does not appear in the KJV (it's a Catholic tradition), but is usually a reference to biblical lists of sins.", + "search_terms": ["abomination", "works of the flesh", "evil"] + }, + { + "query": "purgatory", + "explanation": "The word 'purgatory' does not appear in the KJV. This is a Catholic doctrine not found in scripture.", + "search_terms": ["appointed unto men once to die", "after this the judgment", "absent from the body"] + }, + { + "query": "christmas", + "explanation": "The word 'christmas' does not appear in the KJV, but is usually a reference to the birth of Christ.", + "search_terms": ["born in bethlehem", "manger", "wise men", "shepherds"] + }, + { + "query": "easter", + "explanation": "The word 'easter' appears once in the KJV (Acts 12:4) referring to Passover, but is usually a reference to Christ's resurrection.", + "search_terms": ["easter", "resurrection", "rose again", "third day"] + }, + { + "query": "lucifer", + "explanation": "The name 'Lucifer' appears once in the KJV (Isaiah 14:12), but is usually a reference to Satan and his fall.", + "search_terms": ["lucifer", "devil", "satan", "dragon", "serpent"] + }, + { + "query": "666", + "explanation": "The number '666' is usually a reference to the mark of the beast.", + "search_terms": ["six hundred threescore and six", "mark", "number of his name"] + }, + { + "query": "four horsemen", + "explanation": "The phrase 'four horsemen' does not appear exactly in the KJV, but is usually a reference to the four horsemen of the apocalypse.", + "search_terms": ["horse", "seal", "rider"] + }, + { + "query": "end of the world", + "explanation": "The phrase 'end of the world' is usually a reference to the end times and Christ's return.", + "search_terms": ["end of the world", "last days", "coming of the lord"] + }, + { + "query": "left behind", + "explanation": "The phrase 'left behind' is usually a reference to the rapture and second coming.", + "search_terms": ["one shall be taken", "left", "coming of the son of man"] + }, + { + "query": "prosperity gospel", + "explanation": "The phrase 'prosperity gospel' does not appear in the KJV. This is a false doctrine. Biblical warnings against covetousness and false teachers:", + "search_terms": ["love of money", "covetousness", "false prophets", "perverse disputings"] + }, + { + "query": "speaking in tongues", + "explanation": "The phrase 'speaking in tongues' is usually a reference to tongues and spiritual gifts.", + "search_terms": ["tongues", "unknown tongue", "interpretation"] + }, + { + "query": "slain in the spirit", + "explanation": "The phrase 'slain in the spirit' does not appear in the KJV. This is a charismatic practice not found in scripture.", + "search_terms": ["holy ghost", "filled with the spirit", "spirit of god"] + }, + { + "query": "original sin", + "explanation": "The phrase 'original sin' does not appear in the KJV, but is usually a reference to the doctrine of inherited sin from Adam.", + "search_terms": ["all have sinned", "by one man sin entered", "in adam all die"] + }, + { + "query": "the chosen", + "explanation": "The phrase 'the chosen' is usually a reference to God's elect and chosen people.", + "search_terms": ["chosen", "elect", "called"] + }, + { + "query": "good samaritan", + "explanation": "The phrase 'good samaritan' is usually a reference to the parable of the good Samaritan.", + "search_terms": ["certain samaritan", "neighbour", "priest levite"] + }, + { + "query": "prodigal son", + "explanation": "The phrase 'prodigal son' is usually a reference to the parable of the prodigal son.", + "search_terms": ["younger son", "riotous living", "father said", "was lost"] + }, + { + "query": "jezebel spirit", + "explanation": "The phrase 'jezebel spirit' does not appear in the KJV (it's a charismatic term), but is usually a reference to Jezebel and false prophetesses.", + "search_terms": ["jezebel", "that woman", "seduceth"] + }, + { + "query": "generational curse", + "explanation": "The phrase 'generational curse' does not appear in the KJV (it's a charismatic term), but is usually a reference to visiting iniquity upon children.", + "search_terms": ["visiting the iniquity", "fathers", "children"] + } +] diff --git a/data/grammatical_variants.json b/data/grammatical_variants.json new file mode 100644 index 0000000..069b0c6 --- /dev/null +++ b/data/grammatical_variants.json @@ -0,0 +1,54 @@ +[ + ["you", "ye", "thee", "thou"], + ["your", "thy", "thine"], + ["have", "has", "hath", "hast"], + ["do", "does", "doth", "doest"], + ["say", "says", "said", "saith"], + ["are", "art"], + ["was", "were", "wast", "wert"], + ["will", "wilt", "shall", "shalt"], + ["go", "goes", "goeth", "goest"], + ["know", "knows", "knoweth", "knowest"], + ["see", "sees", "seeth", "seest"], + ["give", "gives", "giveth", "givest"], + ["make", "makes", "maketh", "makest"], + ["come", "comes", "cometh", "comest"], + ["take", "takes", "taketh", "takest"], + ["can", "canst"], + ["may", "mayest"], + ["hear", "hears", "heareth", "hearest"], + ["walk", "walks", "walketh", "walkest"], + ["speak", "speaks", "speaketh", "speakest"], + ["live", "lives", "liveth", "livest"], + ["call", "calls", "calleth", "callest"], + ["fear", "fears", "feareth", "fearest"], + ["love", "loves", "loveth", "lovest"], + ["believe", "believes", "believeth", "believest"], + ["did", "didst"], + ["eat", "eats", "eateth", "eatest"], + ["among", "amongst"], + ["until", "till", "unto"], + ["afterward", "afterwards"], + ["before", "ere"], + ["also", "moreover", "furthermore"], + ["therefore", "wherefore", "thus"], + ["because", "for"], + ["indeed", "verily"], + ["behold", "lo"], + ["ask", "asks", "asketh", "askest"], + ["find", "finds", "findeth", "findest"], + ["seek", "seeks", "seeketh", "seekest"], + ["trust", "trusts", "trusteth", "trustest"], + ["follow", "follows", "followeth", "followest"], + ["serve", "serves", "serveth", "servest"], + ["teach", "teaches", "teacheth", "teachest"], + ["think", "thinks", "thinketh", "thinkest"], + ["lead", "leads", "leadeth", "leadest"], + ["rejoice", "rejoices", "rejoiceth", "rejoicest"], + ["keep", "keeps", "keepeth", "keepest"], + ["abide", "abides", "abideth", "abidest"], + ["judge", "judges", "judgeth", "judgest"], + ["die", "dies", "dieth", "diest"], + ["rise", "rises", "riseth", "risest"], + ["reign", "reigns", "reigneth", "reignest"] +] diff --git a/data/seq.db b/data/seq.db deleted file mode 100644 index 948f6b5..0000000 Binary files a/data/seq.db and /dev/null differ diff --git a/data/sermons.db b/data/sermons.db deleted file mode 100644 index 00b8644..0000000 Binary files a/data/sermons.db and /dev/null differ diff --git a/data/shortcodes.db b/data/shortcodes.db deleted file mode 100644 index 9300b55..0000000 Binary files a/data/shortcodes.db and /dev/null differ diff --git a/data/t_kjv.db b/data/t_kjv.db new file mode 100644 index 0000000..e69de29 diff --git a/data/theological_synonyms.json b/data/theological_synonyms.json new file mode 100644 index 0000000..9721899 --- /dev/null +++ b/data/theological_synonyms.json @@ -0,0 +1,104 @@ +[ + ["charity", "love"], + ["ghost", "spirit"], + ["righteousness", "holiness"], + ["salvation", "redemption"], + ["faith", "belief"], + ["sin", "iniquity", "transgression"], + ["forgive", "pardon"], + ["anger", "wrath"], + ["witness", "testify", "testimony"], + ["heaven", "paradise"], + ["hell", "damnation"], + ["pray", "prayer", "supplication"], + ["bless", "blessed", "blessing"], + ["promise", "covenant"], + ["worship", "praise"], + ["disciple", "apostle"], + ["eternal", "everlasting"], + ["demon", "devil"], + ["miracle", "wonder", "sign"], + ["prophesy", "prophecy"], + ["resurrect", "resurrection", "rise", "risen"], + ["baptize", "baptism"], + ["tempt", "temptation"], + ["humble", "meek"], + ["wise", "wisdom"], + ["truth", "true"], + ["glory", "glorify"], + ["mercy", "compassion"], + ["grace", "favor"], + ["judge", "judgment", "condemn", "condemnation"], + ["command", "commandment"], + ["rejoice", "joy", "gladness"], + ["reverence", "fear"], + ["teach", "doctrine"], + ["preach", "proclaim"], + ["suffer", "suffering", "affliction"], + ["comfort", "console"], + ["obey", "obedience"], + ["peace", "rest"], + ["repent", "repentance", "turn"], + ["holy", "sanctify", "sanctified"], + ["nation", "gentile"], + ["Israel", "Jew", "Hebrew"], + ["king", "kingdom", "reign"], + ["priest", "priesthood"], + ["covenant", "testament"], + ["temple", "tabernacle", "sanctuary"], + ["altar", "sacrifice", "offering"], + ["righteous", "just", "justice"], + ["wicked", "evil", "ungodly"], + ["servant", "minister"], + ["fornication", "whoredom", "adultery"], + ["sodomite", "abomination"], + ["pervert", "corrupt"], + ["destroy", "destruction", "perish"], + ["harlot", "whore"], + ["idolater", "idolatry"], + ["covetous", "covetousness", "greed"], + ["enemy", "adversary"], + ["avenge", "vengeance", "recompense"], + ["tribulation", "trouble", "distress"], + ["scorn", "mock", "despise"], + ["proud", "pride", "haughty"], + ["meek", "lowly", "humble"], + ["wisdom", "understanding", "knowledge"], + ["foolish", "fool", "folly"], + ["strengthen", "strong", "might"], + ["weary", "faint"], + ["prevail", "overcome"], + ["remnant", "residue"], + ["inheritance", "heir"], + ["generation", "seed", "offspring"], + ["pestilence", "plague"], + ["famine", "hunger"], + ["captivity", "bondage"], + ["deliver", "deliverance", "save"], + ["redeem", "ransom"], + ["provoke", "anger", "wrath"], + ["abhor", "hate", "detest"], + ["trust", "confidence"], + ["persecute", "persecution"], + ["blaspheme", "blasphemy"], + ["stone", "stoning"], + ["burn", "fire", "flame"], + ["beloved", "wellbeloved"], + ["delight", "pleasure"], + ["kindness", "lovingkindness"], + ["tender", "tenderness"], + ["longsuffering", "patience", "forbearance"], + ["gentleness", "meekness"], + ["goodness", "kindness"], + ["brotherly", "brother"], + ["wrath", "indignation", "fury"], + ["vengeance", "recompense", "reward"], + ["end times", "last days", "latter days"], + ["appearing", "coming", "return"], + ["trumpet", "shout", "voice"], + ["watch", "watchful", "vigilant"], + ["seal", "sealed"], + ["vial", "bowl"], + ["scroll", "book"], + ["lamb", "lion"] +] diff --git a/html/dictionary.html b/html/dictionary.html index 4488aae..6ba6043 100644 --- a/html/dictionary.html +++ b/html/dictionary.html @@ -1,24 +1,27 @@ + + + + + +Bible Dictionary - KJV API + + + + +
+Home | Dictionary | +Random Verse | Verse of the Day +

Bible Dictionary

-
-

- -
+ + diff --git a/html/response.html b/html/response.html index be6de6d..ac3061c 100644 --- a/html/response.html +++ b/html/response.html @@ -1 +1,4 @@ -{passage}
{text}
+
+ +
{text}
+
diff --git a/html/search.html b/html/search.html index 861327d..596071a 100644 --- a/html/search.html +++ b/html/search.html @@ -1,28 +1,30 @@ + + + + + +Bible Search - KJV API + + + + +
+Home | Quick Search | Bible Search | +Random Verse | Verse of the Day +

Bible Search

-
-

- -