|
@@ -5,27 +5,20 @@
|
|
|
|
|
|
from typing import Iterable, Dict
|
|
|
import os
|
|
|
-from time import time
|
|
|
from urllib.parse import urlencode
|
|
|
from bottle import (
|
|
|
route,
|
|
|
request,
|
|
|
- run,
|
|
|
response,
|
|
|
FormsDict,
|
|
|
redirect,
|
|
|
template,
|
|
|
static_file,
|
|
|
- TEMPLATE_PATH,
|
|
|
)
|
|
|
-import matplotlib
|
|
|
from psycopg import connect
|
|
|
from psycopg.sql import SQL, Literal
|
|
|
-import seaborn as sns
|
|
|
from threading import Lock, Thread
|
|
|
|
|
|
-from queue import Queue, Empty
|
|
|
-
|
|
|
from ..data.filter import(
|
|
|
get_filter,
|
|
|
get_query_param,
|
|
@@ -38,10 +31,6 @@ from . import trend as worker
|
|
|
from . import PARAMS
|
|
|
from .CachedLoadingPage import CachedLoadingPage
|
|
|
|
|
|
-matplotlib.use('agg')
|
|
|
-
|
|
|
-CACHE: Dict[str, CachedLoadingPage] = dict()
|
|
|
-
|
|
|
host = f"host={os.getenv('HOST')}"
|
|
|
db = f"dbname={os.getenv('DB', 'grocery')}"
|
|
|
user = f"user={os.getenv('USER', 'das')}"
|
|
@@ -49,7 +38,20 @@ password = f"password={os.getenv('PASSWORD','')}"
|
|
|
if not password.split('=',1)[1]:
|
|
|
password = ''
|
|
|
conn = connect(f"{host} {db} {user} {password}")
|
|
|
-sns.set_theme(style='darkgrid', palette='pastel')
|
|
|
+
|
|
|
+CACHE: Dict[str, CachedLoadingPage] = dict()
|
|
|
+
|
|
|
+def enforce_limit(cache, limit):
|
|
|
+ for idx, (_, k) in enumerate(sorted([
|
|
|
+ (v.age, k) for k, v in cache.items()
|
|
|
+ ])):
|
|
|
+ if idx > limit: del cache[k]
|
|
|
+
|
|
|
+
|
|
|
+def clear_stale(cache):
|
|
|
+ for k in [k for k, v in cache.items() if v.stale]:
|
|
|
+ del cache[k]
|
|
|
+
|
|
|
|
|
|
def get_product_rollup_statement(filters, having=None):
|
|
|
where = [ get_where_include_exclude(
|
|
@@ -92,7 +94,6 @@ def normalize_query(query: FormsDict, allow: Iterable[str] = None) -> str:
|
|
|
def send_static(filename):
|
|
|
return static_file(filename, root='app/rest/static')
|
|
|
|
|
|
-
|
|
|
global LOCK
|
|
|
LOCK = Lock()
|
|
|
|
|
@@ -110,38 +111,19 @@ def trend():
|
|
|
if request.query_string in CACHE:
|
|
|
page = CACHE[request.query_string]
|
|
|
if not page.stale:
|
|
|
- if not page.loaded:
|
|
|
- return page.update()
|
|
|
- return page.value
|
|
|
- else:
|
|
|
- del CACHE[request.query_string]
|
|
|
-
|
|
|
- if LOCK.acquire(blocking=True):
|
|
|
- if request.query_string in CACHE:
|
|
|
- page = CACHE[request.query_string]
|
|
|
- if not page.stale:
|
|
|
- LOCK.release()
|
|
|
- if not page.loaded:
|
|
|
- return page.update()
|
|
|
- return page.value
|
|
|
-
|
|
|
- try:
|
|
|
- page = CachedLoadingPage(template("loading", progress=[]))
|
|
|
- for k in [k for k, v in CACHE.items() if v.stale]:
|
|
|
- del CACHE[k]
|
|
|
-
|
|
|
- for idx, (_, k) in enumerate(sorted([
|
|
|
- (v.age, k) for k, v in CACHE.items()
|
|
|
- ])):
|
|
|
- if idx > 10: del CACHE[k]
|
|
|
-
|
|
|
- CACHE[request.query_string] = page
|
|
|
- thread = Thread(target=worker.trend, args=(page.queue, conn, path, request.query))
|
|
|
- thread.start()
|
|
|
- return page.value
|
|
|
- finally:
|
|
|
- LOCK.release()
|
|
|
-
|
|
|
+ return page.value if page.loaded else page.update()
|
|
|
+ del CACHE[request.query_string]
|
|
|
+
|
|
|
+ page = CachedLoadingPage(
|
|
|
+ template("loading", progress=[]),
|
|
|
+ lambda queue: Thread(target=worker.trend, args=(
|
|
|
+ queue, conn, path, request.query
|
|
|
+ )).start()
|
|
|
+ )
|
|
|
+ clear_stale(CACHE)
|
|
|
+ enforce_limit(CACHE, 10)
|
|
|
+ CACHE[request.query_string] = page
|
|
|
+ return page.value
|
|
|
|
|
|
@route('/grocery/groups')
|
|
|
def groups():
|