mirror of
https://github.com/Crocmagnon/checkout.git
synced 2024-12-22 14:11:48 +01:00
Add reports by hour
This commit is contained in:
parent
0e005cf815
commit
ca5e948477
8 changed files with 114 additions and 39 deletions
|
@ -7,3 +7,4 @@ __pycache__/
|
|||
**/__pycache__/
|
||||
.pytest_cache/
|
||||
.idea/
|
||||
*.mo
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
python manage.py migrate --noinput
|
||||
inv compilemessages
|
||||
gunicorn checkout.wsgi -b 0.0.0.0:8000 --log-file -
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-26 18:17+0200\n"
|
||||
"POT-Creation-Date: 2022-04-26 20:06+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
|
@ -5,7 +5,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-26 18:17+0200\n"
|
||||
"POT-Creation-Date: 2022-04-26 20:06+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-26 18:17+0200\n"
|
||||
"POT-Creation-Date: 2022-04-26 20:06+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -46,11 +46,11 @@ msgstr ""
|
|||
msgid "updated at"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:37 purchase/models.py:66
|
||||
#: purchase/models.py:37 purchase/models.py:69
|
||||
msgid "name"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:42 purchase/models.py:149
|
||||
#: purchase/models.py:42 purchase/models.py:152
|
||||
msgid "payment method"
|
||||
msgstr ""
|
||||
|
||||
|
@ -58,52 +58,52 @@ msgstr ""
|
|||
msgid "payment methods"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:67
|
||||
#: purchase/models.py:70
|
||||
msgid "image"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:69
|
||||
#: purchase/models.py:72
|
||||
msgid "unit price (cents)"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:69
|
||||
#: purchase/models.py:72
|
||||
msgid "unit price in cents"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:72
|
||||
#: purchase/models.py:75
|
||||
msgid "display order"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:79 purchase/models.py:177
|
||||
#: purchase/models.py:82 purchase/models.py:180
|
||||
msgid "product"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:80
|
||||
#: purchase/models.py:83
|
||||
msgid "products"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:155 purchase/models.py:183
|
||||
#: purchase/models.py:158 purchase/models.py:186
|
||||
msgid "basket"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:156
|
||||
#: purchase/models.py:159
|
||||
msgid "baskets"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:159
|
||||
#: purchase/models.py:162
|
||||
#, python-format
|
||||
msgid "Basket #%(id)s"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:185
|
||||
#: purchase/models.py:188
|
||||
msgid "quantity"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:190
|
||||
#: purchase/models.py:193
|
||||
msgid "basket item"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/models.py:191
|
||||
#: purchase/models.py:194
|
||||
msgid "basket items"
|
||||
msgstr ""
|
||||
|
||||
|
@ -163,6 +163,7 @@ msgstr ""
|
|||
#: purchase/templates/purchase/reports.html:14
|
||||
#: purchase/templates/purchase/snippets/report_payment_methods.html:8
|
||||
#: purchase/templates/purchase/snippets/report_products.html:8
|
||||
#: purchase/views/reports.py:80
|
||||
msgid "Turnover"
|
||||
msgstr ""
|
||||
|
||||
|
@ -170,15 +171,15 @@ msgstr ""
|
|||
msgid "Average basket"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/templates/purchase/reports.html:27
|
||||
#: purchase/templates/purchase/reports.html:29
|
||||
msgid "Products"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/templates/purchase/reports.html:30
|
||||
#: purchase/templates/purchase/reports.html:34
|
||||
msgid "Turnover by payment method"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/templates/purchase/reports.html:33
|
||||
#: purchase/templates/purchase/reports.html:37
|
||||
msgid "Baskets without payment method"
|
||||
msgstr ""
|
||||
|
||||
|
@ -203,6 +204,7 @@ msgid "Product"
|
|||
msgstr ""
|
||||
|
||||
#: purchase/templates/purchase/snippets/report_products.html:7
|
||||
#: purchase/views/reports.py:64
|
||||
msgid "# sold"
|
||||
msgstr ""
|
||||
|
||||
|
@ -217,3 +219,11 @@ msgstr ""
|
|||
#: purchase/views/basket.py:45
|
||||
msgid "Basket successfully deleted."
|
||||
msgstr ""
|
||||
|
||||
#: purchase/views/reports.py:112
|
||||
msgid "Basket count by hour"
|
||||
msgstr ""
|
||||
|
||||
#: purchase/views/reports.py:117
|
||||
msgid "Turnover by hour"
|
||||
msgstr ""
|
||||
|
|
|
@ -5,7 +5,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-26 18:17+0200\n"
|
||||
"POT-Creation-Date: 2022-04-26 20:06+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -43,11 +43,11 @@ msgstr "créé à"
|
|||
msgid "updated at"
|
||||
msgstr "mis à jour à"
|
||||
|
||||
#: purchase/models.py:37 purchase/models.py:66
|
||||
#: purchase/models.py:37 purchase/models.py:69
|
||||
msgid "name"
|
||||
msgstr "nom"
|
||||
|
||||
#: purchase/models.py:42 purchase/models.py:149
|
||||
#: purchase/models.py:42 purchase/models.py:152
|
||||
msgid "payment method"
|
||||
msgstr "moyen de paiement"
|
||||
|
||||
|
@ -55,52 +55,52 @@ msgstr "moyen de paiement"
|
|||
msgid "payment methods"
|
||||
msgstr "moyens de paiement"
|
||||
|
||||
#: purchase/models.py:67
|
||||
#: purchase/models.py:70
|
||||
msgid "image"
|
||||
msgstr "image"
|
||||
|
||||
#: purchase/models.py:69
|
||||
#: purchase/models.py:72
|
||||
msgid "unit price (cents)"
|
||||
msgstr "prix unitaire (centimes)"
|
||||
|
||||
#: purchase/models.py:69
|
||||
#: purchase/models.py:72
|
||||
msgid "unit price in cents"
|
||||
msgstr "prix unitaire en centimes"
|
||||
|
||||
#: purchase/models.py:72
|
||||
#: purchase/models.py:75
|
||||
msgid "display order"
|
||||
msgstr "ordre d'affichage"
|
||||
|
||||
#: purchase/models.py:79 purchase/models.py:177
|
||||
#: purchase/models.py:82 purchase/models.py:180
|
||||
msgid "product"
|
||||
msgstr "produit"
|
||||
|
||||
#: purchase/models.py:80
|
||||
#: purchase/models.py:83
|
||||
msgid "products"
|
||||
msgstr "produits"
|
||||
|
||||
#: purchase/models.py:155 purchase/models.py:183
|
||||
#: purchase/models.py:158 purchase/models.py:186
|
||||
msgid "basket"
|
||||
msgstr "panier"
|
||||
|
||||
#: purchase/models.py:156
|
||||
#: purchase/models.py:159
|
||||
msgid "baskets"
|
||||
msgstr "paniers"
|
||||
|
||||
#: purchase/models.py:159
|
||||
#: purchase/models.py:162
|
||||
#, python-format
|
||||
msgid "Basket #%(id)s"
|
||||
msgstr "Panier n°%(id)s"
|
||||
|
||||
#: purchase/models.py:185
|
||||
#: purchase/models.py:188
|
||||
msgid "quantity"
|
||||
msgstr "quantité"
|
||||
|
||||
#: purchase/models.py:190
|
||||
#: purchase/models.py:193
|
||||
msgid "basket item"
|
||||
msgstr "article de panier"
|
||||
|
||||
#: purchase/models.py:191
|
||||
#: purchase/models.py:194
|
||||
msgid "basket items"
|
||||
msgstr "articles de panier"
|
||||
|
||||
|
@ -160,6 +160,7 @@ msgstr "Par jour"
|
|||
#: purchase/templates/purchase/reports.html:14
|
||||
#: purchase/templates/purchase/snippets/report_payment_methods.html:8
|
||||
#: purchase/templates/purchase/snippets/report_products.html:8
|
||||
#: purchase/views/reports.py:80
|
||||
msgid "Turnover"
|
||||
msgstr "Chiffre d'affaires"
|
||||
|
||||
|
@ -167,15 +168,15 @@ msgstr "Chiffre d'affaires"
|
|||
msgid "Average basket"
|
||||
msgstr "Panier moyen"
|
||||
|
||||
#: purchase/templates/purchase/reports.html:27
|
||||
#: purchase/templates/purchase/reports.html:29
|
||||
msgid "Products"
|
||||
msgstr "Produits"
|
||||
|
||||
#: purchase/templates/purchase/reports.html:30
|
||||
#: purchase/templates/purchase/reports.html:34
|
||||
msgid "Turnover by payment method"
|
||||
msgstr "Chiffre d'affaires par moyen de paiement"
|
||||
|
||||
#: purchase/templates/purchase/reports.html:33
|
||||
#: purchase/templates/purchase/reports.html:37
|
||||
msgid "Baskets without payment method"
|
||||
msgstr "Paniers sans moyen de paiement"
|
||||
|
||||
|
@ -200,6 +201,7 @@ msgid "Product"
|
|||
msgstr "Produit"
|
||||
|
||||
#: purchase/templates/purchase/snippets/report_products.html:7
|
||||
#: purchase/views/reports.py:64
|
||||
msgid "# sold"
|
||||
msgstr "Nb. vendus"
|
||||
|
||||
|
@ -215,6 +217,14 @@ msgstr "Panier correctement modifié."
|
|||
msgid "Basket successfully deleted."
|
||||
msgstr "Panier correctement supprimé."
|
||||
|
||||
#: purchase/views/reports.py:112
|
||||
msgid "Basket count by hour"
|
||||
msgstr "Nombre de paniers par heure"
|
||||
|
||||
#: purchase/views/reports.py:117
|
||||
msgid "Turnover by hour"
|
||||
msgstr "Chiffre d'affaires par heure"
|
||||
|
||||
#, python-format
|
||||
#~ msgid "Total turnover: %(total|currency)s"
|
||||
#~ msgstr "Chiffre d'affaires total : %(total|currency)s"
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{{ by_hour_plot|safe }}
|
||||
|
||||
<h2>{% translate "Products" %}</h2>
|
||||
{% include "purchase/snippets/report_products.html" %}
|
||||
{{ products_sold_plot|safe }}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import datetime
|
||||
from io import StringIO
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
|
@ -5,7 +6,13 @@ from django.views.generic import TemplateView
|
|||
from matplotlib import pyplot as plt
|
||||
from matplotlib import ticker
|
||||
|
||||
from purchase.models import Basket, PaymentMethod, Product, ProductQuerySet
|
||||
from purchase.models import (
|
||||
Basket,
|
||||
BasketQuerySet,
|
||||
PaymentMethod,
|
||||
Product,
|
||||
ProductQuerySet,
|
||||
)
|
||||
from purchase.views.utils import ProtectedViewsMixin
|
||||
|
||||
|
||||
|
@ -26,6 +33,8 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
|||
products = Product.objects.with_turnover().with_sold()
|
||||
products_sold_plot = self.get_products_sold_plot(products)
|
||||
products_turnover_plot = self.get_products_turnover_plot(products)
|
||||
baskets = list(Basket.objects.priced().order_by("created_at"))
|
||||
by_hour_plot = self.by_hour_plot(baskets)
|
||||
context.update(
|
||||
{
|
||||
"turnover": Basket.objects.turnover(),
|
||||
|
@ -35,6 +44,7 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
|||
"products": products,
|
||||
"products_sold_plot": products_sold_plot,
|
||||
"products_turnover_plot": products_turnover_plot,
|
||||
"by_hour_plot": by_hour_plot,
|
||||
"payment_methods": PaymentMethod.objects.with_turnover().with_sold(),
|
||||
"no_payment_method": Basket.objects.no_payment_method().priced(),
|
||||
}
|
||||
|
@ -71,3 +81,44 @@ class ReportsView(ProtectedViewsMixin, TemplateView):
|
|||
fig.savefig(image_data, format="svg")
|
||||
image_data.seek(0)
|
||||
return image_data.getvalue()
|
||||
|
||||
def by_hour_plot(self, baskets):
|
||||
current: datetime.datetime = baskets[0].created_at
|
||||
current = current.replace(minute=0, second=0, microsecond=0)
|
||||
end: datetime.datetime = baskets[-1].created_at
|
||||
basket_index = 0
|
||||
labels = []
|
||||
counts = []
|
||||
turnovers = []
|
||||
while current < end:
|
||||
end_slot = current + datetime.timedelta(hours=1)
|
||||
basket = baskets[basket_index]
|
||||
count = 0
|
||||
turnover = 0
|
||||
while basket.created_at < end_slot and basket_index < len(baskets) - 1:
|
||||
count += 1
|
||||
turnover += basket.price / 100
|
||||
basket_index += 1
|
||||
basket = baskets[basket_index]
|
||||
labels.append(current)
|
||||
counts.append(count)
|
||||
turnovers.append(turnover)
|
||||
current = end_slot
|
||||
fig, ax1 = plt.subplots()
|
||||
hours_in_day = 24
|
||||
color = "tab:orange"
|
||||
ax1.bar(labels, counts, width=1 / hours_in_day, color=color)
|
||||
ax1.tick_params(axis="x", rotation=15)
|
||||
ax1.set_ylabel(_("Basket count by hour"), color=color)
|
||||
|
||||
color = "tab:blue"
|
||||
ax2 = ax1.twinx()
|
||||
ax2.bar(labels, turnovers, width=1 / (hours_in_day * 2), color=color)
|
||||
ax2.set_ylabel(_("Turnover by hour"), color=color)
|
||||
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter("%.2f€"))
|
||||
|
||||
fig.tight_layout()
|
||||
image_data = StringIO()
|
||||
fig.savefig(image_data, format="svg")
|
||||
image_data.seek(0)
|
||||
return image_data.getvalue()
|
||||
|
|
Loading…
Reference in a new issue