Load graphs asynchronously

This commit is contained in:
Gabriel Augendre 2022-09-25 19:51:21 +02:00
parent 68ba920882
commit ae5860fc17
11 changed files with 66 additions and 20 deletions

14
poetry.lock generated
View file

@ -342,6 +342,17 @@ python-versions = ">=3.6"
[package.dependencies] [package.dependencies]
Django = ">=3.2" Django = ">=3.2"
[[package]]
name = "django-htmx"
version = "1.12.2"
description = "Extensions for using Django with htmx."
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
Django = ">=3.2"
[[package]] [[package]]
name = "factory-boy" name = "factory-boy"
version = "3.2.1" version = "3.2.1"
@ -1153,7 +1164,7 @@ h11 = ">=0.9.0,<1"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "e21ae8f93aefc631a43ccf43b79a3660ca11c6870677d60878b0adc88303953d" content-hash = "e0332f1446a90fc3a5dc2fa2ca5d1745eb0d90d8f033094d79b2df9376b79639"
[metadata.files] [metadata.files]
ansicon = [] ansicon = []
@ -1276,6 +1287,7 @@ django-environ = [
{file = "django_environ-0.9.0-py2.py3-none-any.whl", hash = "sha256:f21a5ef8cc603da1870bbf9a09b7e5577ab5f6da451b843dbcc721a7bca6b3d9"}, {file = "django_environ-0.9.0-py2.py3-none-any.whl", hash = "sha256:f21a5ef8cc603da1870bbf9a09b7e5577ab5f6da451b843dbcc721a7bca6b3d9"},
] ]
django-extensions = [] django-extensions = []
django-htmx = []
factory-boy = [] factory-boy = []
faker = [] faker = []
filelock = [] filelock = []

View file

@ -21,6 +21,7 @@ django-crispy-forms = "^1.14.0"
crispy-bootstrap5 = "^0.6" crispy-bootstrap5 = "^0.6"
matplotlib = "^3.5.1" matplotlib = "^3.5.1"
freezegun = "^1.2.1" freezegun = "^1.2.1"
django-htmx = "^1.12.2"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pre-commit = "^2.7" pre-commit = "^2.7"

View file

@ -89,6 +89,7 @@ INSTALLED_APPS = [
"crispy_forms", "crispy_forms",
"crispy_bootstrap5", "crispy_bootstrap5",
"django_extensions", "django_extensions",
"django_htmx",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -102,6 +103,7 @@ MIDDLEWARE = [
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"csp.middleware.CSPMiddleware", "csp.middleware.CSPMiddleware",
"django_htmx.middleware.HtmxMiddleware",
] ]
try: try:

File diff suppressed because one or more lines are too long

View file

