|
@@ -16,42 +16,51 @@ from psycopg.sql import (
|
|
)
|
|
)
|
|
from .util import get_select, get_from
|
|
from .util import get_select, get_from
|
|
|
|
|
|
|
|
+def get_window(unit, organic):
|
|
|
|
+ window = SQL(f"""(
|
|
|
|
+PARTITION BY {'organic,' if organic is not None else ''} product_id
|
|
|
|
+ORDER BY convert_unit(units.name, {{unit}}, products.name) NULLS FIRST, ts
|
|
|
|
+ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
|
|
|
+)""").format(unit=Literal(unit))
|
|
|
|
+
|
|
|
|
+ return window
|
|
|
|
+
|
|
def get_selectors(
|
|
def get_selectors(
|
|
unit: str,
|
|
unit: str,
|
|
- product: str,
|
|
|
|
- window: SQL
|
|
|
|
|
|
+ organic: str
|
|
) -> OrderedDict[Tuple[str, Composable]]:
|
|
) -> OrderedDict[Tuple[str, Composable]]:
|
|
|
|
+ window = get_window(unit, organic)
|
|
return OrderedDict([
|
|
return OrderedDict([
|
|
('id', Identifier('transactions', 'id')),
|
|
('id', Identifier('transactions', 'id')),
|
|
('ts_raw', SQL("""(transactions.ts AT TIME ZONE 'UTC')::timestamp without time zone""")),
|
|
('ts_raw', SQL("""(transactions.ts AT TIME ZONE 'UTC')::timestamp without time zone""")),
|
|
('%d/%m/%y %_I%P', SQL("""(transactions.ts AT TIME ZONE 'UTC')::timestamp without time zone""")),
|
|
('%d/%m/%y %_I%P', SQL("""(transactions.ts AT TIME ZONE 'UTC')::timestamp without time zone""")),
|
|
('code', Identifier('stores', 'code')),
|
|
('code', Identifier('stores', 'code')),
|
|
('$/unit', SQL("""TRUNC(
|
|
('$/unit', SQL("""TRUNC(
|
|
- price / quantity / convert_unit(units.name, {unit}, {product}), 4
|
|
|
|
- )""").format(unit=Literal(unit), product=Literal(product))),
|
|
|
|
|
|
+ price / quantity / convert_unit(units.name, {unit}, products.name), 4
|
|
|
|
+ )""").format(unit=Literal(unit))),
|
|
('last', SQL("""TRUNC(last_value(
|
|
('last', SQL("""TRUNC(last_value(
|
|
- price / quantity / convert_unit(units.name, {unit}, {product})
|
|
|
|
|
|
+ price / quantity / convert_unit(units.name, {unit}, products.name)
|
|
) OVER {window}, 4)
|
|
) OVER {window}, 4)
|
|
-""").format(unit=Literal(unit), product=Literal(product), window=window)),
|
|
|
|
|
|
+""").format(unit=Literal(unit), window=window)),
|
|
('avg', SQL("""TRUNC(sum(CASE
|
|
('avg', SQL("""TRUNC(sum(CASE
|
|
- WHEN convert_unit(units.name, {unit}, {product}) IS NOT NULL THEN price
|
|
|
|
|
|
+ WHEN convert_unit(units.name, {unit}, products.name) IS NOT NULL THEN price
|
|
ELSE NULL
|
|
ELSE NULL
|
|
END) OVER {window} / sum(
|
|
END) OVER {window} / sum(
|
|
- quantity * convert_unit(units.name, {unit}, {product})
|
|
|
|
|
|
+ quantity * convert_unit(units.name, {unit}, products.name)
|
|
) OVER {window}, 4)
|
|
) OVER {window}, 4)
|
|
-""").format(unit=Literal(unit), product=Literal(product), window=window)),
|
|
|
|
|
|
+""").format(unit=Literal(unit), window=window)),
|
|
('min', SQL("""TRUNC(min(
|
|
('min', SQL("""TRUNC(min(
|
|
- price / quantity / convert_unit(units.name, {unit}, {product})
|
|
|
|
|
|
+ price / quantity / convert_unit(units.name, {unit}, products.name)
|
|
) OVER {window}, 4)
|
|
) OVER {window}, 4)
|
|
-""").format(unit=Literal(unit), product=Literal(product), window=window)),
|
|
|
|
|
|
+""").format(unit=Literal(unit), window=window)),
|
|
('max', SQL("""TRUNC(max(
|
|
('max', SQL("""TRUNC(max(
|
|
- price / quantity / convert_unit(units.name, {unit}, {product})
|
|
|
|
|
|
+ price / quantity / convert_unit(units.name, {unit}, products.name)
|
|
) OVER {window}, 4)
|
|
) OVER {window}, 4)
|
|
-""").format(unit=Literal(unit), product=Literal(product), window=window)),
|
|
|
|
|
|
+""").format(unit=Literal(unit), window=window)),
|
|
('price', SQL("""TRUNC(price, 4)""")),
|
|
('price', SQL("""TRUNC(price, 4)""")),
|
|
('quantity', SQL("""TRUNC(
|
|
('quantity', SQL("""TRUNC(
|
|
- quantity * convert_unit(units.name, {unit}, {product}), 4
|
|
|
|
-)""").format(unit=Literal(unit), product=Literal(product))),
|
|
|
|
|
|
+ quantity * convert_unit(units.name, {unit}, products.name), 4
|
|
|
|
+)""").format(unit=Literal(unit))),
|
|
('product', Identifier('products', 'name')),
|
|
('product', Identifier('products', 'name')),
|
|
('category', Identifier('categories', 'name')),
|
|
('category', Identifier('categories', 'name')),
|
|
('group', Identifier('groups', 'name')),
|
|
('group', Identifier('groups', 'name')),
|
|
@@ -106,11 +115,15 @@ def get_where(product=None, category=None, group=None, organic=None, limit='90 d
|
|
]) if where else SQL('')
|
|
]) if where else SQL('')
|
|
|
|
|
|
def get_historic_prices_statement(unit, sort=None, product=None, category=None, group=None, organic=None, limit='90 days'):
|
|
def get_historic_prices_statement(unit, sort=None, product=None, category=None, group=None, organic=None, limit='90 days'):
|
|
- window = SQL(f"""(
|
|
|
|
-PARTITION BY {'organic,' if organic is not None else ''} product_id
|
|
|
|
-ORDER BY convert_unit(units.name, {{unit}}, {{product}}) NULLS FIRST, ts
|
|
|
|
-ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
|
|
|
-)""").format(unit=Literal(unit), product=Literal(product))
|
|
|
|
|
|
+
|
|
|
|
+ return SQL('\n').join([
|
|
|
|
+ get_select(get_selectors(unit, organic)),
|
|
|
|
+ get_from("transactions", JOINS),
|
|
|
|
+ get_where(product=product, category=category, group=group, organic=organic, limit=limit),
|
|
|
|
+ get_sort(sort, organic),
|
|
|
|
+ ])
|
|
|
|
+
|
|
|
|
+def get_sort(sort, organic):
|
|
organic_sort = f"{'organic,' if organic is not None else ''}"
|
|
organic_sort = f"{'organic,' if organic is not None else ''}"
|
|
sort_sql = SQL('').join([
|
|
sort_sql = SQL('').join([
|
|
SQL('{sort} {direction},').format(
|
|
SQL('{sort} {direction},').format(
|
|
@@ -118,16 +131,9 @@ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
|
direction=SQL('DESC' if sort == 'ts' else 'ASC')
|
|
direction=SQL('DESC' if sort == 'ts' else 'ASC')
|
|
),
|
|
),
|
|
]) if sort is not None else SQL('')
|
|
]) if sort is not None else SQL('')
|
|
-
|
|
|
|
- statement = SQL('\n').join([
|
|
|
|
- get_select(get_selectors(unit, product, window)),
|
|
|
|
- get_from("transactions", JOINS),
|
|
|
|
- get_where(product=product, category=category, group=group, organic=organic, limit=limit),
|
|
|
|
- SQL("""
|
|
|
|
|
|
+ return SQL("""
|
|
ORDER BY {organic_sort} {sort} code, product, category, "group", "$/unit" ASC, ts DESC
|
|
ORDER BY {organic_sort} {sort} code, product, category, "group", "$/unit" ASC, ts DESC
|
|
""").format(
|
|
""").format(
|
|
- sort=sort_sql,
|
|
|
|
- organic_sort=SQL(organic_sort),
|
|
|
|
- ),
|
|
|
|
- ])
|
|
|
|
- return statement
|
|
|
|
|
|
+ sort=sort_sql,
|
|
|
|
+ organic_sort=SQL(organic_sort),
|
|
|
|
+ )
|