~openerp/openobject-server/web-dashboard

« back to all changes in this revision

Viewing changes to tools/xml2yml.py

  • Committer: Olivier Dony
  • Author(s): OpenERP Framework R&D Team
  • Date: 2010-05-20 14:35:28 UTC
  • Revision ID: odo@openerp.com-20100520143528-3717ybwyp1exhsd6
[ADD] new experimental utility script: xml2yml for conversion of XML data/test files to the new YAML format

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
#    OpenERP, Open Source Management Solution
 
5
#    Copyright (C) 2004-2010 OpenERP SA (<http://openerp.com>).
 
6
#
 
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.
 
11
#
 
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.
 
16
#
 
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/>.
 
19
#
 
20
##############################################################################
 
21
 
 
22
"""
 
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.
 
27
"""
 
28
 
 
29
import yaml
 
30
import logging
 
31
from lxml import etree
 
32
 
 
33
__VERSION__ = '0.0.2'
 
34
 
 
35
def toString(value):
 
36
    value='"' + value + '"'
 
37
    return value
 
38
 
 
39
class XmlTag(etree.ElementBase):
 
40
    def _to_yaml(self):
 
41
        child_tags = []
 
42
        for node in self:
 
43
            if hasattr(node, '_to_yaml'):
 
44
                child_tags.append(node._to_yaml())
 
45
        return self.tag(attrib=self.attrib, child_tags=child_tags)
 
46
 
 
47
class YamlTag(object):
 
48
    """
 
49
    Superclass for constructors of custom tags defined in yaml file.
 
50
    """
 
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):
 
58
        return None
 
59
    def __repr__(self):
 
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)
 
64
        return st
 
65
 
 
66
# attrib tags
 
67
class ref(YamlTag):
 
68
    yaml_tag = u'!ref'
 
69
    def __init__(self, expr="False"):
 
70
        self.expr = expr
 
71
    def __repr__(self):
 
72
        return "'%s'"%str(self.expr)
 
73
 
 
74
class Eval(YamlTag):
 
75
    yaml_tag = u'!eval'
 
76
    def __init__(self, expr="False"):
 
77
        self.expr = expr
 
78
    def __repr__(self):
 
79
        value=str(self.expr)
 
80
        if value.find("6,") != -1:
 
81
            value = eval(str(eval(value)))
 
82
            value=value[0][2]
 
83
            value = [[value]]
 
84
        else:
 
85
            try:
 
86
                value=int(value)
 
87
            except:
 
88
                if value and value[0] in ['[', '{', '#', '*', '(']:
 
89
                    value = value.replace('"', r'\"')
 
90
                    value = toString(value)
 
91
                else:
 
92
                    try:
 
93
                        value = eval(value)
 
94
                    except Exception:
 
95
                        pass
 
96
        return value
 
97
 
 
98
class Search(YamlTag):
 
99
    yaml_tag = u'!ref'
 
100
 
 
101
# test tag
 
102
class xml_test(XmlTag):
 
103
    def _to_yaml(self):
 
104
        expr = self.attrib.get('expr')
 
105
        text = self.text
 
106
        if text:
 
107
            expr = expr + ' == ' + '"%s"'%text
 
108
        return [[expr]]
 
109
 
 
110
class xml_data(etree.ElementBase):
 
111
    def _to_yaml(self):
 
112
        value = self.attrib.get('noupdate', "0")
 
113
        return data(value)
 
114
 
 
115
# field tag:
 
116
class xml_field(etree.ElementBase):
 
117
    def _to_yaml(self):
 
118
        field = '  ' + self.attrib.pop('name','unknown')
 
119
 
 
120
        if self.attrib.get('search', None):
 
121
            value = Search(attrib=self.attrib, child_tags='').__repr__()
 
122
        else:
 
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}
 
126
 
 
127
# value tag
 
128
class xml_value(etree.ElementBase):
 
129
    def _to_yaml(self):
 
130
 
 
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("'",'""')
 
136
        self.attrib.pop(key)
 
137
        d={}
 
138
        for k,v in self.attrib.iteritems():
 
139
            if k == 'search':
 
140
                v = '"' + v + '"'
 
141
            k='--' + k
 
142
            v=v.replace("'",'""')
 
143
            d[k] = v
 
144
        if d:
 
145
            ls=[[{key:val},dict(d)]]
 
146
        else:
 
147
            ls=[[{key:val}]]
 
148
        return ls
 
149
 
 
150
# data tag
 
151
class data(YamlTag):
 
152
    yaml_tag = u'!context'
 
153
    def __init__(self, noupdate="0"):
 
154
        self.child_tags = {'    noupdate':noupdate}
 
155
    def __repr__(self):
 
156
        return "!!context"
 
157
 
 
158
# Record tag
 
159
class Record(YamlTag):
 
160
    yaml_tag = u'!record'
 
161
class xml_record(XmlTag):
 
162
    tag=Record
 
163
    def _to_yaml(self):
 
164
        child_tags = {}
 
165
        for node in self:
 
166
            if hasattr(node, '_to_yaml'):
 
167
                child_tags.update(node._to_yaml())
 
