|
|
# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ##############################################################################
from openerp.osv import orm, fields from openerp import netsvc
import base64 import tempfile import tarfile import httplib import os
class RstDoc(object): def __init__(self, module, objects): self.dico = { 'name': module.name, 'shortdesc': module.shortdesc, 'latest_version': module.latest_version, 'website': module.website, 'description': self._handle_text( module.description.strip() or 'None' ), 'report_list': self._handle_list_items(module.reports_by_module), 'menu_list': self._handle_list_items(module.menus_by_module), 'view_list': self._handle_list_items(module.views_by_module), 'depends': module.dependencies_id, 'author': module.author, } self.objects = objects self.module = module
def _handle_list_items(self, list_item_as_string): list_item_as_string = list_item_as_string.strip() if list_item_as_string: return [ item.replace("*", r"\*") for item in list_item_as_string.split('\n') ] else: return []
def _handle_text(self, txt): lst = [' %s' % line for line in txt.split('\n')] return '\n'.join(lst)
def _get_download_links(self): def _is_connection_status_good(link): server = "openerp.com" status_good = False try: conn = httplib.HTTPConnection(server) conn.request("HEAD", link) res = conn.getresponse() if res.status in (200, ): status_good = True except (Exception, ), e: logger = netsvc.Logger() msg = """
error connecting to server '%s' with link '%s'. Error message: %s """ % (server, link, str(e))
logger.notifyChannel( "base_module_doc_rst", netsvc.LOG_ERROR, msg ) status_good = False return status_good
versions = ('7.0', '8.0', 'master') download_links = [] for ver in versions: link = 'https://apps.odoo.com/loempia/download/%s/%s.zip' % ( ver, self.dico['name'] ) if _is_connection_status_good(link): download_links.append(" * `%s <%s>`_" % (ver, link))
if download_links: res = '\n'.join(download_links) else: res = "(No download links available)" return res
def _write_header(self): dico = self.dico title = "%s (*%s*)" % (dico['shortdesc'], dico['name']) title_underline = "=" * len(title) dico['title'] = title dico['title_underline'] = title_underline dico['download_links'] = self._get_download_links()
sl = [ "", ".. module:: %(name)s", " :synopsis: %(shortdesc)s", " :noindex:", ".. ", "", ".. raw:: html", "", " <br />", """
<link rel="stylesheet" href="../_static/hide_objects_in_sidebar.css" type="text/css" /> """,
"", "", ".. raw:: html", "", """
<div class="js-kit-rating" title="" permalink="" standalone="yes" path="/%s"></div> """ % (dico['name'],),
""" <script src="http://js-kit.com/ratings.js"></script>""", "", "%(title)s", "%(title_underline)s", ":Module: %(name)s", ":Name: %(shortdesc)s", ":Version: %(latest_version)s", ":Author: %(author)s", ":Directory: %(name)s", ":Web: %(website)s", "", "Description", "-----------", "", "::", "", "%(description)s", "", "Download links", "--------------", "", "You can download this module as a zip file in following version:", "", "%(download_links)s", "", ""] return '\n'.join(sl) % (dico)
def _write_reports(self): sl = ["", "Reports", "-------"] reports = self.dico['report_list'] if reports: for report in reports: if report: sl.append("") sl.append(" * %s" % report) else: sl.extend(["", "None", ""])
sl.append("") return '\n'.join(sl)
def _write_menus(self): sl = ["", "Menus", "-------", ""] menus = self.dico['menu_list'] if menus: for menu in menus: if menu: sl.append(" * %s" % menu) else: sl.extend(["", "None", ""])
sl.append("") return '\n'.join(sl)
def _write_views(self): sl = ["", "Views", "-----", ""] views = self.dico['view_list'] if views: for view in views: if view: sl.append(" * %s" % view) else: sl.extend(["", "None", ""])
sl.append("") return '\n'.join(sl)
def _write_depends(self): sl = ["", "Dependencies", "------------", ""] depends = self.dico['depends'] if depends: for dependency in depends: sl.append(" * :mod:`%s`" % (dependency.name)) else: sl.extend(["", "None", ""]) sl.append("") return '\n'.join(sl)
def _write_objects(self): def write_field(field_def): if not isinstance(field_def, tuple): logger = netsvc.Logger() msg = "Error on Object %s: field_def: %s [type: %s]" % ( obj_name.encode('utf8'), field_def.encode('utf8'), type(field_def) ) logger.notifyChannel( "base_module_doc_rst", netsvc.LOG_ERROR, msg ) return ""
field_name = field_def[0] field_dict = field_def[1] field_required = field_dict.get('required', '') and ', required' field_readonly = field_dict.get('readonly', '') and ', readonly' field_help_s = field_dict.get('help', '') if field_help_s: field_help_s = "*%s*" % (field_help_s) field_help = '\n'.join( [ ' %s' % line.strip() for line in field_help_s.split('\n') ] ) else: field_help = ''
sl = [ "", ":%s: %s, %s%s%s" % (field_name, field_dict.get('string', 'Unknown'), field_dict['type'], field_required, field_readonly), "", field_help, ] return '\n'.join(sl)
sl = ["", "", "Objects", "-------"] if self.objects: for obj in self.objects: obj_name = obj['object'].name obj_model = obj['object'].model title = "Object: %s (%s)" % (obj_name, obj_model) slo = [ "", title, '#' * len(title), "", ]
for field in obj['fields']: slf = [ "", write_field(field), "", ] slo.extend(slf) sl.extend(slo) else: sl.extend(["", "None", ""])
return u'\n'.join([a.decode('utf8') for a in sl])
def _write_relationship_graph(self, module_name=False): sl = ["", "Relationship Graph", "------------------", "", ".. figure:: %s_module.png" % (module_name, ), " :scale: 50", " :align: center", ""] sl.append("") return '\n'.join(sl)
def write(self, module_name=False): s = '' s += self._write_header() s += self._write_depends() s += self._write_reports() s += self._write_menus() s += self._write_views() s += self._write_objects() if module_name: s += self._write_relationship_graph(module_name) return s
class WizardTechGuideRst(orm.TransientModel): _name = "tech.guide.rst" _columns = { 'rst_file': fields.binary('File', required=True, readonly=True), }
def _generate(self, cr, uid, context): module_model = self.pool.get('ir.module.module') module_ids = context['active_ids']
module_index = []
# create a temporary gzipped tarfile: tgz_tmp_filename = tempfile.mktemp('_rst_module_doc.tgz') try: tarf = tarfile.open(tgz_tmp_filename, 'w:gz')
modules = module_model.browse(cr, uid, module_ids) for module in modules: index_dict = { 'name': module.name, 'shortdesc': module.shortdesc, } module_index.append(index_dict)
objects = self._get_objects(cr, uid, module) module.test_views = self._get_views( cr, uid, module.id, context=context ) rstdoc = RstDoc(module, objects)
# Append Relationship Graph on rst graph_mod = False module_name = False if module.file_graph: graph_mod = base64.decodestring(module.file_graph) else: module_data = module_model.get_relation_graph( cr, uid, module.name, context=context ) if module_data['module_file']: graph_mod = base64.decodestring( module_data['module_file'] ) if graph_mod: module_name = module.name try: tmp_file_graph = tempfile.NamedTemporaryFile() tmp_file_graph.write(graph_mod) tmp_file_graph.file.flush() tarf.add( tmp_file_graph.name, arcname=module.name + '_module.png' ) finally: tmp_file_graph.close()
out = rstdoc.write(module_name) try: tmp_file = tempfile.NamedTemporaryFile() tmp_file.write(out.encode('utf8')) tmp_file.file.flush() # write content to file tarf.add(tmp_file.name, arcname=module.name + '.rst') finally: tmp_file.close()
# write index file: tmp_file = tempfile.NamedTemporaryFile() out = self._create_index(module_index) tmp_file.write(out.encode('utf8')) tmp_file.file.flush() tarf.add(tmp_file.name, arcname='index.rst') finally: tarf.close()
f = open(tgz_tmp_filename, 'rb') out = f.read() f.close()
if os.path.exists(tgz_tmp_filename): try: os.unlink(tgz_tmp_filename) except Exception, e: logger = netsvc.Logger() msg = "Temporary file %s could not be deleted. (%s)" % ( tgz_tmp_filename, e ) logger.notifyChannel("warning", netsvc.LOG_WARNING, msg)
return base64.encodestring(out)
def _get_views(self, cr, uid, module_id, context=None): module_module_obj = self.pool.get('ir.module.module') model_data_obj = self.pool.get('ir.model.data') view_obj = self.pool.get('ir.ui.view') report_obj = self.pool.get('ir.actions.report.xml') menu_obj = self.pool.get('ir.ui.menu') res = {} mlist = module_module_obj.browse(cr, uid, [module_id], context=context) mnames = {} for m in mlist: mnames[m.name] = m.id res[m.id] = { 'menus_by_module': [], 'reports_by_module': [], 'views_by_module': [] } view_id = model_data_obj.search( cr, uid, [ ('module', 'in', mnames.keys()), ('model', 'in', ( 'ir.ui.view', 'ir.actions.report.xml', 'ir.ui.menu' )) ] ) for data_id in model_data_obj.browse(cr, uid, view_id, context): # We use try except, because views or menus may not exist try: key = data_id['model'] if key == 'ir.ui.view': v = view_obj.browse(cr, uid, data_id.res_id) v_dict = { 'name': v.name, 'inherit': v.inherit_id, 'type': v.type} res[mnames[data_id.module]]['views_by_module'].append( v_dict ) elif key == 'ir.actions.report.xml': res[mnames[data_id.module]]['reports_by_module'].append( report_obj.browse(cr, uid, data_id.res_id).name ) elif key == 'ir.ui.menu': res[mnames[data_id.module]]['menus_by_module'].append( menu_obj.browse(cr, uid, data_id.res_id).complete_name ) except (KeyError, ): pass return res
def _create_index(self, module_index): sl = ["", ".. _module-technical-guide-link:", "", "Module Technical Guide: Introspection report on objects", "=======================================================", "", ".. toctree::", " :maxdepth: 1", "", ] for mod in module_index: sl.append(" %s" % mod['name']) sl.append("") return '\n'.join(sl)
def _get_objects(self, cr, uid, module): res = [] objects = self._object_find(cr, uid, module) for obj in objects: fields = self._fields_find(cr, uid, obj.model) dico = { 'object': obj, 'fields': fields } res.append(dico) return res
def _object_find(self, cr, uid, module): ir_model_data = self.pool.get('ir.model.data') ids2 = ir_model_data.search( cr, uid, [('module', '=', module.name), ('model', '=', 'ir.model')] ) ids = [] for mod in ir_model_data.browse(cr, uid, ids2): ids.append(mod.res_id) return self.pool.get('ir.model').browse(cr, uid, ids)
def _fields_find(self, cr, uid, obj): modobj = self.pool.get(obj) if modobj: res = modobj.fields_get(cr, uid).items() return res else: logger = netsvc.Logger() msg = "Object %s not found" % (obj) logger.notifyChannel("base_module_doc_rst", netsvc.LOG_ERROR, msg) return ""
_defaults = { 'rst_file': _generate, }
|