Xavier (Open ERP) has proposed merging 
lp:~openerp-dev/openobject-addons/trunk-newimport-phase2-xmo into 
lp:openobject-addons.

Requested reviews:
  OpenERP Core Team (openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-newimport-phase2-xmo/+merge/126659

Adaptation of new client import to new import method 
(https://code.launchpad.net/~openerp-dev/openobject-server/trunk-newimport-phase2-xmo/+merge/126658)

* Use new method
* Don't adapt returned values (output is identical on the python side)
* UI fixes (import error wouldn't hide after triggering new import)
* Improvements to error reporting to use improved error messages returned from 
import
-- 
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-newimport-phase2-xmo/+merge/126659
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openobject-addons/trunk-newimport-phase2-xmo.
=== modified file 'base_import/models.py'
--- base_import/models.py	2012-09-03 14:00:22 +0000
+++ base_import/models.py	2012-09-27 10:46:27 +0000
@@ -83,6 +83,10 @@
         }]
         fields_got = self.pool[model].fields_get(cr, uid, context=context)
         for name, field in fields_got.iteritems():
+            # an empty string means the field is deprecated, @deprecated must
+            # be absent or False to mean not-deprecated
+            if field.get('deprecated', False) is not False:
+                continue
             if field.get('readonly'):
                 states = field.get('states')
                 if not states:
@@ -97,7 +101,7 @@
                 'id': name,
                 'name': name,
                 'string': field['string'],
-                # Y U NO ALWAYS HAVE REQUIRED
+                # Y U NO ALWAYS HAS REQUIRED
                 'required': bool(field.get('required')),
                 'fields': [],
             }
@@ -307,22 +311,14 @@
         except ValueError, e:
             return [{
                 'type': 'error',
-                'message': str(e),
+                'message': unicode(e),
                 'record': False,
             }]
 
-        try:
-            _logger.info('importing %d rows...', len(data))
-            (code, record, message, _wat) = self.pool[record.res_model].import_data(
-                cr, uid, import_fields, data, context=context)
-            _logger.info('done')
-
-        except Exception, e:
-            _logger.exception("Import failed")
-            # TODO: remove when exceptions stop being an "expected"
-            #       behavior of import_data on some (most) invalid
-            #       input.
-            code, record, message = -1, None, str(e)
+        _logger.info('importing %d rows...', len(data))
+        import_result = self.pool[record.res_model].load(
+            cr, uid, import_fields, data, context=context)
+        _logger.info('done')
 
         # If transaction aborted, RELEASE SAVEPOINT is going to raise
         # an InternalError (ROLLBACK should work, maybe). Ignore that.
@@ -339,14 +335,4 @@
         except psycopg2.InternalError:
             pass
 
-        if code != -1:
-            return []
-
-        # TODO: add key for error location?
-        # TODO: error not within normal preview, how to display? Re-preview
-        #       with higher ``count``?
-        return [{
-            'type': 'error',
-            'message': message,
-            'record': record or False
-        }]
+        return import_result['messages']

=== modified file 'base_import/static/src/css/import.css'
--- base_import/static/src/css/import.css	2012-09-11 06:38:49 +0000
+++ base_import/static/src/css/import.css	2012-09-27 10:46:27 +0000
@@ -10,7 +10,8 @@
 .oe_import .oe_import_grid,
 .oe_import .oe_import_error_report,
 .oe_import .oe_import_with_file,
-.oe_import .oe_import_noheaders {
+.oe_import .oe_import_noheaders,
+.oe_import .oe_import_report_more {
     display: none;
 }
 
@@ -19,7 +20,8 @@
 }
 .oe_import.oe_import_error .oe_import_error_report,
 .oe_import.oe_import_with_file .oe_import_with_file,
