~openerp-commiter/openobject-addons/trunk-extra-addons

« back to all changes in this revision

Viewing changes to base_module_doc_rst/wizard/wizard_tech_guide_rst.py

  • Committer: olt at tinyerp
  • Date: 2010-05-19 15:01:40 UTC
  • mfrom: (4548.1.48 addons-extra)
  • Revision ID: olt@tinyerp.com-20100519150140-mz0rdnlavfppfx5p
[MERGE]

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- encoding: utf-8 -*-
2
 
##############################################################################
3
 
#
4
 
#    OpenERP, Open Source Management Solution
5
 
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
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
 
from os.path import join
22
 
import base64
23
 
import tempfile
24
 
import tarfile
25
 
import httplib
26
 
 
27
 
import netsvc
28
 
import wizard
29
 
import pooler
30
 
import os
31
 
import tools
32
 
 
33
 
import base_module_doc_rst
34
 
 
35
 
 
36
 
choose_file_form = '''<?xml version="1.0"?>
37
 
<form string="Create Technical Guide in rst format">
38
 
    <separator string="Technical Guide in rst format" colspan="4"/>
39
 
    <label string="Please choose a file where the Technical Guide will be written." colspan="4"/>
40
 
    <field name="rst_file" />
41
 
    <field name="name" invisible="1"/>
42
 
</form>
43
 
'''
44
 
 
45
 
choose_file_fields = {
46
 
    'rst_file': {'string': 'file', 'type': 'binary', 'required': True, 'readonly': True},
47
 
    'name': {'string': 'filename', 'type': 'char', 'required': True, 'readonly': True},
48
 
}
49
 
 
50
 
class RstDoc(object):
51
 
    def __init__(self, module, objects):
52
 
        self.dico = {
53
 
            'name': module.name,
54
 
            'shortdesc': module.shortdesc,
55
 
            'latest_version': module.latest_version,
56
 
            'website': module.website,
57
 
            'description': self._handle_text(module.description.strip() or 'None'),
58
 
            'report_list': self._handle_list_items(module.reports_by_module),
59
 
            'menu_list': self._handle_list_items(module.menus_by_module),
60
 
            'view_list': self._handle_list_items(module.views_by_module),
61
 
            'depends': module.dependencies_id,
62
 
            'quality_certified': bool(module.certificate) and 'yes' or 'no',
63
 
            'official_module': str(module.certificate)[:2] == '00' and 'yes' or 'no',
64
 
            'author': module.author,
65
 
            'quality_certified_label': self._quality_certified_label(module),
66
 
        }
67
 
        self.objects = objects
68
 
        self.module = module
69
 
 
70
 
    def _quality_certified_label(self, module):
71
 
        label = ""
72
 
        certificate = module.certificate
73
 
        if certificate and len(certificate) > 1:
74
 
            if certificate[:2] == '00':
75
 
                # addons
76
 
                label = "(Official, Quality Certified)"
77
 
            elif certificate[:2] == '01':
78
 
                # extra addons
79
 
                label = "(Quality Certified)"
80
 
 
81
 
        return label
82
 
 
83
 
    def _handle_list_items(self, list_item_as_string):
84
 
        list_item_as_string = list_item_as_string.strip()
85
 
        if list_item_as_string:
86
 
            return [item.replace('*', '\*') for item in list_item_as_string.split('\n')]
87
 
        else:
88
 
            return []
89
 
 
90
 
    def _handle_text(self, txt):
91
 
        lst = ['  %s' % line for line in txt.split('\n')]
92
 
        return '\n'.join(lst)
93
 
 
94
 
    def _get_download_links(self):
95
 
        def _is_connection_status_good(link):
96
 
            server = "openerp.com"
97
 
            status_good = False
98
 
            try:
99
 
                conn = httplib.HTTPConnection(server)
100
 
                conn.request("HEAD", link)
101
 
                res = conn.getresponse()
102
 
                if res.status in (200, ):
103
 
                    status_good = True
104
 
            except (Exception, ), e:
105
 
                logger = netsvc.Logger()
106
 
                msg = "error connecting to server '%s' with link '%s'. Error message: %s" % (server, link, str(e))
107
 
                logger.notifyChannel("base_module_doc_rst", netsvc.LOG_ERROR, msg)