@ -28,5 +28,7 @@
{% endblock %} {% endblock %}
</div> </div>
<script src="{% static "vendor/bootstrap-5.1.3-dist/js/bootstrap.bundle.min.js" %}"></script> <script src="{% static "vendor/bootstrap-5.1.3-dist/js/bootstrap.bundle.min.js" %}"></script>
{% block extrascript %}
{% endblock %}
</body> </body>
</html> </html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -1,7 +1,5 @@
{% extends "common/base.html" %} {% extends "common/base.html" %}
{% load static %} {% load static i18n purchase django_htmx %}
{% load i18n %}
{% load purchase %}
{% block extrahead %} {% block extrahead %}
<link rel="stylesheet" href="{% static "purchase/css/reports.css" %}"> <link rel="stylesheet" href="{% static "purchase/css/reports.css" %}">
@ -29,18 +27,20 @@
{% endfor %} {% endfor %}
</ul> </ul>
{{ by_hour_plot|safe }} {% include "purchase/snippets/htmx_plot.html" with url='purchase:by_hour_plot' %}
<h2>{% translate "Products" %}</h2> <h2>{% translate "Products" %}</h2>
{% include "purchase/snippets/report_products.html" %} {% include "purchase/snippets/report_products.html" %}
{{ products_plot|safe }} {% include "purchase/snippets/htmx_plot.html" with url='purchase:products_plots' %}
{{ 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" %}
<h2>{% translate "Baskets without payment method" %}</h2> <h2>{% translate "Baskets without payment method" %}</h2>
{% include "purchase/snippets/report_no_payment_method.html" %} {% include "purchase/snippets/report_no_payment_method.html" %}
{% endblock %}
{% block extrascript %}
<script src="{% static 'vendor/htmx-1.8.0/htmx.min.js' %}" defer></script>
{% django_htmx_script %}
{% endblock %} {% endblock %}

View file

@ -0,0 +1,7 @@
{% load static %}
<div hx-get="{% url url %}"
hx-trigger="load"
hx-swap="outerHTML"
>
<img class="htmx-indicator" src="{% static 'purchase/spinner.gif' %}" alt="Spinner">
</div>

View file

@ -0,0 +1,3 @@
{% for plot in plots %}
{{ plot|safe }}
{% endfor %}

View file

@ -1,7 +1,7 @@
from django.urls import path from django.urls import path
from purchase.views import delete_basket, list_baskets, new_basket, update_basket from purchase.views import delete_basket, list_baskets, new_basket, update_basket
from purchase.views.reports import reports from purchase.views.reports import by_hour_plot_view, products_plots_view, reports
app_name = "purchase" app_name = "purchase"
urlpatterns = [ urlpatterns = [
@ -10,4 +10,7 @@ urlpatterns = [
path("<int:pk>/update/", update_basket, name="update"), path("<int:pk>/update/", update_basket, name="update"),
path("<int:pk>/delete/", delete_basket, name="delete"), path("<int:pk>/delete/", delete_basket, name="delete"),
path("reports/", reports, name="reports"), path("reports/", reports, name="reports"),
# plots
path("reports/products_plots/", products_plots_view, name="products_plots"),
path("reports/by_hour_plot/", by_hour_plot_view, name="by_hour_plot"),
] ]

View file

@ -7,6 +7,7 @@ import numpy as np
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.shortcuts import render
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
@ -18,7 +19,30 @@ from matplotlib.figure import Figure
from purchase.models import Basket, PaymentMethod, Product, ProductQuerySet from purchase.models import Basket, PaymentMethod, Product, ProductQuerySet
matplotlib.use("TkAgg") matplotlib.use("Agg")
@permission_required("purchase.view_basket")
def products_plots_view(request):
products = Product.objects.with_turnover().with_sold()
(
products_plot,
products_sold_pie,
products_turnover_pie,
) = get_products_plots(products)
context = {
"plots": [products_plot, products_sold_pie, products_turnover_pie],
}
return render(request, "purchase/snippets/plots.html", context)
@permission_required("purchase.view_basket")
def by_hour_plot_view(request):
baskets = list(Basket.objects.priced().order_by("created_at"))
context = {
"plots": [by_hour_plot(baskets)],
}
return render(request, "purchase/snippets/plots.html", context)
@permission_required("purchase.view_basket") @permission_required("purchase.view_basket")
@ -36,11 +60,6 @@ def reports(request):
turnover_by_day = {date: Basket.objects.by_date(date).turnover() for date in dates} turnover_by_day = {date: Basket.objects.by_date(date).turnover() for date in dates}
products = Product.objects.with_turnover().with_sold() products = Product.objects.with_turnover().with_sold()
(
products_plot,
products_sold_pie,
products_turnover_pie,
) = get_products_plots(products)
context = { context = {
"turnover": Basket.objects.turnover(), "turnover": Basket.objects.turnover(),
@ -48,10 +67,6 @@ def reports(request):
"average_basket": Basket.objects.average_basket(), "average_basket": Basket.objects.average_basket(),
"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_sold_pie": products_sold_pie,
"products_turnover_pie": products_turnover_pie,
"by_hour_plot": by_hour_plot(baskets),
"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(),
} }