You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
6.4 KiB

  1. # -*- coding: 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. import os
  22. import base64
  23. import openerp.modules.registry
  24. from openerp import models, fields
  25. class Module(models.Model):
  26. _inherit = 'ir.module.module'
  27. _description = 'Module With Relationship Graph'
  28. file_graph = fields.Binary('Relationship Graph')
  29. def _get_graphical_representation(
  30. self, cr, uid, model_ids, level=1, context=None
  31. ):
  32. obj_model = self.pool.get('ir.model')
  33. if level == 0:
  34. return tuple()
  35. relation = []
  36. for id in model_ids:
  37. model_data = obj_model.browse(cr, uid, id, context=context)
  38. for field in (
  39. f for f in model_data.field_id if f.ttype in (
  40. 'many2many', 'many2one', 'one2many'
  41. )
  42. ):
  43. relation.append(
  44. (
  45. model_data.model,
  46. field.name,
  47. field.ttype,
  48. field.relation,
  49. field.field_description
  50. )
  51. )
  52. new_model_ids = obj_model.search(
  53. cr,
  54. uid,
  55. [('model', '=', field.relation)],
  56. context=context
  57. )
  58. if new_model_ids:
  59. model = obj_model.read(
  60. cr,
  61. uid,
  62. new_model_ids,
  63. ['id', 'name'],
  64. context=context
  65. )[0]
  66. relation.extend(
  67. self._get_graphical_representation(
  68. cr, uid, model['id'], level - 1
  69. )
  70. )
  71. return tuple(relation)
  72. def _get_structure(self, relations, main_element):
  73. res = {}
  74. for rel in relations:
  75. # if we have to display the string along with field name,
  76. # then uncomment the first line n comment the second line
  77. res.setdefault(rel[0], set()).add(rel[1])
  78. res.setdefault(rel[3], set())
  79. val = []
  80. for obj, fieldsx in res.items():
  81. val.append('"%s" [%s label="{<id>%s|%s}"];' % (
  82. obj,
  83. obj in
  84. main_element and
  85. 'fillcolor=yellow, style="filled,rounded"' or
  86. "",
  87. obj,
  88. "|".join(["<%s> %s" % (fn, fn) for fn in fieldsx])
  89. ))
  90. return "\n".join(val)
  91. def _get_arrow(self, field_type='many2one'):
  92. return {
  93. 'many2one': (
  94. 'arrowtail="none" arrowhead="normal" color="red" label="m2o"'
  95. ),
  96. 'many2many': (
  97. 'arrowtail="crow" arrowhead="crow" color="green" label="m2m"'
  98. ),
  99. 'one2many': (
  100. 'arrowtail="none" arrowhead="crow" color="blue" label="o2m"'
  101. ),
  102. }[field_type]
  103. def get_graphical_representation(self, cr, uid, model_ids, context=None):
  104. obj_model = self.pool.get('ir.model')
  105. if context is None:
  106. context = {}
  107. res = {}
  108. models = []
  109. for obj in obj_model.browse(cr, uid, model_ids, context=context):
  110. models.append(obj.model)
  111. relations = set(
  112. self._get_graphical_representation(
  113. cr, uid, model_ids, context.get('level', 1)
  114. )
  115. )
  116. res[obj.model] = (
  117. "digraph G {\nnode [style=rounded, shape=record];\n%s\n%s }" % (
  118. self._get_structure(relations, models),
  119. ''.join('"%s":%s -> "%s":id:n [%s]; // %s\n' % (
  120. m, fn, fr, self._get_arrow(ft), ft
  121. ) for m, fn, ft, fr, fl in relations),
  122. )
  123. )
  124. return res
  125. def _get_module_objects(self, cr, uid, module, context=None):
  126. obj_model = self.pool.get('ir.model')
  127. obj_mod_data = self.pool.get('ir.model.data')
  128. obj_ids = []
  129. model_data_ids = obj_mod_data.search(
  130. cr, uid,
  131. [('module', '=', module), ('model', '=', 'ir.model')],
  132. context=context
  133. )
  134. model_ids = []
  135. for mod in obj_mod_data.browse(
  136. cr, uid, model_data_ids, context=context
  137. ):
  138. model_ids.append(mod.res_id)
  139. models = obj_model.browse(cr, uid, model_ids, context=context)
  140. map(lambda x: obj_ids.append(x.id), models)
  141. return obj_ids
  142. def get_relation_graph(self, cr, uid, module_name, context=None):
  143. if context is None:
  144. context = {}
  145. object_ids = self._get_module_objects(
  146. cr, uid, module_name, context=context
  147. )
  148. if not object_ids:
  149. return {'module_file': False}
  150. # context.update({'level': 1})
  151. dots = self.get_graphical_representation(
  152. cr, uid, object_ids, context=context
  153. )
  154. # todo: use os.realpath
  155. file_path = openerp.modules.module.get_module_path(
  156. 'base_module_doc_rst'
  157. )
  158. path_png = file_path + "/module.png"
  159. for key, val in dots.items():
  160. path_dotfile = file_path + "/%s.dot" % (key,)
  161. fp = file(path_dotfile, "w")
  162. fp.write(val)
  163. fp.close()
  164. os.popen(
  165. 'dot -Tpng' + ' ' + path_dotfile + ' ' + '-o' + ' ' + path_png
  166. )
  167. fp = file(path_png, "r")
  168. x = fp.read()
  169. fp.close()
  170. os.popen('rm ' + path_dotfile + ' ' + path_png)
  171. return {'module_file': base64.encodestring(x)}