Added sequential iteration feature with persistant uid
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
# app.py
|
# app.py
|
||||||
from flask import Flask, request, jsonify, send_file
|
from flask import Flask, request, jsonify, send_file
|
||||||
import random
|
import random
|
||||||
from datetime import date # used for daily verse
|
from datetime import date, datetime # used for daily verse
|
||||||
import sqlite3
|
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 = 0
|
DEBUG = 2
|
||||||
|
|
||||||
"""
|
"""
|
||||||
To-do:
|
To-do:
|
||||||
@@ -19,7 +19,6 @@ Styles
|
|||||||
Bible Reading Plan
|
Bible Reading Plan
|
||||||
Bible Reader
|
Bible Reader
|
||||||
Rich: <hr> on each comma
|
Rich: <hr> on each comma
|
||||||
find: handle ending with :
|
|
||||||
find: handle ending with ,
|
find: handle ending with ,
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -71,7 +70,7 @@ def lookup_bookname(book_id):
|
|||||||
).fetchone()
|
).fetchone()
|
||||||
if result:
|
if result:
|
||||||
result = result[0]
|
result = result[0]
|
||||||
dbg2("lookup_bookname.result = '{}'".format(result))
|
dbg3("lookup_bookname.result = '{}'".format(result))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -149,23 +148,27 @@ def lookup_contains_exact(kw):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def ref_input_cleaning(ref, valid_chars=(string.ascii_letters + string.digits + ":-,")):
|
def ref_input_cleaning(
|
||||||
dbg2("ref_input_cleaning({})".format(ref))
|
ref, valid_chars=(string.ascii_letters + string.digits + ":-,"), ref_search=True
|
||||||
|
):
|
||||||
|
dbg3("ref_input_cleaning({})".format(ref))
|
||||||
ref = ref.replace(" ", "")
|
ref = ref.replace(" ", "")
|
||||||
if ref.count(":") != 1:
|
if ref.count(":") != 1 and ref_search:
|
||||||
dbg2("ref_input_cleaning.if ref.count(:): TRUE")
|
dbg2("ref_input_cleaning.if ref.count(:): TRUE")
|
||||||
dbg2(" -> Assuming user wanted entire chapter")
|
dbg2(" -> Assuming user wanted entire chapter")
|
||||||
ref += ":-"
|
ref += ":-"
|
||||||
if ref.count("-") > 1:
|
if ref.endswith(":"):
|
||||||
|
ref += "-"
|
||||||
|
if ref.count("-") > 1 and ref_search:
|
||||||
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] != "-" and ref_search:
|
||||||
dbg2("ref_input_cleaning.if ref[-1] TRUE")
|
dbg2("ref_input_cleaning.if ref[-1] TRUE")
|
||||||
return False
|
return False
|
||||||
ref = list(ref)
|
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() and ref_search:
|
||||||
prefix = ref.pop(0) + " "
|
prefix = ref.pop(0) + " "
|
||||||
else:
|
else:
|
||||||
prefix = ""
|
prefix = ""
|
||||||
@@ -177,7 +180,10 @@ def ref_input_cleaning(ref, valid_chars=(string.ascii_letters + string.digits +
|
|||||||
# Was there a problem?
|
# Was there a problem?
|
||||||
if len(cleaned_ref) == 0:
|
if len(cleaned_ref) == 0:
|
||||||
return False
|
return False
|
||||||
return prefix + "".join(cleaned_ref).capitalize()
|
if ref_search:
|
||||||
|
return prefix + "".join(cleaned_ref).capitalize()
|
||||||
|
else:
|
||||||
|
return "".join(cleaned_ref)
|
||||||
|
|
||||||
|
|
||||||
def search_input_cleaning(
|
def search_input_cleaning(
|
||||||
@@ -271,8 +277,10 @@ def return_dict_rich(ref, defs):
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/show")
|
@app.get("/show")
|
||||||
def show():
|
def show(kw=None, get_start_verse_id=False):
|
||||||
ref = request.args.get("kw", None)
|
ref = request.args.get("kw", None)
|
||||||
|
if kw:
|
||||||
|
ref = kw
|
||||||
if ref is None:
|
if ref is None:
|
||||||
return send_file("html/show.html")
|
return send_file("html/show.html")
|
||||||
dbg("show({})".format(ref))
|
dbg("show({})".format(ref))
|
||||||
@@ -312,6 +320,8 @@ def show():
|
|||||||
if not ref_book_id:
|
if not ref_book_id:
|
||||||
return "Invalid Query"
|
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 get_start_verse_id:
|
||||||
|
return start_verse_id
|
||||||
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))
|
||||||
@@ -480,3 +490,74 @@ def reading_plan():
|
|||||||
# should default to the entire Bible in 1 year.
|
# should default to the entire Bible in 1 year.
|
||||||
# should start today() and end today()+1year
|
# should start today() and end today()+1year
|
||||||
# Should return a calender with date and verse range.
|
# Should return a calender with date and verse range.
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/seq")
|
||||||
|
def sequential_read(start=0, num=1, uid=None, getuid=False):
|
||||||
|
# /seq?uid=1
|
||||||
|
uid = request.args.get("uid", uid)
|
||||||
|
getuid = request.args.get("getuid", getuid)
|
||||||
|
start = request.args.get("start", start)
|
||||||
|
try:
|
||||||
|
num = int(request.args.get("num", num))
|
||||||
|
except ValueError:
|
||||||
|
return "Invalid num argument"
|
||||||
|
if not uid or not getuid:
|
||||||
|
return send_file("html/seq.html")
|
||||||
|
seq_conn = sqlite3.connect("data/seq.db")
|
||||||
|
seq_cur = seq_conn.cursor()
|
||||||
|
last_used = str(datetime.now())
|
||||||
|
num -= 1 # 0 will mean "next"
|
||||||
|
if num < 0:
|
||||||
|
return jsonify({"error":"You may only increment forward."})
|
||||||
|
|
||||||
|
if getuid:
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
if start:
|
||||||
|
start = verse_ids.index(int(show(kw=start, get_start_verse_id=True)))
|
||||||
|
uid = str(uuid.uuid4())
|
||||||
|
seq_cur.execute(
|
||||||
|
"INSERT INTO reading_tracker VALUES('{}', {}, '{}')".format(
|
||||||
|
uid, start, last_used
|
||||||
|
)
|
||||||
|
)
|
||||||
|
seq_conn.commit()
|
||||||
|
dbg2("Created new UID: {}".format(uid))
|
||||||
|
return "Your new UID is: {}".format(uid)
|
||||||
|
uid = ref_input_cleaning(uid, ref_search=False)
|
||||||
|
try:
|
||||||
|
nextverse = seq_cur.execute(
|
||||||
|
"SELECT verse FROM reading_tracker WHERE uid = '{}'".format(uid)
|
||||||
|
).fetchone()[0]
|
||||||
|
dbg2("Queried UID {} on verse {}".format(uid, nextverse))
|
||||||
|
except TypeError:
|
||||||
|
dbg2("Invalid UID {}".format(uid))
|
||||||
|
return jsonify({"error":"UID Invalid or purged"})
|
||||||
|
if len(verse_ids) < (nextverse + num):
|
||||||
|
# Correct our range to go to ceiling to prevent IndexError
|
||||||
|
diff = len(verse_ids) - (nextverse + num)
|
||||||
|
num = diff - num
|
||||||
|
start_verse = verse_ids[nextverse]
|
||||||
|
end_verse = 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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
seq_cur.execute(
|
||||||
|
"UPDATE reading_tracker SET verse = {}, last_used = '{}' WHERE uid = '{}'".format(
|
||||||
|
nextverse + num + 1, last_used, uid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
seq_conn.commit()
|
||||||
|
arg_view = request.args.get("view", None)
|
||||||
|
if arg_view == "plain" or arg_view == "rich":
|
||||||
|
response = "<br>".join(response_list)
|
||||||
|
else:
|
||||||
|
response = jsonify(response_list)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|||||||
BIN
Binary file not shown.
+35
-5
@@ -5,10 +5,14 @@
|
|||||||
<style>
|
<style>
|
||||||
html{
|
html{
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
font-size: 14pt;
|
||||||
width: 768px;
|
width: 768px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #547720
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<h1>KJV API</h1>
|
<h1>KJV API</h1>
|
||||||
<p>KJV accessible via web calls</p>
|
<p>KJV accessible via web calls</p>
|
||||||
@@ -38,17 +42,41 @@ API ENDPOINT SPECIFICATION v 0.3
|
|||||||
<a href="/search">/search</a> - returns a keyword search result set
|
<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="/show">/show</a> - returns a verse or set of verses by name/location
|
||||||
<a href="/define">/define</a> - 1828 Noah Webster dictionary search
|
<a href="/define">/define</a> - 1828 Noah Webster dictionary search
|
||||||
|
<a href="/seq">/seq</a> - Incremental sequence of Bible verses (per request)
|
||||||
|
|
||||||
URL PARAMETERS
|
URL PARAMETERS
|
||||||
==============
|
==============
|
||||||
?view=
|
view=
|
||||||
- json - json-encoded response
|
- json - json-encoded response
|
||||||
- plain - plaintext response
|
- plain - plaintext response
|
||||||
- rich - pretty response with css
|
- rich - pretty response with css
|
||||||
default: json
|
default: json
|
||||||
|
|
||||||
?kw=
|
kw=
|
||||||
- The keyword(s) of your query.
|
- Used with /search /show/ /define /find
|
||||||
|
- The keyword(s) of your query, separated by commas
|
||||||
|
-- ex ?kw=1cor5:1-5,2Peter2:5
|
||||||
|
|
||||||
|
getuid=
|
||||||
|
- Used only with /seq
|
||||||
|
- Set this to "true" to recieve a new UID for use with /seq?uid=
|
||||||
|
|
||||||
|
start=
|
||||||
|
- Used only with /seq
|
||||||
|
- You can use a ref like "1Cor5:1" to start there
|
||||||
|
instead of Genesis 1:1 as is the default
|
||||||
|
- Only used when creating a new UID
|
||||||
|
- If you want to "start in a new place" - just create a new UID
|
||||||
|
|
||||||
|
uid=
|
||||||
|
- Used only with /seq
|
||||||
|
- Required parameter for normal use of /seq
|
||||||
|
|
||||||
|
num=
|
||||||
|
- Used only with /seq
|
||||||
|
- You can get the "next # verses" in your sequence
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FEATURES
|
FEATURES
|
||||||
@@ -56,7 +84,6 @@ FEATURES
|
|||||||
- King James Only. (No heretical commentaries or false Bibles)
|
- King James Only. (No heretical commentaries or false Bibles)
|
||||||
- High quality source documents
|
- High quality source documents
|
||||||
- Free
|
- Free
|
||||||
-
|
|
||||||
- Developer hates the sodomites
|
- Developer hates the sodomites
|
||||||
|
|
||||||
INTERACTIVE ENDPOINTS
|
INTERACTIVE ENDPOINTS
|
||||||
@@ -77,6 +104,9 @@ 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
|
||||||
|
api.tld/define?kw=faith&view=rich
|
||||||
|
api.tld/seq?getuid=true&start=2peter2:1
|
||||||
|
api.tld/seq?uid=075a8496-9af7-4f44-84ec-ba3e7fa6de29&view=rich
|
||||||
|
|
||||||
INTEGRATION
|
INTEGRATION
|
||||||
===========
|
===========
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<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>Sequential Bible Viewer</h2>
|
||||||
|
<p>In order to use this, you first need a UID, which you
|
||||||
|
can get by querying /seq?getuid=true and optionally include
|
||||||
|
&start=BIBLEREF (where BIBLEREF is like 1Cor2:1) to set
|
||||||
|
a start position other than Genesis 1:1.</p>
|
||||||
|
|
||||||
|
<p>After you have your UID, this will keep track of where you
|
||||||
|
are at. Query /seq?uid=YOURUID and optionally include
|
||||||
|
&num=# (where # is an integer of how many verses you want to
|
||||||
|
include from your current position at a time)</p>
|
||||||
|
|
||||||
|
<p>The &view= argument works with this.<p>
|
||||||
|
|
||||||
|
<h4>Example Pleroma Bot shell script</h4>
|
||||||
|
<pre>
|
||||||
|
<code>
|
||||||
|
#!/bin/sh
|
||||||
|
verse=$(curl -XGET https://api.1611.social/seq?uid=MYUID?view=plain);
|
||||||
|
curl -XPOST -u biblebot:MYPASSWORD \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"status": "'"${verse}"'"}' \
|
||||||
|
https://my.fediverse.tld/api/v1/statuses;
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html{
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 14pt;
|
||||||
|
width: 768px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #547720
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user