108
 
                status_good = False
109
 
            return status_good
110
 
 
111
 
        versions = ('4.2', '5.0', 'trunk')
112
 
        download_links = []
113
 
        for ver in versions:
114
 
            link = 'http://www.openerp.com/download/modules/%s/%s.zip' % (ver, self.dico['name'])
115
 
            if _is_connection_status_good(link):
116
 
                download_links.append("  * `%s <%s>`_" % (ver, link))
117
 
 
118
 
        if download_links:
119
 
            res = '\n'.join(download_links)
120
 
        else:
121
 
            res = "(No download links available)"
122
 
        return res
123
 
 
124
 
    def _write_header(self):
125
 
        dico = self.dico
126
 
        title = "%s (*%s*)" % (dico['shortdesc'], dico['name'])
127
 
        title_underline = "=" * len(title)
128
 
        dico['title'] = title
129
 
        dico['title_underline'] = title_underline
130
 
        dico['download_links'] = self._get_download_links()
131
 
 
132
 
        sl = [
133
 
            "",
134
 
            ".. module:: %(name)s",
135
 
            "    :synopsis: %(shortdesc)s %(quality_certified_label)s",
136
 
            "    :noindex:",
137
 
            ".. ",
138
 
            "",
139
 
            ".. raw:: html",
140
 
            "",
141
 
            "      <br />",
142
 
            """    <link rel="stylesheet" href="../_static/hide_objects_in_sidebar.css" type="text/css" />""",
143
 
            "",
144
 
            """.. tip:: This module is part of the Open ERP software, the leading Open Source """,
145
 
            """  enterprise management system. If you want to discover Open ERP, check our """,
146
 
            """  `screencasts <http://openerp.tv>`_ or download """,
147
 
            """  `Open ERP <http://openerp.com>`_ directly.""",
148
 
            "",
149
 
            ".. raw:: html",
150
 
            "",
151
 
            """    <div class="js-kit-rating" title="" permalink="" standalone="yes" path="/%s"></div>""" % (dico['name'], ),
152
 
            """    <script src="http://js-kit.com/ratings.js"></script>""",
153
 
            "",
154
 
            "%(title)s",
155
 
            "%(title_underline)s",
156
 
            ":Module: %(name)s",
157
 
            ":Name: %(shortdesc)s",
158
 
            ":Version: %(latest_version)s",
159
 
            ":Author: %(author)s",
160
 
            ":Directory: %(name)s",
161
 
            ":Web: %(website)s",
162
 
            ":Official module: %(official_module)s",
163
 
            ":Quality certified: %(quality_certified)s",
164
 
            "",
165
 
            "Description",
166
 
            "-----------",
167
 
            "",
168
 
            "::",
169
 
            "",
170
 
            "%(description)s",
171
 
            "",
172
 
            "Download links",
173
 
            "--------------",
174
 
            "",
175
 
            "You can download this module as a zip file in the following version:",
176
 
            "",
177
 
            "%(download_links)s",
178
 
            "",
179
 
            ""]
180
 
        return '\n'.join(sl) % (dico)
181
 
 
182
 
    def _write_reports(self):
183
 
        sl = ["",
184
 
              "Reports",
185
 
              "-------"]
186
 
        reports = self.dico['report_list']
187
 
        if reports:
188
 
            for report in reports:
189
 
                if report:
190
 
                    sl.append("")
191
 
                    sl.append(" * %s" % report)
192
 
        else:
193
 
            sl.extend(["", "None", ""])
194
 
 
195
 
        sl.append("")
196
 
        return '\n'.join(sl)
197
 
 
198
 
    def _write_menus(self):
199
 
        sl = ["",
200
 
              "Menus",
201
 
              "-------",
202
 
              ""]
203
 
        menus = self.dico['menu_list']
204
 
        if menus:
205
 
            for menu in menus:
206
 
                if menu:
207
 
                    sl.append(" * %s" % menu)
208
 
        else:
209
 
            sl.extend(["", "None", ""])
210
 
 
211
 
        sl.append("")
212
 
        return '\n'.join(sl)
213
 
 
214
 
    def _write_views(self):
215
 
        sl = ["",
216
 
              "Views",
217
 
              "-----",
218
 
              ""]
219
 
        views = self.dico['view_list']
220
 
        if views:
