aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--backend/README.md9
-rwxr-xr-xbackend/server.py18
-rwxr-xr-xbackend/tilo.py (renamed from backend/cgi-bin/data.py)67
-rw-r--r--src/App.vue2
-rw-r--r--src/lib.ts2
-rw-r--r--vite.config.js2
7 files changed, 57 insertions, 44 deletions
diff --git a/.gitignore b/.gitignore
index 99bd1e5..1708166 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,4 @@
/backend/data/pickedEnwikiLabels.txt
/backend/data/pickedNodes.txt
/backend/data/pickedNames.txt
+/backend/__pycache__
diff --git a/backend/README.md b/backend/README.md
index 3a56f76..358ed72 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -1,8 +1,9 @@
# Files
-- cgi-bin/data.py: CGI script for providing tree-of-life data to client
+- server.py: Basic dev server that serves a WSGI script
+- tilo.py: WSGI script that serves tree-of-life data
- data: Contains scripts for generating the tree-of-life database
# During development
-Having generated the database as data/data.db, running `python3 -m http.server --cgi 8000`,
-in this directory, allows the client to access the CGI script, and hence the database,
-via `localhost:8000/cgi-bin/data.py`.
+Having generated the database as data/data.db, running `server.py`,
+in this directory, allows the client to access data from `tilo.py`,
+via `localhost:8000`.
diff --git a/backend/server.py b/backend/server.py
new file mode 100755
index 0000000..a00ab7f
--- /dev/null
+++ b/backend/server.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python3
+
+import sys
+from wsgiref.simple_server import make_server
+from tilo import application
+
+usageInfo = f"""
+Usage: {sys.argv[0]}
+
+Runs a basic dev server that serves a WSGI script
+"""
+if len(sys.argv) > 1:
+ print(usageInfo, file=sys.stderr)
+ sys.exit(1)
+
+with make_server('', 8000, application) as httpd:
+ print("Serving HTTP on port 8000...")
+ httpd.serve_forever()
diff --git a/backend/cgi-bin/data.py b/backend/tilo.py
index c1ea181..2f49baa 100755
--- a/backend/cgi-bin/data.py
+++ b/backend/tilo.py
@@ -13,9 +13,9 @@ ROOT_NAME = "cellular organisms"
usageInfo = f"""
Usage: {sys.argv[0]}
-CGI script that provides tree-of-life data, in JSON form.
+WSGI script that serves tree-of-life data, in JSON form.
-Query parameters:
+Expected HTTP query parameters:
- name: Provides a name, or partial name, of a tree-of-life node. If absent, the root node is used.
- type: Specifies what data to reply with.
If 'node', reply with a name-to-TolNode map, describing the named node and it's children.
@@ -219,22 +219,12 @@ def lookupInfo(name, tree, dbCur):
) if n != None else None for n in [name] + subNames
]
return InfoResponse(nodeInfoObjs[0], nodeInfoObjs[1:])
+def getTableSuffix(tree):
+ return "t" if tree == "trimmed" else "i" if tree == "images" else "p"
-# For handling request
-def respondJson(val):
- content = jsonpickle.encode(val, unpicklable=False).encode("utf-8")
- print("Content-type: application/json")
- if "HTTP_ACCEPT_ENCODING" in os.environ and "gzip" in os.environ["HTTP_ACCEPT_ENCODING"]:
- if len(content) > 100:
- content = gzip.compress(content, compresslevel=5)
- print(f"Content-length: {len(content)}")
- print(f"Content-encoding: gzip")
- print()
- sys.stdout.flush()
- sys.stdout.buffer.write(content)
-def handleReq(dbCur):
+def handleReq(dbCur, environ):
# Get query params
- queryStr = os.environ["QUERY_STRING"] if "QUERY_STRING" in os.environ else ""
+ queryStr = environ["QUERY_STRING"] if "QUERY_STRING" in environ else ""
queryDict = urllib.parse.parse_qs(queryStr)
# Set vars from params
name = queryDict["name"][0] if "name" in queryDict else None
@@ -246,8 +236,7 @@ def handleReq(dbCur):
tree = queryDict["tree"][0] if "tree" in queryDict else "images"
# Check for valid 'tree'
if tree != None and re.fullmatch(r"trimmed|images|picked", tree) == None:
- respondJson(None)
- return
+ return None
# Get data of requested type
if reqType == "node":
toroot = queryDict["toroot"][0] if "toroot" in queryDict else None
@@ -257,8 +246,7 @@ def handleReq(dbCur):
tolNode = tolNodes[name]
childNodeObjs = lookupNodes(tolNode.children, tree, dbCur)
childNodeObjs[name] = tolNode
- respondJson(childNodeObjs)
- return
+ return childNodeObjs
else:
# Get ancestors to skip inclusion of
nodesToSkip = set()
@@ -279,8 +267,7 @@ def handleReq(dbCur):
tolNodes = lookupNodes([name], tree, dbCur)
if len(tolNodes) == 0:
if not ranOnce:
- respondJson(results)
- return
+ return results
print(f"ERROR: Parent-chain node {name} not found", file=sys.stderr)
break
tolNode = tolNodes[name]
@@ -297,8 +284,7 @@ def handleReq(dbCur):
results.update(childNodeObjs)
# Check if root
if tolNode.parent == None or tolNode.parent in nodesToSkip:
- respondJson(results)
- return
+ return results
else:
name = tolNode.parent
elif reqType == "sugg":
@@ -314,20 +300,27 @@ def handleReq(dbCur):
print(f"INFO: Invalid limit {suggLimit}", file=sys.stderr)
# Get search suggestions
if not invalidLimit:
- respondJson(lookupSuggs(name, suggLimit, tree, dbCur))
- return
+ return lookupSuggs(name, suggLimit, tree, dbCur)
elif reqType == "info":
infoResponse = lookupInfo(name, tree, dbCur)
if infoResponse != None:
- respondJson(infoResponse)
- return
+ return infoResponse
# On failure, provide empty response
- respondJson(None)
-def getTableSuffix(tree):
- return "t" if tree == "trimmed" else "i" if tree == "images" else "p"
-
-# Open db
-dbCon = sqlite3.connect(dbFile)
-dbCur = dbCon.cursor()
-# Handle request
-handleReq(dbCur)
+ return None
+def application(environ, start_response):
+ global dbFile
+ # Open db
+ dbCon = sqlite3.connect(dbFile)
+ dbCur = dbCon.cursor()
+ # Get response object
+ val = handleReq(dbCur, environ)
+ # Construct response
+ data = jsonpickle.encode(val, unpicklable=False).encode()
+ headers = [("Content-type", "application/json")]
+ if "HTTP_ACCEPT_ENCODING" in environ and "gzip" in environ["HTTP_ACCEPT_ENCODING"]:
+ if len(data) > 100:
+ data = gzip.compress(data, compresslevel=5)
+ headers.append(("Content-encoding", "gzip"))
+ headers.append(('Content-Length', str(len(data))))
+ start_response('200 OK', headers)
+ return [data]
diff --git a/src/App.vue b/src/App.vue
index e379a1d..77de7ca 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -780,7 +780,7 @@ export default defineComponent({
primeLoadInd(msg: string){
this.pendingLoadingRevealHdlr = setTimeout(() => {
this.loadingMsg = msg;
- }, 300);
+ }, 500);
},
endLoadInd(){
clearTimeout(this.pendingLoadingRevealHdlr);
diff --git a/src/lib.ts b/src/lib.ts
index d737879..0422389 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -7,7 +7,7 @@ import {LayoutOptions} from './layout';
import {getBreakpoint, Breakpoint, getScrollBarWidth, onTouchDevice} from './util';
// For server requests
-const SERVER_URL = window.location.href + 'cgi-bin/data.py'
+const SERVER_URL = window.location.href + 'data'
export async function queryServer(params: URLSearchParams){
// Construct URL
let url = new URL(SERVER_URL);
diff --git a/vite.config.js b/vite.config.js
index ebcdcd3..900daa2 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -6,7 +6,7 @@ export default defineConfig({
plugins: [vue()],
server: {
proxy: {
- '/cgi-bin/data.py': 'http://localhost:8000'
+ '/data': 'http://localhost:8000'
},
watch: {
ignored: ['**/backend', '**/public']