first fixes

This commit is contained in:
Qwann 2016-12-27 15:44:58 +01:00
parent 219835be17
commit 74f4d94f28
2 changed files with 398 additions and 406 deletions

View file

@ -1,44 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils import timezone from django.utils import timezone
from django.utils.decorators import method_decorator
french_days = { from django.views.generic import DetailView
1: "lundi", from django.views.generic.list import BaseListView, MultipleObjectTemplateResponseMixin
2: "mardi", from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResponseMixin
3: "mercredi", from django.contrib.auth.decorators import login_required
4: "jeudi", from kfet.models import (Account, Article, Operation, OperationGroup, Transfer)
5: "vendredi",
6: "samedi",
7: "dimanche",
}
french_months = {
1: "janvier",
2: "février",
3: "mars",
4: "avril",
5: "mai",
6: "juin",
7: "juillet",
8: "août",
9: "septembre",
10: "octobre",
11: "novembre",
12: "décembre",
}
def dayname(date):
return french_days[date.isoweekday()]
def weekname(date):
(_, a, _) = date.isocalendar()
week_num = a
return "semaine %d" % week_num
def monthname(date):
return french_months[date.month]
# Pareil mais pour une liste de dates # Pareil mais pour une liste de dates
@ -46,7 +13,7 @@ def monthname(date):
def daynames(dates): def daynames(dates):
names = {} names = {}
for i in dates: for i in dates:
names[i] = dayname(dates[i]) names[i] = dates[i].strftime("%A")
return names return names
@ -55,7 +22,7 @@ def daynames(dates):
def weeknames(dates): def weeknames(dates):
names = {} names = {}
for i in dates: for i in dates:
names[i] = weekname(dates[i]) names[i] = "Semaine " + dates[i].strftime("%W")
return names return names
@ -64,7 +31,7 @@ def weeknames(dates):
def monthnames(dates): def monthnames(dates):
names = {} names = {}
for i in dates: for i in dates:
names[i] = monthname(dates[i]) names[i] = dates[i].strftime("%B")
return names return names
@ -135,3 +102,389 @@ def tot_ventes(queryset):
for op in queryset: for op in queryset:
res += op.article_nb res += op.article_nb
return res return res
# -----
# Views
# -----
# ---------------
# Vues génériques
# ---------------
# source : docs.djangoproject.com/fr/1.10/topics/class-based-views/mixins/
class JSONResponseMixin(object):
"""
A mixin that can be used to render a JSON response.
"""
def render_to_json_response(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
return JsonResponse(
self.get_data(context),
**response_kwargs
)
def get_data(self, context):
"""
Returns an object that will be serialized as JSON by json.dumps().
"""
# Note: This is *EXTREMELY* naive; in reality, you'll need
# to do much more complex handling to ensure that arbitrary
# objects -- such as Django model instances or querysets
# -- can be serialized as JSON.
return context
class HybridDetailView(JSONResponseMixin,
SingleObjectTemplateResponseMixin,
BaseDetailView):
"""
Returns a DetailView as a html page except if it asked a JSON
file by the GET method in witch case it returns a JSON response.
"""
def render_to_response(self, context):
# Look for a 'format=json' GET argument
if self.request.GET.get('format') == 'json':
return self.render_to_json_response(context)
else:
return super(HybridDetailView, self).render_to_response(context)
class HybridListView(JSONResponseMixin,
MultipleObjectTemplateResponseMixin,
BaseListView):
"""
Returns a ListView as a html page except if it asked a JSON
file by the GET method in witch case it returns a JSON response.
"""
def render_to_response(self, context):
# Look for a 'format=json' GET argument
if self.request.GET.get('format') == 'json':
return self.render_to_json_response(context)
else:
return super(HybridListView, self).render_to_response(context)
class ObjectResumeStat(DetailView):
"""
Summarize all the stats of an object
DOES NOT RETURN A JSON RESPONSE
"""
template_name = 'kfet/object_stat_resume.html'
context_object_name = 'lul'
id_prefix = 'id_a_definir'
# nombre de vues à résumer
nb_stat = 2
# Le combienième est celui par defaut ?
# (entre 0 et nb_stat-1)
nb_default = 0
stat_labels = ['stat_1', 'stat_2']
stat_urls = ['url_1', 'url_2']
# sert à renverser les urls
# utile de le surcharger quand l'url prend d'autres arguments que l'id
def get_object_url_kwargs(self, **kwargs):
return {'pk': self.object.id}
def get_context_data(self, **kwargs):
# On hérite pas
object_id = self.object.id
context = {}
stats = {}
for i in range(self.nb_stat):
stats[i] = {
'label': self.stat_labels[i],
'btn': "btn_%s_%d_%d" % (self.id_prefix,
object_id,
i),
'url': reverse_lazy(self.stat_urls[i],
kwargs=self.get_object_url_kwargs()),
}
prefix = "%s_%d" % (self.id_prefix, object_id)
context['id_prefix'] = prefix
context['content_id'] = "content_%s" % prefix
context['stats'] = stats
context['default_stat'] = self.nb_default
context['object_id'] = object_id
return context
# ------------------
# AccountStatBalance
# ------------------
class AccountStatBalance(HybridDetailView):
"""
Returns a graph (or a JSON Response) of the evolution a the personnal
balance of a trigramm between begin_date and end_date
takes into account the Operations and the Transfers
does not takes intto account the balance offset
"""
model = Account
trigramme_url_kwarg = 'trigramme'
template_name = 'kfet/account_stat_balance.html'
context_object_name = 'account'
begin_date = this_morning()
end_date = timezone.now() # ne gère pas encore autre chose que now
anytime = False # un cas particulier
id_prefix = "lol"
def get_object(self, **kwargs):
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
# On récupère les opérations
# TODO: retirer les opgroup dont tous les op sont annulées
opgroups = list(OperationGroup.objects
.filter(on_acc=account)
.filter(at__gte=self.begin_date)
.filter(at__lte=self.end_date))
# On récupère les transferts reçus
received_transfers = list(Transfer.objects
.filter(to_acc=account)
.filter(canceled_at=None)
.filter(group__at__gte=self.begin_date)
.filter(group__at__lte=self.end_date))
# On récupère les transferts émis
emitted_transfers = list(Transfer.objects
.filter(from_acc=account)
.filter(canceled_at=None)
.filter(group__at__gte=self.begin_date)
.filter(group__at__lte=self.end_date))
# On transforme tout ça en une liste de dictionnaires sous la forme
# {'at': date,
# 'amount': changement de la balance (négatif si diminue la balance,
# positif si l'augmente),
# 'label': text descriptif,
# 'balance': état de la balance après l'action (0 pour le moment,
# sera mis à jour lors d'une
# autre passe)
# }
actions = [
# Maintenant (à changer si on gère autre chose que now)
{
'at': self.end_date.isoformat(),
'amout': 0,
'label': "actuel",
'balance': 0,
}
] + [
{
'at': op.at.isoformat(),
'amount': op.amount,
'label': str(op),
'balance': 0,
} for op in opgroups
] + [
{
'at': tr.group.at.isoformat(),
'amount': tr.amount,
'label': "%d€: %s -> %s" % (tr.amount,
tr.from_acc.trigramme,
tr.to_acc.trigramme),
'balance': 0,
} for tr in received_transfers
] + [
{
'at': tr.group.at.isoformat(),
'amount': -tr.amount,
'label': "%d€: %s -> %s" % (tr.amount,
tr.from_acc.trigramme,
tr.to_acc.trigramme),
'balance': 0,
} for tr in emitted_transfers
]
if not self.anytime:
actions += [
# Date de début :
{
'at': self.begin_date.isoformat(),
'amount': 0,
'label': "début",
'balance': 0,
}
]
# Maintenant on trie la liste des actions par ordre du plus récent
# an plus ancien et on met à jour la balance
actions = sorted(actions, key=lambda k: k['at'], reverse=True)
actions[0]['balance'] = account.balance
for i in range(len(actions)-1):
actions[i+1]['balance'] = actions[i]['balance'] \
- actions[i+1]['amount']
return actions
def get_context_data(self, **kwargs):
context = {}
changes = self.get_changes_list()
context['changes'] = changes
context['chart_id'] = "%s_%s" % (self.id_prefix,
self.object.id)
context['min_date'] = changes[len(changes)-1]['at']
context['max_date'] = changes[0]['at']
# TODO: offset
return context
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(AccountStatBalance, self).dispatch(*args, **kwargs)
# ---------------
# AccountStatLast
# ---------------
class AccountStatLast(HybridDetailView):
"""
Returns a graph (or a JSON Response) of the evolution a the personnal
consommation of a trigramm at the diffent dates precised
"""
model = Account
trigramme_url_kwarg = 'trigramme'
template_name = 'kfet/account_stat_last.html'
context_object_name = 'account'
end_date = timezone.now()
id_prefix = "lol"
# doit rendre un dictionnaire des dates
# la première date correspond au début
# la dernière date est la fin de la dernière plage
def get_dates(self, **kwargs):
pass
# doit rendre un dictionnaire des labels
# le dernier label ne sera pas utilisé
def get_labels(self, **kwargs):
pass
def get_object(self, **kwargs):
trigramme = self.kwargs.get(self.trigramme_url_kwarg)
return get_object_or_404(Account, trigramme=trigramme)
def sort_operations(self, **kwargs):
# On récupère les dates
dates = self.get_dates()
# On ajoute la date de fin
extended_dates = dates.copy()
extended_dates[len(dates)+1] = self.end_date
# 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(type='purchase')
.filter(group__on_acc=self.object)
.filter(canceled_at=None)
)
operations = {}
for i in dates:
operations[i] = (all_operations
.filter(group__at__gte=extended_dates[i])
.filter(group__at__lte=extended_dates[i+1])
)
return operations
def get_context_data(self, **kwargs):
# On hérite
# en fait non, pas besoin et c'est chiant à dumper
# context = super(AccountStat, self).get_context_data(**kwargs)
context = {}
nb_ventes = {}
# On récupère les labels des dates
context['labels'] = self.get_labels().copy()
# On compte les opérations
operations = self.sort_operations()
for i in operations:
nb_ventes[i] = tot_ventes(operations[i])
context['nb_ventes'] = nb_ventes
# ID unique
context['chart_id'] = "%s_%d" % (self.id_prefix,
self.object.id)
return context
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(AccountStatLast, self).dispatch(*args, **kwargs)
# ---------------
# ArticleStatLast
# ---------------
# Rend un graph des ventes sur une plage de temps à préciser.
# Le graphique distingue les ventes sur LIQ et sur les autres trigrammes
class ArticleStatLast(HybridDetailView):
"""
Returns a graph (or a JSON Response) of the consommation
of an article at the diffent dates precised
"""
model = Article
template_name = 'kfet/article_stat_last.html'
context_object_name = 'article'
end_date = timezone.now()
id_prefix = "lol"
def render_to_response(self, context):
# Look for a 'format=json' GET argument
if self.request.GET.get('format') == 'json':
return self.render_to_json_response(context)
else:
return super(ArticleStatLast, self).render_to_response(context)
# doit rendre un dictionnaire des dates
# la première date correspond au début
# la dernière date est la fin de la dernière plage
def get_dates(self, **kwargs):
pass
# doit rendre un dictionnaire des labels
# le dernier label ne sera pas utilisé
def get_labels(self, **kwargs):
pass
def get_context_data(self, **kwargs):
# On hérite
# en fait non, pas besoin et c'est chiant à dumper
# context = super(ArticleStat, self).get_context_data(**kwargs)
context = {}
# On récupère les labels des dates
context['labels'] = self.get_labels().copy()
# On récupère les dates
dates = self.get_dates()
# On ajoute la date de fin
extended_dates = dates.copy()
extended_dates[len(dates)+1] = self.end_date
# 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(type='purchase')
.filter(article=self.object)
.filter(canceled_at=None)
)
operations = {}
for i in dates:
operations[i] = (all_operations
.filter(group__at__gte=extended_dates[i])
.filter(group__at__lte=extended_dates[i+1])
)
# On compte les opérations
nb_ventes = {}
nb_accounts = {}
nb_liq = {}
for i in operations:
nb_ventes[i] = tot_ventes(operations[i])
nb_liq[i] = tot_ventes(
operations[i]
.filter(group__on_acc__trigramme='LIQ')
)
nb_accounts[i] = tot_ventes(
operations[i]
.exclude(group__on_acc__trigramme='LIQ')
)
context['nb_ventes'] = nb_ventes
context['nb_accounts'] = nb_accounts
context['nb_liq'] = nb_liq
# ID unique
context['chart_id'] = "%s_%d" % (self.id_prefix,
self.object.id)
return context
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ArticleStatLast, self).dispatch(*args, **kwargs)

