1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 OpenERP SA (<http://openerp.com>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
23 Experimental script for conversion between OpenERP's XML serialization format
24 and the new YAML serialization format introduced in OpenERP 6.0.
25 Intended to be used as a quick preprocessor for converting data/test files, then
26 to be fine-tuned manually.
31 from lxml import etree
36 value='"' + value + '"'
39 class XmlTag(etree.ElementBase):
43 if hasattr(node, '_to_yaml'):
44 child_tags.append(node._to_yaml())
45 return self.tag(attrib=self.attrib, child_tags=child_tags)
47 class YamlTag(object):
49 Superclass for constructors of custom tags defined in yaml file.
51 def __init__(self, **kwargs):
52 self.__dict__.update(kwargs)
53 self.attrib = self.__dict__.get('attrib', {})
54 self.child_tags = self.__dict__.get('child_tags', '')
55 def __getitem__(self, key):
56 return getattr(self, key)
57 def __getattr__(self, attr):
60 for k,v in self.attrib.iteritems():
61 if str(v) and str(v)[0] in ['[', '{', '#', '*', '(']:
62 self.attrib[k] = toString(self.attrib[k]).replace("'", '')
63 st = self.yaml_tag + ' ' + str(self.attrib)
69 def __init__(self, expr="False"):
72 return "'%s'"%str(self.expr)
76 def __init__(self, expr="False"):
80 if value.find("6,") != -1:
81 value = eval(str(eval(value)))
88 if value and value[0] in ['[', '{', '#', '*', '(']:
89 value = value.replace('"', r'\"')
90 value = toString(value)
98 class Search(YamlTag):
102 class xml_test(XmlTag):
104 expr = self.attrib.get('expr')
107 expr = expr + ' == ' + '"%s"'%text
110 class xml_data(etree.ElementBase):
112 value = self.attrib.get('noupdate', "0")
116 class xml_field(etree.ElementBase):
118 field = ' ' + self.attrib.pop('name','unknown')
120 if self.attrib.get('search', None):
121 value = Search(attrib=self.attrib, child_tags='').__repr__()
123 attr = (self.attrib.get('ref', None) and 'ref') or (self.attrib.get('eval', None) and 'eval') or 'None'
124 value = Eval(self.attrib.get(attr, self.text)).__repr__() or ''
125 return {field: value}
128 class xml_value(etree.ElementBase):
131 if self.attrib.get('eval', None):
132 key, val = 'eval', '"'+self.attrib.get('eval')+'"'
133 elif self.attrib.get('model', None):
134 key, val = 'model', self.attrib.get('model')
135 val=val.replace("'",'""')
138 for k,v in self.attrib.iteritems():
142 v=v.replace("'",'""')
145 ls=[[{key:val},dict(d)]]
152 yaml_tag = u'!context'
153 def __init__(self, noupdate="0"):
154 self.child_tags = {' noupdate':noupdate}
159 class Record(YamlTag):
160 yaml_tag = u'!record'
161 class xml_record(XmlTag):
166 if hasattr(node, '_to_yaml'):
167 child_tags.update(node._to_yaml())
168 return Record(attrib=self.attrib, child_tags=child_tags)
171 class Ir_Set(YamlTag):
172 yaml_tag = u'!ir_set'
176 class xml_ir_set(XmlTag):
181 if hasattr(node, '_to_yaml'):
182 child_tags.update(node._to_yaml())
183 return Ir_Set(attrib=self.attrib, child_tags=child_tags)
186 class Workflow(YamlTag):
187 yaml_tag = u'!workflow'
188 class xml_workflow(XmlTag):
192 class Function(YamlTag):
193 yaml_tag = u'!function'
194 class xml_function(XmlTag):
198 class Assert(YamlTag):
199 yaml_tag = u'!assert'
200 class xml_assert(XmlTag):
203 # menuitem tagresult.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True).replace("'",''))
204 class MenuItem(YamlTag):
205 yaml_tag = u'!menuitem'
206 class xml_menuitem(XmlTag):
210 class ActWindow(YamlTag):
211 yaml_tag = u'!act_window'
212 class xml_act_window(XmlTag):
216 class Report(YamlTag):
217 yaml_tag = u'!report'
218 class xml_report(XmlTag):
222 class Delete(YamlTag):
223 yaml_tag = u'!delete'
224 class xml_delete(XmlTag):
228 class Python(YamlTag):
229 yaml_tag = u'!python'
230 class xml_python(XmlTag):
234 class Context(YamlTag):
235 yaml_tag = u'!context'
236 class xml_context(XmlTag):
242 class xml_url(XmlTag):
246 class Delete(YamlTag):
247 yaml_tag = u'!delete'
248 class xml_delete(XmlTag):
251 def represent_data(dumper, data):
252 return dumper.represent_mapping(u'tag:yaml.org,2002:map', [('!'+str(data), data.child_tags)])
254 yaml.SafeDumper.add_representer(Record, represent_data)
255 yaml.SafeDumper.add_representer(data, represent_data)
256 yaml.SafeDumper.add_representer(Workflow, represent_data)
257 yaml.SafeDumper.add_representer(Function, represent_data)
258 yaml.SafeDumper.add_representer(Assert, represent_data)
259 yaml.SafeDumper.add_representer(MenuItem, represent_data)
260 yaml.SafeDumper.add_representer(Ir_Set, represent_data)
261 yaml.SafeDumper.add_representer(Python, represent_data)
262 yaml.SafeDumper.add_representer(Context, represent_data)
264 class MyLookup(etree.CustomElementClassLookup):
265 def lookup(self, node_type, document, namespace, name):
266 if node_type=='element':
269 'record': xml_record,
271 'workflow': xml_workflow,
272 'function': xml_function,
274 'assert': xml_assert,
276 'menuitem': xml_menuitem,
277 'act_window': xml_act_window,
278 'report': xml_report,
279 'delete': xml_delete,
280 'python': xml_python,
281 'context': xml_context,
283 'ir_set': xml_ir_set,
285 elif node_type=='comment':
286 return None#xml_comment
289 class xml_parse(object):
292 def parse(self, fname):
293 parser = etree.XMLParser()
294 parser.setElementClassLookup(MyLookup())
296 self.root = etree.XML(file(fname).read(), parser)
297 for data in self.root:
298 if hasattr(data, '_to_yaml'):
299 obj = data._to_yaml()
300 if obj.yaml_tag == '!context':
301 result.append(yaml.dump(str(obj)).replace("'",'').split('\n')[0])
302 result.append(yaml.dump(obj.child_tags, default_flow_style=False).replace("'",''))
304 result.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True).replace("'",''))
305 self.context.update(data.attrib)
307 if tag.tag == etree.Comment:
310 if hasattr(tag, '_to_yaml'):
312 if not obj.child_tags:
313 result.append(yaml.dump('!'+str(obj), default_flow_style=False, allow_unicode=True, width=999).replace("'",''))
315 result.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True, width=999).replace('\n:', ':\n').replace("'",''))
316 print "# Experimental OpenERP xml-to-yml conversion! (v%s)"%__VERSION__
317 print "# Please use this as a first conversion/preprocessing step,"
318 print "# not as a production-ready tool!"
319 for record in result:
320 if type(record) != type(''):
322 l= record.split("\n")
324 print '#' + str(line)
327 record = record.replace('- --',' ') #for value tag
328 record = record.replace('!!', '- \n !') #for all parent tags
329 record = record.replace('- - -', ' -') #for many2many fields
330 record = record.replace('? ', '') #for long expressions
331 record = record.replace('""', "'") #for string-value under value tag
334 if __name__=='__main__':
337 parser = optparse.OptionParser(
338 usage = '%s file.xml' % sys.argv[0])
339 (opt, args) = parser.parse_args()
341 parser.error("incorrect number of arguments")