221
 
            for view in views:
222
 
                if view:
223
 
                    sl.append(" * %s" % view)
224
 
        else:
225
 
            sl.extend(["", "None", ""])
226
 
 
227
 
        sl.append("")
228
 
        return '\n'.join(sl)
229
 
 
230
 
    def _write_depends(self):
231
 
        sl = ["",
232
 
              "Dependencies",
233
 
              "------------",
234
 
              ""]
235
 
        depends = self.dico['depends']
236
 
        if depends:
237
 
            for dependency in depends:
238
 
                sl.append(" * :mod:`%s`" % (dependency.name))
239
 
        else:
240
 
            sl.extend(["", "None", ""])
241
 
        sl.append("")
242
 
        return '\n'.join(sl)
243
 
 
244
 
    def _write_objects(self):
245
 
        def write_field(field_def):
246
 
            if not isinstance(field_def, tuple):
247
 
                logger = netsvc.Logger()
248
 
                msg = "Error on Object %s: field_def: %s [type: %s]" % (obj_name.encode('utf8'), field_def.encode('utf8'), type(field_def))
249
 
                logger.notifyChannel("base_module_doc_rst", netsvc.LOG_ERROR, msg)
250
 
                return ""
251
 
 
252
 
            field_name = field_def[0]
253
 
            field_dict = field_def[1]
254
 
            field_required = field_dict.get('required', '') and ', required'
255
 
            field_readonly = field_dict.get('readonly', '') and ', readonly'
256
 
 
257
 
            field_help_s = field_dict.get('help', '').strip()
258
 
            if field_help_s:
259
 
                field_help_s = "*%s*" % (field_help_s)
260
 
                field_help = '\n'.join(['    %s' % line.strip() for line in field_help_s.split('\n')])
261
 
            else:
262
 
                field_help = ''
263
 
 
264
 
            sl = ["",
265
 
                  ":%s: %s, %s%s%s" % (field_name, field_dict.get('string', 'Unknown'), field_dict['type'], field_required, field_readonly),
266
 
                  "",
267
 
                  field_help,
268
 
                 ]
269
 
            return '\n'.join(sl)
270
 
 
271
 
        sl = ["",
272
 
              "",
273
 
              "Objects",
274
 
              "-------"]
275
 
        if self.objects:
276
 
            for obj in self.objects:
277
 
                obj_name = obj['object'].name
278
 
                obj_model = obj['object'].model
279
 
                title = "Object: %s (%s)" % (obj_name, obj_model)
280
 
                slo = [
281
 
                       "",
282
 
                       title,
283
 
                       '#' * len(title),
284
 
                       "",
285
 
                       #".. index::",
286
 
                       #"  single: %s object" % (obj_name),
287
 
                       #".. ",
288
 
                      ]
289
 
 
290
 
                for field in obj['fields']:
291
 
                    slf = [
292
 
                           "",
293
 
                           write_field(field),
294
 
                           "",
295
 
                           #".. index::",
296
 
                           #"  single: %s field" % (field[0]),
297
 
                           #".. ",
298
 
                           #"",
299
 
                           #"",
300
 
                          ]
301
 
                    slo.extend(slf)
302
 
                sl.extend(slo)
303
 
        else:
304
 
            sl.extend(["", "None", ""])
305
 
 
306
 
        return u'\n'.join([a.decode('utf8') for a in sl])
307
 
 
308
 
    def _write_relationship_graph(self, module_name=False):
309
 
        sl = ["",
310
 
              "Relationship Graph",
311
 
              "------------------",
312
 
              "",
313
 
              ".. figure:: %s_module.png" % (module_name, ),
314
 
              "  :scale: 50",
315
 
              "  :align: center",
316
 
              ""]
317
 
        sl.append("")
318
 
        return '\n'.join(sl)
319
 
 
320
 
    def write(self, module_name=False):
321
 
        s = ''
322
 
        s += self._write_header()
323
 
        s += self._write_depends()
324
 
        s += self._write_reports()
325
 
        s += self._write_menus()
326
 
        s += self._write_views()
327
 
        s += self._write_objects()
328
 
        if module_name:
329
 
            s += self._write_relationship_graph(module_name)
330
 
        return s
331
 
 
332
 
 
333
 
