diff --git a/app.py b/app.py index 0ea0b9a..b31e49a 100644 --- a/app.py +++ b/app.py @@ -7,18 +7,19 @@ import string # for literals import json # for dictionary import hunspell # for spell checking import sys +import logging + +timestamp_runtime = datetime.now().strftime("%d%b%y-%H:%M") +logging.basicConfig( + filename=f"logs/kjv-api_{datetime.now().strftime('%d%b%y')}.log", + encoding="utf-8", + level=logging.INFO, +) + +logging.info(f"Started: {timestamp_runtime}") app = Flask(__name__) -if "-vvv" in sys.argv: - DEBUG = 3 -elif "-vv" in sys.argv: - DEBUG = 2 -elif "-v" in sys.argv: - DEBUG = 1 -else: - DEBUG = 0 - """ Current work prefixed with * TODO: @@ -38,66 +39,64 @@ Bible Reader kjv_cur = sqlite3.connect("data/kjv.db").cursor() 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(), + "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(), } + def build_html_reponse(): - html = '' + html = "" return html - def lookup_one_verse(verse_id): - dbg2("lookup_one_verse({})".format(verse_id)) - return kjv_cur.execute( - "SELECT * FROM fts_kjv WHERE id = {};".format(verse_id) - ).fetchone() + logging.debug(f"lookup_one_verse({verse_id})") + return kjv_cur.execute(f"SELECT * FROM fts_kjv WHERE id = {verse_id};").fetchone() def lookup_bookname(book_id): - dbg3("lookup_bookname({})".format(book_id)) + logging.debug(f"lookup_bookname({book_id})") result = kjv_cur.execute( - "SELECT n FROM key_english WHERE b = {};".format(book_id) + f"SELECT n FROM key_english WHERE b = {book_id};" ).fetchone() if result: result = result[0] - dbg3("lookup_bookname.result = '{}'".format(result)) + logging.debug(f"lookup_bookname.result = '{result}'") return result def lookup_book_id(bookname): - dbg3("lookup_book_id({})".format(bookname)) + logging.debug(f"lookup_book_id({bookname})") result = kjv_cur.execute( - "SELECT b FROM key_english WHERE n = '{}';".format(bookname) + f"SELECT b FROM key_english WHERE n = '{bookname}';" ).fetchone() if result: result = result[0] - dbg3("lookup_book_id.result = '{}'".format(result)) + logging.debug(f"lookup_book_id.result = '{result}'") return result def lookup_fts(kwds): - dbg3("lookup_fts.kwds = '{}'".format(kwds)) - return kjv_cur.execute("SELECT * FROM fts_kjv('{}')".format(kwds)).fetchall() + logging.debug(f"lookup_fts.kwds = '{kwds}'") + return kjv_cur.execute(f"SELECT * FROM fts_kjv('{kwds}')").fetchall() def lookup_by_verse_id(start_verse, end_verse=None): if not end_verse: results = kjv_cur.execute( - "SELECT * FROM fts_kjv WHERE id = {};".format(start_verse) + f"SELECT * FROM fts_kjv WHERE id = {start_verse};" ).fetchall() else: results = kjv_cur.execute( - "SELECT * FROM fts_kjv WHERE id BETWEEN {} AND {};".format( - start_verse, end_verse - ) + f"SELECT * FROM fts_kjv WHERE id BETWEEN {start_verse} AND {end_verse};" ).fetchall() return results @@ -110,22 +109,23 @@ def get_verse_id(book_id, chapter, verse): verseid = "001" # Avoid verse_ids list IndexErrors on :- searches return str(book_id) + chapterid + verseid + def ref_input_cleaning( ref, valid_chars=(string.ascii_letters + string.digits + ":-,"), ref_search=True ): - dbg3("ref_input_cleaning({})".format(ref)) + logging.debug(f"ref_input_cleaning({ref})") ref = ref.replace(" ", "") if ref.count(":") != 1 and ref_search: - dbg2("ref_input_cleaning.if ref.count(:): TRUE") - dbg2(" -> Assuming user wanted entire chapter") + logging.debug("ref_input_cleaning.if ref.count(:): TRUE") + logging.debug(" -> Assuming user wanted entire chapter") ref += ":-" if ref.endswith(":"): ref += "-" if ref.count("-") > 1 and ref_search: - dbg2("ref_input_cleaning.if ref.count(-): TRUE") + logging.debug("ref_input_cleaning.if ref.count(-): TRUE") return False if not ref[-1].isdigit() and ref[-1] != "-" and ref_search: - dbg2("ref_input_cleaning.if ref[-1] TRUE") + logging.debug("ref_input_cleaning.if ref[-1] TRUE") return False ref = list(ref) # preserve book number if we can guess the first @@ -159,7 +159,7 @@ def search_input_cleaning( def ref_parse_book(ref): - for book in reversed(cached['book_names']): + for book in reversed(cached["book_names"]): if book.startswith(ref): return book return False @@ -168,19 +168,20 @@ def ref_parse_book(ref): @app.get("/find") def parse_query(): query = request.args.get("kw", None) + logging.info(f"Query: {query}") if not query: return send_file("html/find.html") - if query.endswith(','): + if query.endswith(","): query = query[:-1] result = show() if not hasattr(result, "__len__"): - dbg3("parse_query.show = '{}'".format(result)) + logging.debug(f"parse_query.show = '{result}'") return result if len(result) > 17: return result result = "" - result += "" + result += "" result += "