View file

@ -8,8 +8,6 @@ from django.shortcuts import render, get_object_or_404, redirect
from django.core.exceptions import PermissionDenied, ValidationError from django.core.exceptions import PermissionDenied, ValidationError
from django.core.cache import cache from django.core.cache import cache
from django.views.generic import ListView, DetailView from django.views.generic import ListView, DetailView
from django.views.generic.list import BaseListView, MultipleObjectTemplateResponseMixin
from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResponseMixin
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from django.contrib import messages from django.contrib import messages
@ -41,7 +39,9 @@ import statistics
from .statistic import daynames, monthnames, weeknames, \ from .statistic import daynames, monthnames, weeknames, \
lastdays, lastweeks, lastmonths, \ lastdays, lastweeks, lastmonths, \
this_morning, this_monday_morning, this_first_month_day, \ this_morning, this_monday_morning, this_first_month_day, \
tot_ventes tot_ventes, \
HybridDetailView, ObjectResumeStat, \
ArticleStatLast, AccountStatBalance, AccountStatLast
@login_required @login_required
def home(request): def home(request):
@ -1957,103 +1957,6 @@ class SupplierUpdate(SuccessMessageMixin, UpdateView):
# ========== # ==========
# Statistics # Statistics
# ========== # ==========
# ---------------
# Vues génériques
# ---------------
# source : docs.djangoproject.com/fr/1.10/topics/class-based-views/mixins/
class JSONResponseMixin(object):
"""
A mixin that can be used to render a JSON response.
"""
def render_to_json_response(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
return JsonResponse(
self.get_data(context),
**response_kwargs
)
def get_data(self, context):
"""
Returns an object that will be serialized as JSON by json.dumps().
"""
# Note: This is *EXTREMELY* naive; in reality, you'll need
# to do much more complex handling to ensure that arbitrary
# objects -- such as Django model instances or querysets
# -- can be serialized as JSON.
return context
# Rend un DetailView en html sauf si on lui précise dans
# l'appel à get que l'on veut un json auquel cas il en rend un
class HybridDetailView(JSONResponseMixin,
SingleObjectTemplateResponseMixin,
BaseDetailView):
def render_to_response(self, context):
# Look for a 'format=json' GET argument
if self.request.GET.get('format') == 'json':
return self.render_to_json_response(context)
else:
return super(HybridDetailView, self).render_to_response(context)
# Rend un ListView en html sauf si on lui précise dans
# l'appel à get que l'on veut un json auquel cas il en rend un
class HybridListView(JSONResponseMixin,
MultipleObjectTemplateResponseMixin,
BaseListView):
def render_to_response(self, context):
# Look for a 'format=json' GET argument
if self.request.GET.get('format') == 'json':
return self.render_to_json_response(context)
else:
return super(HybridListView, self).render_to_response(context)
# Un résume des toutes les vues de stat d'un objet
# NE REND PAS DE JSON
class ObjectResumeStat(DetailView):
template_name = 'kfet/object_stat_resume.html'
context_object_name = 'lul'
id_prefix = 'id_a_definir'
# nombre de vues à résumer
nb_stat = 2
# Le combienième est celui par defaut ?
# (entre 0 et nb_stat-1)
nb_default = 0
stat_labels = ['stat_1', 'stat_2']
stat_urls = ['url_1', 'url_2']
# sert à renverser les urls
# utile de le surcharger quand l'url prend d'autres arguments que l'id
def get_object_url_kwargs(self, **kwargs):
return {'pk': self.object.id}
def get_context_data(self, **kwargs):
# On hérite pas
object_id = self.object.id
context = {}
stats = {}
for i in range(self.nb_stat):
stats[i] = {
'label': self.stat_labels[i],
'btn': "btn_%s_%d_%d" % (self.id_prefix,
object_id,
i),
'url': reverse_lazy(self.stat_urls[i],
kwargs=self.get_object_url_kwargs()),
}
prefix = "%s_%d" % (self.id_prefix, object_id)
context['id_prefix'] = prefix
context['content_id'] = "content_%s" % prefix
context['stats'] = stats
context['default_stat'] = self.nb_default
context['object_id'] = object_id
return context
# ----------------------- # -----------------------
# Evolution Balance perso # Evolution Balance perso
# ----------------------- # -----------------------
@ -2093,122 +1996,6 @@ class AccountStatBalanceAll(ObjectResumeStat):
return super(AccountStatBalanceAll, self).dispatch(*args, **kwargs) return super(AccountStatBalanceAll, self).dispatch(*args, **kwargs)
# Rend un graphe (ou un json) de l'évolution de la balance personelle
# entre begin_date et end_date
# prend en compte les opérations et les transferts
# ne prend pas en compte les balance offset (TODO?)
class AccountStatBalance(HybridDetailView):
model = Account
trigramme_url_kwarg = 'trigramme'
template_name = 'kfet/account_stat_balance.html'
context_object_name = 'account'
begin_date = this_morning()
end_date = timezone.now() # ne gère pas encore autre chose que now
anytime = False # un cas particulier
id_prefix = "lol"
def get_object(self, **kwargs):
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
# On récupère les opérations
# TODO: retirer les opgroup dont tous les op sont annulées
opgroups = list(OperationGroup.objects
.filter(on_acc=account)
.filter(at__gte=self.begin_date)
.filter(at__lte=self.end_date))
# On récupère les transferts reçus
received_transfers = list(Transfer.objects
.filter(to_acc=account)
.filter(canceled_at=None)
.filter(group__at__gte=self.begin_date)
.filter(group__at__lte=self.end_date))
# On récupère les transferts émis
emitted_transfers = list(Transfer.objects
.filter(from_acc=account)
.filter(canceled_at=None)
.filter(group__at__gte=self.begin_date)
.filter(group__at__lte=self.end_date))
# On transforme tout ça en une liste de dictionnaires sous la forme
# {'at': date,
# 'amount': changement de la balance (négatif si diminue la balance,
# positif si l'augmente),
# 'label': text descriptif,
# 'balance': état de la balance après l'action (0 pour le moment,
# sera mis à jour lors d'une
# autre passe)
# }
actions = [
# Maintenant (à changer si on gère autre chose que now)
{
'at': self.end_date.isoformat(),
'amout': 0,
'label': "actuel",
'balance': 0,
}
] + [
{
'at': op.at.isoformat(),
'amount': op.amount,
'label': str(op),
'balance': 0,
} for op in opgroups
] + [
{
'at': tr.group.at.isoformat(),
'amount': tr.amount,
'label': "%d€: %s -> %s" % (tr.amount,
tr.from_acc.trigramme,
tr.to_acc.trigramme),
'balance': 0,
} for tr in received_transfers
] + [
{
'at': tr.group.at.isoformat(),
'amount': -tr.amount,
'label': "%d€: %s -> %s" % (tr.amount,
tr.from_acc.trigramme,
tr.to_acc.trigramme),
'balance': 0,
} for tr in emitted_transfers
]
if not self.anytime:
actions += [
# Date de début :
{
'at': self.begin_date.isoformat(),
'amount': 0,
'label': "début",
'balance': 0,
}
]
# Maintenant on trie la liste des actions par ordre du plus récent
# an plus ancien et on met à jour la balance
actions = sorted(actions, key=lambda k: k['at'], reverse=True)
actions[0]['balance'] = account.balance
for i in range(len(actions)-1):
actions[i+1]['balance'] = actions[i]['balance'] \
- actions[i+1]['amount']
return actions
def get_context_data(self, **kwargs):
context = {}
changes = self.get_changes_list()
context['changes'] = changes
context['chart_id'] = "%s_%s" % (self.id_prefix,
self.object.id)
context['min_date'] = changes[len(changes)-1]['at']
context['max_date'] = changes[0]['at']
# TODO: offset
return context
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(AccountStatBalance, self).dispatch(*args, **kwargs)
# Rend l'évolution de la balance perso de ces 30 derniers jours # Rend l'évolution de la balance perso de ces 30 derniers jours
class AccountStatBalanceMonth(AccountStatBalance): class AccountStatBalanceMonth(AccountStatBalance):
begin_date = this_morning() - timezone.timedelta(days=30) begin_date = this_morning() - timezone.timedelta(days=30)
@ -2275,73 +2062,6 @@ class AccountStatLastAll(ObjectResumeStat):
return super(AccountStatLastAll, self).dispatch(*args, **kwargs) return super(AccountStatLastAll, self).dispatch(*args, **kwargs)
class AccountStatLast(HybridDetailView):
model = Account
trigramme_url_kwarg = 'trigramme'
template_name = 'kfet/account_stat_last.html'
context_object_name = 'account'
end_date = timezone.now()
id_prefix = "lol"
# doit rendre un dictionnaire des dates
# la première date correspond au début
# la dernière date est la fin de la dernière plage
def get_dates(self, **kwargs):
pass
# doit rendre un dictionnaire des labels
# le dernier label ne sera pas utilisé
def get_labels(self, **kwargs):
pass
def get_object(self, **kwargs):
trigramme = self.kwargs.get(self.trigramme_url_kwarg)
return get_object_or_404(Account, trigramme=trigramme)
def sort_operations(self, **kwargs):
# On récupère les dates
dates = self.get_dates()
# On ajoute la date de fin
extended_dates = dates.copy()
extended_dates[len(dates)+1] = self.end_date
# 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(type='purchase')
.filter(group__on_acc=self.object)
.filter(canceled_at=None)
)
operations = {}
for i in dates:
operations[i] = (all_operations
.filter(group__at__gte=extended_dates[i])
.filter(group__at__lte=extended_dates[i+1])
)
return operations
def get_context_data(self, **kwargs):
# On hérite
# en fait non, pas besoin et c'est chiant à dumper
# context = super(AccountStat, self).get_context_data(**kwargs)
context = {}
nb_ventes = {}
# On récupère les labels des dates
context['labels'] = self.get_labels().copy()
# On compte les opérations
operations = self.sort_operations()
for i in operations:
nb_ventes[i] = tot_ventes(operations[i])
context['nb_ventes'] = nb_ventes
# ID unique
context['chart_id'] = "%s_%d" % (self.id_prefix,
self.object.id)
return context
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(AccountStatLast, self).dispatch(*args, **kwargs)
# Rend les achats pour ce compte des 7 derniers jours # Rend les achats pour ce compte des 7 derniers jours
@ -2413,87 +2133,6 @@ class ArticleStatLastAll(ObjectResumeStat):
return super(ArticleStatLastAll, self).dispatch(*args, **kwargs) return super(ArticleStatLastAll, self).dispatch(*args, **kwargs)
# Rend un graph des ventes sur une plage de temps à préciser.
# Le graphique distingue les ventes sur LIQ et sur les autres trigrammes
class ArticleStatLast(HybridDetailView):
model = Article
template_name = 'kfet/article_stat_last.html'
context_object_name = 'article'
end_date = timezone.now()
id_prefix = "lol"
def render_to_response(self, context):
# Look for a 'format=json' GET argument
if self.request.GET.get('format') == 'json':
return self.render_to_json_response(context)
else:
return super(ArticleStatLast, self).render_to_response(context)
# doit rendre un dictionnaire des dates
# la première date correspond au début
# la dernière date est la fin de la dernière plage
def get_dates(self, **kwargs):
pass
# doit rendre un dictionnaire des labels
# le dernier label ne sera pas utilisé
def get_labels(self, **kwargs):
pass
def get_context_data(self, **kwargs):
# On hérite
# en fait non, pas besoin et c'est chiant à dumper
# context = super(ArticleStat, self).get_context_data(**kwargs)
context = {}
# On récupère les labels des dates
context['labels'] = self.get_labels().copy()
# On récupère les dates
dates = self.get_dates()
# On ajoute la date de fin
extended_dates = dates.copy()
extended_dates[len(dates)+1] = self.end_date
# 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(type='purchase')
.filter(article=self.object)
.filter(canceled_at=None)
)
operations = {}
for i in dates:
operations[i] = (all_operations
.filter(group__at__gte=extended_dates[i])
.filter(group__at__lte=extended_dates[i+1])
)
# On compte les opérations
nb_ventes = {}
nb_accounts = {}
nb_liq = {}
for i in operations:
nb_ventes[i] = tot_ventes(operations[i])
nb_liq[i] = tot_ventes(
operations[i]
.filter(group__on_acc__trigramme='LIQ')
)
nb_accounts[i] = tot_ventes(
operations[i]
.exclude(group__on_acc__trigramme='LIQ')
)
context['nb_ventes'] = nb_ventes
context['nb_accounts'] = nb_accounts
context['nb_liq'] = nb_liq
# ID unique
context['chart_id'] = "%s_%d" % (self.id_prefix,
self.object.id)
return context
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ArticleStatLast, self).dispatch(*args, **kwargs)
# Rend les ventes des 7 derniers jours # Rend les ventes des 7 derniers jours
# Aujourd'hui non compris # Aujourd'hui non compris
class ArticleStatLastDay(ArticleStatLast): class ArticleStatLastDay(ArticleStatLast):