Daniel Sheffield 1 gadu atpakaļ
vecāks
revīzija
fab271bc76
2 mainītis faili ar 126 papildinājumiem un 3 dzēšanām
  1. 17 3
      app/rest/pyapi.py
  2. 109 0
      app/rest/trend.py

+ 17 - 3
app/rest/pyapi.py

@@ -34,9 +34,9 @@ conn = connect(f"{host} {db} {user} {password}")
 def send_static(filename):
     return static_file(filename, root='app/rest/static')
 
-def trend_thread(conn, path, forms):
+def new_thread(target, conn, path, forms):
     def cb(queue):
-        return Thread(target=worker.trend, args=(
+        return Thread(target=target, args=(
             queue, conn, path, forms
         )).start()
     return cb
@@ -44,6 +44,20 @@ def trend_thread(conn, path, forms):
 PAGE_CACHE = PageCache(100)
 QUERY_CACHE = QueryCache(None)
 
+@route('/grocery/volume', method=['GET', 'POST'])
+@cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
+def volume(key: Tuple[str, int], cache: PageCache):
+    _, _, path, *_ = request.urlparts
+
+    page = cache[key]
+    if page is None:
+        form = key_to_form(key)
+        page = cache.add(key, CachedLoadingPage([], new_thread(worker.volume, conn, path, form)))
+    
+    for i in iter_page(page):
+        yield i
+
+
 @route('/grocery/trend', method=['GET', 'POST'])
 @cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
 def trend(key: Tuple[str, int], cache: PageCache):
@@ -52,7 +66,7 @@ def trend(key: Tuple[str, int], cache: PageCache):
     page = cache[key]
     if page is None:
         form = key_to_form(key)
-        page = cache.add(key, CachedLoadingPage([], trend_thread(conn, path, form)))
+        page = cache.add(key, CachedLoadingPage([], new_thread(worker.trend, conn, path, form)))
     
     for i in iter_page(page):
         yield i

+ 109 - 0
app/rest/trend.py

@@ -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()