mirror of
https://github.com/Crocmagnon/checkout.git
synced 2024-11-22 16:18:03 +01:00
Moar graphs
This commit is contained in:
parent
212b0d9713
commit
282318ac81
6 changed files with 138 additions and 58 deletions
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-04-26 23:30+0200\n"
|
"POT-Creation-Date: 2022-04-27 22:58+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
|
@ -5,7 +5,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-04-26 23:30+0200\n"
|
"POT-Creation-Date: 2022-04-27 22:58+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-04-26 23:30+0200\n"
|
"POT-Creation-Date: 2022-04-27 22:58+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -50,7 +50,7 @@ msgstr ""
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:47 purchase/models.py:170
|
#: purchase/models.py:47 purchase/models.py:167
|
||||||
msgid "payment method"
|
msgid "payment method"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ msgstr ""
|
||||||
msgid "image"
|
msgid "image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:86
|
#: purchase/models.py:86 purchase/models.py:203
|
||||||
msgid "unit price (cents)"
|
msgid "unit price (cents)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ msgstr ""
|
||||||
msgid "display order"
|
msgid "display order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:96 purchase/models.py:198
|
#: purchase/models.py:96 purchase/models.py:193
|
||||||
msgid "product"
|
msgid "product"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -82,28 +82,32 @@ msgstr ""
|
||||||
msgid "products"
|
msgid "products"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:176 purchase/models.py:204
|
#: purchase/models.py:173 purchase/models.py:199
|
||||||
msgid "basket"
|
msgid "basket"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:177
|
#: purchase/models.py:174
|
||||||
msgid "baskets"
|
msgid "baskets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:180
|
#: purchase/models.py:177
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Basket #%(id)s"
|
msgid "Basket #%(id)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:206
|
#: purchase/models.py:201
|
||||||
msgid "quantity"
|
msgid "quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:211
|
#: purchase/models.py:204
|
||||||
|
msgid "product's unit price in cents at the time of purchase"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: purchase/models.py:210
|
||||||
msgid "basket item"
|
msgid "basket item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/models.py:212
|
#: purchase/models.py:211
|
||||||
msgid "basket items"
|
msgid "basket items"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -174,11 +178,11 @@ msgstr ""
|
||||||
msgid "Products"
|
msgid "Products"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/templates/purchase/reports.html:38
|
#: purchase/templates/purchase/reports.html:40
|
||||||
msgid "Turnover by payment method"
|
msgid "Turnover by payment method"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/templates/purchase/reports.html:41
|
#: purchase/templates/purchase/reports.html:43
|
||||||
msgid "Baskets without payment method"
|
msgid "Baskets without payment method"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -203,7 +207,7 @@ msgid "Product"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/templates/purchase/snippets/report_products.html:7
|
#: purchase/templates/purchase/snippets/report_products.html:7
|
||||||
#: purchase/views/reports.py:70
|
#: purchase/views/reports.py:79 purchase/views/reports.py:93
|
||||||
msgid "# sold"
|
msgid "# sold"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -219,18 +223,26 @@ msgstr ""
|
||||||
msgid "Basket successfully deleted."
|
msgid "Basket successfully deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/views/reports.py:28
|
#: purchase/views/reports.py:29
|
||||||
msgid "No sale to report"
|
msgid "No sale to report"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/views/reports.py:75
|
#: purchase/views/reports.py:70
|
||||||
|
msgid "Sales by product"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: purchase/views/reports.py:85 purchase/views/reports.py:100
|
||||||
msgid "Turnover by product"
|
msgid "Turnover by product"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/views/reports.py:113
|
#: purchase/views/reports.py:122
|
||||||
|
msgid "Sales by hour"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: purchase/views/reports.py:133
|
||||||
msgid "Basket count by hour"
|
msgid "Basket count by hour"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: purchase/views/reports.py:118
|
#: purchase/views/reports.py:141
|
||||||
msgid "Turnover by hour"
|
msgid "Turnover by hour"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -5,7 +5,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-04-26 23:30+0200\n"
|
"POT-Creation-Date: 2022-04-27 22:58+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -47,7 +47,7 @@ msgstr "mis à jour à"
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr "nom"
|
msgstr "nom"
|
||||||
|
|
||||||
#: purchase/models.py:47 purchase/models.py:170
|
#: purchase/models.py:47 purchase/models.py:167
|
||||||
msgid "payment method"
|
msgid "payment method"
|
||||||
msgstr "moyen de paiement"
|
msgstr "moyen de paiement"
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ msgstr "moyens de paiement"
|
||||||
msgid "image"
|
msgid "image"
|
||||||
msgstr "image"
|
msgstr "image"
|
||||||
|
|
||||||
#: purchase/models.py:86
|
#: purchase/models.py:86 purchase/models.py:203
|
||||||
msgid "unit price (cents)"
|
msgid "unit price (cents)"
|
||||||
msgstr "prix unitaire (centimes)"
|
msgstr "prix unitaire (centimes)"
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ msgstr "prix unitaire en centimes"
|
||||||
msgid "display order"
|
msgid "display order"
|
||||||
msgstr "ordre d'affichage"
|
msgstr "ordre d'affichage"
|
||||||
|
|
||||||
#: purchase/models.py:96 purchase/models.py:198
|
#: purchase/models.py:96 purchase/models.py:193
|
||||||
msgid "product"
|
msgid "product"
|
||||||
msgstr "produit"
|
msgstr "produit"
|
||||||
|
|
||||||
|
@ -79,28 +79,32 @@ msgstr "produit"
|
||||||
msgid "products"
|
msgid "products"
|
||||||
msgstr "produits"
|
msgstr "produits"
|
||||||
|
|
||||||
#: purchase/models.py:176 purchase/models.py:204
|
#: purchase/models.py:173 purchase/models.py:199
|
||||||
msgid "basket"
|
msgid "basket"
|
||||||
msgstr "panier"
|
msgstr "panier"
|
||||||
|
|
||||||
#: purchase/models.py:177
|
#: purchase/models.py:174
|
||||||
msgid "baskets"
|
msgid "baskets"
|
||||||
msgstr "paniers"
|
msgstr "paniers"
|
||||||
|
|
||||||
#: purchase/models.py:180
|
#: purchase/models.py:177
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Basket #%(id)s"
|
msgid "Basket #%(id)s"
|
||||||
msgstr "Panier n°%(id)s"
|
msgstr "Panier n°%(id)s"
|
||||||
|
|
||||||
#: purchase/models.py:206
|
#: purchase/models.py:201
|
||||||
msgid "quantity"
|
msgid "quantity"
|
||||||
msgstr "quantité"
|
msgstr "quantité"
|
||||||
|
|
||||||
#: purchase/models.py:211
|
#: purchase/models.py:204
|
||||||
|
msgid "product's unit price in cents at the time of purchase"
|
||||||
|
msgstr "prix unitaire du produit en centimes au moment de l'achat"
|
||||||
|
|
||||||
|
#: purchase/models.py:210
|
||||||
msgid "basket item"
|
msgid "basket item"
|
||||||
msgstr "article de panier"
|
msgstr "article de panier"
|
||||||
|
|
||||||
#: purchase/models.py:212
|
#: purchase/models.py:211
|
||||||
msgid "basket items"
|
msgid "basket items"
|
||||||
msgstr "articles de panier"
|
msgstr "articles de panier"
|
||||||
|
|
||||||
|
@ -171,11 +175,11 @@ msgstr "Panier moyen"
|
||||||
msgid "Products"
|
msgid "Products"
|
||||||
msgstr "Produits"
|
msgstr "Produits"
|
||||||
|
|
||||||
#: purchase/templates/purchase/reports.html:38
|
#: purchase/templates/purchase/reports.html:40
|
||||||
msgid "Turnover by payment method"
|
msgid "Turnover by payment method"
|
||||||
msgstr "Chiffre d'affaires par moyen de paiement"
|
msgstr "Chiffre d'affaires par moyen de paiement"
|
||||||
|
|
||||||
#: purchase/templates/purchase/reports.html:41
|
#: purchase/templates/purchase/reports.html:43
|
||||||
msgid "Baskets without payment method"
|
msgid "Baskets without payment method"
|
||||||
msgstr "Paniers sans moyen de paiement"
|
msgstr "Paniers sans moyen de paiement"
|
||||||
|
|
||||||
|
@ -200,7 +204,7 @@ msgid "Product"
|
||||||
msgstr "Produit"
|
msgstr "Produit"
|
||||||
|
|
||||||
#: purchase/templates/purchase/snippets/report_products.html:7
|
#: purchase/templates/purchase/snippets/report_products.html:7
|
||||||
#: purchase/views/reports.py:70
|
#: purchase/views/reports.py:79 purchase/views/reports.py:93
|
||||||
msgid "# sold"
|
msgid "# sold"
|
||||||
msgstr "Nb. vendus"
|
msgstr "Nb. vendus"
|
||||||
|
|
||||||
|
@ -216,19 +220,27 @@ msgstr "Panier correctement modifié."
|
||||||
msgid "Basket successfully deleted."
|
msgid "Basket successfully deleted."
|
||||||
msgstr "Panier correctement supprimé."
|
msgstr "Panier correctement supprimé."
|
||||||
|
|
||||||
#: purchase/views/reports.py:28
|
#: purchase/views/reports.py:29
|
||||||
msgid "No sale to report"
|
msgid "No sale to report"
|
||||||
msgstr "Aucune vente à afficher"
|
msgstr "Aucune vente à afficher"
|
||||||
|
|
||||||
#: purchase/views/reports.py:75
|
#: purchase/views/reports.py:70
|
||||||
|
msgid "Sales by product"
|
||||||
|
msgstr "Ventes par produit"
|
||||||
|
|
||||||
|
#: purchase/views/reports.py:85 purchase/views/reports.py:100
|
||||||
msgid "Turnover by product"
|
msgid "Turnover by product"
|
||||||
msgstr "Chiffre d'affaires par produit"
|
msgstr "Chiffre d'affaires par produit"
|
||||||
|
|
||||||
#: purchase/views/reports.py:113
|
#: purchase/views/reports.py:122
|
||||||
|
msgid "Sales by hour"
|
||||||
|
msgstr "Ventes par heure"
|
||||||
|
|
||||||
|
#: purchase/views/reports.py:133
|
||||||
msgid "Basket count by hour"
|
msgid "Basket count by hour"
|
||||||
msgstr "Nombre de paniers par heure"
|
msgstr "Nombre de paniers par heure"
|
||||||
|
|
||||||
#: purchase/views/reports.py:118
|
#: purchase/views/reports.py:141
|
||||||
msgid "Turnover by hour"
|
msgid "Turnover by hour"
|
||||||
msgstr "Chiffre d'affaires par heure"
|
msgstr "Chiffre d'affaires par heure"
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
<h2>{% translate "Products" %}</h2>
|
<h2>{% translate "Products" %}</h2>
|
||||||
{% include "purchase/snippets/report_products.html" %}
|
{% include "purchase/snippets/report_products.html" %}
|
||||||
{{ products_plot|safe }}
|
{{ products_plot|safe }}
|
||||||
|
{{ products_sold_pie|safe }}
|
||||||
|
{{ products_turnover_pie|safe }}
|
||||||
|
|
||||||
<h2>{% translate "Turnover by payment method" %}</h2>
|
<h2>{% translate "Turnover by payment method" %}</h2>
|
||||||
{% include "purchase/snippets/report_payment_methods.html" %}
|
{% include "purchase/snippets/report_payment_methods.html" %}
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
import datetime
|
import datetime
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from matplotlib import pyplot as plt
|
from matplotlib import pyplot as plt
|
||||||
from matplotlib import ticker
|
from matplotlib import ticker
|
||||||
|
from matplotlib.axes import Axes
|
||||||
|
from matplotlib.container import BarContainer
|
||||||
|
from matplotlib.dates import AutoDateLocator, ConciseDateFormatter, HourLocator
|
||||||
|
from matplotlib.figure import Figure
|
||||||
|
|
||||||
from purchase.models import Basket, PaymentMethod, Product, ProductQuerySet
|
from purchase.models import Basket, PaymentMethod, Product, ProductQuerySet
|
||||||
from purchase.views.utils import ProtectedViewsMixin
|
from purchase.views.utils import ProtectedViewsMixin
|
||||||
|
@ -31,7 +38,11 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
||||||
}
|
}
|
||||||
|
|
||||||
products = Product.objects.with_turnover().with_sold()
|
products = Product.objects.with_turnover().with_sold()
|
||||||
products_plot = self.get_products_plot(products)
|
(
|
||||||
|
products_plot,
|
||||||
|
products_sold_pie,
|
||||||
|
products_turnover_pie,
|
||||||
|
) = self.get_products_plots(products)
|
||||||
by_hour_plot = self.by_hour_plot(baskets)
|
by_hour_plot = self.by_hour_plot(baskets)
|
||||||
context.update(
|
context.update(
|
||||||
{
|
{
|
||||||
|
@ -41,6 +52,8 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
||||||
"average_basket_by_day": average_basket_by_day,
|
"average_basket_by_day": average_basket_by_day,
|
||||||
"products": products,
|
"products": products,
|
||||||
"products_plot": products_plot,
|
"products_plot": products_plot,
|
||||||
|
"products_sold_pie": products_sold_pie,
|
||||||
|
"products_turnover_pie": products_turnover_pie,
|
||||||
"by_hour_plot": by_hour_plot,
|
"by_hour_plot": by_hour_plot,
|
||||||
"payment_methods": PaymentMethod.objects.with_turnover().with_sold(),
|
"payment_methods": PaymentMethod.objects.with_turnover().with_sold(),
|
||||||
"no_payment_method": Basket.objects.no_payment_method().priced(),
|
"no_payment_method": Basket.objects.no_payment_method().priced(),
|
||||||
|
@ -48,26 +61,49 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
||||||
)
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_products_plot(self, products: ProductQuerySet):
|
def get_products_plots(self, products: ProductQuerySet):
|
||||||
labels, sold, turnover = self.get_products_data_for_plot(products)
|
labels, sold, turnover = self.get_products_data_for_plot(products)
|
||||||
|
|
||||||
fig, ax1 = plt.subplots()
|
x = np.arange(len(labels))
|
||||||
|
width = 0.4
|
||||||
|
fig: Figure = plt.figure()
|
||||||
|
fig.suptitle(_("Sales by product"))
|
||||||
|
|
||||||
color = "tab:orange"
|
color = "tab:orange"
|
||||||
ax1.bar(labels, sold, width=0.8, color=color)
|
ax1: Axes = fig.add_subplot()
|
||||||
|
bar: BarContainer = ax1.bar(x - width / 2, sold, width=width, color=color)
|
||||||
|
ax1.bar_label(bar)
|
||||||
ax1.tick_params(axis="x", rotation=15)
|
ax1.tick_params(axis="x", rotation=15)
|
||||||
|
ax1.tick_params(axis="y", labelcolor=color)
|
||||||
|
ax1.set_xticks(x, labels)
|
||||||
ax1.set_ylabel(_("# sold"), color=color)
|
ax1.set_ylabel(_("# sold"), color=color)
|
||||||
|
|
||||||
color = "tab:blue"
|
color = "tab:blue"
|
||||||
ax2 = ax1.twinx()
|
ax2: Axes = ax1.twinx()
|
||||||
ax2.bar(labels, turnover, width=0.4, color=color)
|
bar = ax2.bar(x + width / 2, turnover, width=width, color=color)
|
||||||
|
ax2.bar_label(bar, fmt="%d€")
|
||||||
ax2.set_ylabel(_("Turnover by product"), color=color)
|
ax2.set_ylabel(_("Turnover by product"), color=color)
|
||||||
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter("%.2f€"))
|
ax2.yaxis.set_major_formatter(ticker.FormatStrFormatter("%.2f€"))
|
||||||
|
ax2.tick_params(axis="y", labelcolor=color)
|
||||||
|
|
||||||
fig.tight_layout()
|
fig.tight_layout()
|
||||||
image_data = StringIO()
|
img1 = self.get_image_from_fig(fig)
|
||||||
fig.savefig(image_data, format="svg")
|
|
||||||
image_data.seek(0)
|
fig = plt.figure()
|
||||||
return image_data.getvalue()
|
fig.suptitle(_("# sold"))
|
||||||
|
ax1 = fig.add_subplot()
|
||||||
|
ax1.pie(sold, labels=labels, autopct="%d%%")
|
||||||
|
fig.tight_layout()
|
||||||
|
img2 = self.get_image_from_fig(fig)
|
||||||
|
|
||||||
|
fig = plt.figure()
|
||||||
|
fig.suptitle(_("Turnover by product"))
|
||||||
|
ax1 = fig.add_subplot()
|
||||||
|
ax1.pie(turnover, labels=labels, autopct="%d%%")
|
||||||
|
fig.tight_layout()
|
||||||
|
img3 = self.get_image_from_fig(fig)
|
||||||
|
|
||||||
|
return img1, img2, img3
|
||||||
|
|
||||||
def get_products_data_for_plot(self, products):
|
def get_products_data_for_plot(self, products):
|
||||||
labels = []
|
labels = []
|
||||||
|
@ -80,25 +116,36 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
||||||
return labels, sold, turnover
|
return labels, sold, turnover
|
||||||
|
|
||||||
def by_hour_plot(self, baskets):
|
def by_hour_plot(self, baskets):
|
||||||
counts, labels, turnovers = self.get_by_hour_data_for_plot(baskets)
|
labels, counts, turnovers = self.get_by_hour_data_for_plot(baskets)
|
||||||
fig, ax1 = plt.subplots()
|
|
||||||
hours_in_day = 24
|
hours_in_day = 24
|
||||||
|
fig: Figure = plt.figure()
|
||||||
|
fig.suptitle(_("Sales by hour"))
|
||||||
|
ax1: Axes = fig.add_subplot()
|
||||||
|
|
||||||
color = "tab:orange"
|
color = "tab:orange"
|
||||||
ax1.bar(labels, counts, width=1 / hours_in_day, color=color)
|
ax1.bar(labels, counts, width=1 / hours_in_day, color=color)
|
||||||
ax1.tick_params(axis="x", rotation=15)
|
ax1.tick_params(axis="x", rotation=15)
|
||||||
|
ax1.xaxis.set_minor_locator(HourLocator())
|
||||||
|
tz = ZoneInfo(settings.TIME_ZONE)
|
||||||
|
locator = AutoDateLocator(tz=tz)
|
||||||
|
ax1.xaxis.set_major_locator(locator)
|
||||||
|
ax1.xaxis.set_major_formatter(ConciseDateFormatter(locator, tz=tz))
|
||||||
ax1.set_ylabel(_("Basket count by hour"), color=color)
|
ax1.set_ylabel(_("Basket count by hour"), color=color)
|
||||||
|
ax1.set_yticks(np.linspace(*ax1.get_ybound(), 8))
|
||||||
|
ax1.tick_params(axis="y", labelcolor=color)
|
||||||
|
ax1.grid(visible=True, which="major", axis="y")
|
||||||
|
|
||||||
color = "tab:blue"
|
color = "tab:blue"
|
||||||
ax2 = ax1.twinx()
|
ax2: Axes = ax1.twinx()
|
||||||
ax2.bar(labels, turnovers, width=1 / (hours_in_day * 2), color=color)
|
ax2.plot(labels, turnovers, ".-", color=color)
|
||||||
ax2.set_ylabel(_("Turnover by hour"), color=color)
|
ax2.set_ylabel(_("Turnover by hour"), color=color)
|
||||||
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter("%.2f€"))
|
ax2.set_ylim(bottom=0)
|
||||||
|
ax2.set_yticks(np.linspace(*ax2.get_ybound(), 8))
|
||||||
|
ax2.tick_params(axis="y", labelcolor=color)
|
||||||
|
ax2.yaxis.set_major_formatter(ticker.FormatStrFormatter("%.2f€"))
|
||||||
|
|
||||||
fig.tight_layout()
|
fig.tight_layout()
|
||||||
image_data = StringIO()
|
return self.get_image_from_fig(fig)
|
||||||
fig.savefig(image_data, format="svg")
|
|
||||||
image_data.seek(0)
|
|
||||||
return image_data.getvalue()
|
|
||||||
|
|
||||||
def get_by_hour_data_for_plot(self, baskets):
|
def get_by_hour_data_for_plot(self, baskets):
|
||||||
current: datetime.datetime = baskets[0].created_at
|
current: datetime.datetime = baskets[0].created_at
|
||||||
|
@ -120,8 +167,15 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
||||||
if basket_index == len(baskets):
|
if basket_index == len(baskets):
|
||||||
break
|
break
|
||||||
basket = baskets[basket_index]
|
basket = baskets[basket_index]
|
||||||
labels.append(current)
|
labels.append(current.astimezone(ZoneInfo(settings.TIME_ZONE)))
|
||||||
counts.append(count)
|
counts.append(count)
|
||||||
turnovers.append(turnover)
|
turnovers.append(turnover)
|
||||||
current = end_slot
|
current = end_slot
|
||||||
return counts, labels, turnovers
|
return labels, counts, turnovers
|
||||||
|
|
||||||
|
def get_image_from_fig(self, fig):
|
||||||
|
image_data = StringIO()
|
||||||
|
fig.savefig(image_data, format="svg")
|
||||||
|
image_data.seek(0)
|
||||||
|
img1 = image_data.getvalue()
|
||||||
|
return img1
|
||||||
|
|
Loading…
Reference in a new issue