class wizard_tech_guide_rst(wizard.interface):
334
 
 
335
 
 
336
 
    def _generate(self, cr, uid, data, context):
337
 
        pool = pooler.get_pool(cr.dbname)
338
 
        module_model = pool.get('ir.module.module')
339
 
        module_ids = data['ids']
340
 
 
341
 
        module_index = []
342
 
 
343
 
        # create a temporary gzipped tarfile:
344
 
        tgz_tmp_filename = tempfile.mktemp('_rst_module_doc.tgz')
345
 
        try:
346
 
            tarf = tarfile.open(tgz_tmp_filename, 'w:gz')
347
 
 
348
 
            modules = module_model.browse(cr, uid, module_ids)
349
 
            for module in modules:
350
 
                index_dict = {
351
 
                    'name': module.name,
352
 
                    'shortdesc': module.shortdesc,
353
 
                }
354
 
                module_index.append(index_dict)
355
 
 
356
 
                objects = self._get_objects(cr, uid, module)
357
 
                module.test_views = self._get_views(cr, uid, module.id, context=context)
358
 
                rstdoc = RstDoc(module, objects)
359
 
 
360
 
                # Append Relationship Graph on rst
361
 
                graph_mod = False
362
 
                module_name = False
363
 
                if module.file_graph:
364
 
                    graph_mod = base64.decodestring(module.file_graph)
365
 
                else:
366
 
                    module_data = module_model.get_relation_graph(cr, uid, module.name, context=context)
367
 
                    if module_data['module_file']:
368
 
                        graph_mod = base64.decodestring(module_data['module_file'])
369
 
                if graph_mod:
370
 
                    module_name = module.name
371
 
                    try:
372
 
                        tmpdir = tempfile.mkdtemp()
373
 
                        tmp_file_graph = tempfile.NamedTemporaryFile()
374
 
                        tmp_file_graph.write(graph_mod)
375
 
                        tmp_file_graph.file.flush()
376
 
                        tarf.add(tmp_file_graph.name, arcname= module.name + '_module.png')
377
 
                    finally:
378
 
                        tmp_file_graph.close()
379
 
 
380
 
                out = rstdoc.write(module_name)
381
 
                try:
382
 
                    tmp_file = tempfile.NamedTemporaryFile()
383
 
                    tmp_file.write(out.encode('utf8'))
384
 
                    tmp_file.file.flush() # write content to file
385
 
                    tarf.add(tmp_file.name, arcname=module.name + '.rst')
386
 
                finally:
387
 
                    tmp_file.close()
388
 
 
389
 
            # write index file:
390
 
            tmp_file = tempfile.NamedTemporaryFile()
391
 
            out = self._create_index(module_index)
392
 
            tmp_file.write(out.encode('utf8'))
393
 
            tmp_file.file.flush()
394
 
            tarf.add(tmp_file.name, arcname='index.rst')
395
 
        finally:
396
 
            tarf.close()
397
 
 
398
 
        f = open(tgz_tmp_filename, 'rb')
399
 
        out = f.read()
400
 
        f.close()
401
 
 
402
 
        if os.path.exists(tgz_tmp_filename):
403
 
            try:
404
 
                os.unlink(tgz_tmp_filename)
405
 
            except Exception, e:
406
 
                logger = netsvc.Logger()
407
 
                msg = "Temporary file %s could not be deleted. (%s)" % (tgz_tmp_filename, e)
408
 
                logger.notifyChannel("warning", netsvc.LOG_WARNING, msg)
409
 
 
410
 
        return {
411
 
            'rst_file': base64.encodestring(out),
412
 
            'name': 'modules_technical_guide_rst.tgz'
413
 
        }
414
 
 
415
 
    def _get_views(self, cr, uid, module_id, context={}):
416
 
        pool = pooler.get_pool(cr.dbname)
417
 
        module_module_obj = pool.get('ir.module.module')
418
 
        res = {}
419
 
        model_data_obj = pool.get('ir.model.data')
420
 
        view_obj = pool.get('ir.ui.view')
421
 
        report_obj = pool.get('ir.actions.report.xml')
422
 
        menu_obj = pool.get('ir.ui.menu')
423
 
        mlist = module_module_obj.browse(cr, uid, [module_id], context=context)
424
 
        mnames = {}
425
 
        for m in mlist:
