############################################################################## # # Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved. # Fabien Pinckaers # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## import time, os import netsvc import report,pooler,tools def graph_get(cr, graph, wkf_id, nested=False, workitem={}): import pydot cr.execute('select * from wkf_activity where wkf_id=%d', (wkf_id,)) nodes = cr.dictfetchall() activities = {} actfrom = {} actto = {} for n in nodes: activities[n['id']] = n if n['subflow_id'] and nested: cr.execute('select * from wkf where id=%d', (n['subflow_id'],)) wkfinfo = cr.dictfetchone() graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize=10, label = "Subflow: "+n['name']+'\\nOSV: '+wkfinfo['osv']) (s1,s2) = graph_get(cr, graph2, n['subflow_id'], nested,workitem) graph.add_subgraph(graph2) actfrom[n['id']] = s2 actto[n['id']] = s1 else: args = {} if n['flow_start'] or n['flow_stop']: args['style']='filled' args['color']='lightgrey' args['label']=n['name'] if n['subflow_id']: args['shape'] = 'box' if n['id'] in workitem: args['label']+='\\nx '+str(workitem[n['id']]) args['color'] = "red" graph.add_node(pydot.Node(n['id'], **args)) actfrom[n['id']] = (n['id'],{}) actto[n['id']] = (n['id'],{}) cr.execute('select * from wkf_transition where act_from in ('+','.join(map(lambda x: str(x['id']),nodes))+')') transitions = cr.dictfetchall() for t in transitions: args = {} args['label'] = str(t['condition']) if t['signal']: args['label'] += '\\n'+str(t['signal']) args['style'] = 'bold' if activities[t['act_from']]['split_mode']=='AND': args['arrowtail']='box' elif str(activities[t['act_from']]['split_mode'])=='OR ': args['arrowtail']='inv' if activities[t['act_to']]['join_mode']=='AND': args['arrowhead']='crow' activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0]) activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0]) graph.add_edge(pydot.Edge( activity_from ,activity_to, fontsize=8, **args)) nodes = cr.dictfetchall() cr.execute('select id from wkf_activity where flow_start=True and wkf_id=%d limit 1', (wkf_id,)) start = cr.fetchone()[0] cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id=%d", (wkf_id,)) stop = cr.fetchall() stop = (stop[0][1], dict(stop)) return ((start,{}),stop) def graph_instance_get(cr, graph, inst_id, nested=False): workitems = {} cr.execute('select * from wkf_instance where id=%d', (inst_id,)) inst = cr.dictfetchone() def workitem_get(instance): cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%d group by act_id', (instance,)) workitems = dict(cr.fetchall()) cr.execute('select subflow_id from wkf_workitem where inst_id=%d', (instance,)) for (subflow_id,) in cr.fetchall(): workitems.update(workitem_get(subflow_id)) return workitems graph_get(cr, graph, inst['wkf_id'], nested, workitem_get(inst_id)) # # TODO: pas clean: concurrent !!! # class report_graph_instance(object): def __init__(self, cr, uid, ids, data): try: import pydot except Exception,e: print 'Import Error for pydot, you will not be able to render workflows' print 'Consider Installing PyDot or dependencies: http://dkbza.org/pydot.html' raise e self.done = False try: cr.execute('select * from wkf where osv=%s limit 1', (data['model'],)) wkfinfo = cr.dictfetchone() cr.execute('select id from wkf_instance where res_id=%d and wkf_id=%d order by state limit 1', (data['id'],wkfinfo['id'])) inst_id = cr.fetchone()[0] graph = pydot.Dot(fontsize=16, label="\\n\\nWorkflow: %s\\n OSV: %s" % (wkfinfo['name'],wkfinfo['osv'])) graph_instance_get(cr, graph, inst_id, data.get('nested', False)) ps_string = graph.create_ps(prog='dot') except: # string is in PS, like the success message would have been ps_string = '''%PS-Adobe-3.0 /inch {72 mul} def /Times-Roman findfont 50 scalefont setfont 1.5 inch 15 inch moveto (No workflow available) show showpage''' input, output = os.popen2('ps2pdf -sPAPERSIZE=a3 - -') input.write(ps_string) input.close() self.result = output.read() output.close() self.done = True def is_done(self): return self.done def get(self): if self.done: return self.result else: return None class report_graph(report.interface.report_int): def __init__(self, name, table): report.interface.report_int.__init__(self, name) self.table = table def result(self): if self.obj.is_done(): return (True, self.obj.get(), 'pdf') else: return (False, False, False) def create(self, cr, uid, ids, data, context={}): self.obj = report_graph_instance(cr, uid, ids, data) return (self.obj.get(), 'pdf') report_graph('report.workflow.instance.graph', 'ir.workflow')