# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # # 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 . # ############################################################################## import os import base64 import openerp.modules.registry from openerp import models, fields class Module(models.Model): _inherit = 'ir.module.module' _description = 'Module With Relationship Graph' file_graph = fields.Binary('Relationship Graph') def _get_graphical_representation( self, cr, uid, model_ids, level=1, context=None ): obj_model = self.pool.get('ir.model') if level == 0: return tuple() relation = [] for id in model_ids: model_data = obj_model.browse(cr, uid, id, context=context) for field in ( f for f in model_data.field_id if f.ttype in ( 'many2many', 'many2one', 'one2many' ) ): relation.append( ( model_data.model, field.name, field.ttype, field.relation, field.field_description ) ) new_model_ids = obj_model.search( cr, uid, [('model', '=', field.relation)], context=context ) if new_model_ids: model = obj_model.read( cr, uid, new_model_ids, ['id', 'name'], context=context )[0] relation.extend( self._get_graphical_representation( cr, uid, model['id'], level - 1 ) ) return tuple(relation) def _get_structure(self, relations, main_element): res = {} for rel in relations: # if we have to display the string along with field name, # then uncomment the first line n comment the second line res.setdefault(rel[0], set()).add(rel[1]) res.setdefault(rel[3], set()) val = [] for obj, fieldsx in res.items(): val.append('"%s" [%s label="{%s|%s}"];' % ( obj, obj in main_element and 'fillcolor=yellow, style="filled,rounded"' or "", obj, "|".join(["<%s> %s" % (fn, fn) for fn in fieldsx]) )) return "\n".join(val) def _get_arrow(self, field_type='many2one'): return { 'many2one': ( 'arrowtail="none" arrowhead="normal" color="red" label="m2o"' ), 'many2many': ( 'arrowtail="crow" arrowhead="crow" color="green" label="m2m"' ), 'one2many': ( 'arrowtail="none" arrowhead="crow" color="blue" label="o2m"' ), }[field_type] def get_graphical_representation(self, cr, uid, model_ids, context=None): obj_model = self.pool.get('ir.model') if context is None: context = {} res = {} models = [] for obj in obj_model.browse(cr, uid, model_ids, context=context): models.append(obj.model) relations = set( self._get_graphical_representation( cr, uid, model_ids, context.get('level', 1) ) ) res[obj.model] = ( "digraph G {\nnode [style=rounded, shape=record];\n%s\n%s }" % ( self._get_structure(relations, models), ''.join('"%s":%s -> "%s":id:n [%s]; // %s\n' % ( m, fn, fr, self._get_arrow(ft), ft ) for m, fn, ft, fr, fl in relations), ) ) return res def _get_module_objects(self, cr, uid, module, context=None): obj_model = self.pool.get('ir.model') obj_mod_data = self.pool.get('ir.model.data') obj_ids = [] model_data_ids = obj_mod_data.search( cr, uid, [('module', '=', module), ('model', '=', 'ir.model')], context=context ) model_ids = [] for mod in obj_mod_data.browse( cr, uid, model_data_ids, context=context ): model_ids.append(mod.res_id) models = obj_model.browse(cr, uid, model_ids, context=context) map(lambda x: obj_ids.append(x.id), models) return obj_ids def get_relation_graph(self, cr, uid, module_name, context=None): if context is None: context = {} object_ids = self._get_module_objects( cr, uid, module_name, context=context ) if not object_ids: return {'module_file': False} # context.update({'level': 1}) dots = self.get_graphical_representation( cr, uid, object_ids, context=context ) # todo: use os.realpath file_path = openerp.modules.module.get_module_path( 'base_module_doc_rst' ) path_png = file_path + "/module.png" for key, val in dots.items(): path_dotfile = file_path + "/%s.dot" % (key,) fp = file(path_dotfile, "w") fp.write(val) fp.close() os.popen( 'dot -Tpng' + ' ' + path_dotfile + ' ' + '-o' + ' ' + path_png ) fp = file(path_png, "r") x = fp.read() fp.close() os.popen('rm ' + path_dotfile + ' ' + path_png) return {'module_file': base64.encodestring(x)}