Skip to content
Permalink
Browse files

[IMP] web: use Chart.js in graph view

  • Loading branch information...
Polymorphe57 committed Feb 26, 2019
1 parent 9ccba06 commit 8140dd204ce63851239090ee1b8197874952b475
@@ -5,82 +5,80 @@ var fieldUtils = require('web.field_utils');
var Class = require('web.Class');

var DateClasses = Class.extend({
init: function (dateSets, interval) {
// dateSets are assumed pairwise disjoint and ordered naturally.
// at least one of them is non empty.

// we complete the first inhabited dateSet. Its elements will be
// the default representatives for the classes.
this._maximalLength = 1;
/**
* this small class offers a ligth API to manage equivalence classes of
* dates. Two dates in different dateSets are declared equivalent when
* their indexes are equal.
*
* @param {Array[]} dateSets, a list of list of dates
*/
init: function (dateSets) {
// At least one dateSet must be non empty.
// The completion of the first inhabited dateSet will serve as a reference set.
// The reference set elements will be the default representatives for the classes.
this._dateSets = dateSets;
this._referenceIndex = null;
for (var i = 0; i < dateSets.length; i++) {
var dateSet = dateSets[i];
if (dateSet.length && this._referenceIndex === null) {
this._referenceIndex = i;
}
if (dateSet.length > this._maximalLength) {
this._maximalLength = dateSet.length;
}
}
this._referenceSet = this._contructReferenceSet(dateSets[this._referenceIndex], interval);
this._dateClasses = this._constructDateClasses(dateSets);
},

//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------

representative: function (date, index) {
index = index || this._referenceIndex;
return this._dateClass(date)[index];
/**
* Returns the index of a date in a given datesetIndex. This can be considered
* as the date class itself.
*
* @param {number} datesetIndex
* @param {string} date
* @return {number}
*/
dateClass: function (datesetIndex, date) {
return this.dateSets()[datesetIndex].indexOf(date);
},

//----------------------------------------------------------------------
// Private
//----------------------------------------------------------------------

/**
* @param {moment[]} dateSet, ordered dateSet
* @param {number} num, indicates number of dates to add
* @param {string} interval, determines time interval between two added dates
* @returns {function}
* returns the dates occuring in a given class
*
* @param {number} dateClass
* @return {string[]}
*/
_contructReferenceSet: function (dateSet, interval) {
var additionalDates = [];
var dateSetLength = dateSet.length;
var diff = this._maximalLength - dateSetLength;
var lastDate = dateSet[dateSetLength - 1];
for (var i = 0; i < diff; i++) {
var date = moment(lastDate).add(i + 1, interval).format('DD MMM YYYY');
additionalDates.push(date);
}
return dateSet.concat(additionalDates);
dateClassMembers: function (dateClass) {
return _.uniq(_.compact(this.dateSets().map(function (dateSet) {
return dateSet[dateClass];
})));
},
_constructDateClasses: function (dateSets) {
var dateClasses = [];
for (var index = 0; index < this._maximalLength; index++) {
var dateClass = [];
for (var j = 0; j < dateSets.length; j++) {
var dateSet = j=== this._referenceIndex ? this._referenceSet : dateSets[j];
if (index < dateSet.length) {
dateClass.push(dateSet[index]);
} else {
dateClass.push(undefined);
}
}
dateClasses.push(dateClass);
}
return dateClasses;
/**
* returns the original date sets.
*
* @return {Array[]}
*/
dateSets: function () {
return this._dateSets;
},
_dateClass: function (date) {
var dateClass;
for (var i = 0; i < this._dateClasses.length; i++) {
dateClass = this._dateClasses[i];
if (_.contains(dateClass, date)) {
break;
}
}
return dateClass;
/**
* returns the reference index (the index of the first inhabited date set).
*
* @return {number}
*/
referenceIndex: function () {
return this._referenceIndex;
},
/**
* return the representative of a date class from a date set specified by an
* index.
*
* @param {number} dateClass
* @param {number} index
* @return {string}
*/
representative: function (dateClass, index) {
index = index || this.referenceIndex();
return this.dateSets()[index][dateClass];
},
});
/**
@@ -114,7 +114,6 @@ var GraphController = AbstractController.extend(GroupByMenuMixin,{
_setGroupby: function (groupBy) {
this.update({groupBy: groupBy});
},

/**
* @todo remove this and directly calls update. Update should be overridden
* and modified to call _updateButtons
@@ -138,6 +137,10 @@ var GraphController = AbstractController.extend(GroupByMenuMixin,{
self._updateButtons();
});
},
_toggleStackMode: function (stacked) {
this.update({stacked: stacked});
this._updateButtons();
},
/**
* override
*
@@ -160,6 +163,11 @@ var GraphController = AbstractController.extend(GroupByMenuMixin,{
this.$buttons
.find('.o_graph_button[data-mode="' + state.mode + '"]')
.addClass('active');
this.$buttons
.find('.o_graph_button[data-mode="stack"]')
.data('stacked', state.stacked)
.toggleClass('active', state.stacked)
.toggleClass('o_hidden', state.mode !== 'bar');
_.each(this.$measureList.find('.dropdown-item'), function (item) {
var $item = $(item);
$item.toggleClass('selected', $item.data('field') === state.measure);
@@ -180,7 +188,11 @@ var GraphController = AbstractController.extend(GroupByMenuMixin,{
var $target = $(ev.target);
var field;
if ($target.hasClass('o_graph_button')) {
this._setMode($target.data('mode'));
if (_.contains(['bar','line', 'pie'], $target.data('mode'))) {
this._setMode($target.data('mode'));
} else if ($target.data('mode') === 'stack') {
this._toggleStackMode(!$target.data('stacked'));
}
} else if ($target.parents('.o_graph_measures_list').length) {
ev.preventDefault();
ev.stopPropagation();
@@ -25,12 +25,18 @@ return AbstractModel.extend({
//--------------------------------------------------------------------------

/**
* override
*
* We defend against outside modifications by extending the chart data. It
* may be overkill.
*
* override
* @returns {Object}
*/
get: function () {
return this.chart;
var self = this;
return _.extend({}, this.chart, {
comparisonFieldIndex: self._getComparisonFieldIndex(),
});
},
/**
* Initial loading.
@@ -44,7 +50,9 @@ return AbstractModel.extend({
* @param {Object} params.fields
* @param {string[]} params.comparisonTimeRange
* @param {string[]} params.domain
* @param {string[]} params.groupBy a list of valid field names
* @param {string[]} params.groupBys a list of valid field names
* @param {string[]} params.groupedBy a list of valid field names
* @param {boolean} params.stacked
* @param {string[]} params.timeRange
* @param {string} params.comparisonField
* @param {string} params.comparisonTimeRangeDescription
@@ -72,6 +80,7 @@ return AbstractModel.extend({
measure: params.context.graph_measure || params.measure,
mode: params.context.graph_mode || params.mode,
origins: [],
stacked: params.stacked,
timeRange: params.timeRange,
timeRangeDescription: params.timeRangeDescription,
};
@@ -86,6 +95,7 @@ return AbstractModel.extend({
*
* @param {any} handle ignored!
* @param {Object} params
* @param {boolean} [params.stacked]
* @param {Object} [params.context]
* @param {string[]} [params.domain]
* @param {string[]} [params.groupBy]
@@ -129,6 +139,10 @@ return AbstractModel.extend({
this.chart.mode = params.mode;
return Promise.resolve();
}
if ('stacked' in params) {
this.chart.stacked = params.stacked;
return Promise.resolve();
}
return this._loadGraph(this._getDomains());
},

@@ -137,24 +151,35 @@ return AbstractModel.extend({
//--------------------------------------------------------------------------

/**
* @returns {Object[]}
* @private
* @returns {number}
*/
_getComparisonFieldIndex: function () {
var groupBys = this.chart.groupBy.map(function (gb) {
return gb.split(":")[0];
});
return groupBys.indexOf(this.chart.comparisonField);
},
/**
* @private
* @returns {Array[]}
*/
_getDomains: function () {
var domains = [this.chart.domain.concat(this.chart.timeRange)];
this.chart.origins = [this.chart.timeRangeDescription];
this.chart.origins = [this.chart.timeRangeDescription || ""];
if (this.chart.compare) {
domains.push(this.chart.domain.concat(this.chart.comparisonTimeRange));
this.chart.origins.push(this.chart.comparisonTimeRangeDescription);
}
return domains;
},

/**
* Fetch and process graph data. It is basically a(some) read_group(s)
* with correct fields for each domain. We have to do some light processing
* to separate date groups in the field list, because they can be defined
* with an aggregation function, such as my_date:week.
*
* @private
* @param {Array[]} domains
* @returns {Promise}
*/
@@ -200,6 +225,7 @@ return AbstractModel.extend({
* the object this.chart in argument, or an array or something. We want to
* avoid writing on a this.chart object modified by a subsequent read_group
*
* @private
* @param {number} originIndex
* @param {any} rawData result from the read_group
*/
@@ -239,6 +265,7 @@ return AbstractModel.extend({
* Helper function (for _processData), turns various values in a usable
* string form, that we can display in the interface.
*
* @private
* @param {any} value value for the field fieldName received by the read_group rpc
* @param {string} fieldName
* @returns {string}
Oops, something went wrong.

0 comments on commit 8140dd2

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