168
        return Record(attrib=self.attrib, child_tags=child_tags)
 
169
 
 
170
# ir_set tag
 
171
class Ir_Set(YamlTag):
 
172
    yaml_tag = u'!ir_set'
 
173
    def __repr__(self):
 
174
        st = self.yaml_tag
 
175
        return st
 
176
class xml_ir_set(XmlTag):
 
177
    tag=Ir_Set
 
178
    def _to_yaml(self):
 
179
        child_tags = {}
 
180
        for node in self:
 
181
            if hasattr(node, '_to_yaml'):
 
182
                child_tags.update(node._to_yaml())
 
183
        return Ir_Set(attrib=self.attrib, child_tags=child_tags)
 
184
 
 
185
# workflow tag
 
186
class Workflow(YamlTag):
 
187
    yaml_tag = u'!workflow'
 
188
class xml_workflow(XmlTag):
 
189
    tag=Workflow
 
190
 
 
191
# function tag
 
192
class Function(YamlTag):
 
193
    yaml_tag = u'!function'
 
194
class xml_function(XmlTag):
 
195
    tag=Function
 
196
 
 
197
# function tag
 
198
class Assert(YamlTag):
 
199
    yaml_tag = u'!assert'
 
200
class xml_assert(XmlTag):
 
201
    tag=Assert
 
202
 
 
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):
 
207
    tag=MenuItem
 
208
 
 
209
# act_window tag
 
210
class ActWindow(YamlTag):
 
211
    yaml_tag = u'!act_window'
 
212
class xml_act_window(XmlTag):
 
213
    tag=ActWindow
 
214
 
 
215
# report tag
 
216
class Report(YamlTag):
 
217
    yaml_tag = u'!report'
 
218
class xml_report(XmlTag):
 
219
    tag=Report
 
220
 
 
221
# deletes tag
 
222
class Delete(YamlTag):
 
223
    yaml_tag = u'!delete'
 
224
class xml_delete(XmlTag):
 
225
    tag=Delete
 
226
 
 
227
# python tag
 
228
class Python(YamlTag):
 
229
    yaml_tag = u'!python'
 
230
class xml_python(XmlTag):
 
231
    tag=Python
 
232
 
 
233
# context tag
 
234
class Context(YamlTag):
 
235
    yaml_tag = u'!context'
 
236
class xml_context(XmlTag):
 
237
    tag=Context
 
238
 
 
239
# url tag
 
240
class Url(YamlTag):
 
241
    yaml_tag = u'!url'
 
242
class xml_url(XmlTag):
 
243
    tag=Url
 
244
 
 
245
# delete tag
 
246
class Delete(YamlTag):
 
247
    yaml_tag = u'!delete'
 
248
class xml_delete(XmlTag):
 
249
    tag=Delete
 
250
 
 
251
def represent_data(dumper, data):
 
252
        return dumper.represent_mapping(u'tag:yaml.org,2002:map', [('!'+str(data), data.child_tags)])
 
253
 
 
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)
 
263
 
 
264
class MyLookup(etree.CustomElementClassLookup):
 
265
    def lookup(self, node_type, document, namespace, name):
 
266
        if node_type=='element':
 
267
            return {
 
268
                'data': xml_data,
 
269
                'record': xml_record,
 
270
                'field': xml_field,
 
271
                'workflow': xml_workflow,
 
272
                'function': xml_function,
 
273
                'value': xml_value,
 
274
                'assert': xml_assert,
 
275
                'test': xml_test,
 
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,
 
282
                'url': xml_url,
 
283
                'ir_set': xml_ir_set,
 
284
            }.get(name, None)
 
285
        elif node_type=='comment':
 
286
            return None#xml_comment
 
287
        return None
 
288
 
 
289
class xml_parse(object):
 
290
    def __init__(self):
 
291
        self.context = {}
 
292
    def parse(self, fname):
 
293
        parser = etree.XMLParser()
 
294
        parser.setElementClassLookup(MyLookup())
 
295
        result = []
 
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("'",''))
 
303
                else:
 
304
                    result.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True).replace("'",''))
 
305
            self.context.update(data.attrib)
 
306
            for tag in data:
 
307
                if tag.tag == etree.Comment:
 
308
                    result.append(tag)
 
309
                else:
 
310
                    if hasattr(tag, '_to_yaml'):
 
311
                        obj = 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("'",''))
 
314
                        else:
 
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(''):
 
321
                record=str(record)
 
322
                l= record.split("\n")
 
323
                for line in l:
 
324
                    print '#' + str(line)
 
325
                continue
 
326
            record=str(record)
 
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
 
332
            print record
 
333
 
 
334
if __name__=='__main__':
 
335
    import optparse
 
336
    import sys
 
337
    parser = optparse.OptionParser(
 
338
        usage = '%s file.xml' % sys.argv[0])
 
339
    (opt, args) = parser.parse_args()
 
340
    if len(args) != 1:
 
341
        parser.error("incorrect number of arguments")
 
342
    fname = sys.argv[1]
 
343
    p = xml_parse()
 
344
    p.parse(fname)
 
345