From 0fcb29252b91989c81091337a8f1643b51aa675b Mon Sep 17 00:00:00 2001 From: Evarin Date: Wed, 15 Feb 2017 21:01:54 +0100 Subject: [PATCH] Frontend tout en JS --- kfet/static/kfet/js/statistic.js | 180 ++++++++++++++++-- kfet/templates/kfet/account_read.html | 14 +- kfet/templates/kfet/account_stat_balance.html | 91 --------- kfet/templates/kfet/account_stat_last.html | 52 ----- kfet/templates/kfet/article_stat_last.html | 82 -------- kfet/templates/kfet/object_stat_resume.html | 35 ---- kfet/views.py | 64 ++++--- 7 files changed, 205 insertions(+), 313 deletions(-) delete mode 100644 kfet/templates/kfet/account_stat_balance.html delete mode 100644 kfet/templates/kfet/account_stat_last.html delete mode 100644 kfet/templates/kfet/article_stat_last.html delete mode 100644 kfet/templates/kfet/object_stat_resume.html diff --git a/kfet/static/kfet/js/statistic.js b/kfet/static/kfet/js/statistic.js index 258f8cc8..6ff13df9 100644 --- a/kfet/static/kfet/js/statistic.js +++ b/kfet/static/kfet/js/statistic.js @@ -1,18 +1,166 @@ -var STAT = {}; +(function($){ + window.StatsGroup = function (url, target) { + // context : array of objects {label, url} + // target : element of the DOM where to put the stats + var self = this; + var element = $(target); + var content = $("
"); + var buttons; -jQuery(document).ready(function() { - // FONCTIONS - // Permet de raffraichir un champ, étant donné : - // thing_url : l'url contenant le contenu - // thing_div : le div où le mettre - // empty_... : le truc à dire si on a un contenu vide - STAT.get_thing = function(thing_url, thing_div, empty_thing_message) { - $.get(thing_url, function(data) { - if(jQuery.trim(data).length==0) { - thing_div.html(empty_thing_message); - } else { - thing_div.html(data); + function dictToArray(dict, start) { + if (start === undefined) start = 0; + // converts the dicts returned by JSONResponse to Arrays + // necessary because for..in does not guarantee the order + var array = new Array(); + for (var k in dict) { + array[k] = dict[k]; + } + array.splice(0, start); + return array; + } + + function handleTimeChart(dict) { + var data = dictToArray(dict, 0); + for (var i = 0; i < data.length; i++) { + var source = data[i]; + data[i] = { x: new Date(source.at), + y: source.balance, + label: source.label } + } + return data; + } + + this.showStats = function() { + buttons.find(".focus").removeClass("focus"); + $(this).addClass("focus"); + + $.getJSON(this.stats_target_url + "?format=json", + self.displayStats); + } + + this.displayStats = function(data) { + // create the chart + var canvas = $(""); + var chart_datasets = []; + var charts = dictToArray(data.charts); + var is_time_chart = data.is_time_chart || false; + for (var i = 0; i < charts.length; i++) { + var chart = charts[i]; + chart_datasets.push( + { + label: chart.label, + borderColor: chart.color, + backgroundColor: chart.color, + fill: false, + lineTension: 0, + data: is_time_chart ? handleTimeChart(chart.values) : dictToArray(chart.values, 1), + }); + } + var chart_options = + { + responsive: true, + tooltips: { + mode: 'index', + intersect: false, + }, + hover: { + mode: 'nearest', + intersect: false, + } } - }); - } -}); + if (is_time_chart) { + chart_options['scales'] = { + xAxes: [{ + type: "time", + display: true, + scaleLabel: { + display: false, + labelString: 'Date' + }, + time: { + tooltipFormat: 'll HH:mm', + //min: new Date("{{ min_date }}"), + //max: new Date("{{ max_date }}"), + displayFormats: { + 'millisecond': 'SSS [ms]', + 'second': 'mm:ss a', + 'minute': 'DD MMM', + 'hour': 'ddd h[h]', + 'day': 'DD MMM', + 'week': 'DD MMM', + 'month': 'MMM', + 'quarter': 'MMM', + 'year': 'YYYY', + } + } + + }], + yAxes: [{ + display: true, + scaleLabel: { + display: false, + labelString: 'value' + } + }] + } + } + + var chart_model = + { + type: 'line', + options: chart_options, + data: { + labels: dictToArray(data.labels, 1), + datasets: chart_datasets, + } + }; + + // display + var prev_chart = content.children(); + content.append(canvas); + + // create the chart + var chart = new Chart(canvas, chart_model); + + // clean + prev_chart.remove(); + } + + // initialize the interface + this.initialize = function (data) { + buttons = $("
", + {class: "btn-group btn-group-justified", + role: "group", + "aria-label": "select-period"}); + var to_click = undefined; + var context = dictToArray(data.stats); + + for (var i = 0; i < context.length; i++) { + var btn_wrapper = $("
", + {class: "btn-group", + role:"group"}); + var btn = $(" -
- {% endfor %} -
-
-
- - diff --git a/kfet/views.py b/kfet/views.py index 586f3d8b..6d8ecb7a 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -7,7 +7,7 @@ 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.core.urlresolvers import reverse_lazy +from django.core.urlresolvers import reverse, reverse_lazy from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin from django.contrib.auth import authenticate, login @@ -1983,6 +1983,14 @@ class JSONResponseMixin(object): return context +class JSONDetailView(JSONResponseMixin, + BaseDetailView): + """ + Returns a DetailView that renders a JSON + """ + def render_to_response(self, context): + return self.render_to_json_response(context) + class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView): @@ -2013,12 +2021,11 @@ class HybridListView(JSONResponseMixin, return super(HybridListView, self).render_to_response(context) -class ObjectResumeStat(DetailView): +class ObjectResumeStat(JSONDetailView): """ Summarize all the stats of an object - DOES NOT RETURN A JSON RESPONSE + Handles JSONResponse """ - template_name = 'kfet/object_stat_resume.html' context_object_name = '' id_prefix = '' # nombre de vues à résumer @@ -2049,7 +2056,7 @@ class ObjectResumeStat(DetailView): 'btn': "btn_%s_%d_%d" % (self.id_prefix, object_id, i), - 'url': reverse_lazy(self.stat_urls[i], + 'url': reverse(self.stat_urls[i], kwargs=dict( self.get_object_url_kwargs(), **url_kwargs[i] @@ -2072,7 +2079,7 @@ ID_PREFIX_ACC_BALANCE = "balance_acc" # Un résumé de toutes les vues ArticleStatBalance -# NE REND PAS DE JSON +# REND DU JSON class AccountStatBalanceAll(ObjectResumeStat): model = Account context_object_name = 'account' @@ -2105,9 +2112,9 @@ class AccountStatBalanceAll(ObjectResumeStat): return super(AccountStatBalanceAll, self).dispatch(*args, **kwargs) -class AccountStatBalance(HybridDetailView): +class AccountStatBalance(JSONDetailView): """ - Returns a graph (or a JSON Response) of the evolution a the personnal + Returns a JSON containing the evolution a the personnal balance of a trigramme between timezone.now() and `nb_days` ago (specified to the view as an argument) takes into account the Operations and the Transfers @@ -2116,7 +2123,6 @@ class AccountStatBalance(HybridDetailView): model = Account trigramme_url_kwarg = 'trigramme' nb_date_url_kwargs = 'nb_date' - template_name = 'kfet/account_stat_balance.html' context_object_name = 'account' id_prefix = "" @@ -2223,10 +2229,10 @@ class AccountStatBalance(HybridDetailView): nb_days_string = 'anytime' else: nb_days_string = str(int(nb_days)) - context['changes'] = changes - context['chart_id'] = "%s_%s_%s_days" % (self.id_prefix, - self.object.id, - nb_days_string) + context['charts'] = [ { "color": "rgb(255, 99, 132)", + "label": "Balance", + "values": changes } ] + context['is_time_chart'] = True context['min_date'] = changes[len(changes)-1]['at'] context['max_date'] = changes[0]['at'] # TODO: offset @@ -2272,14 +2278,13 @@ class AccountStatLastAll(ObjectResumeStat): return super(AccountStatLastAll, self).dispatch(*args, **kwargs) -class AccountStatLast(HybridDetailView): +class AccountStatLast(JSONDetailView): """ - Returns a graph (or a JSON Response) of the evolution a the personnal + Returns a JSON containing the evolution a the personnal consommation of a trigramme at the diffent dates specified """ model = Account trigramme_url_kwarg = 'trigramme' - template_name = 'kfet/account_stat_last.html' context_object_name = 'account' end_date = timezone.now() id_prefix = "" @@ -2331,10 +2336,9 @@ class AccountStatLast(HybridDetailView): 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) + context['charts'] = [ { "color": "rgb(255, 99, 132)", + "label": "NB items achetés", + "values": nb_ventes } ] return context @method_decorator(login_required) @@ -2411,13 +2415,12 @@ class ArticleStatLastAll(ObjectResumeStat): return super(ArticleStatLastAll, self).dispatch(*args, **kwargs) -class ArticleStatLast(HybridDetailView): +class ArticleStatLast(JSONDetailView): """ - Returns a graph (or a JSON Response) of the consommation + Returns a JSON containing 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 = "" @@ -2478,12 +2481,15 @@ class ArticleStatLast(HybridDetailView): 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) + context['charts'] = [ { "color": "rgb(255, 99, 132)", + "label": "Toutes consommations", + "values": nb_ventes }, + { "color": "rgb(54, 162, 235)", + "label": "LIQ", + "values": nb_liq }, + { "color": "rgb(255, 205, 86)", + "label": "Comptes K-Fêt", + "values": nb_accounts } ] return context @method_decorator(login_required)