1828 Webster Definitions

" split_query = query.replace(",", " ").split(" ") if len(split_query) > 20: @@ -189,18 +190,22 @@ def parse_query(): definition = define(word) if not definition: continue - result += "" + result += ( + "" + ) result += "
" + definition + "
" - dbg3("parse_query.define = '{}'".format(word)) - result += "" + logging.debug(f"parse_query.define = '{word}'") + result += "" result += "
" result += search() - dbg3("parse_query.search = '{}'".format(result)) + logging.debug(f"parse_query.search = '{result}'") return result @app.get("/define") -def define(word = None): +def define(word=None): ref = request.args.get("kw", None) if word: ref = word @@ -208,18 +213,19 @@ def define(word = None): ref = ref.strip() if not ref: return send_file("html/dictionary.html") - dictionary = cached['json_dictionary'] + dictionary = cached["json_dictionary"] ref = ref.lower() if not ref in dictionary.keys(): if word: return False - return "{} not found.".format(ref) + logging.warning(f"WEBSTER MISSING: {ref}") + return f"{ref} not found." # 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( + # 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) if arg_view == "plain": response = return_dict_plain(ref, dictionary[ref]) @@ -229,6 +235,7 @@ def define(word = None): response = jsonify({ref: dictionary[ref]}) return response + def return_dict_plain(ref, defs): response = "" for e in defs: @@ -240,16 +247,18 @@ def return_dict_rich(ref, defs, simple): if simple: css = "" else: - css = cached['css_response'] + css = cached["css_response"] response = "" for e in defs: response += e + "

" return ( - cached['html_response'].format( + cached["html_response"].format( chapter=ref.split(":")[0], passage=ref.capitalize(), text=response - ) + css + ) + + css ) + @app.get("/show") def show(kw=None, get_start_verse_id=False): ref = request.args.get("kw", None) @@ -258,7 +267,7 @@ def show(kw=None, get_start_verse_id=False): ref = kw if ref is None: return send_file("html/show.html") - dbg("show({})".format(ref)) + logging.debug(f"show({ref})") refs = [] for r in ref.split(","): r_ = ref_input_cleaning(r.strip()) @@ -269,7 +278,7 @@ def show(kw=None, get_start_verse_id=False): verse_results = [] # check if the search is for multiple refs for ref in refs: - dbg("show.reference({})".format(ref)) + logging.debug(f"show.reference({ref})") maybe_chapter = ref.split(":")[0][3:] ref_chapter = "" for maybedigit in maybe_chapter: @@ -299,10 +308,10 @@ def show(kw=None, get_start_verse_id=False): return start_verse_id if end_verse: end_verse_id = get_verse_id(ref_book_id, ref_chapter, end_verse) - dbg2("show.start_verse_id({})".format(start_verse_id)) - dbg2("show.end_verse_id({})".format(end_verse_id)) + logging.debug(f"show.start_verse_id({start_verse_id})") + logging.debug(f"show.end_verse_id({end_verse_id})") results = lookup_by_verse_id(start_verse_id, end_verse_id) - dbg3("show.results({})".format(results)) + logging.debug(f"show.results({results})") for result in results: verse_results.append(result) response_list = response_multi(verse_results) @@ -342,21 +351,23 @@ def response_rich(scripture): verse = str(scripture[3]) text = str(scripture[4]) passage = bookname + " " + chapter + ":" + verse - return ( - cached['html_response'].format( - chapter=passage.split(":")[0], passage=passage, text=text - ) + return cached["html_response"].format( + chapter=passage.split(":")[0], passage=passage, text=text ) def response_single(scripture): - dbg2("response_single({})".format(scripture)) + logging.debug(f"response_single({scripture})") arg_view = request.args.get("view", "json") - dbg("->requested_format = {}".format(arg_view)) + logging.debug(f"->requested_format = {arg_view}") if arg_view == "plain": return response_text(scripture) elif arg_view == "rich": - return cached['html_navbar'].format(query=request.args.get("kw", "")) + cached['css_response'] + response_rich(scripture) + return ( + cached["html_navbar"].format(query=request.args.get("kw", "")) + + cached["css_response"] + + response_rich(scripture) + ) else: response = response_dict(scripture) return jsonify(response) @@ -365,13 +376,13 @@ def response_single(scripture): def response_multi(scriptures): response = [] arg_view = request.args.get("view", "json") - dbg("search ->requested_format = {}".format(arg_view)) + logging.debug(f"search ->requested_format = {arg_view}") if arg_view == "plain": for scripture in scriptures: response.append(response_text(scripture)) elif arg_view == "rich": - response.append(cached['css_response']) - response.append(cached['html_navbar'].format(query=request.args.get("kw", ""))) + response.append(cached["css_response"]) + response.append(cached["html_navbar"].format(query=request.args.get("kw", ""))) for scripture in scriptures: response.append(response_rich(scripture)) else: @@ -390,6 +401,7 @@ def page_not_found(e): @app.get("/") def idx(): + logging.info("Index Served") return send_file("html/index.html") @@ -402,8 +414,9 @@ def salvation(): @app.get("/t/random", defaults={"view": "plain"}) @app.get("/random") def random_verse(): - rand_id = random.SystemRandom().choice(cached['verse_ids']) + rand_id = random.SystemRandom().choice(cached["verse_ids"]) scripture = lookup_one_verse(rand_id) + logging.info(f"Random Served: {scripture[0]}") return response_single(scripture) @@ -412,17 +425,18 @@ def random_verse(): @app.get("/t/votd", defaults={"view": "plain"}) @app.get("/votd") def verse_of_the_day(): + logging.info("VOTD Served") today = int(str(date.today()).replace("-", "")) # this is deterministic because of today's date being used as the seed random.seed(today) - scripture = lookup_one_verse(random.choice(cached['verse_ids'])) + scripture = lookup_one_verse(random.choice(cached["verse_ids"])) return response_single(scripture) @app.get("/search") def search(): keywords = request.args.get("kw", None) - dbg("search({})".format(keywords)) + logging.debug(f"search({keywords})") if keywords: cleaned_keywords = search_input_cleaning(keywords) if not cleaned_keywords: @@ -444,7 +458,7 @@ def search(): response = "
".join(response_list) else: response = jsonify(response_list) - dbg2("Search Response: {}".format(response)) + logging.debug(f"Search Response: {response}") return response @@ -493,46 +507,43 @@ def sequential_read(start=0, num=1, uid=None, getuid=False): if start: - start = cached['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()) try: seq_cur.execute( - "INSERT INTO reading_tracker VALUES('{}', {}, '{}')".format( - uid, start, last_used - ) + f"INSERT INTO reading_tracker VALUES('{uid}', {start}, '{last_used}')" ) except sqlite3.OperationalError: - return jsonify({"error":"Something went wrong with the database. Check permissions?"}) + logging.error(f"UID Database failed to insert {uid} into reading_tracker.") + return jsonify( + {"error": "Something went wrong with the UID database. Check permissions?"} + ) seq_conn.commit() - dbg2("Created new UID: {}".format(uid)) - return "Your new UID is: {}".format(uid) + logging.info(f"Created new SEQ UID: {uid}") + return f"Your new UID is: {uid}" uid = ref_input_cleaning(uid, ref_search=False) try: nextverse = seq_cur.execute( - "SELECT verse FROM reading_tracker WHERE uid = '{}'".format(uid) + f"SELECT verse FROM reading_tracker WHERE uid = '{uid}'" ).fetchone()[0] - dbg2("Queried UID {} on verse {}".format(uid, nextverse)) + logging.debug(f"Queried UID {uid} on verse {nextverse}") except TypeError: - dbg2("Invalid UID {}".format(uid)) + logging.debug(f"Invalid UID {uid}") return jsonify({"error": "UID Invalid or purged"}) - if len(cached['verse_ids']) < (nextverse + num): + if len(cached["verse_ids"]) < (nextverse + num): # Correct our range to go to ceiling to prevent IndexError - diff = len(cached['verse_ids']) - (nextverse + num) + diff = len(cached["verse_ids"]) - (nextverse + num) num = diff - num - start_verse = cached['verse_ids'][nextverse] - end_verse = cached['verse_ids'][nextverse + num] + start_verse = cached["verse_ids"][nextverse] + end_verse = cached["verse_ids"][nextverse + num] result_list = lookup_by_verse_id(start_verse, end_verse) response_list = response_multi(result_list) - dbg2( - "SQL : UPDATE reading_tracker SET verse = {}, last_used = '{}' WHERE uid = '{}'".format( - nextverse + num, last_used, uid - ) - ) + logging.info(f"Served {num+1} verses to UID {uid}") seq_cur.execute( - "UPDATE reading_tracker SET verse = {}, last_used = '{}' WHERE uid = '{}'".format( - nextverse + num + 1, last_used, uid - ) + f"UPDATE reading_tracker SET verse = {nextverse+num+1}, last_used = '{last_used}' WHERE uid = '{uid}'" ) seq_conn.commit() arg_view = request.args.get("view", None) @@ -544,20 +555,6 @@ def sequential_read(start=0, num=1, uid=None, getuid=False): return response -def dbg(message): - if DEBUG >= 1: - print(f"\033[91m{message}\033[0m") - - -def dbg2(message): - if DEBUG >= 2: - print(f" \033[32m{message}\033[0m") - - -def dbg3(message): - if DEBUG >= 3: - print(f" \033[35m{message}\033[0m") - -dbg("Debug Level 1 Enabled") -dbg2("Debug Level 2 Enabled") -dbg3("Debug Level 3 Enabled") \ No newline at end of file +if __name__ == "__main__": + print("You must run this program within a uwsgi environment.") + #app.run(host="127.0.0.1", port=1612) \ No newline at end of file diff --git a/html/find.html b/html/find.html index d9414a1..243dbba 100644 --- a/html/find.html +++ b/html/find.html @@ -7,7 +7,7 @@


- Rich
+