-.oe_import.oe_import_noheaders .oe_import_noheaders {
+.oe_import.oe_import_noheaders .oe_import_noheaders,
+.oe_import .oe_import_report_showmore .oe_import_report_more {
     display: block;
 }
 

=== modified file 'base_import/static/src/js/import.js'
--- base_import/static/src/js/import.js	2012-09-11 06:38:49 +0000
+++ base_import/static/src/js/import.js	2012-09-27 10:46:27 +0000
@@ -63,7 +63,7 @@
             {name: 'quoting', label: _lt("Quoting:"), value: '"'}
         ],
         events: {
-            'change .oe_import_grid input': 'import_dryrun',
+            // 'change .oe_import_grid input': 'import_dryrun',
             'change input.oe_import_file': 'file_update',
             'change input.oe_import_has_header, .oe_import_options input': 'settings_updated',
             'click a.oe_import_csv': function (e) {
@@ -79,15 +79,37 @@
                         ? $el.next()
                         : $el.parent().next())
                     .toggle();
+            },
+            'click .oe_import_report a.oe_import_report_count': function (e) {
+                e.preventDefault();
+                $(e.target).parent().toggleClass('oe_import_report_showmore');
+            },
+            'click .oe_import_moreinfo_action a': function (e) {
+                e.preventDefault();
+                // #data will parse the attribute on its own, we don't like
+                // that sort of things
+                var action = JSON.parse($(e.target).attr('data-action'));
+                // FIXME: when JS-side clean_action
+                action.views = _(action.views).map(function (view) {
+                    var id = view[0], type = view[1];
+                    return [
+                        id,
+                        type !== 'tree' ? type
+                          : action.view_type === 'form' ? 'list'
+                          : 'tree'
+                    ];
+                });
+                this.do_action(_.extend(action, {target: 'new'}));
             }
         },
         init: function (parent, dataset) {
             var self = this;
             this._super(parent, {
                 buttons: [
-                    {text: _t("Import File"), click: function () {
-                        self.do_import();
-                    }, 'class': 'oe_import_dialog_button'}
+                    {text: _t("Import"), click: self.proxy('do_import'),
+                     'class': 'oe_import_dialog_button'},
+                    {text: _t("Validate"), click: self.proxy('import_dryrun'),
+                     'class': 'oe_import_dialog_button'}
                 ]
             });
             this.res_model = parent.model;
@@ -134,11 +156,12 @@
                 .then(this.proxy('preview'));
         },
         preview: function (result) {
+            this.$el.removeClass('oe_import_preview_error oe_import_error');
             this.$el.toggleClass(
                 'oe_import_noheaders',
                 !this.$('input.oe_import_has_header').prop('checked'));
             if (result.error) {
-                this.$el.addClass('oe_import_error');
+                this.$el.addClass('oe_import_preview_error oe_import_error');
                 this.$('.oe_import_error_report').html(
                     QWeb.render('ImportView.preview.error', result));
                 return;
@@ -180,7 +203,7 @@
                 width: 'resolve',
                 dropdownCssClass: 'oe_import_selector'
             });
-            this.import_dryrun();
+            //this.import_dryrun();
         },
         generate_fields_completion: function (root) {
             var basic = [];
@@ -252,7 +275,6 @@
 
         //- import itself
         call_import: function (options) {
-            var self = this;
             var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) {
                 return $(el).select2('val') || false;
             }).get();
