[IMP] move fetching of fields for import to javascript. Also extraction of required fields
bzr revid: xmo@openerp.com-20110923083341-xa3sg2t053zonlsm
This commit is contained in:
parent
6bd754060e
commit
baf367d0e1
|
@ -19,6 +19,7 @@
|
||||||
"static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js",
|
"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/js/jquery-ui-timepicker-addon.js",
|
||||||
"static/lib/jquery.ui.notify/js/jquery.notify.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/json/json2.js",
|
||||||
"static/lib/qweb/qweb2.js",
|
"static/lib/qweb/qweb2.js",
|
||||||
"static/lib/underscore/underscore.js",
|
"static/lib/underscore/underscore.js",
|
||||||
|
|
|
@ -1428,9 +1428,6 @@ class Import(View):
|
||||||
fields = req.session.model(model).fields_get(False, req.session.eval_context(req.context))
|
fields = req.session.model(model).fields_get(False, req.session.eval_context(req.context))
|
||||||
fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}})
|
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 model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2):
|
||||||
def str_comp(x,y):
|
def str_comp(x,y):
|
||||||
if x<y: return 1
|
if x<y: return 1
|
||||||
|
@ -1459,9 +1456,6 @@ class Import(View):
|
||||||
fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}})
|
fields.update({'id':{'string':'ID'},'.id':{'string':'Database ID'}})
|
||||||
model_populate(fields)
|
model_populate(fields)
|
||||||
|
|
||||||
all_fields = _fields.keys()
|
|
||||||
all_fields.sort()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep))
|
data = csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep))
|
||||||
except:
|
except:
|
||||||
|
@ -1504,8 +1498,7 @@ class Import(View):
|
||||||
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
return '<script>window.top.%s(%s);</script>' % (
|
||||||
jsonp, simplejson.dumps({
|
jsonp, simplejson.dumps({
|
||||||
'records':records[1:],'header':header_fields,
|
'records':records[1:],'header':header_fields}))
|
||||||
'all_fields':all_fields,'required_fields':required_fields}))
|
|
||||||
|
|
||||||
@openerpweb.httprequest
|
@openerpweb.httprequest
|
||||||
def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip,
|
def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, csvskip,
|
||||||
|
|
|
@ -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 <COPYRIGHT HOLDER> 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.
|
|
@ -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
|
|
@ -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)
|
|
@ -23,11 +23,36 @@ function jsonp(form, attributes, callback) {
|
||||||
data: options
|
data: options
|
||||||
}, attributes));
|
}, attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
openerp.web.DataImport = openerp.web.Dialog.extend({
|
openerp.web.DataImport = openerp.web.Dialog.extend({
|
||||||
template: 'ImportDataView',
|
template: 'ImportDataView',
|
||||||
dialog_title: "Import Data",
|
dialog_title: "Import Data",
|
||||||
init: function(parent, dataset){
|
init: function(parent, dataset){
|
||||||
|
var self = this;
|
||||||
this._super(parent, {});
|
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() {
|
start: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -51,6 +76,35 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
|
||||||
this.$element.find('fieldset legend').click(function() {
|
this.$element.find('fieldset legend').click(function() {
|
||||||
$(this).next().toggle();
|
$(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) {
|
toggle_import_button: function (newstate) {
|
||||||
this.$dialog.dialog('widget')
|
this.$dialog.dialog('widget')
|
||||||
|
@ -89,15 +143,13 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
|
||||||
var self = this;
|
var self = this;
|
||||||
this.$element.find('.sel_fields').autocomplete({
|
this.$element.find('.sel_fields').autocomplete({
|
||||||
minLength: 0,
|
minLength: 0,
|
||||||
source: results.all_fields,
|
source: this.all_fields,
|
||||||
change: function () {
|
change: self.on_check_field_values
|
||||||
self.on_check_field_values(results['required_fields']);
|
|
||||||
}
|
|
||||||
}).focus(function () {
|
}).focus(function () {
|
||||||
$(this).autocomplete('search');
|
$(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
|
* 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;
|
return duplicates;
|
||||||
},
|
},
|
||||||
on_check_field_values: function (required_fields) {
|
on_check_field_values: function () {
|
||||||
this.$element.find("#message, #msg").remove();
|
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();
|
var duplicates = this.find_duplicate_fields();
|
||||||
if (_.isEmpty(duplicates)) {
|
if (_.isEmpty(duplicates)) {
|
||||||
|
@ -156,15 +208,15 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
check_required: function(required_fields) {
|
check_required: function() {
|
||||||
if (!required_fields.length) { return true; }
|
if (!this.required_fields.length) { return true; }
|
||||||
|
|
||||||
var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
|
var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
|
||||||
.pluck('value')
|
.pluck('value')
|
||||||
.compact()
|
.compact()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
var missing_fields = _.difference(required_fields, selected_fields);
|
var missing_fields = _.difference(this.required_fields, selected_fields);
|
||||||
if (missing_fields.length) {
|
if (missing_fields.length) {
|
||||||
this.$element.find("#result").before('<div id="message" style="color:red">*Required Fields are not selected : ' + missing_fields + '.</div>');
|
this.$element.find("#result").before('<div id="message" style="color:red">*Required Fields are not selected : ' + missing_fields + '.</div>');
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue