فهرست منبع

improve labels

Daniel Sheffield 10 ماه پیش
والد
کامیت
9104f4bcaa
2فایلهای تغییر یافته به همراه118 افزوده شده و 15 حذف شده
  1. 79 0
      app/rest/templates/volume.tpl
  2. 39 15
      app/rest/trend.py

+ 79 - 0
app/rest/templates/volume.tpl

@@ -0,0 +1,79 @@
+% setdefault("start", False)
+% setdefault("end", False)
+% setdefault("error", '')
+% if start:
+<html>
+  <head>
+    <title>Trend</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1"/>
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/purecss@2.1.0/build/pure-min.css" integrity="sha384-yHIFVG6ClnONEA5yB5DJXfW2/KC173DIQrYoZMEtBvGzmf0PKiGyNEqe9N6BNDBH" crossorigin="anonymous"/>
+    <link rel="stylesheet" href="https://shandan.one/css/grids-responsive-min.css"/>
+    <link rel="stylesheet" href="https://shandan.one/css/responsive-visibility-collapse.css"/>
+    <link rel="stylesheet" href="/grocery/static/cloud-gears.css"/>
+    <link rel="manifest" href="/grocery/static/manifest.json"/>
+    <link rel="icon" type="image/png" href="/grocery/static/favicon.png"/>
+    <style>
+html {
+  --scrollbarBG: #333333;
+  --thumbBG: #080808;
+}
+svg {
+  max-height: min(100vh, calc(100vw * 9 / 16));
+  max-width: calc(100vw - 2em);
+}
+body {
+  background-color: #080808;
+  color: #cccccc;
+  text-align: center;
+}
+div.loader-container {
+  position: absolute;
+  left: 50vw;
+  top: 50vh;
+  margin-top: -5.5em;
+  margin-left: -87.5px;
+  padding-bottom: 2em;
+  height: 9em;
+  width: 175px;
+}
+div.loader-container:not(:has(+ .done)) {
+  display: block;
+}
+.loader-container:not(:last-child) {
+  display: none;
+}
+div.progress {
+  margin: 1em 0 1em;
+}
+div.progress:not(:has(+ .done)) {
+  display: block;
+}
+.progress label {
+  text-align:left;
+}
+.progress label:after {
+  content: "...";
+}
+.progress:not(:last-child) {
+  display: none;
+}
+    </style>
+  </head>
+  <body>
+    <div class="loader-container">
+    <span class="loader"></span>
+% end
+% if end:
+    </div>
+    <div class="done"></div>
+{{!form}}
+    % if error:
+    % include('error-500', error=error)
+    % else:
+    %   for plt in svg:
+{{!plt}}
+    %    end
+    % end
+    </body>
+</html>
+% end

+ 39 - 15
app/rest/trend.py

@@ -14,6 +14,7 @@ from bottle import (
 )
 import matplotlib.pyplot as plt
 import matplotlib
+import numpy as np
 from pandas import DataFrame
 import seaborn as sns
 from psycopg import Connection
@@ -37,7 +38,7 @@ matplotlib.use('agg')
 plot_style = {
     "lines.color": "#ffffff",
     "patch.edgecolor": "#ffffff",
-    "text.color": "#000000",
+    "text.color": "#ffffff",
     "axes.facecolor": "#7f7f7f",
     "axes.edgecolor": "#ffffff",
     "axes.labelcolor": "#ffffff",
@@ -208,34 +209,57 @@ def volume_internal(conn: Connection[TupleRow], path: str, query: FormsDict):
             plt.style.use("dark_background")
             plt.rcParams.update(plot_style)
             plt.rcParams.update({"grid.linewidth":0.2, "grid.alpha":0.5})
-            plt.figure(figsize=[16, 9], layout="tight")
-            axes = pivot.plot.pie(subplots=True, figsize=(11, 5), labeldistance=1.1, autopct='%.0f', pctdistance=0.8, rotatelabels=False)
-
-            for ax, title in zip(axes, (
-                f'Expenditure ($)',
-                f'Quantity ({unit})',
-            )):
+            svg = []
+            for title, col, (pre, fmt, suf) in zip((
+                f"Expenditure ${pivot['price'].sum():0.2f}",
+                f"Quantity {pivot['quantity'].sum():0.1f} {unit}",
+            ), (
+                'price',
+                'quantity'
+            ), [
+                ('$', "{0:0.2f}", ''),
+                ('', "{0:0.1f}", f' {unit}')
+            ]):
+                plt.figure(figsize=[16, 9], layout="tight")
+                ax = plt.axes()
+                wedges, *_ = ax.pie(pivot[col].values, startangle=90)
+                bbox_props = dict(boxstyle="square,pad=0.3", lw=0.72, fc='#5f5f5f', ec=plot_style["axes.edgecolor"])
+                kw = dict(arrowprops=dict(arrowstyle="-"),
+                    bbox=bbox_props, zorder=0, va="center")
+                for i, p in enumerate(wedges):
+                    ang = (p.theta2 - p.theta1)/2. + p.theta1
+                    y = np.sin(np.deg2rad(ang))
+                    x = np.cos(np.deg2rad(ang))
+                    horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
+                    connectionstyle = f"angle,angleA=0,angleB={ang}"
+                    kw["arrowprops"].update({"connectionstyle": connectionstyle})
+                    label = pivot.index[i]
+                    val = pivot.loc[label, col]
+                    label = f"{label} {pre}{fmt.format(val)}{suf} ({val*100/pivot[col].sum():0.1f}%)"
+                    ax.annotate(label, xy=(x, y), xytext=(1.35*np.sign(x), 1.4*y),
+                                horizontalalignment=horizontalalignment, **kw)
                 ax.set_title(title)
                 xlabel=''
                 ylabel=''
                 ax.legend().set_visible(False)
-                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')
             
-            progress.update({ "stage": "Rendering chart", "percent": "50"})
-            yield template("done") + template("progress", **progress)
-            
-            f = StringIO()
-            plt.savefig(f, format='svg')
+                progress.update({ "stage": "Rendering chart", "percent": "50"})
+                yield template("done") + template("progress", **progress)
+                
+                f = StringIO()
+                plt.savefig(f, format='svg')
+                svg.append(f.getvalue())
+
             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())
+            yield template("volume", end=True, form=form, svg=svg)
 
     except HTTPError as e:
         if 'data' not in locals():