Persistant search bar. Multiple definitions. Trailing commas handled. Static values cached. argv debug mode. Wildcard searching.

This commit is contained in:
2023-02-09 01:25:00 -08:00
parent b95199aa73
commit 233ab58f24
4 changed files with 96 additions and 63 deletions
+87 -52
View File
@@ -6,23 +6,35 @@ import sqlite3
import string # for literals import string # for literals
import json # for dictionary import json # for dictionary
import hunspell # for spell checking import hunspell # for spell checking
import sys
DEBUG = 0 if "-vvv" in sys.argv:
DEBUG = 3
elif "-vv" in sys.argv:
DEBUG = 2
elif "-v" in sys.argv:
DEBUG = 1
else:
DEBUG = 0
""" """
To-do: Current work prefixed with *
Separate dictionary-dictionary for hunsepll TODO:
Search phrase alias Replace send_file with something that loads headers and nav templates
* Return multiple defintiions for each word in phrase
Search phrase alias dictionary
Response Bible book reading Response Bible book reading
Search highlighting Search highlighting
Styles Styles
Bible Reading Plan Bible Reading Plan
Bible Reader Bible Reader
Rich: <hr> on each comma /seq Going backwards (works but is buggy, disabled for now)
find: handle ending with , /seq Wrap back to Gen 1:1 when you reach the end of Rev
/seq Querying what your UID's next verse is going to be
/seq Purging old UIDs after a certain period of non-use
Separate common functions into own library
""" """
def dbg(message): def dbg(message):
if DEBUG >= 1: if DEBUG >= 1:
print(f"\033[91m{message}\033[0m") print(f"\033[91m{message}\033[0m")
@@ -42,10 +54,19 @@ dbg("Debug Level 1 Enabled")
dbg2("Debug Level 2 Enabled") dbg2("Debug Level 2 Enabled")
dbg3("Debug Level 3 Enabled") dbg3("Debug Level 3 Enabled")
dictionary_json = "data/1828_Webster_KJV.json"
kjv_cur = sqlite3.connect("data/kjv.db").cursor() kjv_cur = sqlite3.connect("data/kjv.db").cursor()
verse_ids = [i[0] for i in kjv_cur.execute("SELECT id FROM fts_kjv;").fetchall()]
book_names = [i[0] for i in kjv_cur.execute("SELECT n FROM key_english").fetchall()] cached = {
"verse_ids":[i[0] for i in kjv_cur.execute("SELECT id FROM fts_kjv;").fetchall()],
"book_names":[i[0] for i in kjv_cur.execute("SELECT n FROM key_english").fetchall()],
"json_dictionary":json.load(open("data/1828_Webster_KJV.json", 'r')),
"html_response":open("html/response.html", "r").read(),
"html_navbar":open("html/navbar.html", "r").read(),
"css_response":open("styles/human_readable.css", "r").read(),
"css_collapsible":open("styles/collapsible.css", "r").read(),
"js_collapsible":open("js/collapsible.js", "r").read(),
}
app = Flask(__name__) app = Flask(__name__)
@@ -189,7 +210,7 @@ def ref_input_cleaning(
def search_input_cleaning( def search_input_cleaning(
query, valid_chars=string.ascii_letters + string.digits + '+^" ' query, valid_chars=string.ascii_letters + string.digits + '+^"* '
): ):
cleaned_query = [] cleaned_query = []
for character in query: for character in query:
@@ -199,7 +220,7 @@ def search_input_cleaning(
def ref_parse_book(ref): def ref_parse_book(ref):
for book in reversed(book_names): for book in reversed(cached['book_names']):
if book.startswith(ref): if book.startswith(ref):
return book return book
return False return False
@@ -210,6 +231,8 @@ def parse_query():
query = request.args.get("kw", None) query = request.args.get("kw", None)
if not query: if not query:
return send_file("html/find.html") return send_file("html/find.html")
if query.endswith(','):
query = query[:-1]
result = show() result = show()
if not hasattr(result, "__len__"): if not hasattr(result, "__len__"):
dbg3("parse_query.show = '{}'".format(result)) dbg3("parse_query.show = '{}'".format(result))
@@ -217,40 +240,50 @@ def parse_query():
if len(result) > 17: if len(result) > 17:
return result return result
result = "" result = "<!DOCTYPE html>"
if len(query.split()) == 1: result += "<style>" + cached['css_collapsible'] + "</style>"
result += "<!DOCTYPE html><style>" result += "<h3>1828 Webster Definitions</h3>"
result += open("styles/collapsible.css", "r").read() split_query = query.replace(",", " ").split(" ")
result += "</style><button type='button' class='collapsible'>See Definition</button><div class='content'>" for word in split_query:
result += define() + "</div><script>" definition = define(word)
result += open("js/collapsible.js", "r").read() + "</script>" if not definition:
dbg3("parse_query.define = '{}'".format(result)) continue
result += "<button type='button' class='collapsible'>" + word.capitalize() + "</button>"
result += "<div class='content'>" + definition + "</div>"
dbg3("parse_query.define = '{}'".format(word))
result += "<script>" + cached['js_collapsible'] + "</script>"
result += "<hr>"
result += search() result += search()
dbg3("parse_query.search = '{}'".format(result)) dbg3("parse_query.search = '{}'".format(result))
return result return result
@app.get("/define") @app.get("/define")
def define(): def define(word = None):
ref = request.args.get("kw", None) ref = request.args.get("kw", None)
if word:
ref = word
if ref: if ref:
ref = ref.strip() ref = ref.strip()
if not ref: if not ref:
return send_file("html/dictionary.html") return send_file("html/dictionary.html")
with open(dictionary_json, "r") as data: dictionary = cached['json_dictionary']
dictionary = json.load(data)
ref = ref.lower() ref = ref.lower()
if not ref in dictionary.keys(): if not ref in dictionary.keys():
h = hunspell.HunSpell("data/kjv.dic", "data/kjv.aff") if word:
suggestions = h.suggest(ref) return False
return "{} not found, try one of the following: {}".format( return "{} not found.".format(ref)
ref, ", ".join(suggestions) # search suggestions kind of suck
) #h = hunspell.HunSpell("data/kjv.dic", "data/kjv.aff")
#suggestions = h.suggest(ref)
#return "{} not found, try one of the following: {}".format(
# ref, ", ".join(suggestions)
#)
arg_view = request.args.get("view", None) arg_view = request.args.get("view", None)
if arg_view == "plain": if arg_view == "plain":
response = return_dict_plain(ref, dictionary[ref]) response = return_dict_plain(ref, dictionary[ref])
elif arg_view == "rich": elif arg_view == "rich":
response = return_dict_rich(ref, dictionary[ref]) response = return_dict_rich(ref, dictionary[ref], simple=word)
else: else:
response = jsonify({ref: dictionary[ref]}) response = jsonify({ref: dictionary[ref]})
return response return response
@@ -263,18 +296,18 @@ def return_dict_plain(ref, defs):
return ref.capitalize() + "<br><br>" + response + "\n" return ref.capitalize() + "<br><br>" + response + "\n"
def return_dict_rich(ref, defs): def return_dict_rich(ref, defs, simple):
header = open("html/header.html", "r") if simple:
css = open("styles/human_readable.css", "r") css = ""
response_html = open("html/response.html", "r") else:
css = cached['css_response']
response = "" response = ""
for e in defs: for e in defs:
response += e + "<br><br>" response += e + "<br><br>"
return ( return (
response_html.read().format( cached['html_response'].format(
chapter=ref.split(":")[0], passage=ref.capitalize(), text=response chapter=ref.split(":")[0], passage=ref.capitalize(), text=response
) ) + css
+ css.read()
) )
@@ -334,7 +367,9 @@ def show(kw=None, get_start_verse_id=False):
verse_results.append(result) verse_results.append(result)
response_list = response_multi(verse_results) response_list = response_multi(verse_results)
arg_view = request.args.get("view", None) arg_view = request.args.get("view", None)
if arg_view == "plain" or arg_view == "rich": if arg_view == "plain":
response = "<br>".join(response_list)
elif arg_view == "rich":
response = "<br>".join(response_list) response = "<br>".join(response_list)
else: else:
response = jsonify(response_list) response = jsonify(response_list)
@@ -361,8 +396,6 @@ def response_text(scripture):
def response_rich(scripture): def response_rich(scripture):
css = open("styles/human_readable.css", "r")
response_html = open("html/response.html", "r")
# (passage, text) is the html formatting arguments # (passage, text) is the html formatting arguments
bookname = lookup_bookname(scripture[idx_book]) bookname = lookup_bookname(scripture[idx_book])
chapter = str(scripture[idx_chapter]) chapter = str(scripture[idx_chapter])
@@ -370,10 +403,9 @@ def response_rich(scripture):
text = str(scripture[idx_text]) text = str(scripture[idx_text])
passage = bookname + " " + chapter + ":" + verse passage = bookname + " " + chapter + ":" + verse
return ( return (
response_html.read().format( cached['html_response'].format(
chapter=passage.split(":")[0], passage=passage, text=text chapter=passage.split(":")[0], passage=passage, text=text
) )
+ css.read()
) )
@@ -384,8 +416,7 @@ def response_single(scripture):
if arg_view == "plain": if arg_view == "plain":
return response_text(scripture) return response_text(scripture)
elif arg_view == "rich": elif arg_view == "rich":
header = open("html/header.html", "r") return cached['html_navbar'].format(query=request.args.get("kw", "")) + cached['css_response'] + response_rich(scripture)
return header.read() + response_rich(scripture)
else: else:
response = response_dict(scripture) response = response_dict(scripture)
return jsonify(response) return jsonify(response)
@@ -399,7 +430,8 @@ def response_multi(scriptures):
for scripture in scriptures: for scripture in scriptures:
response.append(response_text(scripture)) response.append(response_text(scripture))
elif arg_view == "rich": elif arg_view == "rich":
response.append(open("html/header.html", "r").read()) response.append(cached['css_response'])
response.append(cached['html_navbar'].format(query=request.args.get("kw", "")))
for scripture in scriptures: for scripture in scriptures:
response.append(response_rich(scripture)) response.append(response_rich(scripture))
else: else:
@@ -430,19 +462,20 @@ def salvation():
@app.get("/t/random", defaults={"view": "plain"}) @app.get("/t/random", defaults={"view": "plain"})
@app.get("/random") @app.get("/random")
def random_verse(): def random_verse():
rand_id = random.SystemRandom().choice(verse_ids) rand_id = random.SystemRandom().choice(cached['verse_ids'])
scripture = lookup_one_verse(rand_id) scripture = lookup_one_verse(rand_id)
return response_single(scripture) return response_single(scripture)
# backwards-compatibility # backwards-compatibility
# plain takes 38ms -TODO can we cache this?
@app.get("/t/votd", defaults={"view": "plain"}) @app.get("/t/votd", defaults={"view": "plain"})
@app.get("/votd") @app.get("/votd")
def verse_of_the_day(): def verse_of_the_day():
today = int(str(date.today()).replace("-", "")) today = int(str(date.today()).replace("-", ""))
# this is deterministic because of today's date being used as the seed # this is deterministic because of today's date being used as the seed
random.seed(today) random.seed(today)
scripture = lookup_one_verse(random.choice(verse_ids)) scripture = lookup_one_verse(random.choice(cached['verse_ids']))
return response_single(scripture) return response_single(scripture)
@@ -465,7 +498,9 @@ def search():
) )
response_list = response_multi(results) response_list = response_multi(results)
arg_view = request.args.get("view", None) arg_view = request.args.get("view", None)
if arg_view == "plain" or arg_view == "rich": if arg_view == "plain":
response = "<br>".join(response_list)
elif arg_view == "rich":
response = "<br>".join(response_list) response = "<br>".join(response_list)
else: else:
response = jsonify(response_list) response = jsonify(response_list)
@@ -518,7 +553,7 @@ def sequential_read(start=0, num=1, uid=None, getuid=False):
if start: if start:
start = verse_ids.index(int(show(kw=start, get_start_verse_id=True))) start = cached['verse_ids'].index(int(show(kw=start, get_start_verse_id=True)))
uid = str(uuid.uuid4()) uid = str(uuid.uuid4())
try: try:
seq_cur.execute( seq_cur.execute(
@@ -540,12 +575,12 @@ def sequential_read(start=0, num=1, uid=None, getuid=False):
except TypeError: except TypeError:
dbg2("Invalid UID {}".format(uid)) dbg2("Invalid UID {}".format(uid))
return jsonify({"error": "UID Invalid or purged"}) return jsonify({"error": "UID Invalid or purged"})
if len(verse_ids) < (nextverse + num): if len(cached['verse_ids']) < (nextverse + num):
# Correct our range to go to ceiling to prevent IndexError # Correct our range to go to ceiling to prevent IndexError
diff = len(verse_ids) - (nextverse + num) diff = len(cached['verse_ids']) - (nextverse + num)
num = diff - num num = diff - num
start_verse = verse_ids[nextverse] start_verse = cached['verse_ids'][nextverse]
end_verse = verse_ids[nextverse + num] end_verse = cached['verse_ids'][nextverse + num]
result_list = lookup_by_verse_id(start_verse, end_verse) result_list = lookup_by_verse_id(start_verse, end_verse)
response_list = response_multi(result_list) response_list = response_multi(result_list)
+5
View File
@@ -1,4 +1,9 @@
<header> <header>
<form action="/find">
<input type="text" name="kw" value="{query}">
<input type="hidden" name="view" value="rich"">
<input type="submit" value="Search">
<form>
<a href="/">Home</a> | <a href="/find">Quick Search</a> | <a href="/">Home</a> | <a href="/find">Quick Search</a> |
<a href="/random?view=rich">Random Verse</a> | <a href="/random?view=rich">Random Verse</a> |
<a href="/votd?view=rich">Verse of the Day</a></header> <a href="/votd?view=rich">Verse of the Day</a></header>
+1 -9
View File
@@ -1,9 +1 @@
<p> <p><b><a href="/find?kw={chapter}&view=rich">{passage}</a></b><br>{text}</p>
<b>
<a href="/find?kw={chapter}&view=rich">
{passage}
</a>
</b>
<br>
{text}
</p>
+3 -2
View File
@@ -2,8 +2,9 @@
# run from current directory only # run from current directory only
FILE=app.py FILE=app.py
if test -f "$FILE"; then if test -f "$FILE"; then
uwsgi_python3 --http-socket 0.0.0.0:1612 --wsgi-file app.py --callable app uwsgi_python3 --http-socket 0.0.0.0:1612 --wsgi-file app.py --callable app --pyargv "-vvv"
else else
cd /opt/kjv-api cd /opt/kjv-api
uwsgi_python3 --http-socket 0.0.0.0:1612 --wsgi-file /opt/kjv-api/app.py --callable app echo "WARNING: RUNNING FROM /opt/kjv-api"
uwsgi_python3 --http-socket 0.0.0.0:1612 --wsgi-file /opt/kjv-api/app.py --callable app --pyargv "-vvv"
fi fi