Plein de nettoyage partout

This commit is contained in:
Ludovic Stephan 2020-09-16 17:16:49 +02:00
parent 46f447ec5d
commit 8f9c94fe10
2 changed files with 58 additions and 93 deletions

View file

@ -1,6 +1,5 @@
from datetime import date, datetime, time, timedelta from datetime import date, datetime, time, timedelta
from dateutil.parser import parse as dateutil_parse
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.utils import timezone from django.utils import timezone
@ -144,67 +143,24 @@ class MonthScale(Scale):
return to_kfet_day(dt).replace(day=1) return to_kfet_day(dt).replace(day=1)
def scale_url_params(scales_def, **other_url_params): SCALE_CLASS_CHOICES = ((cls.name, cls.name) for cls in Scale.__subclasses__())
SCALE_DICT = {cls.name: cls for cls in Scale.__subclasses__()}
def scale_url_params(scales_def):
""" """
Convertit une spécification de scales en arguments GET utilisables par ScaleMixin. Convertit une spécification de scales en arguments GET utilisables par ScaleMixin.
La spécification est de la forme suivante : La spécification est de la forme suivante :
- scales_def : liste de champs de la forme (label, scale) - scales_def : liste de champs de la forme (label, scale, scale_args, default)
- scale_args : arguments à passer à Scale.__init__ - scale_args : arguments à passer à Scale.__init__
- other_url_params : paramètres GET supplémentaires - default : le graphe à montrer par défaut
""" """
params_list = [] params_list = []
for label, cls, params, default in scales_def: for label, cls, params, default in scales_def:
url_params = {"scale_name": cls.name} url_params = {"scale-name": cls.name}
url_params.update({"scale_" + key: value for key, value in params.items()}) url_params.update({"scale-" + key: value for key, value in params.items()})
url_params.update(other_url_params)
params_list.append(dict(label=label, url_params=url_params, default=default)) params_list.append(dict(label=label, url_params=url_params, default=default))
return params_list return params_list
class ScaleMixin(object):
def parse_scale_args(self):
"""
Récupère les paramètres de subdivision encodés dans une requête GET.
"""
scale_args = {}
name = self.request.GET.get("scale_name", None)
if name is not None:
scale_args["name"] = name
n_steps = self.request.GET.get("scale_n_steps", None)
if n_steps is not None:
scale_args["n_steps"] = int(n_steps)
begin = self.request.GET.get("scale_begin", None)
if begin is not None:
scale_args["begin"] = dateutil_parse(begin)
end = self.request.GET.get("scale_send", None)
if end is not None:
scale_args["end"] = dateutil_parse(end)
last = self.request.GET.get("scale_last", None)
if last is not None:
scale_args["last"] = last in ["true", "True", "1"] and True or False
return scale_args
def get_context_data(self, *args, **kwargs):
# On n'hérite pas
scale_args = self.parse_scale_args()
scale_name = scale_args.pop("name", None)
scale_cls = Scale.by_name(scale_name)
if scale_cls is None:
self.scale = self.get_default_scale()
else:
self.scale = scale_cls(**scale_args)
return {"labels": self.scale.get_labels()}
def get_default_scale(self):
return DayScale(n_steps=7, last=True)

View file

