Merge lp:~openerp-dev/openerp-web/trunk-readgroup-ged into lp:openerp-web
- trunk-readgroup-ged
- Merge into trunk
Proposed by
Géry Debongnie
Status: | Merged |
---|---|
Merged at revision: | 3967 |
Proposed branch: | lp:~openerp-dev/openerp-web/trunk-readgroup-ged |
Merge into: | lp:openerp-web |
Diff against target: |
597 lines (+226/-239) 2 files modified
addons/web/static/src/js/data.js (+31/-8) addons/web_graph/static/src/js/pivot_table.js (+195/-231) |
To merge this branch: | bzr merge lp:~openerp-dev/openerp-web/trunk-readgroup-ged |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Xavier (Open ERP) | Pending | ||
Review via email: mp+214498@code.launchpad.net |
Commit message
Description of the change
Graph view performance optimizations:
this work completely changes the way data is loaded into graph views. It makes use of the new 'eager' functionality of read_group in such a way that it now requires a fixed number of read_group requests (or, more precisely, a number of requests only depending on the dimensions of the pivot table and not depending on the number of groups in the db).
To post a comment you must log in.
- 3964. By Géry Debongnie
-
[MERGE] merge from trunk
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'addons/web/static/src/js/data.js' |
2 | --- addons/web/static/src/js/data.js 2014-04-02 08:53:59 +0000 |
3 | +++ addons/web/static/src/js/data.js 2014-04-08 09:09:18 +0000 |
4 | @@ -27,6 +27,7 @@ |
5 | this._fields = fields; |
6 | this._filter = []; |
7 | this._context = {}; |
8 | + this._lazy = true; |
9 | this._limit = false; |
10 | this._offset = 0; |
11 | this._order_by = []; |
12 | @@ -36,6 +37,7 @@ |
13 | var q = new instance.web.Query(this._model, this._fields); |
14 | q._context = this._context; |
15 | q._filter = this._filter; |
16 | + q._lazy = this._lazy; |
17 | q._limit = this._limit; |
18 | q._offset = this._offset; |
19 | q._order_by = this._order_by; |
20 | @@ -51,6 +53,7 @@ |
21 | q._context = new instance.web.CompoundContext( |
22 | q._context, to_set.context); |
23 | break; |
24 | + case 'lazy': |
25 | case 'limit': |
26 | case 'offset': |
27 | case 'order_by': |
28 | @@ -140,6 +143,7 @@ |
29 | domain: this._model.domain(this._filter), |
30 | context: ctx, |
31 | offset: this._offset, |
32 | + lazy: this._lazy, |
33 | limit: this._limit, |
34 | orderby: instance.web.serialize_sort(this._order_by) || false |
35 | }).then(function (results) { |
36 | @@ -148,8 +152,9 @@ |
37 | result.__context = result.__context || {}; |
38 | result.__context.group_by = result.__context.group_by || []; |
39 | _.defaults(result.__context, ctx); |
40 | + var grouping_fields = self._lazy ? [grouping[0]] : grouping; |
41 | return new instance.web.QueryGroup( |
42 | - self._model.name, grouping[0], result); |
43 | + self._model.name, grouping_fields, result); |
44 | }); |
45 | }); |
46 | }, |
47 | @@ -176,6 +181,18 @@ |
48 | return this.clone({filter: domain}); |
49 | }, |
50 | /** |
51 | + * Creates a new query with the provided parameter lazy replacing the current |
52 | + * query's own. |
53 | + * |
54 | + * @param {Boolean} lazy indicates if the read_group should return only the |
55 | + * first level of groupby records, or should return the records grouped by |
56 | + * all levels at once (so, it makes only 1 db request). |
57 | + * @returns {openerp.web.Query} |
58 | + */ |
59 | + lazy: function (lazy) { |
60 | + return this.clone({lazy: lazy}); |
61 | + }, |
62 | + /** |
63 | * Creates a new query with the provided limit replacing the current |
64 | * query's own limit |
65 | * |
66 | @@ -213,7 +230,7 @@ |
67 | }); |
68 | |
69 | instance.web.QueryGroup = instance.web.Class.extend({ |
70 | - init: function (model, grouping_field, read_group_group) { |
71 | + init: function (model, grouping_fields, read_group_group) { |
72 | // In cases where group_by_no_leaf and no group_by, the result of |
73 | // read_group has aggregate fields but no __context or __domain. |
74 | // Create default (empty) values for those so that things don't break |
75 | @@ -221,12 +238,12 @@ |
76 | {__context: {group_by: []}, __domain: []}, |
77 | read_group_group); |
78 | |
79 | - var raw_field = grouping_field && grouping_field.split(':')[0]; |
80 | + var count_key = (grouping_fields[0] && grouping_fields[0].split(':')[0]) + '_count'; |
81 | var aggregates = {}; |
82 | _(fixed_group).each(function (value, key) { |
83 | if (key.indexOf('__') === 0 |
84 | - || key === raw_field |
85 | - || key === raw_field + '_count') { |
86 | + || _.contains(grouping_fields, key) |
87 | + || (key === count_key)) { |
88 | return; |
89 | } |
90 | aggregates[key] = value || 0; |
91 | @@ -235,15 +252,21 @@ |
92 | this.model = new instance.web.Model( |
93 | model, fixed_group.__context, fixed_group.__domain); |
94 | |
95 | - var group_size = fixed_group[raw_field + '_count'] || fixed_group.__count || 0; |
96 | + var group_size = fixed_group[count_key] || fixed_group.__count || 0; |
97 | var leaf_group = fixed_group.__context.group_by.length === 0; |
98 | |
99 | + var value = (grouping_fields.length === 1) |
100 | + ? fixed_group[grouping_fields[0]] |
101 | + : _.map(grouping_fields, function (field) { return fixed_group[field]; }); |
102 | + var grouped_on = (grouping_fields.length === 1) |
103 | + ? grouping_fields[0] |
104 | + : grouping_fields; |
105 | this.attributes = { |
106 | folded: !!(fixed_group.__fold), |
107 | - grouped_on: grouping_field, |
108 | + grouped_on: grouped_on, |
109 | // if terminal group (or no group) and group_by_no_leaf => use group.__count |
110 | length: group_size, |
111 | - value: fixed_group[raw_field], |
112 | + value: value, |
113 | // A group is open-able if it's not a leaf in group_by_no_leaf mode |
114 | has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']), |
115 | |
116 | |
117 | === modified file 'addons/web_graph/static/src/js/pivot_table.js' |
118 | --- addons/web_graph/static/src/js/pivot_table.js 2014-02-03 11:33:32 +0000 |
119 | +++ addons/web_graph/static/src/js/pivot_table.js 2014-04-08 09:09:18 +0000 |
120 | @@ -82,7 +82,9 @@ |
121 | |
122 | get_values: function (id1, id2, default_values) { |
123 | var cell = _.findWhere(this.cells, {x: Math.min(id1, id2), y: Math.max(id1, id2)}); |
124 | - return (cell !== undefined) ? cell.values : (default_values || new Array(this.measures.length)); |
125 | + return (cell !== undefined) ? |
126 | + cell.values : |
127 | + (default_values || new Array(this.measures.length)); |
128 | }, |
129 | |
130 | // ---------------------------------------------------------------------- |
131 | @@ -144,12 +146,16 @@ |
132 | get_ancestors: function (header) { |
133 | var self = this; |
134 | if (!header.children) return []; |
135 | - return [].concat.apply([], _.map(header.children, function (c) {return self.get_ancestors_and_self(c); })); |
136 | + return [].concat.apply([], _.map(header.children, function (c) { |
137 | + return self.get_ancestors_and_self(c); |
138 | + })); |
139 | }, |
140 | |
141 | get_ancestors_and_self: function (header) { |
142 | var self = this; |
143 | - return [].concat.apply([header], _.map(header.children, function (c) { return self.get_ancestors_and_self(c); })); |
144 | + return [].concat.apply([header], _.map(header.children, function (c) { |
145 | + return self.get_ancestors_and_self(c); |
146 | + })); |
147 | }, |
148 | |
149 | get_total: function (header) { |
150 | @@ -205,54 +211,27 @@ |
151 | expand: function (header_id, groupby) { |
152 | var self = this, |
153 | header = this.get_header(header_id), |
154 | - otherRoot = this.get_other_root(header), |
155 | - fields = otherRoot.groupby.concat(this.measures); |
156 | + other_root = this.get_other_root(header), |
157 | + this_gb = [groupby.field], |
158 | + other_gbs = _.pluck(other_root.groupby, 'field'); |
159 | |
160 | if (header.path.length === header.root.groupby.length) { |
161 | header.root.groupby.push(groupby); |
162 | } |
163 | - groupby = [groupby].concat(otherRoot.groupby); |
164 | - |
165 | - return this.get_groups(groupby, fields, header.domain).then(function (groups) { |
166 | - _.each(groups.reverse(), function (group) { |
167 | - // make header |
168 | - var child = self.make_header(group, header); |
169 | + return this.perform_requests(this_gb, other_gbs, header.domain).then(function () { |
170 | + var data = Array.prototype.slice.call(arguments).slice(other_gbs.length + 1); |
171 | + _.each(data, function (data_pt) { |
172 | + self.make_headers_and_cell( |
173 | + data_pt, header.root.headers, other_root.headers, 1, header.path, true); |
174 | + }); |
175 | + header.expanded = true; |
176 | + header.children.forEach(function (child) { |
177 | child.expanded = false; |
178 | - header.children.splice(0,0, child); |
179 | - header.root.headers.splice(header.root.headers.indexOf(header) + 1, 0, child); |
180 | - // make cells |
181 | - _.each(self.get_ancestors_and_self(group), function (data) { |
182 | - var values = _.map(self.measures, function (m) { |
183 | - return data.attributes.aggregates[m.field]; |
184 | - }); |
185 | - var other = _.find(otherRoot.headers, function (h) { |
186 | - if (header.root === self.cols) { |
187 | - return _.isEqual(data.path.slice(1), h.path); |
188 | - } else { |
189 | - return _.isEqual(_.rest(data.path), h.path); |
190 | - } |
191 | - }); |
192 | - if (other) { |
193 | - self.add_cell(child.id, other.id, values); |
194 | - } |
195 | - }); |
196 | + child.root = header.root; |
197 | }); |
198 | - header.expanded = true; |
199 | }); |
200 | }, |
201 | |
202 | - make_header: function (group, parent) { |
203 | - var title = parent ? group.attributes.value : _t('Total'); |
204 | - return { |
205 | - id: _.uniqueId(), |
206 | - path: parent ? parent.path.concat(title) : [], |
207 | - title: title, |
208 | - children: [], |
209 | - domain: parent ? group.model._domain : this.domain, |
210 | - root: parent ? parent.root : undefined, |
211 | - }; |
212 | - }, |
213 | - |
214 | swap_axis: function () { |
215 | var temp = this.rows; |
216 | this.rows = this.cols; |
217 | @@ -262,206 +241,191 @@ |
218 | // ---------------------------------------------------------------------- |
219 | // Data updating methods |
220 | // ---------------------------------------------------------------------- |
221 | - // Load the data from the db, using the method this.load_data |
222 | // update_data will try to preserve the expand/not expanded status of each |
223 | // column/row. If you want to expand all, then set this.cols.headers/this.rows.headers |
224 | // to null before calling update_data. |
225 | - update_data: function () { |
226 | - var self = this; |
227 | - |
228 | - return this.load_data().then (function (result) { |
229 | - if (result) { |
230 | - self.no_data = false; |
231 | - self[self.cols.headers ? 'update_headers' : 'expand_headers'](self.cols, result.col_headers); |
232 | - self[self.rows.headers ? 'update_headers' : 'expand_headers'](self.rows, result.row_headers); |
233 | - } else { |
234 | - self.no_data = true; |
235 | - } |
236 | - }); |
237 | - }, |
238 | - |
239 | - expand_headers: function (root, new_headers) { |
240 | - root.headers = new_headers; |
241 | - _.each(root.headers, function (header) { |
242 | - header.root = root; |
243 | - header.expanded = (header.children.length > 0); |
244 | - }); |
245 | - }, |
246 | - |
247 | - update_headers: function (root, new_headers) { |
248 | - _.each(root.headers, function (header) { |
249 | - var corresponding_header = _.find(new_headers, function (h) { |
250 | - return _.isEqual(h.path, header.path); |
251 | - }); |
252 | - if (corresponding_header && header.expanded) { |
253 | - corresponding_header.expanded = true; |
254 | - _.each(corresponding_header.children, function (c) { |
255 | - c.expanded = false; |
256 | - }); |
257 | - } |
258 | - if (corresponding_header && (!header.expanded)) { |
259 | - corresponding_header.expanded = false; |
260 | - } |
261 | - }); |
262 | - var updated_headers = _.filter(new_headers, function (header) { |
263 | - return (header.expanded !== undefined); |
264 | - }); |
265 | - _.each(updated_headers, function (header) { |
266 | - if (!header.expanded) { |
267 | - header.children = []; |
268 | - } |
269 | - header.root = root; |
270 | - }); |
271 | - root.headers = updated_headers; |
272 | - }, |
273 | - |
274 | - // ---------------------------------------------------------------------- |
275 | - // Data loading methods |
276 | - // ---------------------------------------------------------------------- |
277 | - |
278 | - // To obtain all the values required to draw the full table, we have to do |
279 | - // at least 2 + min(row.groupby.length, col.groupby.length) |
280 | - // calls to readgroup. To simplify the code, we will always do |
281 | - // 2 + row.groupby.length calls. For example, if row.groupby = [r1, r2, r3] |
282 | - // and col.groupby = [c1, c2], then we will make the call with the following |
283 | - // groupbys: [r1,r2,r3], [c1,r1,r2,r3], [c1,c2,r1,r2,r3], []. |
284 | - load_data: function () { |
285 | + update_data: function () { |
286 | + var self = this; |
287 | + return this.perform_requests().then (function () { |
288 | + var data = Array.prototype.slice.call(arguments); |
289 | + self.no_data = !data[0][0].attributes.length; |
290 | + if (self.no_data) { |
291 | + return; |
292 | + } |
293 | + var row_headers = [], |
294 | + col_headers = []; |
295 | + self.cells = []; |
296 | + |
297 | + var dim_col = self.cols.groupby.length, |
298 | + i, j, index; |
299 | + |
300 | + for (i = 0; i < self.rows.groupby.length + 1; i++) { |
301 | + for (j = 0; j < dim_col + 1; j++) { |
302 | + index = i*(dim_col + 1) + j; |
303 | + self.make_headers_and_cell(data[index], row_headers, col_headers, i); |
304 | + } |
305 | + } |
306 | + self.set_headers(row_headers, self.rows); |
307 | + self.set_headers(col_headers, self.cols); |
308 | + }); |
309 | + }, |
310 | + |
311 | + make_headers_and_cell: function (data_pts, row_headers, col_headers, index, prefix, expand) { |
312 | + var self = this; |
313 | + data_pts.forEach(function (data_pt) { |
314 | + var row_value = (prefix || []).concat(data_pt.attributes.value.slice(0,index)); |
315 | + var col_value = data_pt.attributes.value.slice(index); |
316 | + |
317 | + if (expand && !_.find(col_headers, function (hdr) {return _.isEqual(col_value, hdr.path);})) { |
318 | + return; |
319 | + } |
320 | + var row = self.find_or_create_header(row_headers, row_value, data_pt); |
321 | + var col = self.find_or_create_header(col_headers, col_value, data_pt); |
322 | + |
323 | + var cell_value = _.map(self.measures, function (m) { |
324 | + return data_pt.attributes.aggregates[m.field]; |
325 | + }); |
326 | + self.cells.push({ |
327 | + x: Math.min(row.id, col.id), |
328 | + y: Math.max(row.id, col.id), |
329 | + values: cell_value |
330 | + }); |
331 | + }); |
332 | + }, |
333 | + |
334 | + make_header: function (values) { |
335 | + return _.extend({ |
336 | + children: [], |
337 | + domain: this.domain, |
338 | + expanded: undefined, |
339 | + id: _.uniqueId(), |
340 | + path: [], |
341 | + root: undefined, |
342 | + title: undefined |
343 | + }, values || {}); |
344 | + }, |
345 | + |
346 | + find_or_create_header: function (headers, path, data_pt) { |
347 | + var hdr = _.find(headers, function (header) { |
348 | + return _.isEqual(path, header.path); |
349 | + }); |
350 | + if (hdr) { |
351 | + return hdr; |
352 | + } |
353 | + if (!path.length) { |
354 | + hdr = this.make_header({title: _t('Total')}); |
355 | + headers.push(hdr); |
356 | + return hdr; |
357 | + } |
358 | + hdr = this.make_header({ |
359 | + path:path, |
360 | + domain:data_pt.model._domain, |
361 | + title: _t(_.last(path)) |
362 | + }); |
363 | + var parent = _.find(headers, function (header) { |
364 | + return _.isEqual(header.path, _.initial(path, 1)); |
365 | + }); |
366 | + |
367 | + var previous = parent.children.length ? _.last(parent.children) : parent; |
368 | + headers.splice(headers.indexOf(previous) + 1, 0, hdr); |
369 | + parent.children.push(hdr); |
370 | + return hdr; |
371 | + }, |
372 | + |
373 | + perform_requests: function (group1, group2, domain) { |
374 | var self = this, |
375 | - cols = this.cols.groupby, |
376 | - rows = this.rows.groupby, |
377 | - visible_fields = rows.concat(cols, self.measures); |
378 | - |
379 | - if (this.measures.length === 0) { |
380 | - return $.Deferred.resolve().promise(); |
381 | - } |
382 | - |
383 | - var groupbys = _.map(_.range(cols.length + 1), function (i) { |
384 | - return cols.slice(0, i).concat(rows); |
385 | - }); |
386 | - groupbys.push([]); |
387 | - |
388 | - var get_data_requests = _.map(groupbys, function (groupby) { |
389 | - return self.get_groups(groupby, visible_fields, self.domain); |
390 | - }); |
391 | - |
392 | - return $.when.apply(null, get_data_requests).then(function () { |
393 | - var data = Array.prototype.slice.call(arguments), |
394 | - row_data = data[0], |
395 | - col_data = (cols.length !== 0) ? data[data.length - 2] : [], |
396 | - has_data = data[data.length - 1][0]; |
397 | - |
398 | - return has_data && self.format_data(col_data, row_data, data); |
399 | - }); |
400 | - }, |
401 | - |
402 | - get_groups: function (groupbys, fields, domain, path) { |
403 | - var self = this, |
404 | - groupby = (groupbys.length) ? groupbys[0] : []; |
405 | - path = path || []; |
406 | - |
407 | - return this._query_db(groupby, fields, domain, path).then(function (groups) { |
408 | - if (groupbys.length > 1) { |
409 | - var get_subgroups = $.when.apply(null, _.map(groups, function (group) { |
410 | - return self.get_groups(_.rest(groupbys), fields, group.model._domain, path.concat(group.attributes.value)).then(function (subgroups) { |
411 | - group.children = subgroups; |
412 | - }); |
413 | - })); |
414 | - return get_subgroups.then(function () { |
415 | - return groups; |
416 | - }); |
417 | - } else { |
418 | - return groups; |
419 | + requests = [], |
420 | + row_gbs = _.pluck(this.rows.groupby, 'field'), |
421 | + col_gbs = _.pluck(this.cols.groupby, 'field'), |
422 | + field_list = row_gbs.concat(col_gbs, _.pluck(this.measures, 'field')), |
423 | + fields = field_list.map(function (f) { return self.raw_field(f); }); |
424 | + |
425 | + group1 = group1 || row_gbs; |
426 | + group2 = group2 || col_gbs; |
427 | + |
428 | + var i,j, groupbys; |
429 | + for (i = 0; i < group1.length + 1; i++) { |
430 | + for (j = 0; j < group2.length + 1; j++) { |
431 | + groupbys = group1.slice(0,i).concat(group2.slice(0,j)); |
432 | + requests.push(self.get_groups(groupbys, fields, domain || self.domain)); |
433 | } |
434 | - }); |
435 | - |
436 | - }, |
437 | - |
438 | - _query_db: function (groupby, fields, domain, path) { |
439 | - var self = this, |
440 | - field_ids = _.without(_.pluck(fields, 'field'), '__count'), |
441 | - fields = _.map(field_ids, function(f) { return self.raw_field(f); }); |
442 | - |
443 | - return this.model.query(field_ids) |
444 | - .filter(domain) |
445 | - .group_by(groupby.field) |
446 | - .then(function (results) { |
447 | - var groups = _.filter(results, function (group) { |
448 | - return group.attributes.length > 0; |
449 | - }); |
450 | - return _.map(groups, function (g) { return self.format_group(g, path); }); |
451 | - }); |
452 | - }, |
453 | + } |
454 | + return $.when.apply(null, requests); |
455 | + }, |
456 | + |
457 | + // set the 'expanded' status of new_headers more or less like root.headers, with root as root |
458 | + set_headers: function(new_headers, root) { |
459 | + if (root.headers) { |
460 | + _.each(root.headers, function (header) { |
461 | + var corresponding_header = _.find(new_headers, function (h) { |
462 | + return _.isEqual(h.path, header.path); |
463 | + }); |
464 | + if (corresponding_header && header.expanded) { |
465 | + corresponding_header.expanded = true; |
466 | + _.each(corresponding_header.children, function (c) { |
467 | + c.expanded = false; |
468 | + }); |
469 | + } |
470 | + if (corresponding_header && (!header.expanded)) { |
471 | + corresponding_header.expanded = false; |
472 | + corresponding_header.children = []; |
473 | + } |
474 | + }); |
475 | + var updated_headers = _.filter(new_headers, function (header) { |
476 | + return (header.expanded !== undefined); |
477 | + }); |
478 | + _.each(updated_headers, function (header) { |
479 | + header.root = root; |
480 | + }); |
481 | + root.headers = updated_headers; |
482 | + } else { |
483 | + root.headers = new_headers; |
484 | + _.each(root.headers, function (header) { |
485 | + header.root = root; |
486 | + header.expanded = (header.children.length > 0); |
487 | + }); |
488 | + } |
489 | + return new_headers; |
490 | + }, |
491 | + |
492 | + get_groups: function (groupbys, fields, domain) { |
493 | + var self = this; |
494 | + return this.model.query(_.without(fields, '__count')) |
495 | + .filter(domain) |
496 | + .lazy(false) |
497 | + .group_by(groupbys) |
498 | + .then(function (groups) { |
499 | + return groups.filter(function (group) { |
500 | + return group.attributes.length > 0; |
501 | + }).map(function (group) { |
502 | + var attrs = group.attributes, |
503 | + grouped_on = attrs.grouped_on instanceof Array ? attrs.grouped_on : [attrs.grouped_on], |
504 | + raw_grouped_on = grouped_on.map(function (f) { |
505 | + return self.raw_field(f); |
506 | + }); |
507 | + if (grouped_on.length === 1) { |
508 | + attrs.value = [attrs.value]; |
509 | + } |
510 | + attrs.value = _.range(grouped_on.length).map(function (i) { |
511 | + if (attrs.value[i] === false) { |
512 | + return _t('Undefined'); |
513 | + } else if (attrs.value[i] instanceof Array) { |
514 | + return attrs.value[i][1]; |
515 | + } |
516 | + return attrs.value[i]; |
517 | + }); |
518 | + attrs.aggregates.__count = group.attributes.length; |
519 | + attrs.grouped_on = raw_grouped_on; |
520 | + return group; |
521 | + }); |
522 | + }); |
523 | + }, |
524 | |
525 | // if field is a fieldname, returns field, if field is field_id:interval, retuns field_id |
526 | raw_field: function (field) { |
527 | return field.split(':')[0]; |
528 | }, |
529 | |
530 | - // add the path to the group and sanitize the value... |
531 | - format_group: function (group, current_path) { |
532 | - var attrs = group.attributes, |
533 | - value = attrs.value, |
534 | - grouped_on = attrs.grouped_on ? this.raw_field(attrs.grouped_on) : false; |
535 | - |
536 | - if (value === false) { |
537 | - group.attributes.value = _t('Undefined'); |
538 | - } else if (grouped_on && this.fields[grouped_on].type === 'selection') { |
539 | - var selection = this.fields[grouped_on].selection, |
540 | - value_lookup = _.where(selection, {0:value}); |
541 | - group.attributes.value = value_lookup ? value_lookup[0][1] : _t('Undefined'); |
542 | - } else if (value instanceof Array) { |
543 | - group.attributes.value = value[1]; |
544 | - } |
545 | - |
546 | - group.path = (value !== undefined) ? (current_path || []).concat(group.attributes.value) : []; |
547 | - group.attributes.aggregates.__count = group.attributes.length; |
548 | - |
549 | - return group; |
550 | - }, |
551 | - |
552 | - format_data: function (col_data, row_data, cell_data) { |
553 | - var self = this, |
554 | - dim_row = this.rows.groupby.length, |
555 | - dim_col = this.cols.groupby.length, |
556 | - col_headers = this.get_ancestors_and_self(this.make_headers(col_data, dim_col)), |
557 | - row_headers = this.get_ancestors_and_self(this.make_headers(row_data, dim_row)); |
558 | - |
559 | - this.cells = []; |
560 | - _.each(cell_data, function (data, index) { |
561 | - self.make_cells(data, index, [], row_headers, col_headers); |
562 | - }); // not pretty. make it more functional? |
563 | - |
564 | - return {col_headers: col_headers, row_headers: row_headers}; |
565 | - }, |
566 | - |
567 | - make_headers: function (data, depth, parent) { |
568 | - var self = this, |
569 | - main = this.make_header(data, parent); |
570 | - |
571 | - if (main.path.length < depth) { |
572 | - main.children = _.map(data.children || data, function (data_pt) { |
573 | - return self.make_headers (data_pt, depth, main); |
574 | - }); |
575 | - } |
576 | - return main; |
577 | - }, |
578 | - |
579 | - make_cells: function (data, index, current_path, rows, cols) { |
580 | - var self = this; |
581 | - _.each(data, function (group) { |
582 | - var attr = group.attributes, |
583 | - path = attr.grouped_on ? current_path.concat(attr.value) : current_path, |
584 | - values = _.map(self.measures, function (measure) { return attr.aggregates[measure.field]; }), |
585 | - row = _.find(rows, function (header) { return _.isEqual(header.path, path.slice(index)); }), |
586 | - col = _.find(cols, function (header) { return _.isEqual(header.path, path.slice(0, index)); }); |
587 | - |
588 | - self.add_cell(row.id, col.id, values); |
589 | - if (group.children) { |
590 | - self.make_cells (group.children, index, path, rows, cols); |
591 | - } |
592 | - }); |
593 | - }, |
594 | - |
595 | }); |
596 | |
597 | })(); |