[IMP] separates the pivot table drawing from the pivot table 'computing' (width, cells, colors,...). This will be useful for the export functionality : the table can be easily serialized in json (addon web_graph)

bzr revid: ged@openerp.com-20140204113135-1igakgqdxewey3aj
This commit is contained in:
Gery Debongnie 2014-02-04 12:31:35 +01:00
parent 6ddbb1f8c6
commit cfef034321
1 changed files with 157 additions and 230 deletions

View File

@ -89,8 +89,8 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
groupbys = _.flatten(_.map(filters, function (filter) {
var groupby = py.eval(filter.attrs.context).group_by;
if (!(groupby instanceof Array)) { groupby = [groupby]; }
return _.map(groupby, function(g) {
return {field: g, filter: filter};
return _.map(groupby, function(g) {
return {field: g, filter: filter};
});
}));
@ -362,6 +362,104 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
this.graph_view.register_groupby(this.pivot.rows.groupby, this.pivot.cols.groupby);
},
// ----------------------------------------------------------------------
// Convert Pivot data structure into table structure :
// compute rows, cols, colors, cell width, cell height, ...
// ----------------------------------------------------------------------
build_table: function() {
return {
headers: this.build_headers(),
measure_row: this.build_measure_row(),
rows: this.build_rows(),
};
},
build_headers: function () {
var pivot = this.pivot,
nbr_measures = pivot.measures.length,
height = _.max(_.map(pivot.cols.headers, function(g) {return g.path.length;})),
rows = [];
_.each(pivot.cols.headers, function (col) {
if (col.path.length === 0) { return;}
var cell_width = nbr_measures * (col.expanded ? pivot.get_ancestor_leaves(col).length : 1),
cell_height = col.expanded ? 1 : height - col.path.length + 1,
cell = {width: cell_width, height: cell_height, title: col.title, id: col.id, expanded: col.expanded};
if (rows[col.path.length - 1]) {
rows[col.path.length - 1].push(cell);
} else {
rows[col.path.length - 1] = [cell];
}
});
if (pivot.get_cols_leaves().length > 1) {
rows[0].push({width:nbr_measures, height:height, title: _t('Total'), id: pivot.cols.headers[0].id });
}
if (pivot.cols.headers.length === 1) {
rows = [[{width:nbr_measures, height:1, title: _t('Total'), id: pivot.cols.headers[0].id, expanded: false}]];
}
return rows;
},
build_measure_row: function () {
var nbr_leaves = this.pivot.get_cols_leaves().length,
nbr_cols = nbr_leaves + ((nbr_leaves > 1) ? 1 : 0),
result = [],
add_total = this.pivot.get_cols_leaves().length > 1,
i, m;
for (i = 0; i < nbr_cols; i++) {
for (m = 0; m < this.pivot.measures.length; m++) {
result.push({
text:this.pivot.measures[m].string,
is_bold: add_total && (i === nbr_cols - 1)
});
}
}
return result;
},
add_color: function (cell, row, col, index) {
if (this.heatmap_mode === 'none') { return cell; }
var total = (this.heatmap_mode === 'both') ? this.pivot.get_total()[index]
: (this.heatmap_mode === 'row') ? this.pivot.get_total(row)[index]
: this.pivot.get_total(col)[index];
var color = Math.floor(90 + 165*(total - Math.abs(cell.value))/total);
if (color < 255) {
cell.color = color;
}
return cell;
},
build_rows: function () {
var self = this,
pivot = this.pivot,
m,
add_total = pivot.get_cols_leaves().length > 1;
return _.map(pivot.rows.headers, function (row) {
var cells = [];
_.each(pivot.get_cols_leaves(), function (col) {
var values = pivot.get_values(row.id, col.id);
for (m = 0; m < pivot.measures.length; m++) {
cells.push(self.add_color({value:values[m], type:pivot.measures[m].type}, row,col,m));
}
});
if (add_total) {
var totals = pivot.get_total(row);
for (m = 0; m < pivot.measures.length; m++) {
cells.push(self.add_color({value:totals[m], type:pivot.measures[m].type, is_bold:true},row,pivot.main_col(),m));
}
}
return {
id: row.id,
indent: row.path.length,
title: row.title,
expanded: row.expanded,
cells: cells,
};
});
},
// ----------------------------------------------------------------------
// Main display method
// ----------------------------------------------------------------------
@ -391,159 +489,76 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
// Drawing the table
// ----------------------------------------------------------------------
draw_table: function () {
this.draw_top_headers();
_.each(this.pivot.rows.headers, this.proxy('draw_row'));
var table = this.build_table();
this.draw_headers(table.headers);
this.draw_measure_row(table.measure_row);
this.draw_rows(table.rows);
},
make_border_cell: function (colspan, rowspan, headercell) {
var tag = (headercell) ? $('<th>') : $('<td>');
return tag.addClass('graph_border')
.attr('colspan', colspan || 1)
.attr('rowspan', rowspan || 1);
},
make_header_title: function (header) {
return $('<span> ')
.addClass('web_graph_click')
.attr('href', '#')
.addClass((header.expanded) ? 'fa fa-minus-square' : 'fa fa-plus-square')
.text(' ' + (header.title || 'Undefined'));
},
draw_top_headers: function () {
var self = this,
thead = $('<thead>'),
pivot = this.pivot,
height = _.max(_.map(pivot.cols.headers, function(g) {return g.path.length;})),
header_cells = [[this.make_border_cell(1, height, true)]];
function set_dim (cols) {
_.each(cols.children, set_dim);
if (cols.children.length === 0) {
cols.height = height - cols.path.length + 1;
cols.width = 1;
} else {
cols.height = 1;
cols.width = _.reduce(cols.children, function (sum,c) { return sum + c.width;}, 0);
}
}
function make_col_header (col) {
var cell = self.make_border_cell(col.width*pivot.measures.length, col.height, true);
return cell.append(self.make_header_title(col).attr('data-id', col.id));
}
function make_cells (queue, level) {
var col = queue[0];
queue = _.rest(queue).concat(col.children);
if (col.path.length == level) {
_.last(header_cells).push(make_col_header(col));
} else {
level +=1;
header_cells.push([make_col_header(col)]);
}
if (queue.length !== 0) {
make_cells(queue, level);
}
}
set_dim(pivot.main_col()); // add width and height info to columns headers
if (pivot.main_col().children.length === 0) {
make_cells(pivot.cols.headers, 0);
make_header_cell: function (header) {
var cell = (_.has(header, 'cells') ? $('<td>') : $('<th>'))
.addClass('graph_border')
.attr('rowspan', header.height)
.attr('colspan', header.width);
var content = $('<span>').addClass('web_graph_click')
.attr('href','#')
.text(' ' + (header.title || _t('Undefined')))
.attr('data-id', header.id);
if (_.has(header, 'expanded')) {
content.addClass(header.expanded ? 'fa fa-minus-square' : 'fa fa-plus-square');
} else {
make_cells(pivot.main_col().children, 1);
if (pivot.get_cols_leaves().length > 1) {
header_cells[0].push(self.make_border_cell(pivot.measures.length, height, true).text(_t('Total')).css('font-weight', 'bold'));
}
content.css('font-weight', 'bold');
}
_.each(header_cells, function (cells) {
thead.append($('<tr>').append(cells));
});
if (pivot.measures.length >= 2) {
thead.append(self.make_measure_row());
if (_.has(header, 'indent')) {
for (var i = 0; i < header.indent; i++) { cell.prepend($('<span>', {class:'web_graph_indent'})); }
}
self.table.append(thead);
return cell.append(content);
},
make_measure_cells: function () {
return _.map(this.pivot.measures, function (measure) {
return $('<th>').addClass('measure_row').text(measure.string);
draw_headers: function (headers) {
var make_cell = this.make_header_cell,
empty_cell = $('<th>').attr('rowspan', headers.length),
thead = $('<thead>');
_.each(headers, function (row) {
var html_row = $('<tr>');
_.each(row, function (header) {
html_row.append(make_cell(header));
});
thead.append(html_row);
});
thead.children(':first').prepend(empty_cell);
this.table.append(thead);
},
make_measure_row: function() {
var self = this,
cols = this.pivot.cols.headers,
measure_row = $('<tr>');
measure_row.append($('<th>'));
_.each(cols, function (col) {
if (!col.children.length) {
measure_row.append(self.make_measure_cells());
}
draw_measure_row: function (measure_row) {
if (this.pivot.measures.length === 1) { return; }
var html_row = $('<tr>').append('<th>');
_.each(measure_row, function (cell) {
var measure_cell = $('<th>').addClass('measure_row').text(cell.text);
if (cell.is_bold) {measure_cell.css('font-weight', 'bold');}
html_row.append(measure_cell);
});
if (this.pivot.get_cols_leaves().length > 1) {
measure_row.append(self.make_measure_cells());
}
return measure_row;
this.$('thead').append(html_row);
},
draw_rows: function (rows) {
var table = this.table,
make_cell = this.make_header_cell;
draw_row: function (row) {
var self = this,
pivot = this.pivot,
measure_types = _.pluck(this.pivot.measures, 'type'),
html_row = $('<tr>'),
row_header = this.make_border_cell(1,1)
.append(this.make_header_title(row).attr('data-id', row.id))
.addClass('graph_border');
for (var i = 0; i < row.path.length; i++) {
row_header.prepend($('<span>', {class:'web_graph_indent'}));
}
html_row.append(row_header);
_.each(pivot.cols.headers, function (col) {
if (!col.children.length) {
var values = pivot.get_values(row.id, col.id);
for (var i = 0; i < values.length; i++) {
html_row.append(make_cell(values[i], measure_types[i], i, col));
_.each(rows, function (row) {
var html_row = $('<tr>').append(make_cell(row));
_.each(row.cells, function (cell) {
var value = openerp.web.format_value(cell.value, {type: cell.type}),
html_cell = $('<td>').text(value);
if (_.has(cell, 'color')) {
html_cell.css('background-color', $.Color(255, cell.color, cell.color));
}
}
if (cell.is_bold) { html_cell.css('font-weight', 'bold'); }
html_row.append(html_cell);
});
table.append(html_row);
});
if (pivot.get_cols_leaves().length > 1) {
var total_vals = pivot.get_total(row);
for (var j = 0; j < total_vals.length; j++) {
var cell = make_cell(total_vals[j], measure_types[j], j, pivot.cols[0]).css('font-weight', 'bold');
html_row.append(cell);
}
}
this.table.append(html_row);
function make_cell (value, measure_type, index, col) {
var cell = $('<td>');
if (value === undefined) {
return cell;
}
cell.text(openerp.web.format_value(value, {type: measure_type}));
var total = (self.heatmap_mode === 'both') ? pivot.get_total()[index]
: (self.heatmap_mode === 'row') ? pivot.get_total(row)[index]
: (self.heatmap_mode === 'col') ? pivot.get_total(col)[index]
: undefined;
if (self.heatmap_mode !== 'none') {
var color = Math.floor(90 + 165*(total - Math.abs(value))/total);
cell.css('background-color', $.Color(255, color, color));
}
return cell;
}
},
// ----------------------------------------------------------------------
@ -700,96 +715,11 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
});
},
// ----------------------------------------------------------------------
// Convert Pivot data structure into table structure
// table = {headers: [header_rows],
// measure_row: [_],
// rows: [row]}
// header_rows = [header_cells],
// header_cells = {width:_, height:_, title:_, id:_}
// rows = {id:_, indent:_, title:_, cells: [cell]}
// cell = {row_id:_, col_id:_, value:_}
// ----------------------------------------------------------------------
build_table: function() {
return {
headers: this.build_headers(),
measure_row: this.build_measure_row(),
rows: this.build_rows()
};
},
build_headers: function () {
var pivot = this.pivot,
height = _.max(_.map(pivot.cols.headers, function(g) {return g.path.length;})),
rows = [];
_.each(pivot.cols.headers, function (col) {
if (col.path.length === 0) { return;}
var cell_width = col.expanded ? pivot.get_ancestor_leaves(col).length : 1,
cell_height = col.expanded ? 1 : height - col.path.length + 1,
cell = {width: cell_width, height: cell_height, title: col.title, id: col.id};
if (rows[col.path.length - 1]) {
rows[col.path.length - 1].push(cell);
} else {
rows[col.path.length - 1] = [cell];
}
});
if (pivot.get_cols_leaves().length > 1) {
rows[0].push({width:1, height:1, title: _t('Total'), id: pivot.cols.headers[0].id });
}
if (pivot.cols.headers.length === 1) {
rows = [{width:1, height:1, title: _t('Total'), id: pivot.cols.headers[0].id }];
}
return rows;
},
build_measure_row: function () {
var nbr_leaves = this.pivot.get_cols_leaves().length,
nbr_cols = nbr_leaves + ((nbr_leaves > 1) ? 1 : 0),
result = [],
i, m;
for (i = 0; i < nbr_cols; i++) {
for (m = 0; m < this.pivot.measures.length; m++) {
result.push(this.pivot.measures[m].string);
}
}
return result;
},
build_rows: function () {
var pivot = this.pivot;
var m;
var add_total = pivot.get_cols_leaves().length > 1;
return _.map(pivot.rows.headers, function (row) {
var cells = [];
_.each(pivot.get_cols_leaves(), function (col) {
var values = pivot.get_values(row.id, col.id);
for (m = 0; m < pivot.measures.length; m++) {
cells.push({value:values[m], type:pivot.measures[m].type})
}
});
if (add_total) {
var totals = pivot.get_total(row);
for (m = 0; m < pivot.measures.length; m++) {
cells.push({value:totals[m], type:pivot.measures[m].type})
}
}
return {
id: row.id,
indent: row.path.length,
title: row.title,
cells: cells
};
});
},
// ----------------------------------------------------------------------
// Controller stuff...
// ----------------------------------------------------------------------
export_xls: function() {
openerp.web.blockUI();
openerp.web.blockUI();
this.session.get_file({
url: '/web_graph/export_xls',
data: {data: JSON.stringify(this.build_table())},
@ -798,9 +728,6 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
});
},
});
// Utility function: returns true if the beginning of array2 is array1 and