diff options
| author | Terry Truong <terry06890@gmail.com> | 2022-04-26 13:53:46 +1000 |
|---|---|---|
| committer | Terry Truong <terry06890@gmail.com> | 2022-04-26 13:53:46 +1000 |
| commit | 04e9444746d3ba8ddcc96d0fd16f1c02adce1389 (patch) | |
| tree | 0f48275709dc676bf8b9ba3c2dbc9f1beeff7b75 /backend/server.py | |
| parent | e8fab23fe92230c2cb42412bb9ea6040ff14f072 (diff) | |
Have tol data in sqlite db, and add server script that accesses it
Adapt otol-data-converting script to generate otol.db, add server.py
script that provides access to that db, and adapt the app to query the
server for tol data when needed.
Diffstat (limited to 'backend/server.py')
| -rwxr-xr-x | backend/server.py | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/backend/server.py b/backend/server.py new file mode 100755 index 0000000..2ff7e74 --- /dev/null +++ b/backend/server.py @@ -0,0 +1,142 @@ +#!/usr/bin/python3 + +import sys, re, sqlite3, json +from http.server import HTTPServer, BaseHTTPRequestHandler +import urllib.parse + +hostname = "localhost" +port = 8000 +dbFile = "data/otol.db" +tolnodeReqDepth = 2 + # For a /tolnode/name1 request, respond with name1's node, and descendent nodes in a subtree to some depth + # A depth of 0 means only respond with one node + +usageInfo = f"usage: {sys.argv[0]}\n" +usageInfo += "Starts a server that listens for GET requests to " + hostname + ":" + str(port) + "/tolnode/name1,\n" +usageInfo += "and responds with JSON representing an object mapping names to node objects.\n" +usageInfo += "Normally, the response includes node name1, and child nodes up to depth " + str(tolnodeReqDepth) + ".\n" +usageInfo += "\n" +usageInfo += "A query section ?type=type1 can be used to affect the result.\n" +usageInfo += "If type1 is 'children', the operation acts on node name1's children, and the results combined.\n" +usageInfo += "If type1 is 'chain', the response includes nodes from name1 up to the root, and their direct children.\n" + +dbCon = sqlite3.connect(dbFile) +def lookupName(name): + cur = dbCon.cursor() + cur.execute("SELECT name, data FROM nodes WHERE name = ?", (name,)) + row = cur.fetchone() + return row[1] if row != None else None +#def lookupNameLike(name): +# cur = dbCon.cursor() +# found = False +# cur.execute("SELECT name, data FROM nodes WHERE name like ?", ("%{}%".format(name),)) +# rows = cur.fetchall() +# if len(rows) == 0: +# return None +# else: +# jsonStr = "{" +# for i in range(len(rows)): +# jsonStr += json.dumps(rows[i][0]) + ":" + rows[i][1] +# if i < len(njList) - 1: +# jsonStr += "," +# jsonStr += "}" +# return jsonFromNameJsonList(rows) + +class DbServer(BaseHTTPRequestHandler): + def do_GET(self): + # Parse URL + urlParts = urllib.parse.urlparse(self.path) + path = urllib.parse.unquote(urlParts.path) + queryDict = urllib.parse.parse_qs(urlParts.query) + # Check first element of path + match = re.match(r"/([^/]+)/(.+)", path) + if match != None: + reqType = match.group(1) + if reqType == "tolnode": + name = match.group(2) + # Check query string + if "type" not in queryDict: + nodeJson = lookupName(name) + if nodeJson != None: + results = [] + getResultsUntilDepth(name, nodeJson, tolnodeReqDepth, results) + self.respondJson(nodeResultsToJSON(results)) + return + elif queryDict["type"][0] == "children": + nodeJson = lookupName(name) + if nodeJson != None: + obj = json.loads(nodeJson) + results = [] + for childName in obj["children"]: + nodeJson = lookupName(childName) + if nodeJson != None: + getResultsUntilDepth(childName, nodeJson, tolnodeReqDepth, results) + self.respondJson(nodeResultsToJSON(results)) + return + elif queryDict["type"][0] == "chain": + results = [] + ranOnce = False + while True: + jsonResult = lookupName(name) + if jsonResult == None: + if ranOnce: + print("ERROR: Parent-chain node {} not found".format(name), file=sys.stderr) + break + results.append([name, jsonResult]) + obj = json.loads(jsonResult) + # Add children + if not ranOnce: + ranOnce = True + else: + internalFail = False + for childName in obj["children"]: + jsonResult = lookupName(childName) + if jsonResult == None: + print("ERROR: Parent-chain-child node {} not found".format(name), file=sys.stderr) + internalFail = True + break + results.append([childName, jsonResult]) + if internalFail: + break + # Check if root + if obj["parent"] == None: + self.respondJson(nodeResultsToJSON(results)) + return + else: + name = obj["parent"] + self.send_response(404) + self.end_headers() + self.end_headers() + def respondJson(self, jsonStr): + self.send_response(200) + self.send_header("Content-type", "application/json") + self.end_headers() + self.wfile.write(jsonStr.encode("utf-8")) +def getResultsUntilDepth(name, nodeJson, depth, results): + """Given a node [name, nodeJson] pair, adds child node pairs to 'results', up until 'depth'""" + results.append([name, nodeJson]) + if depth > 0: + obj = json.loads(nodeJson) + for childName in obj["children"]: + childJson = lookupName(childName) + if childJson != None: + getResultsUntilDepth(childName, childJson, depth-1, results) +def nodeResultsToJSON(results): + """Given a list of [name, nodeJson] pairs, returns a representative JSON string""" + jsonStr = "{" + for i in range(len(results)): + jsonStr += json.dumps(results[i][0]) + ":" + results[i][1] + if i < len(results) - 1: + jsonStr += "," + jsonStr += "}" + return jsonStr + +server = HTTPServer((hostname, port), DbServer) +print("Server started at http://{}:{}".format(hostname, port)) +try: + server.serve_forever() +except KeyboardInterrupt: + pass +server.server_close() +dbCon.close() +print("Server stopped") |
