Преглед изворни кода

move route decorators into separate file

Daniel Sheffield пре 1 година
родитељ
комит
f32bf36602
2 измењених фајлова са 84 додато и 66 уклоњено
  1. 14 66
      app/rest/pyapi.py
  2. 70 0
      app/rest/route_decorators.py

+ 14 - 66
app/rest/pyapi.py

@@ -3,31 +3,16 @@
 # All rights reserved
 #
 # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
-from typing import Iterable
 import os
-from urllib.parse import urlencode
-from bottle import (
-    route,
-    request,
-    response,
-    FormsDict,
-    redirect,
-    template,
-    static_file,
-)
+from bottle import route, request, response, template, static_file
 from psycopg import connect
 from threading import Thread
 
+from .route_decorators import normalize, poison, cursor
 from .query_to_xml import get_categories, get_groups, get_products, get_tags
-
-from ..data.filter import(
-    get_filter,
-    get_query_param,
-)
-from . import trend as worker
-from . import PARAMS
 from .CachedLoadingPage import CachedLoadingPage
 from .Cache import Cache
+from . import trend as worker
 
 host = f"host={os.getenv('HOST')}"
 db = f"dbname={os.getenv('DB', 'grocery')}"
@@ -39,66 +24,28 @@ conn = connect(f"{host} {db} {user} {password}")
 
 CACHE = Cache(10)
 
-def normalize_query(query: FormsDict, allow: Iterable[str] = None) -> str:
-    param = get_filter(query, allow=allow)
-    return urlencode([
-        (k, get_query_param(*param[k])) for k in sorted(param) if param[k]
-    ])
-
-def _normalize_decorator(func, poison_on_reload=False):
-    def wrap(*args, **kwargs):
-        _, _, path, *_ = request.urlparts
-        normalized = normalize_query(request.query, allow=PARAMS)
-        if poison_on_reload and request.params.get('reload') == 'true':
-            CACHE.remove(normalized)
-        if request.query_string != normalized:
-            return redirect(f'{path}?{normalized}')
-        return func(*args, **kwargs)
-    return wrap
-
-def normalize(*args, **kwargs):
-    if not len(args):
-        return lambda f: _normalize_decorator(f, **kwargs)
-    
-    return _normalize_decorator(*args)
-
-def cursor(func):
-    def wrap(*args, **kwargs):
-        try:
-            with conn.cursor() as cur:
-                return func(cur, *args, **kwargs)
-        finally:
-            conn.commit()
-    return wrap
-
-
 @route('/grocery/static/<filename:path>')
 def send_static(filename):
     return static_file(filename, root='app/rest/static')
 
+
 @route('/grocery/trend')
-@normalize(poison_on_reload=True)
+@poison(cache=CACHE)
+@normalize
 def trend():
+    page = CACHE.get(request.query_string)
     _, _, path, *_ = request.urlparts
-    normalized = normalize_query(request.query, allow=PARAMS)
-    if request.params.get('reload') == 'true':
-        CACHE.remove(normalized)
-
-    if request.query_string != normalized:
-        return redirect(f'{path}?{normalized}')
-    
-    page = CACHE.get(normalized)
-
-    return page if page else CACHE.add(normalized, CachedLoadingPage(
+    return page if page else CACHE.add(request.query_string, CachedLoadingPage(
         template("loading", progress=[]),
         lambda queue: Thread(target=worker.trend, args=(
             queue, conn, path, request.query
         )).start()
     ))
 
+
 @route('/grocery/groups')
 @normalize
-@cursor
+@cursor(connection=conn)
 def groups(cur):
     response.content_type = 'application/xhtml+xml; charset=utf-8'
     return get_groups(cur, request.query)
@@ -106,14 +53,15 @@ def groups(cur):
 
 @route('/grocery/categories')
 @normalize
-@cursor
+@cursor(connection=conn)
 def categories(cur):
     response.content_type = 'application/xhtml+xml; charset=utf-8'
     return get_categories(cur, request.query)
 
+
 @route('/grocery/products')
 @normalize
-@cursor
+@cursor(connection=conn)
 def products(cur):
     response.content_type = 'application/xhtml+xml; charset=utf-8'
     return get_products(cur, request.query)
@@ -121,7 +69,7 @@ def products(cur):
 
 @route('/grocery/tags')
 @normalize
-@cursor
+@cursor(connection=conn)
 def tags(cur):
     response.content_type = 'application/xhtml+xml; charset=utf-8'
     return get_tags(cur, request.query)

+ 70 - 0
app/rest/route_decorators.py

@@ -0,0 +1,70 @@
+#
+# Copyright (c) Daniel Sheffield 2023
+# All rights reserved
+#
+# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
+from bottle import request, FormsDict, redirect
+from typing import Callable, Iterable
+from urllib.parse import urlencode
+from psycopg import Connection
+from psycopg.connection import TupleRow
+
+from ..data.filter import get_filter, get_query_param
+from . import PARAMS
+from .Cache import Cache
+
+
+def normalize_query(query: FormsDict, allow: Iterable[str] = None) -> str:
+    param = get_filter(query, allow=allow)
+    return urlencode([
+        (k, get_query_param(*param[k])) for k in sorted(param) if param[k]
+    ])
+
+
+def _normalize_decorator(func: Callable, poison_on_reload: bool = False):
+    def wrap(*args, **kwargs):
+        _, _, path, *_ = request.urlparts
+        normalized = normalize_query(request.query, allow=PARAMS)
+        if request.query_string != normalized:
+            return redirect(f'{path}?{normalized}')
+        return func(*args, **kwargs)
+    return wrap
+
+
+def normalize(*args, **kwargs):
+    if not len(args):
+        return lambda f: _normalize_decorator(f, **kwargs)
+    
+    return _normalize_decorator(*args)
+
+
+def _poison_decorator(func: Callable, cache: Cache = None):
+    def wrap(*args, **kwargs):
+        normalized = normalize_query(request.query, allow=PARAMS)
+        if request.params.get('reload') == 'true':
+            cache.remove(normalized)
+        return func(*args, **kwargs)
+    return wrap
+
+
+def poison(*args, **kwargs):
+    if not len(args):
+        return lambda f: _poison_decorator(f, **kwargs)
+    raise Exception("decorator argument required")
+
+
+def _cursor_decorator(func: Callable, connection: Connection[TupleRow] = None):
+    def wrap(*args, **kwargs):
+        try:
+            with connection.cursor() as cur:
+                return func(cur, *args, **kwargs)
+        finally:
+            connection.commit()
+    return wrap
+
+
+def cursor(*args, **kwargs):
+    if not len(args):
+        return lambda f: _cursor_decorator(f, **kwargs)
+    raise Exception("decorator argument required")
+