@ -1,4 +1,3 @@
import ast
import heapq import heapq
import statistics import statistics
from collections import defaultdict from collections import defaultdict
@ -12,6 +11,7 @@ from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.models import Permission, User from django.contrib.auth.models import Permission, User
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import SuspiciousOperation
from django.db import transaction from django.db import transaction
from django.db.models import Count, F, Prefetch, Q, Sum from django.db.models import Count, F, Prefetch, Q, Sum
from django.forms import formset_factory from django.forms import formset_factory
@ -36,6 +36,7 @@ from kfet.forms import (
AccountNoTriForm, AccountNoTriForm,
AccountPwdForm, AccountPwdForm,
AccountRestrictForm, AccountRestrictForm,
AccountStatForm,
AccountTriForm, AccountTriForm,
AddcostForm, AddcostForm,
ArticleForm, ArticleForm,
@ -55,6 +56,7 @@ from kfet.forms import (
KPsulOperationGroupForm, KPsulOperationGroupForm,
OrderArticleForm, OrderArticleForm,
OrderArticleToInventoryForm, OrderArticleToInventoryForm,
StatScaleForm,
TransferFormSet, TransferFormSet,
UserForm, UserForm,
UserGroupForm, UserGroupForm,
@ -78,7 +80,7 @@ from kfet.models import (
Transfer, Transfer,
TransferGroup, TransferGroup,
) )
from kfet.statistic import DayScale, MonthScale, ScaleMixin, WeekScale, scale_url_params from kfet.statistic import SCALE_DICT, DayScale, MonthScale, WeekScale, scale_url_params
from shared.views import AutocompleteView from shared.views import AutocompleteView
from .auth import KFET_GENERIC_TRIGRAMME from .auth import KFET_GENERIC_TRIGRAMME
@ -2295,6 +2297,26 @@ class UserAccountMixin:
return obj return obj
class ScaleMixin(object):
"""Mixin pour utiliser les outils de `kfet.statistic`."""
def get_context_data(self, *args, **kwargs):
# On n'hérite pas
form = StatScaleForm(self.request.GET, prefix="scale")
if not form.is_valid():
raise SuspiciousOperation(
"Invalid StatScaleForm. Did someone tamper with the GET parameters ?"
)
scale_name = form.cleaned_data.pop("name")
scale_cls = SCALE_DICT.get(scale_name)
self.scale = scale_cls(**form.cleaned_data)
return {"labels": self.scale.get_labels()}
# ----------------------- # -----------------------
# Evolution Balance perso # Evolution Balance perso
# ----------------------- # -----------------------
@ -2407,15 +2429,14 @@ class AccountStatBalance(UserAccountMixin, JSONDetailView):
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
context = {} context = {}
last_days = self.request.GET.get("last_days", None) form = AccountStatForm(self.request.GET)
if last_days is not None:
last_days = int(last_days)
begin_date = self.request.GET.get("begin_date", None)
end_date = self.request.GET.get("end_date", None)
changes = self.get_changes_list( if not form.is_valid():
last_days=last_days, begin_date=begin_date, end_date=end_date raise SuspiciousOperation(
) "Invalid AccountStatForm. Did someone tamper with the GET parameters ?"
)
changes = self.get_changes_list(**form.cleaned_data)
context["charts"] = [ context["charts"] = [
{"color": "rgb(200, 20, 60)", "label": "Balance", "values": changes} {"color": "rgb(200, 20, 60)", "label": "Balance", "values": changes}
@ -2470,28 +2491,16 @@ class AccountStatOperation(UserAccountMixin, ScaleMixin, JSONDetailView):
slug_url_kwarg = "trigramme" slug_url_kwarg = "trigramme"
slug_field = "trigramme" slug_field = "trigramme"
def get_operations(self, types=None):
# On selectionne les opérations qui correspondent
# à l'article en question et qui ne sont pas annulées
# puis on choisi pour chaques intervalle les opérations
# effectuées dans ces intervalles de temps
all_operations = (
Operation.objects.filter(group__on_acc=self.object, canceled_at=None)
.values("article_nb", "group__at")
.order_by("group__at")
)
if types is not None:
all_operations = all_operations.filter(type__in=types)
return all_operations
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs) context = super().get_context_data(*args, **kwargs)
types = self.request.GET.get("types", None) operations = (
if types is not None: Operation.objects.filter(
types = ast.literal_eval(types) type=Operation.PURCHASE, group__on_acc=self.object, canceled_at=None
)
operations = self.get_operations(types=types) .values("article_nb", "group__at")
.order_by("group__at")
)
# On compte les opérations # On compte les opérations
nb_ventes = self.scale.chunkify_qs( nb_ventes = self.scale.chunkify_qs(
operations, field="group__at", aggregate=Sum("article_nb") operations, field="group__at", aggregate=Sum("article_nb")
@ -2508,7 +2517,7 @@ class AccountStatOperation(UserAccountMixin, ScaleMixin, JSONDetailView):
# ------------------------ # ------------------------
# Article Satistiques Last # Article Statistiques Last
# ------------------------ # ------------------------
@ -2563,16 +2572,16 @@ class ArticleStatSales(ScaleMixin, JSONDetailView):
.values("group__at", "article_nb") .values("group__at", "article_nb")
.order_by("group__at") .order_by("group__at")
) )
liq_only = all_purchases.filter(group__on_acc__trigramme="LIQ") cof_accts = all_purchases.filter(group__on_acc__profile__is_cof=True)
liq_exclude = all_purchases.exclude(group__on_acc__trigramme="LIQ") noncof_accts = all_purchases.exclude(group__on_acc__profile__is_cof=True)
nb_liq = scale.chunkify_qs( nb_cof = scale.chunkify_qs(
liq_only, field="group__at", aggregate=Sum("article_nb") cof_accts, field="group__at", aggregate=Sum("article_nb")
) )
nb_accounts = scale.chunkify_qs( nb_noncof = scale.chunkify_qs(
liq_exclude, field="group__at", aggregate=Sum("article_nb") noncof_accts, field="group__at", aggregate=Sum("article_nb")
) )
nb_ventes = [n1 + n2 for n1, n2 in zip(nb_liq, nb_accounts)] nb_ventes = [n1 + n2 for n1, n2 in zip(nb_cof, nb_noncof)]
context["charts"] = [ context["charts"] = [
{ {
@ -2580,11 +2589,11 @@ class ArticleStatSales(ScaleMixin, JSONDetailView):
"label": "Toutes consommations", "label": "Toutes consommations",
"values": nb_ventes, "values": nb_ventes,
}, },
{"color": "rgb(54, 162, 235)", "label": "LIQ", "values": nb_liq}, {"color": "rgb(54, 162, 235)", "label": "LIQ", "values": nb_cof},
{ {
"color": "rgb(255, 205, 86)", "color": "rgb(255, 205, 86)",
"label": "Comptes K-Fêt", "label": "Comptes K-Fêt",
"values": nb_accounts, "values": nb_noncof,
}, },
] ]
return context return context