forked from DGNum/gestioCOF
Refactor py base stats and account balance stats
New mixin: PkUrlMixin - use with SingleObjectMixin standard django mixin (used by DetailView...) - `get_object` use field declared in `pk_url_kwarg` to get... the object SingleResumeStat - clean (part of) py code AccountStatBalanceList - renamed from `AccountStatBalanceAll` - url modified - add permission checking (only the connected user can get balance stats manifest) - clean py code AccountStatBalance - cleaner filtering management - merge urls using this class - clean py code
This commit is contained in:
parent
f6022ecf7d
commit
87b9db520f
2 changed files with 141 additions and 142 deletions
13
kfet/urls.py
13
kfet/urls.py
|
@ -82,15 +82,12 @@ urlpatterns = [
|
||||||
views.AccountStatLastDay.as_view(),
|
views.AccountStatLastDay.as_view(),
|
||||||
name = 'kfet.account.stat.last.day'),
|
name = 'kfet.account.stat.last.day'),
|
||||||
|
|
||||||
url('^accounts/(?P<trigramme>.{3})/stat/balance/$',
|
url(r'^accounts/(?P<trigramme>.{3})/stat/balance/list$',
|
||||||
views.AccountStatBalanceAll.as_view(),
|
views.AccountStatBalanceList.as_view(),
|
||||||
name = 'kfet.account.stat.balance'),
|
name='kfet.account.stat.balance.list'),
|
||||||
url('^accounts/(?P<trigramme>.{3})/stat/balance/d/(?P<nb_date>\d*)/$',
|
url(r'^accounts/(?P<trigramme>.{3})/stat/balance$',
|
||||||
views.AccountStatBalance.as_view(),
|
views.AccountStatBalance.as_view(),
|
||||||
name = 'kfet.account.stat.balance.days'),
|
name='kfet.account.stat.balance'),
|
||||||
url('^accounts/(?P<trigramme>.{3})/stat/balance/anytime/$',
|
|
||||||
views.AccountStatBalance.as_view(),
|
|
||||||
name = 'kfet.account.stat.balance.anytime'),
|
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
# Checkout urls
|
# Checkout urls
|
||||||
|
|
270
kfet/views.py
270
kfet/views.py
|
@ -1,12 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.views.generic import ListView, DetailView, TemplateView
|
from django.views.generic import ListView, DetailView, TemplateView
|
||||||
from django.views.generic.list import BaseListView, MultipleObjectTemplateResponseMixin
|
from django.views.generic.detail import BaseDetailView
|
||||||
from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResponseMixin
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
|
||||||
from django.core.urlresolvers import reverse, reverse_lazy
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
|
@ -2041,49 +2042,46 @@ class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
||||||
return self.render_to_json_response(context)
|
return self.render_to_json_response(context)
|
||||||
|
|
||||||
|
|
||||||
class ObjectResumeStat(JSONDetailView):
|
class PkUrlMixin(object):
|
||||||
|
|
||||||
|
def get_object(self, *args, **kwargs):
|
||||||
|
get_by = self.kwargs.get(self.pk_url_kwarg)
|
||||||
|
return get_object_or_404(self.model, **{self.pk_url_kwarg: get_by})
|
||||||
|
|
||||||
|
|
||||||
|
class SingleResumeStat(JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Summarize all the stats of an object
|
Summarize all the stats of an object
|
||||||
Handles JSONResponse
|
Handles JSONResponse
|
||||||
|
|
||||||
"""
|
"""
|
||||||
context_object_name = ''
|
|
||||||
id_prefix = ''
|
id_prefix = ''
|
||||||
# nombre de vues à résumer
|
|
||||||
nb_stat = 2
|
|
||||||
# Le combienième est celui par defaut ?
|
|
||||||
# (entre 0 et nb_stat-1)
|
|
||||||
nb_default = 0
|
nb_default = 0
|
||||||
stat_labels = ['stat_1', 'stat_2']
|
|
||||||
stat_urls = ['url_1', 'url_2']
|
|
||||||
|
|
||||||
# sert à renverser les urls
|
stats = []
|
||||||
# utile de le surcharger quand l'url prend d'autres arguments que l'id
|
url_stat = None
|
||||||
def get_object_url_kwargs(self, **kwargs):
|
|
||||||
return {'pk': self.object.id}
|
|
||||||
|
|
||||||
def url_kwargs(self, **kwargs):
|
|
||||||
return [{}] * self.nb_stat
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
# On n'hérite pas
|
# On n'hérite pas
|
||||||
object_id = self.object.id
|
object_id = self.object.id
|
||||||
url_kwargs = self.url_kwargs()
|
|
||||||
context = {}
|
context = {}
|
||||||
stats = {}
|
stats = []
|
||||||
for i in range(self.nb_stat):
|
prefix = '{}_{}'.format(self.id_prefix, object_id)
|
||||||
stats[i] = {
|
for i, stat_def in enumerate(self.stats):
|
||||||
'label': self.stat_labels[i],
|
url_pk = getattr(self.object, self.pk_url_kwarg)
|
||||||
'btn': "btn_%s_%d_%d" % (self.id_prefix,
|
url_params_d = stat_def.get('url_params', {})
|
||||||
object_id,
|
if len(url_params_d) > 0:
|
||||||
i),
|
url_params = '?{}'.format(urlencode(url_params_d))
|
||||||
'url': reverse(self.stat_urls[i],
|
else:
|
||||||
kwargs=dict(
|
url_params = ''
|
||||||
self.get_object_url_kwargs(),
|
stats.append({
|
||||||
**url_kwargs[i]
|
'label': stat_def['label'],
|
||||||
),
|
'btn': 'btn_{}_{}'.format(prefix, i),
|
||||||
),
|
'url': '{url}{params}'.format(
|
||||||
}
|
url=reverse(self.url_stat, args=[url_pk]),
|
||||||
prefix = "%s_%d" % (self.id_prefix, object_id)
|
params=url_params,
|
||||||
|
),
|
||||||
|
})
|
||||||
context['id_prefix'] = prefix
|
context['id_prefix'] = prefix
|
||||||
context['content_id'] = "content_%s" % prefix
|
context['content_id'] = "content_%s" % prefix
|
||||||
context['stats'] = stats
|
context['stats'] = stats
|
||||||
|
@ -2100,39 +2098,47 @@ ID_PREFIX_ACC_BALANCE = "balance_acc"
|
||||||
|
|
||||||
# Un résumé de toutes les vues ArticleStatBalance
|
# Un résumé de toutes les vues ArticleStatBalance
|
||||||
# REND DU JSON
|
# REND DU JSON
|
||||||
class AccountStatBalanceAll(ObjectResumeStat):
|
class AccountStatBalanceList(PkUrlMixin, SingleResumeStat):
|
||||||
model = Account
|
model = Account
|
||||||
context_object_name = 'account'
|
context_object_name = 'account'
|
||||||
trigramme_url_kwarg = 'trigramme'
|
pk_url_kwarg = 'trigramme'
|
||||||
|
url_stat = 'kfet.account.stat.balance'
|
||||||
id_prefix = ID_PREFIX_ACC_BALANCE
|
id_prefix = ID_PREFIX_ACC_BALANCE
|
||||||
nb_stat = 5
|
stats = [
|
||||||
|
{
|
||||||
|
'label': 'Tout le temps',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': '1 an',
|
||||||
|
'url_params': {'last_days': 365},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': '6 mois',
|
||||||
|
'url_params': {'last_days': 183},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': '3 mois',
|
||||||
|
'url_params': {'last_days': 90},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': '30 jours',
|
||||||
|
'url_params': {'last_days': 30},
|
||||||
|
},
|
||||||
|
]
|
||||||
nb_default = 0
|
nb_default = 0
|
||||||
stat_labels = ["Tout le temps", "1 an", "6 mois", "3 mois", "30 jours"]
|
|
||||||
stat_urls = ['kfet.account.stat.balance.anytime'] \
|
|
||||||
+ ['kfet.account.stat.balance.days'] * 4
|
|
||||||
|
|
||||||
def get_object(self, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
trigramme = self.kwargs.get(self.trigramme_url_kwarg)
|
obj = super().get_object(*args, **kwargs)
|
||||||
return get_object_or_404(Account, trigramme=trigramme)
|
if self.request.user != obj.user:
|
||||||
|
raise PermissionDenied
|
||||||
def get_object_url_kwargs(self, **kwargs):
|
return obj
|
||||||
return {'trigramme': self.object.trigramme}
|
|
||||||
|
|
||||||
def url_kwargs(self, **kwargs):
|
|
||||||
context_list = (super(AccountStatBalanceAll, self)
|
|
||||||
.url_kwargs(**kwargs))
|
|
||||||
context_list[1] = {'nb_date': 365}
|
|
||||||
context_list[2] = {'nb_date': 183}
|
|
||||||
context_list[3] = {'nb_date': 90}
|
|
||||||
context_list[4] = {'nb_date': 30}
|
|
||||||
return context_list
|
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(AccountStatBalanceAll, self).dispatch(*args, **kwargs)
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AccountStatBalance(JSONDetailView):
|
class AccountStatBalance(PkUrlMixin, JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Returns a JSON containing the evolution a the personnal
|
Returns a JSON containing the evolution a the personnal
|
||||||
balance of a trigramme between timezone.now() and `nb_days`
|
balance of a trigramme between timezone.now() and `nb_days`
|
||||||
|
@ -2141,44 +2147,38 @@ class AccountStatBalance(JSONDetailView):
|
||||||
does not takes into account the balance offset
|
does not takes into account the balance offset
|
||||||
"""
|
"""
|
||||||
model = Account
|
model = Account
|
||||||
trigramme_url_kwarg = 'trigramme'
|
pk_url_kwarg = 'trigramme'
|
||||||
nb_date_url_kwargs = 'nb_date'
|
|
||||||
context_object_name = 'account'
|
context_object_name = 'account'
|
||||||
id_prefix = ""
|
|
||||||
|
|
||||||
def get_object(self, **kwargs):
|
def get_changes_list(self, last_days=None, begin_date=None, end_date=None):
|
||||||
trigramme = self.kwargs.get(self.trigramme_url_kwarg)
|
|
||||||
return get_object_or_404(Account, trigramme=trigramme)
|
|
||||||
|
|
||||||
def get_changes_list(self, **kwargs):
|
|
||||||
account = self.object
|
account = self.object
|
||||||
nb_date = self.kwargs.get(self.nb_date_url_kwargs, None)
|
|
||||||
end_date = this_morning()
|
# prepare filters
|
||||||
if nb_date is None:
|
if end_date is None:
|
||||||
begin_date = timezone.datetime(year=1980, month=1, day=1)
|
end_date = this_morning()
|
||||||
anytime = True
|
|
||||||
else:
|
if last_days is not None:
|
||||||
begin_date = this_morning() \
|
begin_date = end_date - timezone.timedelta(days=last_days)
|
||||||
- timezone.timedelta(days=int(nb_date))
|
|
||||||
anytime = False
|
# prepare querysets
|
||||||
# On récupère les opérations
|
|
||||||
# TODO: retirer les opgroup dont tous les op sont annulées
|
# TODO: retirer les opgroup dont tous les op sont annulées
|
||||||
opgroups = list(OperationGroup.objects
|
opegroups = OperationGroup.objects.filter(on_acc=account)
|
||||||
.filter(on_acc=account)
|
recv_transfers = Transfer.objects.filter(to_acc=account,
|
||||||
.filter(at__gte=begin_date)
|
canceled_at=None)
|
||||||
.filter(at__lte=end_date))
|
sent_transfers = Transfer.objects.filter(from_acc=account,
|
||||||
# On récupère les transferts reçus
|
canceled_at=None)
|
||||||
received_transfers = list(Transfer.objects
|
|
||||||
.filter(to_acc=account)
|
# apply filters
|
||||||
.filter(canceled_at=None)
|
if begin_date is not None:
|
||||||
.filter(group__at__gte=begin_date)
|
opegroups = opegroups.filter(at__gte=begin_date)
|
||||||
.filter(group__at__lte=end_date))
|
recv_transfers = recv_transfers.filter(group__at__gte=begin_date)
|
||||||
# On récupère les transferts émis
|
sent_transfers = sent_transfers.filter(group__at__gte=begin_date)
|
||||||
emitted_transfers = list(Transfer.objects
|
|
||||||
.filter(from_acc=account)
|
if end_date is not None:
|
||||||
.filter(canceled_at=None)
|
opegroups = opegroups.filter(at__lte=end_date)
|
||||||
.filter(group__at__gte=begin_date)
|
recv_transfers = recv_transfers.filter(group__at__lte=end_date)
|
||||||
.filter(group__at__lte=end_date))
|
sent_transfers = sent_transfers.filter(group__at__lte=end_date)
|
||||||
|
|
||||||
# On transforme tout ça en une liste de dictionnaires sous la forme
|
# On transforme tout ça en une liste de dictionnaires sous la forme
|
||||||
# {'at': date,
|
# {'at': date,
|
||||||
# 'amount': changement de la balance (négatif si diminue la balance,
|
# 'amount': changement de la balance (négatif si diminue la balance,
|
||||||
|
@ -2188,72 +2188,74 @@ class AccountStatBalance(JSONDetailView):
|
||||||
# sera mis à jour lors d'une
|
# sera mis à jour lors d'une
|
||||||
# autre passe)
|
# autre passe)
|
||||||
# }
|
# }
|
||||||
actions = [
|
actions = []
|
||||||
# Maintenant (à changer si on gère autre chose que now)
|
if begin_date is not None:
|
||||||
{
|
actions.append({
|
||||||
|
'at': begin_date.isoformat(),
|
||||||
|
'amount': 0,
|
||||||
|
'label': "début",
|
||||||
|
'balance': 0,
|
||||||
|
})
|
||||||
|
if end_date is not None:
|
||||||
|
actions.append({
|
||||||
'at': end_date.isoformat(),
|
'at': end_date.isoformat(),
|
||||||
'amout': 0,
|
'amount': 0,
|
||||||
'label': "actuel",
|
'label': "actuel",
|
||||||
'balance': 0,
|
'balance': 0,
|
||||||
}
|
})
|
||||||
] + [
|
|
||||||
|
actions += [
|
||||||
{
|
{
|
||||||
'at': op.at.isoformat(),
|
'at': ope_grp.at.isoformat(),
|
||||||
'amount': op.amount,
|
'amount': ope_grp.amount,
|
||||||
'label': str(op),
|
'label': str(ope_grp),
|
||||||
'balance': 0,
|
'balance': 0,
|
||||||
} for op in opgroups
|
} for ope_grp in opegroups
|
||||||
] + [
|
] + [
|
||||||
{
|
{
|
||||||
'at': tr.group.at.isoformat(),
|
'at': tr.group.at.isoformat(),
|
||||||
'amount': tr.amount,
|
'amount': tr.amount,
|
||||||
'label': "%d€: %s -> %s" % (tr.amount,
|
'label': str(tr),
|
||||||
tr.from_acc.trigramme,
|
|
||||||
tr.to_acc.trigramme),
|
|
||||||
'balance': 0,
|
'balance': 0,
|
||||||
} for tr in received_transfers
|
} for tr in recv_transfers
|
||||||
] + [
|
] + [
|
||||||
{
|
{
|
||||||
'at': tr.group.at.isoformat(),
|
'at': tr.group.at.isoformat(),
|
||||||
'amount': -tr.amount,
|
'amount': -tr.amount,
|
||||||
'label': "%d€: %s -> %s" % (tr.amount,
|
'label': str(tr),
|
||||||
tr.from_acc.trigramme,
|
|
||||||
tr.to_acc.trigramme),
|
|
||||||
'balance': 0,
|
'balance': 0,
|
||||||
} for tr in emitted_transfers
|
} for tr in sent_transfers
|
||||||
]
|
]
|
||||||
if not anytime:
|
|
||||||
actions += [
|
|
||||||
# Date de début :
|
|
||||||
{
|
|
||||||
'at': begin_date.isoformat(),
|
|
||||||
'amount': 0,
|
|
||||||
'label': "début",
|
|
||||||
'balance': 0,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
# Maintenant on trie la liste des actions par ordre du plus récent
|
# Maintenant on trie la liste des actions par ordre du plus récent
|
||||||
# an plus ancien et on met à jour la balance
|
# an plus ancien et on met à jour la balance
|
||||||
actions = sorted(actions, key=lambda k: k['at'], reverse=True)
|
actions = sorted(actions, key=lambda k: k['at'], reverse=True)
|
||||||
actions[0]['balance'] = account.balance
|
actions[0]['balance'] = account.balance
|
||||||
for i in range(len(actions)-1):
|
for i in range(len(actions)-1):
|
||||||
actions[i+1]['balance'] = actions[i]['balance'] \
|
actions[i+1]['balance'] = \
|
||||||
- actions[i+1]['amount']
|
actions[i]['balance'] - actions[i+1]['amount']
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
context = {}
|
context = {}
|
||||||
changes = self.get_changes_list()
|
|
||||||
nb_days = self.kwargs.get(self.nb_date_url_kwargs, None)
|
last_days = self.request.GET.get('last_days', None)
|
||||||
if nb_days is None:
|
if last_days is not None:
|
||||||
nb_days_string = 'anytime'
|
last_days = int(last_days)
|
||||||
else:
|
begin_date = self.request.GET.get('begin_date', None)
|
||||||
nb_days_string = str(int(nb_days))
|
end_date = self.request.GET.get('end_date', None)
|
||||||
context['charts'] = [ { "color": "rgb(255, 99, 132)",
|
|
||||||
"label": "Balance",
|
changes = self.get_changes_list(
|
||||||
"values": changes } ]
|
last_days=last_days,
|
||||||
|
begin_date=begin_date, end_date=end_date,
|
||||||
|
)
|
||||||
|
|
||||||
|
context['charts'] = [{
|
||||||
|
"color": "rgb(255, 99, 132)",
|
||||||
|
"label": "Balance",
|
||||||
|
"values": changes,
|
||||||
|
}]
|
||||||
context['is_time_chart'] = True
|
context['is_time_chart'] = True
|
||||||
context['min_date'] = changes[len(changes)-1]['at']
|
context['min_date'] = changes[-1]['at']
|
||||||
context['max_date'] = changes[0]['at']
|
context['max_date'] = changes[0]['at']
|
||||||
# TODO: offset
|
# TODO: offset
|
||||||
return context
|
return context
|
||||||
|
@ -2274,7 +2276,7 @@ ID_PREFIX_ACC_LAST_MONTHS = "last_months_acc"
|
||||||
|
|
||||||
# Un résumé de toutes les vues ArticleStatLast
|
# Un résumé de toutes les vues ArticleStatLast
|
||||||
# NE REND PAS DE JSON
|
# NE REND PAS DE JSON
|
||||||
class AccountStatLastAll(ObjectResumeStat):
|
class AccountStatLastAll(SingleResumeStat):
|
||||||
model = Account
|
model = Account
|
||||||
context_object_name = 'account'
|
context_object_name = 'account'
|
||||||
trigramme_url_kwarg = 'trigramme'
|
trigramme_url_kwarg = 'trigramme'
|
||||||
|
@ -2419,7 +2421,7 @@ ID_PREFIX_ART_LAST_MONTHS = "last_months_art"
|
||||||
|
|
||||||
# Un résumé de toutes les vues ArticleStatLast
|
# Un résumé de toutes les vues ArticleStatLast
|
||||||
# NE REND PAS DE JSON
|
# NE REND PAS DE JSON
|
||||||
class ArticleStatLastAll(ObjectResumeStat):
|
class ArticleStatLastAll(SingleResumeStat):
|
||||||
model = Article
|
model = Article
|
||||||
context_object_name = 'article'
|
context_object_name = 'article'
|
||||||
id_prefix = ID_PREFIX_ART_LAST
|
id_prefix = ID_PREFIX_ART_LAST
|
||||||
|
|
Loading…
Reference in a new issue