factorize stylesheets and js from templates (fixes #12734)
This commit is contained in:
parent
fb23c28f1f
commit
6cec6f382c
|
@ -3,4 +3,4 @@ include VERSION
|
|||
include tox.ini
|
||||
recursive-include tests *.py
|
||||
recursive-include bijoe/templates *.html
|
||||
recursive-include bijoe/static *.js
|
||||
recursive-include bijoe/static *.js *.css
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/* Printing */
|
||||
@media print {
|
||||
/* hide UI */
|
||||
#top, #header, #more-user-links, .download, #content form {
|
||||
display: none;
|
||||
}
|
||||
/* resize content */
|
||||
#data {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Breadcrumb */
|
||||
#bijoe-slice-url-span {
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
max-width: 30em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
|
||||
/* Cube form */
|
||||
|
||||
/* make a form a column on the right if not on mobile */
|
||||
@media screen and (min-width: 800px) {
|
||||
form {
|
||||
float: right;
|
||||
width: 20em;
|
||||
padding-left: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
/* size daterange fields in order to fit on one line inside the right column */
|
||||
input[type=date] {
|
||||
width: calc(100% / 2 - 8px);
|
||||
}
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
}
|
||||
form p { /* reduce spacing between fields */
|
||||
margin-bottom: 0.5ex;
|
||||
}
|
||||
form h3 { /* reduce spacing between field groups headers */
|
||||
margin-top: 0.5ex;
|
||||
margin-bottom: 0.5ex;
|
||||
}
|
||||
/* work-around too genereic css rule in gadjo incompatible with jquery-ui style */
|
||||
.ui-datepicker select {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
/* add some between range field's subfields */
|
||||
#id_filter__receipt_time_1 {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
/* display representation field as an horizontal list of radio buttons */
|
||||
#id_representation {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
text-align: justify;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
/* put ODS export export slightly on the right of the repesentation header */
|
||||
#ods {
|
||||
margin: 1ex;
|
||||
top: -2ex;
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
|
||||
/* size main content to let some place on the right for the form */
|
||||
@media screen and (min-width: 800px) {
|
||||
#data {
|
||||
width: calc(100% - 23em);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* table representation */
|
||||
#data table {
|
||||
width: 100%;
|
||||
/* does not user normal table borders */
|
||||
border-collapse: collapse;
|
||||
/* increase line height */
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
/* align measure columns to the right */
|
||||
#data .bijoe-measure {
|
||||
text-align: right;
|
||||
}
|
||||
#data .bijoe-drilldown {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* center cells vertically */
|
||||
#data td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* add some spacing around data */
|
||||
#data td, #data th {
|
||||
padding-left: 1ex;
|
||||
padding-right: 1ex;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
/* highlight data rows on hover */
|
||||
tbody tr:nth-child(even):hover td:not([rowspan]) {
|
||||
background: #F1F1F1;
|
||||
}
|
||||
tbody tr:nth-child(odd):hover td:not([rowspan]) {
|
||||
background: #F1F1F1;
|
||||
}
|
||||
tbody tr:nth-child(even) td:not([rowspan]) {
|
||||
background: lightgrey;
|
||||
}
|
||||
|
||||
/* chart representation */
|
||||
|
||||
.bijoe-png-button {
|
||||
/* move PNG button slightly inside the chart frame */
|
||||
float: right;
|
||||
bottom: -3em;
|
||||
left: -2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* misc */
|
||||
.bijoe-button {
|
||||
background: #aaaaaa linear-gradient(to bottom, #f9f9f9, #eeeeee) repeat scroll 0 0;
|
||||
border: 1px solid #b7b7b7;
|
||||
border-radius: 1px;
|
||||
box-shadow: 0 2px 2px 0 #ddd;
|
||||
color: #424258;
|
||||
cursor: pointer;
|
||||
line-height: 100%;
|
||||
padding: 1ex 2ex;
|
||||
text-decoration: none;
|
||||
}
|
|
@ -0,0 +1,301 @@
|
|||
|
||||
/* Utility functions */
|
||||
|
||||
function capfirst(a) {
|
||||
if (! a.length) {
|
||||
return a;
|
||||
}
|
||||
if (a.charCodeAt(0) > 96 && a.charCodeAt(0) < 123) {
|
||||
return String.fromCharCode(a.charCodeAt(0) - 32) + a.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
function human_join(a) {
|
||||
/* Build french string for an enumeration */
|
||||
if (a.length == 0) {
|
||||
return '';
|
||||
}else if (a.length == 1) {
|
||||
return a[0].toString();
|
||||
} else {
|
||||
return a.slice(0, -1).join(', ') + ' et ' + a.slice(-1)[0];
|
||||
}
|
||||
}
|
||||
|
||||
function wrap_text(s, width) {
|
||||
/* Wrap text after width characters */
|
||||
var words = s.split(" ");
|
||||
var s = "";
|
||||
var l = 0;
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
l += 1 + words[i].length;
|
||||
if (l > width) {
|
||||
s = s + "\n";
|
||||
l = words[i].length;
|
||||
} else {
|
||||
s = s + " ";
|
||||
}
|
||||
s = s + words[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function spaced_hsla(i, n, s, l, a) {
|
||||
/* Compute a specific color in a hue gradient in hsla colorspace, used to generate color maps for
|
||||
* charts. */
|
||||
var h = 360 * i/n;
|
||||
return "hsla(" + h.toString() + ', ' + s.toString() + '%, ' + l.toString() + '%, ' + a.toString() + ')';
|
||||
};
|
||||
|
||||
function toDataURL(canvas, filename) {
|
||||
/* Convert a canvas to a PNG image with a white background, and generate a data url for it */
|
||||
|
||||
/* Temporary canvas with white background to print original canvas over */
|
||||
destinationCanvas = document.createElement("canvas");
|
||||
destinationCanvas.width = canvas.width;
|
||||
destinationCanvas.height = canvas.height;
|
||||
|
||||
destCtx = destinationCanvas.getContext('2d');
|
||||
|
||||
// create a rectangle with the desired color
|
||||
destCtx.fillStyle = "#FFFFFF";
|
||||
destCtx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// draw the original canvas onto the destination canvas
|
||||
destCtx.drawImage(canvas, 0, 0);
|
||||
var data = destinationCanvas.toDataURL("image/png");
|
||||
data = data.split(";", 2)[1];
|
||||
data = "data:application/octet-stream;headers=Content-Disposition%3A%20attachment%3B%20filename=" + filename + ";" + data;
|
||||
return data
|
||||
}
|
||||
|
||||
var defaul_cn_options = {
|
||||
/* Default ChartNew.js options */
|
||||
animationSteps: 10,
|
||||
/* Adapt graph to change in layout */
|
||||
responsive: true,
|
||||
/* Re-scale font based on responsive relayout */
|
||||
responseScaleContent: true,
|
||||
/* Show legend describing each color */
|
||||
legend: true,
|
||||
/* Start animation immediately do not wait for the chart to be visible */
|
||||
dynamicDisplay: false,
|
||||
/* Mimimum interval between y-axis ticks */
|
||||
yAxisMinimumInterval: 10,
|
||||
/* Show label on bars/points */
|
||||
inGraphDataShow: true,
|
||||
/* Set legend text color to black */
|
||||
legendFontColor: "rgb(0,0,0,1)",
|
||||
/* Set axis text color to black */
|
||||
scaleFontColor: "rgb(0,0,0,1)",
|
||||
/* Round number to two decimals */
|
||||
roundNumber: -2,
|
||||
/* Display data title when mouse is over the chart */
|
||||
annotateDisplay : true,
|
||||
/* Label do display on scale */
|
||||
scaleLabel: "<%=value%>",
|
||||
/* Set style for axis texts */
|
||||
scaleFontSize : 16,
|
||||
/* Display border around charts */
|
||||
canvasBorders : true,
|
||||
/* Set style of graph title */
|
||||
graphTitleFontFamily : "'Arial'",
|
||||
graphTitleFontSize : 24,
|
||||
graphTitleFontStyle : "bold",
|
||||
graphTitleFontColor : "#666",
|
||||
/* Set style of footnote text (only used in piecharts) */
|
||||
footNoteFontSize: 16,
|
||||
}
|
||||
|
||||
function draw_piechart(canvas, dimensions, measures, measure, data) {
|
||||
/* Draw piechart given a cavans element, a list of dimensions, a list of measure, the name of the
|
||||
* measure to represent and data rows containing in the same order the dimensions and measures
|
||||
* values */
|
||||
var datasets = [];
|
||||
var linedata = {datasets: datasets};
|
||||
linedata.labels = [""];
|
||||
var empty = []
|
||||
var n = dimensions.length;
|
||||
var option = $.extend({}, defaul_cn_options);
|
||||
var ctx = canvas.getContext("2d");
|
||||
|
||||
for (var j = 0; j < measures.length; j++) {
|
||||
if (measures[j].name == measure) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == measures.length) {
|
||||
// measure not found
|
||||
return;
|
||||
}
|
||||
option.graphTitle = wrap_text(
|
||||
capfirst(measures[j].label) + " par " + human_join(dimension_labels), 30)
|
||||
|
||||
/* Build one dataset by measurement, it's the way the piecharts expects it. */
|
||||
var n = data.length;
|
||||
for (var i = 0; i < n; i++) {
|
||||
var value = data[i].measures[j].value;
|
||||
var title = [];
|
||||
|
||||
for (var k = 0; k < data[i].coords.length; k++) {
|
||||
title.push(data[i].coords[k].value);
|
||||
}
|
||||
title = title.join();
|
||||
|
||||
if (value > 1) {
|
||||
dataset = {
|
||||
data: [value],
|
||||
title: title,
|
||||
fillColor: spaced_hsla(i, n, 100, 30, 0.5),
|
||||
strokeColor: spaced_hsla(i, n, 100, 30, 0.75),
|
||||
highlightFill: spaced_hsla(i, n, 100, 30, 0.75),
|
||||
highlightStroke: spaced_hsla(i, n, 100, 30, 1)
|
||||
};
|
||||
datasets.push(dataset);
|
||||
} else {
|
||||
/* push empty pie slices to the empty array */
|
||||
empty.push(title);
|
||||
}
|
||||
}
|
||||
|
||||
/* show only percentages */
|
||||
option.inGraphDataTmpl = "<%=v1+' '+v6+' %'%>";
|
||||
option.annotateLabel = "<%=v1+' '+v6+' %'%>";
|
||||
|
||||
/* sort pie by slice size */
|
||||
datasets.sort(function (a, b) {
|
||||
return a.value - b.value;
|
||||
});
|
||||
|
||||
/* if some slices are empty or nearly empty, do not try to show them in the piechart but add a
|
||||
* footnore */
|
||||
if (empty.length) {
|
||||
option.footNote = wrap_text("Les valeurs pour " + human_join(empty)
|
||||
+ " sont nulles ou presque.", 110);
|
||||
}
|
||||
new Chart(ctx).Pie(linedata, option);
|
||||
}
|
||||
|
||||
function draw_barchart(canvas, dimensions, measures, measure, data) {
|
||||
/* Draw bar chart given a cavans element, a list of dimensions, a list of measure, the name of the
|
||||
* measure to represent and data rows containing in the same order the dimensions and measures
|
||||
* values */
|
||||
var dataset = {
|
||||
data: [],
|
||||
title: "",
|
||||
fillColor: [],
|
||||
strokeColor: [],
|
||||
highlightFill: [],
|
||||
highlightStroke: []
|
||||
}
|
||||
var datasets = [dataset];
|
||||
var linedata = {labels: [], datasets: datasets};
|
||||
var empty = []
|
||||
var n = dimensions.length;
|
||||
var option = $.extend({}, defaul_cn_options);
|
||||
var ctx = canvas.getContext("2d");
|
||||
|
||||
for (var j = 0; j < measures.length; j++) {
|
||||
if (measures[j].name == measure) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == measures.length) {
|
||||
// measure not found
|
||||
return;
|
||||
}
|
||||
measure = measures[j]
|
||||
|
||||
dataset.title = measures[j].label;
|
||||
option.graphTitle = wrap_text(
|
||||
capfirst(measures[j].label) + " par " + human_join(dimension_labels), 30)
|
||||
|
||||
var n = data.length;
|
||||
for (var i = 0; i < n; i++) {
|
||||
var row = data[i];
|
||||
var title = [];
|
||||
|
||||
for (var k = 0; k < data[i].coords.length; k++) {
|
||||
title.push(data[i].coords[k].value);
|
||||
}
|
||||
title = title.join();
|
||||
linedata.labels.push(title)
|
||||
|
||||
console.log(i, n);
|
||||
dataset.data.push(row.measures[j].value);
|
||||
dataset.fillColor.push(spaced_hsla(i, n, 100, 30, 0.5));
|
||||
dataset.strokeColor.push(spaced_hsla(i, n, 100, 30, 0.75));
|
||||
dataset.highlightFill.push(spaced_hsla(i, n, 100, 30, 0.75));
|
||||
dataset.highlightStroke.push(spaced_hsla(i, n, 100, 30, 1));
|
||||
}
|
||||
|
||||
if (measure.name.indexOf("delay") != -1) {
|
||||
option.yAxisLabel = "jours";
|
||||
}
|
||||
if (measure.name == "count") {
|
||||
option.yAxisLabel = "quantité";
|
||||
}
|
||||
if (measure.name == "percent") {
|
||||
option.yAxisLabel = "pourcentage";
|
||||
}
|
||||
|
||||
$.extend(option, {
|
||||
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero : true,
|
||||
|
||||
//Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
|
||||
//Boolean - If there is a stroke on each bar
|
||||
// barShowStroke : true,
|
||||
|
||||
//Number - Pixel width of the bar stroke
|
||||
barStrokeWidth : 2,
|
||||
|
||||
//Number - Spacing between each of the X value sets
|
||||
barValueSpacing : 5,
|
||||
|
||||
//Number - Spacing between data sets within X values
|
||||
barDatasetSpacing : 1,
|
||||
});
|
||||
console.log(linedata);
|
||||
new Chart(ctx).Bar(linedata, option);
|
||||
}
|
||||
|
||||
/* jQuery event handlers and widget setup */
|
||||
$(function () {
|
||||
/* Setup datepicker on input fields */
|
||||
$('input[type=date]').datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
},
|
||||
$.datepicker.regional['fr']);
|
||||
|
||||
/* Submit after 300ms on any change to form input */
|
||||
$('input, select').on('change', function () {
|
||||
setTimeout(function () {
|
||||
$('form').ajaxSubmit({success: function (data) {
|
||||
var $content = $(data);
|
||||
$('#data').replaceWith($content.find('#data'));
|
||||
$('#breadcrumb').replaceWith($content.find('#breadcrumb'));
|
||||
location.hash = 'data';
|
||||
}});
|
||||
}, 300);
|
||||
return true;
|
||||
});
|
||||
|
||||
/* Add clear button to date fields */
|
||||
$('input[type=date]').addClear();
|
||||
})
|
|
@ -1 +1,7 @@
|
|||
{% extends "gadjo/base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block extrascripts %}
|
||||
{{ block.super }}
|
||||
<link href="{% static "css/bijoe.css" %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,136 +2,52 @@
|
|||
{% load i18n staticfiles bijoe %}
|
||||
|
||||
{% block extrascripts %}
|
||||
{% import_django_select2_js_css %}
|
||||
{{ block.super }}
|
||||
<script src="{% static "js/jquery.form.js" %}"></script>
|
||||
<script src="{% static "js/addclear.js" %}"></script>
|
||||
<script src="{% static "xstatic/ChartNew.js" %}"></script>
|
||||
{{ form.media.css }}
|
||||
{{ form.media.js }}
|
||||
<style>
|
||||
form p {
|
||||
margin-bottom: 0.5ex;
|
||||
}
|
||||
form h3 {
|
||||
margin-top: 0.5ex;
|
||||
margin-bottom: 0.5ex;
|
||||
}
|
||||
.ui-datepicker select {
|
||||
padding-right: 0px;
|
||||
}
|
||||
#id_filter__receipt_time_1 {
|
||||
margin-left: 16px;
|
||||
}
|
||||
#data {
|
||||
width: calc(100% - 23em);
|
||||
}
|
||||
#id_representation {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
text-align: justify;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
#ods {
|
||||
margin: 1ex;
|
||||
top: -2ex;
|
||||
}
|
||||
form {
|
||||
float: right;
|
||||
width: 20em;
|
||||
padding-left: 2em;
|
||||
}
|
||||
input[type=date] {
|
||||
width: calc(100% / 2 - 8px);
|
||||
}
|
||||
select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#data td, #data th {
|
||||
padding-left: 1ex;
|
||||
padding-right: 1ex;
|
||||
}
|
||||
|
||||
#data tbody td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#data tr:hover {
|
||||
border: 3px solid grey;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) td:not([rowspan]) {
|
||||
background: lightgrey;
|
||||
}
|
||||
#data table {
|
||||
border-collapse: collapse;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
float: right;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
.filter { display: inline-block; }
|
||||
th {
|
||||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
}
|
||||
.download {
|
||||
float: right;
|
||||
bottom: -3em;
|
||||
left: -2em;
|
||||
position: relative;
|
||||
background: #aaaaaa linear-gradient(to bottom, #f9f9f9, #eeeeee) repeat scroll 0 0;
|
||||
border: 1px solid #b7b7b7;
|
||||
border-radius: 1px;
|
||||
box-shadow: 0 2px 2px 0 #ddd;
|
||||
color: #424258;
|
||||
cursor: pointer;
|
||||
line-height: 100%;
|
||||
padding: 1ex 2ex;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function human_join(a) {
|
||||
if (a.length == 0) {
|
||||
return '';
|
||||
}else if (a.length == 1) {
|
||||
return a[0].toString();
|
||||
} else {
|
||||
return a.slice(0, -1).join(', ') + ' et ' + a.slice(-1)[0];
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/css" media="print">
|
||||
/* hide UI */
|
||||
#top, #header, #more-user-links, .download, #content form {
|
||||
display: none;
|
||||
}
|
||||
/* resize content */
|
||||
#data {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
{% import_django_select2_js_css %}
|
||||
{{ block.super }}
|
||||
<script src="{% static "js/jquery.form.js" %}"></script>
|
||||
<script src="{% static "js/addclear.js" %}"></script>
|
||||
<script src="{% static "xstatic/ChartNew.js" %}"></script>
|
||||
<script src="{% static "js/bijoe.js" %}"></script>
|
||||
{{ form.media.css }}
|
||||
{{ form.media.js }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block breadcrumb %}
|
||||
{% url 'homepage' as homepage_url %}
|
||||
{% url 'warehouse' warehouse=warehouse.name as warehouse_url %}
|
||||
|
||||
{% url 'homepage' as homepage_url %}
|
||||
<a href="{{ homepage_url }}">{% trans "Homepage" %}</a>
|
||||
<a href="{{ homepage_url }}">{% trans "Accueil" %}</a>
|
||||
<a href="{{ warehouse_url }}">{{ warehouse.label }}</a>
|
||||
<a>{{ cube.label }}</a>
|
||||
|
||||
{% url 'warehouse' warehouse=warehouse.name as warehouse_url %}
|
||||
<a href="{{ warehouse_url }}">{{ warehouse.label }}</a>
|
||||
|
||||
<a>{{ cube.label }}</a>
|
||||
{% if data %}
|
||||
<a href="?{% firstof view.request.POST.urlencode view.request.GET.urlencode %}">
|
||||
<span id="bijoe-slice-url-span">
|
||||
{% for dim in drilldown %}
|
||||
{% if not forloop.first %}
|
||||
{% if forloop.last %}
|
||||
{% trans "et" %}
|
||||
{% else %}
|
||||
,
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{{ dim.label }}
|
||||
{% endfor %}
|
||||
par
|
||||
{% for measure in measures %}
|
||||
{% if not forloop.first %}
|
||||
{% if forloop.last %}
|
||||
{% trans "et" %}
|
||||
{% else %}
|
||||
,
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{{ measure.label }}
|
||||
{% endfor %}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -139,295 +55,28 @@ function human_join(a) {
|
|||
{% csrf_token %}
|
||||
<input type="submit" value="ODS" name="ods" id="ods"/>
|
||||
<h3>Représentation</h3>
|
||||
{% with field=form.representation %}
|
||||
<p {% if field.css_classes %}class="{{ field.css_classes }}"{% endif %}>
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endwith %}
|
||||
{% include "bijoe/field.html" with field=form.representation %}
|
||||
<h3>Mesure(s)</h3>
|
||||
{% with field=form.measures %}
|
||||
<p {% if field.css_classes %}class="{{ field.css_classes }}"{% endif %}>
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endwith %}
|
||||
{% include "bijoe/field.html" with field=form.measures %}
|
||||
<h3>Regroupement(s)</h3>
|
||||
{% with field=form.drilldown %}
|
||||
<p {% if field.css_classes %}class="{{ field.css_classes }}"{% endif %}>
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endwith %}
|
||||
{% include "bijoe/field.html" with field=form.drilldown %}
|
||||
<h3>Filtre(s)</h3>
|
||||
{% for field in form %}
|
||||
{% if not field.is_hidden and field.name != "measures" and field.name != "drilldown" and field.name != "representation" %}
|
||||
<p {% if field.css_classes %}class="{{ field.css_classes }}"{% endif %}>
|
||||
{{ field.label_tag }}
|
||||
{% if field.errors %}
|
||||
<ul class="errorlist">
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% include "bijoe/filter_field.html" with field=field %}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
</form>
|
||||
<div id="data">
|
||||
{% if data %}
|
||||
<a href="?{% firstof view.request.POST.urlencode view.request.GET.urlencode %}">URL</a>
|
||||
{% if representation == 'table' %}
|
||||
<table class="bijoe-table">
|
||||
<thead>
|
||||
{% for dimension in drilldown %}
|
||||
<th><span>{{ dimension.label.capitalize }}</span></th>
|
||||
{% endfor %}
|
||||
{% for measure in measures %}
|
||||
<th><span>{{ measure.label.capitalize }}</span></th>
|
||||
{% endfor %}
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in grouped_data %}
|
||||
<tr>
|
||||
{% for cell, count in row %}
|
||||
<td {% if count > 1 %}rowspan="{{ count }}"{% elif count == 0 %}class="measure"{% endif %}>{% if cell == None %}{% trans "None" %}{% else %}{{ cell }}{% endif %}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include "bijoe/cube_table.html" %}
|
||||
{% else %}
|
||||
{% for measure in measures %}
|
||||
<a href="#" download="graph.png" class="download">PNG</a>
|
||||
<canvas style="width: 100%" id="canvas-{{ measure.name }}"></canvas>
|
||||
<script>
|
||||
function wrap_text(s, width) {
|
||||
var words = s.split(" ");
|
||||
var s = "";
|
||||
var l = 0;
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
l += 1 + words[i].length;
|
||||
if (l > width) {
|
||||
s = s + "\n";
|
||||
l = words[i].length;
|
||||
} else {
|
||||
s = s + " ";
|
||||
}
|
||||
s = s + words[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
var Colors = {};
|
||||
Colors.spaced_hsla = function (i, n, s, l, a) {
|
||||
var h = 360 * i/n;
|
||||
return "hsla(" + h.toString() + ', ' + s.toString() + '%, ' + l.toString() + '%, ' + a.toString() + ')';
|
||||
};
|
||||
var data = {{ json|safe }};
|
||||
var dataset = {data: [], title: "{{ measure.label }}", fillColor: [], strokeColor: [],
|
||||
highlightFill: [], highlightStroke: []}
|
||||
var linedata = {labels: [], datasets: [dataset]};
|
||||
var dimensions = {{ drilldown_json|safe }};
|
||||
var dimension_labels = [];
|
||||
for (var i = 0; i < dimensions.length; i++) {
|
||||
dimension_labels.push(dimensions[i].label);
|
||||
}
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var row = data[i];
|
||||
var label = [];
|
||||
var datasets = linedata.datasets;
|
||||
for (var j = 0; j < row.coords.length; j++) {
|
||||
label.push(row.coords[j].value)
|
||||
}
|
||||
label = label.join(', ');
|
||||
linedata.labels.push(label)
|
||||
var n = data.length;
|
||||
for (var j = 0; j < row.measures.length; j++) {
|
||||
if (row.measures[j].label != "{{ measure.label }}") {
|
||||
continue;
|
||||
}
|
||||
dataset.data.push(row.measures[j].value);
|
||||
dataset.fillColor.push(Colors.spaced_hsla(i, n, 100, 30, 0.5));
|
||||
dataset.strokeColor.push(Colors.spaced_hsla(i, n, 100, 30, 0.75));
|
||||
dataset.highlightFill.push(Colors.spaced_hsla(i, n, 100, 30, 0.75));
|
||||
dataset.highlightStroke.push(Colors.spaced_hsla(i, n, 100, 30, 1));
|
||||
}
|
||||
}
|
||||
console.log("linedata", linedata);
|
||||
var canvas = $('#canvas-{{ measure.name }}')[0];
|
||||
var ctx = canvas.getContext("2d");
|
||||
|
||||
var option = {
|
||||
animationSteps: 10,
|
||||
//String - A legend template
|
||||
responsive: true,
|
||||
responseScaleContent: true,
|
||||
legend: true,
|
||||
dynamicDisplay: false,
|
||||
yAxisMinimumInterval: 10,
|
||||
inGraphDataShow: true,
|
||||
legendFontColor: "rgb(0,0,0,1)",
|
||||
scaleFontColor: "rgb(0,0,0,1)",
|
||||
roundNumber: -2,
|
||||
annotateDisplay : true,
|
||||
datasetFill : true,
|
||||
scaleLabel: "<%=value%>",
|
||||
scaleFontSize : 16,
|
||||
canvasBorders : true,
|
||||
graphTitle : wrap_text("{{ measure.label|capfirst }}" + " par " + human_join(dimension_labels), 60),
|
||||
graphTitleFontFamily : "'Arial'",
|
||||
graphTitleFontSize : 24,
|
||||
graphTitleFontStyle : "bold",
|
||||
graphTitleFontColor : "#666",
|
||||
legend : false,
|
||||
}
|
||||
if ("{{ measure.name }}".indexOf("delay") != -1) {
|
||||
option.yAxisLabel = "jours";
|
||||
}
|
||||
if ("{{ measure.name }}" == "count") {
|
||||
option.yAxisLabel = "quantité";
|
||||
}
|
||||
if ("{{ measure.name }}" == "percent") {
|
||||
option.yAxisLabel = "pourcentage";
|
||||
}
|
||||
|
||||
if ("{{ measure.name }}" == "percent") {
|
||||
// pivot
|
||||
var labels = linedata.labels;
|
||||
linedata.labels = [""];
|
||||
var datasets = [];
|
||||
var empty = []
|
||||
var Colors = {};
|
||||
Colors.spaced_hsla = function (i, n, s, l, a) {
|
||||
var h = 360 * i/n;
|
||||
return "hsla(" + h.toString() + ', ' + s.toString() + '%, ' + l.toString() + '%, ' + a.toString() + ')';
|
||||
}
|
||||
var n = linedata.datasets[0].data.length;
|
||||
for (var i = 0; i < n; i++) {
|
||||
var value = linedata.datasets[0].data[i];
|
||||
if (value > 1) {
|
||||
dataset = {data: [value], title: labels[i] || "Aucun(e)"};
|
||||
$.extend(dataset, {
|
||||
fillColor: Colors.spaced_hsla(i, n, 100, 30, 0.5),
|
||||
strokeColor: Colors.spaced_hsla(i, n, 100, 30, 0.75),
|
||||
highlightFill: Colors.spaced_hsla(i, n, 100, 30, 0.75),
|
||||
highlightStroke: Colors.spaced_hsla(i, n, 100, 30, 1)
|
||||
})
|
||||
datasets.push(dataset);
|
||||
} else {
|
||||
empty.push(labels[i]);
|
||||
}
|
||||
}
|
||||
option.inGraphDataTmpl = "<%=v1+' '+v6+' %'%>";
|
||||
option.annotateLabel = "<%=v1+' '+v6+' %'%>";
|
||||
datasets.sort(function (a, b) { return a.value - b.value; });
|
||||
if (empty.length) {
|
||||
option.footNote = wrap_text("Les valeurs pour " + human_join(empty) + " sont nulles ou presque.", 110);
|
||||
}
|
||||
linedata.datasets = datasets;
|
||||
console.log("pie data", linedata);
|
||||
new Chart(ctx).Pie(linedata, option);
|
||||
} else {
|
||||
$.extend(option, {
|
||||
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero : true,
|
||||
|
||||
//Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines : true,
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor : "rgba(0,0,0,.05)",
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth : 1,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
|
||||
//Boolean - If there is a stroke on each bar
|
||||
// barShowStroke : true,
|
||||
|
||||
//Number - Pixel width of the bar stroke
|
||||
barStrokeWidth : 2,
|
||||
|
||||
//Number - Spacing between each of the X value sets
|
||||
barValueSpacing : 5,
|
||||
|
||||
//Number - Spacing between data sets within X values
|
||||
barDatasetSpacing : 1,
|
||||
});
|
||||
new Chart(ctx).Bar(linedata, option);
|
||||
}
|
||||
console.log("option", option);
|
||||
function toDataURL(canvas) {
|
||||
destinationCanvas = document.createElement("canvas");
|
||||
destinationCanvas.width = canvas.width;
|
||||
destinationCanvas.height = canvas.height;
|
||||
|
||||
destCtx = destinationCanvas.getContext('2d');
|
||||
|
||||
//create a rectangle with the desired color
|
||||
destCtx.fillStyle = "#FFFFFF";
|
||||
destCtx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
//draw the original canvas onto the destination canvas
|
||||
destCtx.drawImage(canvas, 0, 0);
|
||||
var data = destinationCanvas.toDataURL("image/png");
|
||||
data = data.split(";", 2)[1];
|
||||
data = "data:application/octet-stream;headers=Content-Disposition%3A%20attachment%3B%20filename=graph.png;" + data;
|
||||
return data
|
||||
}
|
||||
|
||||
$(".download").on('click', function() {
|
||||
this.href = toDataURL($(this).next("canvas")[0]);
|
||||
})
|
||||
</script>
|
||||
{% endfor %}
|
||||
{% include "bijoe/cube_chart.html" %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="big-msg-info">Veuillez choisir des mesures et des regroupements</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script>
|
||||
$(function () {
|
||||
$('input[type=date]').datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
},
|
||||
$.datepicker.regional['fr']);
|
||||
$('input, select').on('change', function () {
|
||||
setTimeout(function () {
|
||||
$('form').ajaxSubmit({success: function (data) {
|
||||
var $content = $(data);
|
||||
$('#data').empty();
|
||||
$('#data').replaceWith($content.find('#data'));
|
||||
}});
|
||||
}, 800);
|
||||
return true;
|
||||
});
|
||||
$('input[type=date]').addClear();
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block page-end %}
|
||||
<script>
|
||||
{% if data %}
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<script>
|
||||
var data = {{ json|safe }};
|
||||
var dimensions = {{ drilldown_json|safe }};
|
||||
var measures = {{ measures_json|safe }};
|
||||
var dimension_labels = [];
|
||||
for (var i = 0; i < dimensions.length; i++) {
|
||||
dimension_labels.push(dimensions[i].label);
|
||||
}
|
||||
</script>
|
||||
|
||||
{% for measure in measures %}
|
||||
<a href="#" target="none" class="bijoe-button bijoe-png-button">PNG</a>
|
||||
<canvas id="canvas-{{ measure.name }}"></canvas>
|
||||
<script>
|
||||
var canvas = $('#canvas-{{ measure.name }}')[0];
|
||||
if ("{{ measure.name }}" == "percent") {
|
||||
draw_piechart(canvas, dimensions, measures, "{{ measure.name }}", data);
|
||||
} else {
|
||||
draw_barchart(canvas, dimensions, measures, "{{ measure.name }}", data);
|
||||
|
||||
/* Allow getting a PNG without using 'Save image as' */
|
||||
$(".bijoe-png-button").on('click', function() {
|
||||
this.href = toDataURL($(this).next("canvas")[0], "graph.png");
|
||||
})
|
||||
}
|
||||
</script>
|
||||
{% endfor %}
|
|
@ -0,0 +1,21 @@
|
|||
{% load i18n %}
|
||||
|
||||
<table class="bijoe-table">
|
||||
<thead>
|
||||
{% for dimension in drilldown %}
|
||||
<th class="bijoe-drilldown"><span>{{ dimension.label.capitalize }}</span></th>
|
||||
{% endfor %}
|
||||
{% for measure in measures %}
|
||||
<th class="bijoe-measure"><span>{{ measure.label.capitalize }}</span></th>
|
||||
{% endfor %}
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in grouped_data %}
|
||||
<tr>
|
||||
{% for cell, count in row %}
|
||||
<td {% if count > 1 %}rowspan="{{ count }}"{% endif %} {% if count > 0 %}class="bijoe-drilldown"{% elif count == 0 %}class="bijoe-measure"{% endif %}>{% if cell == None %}{% trans "Aucun(e)" %}{% else %}{{ cell }}{% endif %}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
|
@ -0,0 +1,6 @@
|
|||
<p {% if field.css_classes %}class="{{ field.css_classes }}"{% endif %}>
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</p>
|
|
@ -0,0 +1,14 @@
|
|||
<p {% if field.css_classes %}class="{{ field.css_classes }}"{% endif %}>
|
||||
{{ field.label_tag }}
|
||||
{% if field.errors %}
|
||||
<ul class="errorlist">
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</p>
|
Loading…
Reference in New Issue