~clearcorp-drivers/aeroo/7.0-ccorp

« back to all changes in this revision

Viewing changes to report_aeroo/translate.py

  • Committer: root
  • Date: 2013-02-01 16:11:17 UTC
  • Revision ID: root@erp.kndati.lv-20130201161117-1n3261woqk45mw5c
1.1.0

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-2009 Tiny SPRL (<http://tiny.be>).
 
6
#    Copyright (c) 2009-2011 Alistek Ltd (http://www.alistek.com).
 
7
#
 
8
#    This program is free software: you can redistribute it and/or modify
 
9
#    it under the terms of the GNU Affero General Public License as
 
10
#    published by the Free Software Foundation, either version 3 of the
 
11
#    License, or (at your option) any later version.
 
12
#
 
13
#    This program is distributed in the hope that it will be useful,
 
14
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
#    GNU Affero General Public License for more details.
 
17
#
 
18
#    You should have received a copy of the GNU Affero General Public License
 
19
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
#
 
21
##############################################################################
 
22
 
 
23
import netsvc
 
24
import os
 
25
import logging
 
26
import pooler
 
27
import re
 
28
import tools
 
29
from tools.translate import trans_parse_rml, trans_parse_xsl, trans_parse_view
 
30
import itertools
 
31
import fnmatch
 
32
from os.path import join
 
33
from lxml import etree
 
34
from tools.misc import UpdateableStr
 
35
 
 
36
def extend_trans_generate(lang, modules, cr):
 
37
    logger = logging.getLogger('i18n')
 
38
    dbname = cr.dbname
 
39
 
 
40
    pool = pooler.get_pool(dbname)
 
41
    trans_obj = pool.get('ir.translation')
 
42
    model_data_obj = pool.get('ir.model.data')
 
43
    uid = 1
 
44
    l = pool.obj_list()
 
45
    l.sort()
 
46
 
 
47
    query = 'SELECT name, model, res_id, module'    \
 
48
            '  FROM ir_model_data'
 
49
 
 
50
    query_models = """SELECT m.id, m.model, imd.module
 
51
            FROM ir_model AS m, ir_model_data AS imd
 
52
            WHERE m.id = imd.res_id AND imd.model = 'ir.model' """
 
53
 
 
54
    if 'all_installed' in modules:
 
55
        query += ' WHERE module IN ( SELECT name FROM ir_module_module WHERE state = \'installed\') '
 
56
        query_models += " AND imd.module in ( SELECT name FROM ir_module_module WHERE state = 'installed') "
 
57
    query_param = None
 
58
    if 'all' not in modules:
 
59
        query += ' WHERE module IN %s'
 
60
        query_models += ' AND imd.module in %s'
 
61
        query_param = (tuple(modules),)
 
62
    query += ' ORDER BY module, model, name'
 
63
    query_models += ' ORDER BY module, model'
 
64
 
 
65
    cr.execute(query, query_param)
 
66
 
 
67
    _to_translate = []
 
68
    def push_translation(module, type, name, id, source):
 
69
        tuple = (module, source, name, id, type)
 
70
        if source and tuple not in _to_translate:
 
71
            _to_translate.append(tuple)
 
72
 
 
73
    def encode(s):
 
74
        if isinstance(s, unicode):
 
75
            return s.encode('utf8')
 
76
        return s
 
77
 
 
78
    for (xml_name,model,res_id,module) in cr.fetchall():
 
79
        module = encode(module)
 
80
        model = encode(model)
 
81
        xml_name = "%s.%s" % (module, encode(xml_name))
 
82
 
 
83
        if not pool.get(model):
 
84
            logger.error("Unable to find object %r", model)
 
85
            continue
 
86
 
 
87
        exists = pool.get(model).exists(cr, uid, res_id)
 
88
        if not exists:
 
89
            logger.warning("Unable to find object %r with id %d", model, res_id)
 
90
            continue
 
91
        obj = pool.get(model).browse(cr, uid, res_id)
 
92
 
 
93
        if model=='ir.ui.view':
 
94
            d = etree.XML(encode(obj.arch))
 
95
            for t in trans_parse_view(d):
 
96
                push_translation(module, 'view', encode(obj.model), 0, t)
 
97
        elif model=='ir.actions.wizard':
 
98
            service_name = 'wizard.'+encode(obj.wiz_name)
 
99
            if netsvc.Service._services.get(service_name):
 
100
                obj2 = netsvc.Service._services[service_name]
 
101
                for state_name, state_def in obj2.states.iteritems():
 
102
                    if 'result' in state_def:
 
103
                        result = state_def['result']
 
104
                        if result['type'] != 'form':
 
105
                            continue
 
106
                        name = "%s,%s" % (encode(obj.wiz_name), state_name)
 
107
 
 
108
                        def_params = {
 
109
                            'string': ('wizard_field', lambda s: [encode(s)]),
 
110
                            'selection': ('selection', lambda s: [encode(e[1]) for e in ((not callable(s)) and s or [])]),
 
111
                            'help': ('help', lambda s: [encode(s)]),
 
112
                        }
 
113
 
 
114
                        # export fields
 
115
                        if not result.has_key('fields'):
 
116
                            logger.warning("res has no fields: %r", result)
 
117
                            continue
 
118
                        for field_name, field_def in result['fields'].iteritems():
 
119
                            res_name = name + ',' + field_name
 
120
 
 
121
                            for fn in def_params:
 
122
                                if fn in field_def:
 
123
                                    transtype, modifier = def_params[fn]
 
124
                                    for val in modifier(field_def[fn]):
 
125
                                        push_translation(module, transtype, res_name, 0, val)
 
126
 
 
127
                        # export arch
 
128
                        arch = result['arch']
 
129
                        if arch and not isinstance(arch, UpdateableStr):
 
130
                            d = etree.XML(arch)
 
131
                            for t in trans_parse_view(d):
 
132
                                push_translation(module, 'wizard_view', name, 0, t)
 
133
 
 
134
                        # export button labels
 
135
                        for but_args in result['state']:
 
136
                            button_name = but_args[0]
 
137
                            button_label = but_args[1]
 
138
                            res_name = name + ',' + button_name
 
139
                            push_translation(module, 'wizard_button', res_name, 0, button_label)
 
140
 
 
141
        elif model=='ir.model.fields':
 
142
            try:
 
143
                field_name = encode(obj.name)
 
144
            except AttributeError, exc:
 
145
                logger.error("name error in %s: %s", xml_name, str(exc))
 
146
                continue
 
147
            objmodel = pool.get(obj.model)
 
148
            if not objmodel or not field_name in objmodel._columns:
 
149
                continue
 
150
            field_def = objmodel._columns[field_name]
 
151
 
 
152
            name = "%s,%s" % (encode(obj.model), field_name)
 
153
            push_translation(module, 'field', name, 0, encode(field_def.string))
 
154
 
 
155
            if field_def.help:
 
156
                push_translation(module, 'help', name, 0, encode(field_def.help))
 
157
 
 
158
            if field_def.translate:
 
159
                ids = objmodel.search(cr, uid, [])
 
160
                obj_values = objmodel.read(cr, uid, ids, [field_name])
 
161
                for obj_value in obj_values:
 
162
                    res_id = obj_value['id']
 
163
                    if obj.name in ('ir.model', 'ir.ui.menu'):
 
164
                        res_id = 0
 
165
                    model_data_ids = model_data_obj.search(cr, uid, [
 
166
                        ('model', '=', model),
 
167
                        ('res_id', '=', res_id),
 
168
                        ])
 
169
                    if not model_data_ids:
 
170
                        push_translation(module, 'model', name, 0, encode(obj_value[field_name]))
 
171
 
 
172
            if hasattr(field_def, 'selection') and isinstance(field_def.selection, (list, tuple)):
 
173
                for dummy, val in field_def.selection:
 
174
                    push_translation(module, 'selection', name, 0, encode(val))
 
175
 
 
176
        elif model=='ir.actions.report.xml':
 
177
            name = encode(obj.report_name)
 
178
            fname = ""
 
179
            ##### Changes for Aeroo ######
 
180
            if obj.report_type == 'aeroo':
 
181
                trans_ids = trans_obj.search(cr, uid, [('type', '=', 'report'),('res_id', '=', obj.id)])
 
182
                for t in trans_obj.read(cr, uid, trans_ids, ['name','src']):
 
183
                    push_translation(module, "report", t['name'], xml_name, t['src'])
 
184
            ##############################
 
185
            else:
 
186
                if obj.report_rml:
 
187
                    fname = obj.report_rml
 
188
                    parse_func = trans_parse_rml
 
189
                    report_type = "report"
 
190
                elif obj.report_xsl:
 
191
                    fname = obj.report_xsl
 
192
                    parse_func = trans_parse_xsl
 
193
                    report_type = "xsl"
 
194
                if fname and obj.report_type in ('pdf', 'xsl'):
 
195
                    try:
 
196
                        report_file = tools.file_open(fname)
 
197
                        try:
 
198
                            d = etree.parse(report_file)
 
199
                            for t in parse_func(d.iter()):
 
200
                                push_translation(module, report_type, name, 0, t)
 
201
                        finally:
 
202
                            report_file.close()
 
203
                    except (IOError, etree.XMLSyntaxError):
 
204
                        logger.exception("couldn't export translation for report %s %s %s", name, report_type, fname)
 
205
 
 
206
        for field_name,field_def in obj._table._columns.items():
 
207
            if field_def.translate:
 
208
                name = model + "," + field_name
 
209
                try:
 
210
                    trad = getattr(obj, field_name) or ''
 
211
                except:
 
212
                    trad = ''
 
213
                push_translation(module, 'model', name, xml_name, encode(trad))
 
214
 
 
215
        # End of data for ir.model.data query results
 
216
 
 
217
    cr.execute(query_models, query_param)
 
218
 
 
219
    def push_constraint_msg(module, term_type, model, msg):
 
220
        # Check presence of __call__ directly instead of using
 
221
        # callable() because it will be deprecated as of Python 3.0
 
222
        if not hasattr(msg, '__call__'):
 
223
            push_translation(module, term_type, model, 0, encode(msg))
 
224
 
 
225
    for (model_id, model, module) in cr.fetchall():
 
226
        module = encode(module)
 
227
        model = encode(model)
 
228
 
 
229
        model_obj = pool.get(model)
 
230
 
 
231
        if not model_obj:
 
232
            logging.getLogger("i18n").error("Unable to find object %r", model)
 
233
            continue
 
234
 
 
235
        for constraint in getattr(model_obj, '_constraints', []):
 
236
            push_constraint_msg(module, 'constraint', model, constraint[1])
 
237
 
 
238
        for constraint in getattr(model_obj, '_sql_constraints', []):
 
239
            push_constraint_msg(module, 'sql_constraint', model, constraint[2])
 
240
 
 
241
    # parse source code for _() calls
 
242
    def get_module_from_path(path, mod_paths=None):
 
243
        if not mod_paths:
 
244
            # First, construct a list of possible paths
 
245
            def_path = os.path.abspath(os.path.join(tools.config['root_path'], 'addons'))     # default addons path (base)
 
246
            ad_paths= map(lambda m: os.path.abspath(m.strip()),tools.config['addons_path'].split(','))
 
247
            mod_paths=[def_path]
 
248
            for adp in ad_paths:
 
249
                mod_paths.append(adp)
 
250
                if not os.path.isabs(adp):
 
251
                    mod_paths.append(adp)
 
252
                elif adp.startswith(def_path):
 
253
                    mod_paths.append(adp[len(def_path)+1:])
 
254
        for mp in mod_paths:
 
255
            if path.startswith(mp) and (os.path.dirname(path) != mp):
 
256
                path = path[len(mp)+1:]
 
257
                return path.split(os.path.sep)[0]
 
258
        return 'base'   # files that are not in a module are considered as being in 'base' module
 
259
 
 
260
    modobj = pool.get('ir.module.module')
 
261
    installed_modids = modobj.search(cr, uid, [('state', '=', 'installed')])
 
262
    installed_modules = map(lambda m: m['name'], modobj.read(cr, uid, installed_modids, ['name']))
 
263
 
 
264
    root_path = os.path.join(tools.config['root_path'], 'addons')
 
265
 
 
266
    apaths = map(os.path.abspath, map(str.strip, tools.config['addons_path'].split(',')))
 
267
    if root_path in apaths:
 
268
        path_list = apaths
 
269
    else :
 
270
        path_list = [root_path,] + apaths
 
271
 
 
272
    # Also scan these non-addon paths
 
273
    for bin_path in ['osv', 'report' ]:
 
274
        path_list.append(os.path.join(tools.config['root_path'], bin_path))
 
275
 
 
276
    logger.debug("Scanning modules at paths: ", path_list)
 
277
 
 
278
    mod_paths = []
 
279
    join_dquotes = re.compile(r'([^\\])"[\s\\]*"', re.DOTALL)
 
280
    join_quotes = re.compile(r'([^\\])\'[\s\\]*\'', re.DOTALL)
 
281
    re_dquotes = re.compile(r'[^a-zA-Z0-9_]_\([\s]*"(.+?)"[\s]*?\)', re.DOTALL)
 
282
    re_quotes = re.compile(r'[^a-zA-Z0-9_]_\([\s]*\'(.+?)\'[\s]*?\)', re.DOTALL)
 
283
 
 
284
    def export_code_terms_from_file(fname, path, root, terms_type):
 
285
        fabsolutepath = join(root, fname)
 
286
        frelativepath = fabsolutepath[len(path):]
 
287
        module = get_module_from_path(fabsolutepath, mod_paths=mod_paths)
 
288
        is_mod_installed = module in installed_modules
 
289
        if (('all' in modules) or (module in modules)) and is_mod_installed:
 
290
            logger.debug("Scanning code of %s at module: %s", frelativepath, module)
 
291
            src_file = tools.file_open(fabsolutepath, subdir='')
 
292
            try:
 
293
                code_string = src_file.read()
 
294
            finally:
 
295
                src_file.close()
 
296
            if module in installed_modules:
 
297
                frelativepath = str("addons" + frelativepath)
 
298
            ite = re_dquotes.finditer(code_string)
 
299
            code_offset = 0
 
300
            code_line = 1
 
301
            for i in ite:
 
302
                src = i.group(1)
 
303
                if src.startswith('""'):
 
304
                    assert src.endswith('""'), "Incorrect usage of _(..) function (should contain only literal strings!) in file %s near: %s" % (frelativepath, src[:30])
 
305
                    src = src[2:-2]
 
306
                else:
 
307
                    src = join_dquotes.sub(r'\1', src)
 
308
                # try to count the lines from the last pos to our place:
 
309
                code_line += code_string[code_offset:i.start(1)].count('\n')
 
310
                # now, since we did a binary read of a python source file, we
 
311
                # have to expand pythonic escapes like the interpreter does.
 
312
                src = src.decode('string_escape')
 
313
                push_translation(module, terms_type, frelativepath, code_line, encode(src))
 
314
                code_line += i.group(1).count('\n')
 
315
                code_offset = i.end() # we have counted newlines up to the match end
 
316
 
 
317
            ite = re_quotes.finditer(code_string)
 
318
            code_offset = 0 #reset counters
 
319
            code_line = 1
 
320
            for i in ite:
 
321
                src = i.group(1)
 
322
                if src.startswith("''"):
 
323
                    assert src.endswith("''"), "Incorrect usage of _(..) function (should contain only literal strings!) in file %s near: %s" % (frelativepath, src[:30])
 
324
                    src = src[2:-2]
 
325
                else:
 
326
                    src = join_quotes.sub(r'\1', src)
 
327
                code_line += code_string[code_offset:i.start(1)].count('\n')
 
328
                src = src.decode('string_escape')
 
329
                push_translation(module, terms_type, frelativepath, code_line, encode(src))
 
330
                code_line += i.group(1).count('\n')
 
331
                code_offset = i.end() # we have counted newlines up to the match end
 
332
 
 
333
    for path in path_list:
 
334
        logger.debug("Scanning files of modules at %s", path)
 
335
        for root, dummy, files in tools.osutil.walksymlinks(path):
 
336
            for fname in itertools.chain(fnmatch.filter(files, '*.py')):
 
337
                export_code_terms_from_file(fname, path, root, 'code')
 
338
            for fname in itertools.chain(fnmatch.filter(files, '*.mako')):
 
339
                export_code_terms_from_file(fname, path, root, 'report')
 
340
 
 
341
 
 
342
    out = [["module","type","name","res_id","src","value"]] # header
 
343
    _to_translate.sort()
 
344
    # translate strings marked as to be translated
 
345
    for module, source, name, id, type in _to_translate:
 
346
        trans = trans_obj._get_source(cr, uid, name, type, lang, source)
 
347
        out.append([module, type, name, id, source, encode(trans) or ''])
 
348
 
 
349
    return out
 
350
 
 
351
import sys
 
352
sys.modules['tools.translate'].trans_generate = extend_trans_generate
 
353