From 0dcd78a54e59f89567cc079bd3399bbf73b5ea16 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 10 May 2011 10:34:20 +0200 Subject: [PATCH 1/5] [ADD] DataGroup API bzr revid: xmo@openerp.com-20110510083420-18zr6j8ab5d9c023 --- addons/base/controllers/main.py | 7 +++ addons/base/static/src/js/data.js | 79 +++++++++++++++++++++++++++++-- addons/base/static/src/js/list.js | 12 +++++ 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 085e48d89e0..e21f4fb853f 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -425,6 +425,13 @@ class DataSet(openerpweb.Controller): r = m.default_get(fields, context) return {'result': r} +class DataGroup(openerpweb.Controller): + _cp_path = "/base/group" + @openerpweb.jsonrequest + def read(self, request, model, group_by_fields, domain=None, context=None): + Model = request.session.model(model) + return Model.read_group(domain or False, False, group_by_fields, 0, False, context or False) + class View(openerpweb.Controller): def fields_view_get(self, request, model, view_id, view_type, transform=True, toolbar=False, submenu=False): diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index da0f18001a8..bb190e068c6 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -3,15 +3,88 @@ openerp.base.data = function(openerp) { openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{ /** - * Management interface between views and the collection of selected OpenERP - * records (represents the view's state?) + * Management interface between views and grouped collections of OpenERP + * records * * @constructs * @extends openerp.base.Controller + * + * @param {Array} group_by sequence of fields by which to group * @param {openerp.base.Session} session Current OpenERP session + * @param {openerp.base.DataSet} dataset root dataset, holder of base view state */ - init: function(session) { + init: function(session, group_by, dataset) { this._super(session, null); + this.model = dataset.model; + this.context = dataset.context; + this.domain = dataset.domain; + + this.group_by = group_by; + + this.groups = null; + }, + fetch: function () { + var d = new $.Deferred(); + var self = this; + + if (this.groups) { + d.resolveWith(this, [this.groups]); + } else { + this.rpc('/base/group/read', { + model: this.model, + context: this.context, + domain: this.domain, + group_by_fields: this.group_by + }, function () { }).then(function (response) { + self.groups = response.result; + d.resolveWith(self, [response.result]); + }, function () { + d.rejectWith.apply(d, self, [arguments]); + }); + } + return d.promise(); + }, + /** + * Retrieves the content of the nth-level item in the DataGroup, which + * results in either a DataSet or a DataGroup instance. + * + * Calling :js:func:`~openerp.base.DataGroup.get` without having called + * :js:func:`~openerp.base.DataGroup.list` beforehand will likely result + * in an error. + * + * @param {Number} index the index of the group to open in the datagroup's collection + * @param {Function} ifDataSet executed if the item results in a DataSet, provided with the dataset as parameter and as context + * @param {Function} ifDataGroup executed if the item results in a DataSet, provided with the datagroup as parameter and as context + */ + get: function (index, ifDataSet, ifDataGroup) { + var group = this.groups[index]; + if (!group) { + throw new Error("No group at index " + index); + } + + var child_context = _.extend({}, this.context, group.__context); + if (group.__context.group_by.length) { + var datagroup = new openerp.base.DataGroup( + this.session, group.__context.group_by, { + model: this.model, + context: child_context, + domain: group.__domain + }); + ifDataGroup.call(datagroup, datagroup); + } else { + var dataset = new openerp.base.DataSetSearch(this.session, this.model); + dataset.domain = group.__domain; + dataset.context = child_context; + ifDataSet.call(dataset, dataset); + } + }, + /** + * Provides a list of all top-level items of the data group. + * + * @returns {$.Deferred} + */ + list: function () { + return this.fetch(); } }); diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 62d29da0cba..f4a2e481418 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -290,6 +290,18 @@ openerp.base.ListView = openerp.base.Controller.extend( // TODO: handle non-empty results.group_by with read_group self.dataset.context = results.context; self.dataset.domain = results.domain; + if (results.group_by.length) { + var group = new openerp.base.DataGroup( + self.session, results.group_by, self.dataset); + group.list().then(function (lst) { + console.log(this, lst); + this.get(0, function () { + console.log('set', this); + }, function () { + console.log('group', this); + }); + }); + } return self.do_reload(); }); }, From 45f47ae65c0e22e7a780806f69eedcaa57c37259 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 10 May 2011 13:07:23 +0200 Subject: [PATCH 2/5] [ADD] display groups in a most dreadful manner, need to cleanup the generation function and to handle displaying it in the correct table bzr revid: xmo@openerp.com-20110510110723-ic13ty3rs92zg9z9 --- addons/base/static/src/js/data.js | 23 +++++++++-- addons/base/static/src/js/list.js | 68 ++++++++++++++++++++++++++----- 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index bb190e068c6..c3683ed592e 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -37,7 +37,19 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas group_by_fields: this.group_by }, function () { }).then(function (response) { self.groups = response.result; - d.resolveWith(self, [response.result]); + // read_group results are annoying: they use the name of the + // field grouped on to hold the value and the count, so no + // generic access to those values is possible. + // Alias them to `value` and `length`. + d.resolveWith(self, [_(response.result).map(function (group) { + var field_name = self.group_by[0]; + return _.extend({}, group, { + // provide field used for grouping + grouped_on: field_name, + length: group[field_name + '_count'], + value: group[field_name] + }); + })]); }, function () { d.rejectWith.apply(d, self, [arguments]); }); @@ -52,9 +64,14 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas * :js:func:`~openerp.base.DataGroup.list` beforehand will likely result * in an error. * + * The resulting :js:class:`~openerp.base.DataGroup` or + * :js:class:`~openerp.base.DataSet` will be provided through the relevant + * callback function. In both functions, the current DataGroup will be + * provided as context (``this``) + * * @param {Number} index the index of the group to open in the datagroup's collection - * @param {Function} ifDataSet executed if the item results in a DataSet, provided with the dataset as parameter and as context - * @param {Function} ifDataGroup executed if the item results in a DataSet, provided with the datagroup as parameter and as context + * @param {Function} ifDataSet executed if the item results in a DataSet, provided with the new dataset as parameter + * @param {Function} ifDataGroup executed if the item results in a DataSet, provided with the new datagroup as parameter */ get: function (index, ifDataSet, ifDataGroup) { var group = this.groups[index]; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index f4a2e481418..87a59f0a469 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -61,7 +61,11 @@ openerp.base.ListView = openerp.base.Controller.extend( columns: this.columns, rows: this.rows }); - $(this.list).bind({ + this.groups = new openerp.base.ListView.Groups({ + options: this.options, + columns: this.columns + }); + $([this.list, this.groups]).bind({ 'selected': function (e, selection) { self.$element.find('#oe-list-delete') .toggle(!!selection.length); @@ -291,16 +295,10 @@ openerp.base.ListView = openerp.base.Controller.extend( self.dataset.context = results.context; self.dataset.domain = results.domain; if (results.group_by.length) { - var group = new openerp.base.DataGroup( + self.groups.datagroup = new openerp.base.DataGroup( self.session, results.group_by, self.dataset); - group.list().then(function (lst) { - console.log(this, lst); - this.get(0, function () { - console.log('set', this); - }, function () { - console.log('group', this); - }); - }); + self.$element.html(self.groups.render()); + return; } return self.do_reload(); }); @@ -476,7 +474,55 @@ openerp.base.ListView.List = Class.extend( // drag and drop // editable? }); - +openerp.base.ListView.Groups = Class.extend( + /** @lends openerp.base.ListView.Groups# */{ + /** + * Grouped display for the ListView. Handles basic DOM events and interacts + * with the :js:class:`~openerp.base.DataGroup` bound to it. + * + * Provides events similar to those of + * :js:class:`~openerp.base.ListView.List` + */ + init: function (opts) { + this.options = opts.options; + this.columns = opts.columns; + this.datagroup = {}; + }, + make_level: function (datagroup) { + var self = this, $root = $('
'); + datagroup.list().then(function (list) { + _(list).each(function (group, index) { + var $title = $('
') + .text(group.grouped_on + ': ' + group.value + ' (' + group.length + ')') + .appendTo($root); + $title.click(function () { + datagroup.get(index, function (new_dataset) { + var $content = $('
    ').appendTo( + $('
    ').insertAfter($title)); + new_dataset.read_slice([], null, null, function (records) { + _(records).each(function (record) { + $('
  • ') + .appendTo($content) + .text(_(record).map(function (value, key) { + return key + ': ' + value; + }).join(', ')); + }); + }); + }, function (new_datagroup) { + console.log(new_datagroup); + $('
    ') + .insertAfter($title) + .append(self.make_level(new_datagroup)); + }); + }); + }); + }); + return $root; + }, + render: function () { + return this.make_level(this.datagroup); + } +}); openerp.base.TreeView = openerp.base.Controller.extend({ }); From 1e6e428d7e89c544bee95b98255c9d1ee201046e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 10 May 2011 13:12:04 +0200 Subject: [PATCH 3/5] [IMP] don't use a parameter object for DataGroup, provide each parameter individually bzr revid: xmo@openerp.com-20110510111204-8ypp5zdujd0azncf --- addons/base/static/src/js/data.js | 21 ++++++++++----------- addons/base/static/src/js/list.js | 4 +++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index c3683ed592e..2da920108b6 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -9,15 +9,17 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas * @constructs * @extends openerp.base.Controller * - * @param {Array} group_by sequence of fields by which to group * @param {openerp.base.Session} session Current OpenERP session - * @param {openerp.base.DataSet} dataset root dataset, holder of base view state + * @param {String} model name of the model managed by this DataGroup + * @param {Array} domain search domain for this DataGroup + * @param {Object} context context of the DataGroup's searches + * @param {Array} group_by sequence of fields by which to group */ - init: function(session, group_by, dataset) { + init: function(session, model, domain, context, group_by) { this._super(session, null); - this.model = dataset.model; - this.context = dataset.context; - this.domain = dataset.domain; + this.model = model; + this.context = context; + this.domain = domain; this.group_by = group_by; @@ -82,11 +84,8 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas var child_context = _.extend({}, this.context, group.__context); if (group.__context.group_by.length) { var datagroup = new openerp.base.DataGroup( - this.session, group.__context.group_by, { - model: this.model, - context: child_context, - domain: group.__domain - }); + this.session, this.model, group.__domain, child_context, + group.__context.group_by); ifDataGroup.call(datagroup, datagroup); } else { var dataset = new openerp.base.DataSetSearch(this.session, this.model); diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 87a59f0a469..775eaa19df3 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -296,7 +296,9 @@ openerp.base.ListView = openerp.base.Controller.extend( self.dataset.domain = results.domain; if (results.group_by.length) { self.groups.datagroup = new openerp.base.DataGroup( - self.session, results.group_by, self.dataset); + self.session, self.dataset.model, + results.domain, results.context, + results.group_by); self.$element.html(self.groups.render()); return; } From 6aae474545bf8979cdab05bc5ab6e5d975022578 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 10 May 2011 13:12:50 +0200 Subject: [PATCH 4/5] [FIX] callback parameters to DataGroup.get should use the root DataGroup as their context bzr revid: xmo@openerp.com-20110510111250-6bfx1bmq87od8n9e --- addons/base/static/src/js/data.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 2da920108b6..1686a803a50 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -86,12 +86,12 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas var datagroup = new openerp.base.DataGroup( this.session, this.model, group.__domain, child_context, group.__context.group_by); - ifDataGroup.call(datagroup, datagroup); + ifDataGroup.call(this, datagroup); } else { var dataset = new openerp.base.DataSetSearch(this.session, this.model); dataset.domain = group.__domain; dataset.context = child_context; - ifDataSet.call(dataset, dataset); + ifDataSet.call(this, dataset); } }, /** From bfd76b67d5585e512ab9020b787808048519342b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 11 May 2011 14:10:21 +0200 Subject: [PATCH 5/5] [IMP] DataGroup API doc bzr revid: xmo@openerp.com-20110511121021-ivie7r93fc6u6quf --- addons/base/static/src/js/data.js | 43 ++++++++++++++++++++++++++++--- doc/source/addons.rst | 2 -- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 1686a803a50..0cef3c83e0f 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -4,7 +4,15 @@ openerp.base.data = function(openerp) { openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{ /** * Management interface between views and grouped collections of OpenERP - * records + * records. + * + * The root DataGroup is instantiated with the relevant information + * (a session, a model, a domain, a context and a group_by sequence), the + * domain and context may be empty. It is then interacted with via + * :js:func:`~openerp.base.DataGroup.list`, which is used to read the + * content of the current grouping level, and + * :js:func:`~openerp.base.DataGroup.get`, which is used to fetch an item + * of the current grouping level. * * @constructs * @extends openerp.base.Controller @@ -26,6 +34,7 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas this.groups = null; }, fetch: function () { + // internal method var d = new $.Deferred(); var self = this; @@ -59,8 +68,8 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas return d.promise(); }, /** - * Retrieves the content of the nth-level item in the DataGroup, which - * results in either a DataSet or a DataGroup instance. + * Retrieves the content of an item in the DataGroup, which results in + * either a DataSet or new DataGroup instance. * * Calling :js:func:`~openerp.base.DataGroup.get` without having called * :js:func:`~openerp.base.DataGroup.list` beforehand will likely result @@ -95,7 +104,33 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas } }, /** - * Provides a list of all top-level items of the data group. + * Gathers the content of the current data group (the current grouping + * level), and provides it via a promise object to which callbacks should + * be added:: + * + * datagroup.list().then(function (list) { + * // manipulate list here + * }); + * + * The argument to the callback is the list of elements fetched, the + * context (``this``) is the datagroup itself. + * + * The items of a list are the standard objects returned by ``read_group`` + * with three properties added: + * + * ``length`` + * the number of records contained in the group (and all of its + * sub-groups). This does *not* provide the size of the "next level" + * of the group, unless the group is terminal (no more groups within + * it). + * ``grouped_on`` + * the name of the field this level was grouped on, this is mostly + * used for display purposes, in order to know the name of the current + * level of grouping. The ``grouped_on`` should be the same for all + * objects of the list. + * ``value`` + * the value which led to this group (this is the value all contained + * records have for the current ``grouped_on`` field name). * * @returns {$.Deferred} */ diff --git a/doc/source/addons.rst b/doc/source/addons.rst index bca568f58df..7675b829f92 100644 --- a/doc/source/addons.rst +++ b/doc/source/addons.rst @@ -307,8 +307,6 @@ Javascript :returns: the dataset's current active id -.. js:class:: openerp.base.DataRecord(session, model, fields, values) - Ad-hoc objects and structural types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~