1 Commits

Author SHA1 Message Date
imperosol
00acdcd1a5 refactor: remove useless Group methods 2025-11-24 18:15:28 +01:00
8 changed files with 106 additions and 83 deletions

View File

@@ -38,7 +38,6 @@ from django.contrib.auth.models import AnonymousUser as AuthAnonymousUser
from django.contrib.auth.models import Group as AuthGroup
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core import validators
from django.core.cache import cache
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.files import File
from django.core.files.base import ContentFile
@@ -77,16 +76,6 @@ class Group(AuthGroup):
def get_absolute_url(self) -> str:
return reverse("core:group_list")
def save(self, *args, **kwargs) -> None:
super().save(*args, **kwargs)
cache.set(f"sith_group_{self.id}", self)
cache.set(f"sith_group_{self.name.replace(' ', '_')}", self)
def delete(self, *args, **kwargs) -> None:
super().delete(*args, **kwargs)
cache.delete(f"sith_group_{self.id}")
cache.delete(f"sith_group_{self.name.replace(' ', '_')}")
def validate_promo(value: int) -> None:
last_promo = get_last_promo()

View File

@@ -11,35 +11,32 @@
{% block content %}
<div class="container">
<div class="row">
{% if total_perm_time %}
{% if profile.permanencies %}
<div>
<h3>{% trans %}Permanencies{% endtrans %}</h3>
<div class="flexed">
{% for perm in perm_time %}
<div>
<span>{{ perm["counter__name"] }} :</span>
<span>{{ perm["total"]|format_timedelta }}</span>
</div>
{% endfor %}
<div><b>Total :</b><b>{{ total_perm_time|format_timedelta }}</b></div>
<div><span>Foyer :</span><span>{{ total_foyer_time }}</span></div>
<div><span>Gommette :</span><span>{{ total_gommette_time }}</span></div>
<div><span>MDE :</span><span>{{ total_mde_time }}</span></div>
<div><b>Total :</b><b>{{ total_perm_time }}</b></div>
</div>
</div>
{% endif %}
<div>
<h3>{% trans %}Buyings{% endtrans %}</h3>
<div class="flexed">
{% for sum in purchase_sums %}
<div><span>Foyer :</span><span>{{ total_foyer_buyings }}&nbsp;€</span></div>
<div><span>Gommette :</span><span>{{ total_gommette_buyings }}&nbsp;€</span></div>
<div><span>MDE :</span><span>{{ total_mde_buyings }}&nbsp;€</span></div>
<div><b>Total :</b><b>{{ total_foyer_buyings + total_gommette_buyings + total_mde_buyings }}&nbsp;€</b>
</div>
</div>
</div>
</div>
<div>
<span>{{ sum["counter__name"] }}</span>
<span>{{ sum["total"] }} €</span>
</div>
{% endfor %}
<div><b>Total : </b><b>{{ total_purchases }} €</b></div>
</div>
</div>
</div>
<div>
<h3>{% trans %}Product top 15{% endtrans %}</h3>
<h3>{% trans %}Product top 10{% endtrans %}</h3>
<table>
<thead>
<tr>

View File

