diff options
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/models.py | 10 | ||||
-rw-r--r-- | ishtar_common/templates/blocks/DataTables-stats.html | 125 | ||||
-rw-r--r-- | ishtar_common/views_item.py | 48 |
3 files changed, 178 insertions, 5 deletions
diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 15b341cb7..7aa8706b3 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -21,6 +21,7 @@ Models description """ import copy +from collections import OrderedDict import datetime import inspect from importlib import import_module @@ -1929,7 +1930,14 @@ class TemplateItem: return cls._label_templates_q() -class BaseHistorizedItem(TemplateItem, FullSearch, Imported, +class StatisticItem: + STATISTIC_MODALITIES = [] # example: "operation type", "material type" + STATISTIC_SUM_VARIABLE = OrderedDict( + (("pk", _("number")),) + ) # example: "price", "volume" + + +class BaseHistorizedItem(StatisticItem, TemplateItem, FullSearch, Imported, JsonData, FixAssociated): """ Historized item with external ID management. diff --git a/ishtar_common/templates/blocks/DataTables-stats.html b/ishtar_common/templates/blocks/DataTables-stats.html new file mode 100644 index 000000000..da3a85179 --- /dev/null +++ b/ishtar_common/templates/blocks/DataTables-stats.html @@ -0,0 +1,125 @@ +{% load i18n %} +<div> + <div id="stats-form-{{name}}"> + <label for="stats_renderer-{{name}}">{% trans "Type" %}</label> + <select id="stats_renderer-{{name}}" name="stats_renderer"> + <option value="line" selected='selected'>{% trans "Line" %}</option> + <option value="bar">{% trans "Bar" %}</option> + <option value="pie">{% trans "Pie" %}</option> + <option value="table">{% trans "Table" %}</option> + </select> + <label for="stats_modality_1-{{name}}">{% trans "Modality 1" %}</label> + <select id="stats_modality_1-{{name}}" name="stats_modality_1"> + {% for modality, modality_lbl in current_model.STATISTIC_MODALITIES_OPTIONS.items %} + <option value="{{modality}}"{% if forloop.first %} selected='selected'{% endif %}>{{modality_lbl}}</option> + {% endfor %} + </select> + <label for="stats_modality_2-{{name}}">{% trans "Modality 2" %}</label> + <select id="stats_modality_2-{{name}}" name="stats_modality_2"> + <option value="" selected='selected'>--</option> + {% for modality, modality_lbl in current_model.STATISTIC_MODALITIES_OPTIONS.items %} + <option value="{{modality}}">{{modality_lbl}}</option> + {% endfor %} + </select> + <label for="stats_sum-{{name}}">{% trans "Sum" %}</label> + <select id="stats_sum-{{name}}" name="stats_sum"> + {% for sum_var, sum_var_lbl in current_model.STATISTIC_SUM_VARIABLE.items %} + <option value="{{sum_var}}"{% if forloop.first %} selected='selected'{% endif %}>{{sum_var_lbl}}</option> + {% endfor %} + </select> + </div> + <hr/> + <div id="charts-{{name}}"> + <div id="chart-{{name}}" + style="height:400px; width:700px; margin-right:auto; margin-left:auto"></div> + <hr/> + <p class='alert alert-info' id="stats-zoom-help-{{name}}" style="z-index:-1"> + <i class="fa fa-info-circle" aria-hidden="true"></i> + {% trans 'Draw rectangle on the graph to zoom. Double-click to reinitialize.' %} + </p> + <div class='form chart-img-form'> + <div class="text-center"> + <button id="chart-img-display-{{name}}" + type='button' class='btn btn-secondary'> + {% trans "Display as an image" %} + </button> + </div> + <br> + <div id="chart-img-{{name}}" class='chart-img'> + <div class="card"> + <div id="img-{{name}}" + class="card-img-top text-center ml-3 mt-3"></div> + <div class="card-body"> + <div class='alert alert-info'> + <i class="fa fa-info-circle" aria-hidden="true"></i> + {% trans 'Right-click on this image to save it.' %} + </div> + </div> + </div> + </div> + </div> + </div> + <div id="stats-table-{{name}}"> + <div id="stats-table-content-{{name}}"></div> + <hr> + <div class="text-center"> + <a id="stats-table-csv-{{name}}" href="#" + type='button' class='btn btn-secondary'> + {% trans "Export as CSV" %} + </a> + </div> + </div> +</div> + +<script language="javascript" type="text/javascript"> + +{% comment %} + var plot_{{name}} = $.jqplot('chart_{{name}}', + [{% for idx, lbl, values in dashboard.values %}{% if forloop.counter0 > 0 %} {% if forloop.counter0 > 1 %}, {% endif%} values_{{forloop.counter0}}_{{name}} {% endif %} {% endfor%}], { + axes:{ {%ifequal slicing 'year'%} + xaxis:{ + label:'{% trans "Year" %}', + tickOptions: { + formatString: "%d" + } + },{%endifequal%}{%ifequal slicing 'month'%} + xaxis:{ + label:'{% trans "Month" %}', + renderer:$.jqplot.DateAxisRenderer, + tickRenderer:$.jqplot.CanvasAxisTickRenderer, + tickOptions:{ + formatString:'%b %Y', + angle:-25 + } + },{%endifequal%} + yaxis:{ + label:'{% trans "Number"%}', + min:0 + } + }, + highlighter: { + show: true, + sizeAdjust: 7.5 + }, + series:[{% for label in dashboard.serie_labels %} + {%if forloop.counter0%}, {% endif %}{label:"{{label}}", showmarker:showmarker}{% endfor %} + ], + cursor:{ + show: true, + zoom:true, + showTooltip:false + }, + legend: { show:true, location: 'nw' } + }); + } + + $('#search_{{name}}').click(function (){ + $.post("{% url 'dashboard-main-detail' item_name %}", + $("#{{name}}_form").serialize(), + function(data){ + $("#{{name}}-tab").parent().html(data); + }); + return false; + }); + {% endcomment %} +</script>
\ No newline at end of file diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 01475f6ae..145c56ca4 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -18,8 +18,8 @@ from django.contrib.staticfiles.templatetags.staticfiles import static from django.core.cache import cache from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse, NoReverseMatch -from django.db.models import Q, Count, ImageField, Func, ExpressionWrapper, \ - FloatField +from django.db.models import Q, Count, Sum, ImageField, Func, \ + ExpressionWrapper, FloatField from django.db.models.fields import FieldDoesNotExist from django.http import HttpResponse from django.shortcuts import render @@ -1205,6 +1205,25 @@ def _get_data_from_query_old(items, query_table_cols, request, return datas +def _get_json_stats(items, stats_sum_variable, stats_modality_1, + stats_modality_2): + q = items.values(stats_modality_1, stats_modality_2) + if stats_sum_variable == 'pk': + q = q.annotate(sum=Count('pk')) + else: + q = q.annotate(sum=Sum(stats_sum_variable)) + data = [] + for values in q.order_by(stats_modality_1, stats_modality_2).all(): + modality_1 = values[stats_modality_1] + if not data or data[-1][0] != modality_1: + data.append([modality_1, []]) + data[-1][1].append( + (values[stats_modality_2], values["sum"]) + ) + data = json.dumps({"data": data}) + return HttpResponse(data, content_type='application/json') + + DEFAULT_ROW_NUMBER = 10 # length is used by ajax DataTables requests EXCLUDED_FIELDS = ['length'] @@ -1252,10 +1271,14 @@ def get_item(model, func_name, default_name, extra_request_keys=None, data_type = dct.pop('type') if not data_type: data_type = 'json' - if data_type == "json": + if "json" in data_type: EMPTY = '[]' - if data_type not in ('json', 'csv', 'json-image', 'json-map'): + if data_type not in ('json', 'csv', 'json-image', 'json-map', + 'json-stats'): + return HttpResponse(EMPTY, content_type='text/plain') + + if data_type == 'json-stats' and len(model.STATISTIC_MODALITIES) < 2: return HttpResponse(EMPTY, content_type='text/plain') model_to_check = model @@ -1605,6 +1628,23 @@ def get_item(model, func_name, default_name, extra_request_keys=None, table_cols = list(table_cols) if data_type == "json-map": table_cols = [] # only pk for map + elif data_type == "json-stats": + stats_modality_1 = request_items.get("stats_modality_1", None) + stats_modality_2 = request_items.get("stats_modality_2", None) + if not stats_modality_1 or \ + stats_modality_1 not in model.STATISTIC_MODALITIES: + stats_modality_1 = model.STATISTIC_MODALITIES[0] + if not stats_modality_2 or \ + stats_modality_2 not in model.STATISTIC_MODALITIES: + stats_modality_2 = model.STATISTIC_MODALITIES[1] + stats_sum_variable = request_items.get('stats_sum_variable', None) + stats_sum_variable_keys = list(model.STATISTIC_SUM_VARIABLE.keys()) + if not stats_sum_variable or \ + stats_sum_variable not in stats_sum_variable_keys: + stats_sum_variable = stats_sum_variable_keys[0] + return _get_json_stats( + items, stats_sum_variable, stats_modality_1, stats_modality_2) + query_table_cols = [] for idx, cols in enumerate(table_cols): if type(cols) not in (list, tuple): |