diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 8808a92820d..36eeadd0cf7 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -66,6 +66,10 @@ def is_url(node): def is_eval(node): return isinstance(node, yaml_tag.Eval) +def is_ref(node): + return isinstance(node, yaml_tag.Ref) \ + or _is_yaml_mapping(node, yaml_tag.Ref) + def is_ir_set(node): return _is_yaml_mapping(node, yaml_tag.IrSet) @@ -122,7 +126,7 @@ class YamlInterpreter(object): self.pool = pooler.get_pool(cr.dbname) self.uid = 1 self.context = {} # opererp context - self.eval_context = {'ref': self._ref, '_ref': self._ref} # added '_ref' so that record['ref'] is possible + self.eval_context = {'ref': self._ref, '_ref': self._ref, 'time': time} # added '_ref' so that record['ref'] is possible def _ref(self): return lambda xml_id: self.get_id(xml_id) @@ -155,10 +159,11 @@ class YamlInterpreter(object): id = self.id_map[xml_id] else: if '.' in xml_id: - module, xml_id = xml_id.split('.', 1) + module, checked_xml_id = xml_id.split('.', 1) else: module = self.module - ir_id = self.pool.get('ir.model.data')._get_id(self.cr, self.uid, module, xml_id) + checked_xml_id = xml_id + ir_id = self.pool.get('ir.model.data')._get_id(self.cr, self.uid, module, checked_xml_id) obj = self.pool.get('ir.model.data').read(self.cr, self.uid, ir_id, ['res_id']) id = int(obj['res_id']) self.id_map[xml_id] = id @@ -224,19 +229,39 @@ class YamlInterpreter(object): else: # all tests were successful for this assertion tag (no break) self.assert_report.record(True, assertion.severity) + def _coerce_bool(self, value, default=False): + if isinstance(value, types.BooleanType): + b = value + if isinstance(value, types.StringTypes): + b = value.strip().lower() not in ('0', 'false', 'off', 'no') + elif isinstance(value, types.IntType): + b = bool(value) + else: + b = default + return b + def process_record(self, node): record, fields = node.items()[0] self.validate_xml_id(record.id) if self.isnoupdate(record) and self.mode != 'init': - model = self.get_model(record.model) - record_dict = self._create_record(model, fields) - self.logger.debug("RECORD_DICT %s" % record_dict) - id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \ - self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode) - self.id_map[record.id] = int(id) - if config.get('import_partial', False): - self.cr.commit() + id = self.pool.get('ir.model.data')._update_dummy(self.cr, self.uid, record.model, self.module, record.id) + # check if the resource already existed at the last update + if id: + self.id_map[record] = int(id) + return None + else: + if not self._coerce_bool(record.forcecreate): + return None + + model = self.get_model(record.model) + record_dict = self._create_record(model, fields) + self.logger.debug("RECORD_DICT %s" % record_dict) + id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \ + self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode) + self.id_map[record.id] = int(id) + if config.get('import_partial', False): + self.cr.commit() def _create_record(self, model, fields): record_dict = {} @@ -247,17 +272,35 @@ class YamlInterpreter(object): def _eval_field(self, model, field_name, expression): column = model._columns[field_name] - if column._type == "many2one": + if is_ref(expression): + if expression.model: + other_model_name = expression.model + else: + other_model_name = column._obj + other_model = self.get_model(other_model_name) + if expression.search: + q = eval(expression.search, self.eval_context) + ids = other_model.search(self.cr, self.uid, q) + if expression.use: + instances = other_model.browse(self.cr, self.uid, ids) + elements = [inst[expression.use] for inst in instances] + else: + elements = ids + if column._type in ("many2many", "one2many"): + value = [(6, 0, elements)] + else: # many2one + value = elements[0] + elif column._type == "many2one": value = self.get_id(expression) elif column._type == "one2many": other_model = self.get_model(column._obj) value = [(0, 0, self._create_record(other_model, fields)) for fields in expression] elif column._type == "many2many": ids = [self.get_id(xml_id) for xml_id in expression] - value= [(6, 0, ids)] + value = [(6, 0, ids)] else: # scalar field - if isinstance(expression, yaml_tag.Eval): - value = eval(expression.expression) + if is_eval(expression): + value = eval(expression.expression, self.eval_context) else: value = expression # raise YamlImportException('Unsupported column "%s" or value %s:%s' % (field_name, type(expression), expression)) diff --git a/bin/tools/yaml_tag.py b/bin/tools/yaml_tag.py index 0a5286882ed..83f6dde9c8d 100644 --- a/bin/tools/yaml_tag.py +++ b/bin/tools/yaml_tag.py @@ -23,7 +23,7 @@ class Assert(YamlTag): super(Assert, self).__init__(**kwargs) class Record(YamlTag): - def __init__(self, model, id, **kwargs): + def __init__(self, model, id, use='id', **kwargs): self.model = model self.id = id super(Record, self).__init__(**kwargs) @@ -86,6 +86,12 @@ class Eval(YamlTag): self.expression = expression super(Eval, self).__init__() +class Ref(YamlTag): + # 'model' parameter is optional since it defaults to the column type + def __init__(self, search): + self.search = search + super(Ref, self).__init__() + class IrSet(YamlTag): def __init__(self): super(IrSet, self).__init__() @@ -138,10 +144,17 @@ def eval_constructor(loader, node): expression = loader.construct_scalar(node) return Eval(expression) +def ref_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Ref(**kwargs) + def ir_set_constructor(loader, node): kwargs = loader.construct_mapping(node) return IrSet(**kwargs) - + +# Registers constructors for custom tags. +# Constructors are actually defined globally: do not redefined them in another +# class/file/package. This means that module recorder need import this file. yaml.add_constructor(u"!assert", assert_constructor) yaml.add_constructor(u"!record", record_constructor) yaml.add_constructor(u"!python", python_constructor) @@ -154,4 +167,5 @@ yaml.add_constructor(u"!context", context_constructor) yaml.add_constructor(u"!delete", delete_constructor) yaml.add_constructor(u"!url", url_constructor) yaml.add_constructor(u"!eval", eval_constructor) +yaml.add_constructor(u"!ref", ref_constructor) yaml.add_constructor(u"!ir_set", ir_set_constructor)