forked from DGNum/gestioCOF
Clean Article stats
kfet.statistic - delete no longer used defs - new mixin - ScaleMixin - get scale args from GET params - chunkify querysets according to a scale Article stats - use SingleResumeStat for manifest - use ScaleMixin for sales - update urls - update permission required: teamkfet Account stats - update permission required: teamkfet - operations use ScaleMixin - fix manifests urls
This commit is contained in:
parent
f585247224
commit
c01de558e1
5 changed files with 132 additions and 221 deletions
|
@ -1,5 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ast
|
||||||
|
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -50,7 +52,7 @@ class StatScale(object):
|
||||||
for cls in StatScale.__subclasses__():
|
for cls in StatScale.__subclasses__():
|
||||||
if cls.name == name:
|
if cls.name == name:
|
||||||
return cls
|
return cls
|
||||||
raise Exception('scale %s not found' % name)
|
return None
|
||||||
|
|
||||||
def get_from(self, dt):
|
def get_from(self, dt):
|
||||||
return self.std_chunk and self.get_chunk_start(dt) or dt
|
return self.std_chunk and self.get_chunk_start(dt) or dt
|
||||||
|
@ -114,32 +116,38 @@ class MonthStatScale(StatScale):
|
||||||
return to_kfet_day(dt).replace(day=1)
|
return to_kfet_day(dt).replace(day=1)
|
||||||
|
|
||||||
|
|
||||||
def this_first_month_day():
|
def stat_manifest(scales_def=None, scale_args=None, **url_params):
|
||||||
now = timezone.now()
|
if scales_def is None:
|
||||||
first_day = timezone.datetime(year=now.year,
|
scales_def = []
|
||||||
month=now.month,
|
if scale_args is None:
|
||||||
day=1,
|
scale_args = {}
|
||||||
hour=KFET_WAKES_UP_AT)
|
return [
|
||||||
return first_day
|
dict(
|
||||||
|
label=label,
|
||||||
|
url_params=dict(
|
||||||
|
scale=cls.name,
|
||||||
|
scale_args=scale_args,
|
||||||
|
**url_params,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for label, cls in scales_def
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def this_monday_morning():
|
def last_stats_manifest(scales_def=None, scale_args=None, **url_params):
|
||||||
now = timezone.now()
|
scales_def = [
|
||||||
monday = now - timezone.timedelta(days=now.isoweekday()-1)
|
('Derniers mois', MonthStatScale, ),
|
||||||
monday_morning = timezone.datetime(year=monday.year,
|
('Dernières semaines', WeekStatScale, ),
|
||||||
month=monday.month,
|
('Derniers jours', DayStatScale, ),
|
||||||
day=monday.day,
|
]
|
||||||
hour=KFET_WAKES_UP_AT)
|
if scale_args is None:
|
||||||
return monday_morning
|
scale_args = {}
|
||||||
|
scale_args.update(dict(
|
||||||
|
last=True,
|
||||||
def this_morning():
|
n_steps=7,
|
||||||
now = timezone.now()
|
))
|
||||||
morning = timezone.datetime(year=now.year,
|
return stat_manifest(scales_def=scales_def, scale_args=scale_args,
|
||||||
month=now.month,
|
**url_params)
|
||||||
day=now.day,
|
|
||||||
hour=KFET_WAKES_UP_AT)
|
|
||||||
return morning
|
|
||||||
|
|
||||||
|
|
||||||
# Étant donné un queryset d'operations
|
# Étant donné un queryset d'operations
|
||||||
|
@ -147,3 +155,38 @@ def this_morning():
|
||||||
def tot_ventes(queryset):
|
def tot_ventes(queryset):
|
||||||
res = queryset.aggregate(Sum('article_nb'))['article_nb__sum']
|
res = queryset.aggregate(Sum('article_nb'))['article_nb__sum']
|
||||||
return res and res or 0
|
return res and res or 0
|
||||||
|
|
||||||
|
|
||||||
|
class ScaleMixin(object):
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super().get_context_data(*args, **kwargs)
|
||||||
|
|
||||||
|
scale_name = self.request.GET.get('scale', None)
|
||||||
|
scale_args = self.request.GET.get('scale_args', None)
|
||||||
|
|
||||||
|
cls = StatScale.by_name(scale_name)
|
||||||
|
if cls is None:
|
||||||
|
scale = self.get_default_scale()
|
||||||
|
else:
|
||||||
|
scale_args = self.request.GET.get('scale_args', {})
|
||||||
|
if isinstance(scale_args, str):
|
||||||
|
scale_args = ast.literal_eval(scale_args)
|
||||||
|
scale = cls(**scale_args)
|
||||||
|
|
||||||
|
self.scale = scale
|
||||||
|
context['labels'] = scale.get_labels()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_default_scale(self):
|
||||||
|
return DayStatScale(n_steps=7, last=True)
|
||||||
|
|
||||||
|
def chunkify_qs(self, qs, scale, field=None):
|
||||||
|
if field is None:
|
||||||
|
field = 'at'
|
||||||
|
begin_f = '{}__gte'.format(field)
|
||||||
|
end_f = '{}__lte'.format(field)
|
||||||
|
return [
|
||||||
|
qs.filter(**{begin_f: begin, end_f: end})
|
||||||
|
for begin, end in scale
|
||||||
|
]
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var stat_last = new StatsGroup(
|
var stat_last = new StatsGroup(
|
||||||
"{% url 'kfet.account.stat.last' trigramme=account.trigramme %}",
|
"{% url 'kfet.account.stat.operation.list' trigramme=account.trigramme %}",
|
||||||
$("#stat_last"),
|
$("#stat_last"),
|
||||||
);
|
);
|
||||||
var stat_balance = new StatsGroup(
|
var stat_balance = new StatsGroup(
|
||||||
"{% url 'kfet.account.stat.balance' trigramme=account.trigramme %}",
|
"{% url 'kfet.account.stat.balance.list' trigramme=account.trigramme %}",
|
||||||
$("#stat_balance"),
|
$("#stat_balance"),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -99,13 +99,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
console.log('echo');
|
||||||
var stat_last = new StatsGroup(
|
var stat_last = new StatsGroup(
|
||||||
"{% url 'kfet.article.stat.last' article.id %}",
|
"{% url 'kfet.article.stat.sales.list' article.id %}",
|
||||||
$("#stat_last"),
|
$("#stat_last"),
|
||||||
);
|
);
|
||||||
|
console.log('echo');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
18
kfet/urls.py
18
kfet/urls.py
|
@ -142,18 +142,12 @@ urlpatterns = [
|
||||||
teamkfet_required(views.ArticleUpdate.as_view()),
|
teamkfet_required(views.ArticleUpdate.as_view()),
|
||||||
name='kfet.article.update'),
|
name='kfet.article.update'),
|
||||||
# Article - Statistics
|
# Article - Statistics
|
||||||
url('^articles/(?P<pk>\d+)/stat/last/$',
|
url(r'^articles/(?P<pk>\d+)/stat/sales/list$',
|
||||||
views.ArticleStatLastAll.as_view(),
|
views.ArticleStatSalesList.as_view(),
|
||||||
name = 'kfet.article.stat.last'),
|
name='kfet.article.stat.sales.list'),
|
||||||
url('^articles/(?P<pk>\d+)/stat/last/month/$',
|
url(r'^articles/(?P<pk>\d+)/stat/sales$',
|
||||||
views.ArticleStatLastMonth.as_view(),
|
views.ArticleStatSales.as_view(),
|
||||||
name = 'kfet.article.stat.last.month'),
|
name='kfet.article.stat.sales'),
|
||||||
url('^articles/(?P<pk>\d+)/stat/last/week/$',
|
|
||||||
views.ArticleStatLastWeek.as_view(),
|
|
||||||
name = 'kfet.article.stat.last.week'),
|
|
||||||
url('^articles/(?P<pk>\d+)/stat/last/day/$',
|
|
||||||
views.ArticleStatLastDay.as_view(),
|
|
||||||
name = 'kfet.article.stat.last.day'),
|
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
# K-Psul urls
|
# K-Psul urls
|
||||||
|
|
207
kfet/views.py
207
kfet/views.py
|
@ -48,10 +48,8 @@ from decimal import Decimal
|
||||||
import django_cas_ng
|
import django_cas_ng
|
||||||
import heapq
|
import heapq
|
||||||
import statistics
|
import statistics
|
||||||
from kfet.statistic import DayStatScale, MonthStatScale, StatScale, WeekStatScale
|
from kfet.statistic import ScaleMixin, last_stats_manifest, tot_ventes
|
||||||
from .statistic import (
|
|
||||||
this_morning, this_monday_morning, this_first_month_day, tot_ventes,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Home(TemplateView):
|
class Home(TemplateView):
|
||||||
template_name = "kfet/home.html"
|
template_name = "kfet/home.html"
|
||||||
|
@ -2155,10 +2153,8 @@ class AccountStatBalance(PkUrlMixin, JSONDetailView):
|
||||||
account = self.object
|
account = self.object
|
||||||
|
|
||||||
# prepare filters
|
# prepare filters
|
||||||
if end_date is None:
|
|
||||||
end_date = this_morning()
|
|
||||||
|
|
||||||
if last_days is not None:
|
if last_days is not None:
|
||||||
|
end_date = timezone.now()
|
||||||
begin_date = end_date - timezone.timedelta(days=last_days)
|
begin_date = end_date - timezone.timedelta(days=last_days)
|
||||||
|
|
||||||
# prepare querysets
|
# prepare querysets
|
||||||
|
@ -2289,43 +2285,7 @@ class AccountStatOperationList(PkUrlMixin, SingleResumeStat):
|
||||||
pk_url_kwarg = 'trigramme'
|
pk_url_kwarg = 'trigramme'
|
||||||
id_prefix = ID_PREFIX_ACC_LAST
|
id_prefix = ID_PREFIX_ACC_LAST
|
||||||
nb_default = 2
|
nb_default = 2
|
||||||
stats = [
|
stats = last_stats_manifest(types=[Operation.PURCHASE])
|
||||||
{
|
|
||||||
'label': 'Derniers mois',
|
|
||||||
'url_params': dict(
|
|
||||||
types=[Operation.PURCHASE],
|
|
||||||
scale=MonthStatScale.name,
|
|
||||||
scale_args=dict(
|
|
||||||
last=True,
|
|
||||||
n_steps=7,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': 'Dernières semaines',
|
|
||||||
'url_params': dict(
|
|
||||||
types=[Operation.PURCHASE],
|
|
||||||
last_days=49,
|
|
||||||
scale=WeekStatScale.name,
|
|
||||||
scale_args=dict(
|
|
||||||
last=True,
|
|
||||||
n_steps=7,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': 'Derniers jours',
|
|
||||||
'url_params': dict(
|
|
||||||
types=[Operation.PURCHASE],
|
|
||||||
last_days=7,
|
|
||||||
scale=DayStatScale.name,
|
|
||||||
scale_args=dict(
|
|
||||||
last=True,
|
|
||||||
n_steps=7,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
url_stat = 'kfet.account.stat.operation'
|
url_stat = 'kfet.account.stat.operation'
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
|
@ -2339,7 +2299,7 @@ class AccountStatOperationList(PkUrlMixin, SingleResumeStat):
|
||||||
return super().dispatch(*args, **kwargs)
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AccountStatOperation(PkUrlMixin, JSONDetailView):
|
class AccountStatOperation(ScaleMixin, PkUrlMixin, JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Returns a JSON containing the evolution a the personnal
|
Returns a JSON containing the evolution a the personnal
|
||||||
consommation of a trigramme at the diffent dates specified
|
consommation of a trigramme at the diffent dates specified
|
||||||
|
@ -2359,38 +2319,24 @@ class AccountStatOperation(PkUrlMixin, JSONDetailView):
|
||||||
.filter(group__on_acc=self.object)
|
.filter(group__on_acc=self.object)
|
||||||
.filter(canceled_at=None)
|
.filter(canceled_at=None)
|
||||||
)
|
)
|
||||||
operations = []
|
chunks = self.chunkify_qs(all_operations, scale, field='group__at')
|
||||||
for begin, end in scale:
|
return chunks
|
||||||
operations.append(all_operations
|
|
||||||
.filter(group__at__gte=begin,
|
|
||||||
group__at__lte=end))
|
|
||||||
return operations
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
context = {}
|
old_ctx = super().get_context_data(*args, **kwargs)
|
||||||
|
context = {'labels': old_ctx['labels']}
|
||||||
scale = self.request.GET.get('scale', None)
|
scale = self.scale
|
||||||
if scale is None:
|
|
||||||
scale = DayStatScale(n_steps=7, last=True)
|
|
||||||
else:
|
|
||||||
scale_cls = StatScale.by_name(scale)
|
|
||||||
scale_args = self.request.GET.get('scale_args', '{}')
|
|
||||||
scale = scale_cls(**ast.literal_eval(scale_args))
|
|
||||||
print(scale.datetimes)
|
|
||||||
|
|
||||||
types = self.request.GET.get('types', None)
|
types = self.request.GET.get('types', None)
|
||||||
if types is not None:
|
if types is not None:
|
||||||
types = ast.literal_eval(types)
|
types = ast.literal_eval(types)
|
||||||
|
|
||||||
operations = self.get_operations(types=types, scale=scale)
|
operations = self.get_operations(types=types, scale=scale)
|
||||||
# On récupère les labels des dates
|
|
||||||
context['labels'] = scale.get_labels()
|
|
||||||
# On compte les opérations
|
# On compte les opérations
|
||||||
nb_ventes = []
|
nb_ventes = []
|
||||||
for chunk in operations:
|
for chunk in operations:
|
||||||
nb_ventes.append(tot_ventes(chunk))
|
nb_ventes.append(tot_ventes(chunk))
|
||||||
|
|
||||||
context['labels'] = scale.get_labels()
|
|
||||||
context['charts'] = [{"color": "rgb(255, 99, 132)",
|
context['charts'] = [{"color": "rgb(255, 99, 132)",
|
||||||
"label": "NB items achetés",
|
"label": "NB items achetés",
|
||||||
"values": nb_ventes}]
|
"values": nb_ventes}]
|
||||||
|
@ -2418,88 +2364,55 @@ 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(SingleResumeStat):
|
class ArticleStatSalesList(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
|
||||||
nb_stat = 3
|
|
||||||
nb_default = 2
|
nb_default = 2
|
||||||
stat_labels = ["Derniers mois", "Dernières semaines", "Derniers jours"]
|
url_stat = 'kfet.article.stat.sales'
|
||||||
stat_urls = ['kfet.article.stat.last.month',
|
stats = last_stats_manifest()
|
||||||
'kfet.article.stat.last.week',
|
|
||||||
'kfet.article.stat.last.day']
|
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(teamkfet_required)
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(ArticleStatLastAll, self).dispatch(*args, **kwargs)
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ArticleStatLast(JSONDetailView):
|
class ArticleStatSales(ScaleMixin, JSONDetailView):
|
||||||
"""
|
"""
|
||||||
Returns a JSON containing the consommation
|
Returns a JSON containing the consommation
|
||||||
of an article at the diffent dates precised
|
of an article at the diffent dates precised
|
||||||
"""
|
"""
|
||||||
model = Article
|
model = Article
|
||||||
context_object_name = 'article'
|
context_object_name = 'article'
|
||||||
end_date = timezone.now()
|
|
||||||
id_prefix = ""
|
|
||||||
|
|
||||||
def render_to_response(self, context):
|
def get_context_data(self, *args, **kwargs):
|
||||||
# Look for a 'format=json' GET argument
|
old_ctx = super().get_context_data(*args, **kwargs)
|
||||||
if self.request.GET.get('format') == 'json':
|
context = {'labels': old_ctx['labels']}
|
||||||
return self.render_to_json_response(context)
|
scale = self.scale
|
||||||
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):
|
|
||||||
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
|
# On selectionne les opérations qui correspondent
|
||||||
# à l'article en question et qui ne sont pas annulées
|
# à l'article en question et qui ne sont pas annulées
|
||||||
# puis on choisi pour chaques intervalle les opérations
|
# puis on choisi pour chaques intervalle les opérations
|
||||||
# effectuées dans ces intervalles de temps
|
# effectuées dans ces intervalles de temps
|
||||||
all_operations = (Operation.objects
|
all_operations = (
|
||||||
.filter(type='purchase')
|
Operation.objects
|
||||||
.filter(article=self.object)
|
.filter(type=Operation.PURCHASE,
|
||||||
.filter(canceled_at=None)
|
article=self.object,
|
||||||
|
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])
|
|
||||||
)
|
)
|
||||||
|
chunks = self.chunkify_qs(all_operations, scale, field='group__at')
|
||||||
# On compte les opérations
|
# On compte les opérations
|
||||||
nb_ventes = {}
|
nb_ventes = []
|
||||||
nb_accounts = {}
|
nb_accounts = []
|
||||||
nb_liq = {}
|
nb_liq = []
|
||||||
for i in operations:
|
for qs in chunks:
|
||||||
nb_ventes[i] = tot_ventes(operations[i])
|
nb_ventes.append(
|
||||||
nb_liq[i] = tot_ventes(
|
tot_ventes(qs))
|
||||||
operations[i]
|
nb_liq.append(
|
||||||
.filter(group__on_acc__trigramme='LIQ')
|
tot_ventes(qs.filter(group__on_acc__trigramme='LIQ')))
|
||||||
)
|
nb_accounts.append(
|
||||||
nb_accounts[i] = tot_ventes(
|
tot_ventes(qs.exclude(group__on_acc__trigramme='LIQ')))
|
||||||
operations[i]
|
|
||||||
.exclude(group__on_acc__trigramme='LIQ')
|
|
||||||
)
|
|
||||||
context['charts'] = [{"color": "rgb(255, 99, 132)",
|
context['charts'] = [{"color": "rgb(255, 99, 132)",
|
||||||
"label": "Toutes consommations",
|
"label": "Toutes consommations",
|
||||||
"values": nb_ventes},
|
"values": nb_ventes},
|
||||||
|
@ -2511,48 +2424,6 @@ class ArticleStatLast(JSONDetailView):
|
||||||
"values": nb_accounts}]
|
"values": nb_accounts}]
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(teamkfet_required)
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(ArticleStatLast, self).dispatch(*args, **kwargs)
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# Rend les ventes des 7 derniers jours
|
|
||||||
# Aujourd'hui non compris
|
|
||||||
class ArticleStatLastDay(ArticleStatLast):
|
|
||||||
end_date = this_morning()
|
|
||||||
id_prefix = ID_PREFIX_ART_LAST_DAYS
|
|
||||||
|
|
||||||
def get_dates(self, **kwargs):
|
|
||||||
return lastdays(7)
|
|
||||||
|
|
||||||
def get_labels(self, **kwargs):
|
|
||||||
days = lastdays(7)
|
|
||||||
return daynames(days)
|
|
||||||
|
|
||||||
|
|
||||||
# Rend les ventes de 7 dernières semaines
|
|
||||||
# La semaine en cours n'est pas comprise
|
|
||||||
class ArticleStatLastWeek(ArticleStatLast):
|
|
||||||
end_date = this_monday_morning()
|
|
||||||
id_prefix = ID_PREFIX_ART_LAST_WEEKS
|
|
||||||
|
|
||||||
def get_dates(self, **kwargs):
|
|
||||||
return lastweeks(7)
|
|
||||||
|
|
||||||
def get_labels(self, **kwargs):
|
|
||||||
weeks = lastweeks(7)
|
|
||||||
return weeknames(weeks)
|
|
||||||
|
|
||||||
|
|
||||||
# Rend les ventes des 7 derniers mois
|
|
||||||
# Le mois en cours n'est pas compris
|
|
||||||
class ArticleStatLastMonth(ArticleStatLast):
|
|
||||||
end_date = this_monday_morning()
|
|
||||||
id_prefix = ID_PREFIX_ART_LAST_MONTHS
|
|
||||||
|
|
||||||
def get_dates(self, **kwargs):
|
|
||||||
return lastmonths(7)
|
|
||||||
|
|
||||||
def get_labels(self, **kwargs):
|
|
||||||
months = lastmonths(7)
|
|
||||||
return monthnames(months)
|
|
||||||
|
|
Loading…
Reference in a new issue