@@ -55,17 +55,31 @@ def phonenumber(
return value
@register.filter(name="truncate_time")
def truncate_time(value, time_unit):
"""Remove everything in the time format lower than the specified unit.
Args:
value: the value to truncate
time_unit: the lowest unit to display
"""
value = str(value)
return {
"millis": lambda: value.split(".")[0],
"seconds": lambda: value.rsplit(":", maxsplit=1)[0],
"minutes": lambda: value.split(":", maxsplit=1)[0],
"hours": lambda: value.rsplit(" ")[0],
}[time_unit]()
@register.filter(name="format_timedelta")
def format_timedelta(value: datetime.timedelta) -> str:
value = value - datetime.timedelta(microseconds=value.microseconds)
days = value.days
if days == 0:
return str(value)
remainder = value - datetime.timedelta(days=days)
return ngettext(
"%(nb_days)d day, %(remainder)s",
"%(nb_days)d days, %(remainder)s",
days,
"%(nb_days)d day, %(remainder)s", "%(nb_days)d days, %(remainder)s", days
) % {"nb_days": days, "remainder": str(remainder)}

View File

@@ -1,4 +1,3 @@
import itertools
from datetime import timedelta
from unittest import mock
@@ -24,7 +23,7 @@ from core.baker_recipes import (
from core.models import AnonymousUser, Group, User
from core.views import UserTabsMixin
from counter.baker_recipes import sale_recipe
from counter.models import Counter, Customer, Permanency, Refilling, Selling
from counter.models import Counter, Customer, Refilling, Selling
from counter.utils import is_logged_in_counter
from eboutic.models import Invoice, InvoiceItem
@@ -425,28 +424,3 @@ class TestUserQuerySetViewableBy:
user = user_factory()
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
assert not viewable.exists()
@pytest.mark.django_db
def test_user_stats(client: Client):
user = subscriber_user.make()
baker.make(Refilling, customer=user.customer, amount=99999)
bars = [b[0] for b in settings.SITH_COUNTER_BARS]
baker.make(
Permanency,
end=now() - timedelta(days=5),
start=now() - timedelta(days=5, hours=3),
counter_id=itertools.cycle(bars),
_quantity=5,
_bulk_create=True,
)
sale_recipe.make(
counter_id=itertools.cycle(bars),
customer=user.customer,
unit_price=1,
quantity=1,
_quantity=5,
)
client.force_login(user)
response = client.get(reverse("core:user_stats", kwargs={"user_id": user.id}))
assert response.status_code == 200

View File

@@ -22,9 +22,9 @@
#
#
import itertools
from datetime import timedelta
# This file contains all the views that concern the user model
from datetime import date, timedelta
from operator import itemgetter
from smtplib import SMTPException
@@ -32,7 +32,7 @@ from django.contrib.auth import login, views
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.db.models import DateField, F, QuerySet, Sum
from django.db.models import DateField, QuerySet
from django.db.models.functions import Trunc
from django.forms.models import modelform_factory
from django.http import Http404
@@ -66,8 +66,9 @@ from core.views.forms import (
UserProfileForm,
)
from core.views.mixins import TabedViewMixin, UseFragmentsMixin
from counter.models import Refilling, Selling
from counter.models import Counter, Refilling, Selling
from eboutic.models import Invoice
from subscription.models import Subscription
from trombi.views import UserTrombiForm
@@ -352,40 +353,87 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
context_object_name = "profile"
template_name = "core/user_stats.jinja"
current_tab = "stats"
queryset = User.objects.exclude(customer=None).select_related("customer")
def dispatch(self, request, *arg, **kwargs):
profile = self.get_object()
if not hasattr(profile, "customer"):
raise Http404
if not (
profile == request.user or request.user.has_perm("counter.view_customer")
):
raise PermissionDenied
return super().dispatch(request, *arg, **kwargs)
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
from django.db.models import Sum
kwargs["perm_time"] = list(
self.object.permanencies.filter(end__isnull=False, counter__type="BAR")
.values("counter", "counter__name")
.annotate(total=Sum(F("end") - F("start"), default=timedelta(seconds=0)))
.order_by("-total")
)
foyer = Counter.objects.filter(name="Foyer").first()
mde = Counter.objects.filter(name="MDE").first()
gommette = Counter.objects.filter(name="La Gommette").first()
semester_start = Subscription.compute_start(d=date.today(), duration=3)
kwargs["total_perm_time"] = sum(
[perm["total"] for perm in kwargs["perm_time"]], start=timedelta(seconds=0)
[p.end - p.start for p in self.object.permanencies.exclude(end=None)],
timedelta(),
)
kwargs["purchase_sums"] = list(
self.object.customer.buyings.filter(counter__type="BAR")
.values("counter", "counter__name")
.annotate(total=Sum(F("unit_price") * F("quantity")))
.order_by("-total")
kwargs["total_foyer_time"] = sum(
[
p.end - p.start
for p in self.object.permanencies.filter(counter=foyer).exclude(
end=None
)
],
timedelta(),
)
kwargs["total_mde_time"] = sum(
[
p.end - p.start
for p in self.object.permanencies.filter(counter=mde).exclude(end=None)
],
timedelta(),
)
kwargs["total_gommette_time"] = sum(
[
p.end - p.start
for p in self.object.permanencies.filter(counter=gommette).exclude(
end=None
)
],
timedelta(),
)
kwargs["total_foyer_buyings"] = sum(
[
b.unit_price * b.quantity
for b in self.object.customer.buyings.filter(
counter=foyer, date__gte=semester_start
)
]
)
kwargs["total_mde_buyings"] = sum(
[
b.unit_price * b.quantity
for b in self.object.customer.buyings.filter(
counter=mde, date__gte=semester_start
)
]
)
kwargs["total_gommette_buyings"] = sum(
[
b.unit_price * b.quantity
for b in self.object.customer.buyings.filter(
counter=gommette, date__gte=semester_start
)
]
)
kwargs["total_purchases"] = sum(s["total"] for s in kwargs["purchase_sums"])
kwargs["top_product"] = (
self.object.customer.buyings.values("product__name")
.annotate(product_sum=Sum("quantity"))
.exclude(product_sum=None)
.order_by("-product_sum")
.all()[:15]
.all()[:10]
)
return kwargs

View File

@@ -51,7 +51,7 @@
<td>{{ loop.index }}</td>
<td>{{ barman.name }} {% if barman.nickname %}({{ barman.nickname }}){% endif %}</td>
<td>{{ barman.promo or '' }}</td>
<td>{{ barman.perm_sum|format_timedelta }}</td>
<td>{{ barman.perm_sum|format_timedelta|truncate_time("millis") }}</td>
</tr>
{% endfor %}
</tbody>
@@ -73,7 +73,7 @@
<td>{{ loop.index }}</td>
<td>{{ barman.name }} {% if barman.nickname %}({{ barman.nickname }}){% endif %}</td>
<td>{{ barman.promo or '' }}</td>
<td>{{ barman.perm_sum|format_timedelta }}</td>
<td>{{ barman.perm_sum|format_timedelta|truncate_time("millis") }}</td>
</tr>
{% endfor %}
</tbody>

View File

@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-24 11:05+0100\n"
"POT-Creation-Date: 2025-11-19 21:00+0100\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
@@ -2658,8 +2658,8 @@ msgid "Buyings"
msgstr "Achats"
#: core/templates/core/user_stats.jinja
msgid "Product top 15"
msgstr "Top 15 produits"
msgid "Product top 10"
msgstr "Top 10 produits"
#: core/templates/core/user_stats.jinja
msgid "Product"
@@ -2819,8 +2819,8 @@ msgstr "Outils Trombi"
#, python-format
msgid "%(nb_days)d day, %(remainder)s"
msgid_plural "%(nb_days)d days, %(remainder)s"
msgstr[0] "%(nb_days)d jour, %(remainder)s"
msgstr[1] "%(nb_days)d jours, %(remainder)s"
msgstr[0] ""
msgstr[1] ""
#: core/views/files.py
msgid "Add a new folder"

View File

@@ -177,6 +177,7 @@ TEMPLATES = [
"filters": {
"markdown": "core.templatetags.renderer.markdown",
"phonenumber": "core.templatetags.renderer.phonenumber",
"truncate_time": "core.templatetags.renderer.truncate_time",
"format_timedelta": "core.templatetags.renderer.format_timedelta",
"add_attr": "core.templatetags.renderer.add_attr",
},