Commit c60eae9d authored by Jérome Perrin's avatar Jérome Perrin

graph_editor: use pydot to layout the graph server side

parent 36948198
##############################################################################
#
# Copyright (c) 2013 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility 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 advised 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 six
try:
import pydot
except ImportError:
pydot = None
def ERP5Site_getGraphEditorGraphLayout(self, graph_editor_dict):
if pydot is None:
return dict(node=dict())
graph = pydot.Dot()
for node_id in graph_editor_dict['node']:
graph.add_node(pydot.Node(node_id))
for edge in six.itervalues(graph_editor_dict['edge']):
graph.add_edge(pydot.Edge(
edge['source'],
edge['destination'],
))
new_graph, = pydot.graph_from_dot_data(graph.create_dot()) # pylint:disable=unpacking-non-sequence
# calulate the ratio from the size of the bounding box
origin_left, origin_top, max_left, max_top = [
float(p) for p in new_graph.get_bb().strip('"').split(',')
]
ratio_top = max_top - origin_top
ratio_left = max_left - origin_left
node_position_dict = dict()
for node in new_graph.get_nodes():
# skip technical nodes
if node.get_name() in ('graph', 'node', 'edge'):
continue
left, top = [float(p) for p in node.get_pos()[1:-1].split(",")]
node_position_dict[node.get_name().strip('"')] = dict(
coordinate=dict(
top=1 - (top / ratio_top),
left=1 - (left / ratio_left),
))
return dict(node=node_position_dict)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Extension Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>GraphEditorUtils</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.GraphEditorUtils</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -2,11 +2,6 @@ from Products.ERP5Type.Message import translateString ...@@ -2,11 +2,6 @@ from Products.ERP5Type.Message import translateString
import json import json
portal = context.getPortalObject() portal = context.getPortalObject()
# if a graph has been saved, we use this info for node coordinates.
position_graph = context.getProperty('jsplumb_graph')
if position_graph:
position_graph = json.loads(position_graph)['graph']
visited_business_process_set = set() # prevent infinite recurisions visited_business_process_set = set() # prevent infinite recurisions
def getBusinessProcessGraph(business_process): def getBusinessProcessGraph(business_process):
...@@ -23,10 +18,6 @@ def getBusinessProcessGraph(business_process): ...@@ -23,10 +18,6 @@ def getBusinessProcessGraph(business_process):
_class='erp5.business_process.trade_state', _class='erp5.business_process.trade_state',
name=trade_state.getTranslatedTitle()) name=trade_state.getTranslatedTitle())
for state_id in graph['node'].keys():
if position_graph and state_id in position_graph['node']:
graph['node'][state_id]['coordinate'] = position_graph['node'][state_id]['coordinate']
if business_process in visited_business_process_set: if business_process in visited_business_process_set:
return graph return graph
visited_business_process_set.add(business_process) visited_business_process_set.add(business_process)
...@@ -53,6 +44,20 @@ def getBusinessProcessGraph(business_process): ...@@ -53,6 +44,20 @@ def getBusinessProcessGraph(business_process):
graph['node'].setdefault(node_id, node_data) graph['node'].setdefault(node_id, node_data)
for node_id, node_data in specialise_graph['edge'].items(): for node_id, node_data in specialise_graph['edge'].items():
graph['edge'].setdefault(node_id, node_data) graph['edge'].setdefault(node_id, node_data)
position_graph = context.getProperty('jsplumb_graph')
if position_graph:
# if a graph has been saved, we use this info for node coordinates.
position_graph = json.loads(position_graph)['graph']
else:
position_graph = context.ERP5Site_getGraphEditorGraphLayout(graph)
for state_id in graph['node']:
if position_graph and state_id in position_graph['node']:
graph['node'][state_id]['coordinate'] = position_graph['node'][state_id]['coordinate']
return graph return graph
......
import json import json
# if a graph has been saved, we use this info for node coordinates.
position_graph = context.getProperty('jsplumb_graph')
if position_graph:
position_graph = json.loads(position_graph)['graph']
# TODO: # TODO:
# select after script in edge properties # select after script in edge properties
# checked box for validation ? or at least select before script # checked box for validation ? or at least select before script
...@@ -34,10 +29,16 @@ def getDCWorkflowGraph(dc_workflow): ...@@ -34,10 +29,16 @@ def getDCWorkflowGraph(dc_workflow):
transition_id=transition.getId() # used for edition. transition_id=transition.getId() # used for edition.
)) ))
position_graph = context.getProperty('jsplumb_graph')
if position_graph: if position_graph:
for state_id in graph['node'].keys(): # if a graph has been saved, we use this info for node coordinates.
if state_id in position_graph['node']: position_graph = json.loads(position_graph)['graph']
graph['node'][state_id]['coordinate'] = position_graph['node'][state_id]['coordinate'] else:
position_graph = context.ERP5Site_getGraphEditorGraphLayout(graph)
for state_id in graph['node']:
if state_id in position_graph['node']:
graph['node'][state_id]['coordinate'] = position_graph['node'][state_id]['coordinate']
return graph return graph
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>ERP5Site_getGraphEditorGraphLayout</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>GraphEditorUtils</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_getGraphEditorGraphLayout</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
import json import json
# if a graph has been saved, we use this info for node coordinates.
position_graph = context.getProperty('jsplumb_graph')
if position_graph:
position_graph = json.loads(position_graph)['graph']
# TODO: # TODO:
# select after script in edge properties # select after script in edge properties
# checked box for validation ? or at least select before script # checked box for validation ? or at least select before script
...@@ -45,10 +40,16 @@ def getWorkflowGraph(workflow): ...@@ -45,10 +40,16 @@ def getWorkflowGraph(workflow):
} }
position_graph = context.getProperty('jsplumb_graph')
if position_graph: if position_graph:
for state_id in graph['node'].keys(): # if a graph has been saved, we use this info for node coordinates.
if state_id in position_graph['node']: position_graph = json.loads(position_graph)['graph']
graph['node'][state_id]['coordinate'] = position_graph['node'][state_id]['coordinate'] else:
position_graph = context.ERP5Site_getGraphEditorGraphLayout(graph)
for state_id in graph['node']:
if state_id in position_graph['node']:
graph['node'][state_id]['coordinate'] = position_graph['node'][state_id]['coordinate']
return graph return graph
return json.dumps(dict(graph=getWorkflowGraph(context), class_definition={}), indent=2) return json.dumps(dict(graph=getWorkflowGraph(context), class_definition={}), indent=2)
extension.erp5.GraphEditorUtils
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment