Unified search bar, initial styling

This commit is contained in:
2023-01-17 03:36:40 -08:00
parent 85b0283791
commit 77a3ba1311
10 changed files with 204 additions and 68 deletions
+72 -19
View File
@@ -6,25 +6,31 @@ 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
DEBUG = 2 DEBUG = 0
""" """
To-do: To-do:
Separate dictionary-dictionary for hunsepll
Search phrase alias
Response Bible book reading
Search highlighting
Styles Styles
Bible Reading Plan Bible Reading Plan
Bible Viewer Bible Reader
""" """
def dbg(message): def dbg(message):
# if DEBUG is at or above a value of one, a call from debug level one will print red
if DEBUG >= 1: if DEBUG >= 1:
print(f'\033[91m{message}\033[0m') print(f'\033[91m{message}\033[0m')
def dbg2(message): def dbg2(message):
# if DEBUG is at or above a value of two, a call from debug level one will print green
if DEBUG >= 2: if DEBUG >= 2:
print(f'\033[32m{message}\033[0m') 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") dbg("Debug Level 1 Enabled")
dbg2("Debug Level 2 Enabled") dbg2("Debug Level 2 Enabled")
dbg3("Debug Level 3 Enabled")
dictionary_json = "data/1828_Webster_KJV.json" dictionary_json = "data/1828_Webster_KJV.json"
kjv_cur = sqlite3.connect("data/kjv.db").cursor() kjv_cur = sqlite3.connect("data/kjv.db").cursor()
@@ -40,31 +46,32 @@ idx_verse = 3
idx_text = 4 idx_text = 4
def lookup_one_verse(verse_id): def lookup_one_verse(verse_id):
dbg("lookup_one_verse({})".format(verse_id)) dbg2("lookup_one_verse({})".format(verse_id))
return(kjv_cur.execute("SELECT * FROM fts_kjv WHERE id = {};".format(verse_id)).fetchone()) return(kjv_cur.execute("SELECT * FROM fts_kjv WHERE id = {};".format(verse_id)).fetchone())
def lookup_bookname(book_id): def lookup_bookname(book_id):
dbg2("lookup_bookname({})".format(book_id)) dbg3("lookup_bookname({})".format(book_id))
result = kjv_cur.execute("SELECT n FROM key_english WHERE b = {};".format(book_id)).fetchone() result = kjv_cur.execute("SELECT n FROM key_english WHERE b = {};".format(book_id)).fetchone()
if result: if result:
result = result[0] result = result[0]
dbg("lookup_bookname.result = '{}'".format(result)) dbg2("lookup_bookname.result = '{}'".format(result))
return result return result
def lookup_book_id(bookname): def lookup_book_id(bookname):
dbg2("lookup_book_id({})".format(bookname)) dbg3("lookup_book_id({})".format(bookname))
result = kjv_cur.execute("SELECT b FROM key_english WHERE n = '{}';".format(bookname)).fetchone() result = kjv_cur.execute("SELECT b FROM key_english WHERE n = '{}';".format(bookname)).fetchone()
if result: if result:
result = result[0] result = result[0]
dbg2("lookup_book_id.result = '{}'".format(result)) dbg3("lookup_book_id.result = '{}'".format(result))
return result return result
def lookup_fts(kwds): def lookup_fts(kwds):
dbg3("lookup_fts.kwds = '{}'".format(kwds))
return kjv_cur.execute("SELECT * FROM fts_kjv('{}')".format(kwds)).fetchall() return kjv_cur.execute("SELECT * FROM fts_kjv('{}')".format(kwds)).fetchall()
def lookup_contains(kw): def lookup_contains(kw):
results = kjv_cur.execute("SELECT * FROM fts_kjv WHERE t LIKE '%{}%';".format(kw)).fetchall() results = kjv_cur.execute("SELECT * FROM fts_kjv WHERE t LIKE '%{}%';".format(kw)).fetchall()
dbg("lookup_contains({}) {} results".format(kw,len(results))) dbg2("lookup_contains({}) {} results".format(kw,len(results)))
return results return results
def lookup_by_verse_id(start_verse, end_verse = None): def lookup_by_verse_id(start_verse, end_verse = None):
@@ -110,16 +117,18 @@ def lookup_contains_exact(kw):
def ref_input_cleaning(ref, valid_chars=(string.ascii_letters + string.digits + ":-,")): def ref_input_cleaning(ref, valid_chars=(string.ascii_letters + string.digits + ":-,")):
dbg2("ref_input_cleaning({})".format(ref)) dbg2("ref_input_cleaning({})".format(ref))
ref = list(ref.replace(" ","")) ref = ref.replace(" ","")
if ref.count(':') != 1: if ref.count(':') != 1:
dbg2("ref_input_cleaning.if ref.count(:): TRUE") dbg2("ref_input_cleaning.if ref.count(:): TRUE")
return False dbg2(" -> Assuming user wanted entire chapter")
ref += ":-"
if ref.count("-") > 1: if ref.count("-") > 1:
dbg2("ref_input_cleaning.if ref.count(-): TRUE") dbg2("ref_input_cleaning.if ref.count(-): TRUE")
return False return False
if not ref[-1].isdigit() and ref[-1] != '-': if not ref[-1].isdigit() and ref[-1] != '-':
dbg2("ref_input_cleaning.if ref[-1] TRUE") dbg2("ref_input_cleaning.if ref[-1] TRUE")
return False return False
ref = list(ref)
# preserve book number if we can guess the first # preserve book number if we can guess the first
# character is a number and 3nd is a letter # character is a number and 3nd is a letter
if ref[0].isdigit() and ref[1].isalpha(): if ref[0].isdigit() and ref[1].isalpha():
@@ -149,6 +158,30 @@ def ref_parse_book(ref):
return book return book
return False return False
@app.get("/find")
def parse_query():
query = request.args.get("kw", None)
if not query:
return send_file("html/find.html")
result = show()
if not hasattr(result, '__len__'):
dbg3("parse_query.show = '{}'".format(result))
return result
if len(result) > 17:
return result
result = ''
if len(query.split()) == 1:
result += "<!DOCTYPE html><style>"
result += open("styles/collapsible.css", 'r').read()
result += "</style><button type='button' class='collapsible'>See Definition</button><div class='content'>"
result += define() + "</div><script>"
result += open("js/collapsible.js", 'r').read() + "</script>"
dbg3("parse_query.define = '{}'".format(result))
result += search()
dbg3("parse_query.search = '{}'".format(result))
return result
@app.get("/define") @app.get("/define")
def define(): def define():
ref = request.args.get("kw", None) ref = request.args.get("kw", None)
@@ -175,7 +208,7 @@ def return_dict_plain(ref, defs):
response = '' response = ''
for e in defs: for e in defs:
response += e + "<br><br>" response += e + "<br><br>"
return ref.capitalize() + "<br><br>" + response return ref.capitalize() + "<br><br>" + response + "\n"
def return_dict_rich(ref, defs): def return_dict_rich(ref, defs):
header = open('html/header.html', 'r') header = open('html/header.html', 'r')
@@ -184,7 +217,8 @@ def return_dict_rich(ref, defs):
response = '' response = ''
for e in defs: for e in defs:
response += e + "<br><br>" response += e + "<br><br>"
return (header.read() + response_html.read().format(passage = ref.capitalize(), text = response) + css.read()) # return (header.read() + response_html.read().format(passage = ref.capitalize(), text = response) + css.read())
return (response_html.read().format(passage = ref.capitalize(), text = response) + css.read())
@app.get("/show") @app.get("/show")
def show(): def show():
@@ -197,7 +231,7 @@ def show():
r_ = ref_input_cleaning(r.strip()) r_ = ref_input_cleaning(r.strip())
if r_: if r_:
refs.append(r_) refs.append(r_)
if not ref: if not refs:
return("Invalid Query") return("Invalid Query")
verse_results = [] verse_results = []
#check if the search is for multiple refs #check if the search is for multiple refs
@@ -223,14 +257,14 @@ def show():
ref_bookname = ref_parse_book(book_str) ref_bookname = ref_parse_book(book_str)
ref_book_id = lookup_book_id(ref_bookname) ref_book_id = lookup_book_id(ref_bookname)
if not ref_book_id: if not ref_book_id:
return("Book reference invalid. Ex: 1John3:16") return("Invalid Query")
start_verse_id = get_verse_id(ref_book_id, ref_chapter, start_verse) start_verse_id = get_verse_id(ref_book_id, ref_chapter, start_verse)
if end_verse: if end_verse:
end_verse_id = get_verse_id(ref_book_id, ref_chapter, 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.start_verse_id({})".format(start_verse_id))
dbg2("show.end_verse_id({})".format(end_verse_id)) dbg2("show.end_verse_id({})".format(end_verse_id))
results = lookup_by_verse_id(start_verse_id, end_verse_id) results = lookup_by_verse_id(start_verse_id, end_verse_id)
dbg2("show.results({})".format(results)) dbg3("show.results({})".format(results))
for result in results: for result in results:
verse_results.append(result) verse_results.append(result)
response_list = response_multi(verse_results) response_list = response_multi(verse_results)
@@ -257,7 +291,7 @@ def response_text(scripture):
chapter = str(scripture[idx_chapter]) chapter = str(scripture[idx_chapter])
verse = str(scripture[idx_verse]) verse = str(scripture[idx_verse])
text = str(scripture[idx_text]) text = str(scripture[idx_text])
return (bookname + " " + chapter + ":" + verse + " " + text) return (bookname + " " + chapter + ":" + verse + " " + text + "\n")
def response_rich(scripture): def response_rich(scripture):
css = open('styles/human_readable.css', 'r') css = open('styles/human_readable.css', 'r')
@@ -353,3 +387,22 @@ def search():
response = jsonify(response_list) response = jsonify(response_list)
dbg2("Search Response: {}".format(response)) dbg2("Search Response: {}".format(response))
return response return response
@app.get("/reader")
def bible_reader():
return send_file("html/reader.html")
# We should return an index of all books with
# drop down menus of each chapter, and a search
# bar at the top for quick seeking, which can use
# show(). Each page should return a header and footer.
# header should have a navigation bar and "next/previous chapter"
# Footer should have next/previous as well.
# Maybe preliminary theming support
@app.get("/plan")
def reading_plan():
return send_file("html/reading_plan.html")
# needs 2 sets of data, books and time frame.
# should default to the entire Bible in 1 year.
# should start today() and end today()+1year
# Should return a calender with date and verse range.
+13
View File
@@ -0,0 +1,13 @@
<header>
<a href="/">Home</a> | <a href="/find">Quick Search</a> |
<a href="/random?view=rich">Random Verse</a> |
<a href="/votd?view=rich">Verse of the Day</a></header>
<h2>Quick Search</h2>
<form action="/find">
<label for='kw'>Query</label><br>
<input type="text" name="kw"><br><br>
<input type="hidden" name="view" value="rich"">Rich<br>
<input type="submit">
<form>
+3 -8
View File
@@ -1,9 +1,4 @@
<DOCTYPE html>
<header> <header>
<a href="/">Home</a> <a href="/">Home</a> | <a href="/find">Quick Search</a> |
<a href="/search">Bible Search</a> <a href="/random?view=rich">Random Verse</a> |
<a href="/show">Bible Verses</a> <a href="/votd?view=rich">Verse of the Day</a></header>
<a href="/define">Bible Define</a>
<a href="/random?view=rich">Random Verse</a>
<a href="/votd?view=rich">Verse of the Day</a>
</header>
+48 -39
View File
@@ -13,43 +13,36 @@ html{
<h1>KJV API</h1> <h1>KJV API</h1>
<p>KJV accessible via web calls</p> <p>KJV accessible via web calls</p>
<h3>Usage</h3> <h2>Quick Search</h2>
<p>URL Root: <a href="https://api.1611.social/">https://api.1611.social/<a> (you are here)</p> <form action="/find">
<ul> <label for='kw'>Query</label><br>
<li><a href="/salvation">/salvation</a> - Learn what you have to do to go to heaven.</li> <input type="text" name="kw"><br><br>
<li><a href="/random">/random</a> - A random verse from the KJV</li>
<li><a href="/votd">/votd</a> - A random verse that changes from day-to-day.</li>
<li><a href="/search">/search</a> - Search keywords. No arguments = landing page.</li>
<li><a href="/show">/show</a> - Show a set of verses. No arguments = landing page.</li>
<li><a href="/define">/define</a> - Search the Dictionary. No arguments = landing page.</li>
</ul>
<h3>Verse searching</h3> <label for='view'>View<label><br>
<p>'/show?kw=' will try to match single verses and verse ranges. (not sets) For example:</p> <input type="radio" name="view" value="rich" checked="checked">Rich<br>
<pre> <input type="radio" name="view" value="plain">Plain<br>
<code> <input type="radio" name="view" value="json">JSON<br>
/show?kw=john3:16
/show?kw=1 John 3:3- (gets the whole rest of the chapter)
</code>
</pre>
<h3>Keyword searching</h3> <input type="submit">
<p>'/search?kw=keyword' accepts a search word, and will return any verses matching the query. For example:</p> <form>
<h3>Integration</h3>
<p>If you want to put the verse of the day into your website, you can use the following example php:</p>
<p>echo curl_exec(curl_init('https://api.1611.social/votd?view=plain'));</p>
<pre> <pre>
<code> <code>
API ENDPOINT SPECIFICATION v 0.2 API ENDPOINT SPECIFICATION v 0.3
================================ ================================
/random - outputs a random verse <a href="/find">
/votd - returns verse of the day /find</a> - Tries to show a verse, if it's not a verse
/search - returns a keyword search result set it will try to search your query. If it's a
/show - returns a verse or set of verses by name/location single query it will try to find a definition
/define - 1828 Noah Webster dictionary for the word as well. Only avaliable in "Rich"
and meant for interactive use/browsing
<a href="/random">/random</a> - outputs a random verse
<a href="/votd">/votd</a> - returns verse of the day
<a href="/search">/search</a> - returns a keyword search result set
<a href="/show">/show</a> - returns a verse or set of verses by name/location
<a href="/define">/define</a> - 1828 Noah Webster dictionary search
URL PARAMETERS URL PARAMETERS
============== ==============
@@ -60,14 +53,26 @@ URL PARAMETERS
default: json default: json
?kw= ?kw=
- comma-delimited keyword search for /search - The keyword(s) of your query.
FEATURES
========
- King James Only. (No heretical commentaries or false Bibles)
- High quality source documents
- Free
-
- Developer hates the sodomites
INTERACTIVE ENDPOINTS INTERACTIVE ENDPOINTS
===================== =====================
/search and /define will show you a landing page where you can The following endpoints will present you with a query box and
use HTML forms to complete your search. Both of these will radio selector for choosing your output format.
offer you spelling suggestions if it can't find the right word. - / (same function as /find)
This will only offer words that are actually in the Bible. - /search
- /define
- /show
EXAMPLES EXAMPLES
======== ========
@@ -77,11 +82,15 @@ api.tld/show?kw=john3:16,1john5:7&view=plain
api.tld/search?kw=faith api.tld/search?kw=faith
api.tld/search?kw=nimrod&view=rich api.tld/search?kw=nimrod&view=rich
api.tld/define?kw=faith&view=rich api.tld/define?kw=faith&view=rich
INTEGRATION
===========
If you want to put the verse of the day into your website, you can use the following example php:
echo curl_exec(curl_init('https://api.1611.social/votd?view=plain'));
BUGS
====
Suggestions, bug reports or improvements can be reported to <a href="https://1611.social/@tyler">@tyler@1611.social</a>
</code> </code>
</pre> </pre>
<h3>Bugs</h3>
<p>Suggestions, bug reports or improvements can be reported to <a href="https://1611.social/@tyler">@tyler@1611.social</a></p>
</html> </html>
View File
View File
+1 -2
View File
@@ -1,3 +1,2 @@
<!DOCTYPE html> <h3>{passage}</h3>
<h1>{passage}</h1>
<p>{text}</p> <p>{text}</p>
+14
View File
@@ -0,0 +1,14 @@
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
+25
View File
@@ -0,0 +1,25 @@
/* Style the button that is used to open and close the collapsible content */
.collapsible {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active, .collapsible:hover {
background-color: #ccc;
}
/* Style the collapsible content. Note: hidden by default */
.content {
padding: 0 18px;
display: none;
overflow: hidden;
background-color: #f1f1f1;
}
+28
View File
@@ -0,0 +1,28 @@
* {
text-align: left;
background-color: rgb(35, 35, 36);
}
@font-face {
font-family: Arizona-Regular;
src: url(./fonts/Arizonia-Regular.ttf);
}
@font-face {
font-family: Parisienne;
src: url(./fonts/Parisienne-Regular.ttf);
}
h1 {
background-color: aqua;
font-size: 50px;
text-align: center;
margin: auto;
font-family: Parisienne;
}
p {
color: bisque;
margin-top: 20px;
font-size: 20px;
}