|
@@ -25,6 +25,7 @@ from psycopg import connect
|
|
|
from psycopg.sql import SQL, Literal
|
|
|
import matplotlib.pyplot as plt
|
|
|
import seaborn as sns
|
|
|
+from multiprocessing import Lock
|
|
|
from ..activities.Plot import (
|
|
|
get_data,
|
|
|
)
|
|
@@ -33,6 +34,8 @@ from ..data.util import(
|
|
|
get_include_exclude,
|
|
|
get_where_include_exclude
|
|
|
)
|
|
|
+import matplotlib
|
|
|
+matplotlib.use('agg')
|
|
|
|
|
|
def line(pivot, ylabel=None, xlabel=None):
|
|
|
ax = sns.lineplot(data=pivot, markers=True)
|
|
@@ -178,6 +181,11 @@ def get_form(action, method, filter_data, data):
|
|
|
def send_static(filename):
|
|
|
return static_file(filename, root='app/rest/static')
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+global LOCK
|
|
|
+LOCK = Lock()
|
|
|
+
|
|
|
@route('/grocery/trend')
|
|
|
def trend():
|
|
|
_, _, path, *_ = request.urlparts
|
|
@@ -186,12 +194,33 @@ def trend():
|
|
|
if request.query_string != normalized:
|
|
|
return redirect(f'{path}?{normalized}')
|
|
|
|
|
|
+ loading = template("app/rest/loading", progress=[])
|
|
|
if request.query_string in CACHE:
|
|
|
- return next(CACHE[request.query_string], None)
|
|
|
-
|
|
|
- CACHE[request.query_string] = trend_internal(path, request.query)
|
|
|
+ if LOCK.acquire(block=False):
|
|
|
+ try:
|
|
|
+ return CACHE[request.query_string]["state"]
|
|
|
+ finally:
|
|
|
+ try:
|
|
|
+ CACHE[request.query_string]["state"] = next(CACHE[request.query_string]["iter"])
|
|
|
+ except StopIteration:
|
|
|
+ del CACHE[request.query_string]
|
|
|
+ finally:
|
|
|
+ LOCK.release()
|
|
|
+ else:
|
|
|
+ return CACHE[request.query_string]["state"]
|
|
|
|
|
|
- return template("app/rest/loading", progress=[])
|
|
|
+ if LOCK.acquire(block=False):
|
|
|
+ if request.query_string in CACHE:
|
|
|
+ LOCK.release()
|
|
|
+ return CACHE[request.query_string]["state"]
|
|
|
+ try:
|
|
|
+ CACHE[request.query_string] = {
|
|
|
+ "iter": trend_internal(path, request.query),
|
|
|
+ "state": loading,
|
|
|
+ }
|
|
|
+ finally:
|
|
|
+ LOCK.release()
|
|
|
+ return loading
|
|
|
|
|
|
def trend_internal(path, query):
|
|
|
progress = []
|
|
@@ -206,7 +235,9 @@ def trend_internal(path, query):
|
|
|
progress.append({ "name": "Loading data", "status": ""})
|
|
|
yield template("app/rest/loading", progress=progress)
|
|
|
data = get_data(query_manager, **fields)
|
|
|
+
|
|
|
progress[-1]["status"] = "done"
|
|
|
+ yield template("app/rest/loading", progress=progress)
|
|
|
|
|
|
if data.empty:
|
|
|
raise abort(404, f"No data for {fields}")
|
|
@@ -217,9 +248,21 @@ def trend_internal(path, query):
|
|
|
pivot.columns = pivot.columns.droplevel()
|
|
|
plt.figure(figsize=[16, 9])
|
|
|
line(pivot, xlabel='Time', ylabel=f'$ / {unit}')
|
|
|
+
|
|
|
+ progress[-1]["status"] = "done"
|
|
|
+ yield template("app/rest/loading", progress=progress)
|
|
|
+
|
|
|
+ progress.append({ "name": "Rendering chart", "status": ""})
|
|
|
+ yield template("app/rest/loading", progress=progress)
|
|
|
+
|
|
|
f = StringIO()
|
|
|
plt.savefig(f, format='svg')
|
|
|
form = get_form(path, 'get', get_filter(request.query, allow=PARAMS), data)
|
|
|
+
|
|
|
+ progress[-1]["status"] = "done"
|
|
|
+ yield template("app/rest/loading", progress=progress)
|
|
|
+
|
|
|
+ final = template("app/rest/trend", form=form, svg=f.getvalue())
|
|
|
resp = lambda: template("app/rest/trend", form=form, svg=f.getvalue())
|
|
|
|
|
|
except HTTPError as e:
|
|
@@ -347,4 +390,4 @@ SELECT query_to_xml_and_xmlschema({inner}, false, false, ''::text)
|
|
|
xml=xml
|
|
|
)
|
|
|
|
|
|
-run(host='0.0.0.0', port=6772)
|
|
|
+run(host='0.0.0.0', port=6772, server='gunicorn')
|