59 |
59 |
del node.attrib[att]
|
60 |
60 |
|
61 |
61 |
|
62 |
|
def remove_style(node, top):
|
|
62 |
def remove_style(node, top, colours, white_text=False):
|
63 |
63 |
remove_tag(node, TITLE)
|
64 |
64 |
if node.get('fill') == 'white' and node.get('stroke') == 'white':
|
65 |
65 |
# this is the general white background, wipe it to be transparent
|
66 |
66 |
node.attrib['fill'] = 'transparent'
|
67 |
67 |
node.attrib['stroke'] = 'transparent'
|
|
68 |
if node.tag == svg('text') and white_text:
|
|
69 |
node.attrib['fill'] = 'white'
|
68 |
70 |
for child in node:
|
69 |
71 |
remove_attribute(child, XLINK_TITLE)
|
70 |
|
style = child.get('style', None)
|
71 |
|
# Beware ! HACK ! salmon is matched and converted to class="page-subject"
|
72 |
|
if style:
|
73 |
|
m = re.search('(?:stroke|fill):salmon', style)
|
74 |
|
if m:
|
75 |
|
top.set('class', top.get('class','') + ' page-subject')
|
76 |
|
if child.get('fill') == 'salmon':
|
77 |
|
top.set('class', top.get('class', '') + ' page-subject')
|
|
72 |
if child.get('fill') in colours:
|
|
73 |
matching_hexa = colours.get(child.get('fill'))
|
|
74 |
child.attrib['fill'] = '#' + matching_hexa
|
|
75 |
del child.attrib['stroke']
|
|
76 |
if misc.get_foreground_colour(matching_hexa) == 'white':
|
|
77 |
white_text = True
|
78 |
78 |
if child.get('font-family'):
|
79 |
79 |
del child.attrib['font-family']
|
80 |
80 |
if child.get('font-size'):
|
81 |
81 |
child.attrib['font-size'] = str(float(child.attrib['font-size'])*0.8)
|
82 |
82 |
remove_attribute(child, 'style')
|
83 |
|
remove_style(child, top)
|
|
83 |
remove_style(child, top, colours, white_text=white_text)
|
84 |
84 |
|
85 |
|
def graphviz_post_treatment(content):
|
|
85 |
def graphviz_post_treatment(content, colours):
|
86 |
86 |
''' Remove all svg:title and top-level svg:polygon nodes, remove style
|
87 |
87 |
attributes and xlink:title attributes.
|
88 |
88 |
|
... | ... | |
90 |
90 |
part on as class selector on the top level svg:g element.
|
91 |
91 |
'''
|
92 |
92 |
tree = ET.fromstring(content)
|
|
93 |
style = ET.SubElement(tree, svg('style'))
|
|
94 |
style.attrib['type'] = 'text/css'
|
|
95 |
css_url = '%s%s%s' % (get_publisher().get_root_url(),
|
|
96 |
get_publisher().qommon_static_dir,
|
|
97 |
get_publisher().qommon_admin_css)
|
|
98 |
style.text = '@import url(%s);' % css_url
|
93 |
99 |
|
94 |
100 |
for root in tree:
|
95 |
101 |
remove_tag(root, TITLE)
|
96 |
102 |
# remove_tag(root, POLYGON)
|
97 |
103 |
for child in root:
|
98 |
|
remove_style(child, child)
|
|
104 |
remove_style(child, child, colours)
|
99 |
105 |
return ET.tostring(tree)
|
100 |
106 |
|
101 |
107 |
def graphviz(workflow, url_prefix='', select=None, svg=True,
|
102 |
108 |
include=False):
|
103 |
109 |
out = StringIO()
|
|
110 |
# a list of colours known to graphviz, they will serve as key to get back
|
|
111 |
# to the colours defined in wcs.
|
|
112 |
graphviz_colours = [
|
|
113 |
'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige',
|
|
114 |
'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown',
|
|
115 |
'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral',
|
|
116 |
'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue',
|
|
117 |
'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey',
|
|
118 |
'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange',
|
|
119 |
'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue',
|
|
120 |
'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet',
|
|
121 |
'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue',
|
|
122 |
'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
|
|
123 |
'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
|
|
124 |
'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory',
|
|
125 |
'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon',
|
|
126 |
'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow',
|
|
127 |
'lightgray', 'lightgreen', 'lightgrey', 'lightpink', ]
|
|
128 |
|
|
129 |
colours = {}
|
|
130 |
revert_colours = {}
|
104 |
131 |
print >>out, 'digraph main {'
|
105 |
132 |
# print >>out, 'graph [ rankdir=LR ];'
|
106 |
133 |
print >>out, 'node [shape=box,style=filled];'
|
... | ... | |
110 |
137 |
print >>out, 'status%s' % i,
|
111 |
138 |
print >>out, '[label="%s"' % status.name,
|
112 |
139 |
if select == str(i):
|
113 |
|
print >>out, ',color=salmon'
|
|
140 |
print >>out, ',id=current_status'
|
|
141 |
if status.colour:
|
|
142 |
if status.colour not in colours:
|
|
143 |
colours[status.colour] = graphviz_colours.pop()
|
|
144 |
revert_colours[colours[status.colour]] = status.colour
|
|
145 |
print >>out, ',color=%s' % colours[status.colour]
|
114 |
146 |
print >>out, ' URL="%sstatus/%s/"];' % (url_prefix, i)
|
115 |
147 |
|
116 |
148 |
for status in workflow.possible_status:
|
... | ... | |
118 |
150 |
for item in status.items:
|
119 |
151 |
next_status_ids = [x.id for x in item.get_target_status()]
|
120 |
152 |
if not next_status_ids:
|
121 |
|
next_status_ids = [status.id]
|
|
153 |
continue
|
122 |
154 |
for next_id in next_status_ids:
|
123 |
155 |
print >>out, 'status%s -> status%s' % (i, next_id)
|
|
156 |
if item.key == 'timeout' or (item.key == 'jump' and (
|
|
157 |
item.condition or item.trigger or item.timeout)):
|
|
158 |
print >> out, ' [style=dotted]'
|
124 |
159 |
url = 'status/%s/items/%s/' % (i, item.id)
|
125 |
|
if getattr(item, 'label', None):
|
126 |
|
label = item.label
|
127 |
|
if getattr(item, 'by', None):
|
128 |
|
roles = workflow.render_list_of_roles(item.by)
|
129 |
|
label += ' %s %s' % (_('by'), roles)
|
130 |
|
else:
|
131 |
|
label = item.render_as_line()
|
132 |
|
label = label.replace('"', '\\"')
|
133 |
|
label = label.decode('utf8')
|
134 |
|
label = textwrap.fill(label, 20, break_long_words=False)
|
135 |
|
label = label.encode('utf8')
|
136 |
|
label = label.replace('\n', '\\n')
|
137 |
|
print >>out, '[label="%s"' % label,
|
138 |
|
if select == '%s-%s' % (i, item.id):
|
139 |
|
print >>out, ',color=salmon'
|
140 |
|
print >>out, ',URL="%s%s"]' % (url_prefix, url)
|
|
160 |
continue
|
141 |
161 |
print >>out, '}'
|
142 |
162 |
out = out.getvalue()
|
143 |
163 |
if svg:
|
... | ... | |
146 |
166 |
out, err = process.communicate(out)
|
147 |
167 |
except OSError:
|
148 |
168 |
return ''
|
|
169 |
out = graphviz_post_treatment(out, revert_colours)
|
149 |
170 |
if include:
|
150 |
|
out = graphviz_post_treatment(out)
|
151 |
171 |
# It seems webkit refuse to accept SVG when using its proper namespace,
|
152 |
172 |
# and xlink namespace prefix must be xlink: to be acceptable
|
153 |
173 |
out = out.replace('ns0:', '')
|