123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- from typing import Callable, Iterable, Tuple
- from urllib.parse import urlencode
- from bottle import request, FormsDict, redirect, HTTPError
- from psycopg import Connection
- from psycopg.connection import TupleRow
- from .QueryCache import QueryCache, get_hash
- from ..data.filter import get_filter, get_query_param
- from . import BOOLEAN, PARAMS
- from .PageCache import PageCache
- from .hash_util import base32_to_hash, hash_to_base32, normalize_base32
- def normalize_query(query: FormsDict, allow: Iterable[str] = None) -> Tuple[str, str]:
- if query.hash:
- _hash = normalize_base32(query.hash)
- else:
- _hash = None
- allow = allow or PARAMS
- param = get_filter(query, allow=allow)
- norm = urlencode([
- (
- k, get_query_param(*param[k])
- ) if k != 'organic' else (
- "organic", BOOLEAN[BOOLEAN.get(query.organic, None)]
- ) for k in sorted(param) if param[k]
- ])
- return norm if not _hash or len(query.keys()) > 1 else None, _hash
- def _cache_decorator(func: Callable, query_cache: QueryCache = None, page_cache: PageCache = None):
- def wrap(*args, **kwargs):
- _, _, path, *_ = request.urlparts
-
- endpoint = path.split('/', 2)[-1]
- query, _hash = normalize_query(request.params)
- query = f"{endpoint}?{query}"
- if not _hash:
- _hashInt = get_hash(query)
- _hash = hash_to_base32(_hashInt)
- key = (query, _hashInt)
- else:
- key = (query, base32_to_hash(_hash))
-
- if request.params.reload == "true":
- page_cache.remove(key)
-
- cached = query_cache.get(key)
- if not cached:
- if query:
- cached = query_cache.add(key, None)
- else:
- return HTTPError(404, f"No query found for hash: {_hash}")
- if not request.params.hash:
- if cached and len(cached) > 2000:
- return redirect(f"{path}?hash={_hash}")
- if cached and f"{endpoint}?{request.query_string}" != cached:
- return redirect(cached)
- return func((cached, key[1]), page_cache, *args, **kwargs)
- return wrap
- def cache(*args, **kwargs):
- if not len(args):
- return lambda f: _cache_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")
|