@@ -260,32 +282,78 @@
                 'do', [this.id, fields, this.import_options()], options);
         },
         import_dryrun: function () {
-//            this.call_import({ dryrun: true })
-//                .then(this.proxy('render_import_errors'));
+            return this.call_import({ dryrun: true })
+                .then(this.proxy('render_import_result'));
         },
         do_import: function () {
             var self = this;
-            this.call_import({ dryrun: false }).then(function (errors) {
-                if (_.isEmpty(errors)) {
+            return this.call_import({ dryrun: false }).then(function (message) {
+                if (!_.any(message, function (message) {
+                        return message.type === 'error' })) {
                     if (self.getParent().reload_content) {
                         self.getParent().reload_content();
                     }
                     self.close();
                     return;
                 }
-                self.render_import_errors(errors);
+                self.render_import_result(message);
             });
         },
-        render_import_errors: function (errors) {
-            if (_.isEmpty(errors)) {
+        render_import_result: function (message) {
+            if (_.isEmpty(message)) {
                 this.$el.removeClass('oe_import_error');
                 return;
             }
-            // import failed (or maybe just warnings, if we ever get
-            // warnings?)
+            // row indexes come back 0-indexed, spreadsheets
+            // display 1-indexed.
+            var offset = 1;
+            // offset more if header
+            if (this.import_options().header) { offset += 1; }
+
             this.$el.addClass('oe_import_error');
             this.$('.oe_import_error_report').html(
-                QWeb.render('ImportView.error', {errors: errors}));
+                QWeb.render('ImportView.error', {
+                    errors: _(message).groupBy('message'),
+                    at: function (rows) {
+                        var from = rows.from + offset;
+                        var to = rows.to + offset;
+                        if (from === to) {
+                            return _.str.sprintf(_t("at row %d"), from);
+                        }
+                        return _.str.sprintf(_t("between rows %d and %d"),
+                                             from, to);
+                    },
+                    more: function (n) {
+                        return _.str.sprintf(_t("(%d more)"), n);
+                    },
+                    info: function (msg) {
+                        if (typeof msg === 'string') {
+                            return _.str.sprintf(
+                                '<div class="oe_import_moreinfo oe_import_moreinfo_message">%s</div>',
+                                _.str.escapeHTML(msg));
+                        }
+                        if (msg instanceof Array) {
+                            return _.str.sprintf(
+                                '<div class="oe_import_moreinfo oe_import_moreinfo_choices">%s <ul>%s</ul></div>',
+                                _.str.escapeHTML(_t("Here are the possible values:")),
+                                _(msg).map(function (msg) {
+                                    return '<li>'
+                                        + _.str.escapeHTML(msg)
+                                    + '</li>';
+                                }).join(''));
+                        }
+                        // Final should be object, action descriptor
+                        return [
+                            '<div class="oe_import_moreinfo oe_import_moreinfo_action">',
+                                _.str.sprintf('<a href="#" data-action="%s">',
+                                        _.str.escapeHTML(JSON.stringify(msg))),
+                                    _.str.escapeHTML(
+                                        _t("Get all possible values")),
+                                '</a>',
+                            '</div>'
+                        ].join('')
+                    },
+                }));
         },
     });
 };

=== modified file 'base_import/static/src/xml/import.xml'
--- base_import/static/src/xml/import.xml	2012-09-11 06:38:49 +0000
+++ base_import/static/src/xml/import.xml	2012-09-27 10:46:27 +0000
@@ -86,12 +86,31 @@
         <pre><t t-esc="preview"/></pre>
     </t>
     <ul t-name="ImportView.error">
-        <li t-foreach="errors" t-as="error" t-attf-class="oe_import_report_#{error.type}">
-            <!-- can also have error.record, but may be *huge* if
-                 e.g. has image fields -->
-            <t t-esc="error.message"/>
+        <li t-foreach="errors" t-as="error"
+            t-attf-class="oe_import_report oe_import_report_#{error_value[0].type}">
+            <t t-call="ImportView.error.each">
+                <t t-set="error" t-value="error_value[0]"/>
+            </t>
+
+            <a href="#" class="oe_import_report_count" t-if="error_value.length gt 1">
+                <t t-esc="more(error_value.length - 1)"/>
+            </a>
+            <ul class="oe_import_report_more" t-if="error_value.length gt 1">
+                <li t-foreach="error_value.length - 1" t-as="index">
+                    <t t-call="ImportView.error.each">
+                        <t t-set="error" t-value="error_value[index + 1]"/>
+                    </t>
+                </li>
+            </ul>
         </li>
     </ul>
+    <t t-name="ImportView.error.each">
+        <span class="oe_import_report_message">
+            <t t-esc="error.message"/>
+        </span>
+        <t t-if="error.rows"  t-esc="at(error.rows)"/>
+        <t t-if="error.moreinfo" t-raw="info(error.moreinfo)"/>
+    </t>
     <t t-extend="ListView.buttons">
         <t t-jquery="span.oe_alternative">
             this.attr('t-if', 'widget.options.import_enabled');

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to