[IMP] allow converters to add data to import messages, formalize message keys
bzr revid: xmo@openerp.com-20120924150417-c2y7g7vdsfz66363
This commit is contained in:
parent
a9c2cfcdb9
commit
f143902d1a
|
@ -38,6 +38,26 @@ class ir_fields_converter(orm.Model):
|
|||
By default, tries to get a method on itself with a name matching the
|
||||
pattern ``_$fromtype_$column._type`` and returns it.
|
||||
|
||||
Converter callables can either return a value to their caller or raise
|
||||
``ValueError``, which will be interpreted as a validation & conversion
|
||||
failure.
|
||||
|
||||
ValueError can have either one or two parameters. The first parameter
|
||||
is mandatory, **must** be a unicode string and will be used as the
|
||||
user-visible message for the error (it should be translatable and
|
||||
translated). It can contain a ``field`` named format placeholder so the
|
||||
caller can inject the field's translated, user-facing name (@string).
|
||||
|
||||
The second parameter is optional and, if provided, must be a mapping.
|
||||
This mapping will be merged into the error dictionary returned to the
|
||||
client.
|
||||
|
||||
If a converter can perform its function but has to make assumptions
|
||||
about the data, it can send a warning to the user through signalling
|
||||
an :class:`~openerp.osv.orm.ImportWarning` (via ``warnings.warn``). The
|
||||
handling of a warning at the upper levels is the same as
|
||||
``ValueError`` above.
|
||||
|
||||
:param cr: openerp cursor
|
||||
:param uid: ID of user calling the converter
|
||||
:param column: column object to generate a value for
|
||||
|
@ -74,10 +94,9 @@ class ir_fields_converter(orm.Model):
|
|||
))
|
||||
if value.lower() in falses: return False
|
||||
|
||||
warnings.warn(
|
||||
warnings.warn(orm.ImportWarning(
|
||||
_(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'")
|
||||
% (value, yes),
|
||||
orm.ImportWarning)
|
||||
% (value, yes)))
|
||||
return True
|
||||
|
||||
def _str_to_integer(self, cr, uid, model, column, value, context=None):
|
||||
|
@ -124,7 +143,9 @@ class ir_fields_converter(orm.Model):
|
|||
return item
|
||||
raise ValueError(
|
||||
_(u"Value '%s' not found in selection field '%%(field)s'") % (
|
||||
value))
|
||||
value), {
|
||||
'moreinfo': map(operator.itemgetter(1), selection)
|
||||
})
|
||||
|
||||
|
||||
def db_id_for(self, cr, uid, model, column, subfield, value, context=None):
|
||||
|
@ -174,9 +195,9 @@ class ir_fields_converter(orm.Model):
|
|||
cr, uid, name=value, operator='=', context=context)
|
||||
if ids:
|
||||
if len(ids) > 1:
|
||||
warnings.warn(
|
||||
warnings.warn(orm.ImportWarning(
|
||||
_(u"Found multiple matches for field '%%(field)s' (%d matches)")
|
||||
% (len(ids)), orm.ImportWarning)
|
||||
% (len(ids))))
|
||||
id, _name = ids[0]
|
||||
else:
|
||||
raise Exception(u"Unknown sub-field '%s'" % subfield)
|
||||
|
|
|
@ -1444,6 +1444,40 @@ class BaseModel(object):
|
|||
|
||||
def load(self, cr, uid, fields, data, context=None):
|
||||
"""
|
||||
Attempts to load the data matrix, and returns a list of ids (or
|
||||
``False`` if there was an error and no id could be generated) and a
|
||||
list of messages.
|
||||
|
||||
Each message is a dictionary with the following keys:
|
||||
|
||||
``type``
|
||||
the type of message, either ``warning`` or ``error``. Any ``error``
|
||||
message indicates the import failed and was rolled back.
|
||||
``message``
|
||||
the message's actual text, which should be translated and can be
|
||||
shown to the user directly
|
||||
``rows``
|
||||
a dict with 2 keys ``from`` and ``to``, indicates the range of rows
|
||||
in ``data`` which generated the message
|
||||
``record``
|
||||
a single integer, for warnings the index of the record which
|
||||
generated the message (can be obtained from a non-false ``ids``
|
||||
result)
|
||||
``field``
|
||||
the name of the (logical) OpenERP field for which the error or
|
||||
warning was generated
|
||||
``moreinfo`` (optional)
|
||||
A string, a list or a dict, leading to more information about the
|
||||
warning.
|
||||
|
||||
* If ``moreinfo`` is a string, it is a supplementary warnings
|
||||
message which should be hidden by default
|
||||
* If ``moreinfo`` is a list, it provides a number of possible or
|
||||
alternative values for the string
|
||||
* If ``moreinfo`` is a dict, it is an OpenERP action descriptor
|
||||
which can be executed to get more information about the issues
|
||||
with the field. If present, the ``help`` key serves as a label
|
||||
for the action (e.g. the text of the link).
|
||||
|
||||
:param cr: cursor for the request
|
||||
:param int uid: ID of the user attempting the data import
|
||||
|
@ -1598,6 +1632,14 @@ class BaseModel(object):
|
|||
(k, Converter.to_field(cr, uid, self, column, context=context))
|
||||
for k, column in columns.iteritems())
|
||||
|
||||
def _log(base, field, exception):
|
||||
type = 'warning' if isinstance(exception, Warning) else 'error'
|
||||
record = dict(base, field=field, type=type,
|
||||
message=unicode(exception.args[0]) % base)
|
||||
if len(exception.args) > 1 and exception.args[1]:
|
||||
record.update(exception.args[1])
|
||||
log(record)
|
||||
|
||||
stream = CountingStream(records)
|
||||
for record, extras in stream:
|
||||
dbid = False
|
||||
|
@ -1630,21 +1672,23 @@ class BaseModel(object):
|
|||
# field name
|
||||
message_base = dict(
|
||||
extras, record=stream.index, field=field_names[field])
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
try:
|
||||
try:
|
||||
with warnings.catch_warnings(record=True) as ws:
|
||||
converted[field] = converters[field](strvalue)
|
||||
|
||||
# In warning and error returned, use logical field name
|
||||
# as field so client can reverse
|
||||
for warning in w:
|
||||
log(dict(message_base, type='warning', field=field,
|
||||
message=unicode(warning.message) % message_base))
|
||||
except ValueError, e:
|
||||
log(dict(message_base,
|
||||
type='error',
|
||||
field=field,
|
||||
message=unicode(e) % message_base,
|
||||
))
|
||||
for warning in ws:
|
||||
# bubble non-import warnings upward
|
||||
if warning.category != ImportWarning:
|
||||
warnings.warn(warning.message, warning.category)
|
||||
continue
|
||||
w = warning.message
|
||||
if isinstance(w, basestring):
|
||||
# wrap warning string in an ImportWarning for
|
||||
# uniform handling
|
||||
w = ImportWarning(w)
|
||||
_log(message_base, field, w)
|
||||
except ValueError, e:
|
||||
_log(message_base, field, e)
|
||||
|
||||
yield dbid, xid, converted, dict(extras, record=stream.index)
|
||||
|
||||
|
|
|
@ -5,14 +5,10 @@ import openerp
|
|||
from openerp.tests import common
|
||||
from openerp.tools.misc import mute_logger
|
||||
|
||||
def message(msg, type='error', from_=0, to_=0, record=0, field='value'):
|
||||
return {
|
||||
'type': type,
|
||||
'rows': {'from': from_, 'to': to_},
|
||||
'record': record,
|
||||
'field': field,
|
||||
'message': msg
|
||||
}
|
||||
def message(msg, type='error', from_=0, to_=0, record=0, field='value', **kwargs):
|
||||
return dict(kwargs,
|
||||
type=type, rows={'from': from_, 'to': to_}, record=record,
|
||||
field=field, message=msg)
|
||||
|
||||
def error(row, message, record=None, **kwargs):
|
||||
""" Failed import of the record ``record`` at line ``row``, with the error
|
||||
|
@ -455,23 +451,15 @@ class test_selection(ImporterCase):
|
|||
def test_invalid(self):
|
||||
ids, messages = self.import_(['value'], [['Baz']])
|
||||
self.assertIs(ids, False)
|
||||
self.assertEqual(messages, [{
|
||||
'type': 'error',
|
||||
'rows': {'from': 0, 'to': 0},
|
||||
'record': 0,
|
||||
'field': 'value',
|
||||
'message': "Value 'Baz' not found in selection field 'unknown'",
|
||||
}])
|
||||
self.assertEqual(messages, [message(
|
||||
u"Value 'Baz' not found in selection field 'unknown'",
|
||||
moreinfo="Foo Bar Qux".split())])
|
||||
|
||||
ids, messages = self.import_(['value'], [[42]])
|
||||
self.assertIs(ids, False)
|
||||
self.assertEqual(messages, [{
|
||||
'type': 'error',
|
||||
'rows': {'from': 0, 'to': 0},
|
||||
'record': 0,
|
||||
'field': 'value',
|
||||
'message': "Value '42' not found in selection field 'unknown'",
|
||||
}])
|
||||
self.assertEqual(messages, [message(
|
||||
u"Value '42' not found in selection field 'unknown'",
|
||||
moreinfo="Foo Bar Qux".split())])
|
||||
|
||||
class test_selection_function(ImporterCase):
|
||||
model_name = 'export.selection.function'
|
||||
|
|
Loading…
Reference in New Issue