426
 
            mnames[m.name] = m.id
427
 
            res[m.id] = {
428
 
                'menus_by_module': [],
429
 
                'reports_by_module': [],
430
 
                'views_by_module': []
431
 
            }
432
 
        view_id = model_data_obj.search(cr, uid, [('module', 'in', mnames.keys()),
433
 
            ('model', 'in', ('ir.ui.view', 'ir.actions.report.xml', 'ir.ui.menu'))])
434
 
        for data_id in model_data_obj.browse(cr, uid, view_id, context):
435
 
            # We use try except, because views or menus may not exist
436
 
            try:
437
 
                key = data_id['model']
438
 
                if key == 'ir.ui.view':
439
 
                    v = view_obj.browse(cr, uid, data_id.res_id)
440
 
                    v_dict = {
441
 
                        'name': v.name,
442
 
                        'inherit': v.inherit_id,
443
 
                        'type': v.type}
444
 
                    res[mnames[data_id.module]]['views_by_module'].append(v_dict)
445
 
                elif key == 'ir.actions.report.xml':
446
 
                    res[mnames[data_id.module]]['reports_by_module'].append(report_obj.browse(cr, uid, data_id.res_id).name)
447
 
                elif key == 'ir.ui.menu':
448
 
                    res[mnames[data_id.module]]['menus_by_module'].append(menu_obj.browse(cr, uid, data_id.res_id).complete_name)
449
 
            except (KeyError, ):
450
 
                pass
451
 
        return res
452
 
 
453
 
    def _create_index(self, module_index):
454
 
        sl = ["",
455
 
              ".. _module-technical-guide-link:",
456
 
              "",
457
 
              "Module Technical Guide: Introspection report on objects",
458
 
              "=======================================================",
459
 
              "",
460
 
              ".. toctree::",
461
 
              "    :maxdepth: 1",
462
 
              "",
463
 
              ]
464
 
        for mod in module_index:
465
 
            sl.append("    %s" % mod['name'])
466
 
        sl.append("")
467
 
        return '\n'.join(sl)
468
 
 
469
 
    def _get_objects(self, cr, uid, module):
470
 
        res = []
471
 
        objects = self._object_find(cr, uid, module)
472
 
        for obj in objects:
473
 
            fields = self._fields_find(cr, uid, obj.model)
474
 
            dico = {
475
 
                'object': obj,
476
 
                'fields': fields
477
 
            }
478
 
            res.append(dico)
479
 
        return res
480
 
 
481
 
    def _object_find(self, cr, uid, module):
482
 
        pool = pooler.get_pool(cr.dbname)
483
 
        ids2 = pool.get('ir.model.data').search(cr, uid, [('module', '=', module.name), ('model', '=', 'ir.model')])
484
 
        ids = []
485
 
        for mod in pool.get('ir.model.data').browse(cr, uid, ids2):
486
 
            ids.append(mod.res_id)
487
 
        modobj = pool.get('ir.model')
488
 
        return modobj.browse(cr, uid, ids)
489
 
 
490
 
    def _fields_find(self, cr, uid, obj):
491
 
        pool = pooler.get_pool(cr.dbname)
492
 
        modobj = pool.get(obj)
493
 
        if modobj:
494
 
            res = modobj.fields_get(cr, uid).items()
495
 
            return res
496
 
        else:
497
 
            logger = netsvc.Logger()
498
 
            msg = "Object %s not found" % (obj)
499
 
            logger.notifyChannel("base_module_doc_rst", netsvc.LOG_ERROR, msg)
500
 
            return ""
501
 
 
502
 
##     def _object_doc(self, cr, uid, obj):
503
 
##         pool = pooler.get_pool(cr.dbname)
504
 
##         modobj = pool.get(obj)
505
 
##         return modobj.__doc__
506
 
 
507
 
    states = {
508
 
        'init': {
509
 
            'actions': [_generate],
510
 
            'result': {
511
 
                'type': 'form',
512
 
                'arch': choose_file_form,
513
 
                'fields': choose_file_fields,
514
 
                'state': [
515
 
                    ('end', 'Close', 'gtk-close'),
516
 
                ]
517
 
            }
518
 
        },
519
 
    }
520
 
 
521
 
wizard_tech_guide_rst('tech.guide.rst')
522