Skip to content
Permalink
Browse files

[IMP] web: Pie Chart Widget for Dashboard View

This commit bring a Dashboard Widget used in dashboard view. We modify the
dashboard_view.xml of website_sale_dashboard to add two widgets.
  • Loading branch information...
FVolral committed Jul 18, 2018
1 parent 17f6e43 commit d647d36b5ef97574dda51f0cb513729be4a6ad2e
@@ -0,0 +1,202 @@
odoo.define('web.PieChart', function (require) {
"use strict";

/**
* This widget render a Pie Chart. It is used in the dashboard view.
*/
var ajax = require('web.ajax');
var core = require('web.core');
var config = require('web.config');
var Widget = require('web.Widget');
var widgetRegistry = require('web.widget_registry');
var GROUPABLE_TYPES = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];

var PieChart = Widget.extend({
className: 'o_pie_chart',
events: {
},
cssLibs: [
'/web/static/lib/nvd3/nv.d3.css'
],
jsLibs: [
'/web/static/lib/nvd3/d3.v3.js',
'/web/static/lib/nvd3/nv.d3.js',
'/web/static/src/js/libs/nvd3.js'
],

/**
* override
*
* @param {Widget} parent
* @param {Object} record
* @param {Object} node
*/
init: function (parent, record, node) {
this._super(parent);

this.record = record;
this.model = record.model;

if (node.attrs.modifiers) {
this.measure = node.attrs.modifiers.measure || '';
this.measureField = this.record.fields[this.measure];
this.groupBy = node.attrs.modifiers.groupby.split(':')[0] || '';
this.interval = node.attrs.modifiers.groupby.split(':')[1];

if (!_.contains(Object.keys(this.record.fields), this.groupBy)) {
return;
}

this.groupByField = this.record.fields[this.groupBy];
this.groupByType = this.groupByField.type;
this.title = node.attrs.modifiers.title || this.measure || '';
this.trueLabel = node.attrs.modifiers.trueLabel || 'True';
this.falseLabel = node.attrs.modifiers.falseLabel || 'False';
}

this.error = false;
if (this.groupByField.type != 'boolean') this.error = true;
},
/**
* override
*/
willStart: function () {
var self = this;
this.data = [];

var query = {
model: self.model,
method: 'read_group',
domain: [],
groupBy: [self.groupBy + (self.interval ? ':' + self.interval : '')],
fields: [self.measure],
lazy: false,
};

// Handle case where measure attribute is missing.
query.fields = (this.measure) ? [self.measure] : [];

return $.when(this._rpc(query), ajax.loadLibs(this)).then(
function(result) {
if (result) {
if (_.contains(GROUPABLE_TYPES, self.groupByType)) {
for (var i = 0; i < result.length; i++) {
var value = result[i][self.measure];
var label = undefined;
var interval = self.interval ? ':' + self.interval : '';
var groupby = self.groupBy + interval;

switch(self.groupByType) {
case 'boolean':
label = ['False', 'True'][i];
break;
case 'many2one':
label = result[i][groupby][1];
break;
case 'date':
case 'datetime':
default:
label = result[i][groupby];
}

self.data.push({
'label': label,
'value': value,
});
}
}
}
});
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* This method ensure that Pie Chart is attached to DOM before being rendered.
*
* If we don't do that, Pie Chart can be rendered without being aware of the
* size of its container, it is then rendered with a size of 0 0 and remains
* invisible (even with a good lens).
*/
on_attach_callback: function() {
this._render();
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
*/
_render: function () {
// Note: The rendering of this widget is aynchronous as NVD3 does a
// setTimeout(0) before executing the callback given to addGraph.
var self = this;
if(!self.data || !_.isArray(self.data)) {
return;
}
// this.chart = null;

var $label = this._renderLabel(self.title);
self.$el.empty()
$label.appendTo(self.$el);
$('<svg width=auto height=auto>').appendTo(self.$el);

var legend_right = config.device.size_class > config.device.SIZES.XS;
var legendPosition = legend_right ? 'right' : 'top';
var color = d3.scale.category10().range();

nv.addGraph(function() {
self.chart = nv.models.pieChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.legendPosition(legendPosition)
.labelType('percent')
.showLabels(true)
.showLegend(true)
.color(color);


self.chart.legend.rightAlign(false);
self.chart.legend.align(true);
self.chart.legend.expanded(true);

d3.select(self.$('svg')[0])
.datum(self.data)
.transition().duration(0)
.call(self.chart);
self.chart.update();
});
},
/**
* @private
* @param {any} value
* @param {any} field
* @returns {string}
*/
_getNumberedValue: function (value, field) {
var id= value[0];
var name= value[1];
this.numbering[field] = this.numbering[field] || {};
this.numbering[field][name] = this.numbering[field][name] || {};
var numbers = this.numbering[field][name];
numbers[id] = numbers[id] || _.size(numbers) + 1;
return name + (numbers[id] > 1 ? " (" + numbers[id] + ")" : "");
},
/**
* Renders a pie chart's label.
*
* @private
* @param {String} title
* @returns {jQueryElement}
*/
_renderLabel: function (title) {
var $result = $('<label>', {text: title});
return $result;
},
});

widgetRegistry.add('pie_chart', PieChart);

return PieChart;

});
@@ -0,0 +1,66 @@
odoo.define('web.PieChart_tests', function (require) {
"use strict";

var PieChart = require('web.PieChart');
var testUtils = require('web.test_utils');


function createPieChart(record, node, params) {
params = params || {};
var target = params.debug ? document.body : $('#qunit-fixture');
var pie = new PieChart(null, record, node);
testUtils.addMockEnvironment(pie, params);
pie.appendTo(target);
return pie;
}

QUnit.module('widgets', {}, function () {

QUnit.module('PieChart', {
beforeEach: function () {
this.node = {
attrs: {
modifiers: {
title: 'Super Pie Chart',
measure: 'float_field',
groupby: 'bar',
}
}
};
this.data = {
partner: {
fields: {
date_field: {string: "Date", type: "date", store: true, sortable: true},
birthday: {string: "Birthday", type: "date", store: true, sortable: true},
foo: {string: "Foo", type: "char", store: true, sortable: true},
bar: {string: "Bar", type: "many2one", relation: 'partner'},
float_field: {string: "Float", type: "float"},
},
records: [
{id: 1, display_name: "First record", foo: "yop", bar: 2, date_field: "2017-01-25", birthday: "1983-07-15", float_field: 1},
{id: 2, display_name: "Second record", foo: "blip", bar: 1, date_field: "2017-01-24", birthday: "1982-06-04",float_field: 2},
{id: 3, display_name: "Third record", foo: "gnap", bar: 1, date_field: "2017-01-13", birthday: "1985-09-13",float_field: 1.618},
{id: 4, display_name: "Fourth record", foo: "plop", bar: 2, date_field: "2017-02-25", birthday: "1983-05-05",float_field: -1},
{id: 5, display_name: "Fifth record", foo: "zoup", bar: 2, date_field: "2016-01-25", birthday: "1800-01-01",float_field: 13},
],
},
};
this.record = {
fields: this.data.partner.fields,
model: 'partner',
domain: [],
};

},
}, function () {

QUnit.test('rendering a PieChart', function (assert) {

assert.expect(0);

// var pieChart = createPieChart(this.record, this.node, {data: this.data});

});
});
});
});
@@ -234,6 +234,7 @@
<script type="text/javascript" src="/web/static/src/js/views/search/groupby_menu.js"></script>
<script type="text/javascript" src="/web/static/src/js/views/search/groupby_menu_interface_mixin.js"></script>
<script type="text/javascript" src="/web/static/src/js/widgets/dropdown_menu.js"></script>
<script type="text/javascript" src="/web/static/src/js/widgets/pie_chart.js"></script>
<script type="text/javascript" src="/web/static/src/js/views/search/filters_menu.js"></script>
<script type="text/javascript" src="/web/static/src/js/apps.js"></script>

@@ -540,6 +541,7 @@
<script type="text/javascript" src="/web/static/tests/views/view_dialogs_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/search_filters_menu_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/search_groupby_menu_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/widgets/pie_chart_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/widgets/dropdown_menu_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/search_view_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/core/ajax_tests.js"></script>

0 comments on commit d647d36

Please sign in to comment.
You can’t perform that action at this time.