|
@@ -5,6 +5,7 @@
|
|
|
#
|
|
|
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
|
|
|
from io import StringIO
|
|
|
+from datetime import date, datetime
|
|
|
from queue import Queue
|
|
|
from bottle import (
|
|
|
FormsDict,
|
|
@@ -139,3 +140,111 @@ def trend_internal(conn: Connection[TupleRow], path: str, query: FormsDict):
|
|
|
|
|
|
finally:
|
|
|
conn.commit()
|
|
|
+
|
|
|
+def volume(queue: Queue, conn: Connection[TupleRow], path: str, query: FormsDict):
|
|
|
+ for item in volume_internal(conn, path, query):
|
|
|
+ queue.put(item, block=True)
|
|
|
+ queue.put(None)
|
|
|
+
|
|
|
+def volume_internal(conn: Connection[TupleRow], path: str, query: FormsDict):
|
|
|
+ progress = {
|
|
|
+ 'stage': None,
|
|
|
+ 'percent': None,
|
|
|
+ }
|
|
|
+ action = path.split('/')[-1]
|
|
|
+ organic = BOOLEAN.get(query.organic, None)
|
|
|
+ _filter = get_filter(query, allow=PARAMS)
|
|
|
+ yield template("trend", start=True)
|
|
|
+ try:
|
|
|
+ with conn.cursor() as cur:
|
|
|
+ query_manager = QueryManager(cur, display_mapper)
|
|
|
+ fields = {
|
|
|
+ k: get_query_param(*_filter[k])
|
|
|
+ for k in sorted(_filter) if k not in ('organic', 'unit') and _filter[k]
|
|
|
+ }
|
|
|
+ unit = fields['unit'] = query.unit or 'kg'
|
|
|
+ fields['organic'] = BOOLEAN.get(query.organic, None)
|
|
|
+ if unit and unit not in ALL_UNITS:
|
|
|
+ abort(400, f"Unsupported unit: {unit}")
|
|
|
+
|
|
|
+ progress.update({ "stage": "Querying database", "percent": "10"})
|
|
|
+ yield template("done") + template("progress", **progress)
|
|
|
+ data = get_data(query_manager, **fields)
|
|
|
+
|
|
|
+ if data.empty:
|
|
|
+ abort(404, f"No data.")
|
|
|
+
|
|
|
+ progress.update({ "stage": "Preparing data", "percent": "30"})
|
|
|
+ yield template("done") + template("progress", **progress)
|
|
|
+
|
|
|
+ now = datetime.now().date()
|
|
|
+ data = data[data['ts_month'] == date(now.year,now.month-1,1)]
|
|
|
+ pivot = data.groupby(['ts_month','group',])['price', 'quantity'].sum()
|
|
|
+ pivot = pivot.reset_index().set_index('group')
|
|
|
+
|
|
|
+ if pivot.empty:
|
|
|
+ abort(404, f"No data.")
|
|
|
+
|
|
|
+ sns.set_theme(style='darkgrid', palette='pastel', context="talk")
|
|
|
+ plt.style.use("dark_background")
|
|
|
+ plt.rcParams.update({
|
|
|
+ "lines.color": "#ffffff",
|
|
|
+ "patch.edgecolor": "#ffffff",
|
|
|
+ "text.color": "#ffffff",
|
|
|
+ "axes.facecolor": "#7f7f7f",
|
|
|
+ "axes.edgecolor": "#ffffff",
|
|
|
+ "axes.labelcolor": "#ffffff",
|
|
|
+ "xtick.color": "#ffffff",
|
|
|
+ "ytick.color": "#ffffff",
|
|
|
+ "grid.color": "#ffffff",
|
|
|
+ "figure.facecolor": "#7f7f7f",
|
|
|
+ "figure.edgecolor": "#7f7f7f",
|
|
|
+ "savefig.facecolor": "#7f7f7f",
|
|
|
+ "savefig.edgecolor": "#7f7f7f"})
|
|
|
+ plt.rcParams.update({"grid.linewidth":0.2, "grid.alpha":0.5})
|
|
|
+ plt.figure(figsize=[16, 9], layout="tight")
|
|
|
+ ax = pivot.plot.pie(y='quantity', figsize=(5, 5))
|
|
|
+ #ax.get_legend().remove()
|
|
|
+ ax.set_title(f'Quantity ({unit})')
|
|
|
+ #pie(pivot, col='price', title='Price ($)')
|
|
|
+ xlabel=''
|
|
|
+ ylabel=''
|
|
|
+ # if pivot.columns.size > 50:
|
|
|
+ # ax = sns.scatterplot(data=pivot, markers=True)
|
|
|
+ # else:
|
|
|
+ # ax = sns.lineplot(data=pivot, markers=True)
|
|
|
+ # legend = plt.figlegend(
|
|
|
+ # loc='upper center', ncol=6,
|
|
|
+ # title_fontsize="14", fontsize="12", labelcolor='#ffffff',
|
|
|
+ # framealpha=0.5
|
|
|
+ # )
|
|
|
+ # legend.set_title(title="Products")
|
|
|
+ ax.legend().set_visible(False)
|
|
|
+
|
|
|
+ ax.set_xlabel(xlabel, fontsize="14")
|
|
|
+ ax.set_ylabel(ylabel, fontsize="14")
|
|
|
+ ax.axes.tick_params(labelsize="12", which='both')
|
|
|
+ for _, spine in ax.spines.items():
|
|
|
+ spine.set_color('#ffffff')
|
|
|
+
|
|
|
+ progress.update({ "stage": "Rendering chart", "percent": "50"})
|
|
|
+ yield template("done") + template("progress", **progress)
|
|
|
+
|
|
|
+ f = StringIO()
|
|
|
+ plt.savefig(f, format='svg')
|
|
|
+ progress.update({ "stage": "Done", "percent": "100" })
|
|
|
+ yield template("done") + template("progress", **progress)
|
|
|
+
|
|
|
+ form = get_form(action, 'post', _filter, organic, data)
|
|
|
+
|
|
|
+ yield template("trend", end=True, form=form, svg=f.getvalue())
|
|
|
+
|
|
|
+ except HTTPError as e:
|
|
|
+ if 'data' not in locals():
|
|
|
+ data = DataFrame()
|
|
|
+ if 'form' not in locals():
|
|
|
+ form = get_form(action, 'post', _filter, organic, data)
|
|
|
+ yield template("done") + template("trend", end=True, form=form, error=e.body)
|
|
|
+
|
|
|
+ finally:
|
|
|
+ conn.commit()
|