2011-04-15 06:42:41 +00:00
|
|
|
/*---------------------------------------------------------
|
2011-09-05 12:28:15 +00:00
|
|
|
* OpenERP web_graph
|
2011-04-15 06:42:41 +00:00
|
|
|
*---------------------------------------------------------*/
|
2013-11-08 15:41:14 +00:00
|
|
|
|
2013-11-08 13:08:06 +00:00
|
|
|
'use strict';
|
2013-11-12 08:38:27 +00:00
|
|
|
/* jshint undef: false */
|
|
|
|
|
2011-04-15 06:42:41 +00:00
|
|
|
|
2012-04-17 12:48:30 +00:00
|
|
|
openerp.web_graph = function (instance) {
|
2011-06-17 13:25:50 +00:00
|
|
|
|
2012-06-07 14:17:12 +00:00
|
|
|
var _lt = instance.web._lt;
|
2013-01-25 07:02:40 +00:00
|
|
|
var _t = instance.web._t;
|
2013-11-12 13:53:44 +00:00
|
|
|
var QWeb = instance.web.qweb;
|
2012-05-07 08:19:08 +00:00
|
|
|
|
2012-04-17 12:48:30 +00:00
|
|
|
instance.web.views.add('graph', 'instance.web_graph.GraphView');
|
2013-11-08 13:08:06 +00:00
|
|
|
|
2013-11-08 15:20:48 +00:00
|
|
|
/**
|
|
|
|
* GraphView view. It mostly contains two widgets (PivotTable and ChartView)
|
|
|
|
* and some data.
|
|
|
|
*/
|
2012-04-17 12:48:30 +00:00
|
|
|
instance.web_graph.GraphView = instance.web.View.extend({
|
2013-11-08 13:08:06 +00:00
|
|
|
template: 'GraphView',
|
2011-12-16 13:00:00 +00:00
|
|
|
display_name: _lt('Graph'),
|
2013-11-08 13:08:06 +00:00
|
|
|
view_type: 'graph',
|
2013-11-12 16:10:34 +00:00
|
|
|
mode: 'pivot', // pivot => display pivot table, chart => display chart
|
2013-11-12 11:31:34 +00:00
|
|
|
|
2013-11-08 13:08:06 +00:00
|
|
|
events: {
|
|
|
|
'click .graph_mode_selection li' : function (event) {
|
|
|
|
event.preventDefault();
|
2013-11-12 16:10:34 +00:00
|
|
|
var view_mode = event.target.attributes['data-mode'].nodeValue;
|
|
|
|
if (view_mode === 'data') {
|
|
|
|
this.mode = 'pivot';
|
2013-11-08 13:08:06 +00:00
|
|
|
} else {
|
2013-11-12 16:10:34 +00:00
|
|
|
this.mode = 'chart';
|
|
|
|
this.chart_view.set_mode(view_mode);
|
2013-11-08 13:08:06 +00:00
|
|
|
}
|
2013-11-12 16:10:34 +00:00
|
|
|
this.display_data();
|
2013-11-08 13:08:06 +00:00
|
|
|
},
|
|
|
|
},
|
2011-04-15 06:42:41 +00:00
|
|
|
|
2013-11-08 13:08:06 +00:00
|
|
|
view_loading: function (fields_view_get) {
|
2013-11-08 15:20:48 +00:00
|
|
|
var self = this;
|
2013-11-12 16:10:34 +00:00
|
|
|
this.model = new instance.web.Model(fields_view_get.model, {group_by_no_leaf: true});
|
|
|
|
this.measure = null;
|
|
|
|
this.groupbys = [];
|
2013-11-08 15:20:48 +00:00
|
|
|
|
|
|
|
// get the default groupbys and measure defined in the field view
|
|
|
|
_.each(fields_view_get.arch.children, function (field) {
|
|
|
|
if ('name' in field.attrs) {
|
|
|
|
if ('operator' in field.attrs) {
|
2013-11-12 16:10:34 +00:00
|
|
|
self.measure = field.attrs.name;
|
2013-11-08 15:20:48 +00:00
|
|
|
} else {
|
2013-11-12 16:10:34 +00:00
|
|
|
self.groupbys.push(field.attrs.name);
|
2013-11-08 15:20:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-11-12 16:10:34 +00:00
|
|
|
return this.model.call('fields_get', []).then(function (fields) {
|
|
|
|
self.pivot_table = new PivotTable(fields, self.groupbys, self.measure);
|
|
|
|
self.chart_view = new ChartView(fields, self.groupbys, self.measure);
|
|
|
|
self.pivot_table.appendTo('.graph_main_content');
|
|
|
|
self.chart_view.appendTo('.graph_main_content');
|
2013-11-12 08:38:27 +00:00
|
|
|
});
|
2013-11-08 15:20:48 +00:00
|
|
|
},
|
|
|
|
|
2013-11-12 16:10:34 +00:00
|
|
|
get_data: function () {
|
|
|
|
var view_fields = this.groupbys.concat(this.measure);
|
|
|
|
return query_groups(this.model, view_fields, this.domain, this.groupbys);
|
|
|
|
},
|
|
|
|
|
2013-11-13 09:18:07 +00:00
|
|
|
display_data : function () {
|
|
|
|
if (this.mode === 'pivot') {
|
|
|
|
this.chart_view.hide();
|
|
|
|
this.pivot_table.show();
|
|
|
|
} else {
|
|
|
|
this.pivot_table.hide();
|
|
|
|
this.chart_view.show();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-11-08 15:20:48 +00:00
|
|
|
do_search: function (domain, context, group_by) {
|
2013-11-12 16:10:34 +00:00
|
|
|
var self = this;
|
2013-11-08 15:20:48 +00:00
|
|
|
this.domain = new instance.web.CompoundDomain(domain);
|
2013-11-12 16:10:34 +00:00
|
|
|
return this.get_data().done(function (data) {
|
|
|
|
self.pivot_table.set_data(data);
|
|
|
|
self.chart_view.set_data(data);
|
|
|
|
self.display_data();
|
|
|
|
});
|
2011-05-06 12:41:08 +00:00
|
|
|
},
|
2012-05-07 09:40:59 +00:00
|
|
|
|
2013-11-08 11:32:10 +00:00
|
|
|
do_show: function () {
|
2012-01-03 16:06:50 +00:00
|
|
|
this.do_push_state({});
|
|
|
|
return this._super();
|
2012-05-07 08:19:08 +00:00
|
|
|
},
|
2013-11-08 15:20:48 +00:00
|
|
|
|
2013-11-08 13:08:06 +00:00
|
|
|
});
|
|
|
|
|
2013-11-08 15:20:48 +00:00
|
|
|
/**
|
2013-11-12 11:31:34 +00:00
|
|
|
* BasicDataView widget. Basic widget to manage show/hide functionality
|
|
|
|
* and to initialize some attributes. It is inherited by PivotTable
|
|
|
|
* and ChartView widget.
|
2013-11-08 15:20:48 +00:00
|
|
|
*/
|
2013-11-12 11:31:34 +00:00
|
|
|
var BasicDataView = instance.web.Widget.extend({
|
2013-11-13 09:26:46 +00:00
|
|
|
|
|
|
|
need_redraw: false,
|
|
|
|
|
2013-11-12 16:10:34 +00:00
|
|
|
init: function (fields, groupby, measure) {
|
2013-11-12 11:31:34 +00:00
|
|
|
this.fields = fields;
|
|
|
|
this.groupby = groupby;
|
|
|
|
this.measure = measure;
|
2013-11-12 13:53:44 +00:00
|
|
|
this.measure_label = measure ? fields[measure].string : 'Quantity';
|
2013-11-12 16:10:34 +00:00
|
|
|
this.data = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
set_data : function (data) {
|
|
|
|
this.data = data;
|
2013-11-13 09:26:46 +00:00
|
|
|
this.need_redraw = true;
|
2013-11-12 11:31:34 +00:00
|
|
|
},
|
2013-11-08 13:08:06 +00:00
|
|
|
|
|
|
|
show: function () {
|
2013-11-13 09:26:46 +00:00
|
|
|
if (this.need_redraw) {
|
|
|
|
this.draw();
|
|
|
|
this.need_redraw = false;
|
|
|
|
}
|
2013-11-08 13:08:06 +00:00
|
|
|
this.$el.css('display', 'block');
|
|
|
|
},
|
|
|
|
|
|
|
|
hide: function () {
|
|
|
|
this.$el.css('display', 'none');
|
|
|
|
},
|
2013-11-12 16:10:34 +00:00
|
|
|
|
|
|
|
draw: function() {
|
|
|
|
},
|
2013-11-12 11:31:34 +00:00
|
|
|
});
|
2013-11-08 13:08:06 +00:00
|
|
|
|
2013-11-12 11:31:34 +00:00
|
|
|
/**
|
|
|
|
* PivotTable widget. It displays the data in tabular data and allows the
|
|
|
|
* user to drill down and up in the table
|
|
|
|
*/
|
|
|
|
var PivotTable = BasicDataView.extend({
|
|
|
|
template: 'pivot_table',
|
|
|
|
|
|
|
|
draw: function () {
|
2013-11-12 16:10:34 +00:00
|
|
|
this.$el.empty();
|
2013-11-12 13:53:44 +00:00
|
|
|
var self = this;
|
|
|
|
|
2013-11-13 09:18:07 +00:00
|
|
|
var rows = '<tr><td class="graph_border"><i class="fa fa-sort-asc"></i> fa-sort-asc' +
|
2013-11-12 13:53:44 +00:00
|
|
|
this.fields[this.groupby[0]].string +
|
2013-11-12 16:10:34 +00:00
|
|
|
'</td><td class="graph_border">' +
|
2013-11-12 13:53:44 +00:00
|
|
|
this.measure_label +
|
|
|
|
'</td></tr>';
|
|
|
|
_.each(this.data, function (datapt) {
|
2013-11-12 16:10:34 +00:00
|
|
|
rows += '<tr><td class="graph_border">' +
|
2013-11-12 13:53:44 +00:00
|
|
|
datapt.attributes.value[1] +
|
2013-11-12 16:10:34 +00:00
|
|
|
'</td><td>' +
|
|
|
|
datapt.attributes.aggregates[self.measure] +
|
2013-11-12 13:53:44 +00:00
|
|
|
'</td></tr>';
|
|
|
|
});
|
|
|
|
|
|
|
|
this.$el.append(rows);
|
2013-11-08 15:20:48 +00:00
|
|
|
},
|
2013-11-08 13:08:06 +00:00
|
|
|
});
|
|
|
|
|
2013-11-08 15:20:48 +00:00
|
|
|
/**
|
|
|
|
* ChartView widget. It displays the data in chart form, using the nvd3
|
|
|
|
* library. Various modes include bar charts, pie charts or line charts.
|
|
|
|
*/
|
2013-11-12 11:31:34 +00:00
|
|
|
var ChartView = BasicDataView.extend({
|
2013-11-08 13:08:06 +00:00
|
|
|
template: 'chart_view',
|
|
|
|
|
2013-11-12 11:31:34 +00:00
|
|
|
set_mode: function (mode) {
|
2013-11-12 16:10:34 +00:00
|
|
|
var render_functions = {
|
|
|
|
'bar_chart' : this.render_bar_chart,
|
|
|
|
'line_chart' : this.render_line_chart,
|
|
|
|
'pie_chart' : this.render_pie_chart,
|
|
|
|
};
|
|
|
|
|
|
|
|
this.render = render_functions[mode];
|
2013-11-13 09:26:46 +00:00
|
|
|
this.need_redraw = true;
|
2013-11-08 15:20:48 +00:00
|
|
|
},
|
|
|
|
|
2013-11-12 11:31:34 +00:00
|
|
|
draw: function () {
|
2013-11-12 08:49:55 +00:00
|
|
|
this.$el.empty();
|
|
|
|
this.$el.append('<svg></svg>');
|
2013-11-12 16:10:34 +00:00
|
|
|
this.render();
|
2013-11-08 15:20:48 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
format_data: function (datapt) {
|
|
|
|
var val = datapt.attributes;
|
|
|
|
return {
|
|
|
|
x: datapt.attributes.value[1],
|
|
|
|
y: this.measure ? val.aggregates[this.measure] : val.length,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2013-11-12 11:31:34 +00:00
|
|
|
render_bar_chart: function () {
|
2013-11-08 15:20:48 +00:00
|
|
|
var formatted_data = [{
|
|
|
|
key: 'Bar chart',
|
2013-11-12 11:31:34 +00:00
|
|
|
values: _.map(this.data, this.format_data.bind(this)),
|
2013-11-08 15:20:48 +00:00
|
|
|
}];
|
|
|
|
|
|
|
|
nv.addGraph(function () {
|
|
|
|
var chart = nv.models.discreteBarChart()
|
|
|
|
.tooltips(false)
|
|
|
|
.showValues(true)
|
|
|
|
.staggerLabels(true)
|
|
|
|
.width(650)
|
|
|
|
.height(400);
|
|
|
|
|
2013-11-08 15:27:17 +00:00
|
|
|
d3.select('.graph_chart svg')
|
2013-11-08 15:20:48 +00:00
|
|
|
.datum(formatted_data)
|
|
|
|
.attr('width', 650)
|
|
|
|
.attr('height', 400)
|
|
|
|
.call(chart);
|
|
|
|
|
|
|
|
nv.utils.windowResize(chart.update);
|
|
|
|
return chart;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-11-12 11:31:34 +00:00
|
|
|
render_line_chart: function () {
|
2013-11-08 15:20:48 +00:00
|
|
|
var formatted_data = [{
|
2013-11-12 13:53:44 +00:00
|
|
|
key: this.measure_label,
|
2013-11-12 11:31:34 +00:00
|
|
|
values: _.map(this.data, this.format_data.bind(this))
|
2013-11-08 15:20:48 +00:00
|
|
|
}];
|
|
|
|
|
|
|
|
nv.addGraph(function () {
|
|
|
|
var chart = nv.models.lineChart()
|
|
|
|
.x(function (d,u) { return u; })
|
2013-11-08 15:27:17 +00:00
|
|
|
.width(600)
|
|
|
|
.height(300)
|
2013-11-08 15:20:48 +00:00
|
|
|
.margin({top: 30, right: 20, bottom: 20, left: 60});
|
|
|
|
|
2013-11-08 15:27:17 +00:00
|
|
|
d3.select('.graph_chart svg')
|
|
|
|
.attr('width', 600)
|
|
|
|
.attr('height', 300)
|
2013-11-08 15:20:48 +00:00
|
|
|
.datum(formatted_data)
|
|
|
|
.call(chart);
|
|
|
|
|
|
|
|
return chart;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-11-12 11:31:34 +00:00
|
|
|
render_pie_chart: function () {
|
|
|
|
var formatted_data = _.map(this.data, this.format_data.bind(this));
|
2013-11-08 15:27:17 +00:00
|
|
|
|
|
|
|
nv.addGraph(function () {
|
|
|
|
var chart = nv.models.pieChart()
|
|
|
|
.color(d3.scale.category10().range())
|
|
|
|
.width(650)
|
|
|
|
.height(400);
|
|
|
|
|
|
|
|
d3.select('.graph_chart svg')
|
|
|
|
.datum(formatted_data)
|
|
|
|
.transition().duration(1200)
|
|
|
|
.attr('width', 650)
|
|
|
|
.attr('height', 400)
|
|
|
|
.call(chart);
|
|
|
|
|
|
|
|
nv.utils.windowResize(chart.update);
|
|
|
|
return chart;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2011-04-15 06:42:41 +00:00
|
|
|
});
|
2013-11-08 13:08:06 +00:00
|
|
|
|
2013-11-08 15:20:48 +00:00
|
|
|
/**
|
|
|
|
* Query the server and return a deferred which will return the data
|
|
|
|
* with all the groupbys applied (this is done for now, but the goal
|
|
|
|
* is to modify read_group in order to allow eager and lazy groupbys
|
|
|
|
*/
|
|
|
|
function query_groups (model, fields, domain, groupbys) {
|
|
|
|
return model.query(fields)
|
|
|
|
.filter(domain)
|
|
|
|
.group_by(groupbys)
|
|
|
|
.then(function (results) {
|
|
|
|
var non_empty_results = _.filter(results, function (group) {
|
|
|
|
return group.attributes.length > 0;
|
|
|
|
});
|
|
|
|
if (groupbys.length <= 1) {
|
|
|
|
return non_empty_results;
|
|
|
|
} else {
|
|
|
|
var get_subgroups = $.when.apply(null, _.map(non_empty_results, function (result) {
|
|
|
|
var new_domain = result.model._domain;
|
|
|
|
var new_groupings = groupbys.slice(1);
|
|
|
|
return query_groups(model, fields,new_domain, new_groupings).then(function (subgroups) {
|
|
|
|
result.subgroups_data = subgroups;
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
return get_subgroups.then(function () {
|
|
|
|
return non_empty_results;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2013-11-08 13:08:06 +00:00
|
|
|
|
|
|
|
|
2011-04-15 06:42:41 +00:00
|
|
|
};
|
2013-11-12 08:38:27 +00:00
|
|
|
|