price_view.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #
  2. # Copyright (c) Daniel Sheffield 2021 - 2022
  3. #
  4. # All rights reserved
  5. #
  6. # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
  7. from psycopg2.sql import (
  8. Identifier,
  9. SQL,
  10. Literal,
  11. Placeholder,
  12. Composed,
  13. )
  14. from collections import (
  15. OrderedDict,
  16. )
  17. def get_where(product, unit, organic=None, limit='90 days'):
  18. where = [ ]
  19. where.append(SQL(' ').join([
  20. Identifier('products', 'name'),
  21. SQL('='),
  22. Literal(product)
  23. ]))
  24. if organic is not None:
  25. where.append(SQL(' ').join([
  26. Identifier('organic'),
  27. SQL('='),
  28. Literal(organic),
  29. ]))
  30. where.append(
  31. SQL("{ts} at time zone 'utc' BETWEEN now() - {interval} AND now()").format(
  32. ts=Identifier('ts'),
  33. interval=SQL("{literal}::interval").format(literal=Literal(limit))
  34. )
  35. )
  36. where.append(
  37. SQL("(SELECT id FROM units WHERE name = {unit}) = conv._to").format(
  38. unit=Literal(unit),
  39. )
  40. )
  41. return SQL('').join([
  42. SQL("WHERE"
  43. "\n "),
  44. SQL("\n AND ").join(where),
  45. ])
  46. def get_historic_prices_statement(sort, product, unit, organic=None, limit='90 days'):
  47. partition = f"(PARTITION BY {'organic,' if organic is not None else ''} product_id, unit_id)"
  48. organic_sort = f"{'organic,' if organic is not None else ''}"
  49. _with = SQL("""WITH conv AS (
  50. SELECT
  51. _1._from, _1._to, factor FROM conversions AS _1
  52. UNION
  53. SELECT _2._to, _2._from, 1/_2.factor FROM conversions AS _2
  54. UNION
  55. SELECT _3._from, _3._from, 1 FROM conversions AS _3
  56. UNION
  57. SELECT _4.id AS _from, _4.id AS _to, 1 FROM units AS _4
  58. )""")
  59. select = OrderedDict([
  60. ('id', Identifier('transactions','id')),
  61. #('ts', SQL('{identifier}::date').format(
  62. # identifier=Identifier('ts'),
  63. #)),
  64. ('%d/%m %_I%P', Identifier('transactions', 'ts')),
  65. #('store', Identifier('stores', 'name')),
  66. ('code', Identifier('stores', 'code')),
  67. #('description', Identifier('transactions','description')),
  68. #('volume', Identifier('quantity')),
  69. #('unit', Identifier('units', 'name')),
  70. #('price', Identifier('price')),
  71. ('$/unit', SQL("""TRUNC(price / (quantity * factor), 4)""")),
  72. ('avg', SQL(f"""TRUNC(sum(price) OVER {partition} / sum(quantity * factor) OVER {partition}, 4)""")),
  73. ('min', SQL(f"""TRUNC(min(price / (quantity * factor)) OVER {partition}, 4)""")),
  74. ('max', SQL(f"""TRUNC(max(price / (quantity * factor)) OVER {partition}, 4)""")),
  75. #('total', SQL("""sum(transactions.price) OVER (PARTITION BY transactions.ts::date ORDER BY transactions.id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)""")),
  76. #('group', Identifier('groups','name')),
  77. #('category', Identifier('categories','name')),
  78. #('product', Identifier('products','name')),
  79. ('organic', Identifier('organic')),
  80. #('price_sum', SQL(f"""sum(price) OVER {partition}""")),
  81. #('quantity_sum', SQL(f"""sum(quantity) OVER {partition}""")),
  82. #('price_min', SQL(f"""min(price/quantity) OVER {partition}""")),
  83. #('price_max', SQL(f"""max(price/quantity) OVER {partition}""")),
  84. ])
  85. statement = SQL('\n').join([
  86. _with,
  87. SQL('').join([
  88. SQL("SELECT"
  89. "\n "),
  90. SQL(','
  91. "\n ").join([
  92. SQL(' ').join([v, SQL('AS'), Identifier(f'{k}')]) for k, v in select.items()
  93. ])
  94. ]),
  95. SQL('').join([
  96. SQL("FROM"
  97. "\n "),
  98. SQL("\n JOIN ").join([
  99. SQL("transactions"),
  100. SQL("{table} ON ({table}.{key} = {index})").format(
  101. table=Identifier('conv'),
  102. key=Identifier('_from'),
  103. index=Identifier('unit_id'),
  104. ),
  105. SQL("{table} ON {table}.{key} = {index}").format(
  106. table=Identifier('units'),
  107. key=Identifier('id'),
  108. index=Identifier('unit_id'),
  109. ),
  110. SQL("{table} ON {table}.{key} = {index}").format(
  111. table=Identifier('stores'),
  112. key=Identifier('id'),
  113. index=Identifier('store_id')
  114. ),
  115. SQL("{table} ON {table}.{key} = {index}").format(
  116. table=Identifier('products'),
  117. key=Identifier('id'),
  118. index=Identifier('product_id')
  119. ),
  120. SQL("{table} ON {table}.{key} = {index}").format(
  121. table=Identifier('categories'),
  122. key=Identifier('id'),
  123. index=Identifier('category_id')
  124. ),
  125. SQL("{table} ON {table}.{key} = {index}").format(
  126. table=Identifier('groups'),
  127. key=Identifier('id'),
  128. index=Identifier('group_id')
  129. ),
  130. ]),
  131. ]),
  132. get_where(product, unit, organic=organic, limit=limit),
  133. SQL('ORDER BY {organic_sort} {sort} {direction}, code, "$/unit" ASC, ts DESC').format(
  134. sort=Identifier(sort),
  135. direction=SQL('DESC' if sort == 'ts' else 'ASC'),
  136. organic_sort=SQL(organic_sort),
  137. ),
  138. ])
  139. return statement
  140. # cols = ('product', 'unit', 'organic', 'store', '$/unit',)
  141. # aggs = {
  142. # 'avg': SQL("""TRUNC(price_sum/quantity_sum, 4)"""),
  143. # 'min': SQL("""TRUNC(price_min, 4)"""),
  144. # 'max': SQL("""TRUNC(price_max, 4)"""),
  145. # #'$/unit': SQL("""sum({price})/sum({quantity})""").format(
  146. # # price=Identifier("$/unit"),
  147. # # quantity=Identifier("quantity"),
  148. # #)
  149. # }
  150. # groups = ( 'price_sum', 'quantity_sum', 'price_min', 'price_max')
  151. # ret = SQL('\n').join([
  152. # SQL('').join([
  153. # SQL("SELECT"
  154. # "\n "),
  155. # SQL(','
  156. # "\n ").join([Identifier(f'{k}') for k in groups ])
  157. # ])
  158. # SQL('').join([
  159. # SQL("SELECT"
  160. # "\n "),
  161. # SQL(','
  162. # "\n ").join([Identifier(f'{k}') for k in select ])
  163. # ])
  164. # SQL('ORDER BY {_id} DESC').format(_id=Identifier('transactions','id')),
  165. # ])
  166. #SELECT product, organic, store, unit, TRUNC("$/unit",2) AS "$/unit", TRUNC(price_sum/quantity_sum, 2) AS "avg", TRUNC(price_min,2) AS "min", TRUNC(price_max,2) AS "max", date
  167. #FROM (
  168. #sub
  169. #) AS subq
  170. #WHERE product = 'Apples'
  171. #GROUP BY organic, product, unit, "$/unit", price_sum, quantity_sum, price_min, price_max, date, store ;