From baf367d0e1945404fe20f250e8e25acea4cc40fe Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Sep 2011 10:33:41 +0200 Subject: [PATCH] [IMP] move fetching of fields for import to javascript. Also extraction of required fields bzr revid: xmo@openerp.com-20110923083341-xa3sg2t053zonlsm --- addons/web/__openerp__.py | 1 + addons/web/controllers/main.py | 9 +-- .../static/lib/jquery.deferred-queue/LICENSE | 21 ++++++ .../static/lib/jquery.deferred-queue/README | 59 +++++++++++++++ .../jquery.deferred-queue.js | 34 +++++++++ addons/web/static/src/js/data_import.js | 72 ++++++++++++++++--- 6 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 addons/web/static/lib/jquery.deferred-queue/LICENSE create mode 100644 addons/web/static/lib/jquery.deferred-queue/README create mode 100644 addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index ff0a038aae2..06175ba6199 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -19,6 +19,7 @@ "static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js", "static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js", "static/lib/jquery.ui.notify/js/jquery.notify.js", + "static/lib/jquery.deferred-queue/jquery.deferred-queue.js", "static/lib/json/json2.js", "static/lib/qweb/qweb2.js", "static/lib/underscore/underscore.js", diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 6ded38b5f93..85f13a9f9f1 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1428,9 +1428,6 @@ class Import(View): fields = req.session.model(model).fields_get(False, req.session.eval_context(req.context)) fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) - required_fields = [field_name for field_name, field in fields.iteritems() - if field.get('required')] - def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): def str_comp(x,y): if xwindow.top.%s(%s);' % ( jsonp, simplejson.dumps({ - 'records':records[1:],'header':header_fields, - 'all_fields':all_fields,'required_fields':required_fields})) + 'records':records[1:],'header':header_fields})) @openerpweb.httprequest def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip, diff --git a/addons/web/static/lib/jquery.deferred-queue/LICENSE b/addons/web/static/lib/jquery.deferred-queue/LICENSE new file mode 100644 index 00000000000..ee8cf44578c --- /dev/null +++ b/addons/web/static/lib/jquery.deferred-queue/LICENSE @@ -0,0 +1,21 @@ +Copyright 2011 Xavier Morel. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY XAVIER MOREL ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/addons/web/static/lib/jquery.deferred-queue/README b/addons/web/static/lib/jquery.deferred-queue/README new file mode 100644 index 00000000000..71d980199c9 --- /dev/null +++ b/addons/web/static/lib/jquery.deferred-queue/README @@ -0,0 +1,59 @@ +.. -*- restructuredtext -*- + +In jQuery 1.5, jQuery has introduced a Deferred object in order to +better handle callbacks. + +Along with Deferred, it introduced ``jQuery.when`` which allows, among +other things, for multiplexing deferreds (waiting on multiple +deferreds at the same time). + +While this is very nice if all deferreds are available at the same +point, it doesn't really work if the resolution of a deferred can +generate more deferreds, or for collections of deferreds coming from +multiple sources (which may not be aware of one another). + +Deferred.queue tries to be a solution to this. It is based on the +principle of FIFO multiple-producers multiple-consumers tasks queues, +such as Python's `queue.Queue`_. Any code with a reference to the +queue can add a promise to the queue, and the queue (which is a +promise itself) will only be resolved once all promises within are +resolved. + +Quickstart +---------- + +Deferred.queue has a very simple life cycle: it is dormant when +created, it starts working as soon as promises get added to it (via +``.push``, or by providing them directly to its constructor), and as +soon as the last promise in the queue is resolved it resolves itself. + +If any promise in the queue fails, the queue itself will be rejected +(without waiting for further promises). + +Once a queue has been resolved or rejected, adding new promises to it +results in an error. + +API +--- + +``jQuery.Deferred.queue([promises...])`` + Creates a new deferred queue. Can be primed with a series of promise + objects. + +``.push(promise...)`` + Adds promises to the queue. Returns the queue itself (can be + chained). + + If the queue has already been resolved or rejected, raises an error. + +``.then([doneCallbacks][, failCallbacks])`` + Promise/A ``then`` method. + +``.done(doneCallbacks)`` + jQuery ``done`` extension to promise objects + +``.fail(failCallbacks)`` + jQuery ``fail`` extension to promise objects + +.. _queue.Queue: + http://docs.python.org/dev/library/queue.html diff --git a/addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js b/addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js new file mode 100644 index 00000000000..7ef83342f77 --- /dev/null +++ b/addons/web/static/lib/jquery.deferred-queue/jquery.deferred-queue.js @@ -0,0 +1,34 @@ +(function ($) { + "use strict"; + $.extend($.Deferred, { + queue: function () { + var queueDeferred = $.Deferred(); + var promises = 0; + + function resolve() { + if (--promises > 0) { + return; + } + setTimeout($.proxy(queueDeferred, 'resolve'), 0); + } + + var promise = $.extend(queueDeferred.promise(), { + push: function () { + if (this.isResolved() || this.isRejected()) { + throw new Error("Can not add promises to a resolved " + + "or rejected promise queue"); + } + + promises += 1; + $.when.apply(null, arguments).then( + resolve, $.proxy(queueDeferred, 'reject')); + return this; + } + }); + if (arguments.length) { + promise.push.apply(promise, arguments); + } + return promise; + } + }); +})(jQuery) diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index e3624b9ec57..1453c39fd17 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -23,11 +23,36 @@ function jsonp(form, attributes, callback) { data: options }, attributes)); } + openerp.web.DataImport = openerp.web.Dialog.extend({ template: 'ImportDataView', dialog_title: "Import Data", init: function(parent, dataset){ + var self = this; this._super(parent, {}); + this.model = parent.model; + this.fields = []; + this.all_fields = []; + this.required_fields; + + var convert_fields = function (root, prefix) { + prefix = prefix || ''; + _(root.fields).each(function (f) { + var name = prefix + f.name; + self.all_fields.push(name); + if (f.fields) { + convert_fields(f, name + '/'); + } + }); + }; + this.ready = $.Deferred.queue().then(function () { + self.required_fields = _(self.fields).chain() + .filter(function (field) { return field.required; }) + .pluck('name') + .value(); + convert_fields(self); + self.all_fields.sort(); + }); }, start: function() { var self = this; @@ -51,6 +76,35 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.$element.find('fieldset legend').click(function() { $(this).next().toggle(); }); + this.ready.push(new openerp.web.DataSet(this, this.model).call( + 'fields_get', [], function (fields) { + self.graft_fields(fields); + })); + }, + graft_fields: function (fields, parent, level) { + parent = parent || this; + level = level || 0; + + var self = this; + _(fields).each(function (field, field_name) { + var f = { + name: field_name, + string: field.string, + required: field.required + }; + + if (field.type === 'one2many') { + f.fields = []; + // only fetch sub-fields to a depth of 2 levels + if (level < 2) { + self.ready.push(new openerp.web.DataSet(self, field.relation).call( + 'fields_get', [], function (fields) { + self.graft_fields(fields, f, level+1); + })); + } + } + parent.fields.push(f); + }); }, toggle_import_button: function (newstate) { this.$dialog.dialog('widget') @@ -89,15 +143,13 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ var self = this; this.$element.find('.sel_fields').autocomplete({ minLength: 0, - source: results.all_fields, - change: function () { - self.on_check_field_values(results['required_fields']); - } + source: this.all_fields, + change: self.on_check_field_values }).focus(function () { $(this).autocomplete('search'); }); - this.on_check_field_values(results['required_fields']); + this.on_check_field_values(); }, /** * Looks through all the field selections, and tries to find if two @@ -134,10 +186,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ }); return duplicates; }, - on_check_field_values: function (required_fields) { + on_check_field_values: function () { this.$element.find("#message, #msg").remove(); - var required_valid = this.check_required(required_fields); + var required_valid = this.check_required(); var duplicates = this.find_duplicate_fields(); if (_.isEmpty(duplicates)) { @@ -156,15 +208,15 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ } }, - check_required: function(required_fields) { - if (!required_fields.length) { return true; } + check_required: function() { + if (!this.required_fields.length) { return true; } var selected_fields = _(this.$element.find('.sel_fields').get()).chain() .pluck('value') .compact() .value(); - var missing_fields = _.difference(required_fields, selected_fields); + var missing_fields = _.difference(this.required_fields, selected_fields); if (missing_fields.length) { this.$element.find("#result").before('
*Required Fields are not selected : ' + missing_fields + '.
'); return false;