Spaces:
Running
Running
can choose the best path for the vizi
Browse files- modules/toWizard.py +101 -17
- modules/toXML.py +11 -9
modules/toWizard.py
CHANGED
@@ -2,6 +2,37 @@ import xml.etree.ElementTree as ET
|
|
2 |
from modules.utils import class_dict
|
3 |
from xml.dom import minidom
|
4 |
from modules.utils import error
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
def rescale(scale, boxes):
|
7 |
for i in range(len(boxes)):
|
@@ -56,8 +87,8 @@ def connect(data, text_mapping, i):
|
|
56 |
target_idx = data['links'][i][1]
|
57 |
# Check if the target index is valid
|
58 |
if target_idx==None or target_idx >= len(data['links']):
|
59 |
-
error('There
|
60 |
-
return None, None
|
61 |
|
62 |
current_id = data['BPMN_id'][i]
|
63 |
current_text = text_mapping[current_id]
|
@@ -111,7 +142,44 @@ def find_merge(bpmn_id, links):
|
|
111 |
|
112 |
return merge_elements
|
113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
def create_wizard_file(data, text_mapping):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
root = ET.Element('methodAndStyleWizard')
|
116 |
|
117 |
modelName = ET.SubElement(root, 'modelName')
|
@@ -128,32 +196,44 @@ def create_wizard_file(data, text_mapping):
|
|
128 |
|
129 |
processDescription = ET.SubElement(root, 'processDescription')
|
130 |
|
|
|
131 |
for idx, Bpmn_id in enumerate(data['BPMN_id']):
|
132 |
# Start Event
|
133 |
element_type = Bpmn_id.split('_')[0]
|
134 |
-
if element_type == '
|
135 |
eventType = 'Message'
|
136 |
elif element_type == 'event':
|
137 |
eventType = 'None'
|
138 |
if idx >= len(data['links']):
|
139 |
continue
|
140 |
-
if check_start(data['links'][idx]) and (element_type=='event' or element_type=='
|
141 |
-
|
|
|
|
|
142 |
|
143 |
requestMessage = ET.SubElement(root, 'requestMessage')
|
144 |
requester = ET.SubElement(root, 'requester')
|
145 |
|
146 |
endEvents = ET.SubElement(root, 'endStates')
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
# Add end states event to the collaboration element
|
149 |
for idx, Bpmn_id in enumerate(data['BPMN_id']):
|
150 |
# End States
|
151 |
if idx >= len(data['links']):
|
152 |
continue
|
153 |
-
if check_end(data['links'][idx]) and (Bpmn_id.split('_')[0] == 'event' or Bpmn_id.split('_')[0] == '
|
154 |
if text_mapping[Bpmn_id] == '':
|
155 |
text_mapping[Bpmn_id] = '(unnamed)'
|
156 |
-
|
|
|
|
|
|
|
|
|
157 |
|
158 |
|
159 |
# Add activities to the collaboration element
|
@@ -166,9 +246,15 @@ def create_wizard_file(data, text_mapping):
|
|
166 |
if next_text is not None and len(next_text) == 1:
|
167 |
ET.SubElement(endStates, 'endState', attrib={'name': next_text[0], 'isRegular': 'True'})
|
168 |
elif next_text is not None and len(next_text) >= 2 and next_id.split('_')[0] == 'exclusiveGateway':
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
ET.SubElement(activity, 'subActivities')
|
173 |
ET.SubElement(activity, 'subActivityFlows')
|
174 |
ET.SubElement(activity, 'messageFlows')
|
@@ -176,15 +262,15 @@ def create_wizard_file(data, text_mapping):
|
|
176 |
merge_object = find_merge(data['BPMN_id'], data['links'])
|
177 |
|
178 |
activityFlows = ET.SubElement(root, 'activityFlows')
|
179 |
-
i=0
|
180 |
for i, link in enumerate(data['links']):
|
181 |
# create flow with start event
|
182 |
-
if link[0] is None and link[1] is not None and (data['BPMN_id'][i].split('_')[0] == 'event' or data['BPMN_id'][i].split('_')[0] == '
|
183 |
current_text, next_text, _ = connect(data, text_mapping, i)
|
184 |
if current_text is None or next_text is None:
|
185 |
continue
|
186 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'startEvent': current_text, 'endState': '---', 'target': next_text[0], 'isMerging': 'False', 'isPredefined': 'True'})
|
187 |
-
|
|
|
188 |
# create flow with tasks
|
189 |
if link[0] is not None and link[1] is not None and data['BPMN_id'][i].split('_')[0] == 'task':
|
190 |
current_text, next_text, next_id = connect(data, text_mapping, i)
|
@@ -197,8 +283,8 @@ def create_wizard_file(data, text_mapping):
|
|
197 |
merge = 'False'
|
198 |
|
199 |
if len(next_text) == 2 and next_id.split('_')[0] == 'exclusiveGateway':
|
200 |
-
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': next_text[0]
|
201 |
-
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState':
|
202 |
elif len(next_text) > 1 and next_id.split('_')[0] == 'parallelGateway':
|
203 |
for next in next_text:
|
204 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': '---', 'target': next, 'isMerging': merge, 'isPredefined': 'True'})
|
@@ -206,8 +292,6 @@ def create_wizard_file(data, text_mapping):
|
|
206 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': '---', 'target': next_text[0], 'isMerging': merge, 'isPredefined': 'True'})
|
207 |
else:
|
208 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': '---', 'target': next_text, 'isMerging': merge, 'isPredefined': 'True'})
|
209 |
-
|
210 |
-
i+=1
|
211 |
|
212 |
ET.SubElement(root, 'participants')
|
213 |
|
|
|
2 |
from modules.utils import class_dict
|
3 |
from xml.dom import minidom
|
4 |
from modules.utils import error
|
5 |
+
from transformers import pipeline
|
6 |
+
|
7 |
+
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
8 |
+
import torch
|
9 |
+
import logging
|
10 |
+
|
11 |
+
# Suppress specific warnings from transformers
|
12 |
+
logging.getLogger("transformers.modeling_utils").setLevel(logging.ERROR)
|
13 |
+
|
14 |
+
# Function to initialize the model and tokenizer
|
15 |
+
def initialize_model():
|
16 |
+
tokenizer = AutoTokenizer.from_pretrained("cardiffnlp/twitter-roberta-base-sentiment-latest")
|
17 |
+
model = AutoModelForSequenceClassification.from_pretrained("cardiffnlp/twitter-roberta-base-sentiment-latest")
|
18 |
+
return tokenizer, model
|
19 |
+
|
20 |
+
# Function to perform sentiment analysis and return the highest scoring emotion and its score between positive and negative
|
21 |
+
def analyze_sentiment(sentence, tokenizer, model):
|
22 |
+
inputs = tokenizer(sentence, return_tensors="pt")
|
23 |
+
outputs = model(**inputs)
|
24 |
+
probs = torch.nn.functional.softmax(outputs.logits, dim=-1).squeeze().tolist()
|
25 |
+
labels = ["negative", "neutral", "positive"]
|
26 |
+
results = dict(zip(labels, probs))
|
27 |
+
|
28 |
+
# Filter out the neutral score and get the highest score between positive and negative
|
29 |
+
relevant_results = {k: results[k] for k in ["positive", "negative"]}
|
30 |
+
highest_emotion = max(relevant_results, key=relevant_results.get)
|
31 |
+
highest_score = relevant_results[highest_emotion]
|
32 |
+
return highest_emotion, highest_score
|
33 |
+
|
34 |
+
# Initialize model and tokenizer
|
35 |
+
tokenizer, model = initialize_model()
|
36 |
|
37 |
def rescale(scale, boxes):
|
38 |
for i in range(len(boxes)):
|
|
|
87 |
target_idx = data['links'][i][1]
|
88 |
# Check if the target index is valid
|
89 |
if target_idx==None or target_idx >= len(data['links']):
|
90 |
+
error('There may be an error with the Vizi file, care when you download it.')
|
91 |
+
return None, None, None
|
92 |
|
93 |
current_id = data['BPMN_id'][i]
|
94 |
current_text = text_mapping[current_id]
|
|
|
142 |
|
143 |
return merge_elements
|
144 |
|
145 |
+
def find_positive_end(bpmn_ids, links, text_mapping):
|
146 |
+
emotion_data = []
|
147 |
+
for idx, bpmn_id in enumerate(bpmn_ids):
|
148 |
+
if idx >= len(links):
|
149 |
+
continue
|
150 |
+
if check_end(links[idx]) and (bpmn_id.split('_')[0] in ['event', 'message']):
|
151 |
+
# Perform sentiment analysis and get the highest scoring emotion and its score between positive and negative
|
152 |
+
highest_emotion, highest_score = analyze_sentiment(text_mapping[bpmn_id], tokenizer, model)
|
153 |
+
emotion_data.append((bpmn_id, highest_emotion, highest_score))
|
154 |
+
|
155 |
+
# Sort by emotion label with 'positive' first and 'negative' second,
|
156 |
+
# then by score in descending order
|
157 |
+
sorted_emotions = sorted(emotion_data, key=lambda x: (x[1] != 'positive', -x[2]))
|
158 |
+
|
159 |
+
return sorted_emotions[0][0] if len(sorted_emotions) > 0 else None
|
160 |
+
|
161 |
+
def find_best_direction(texts_list):
|
162 |
+
emotion_data = []
|
163 |
+
for text in texts_list:
|
164 |
+
highest_emotion, highest_score = analyze_sentiment(text, tokenizer, model)
|
165 |
+
emotion_data.append((text, highest_emotion, highest_score))
|
166 |
+
|
167 |
+
# Sort by emotion label with 'positive' first and 'negative' second,
|
168 |
+
# then by score in descending order
|
169 |
+
sorted_emotions = sorted(emotion_data, key=lambda x: (x[1] != 'positive', -x[2]))
|
170 |
+
|
171 |
+
return sorted_emotions[0][0] if len(sorted_emotions) > 0 else None
|
172 |
+
|
173 |
+
|
174 |
+
|
175 |
def create_wizard_file(data, text_mapping):
|
176 |
+
|
177 |
+
#add a name into the text_mapping when there is no name
|
178 |
+
for idx, key in enumerate(text_mapping.keys()):
|
179 |
+
if text_mapping[key] == '':
|
180 |
+
text_mapping[key] = f'unnamed_{key}'
|
181 |
+
|
182 |
+
|
183 |
root = ET.Element('methodAndStyleWizard')
|
184 |
|
185 |
modelName = ET.SubElement(root, 'modelName')
|
|
|
196 |
|
197 |
processDescription = ET.SubElement(root, 'processDescription')
|
198 |
|
199 |
+
first = False
|
200 |
for idx, Bpmn_id in enumerate(data['BPMN_id']):
|
201 |
# Start Event
|
202 |
element_type = Bpmn_id.split('_')[0]
|
203 |
+
if element_type == 'message':
|
204 |
eventType = 'Message'
|
205 |
elif element_type == 'event':
|
206 |
eventType = 'None'
|
207 |
if idx >= len(data['links']):
|
208 |
continue
|
209 |
+
if check_start(data['links'][idx]) and (element_type=='event' or element_type=='message'):
|
210 |
+
if text_mapping[Bpmn_id] == '':
|
211 |
+
text_mapping[Bpmn_id] = 'start'
|
212 |
+
startEvent = ET.SubElement(root, 'startEvent', attrib={'name': text_mapping[Bpmn_id], 'eventType': eventType, 'isRegular': 'True'})
|
213 |
|
214 |
requestMessage = ET.SubElement(root, 'requestMessage')
|
215 |
requester = ET.SubElement(root, 'requester')
|
216 |
|
217 |
endEvents = ET.SubElement(root, 'endStates')
|
218 |
+
|
219 |
+
positive_end = find_positive_end(data['BPMN_id'], data['links'], text_mapping)
|
220 |
+
if positive_end is not None:
|
221 |
+
print("Best end is: ",text_mapping[positive_end])
|
222 |
+
|
223 |
|
224 |
# Add end states event to the collaboration element
|
225 |
for idx, Bpmn_id in enumerate(data['BPMN_id']):
|
226 |
# End States
|
227 |
if idx >= len(data['links']):
|
228 |
continue
|
229 |
+
if check_end(data['links'][idx]) and (Bpmn_id.split('_')[0] == 'event' or Bpmn_id.split('_')[0] == 'message'):
|
230 |
if text_mapping[Bpmn_id] == '':
|
231 |
text_mapping[Bpmn_id] = '(unnamed)'
|
232 |
+
|
233 |
+
if Bpmn_id == positive_end:
|
234 |
+
ET.SubElement(endEvents, 'endState', attrib={'name': text_mapping[Bpmn_id], 'eventType': 'None', 'isRegular': 'True'})
|
235 |
+
else:
|
236 |
+
ET.SubElement(endEvents, 'endState', attrib={'name': text_mapping[Bpmn_id], 'eventType': 'None', 'isRegular': 'False'})
|
237 |
|
238 |
|
239 |
# Add activities to the collaboration element
|
|
|
246 |
if next_text is not None and len(next_text) == 1:
|
247 |
ET.SubElement(endStates, 'endState', attrib={'name': next_text[0], 'isRegular': 'True'})
|
248 |
elif next_text is not None and len(next_text) >= 2 and next_id.split('_')[0] == 'exclusiveGateway':
|
249 |
+
best_direction = find_best_direction(next_text)
|
250 |
+
if best_direction is not None:
|
251 |
+
print("Best direction is: ", best_direction)
|
252 |
+
for i in range(len(next_text)):
|
253 |
+
if next_text[i] == best_direction:
|
254 |
+
ET.SubElement(endStates, 'endState', attrib={'name': next_text[i], 'isRegular': 'True'})
|
255 |
+
else:
|
256 |
+
ET.SubElement(endStates, 'endState', attrib={'name': next_text[i], 'isRegular': 'False'})
|
257 |
+
|
258 |
ET.SubElement(activity, 'subActivities')
|
259 |
ET.SubElement(activity, 'subActivityFlows')
|
260 |
ET.SubElement(activity, 'messageFlows')
|
|
|
262 |
merge_object = find_merge(data['BPMN_id'], data['links'])
|
263 |
|
264 |
activityFlows = ET.SubElement(root, 'activityFlows')
|
|
|
265 |
for i, link in enumerate(data['links']):
|
266 |
# create flow with start event
|
267 |
+
if link[0] is None and link[1] is not None and (data['BPMN_id'][i].split('_')[0] == 'event' or data['BPMN_id'][i].split('_')[0] == 'message'):
|
268 |
current_text, next_text, _ = connect(data, text_mapping, i)
|
269 |
if current_text is None or next_text is None:
|
270 |
continue
|
271 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'startEvent': current_text, 'endState': '---', 'target': next_text[0], 'isMerging': 'False', 'isPredefined': 'True'})
|
272 |
+
continue
|
273 |
+
|
274 |
# create flow with tasks
|
275 |
if link[0] is not None and link[1] is not None and data['BPMN_id'][i].split('_')[0] == 'task':
|
276 |
current_text, next_text, next_id = connect(data, text_mapping, i)
|
|
|
283 |
merge = 'False'
|
284 |
|
285 |
if len(next_text) == 2 and next_id.split('_')[0] == 'exclusiveGateway':
|
286 |
+
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': next_text[0], 'target': next_text[0], 'isMerging': 'False', 'isPredefined': 'True'})
|
287 |
+
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': next_text[1], 'target': next_text[1], 'isMerging': 'False', 'isPredefined': 'True'})
|
288 |
elif len(next_text) > 1 and next_id.split('_')[0] == 'parallelGateway':
|
289 |
for next in next_text:
|
290 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': '---', 'target': next, 'isMerging': merge, 'isPredefined': 'True'})
|
|
|
292 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': '---', 'target': next_text[0], 'isMerging': merge, 'isPredefined': 'True'})
|
293 |
else:
|
294 |
ET.SubElement(activityFlows, 'activityFlow', attrib={'activity': current_text, 'endState': '---', 'target': next_text, 'isMerging': merge, 'isPredefined': 'True'})
|
|
|
|
|
295 |
|
296 |
ET.SubElement(root, 'participants')
|
297 |
|
modules/toXML.py
CHANGED
@@ -181,7 +181,7 @@ def create_XML(full_pred, text_mapping, size_scale, scale):
|
|
181 |
|
182 |
#if there is no pool or lane, create a pool with all elements
|
183 |
if len(full_pred['pool_dict']) == 0 or (len(full_pred['pool_dict']) == 1 and len(next(iter(full_pred['pool_dict'].values()))) == len(full_pred['labels'])):
|
184 |
-
full_pred, text_mapping = create_big_pool(full_pred, text_mapping)
|
185 |
|
186 |
#modify the boxes positions
|
187 |
old_boxes = copy.deepcopy(full_pred)
|
@@ -250,13 +250,13 @@ def create_XML(full_pred, text_mapping, size_scale, scale):
|
|
250 |
return pretty_xml_as_string
|
251 |
|
252 |
# Function that creates a single pool with all elements
|
253 |
-
def create_big_pool(full_pred, text_mapping, marge=50):
|
254 |
# If no pools or lanes are detected, create a single pool with all elements
|
255 |
new_pool_index = 'pool_1'
|
256 |
size_elements = get_size_elements(st.session_state.size_scale)
|
257 |
elements_pool = list(range(len(full_pred['boxes'])))
|
258 |
min_x, min_y, max_x, max_y = calculate_pool_bounds(full_pred['boxes'],full_pred['labels'], elements_pool, size_elements)
|
259 |
-
box = [min_x-marge, min_y-marge, max_x+marge, max_y+marge]
|
260 |
full_pred['boxes'] = np.append(full_pred['boxes'], [box], axis=0)
|
261 |
full_pred['pool_dict'][new_pool_index] = elements_pool
|
262 |
full_pred['BPMN_id'].append('pool_1')
|
@@ -557,26 +557,28 @@ def create_bpmn_object(process, bpmnplane, text_mapping, definitions, size, data
|
|
557 |
ET.SubElement(element, 'bpmn:timerEventDefinition', id=f'TimerEventDefinition_{i+1}')
|
558 |
add_diagram_elements(bpmnplane, element_id, x, y, size['timerEvent'][0], size['timerEvent'][1])
|
559 |
|
560 |
-
|
561 |
-
def calculate_pool_bounds(boxes, labels, keep_elements, size):
|
562 |
min_x, min_y = float('inf'), float('inf')
|
563 |
max_x, max_y = float('-inf'), float('-inf')
|
564 |
|
565 |
for i in keep_elements:
|
566 |
if i >= len(labels):
|
567 |
-
print("Problem with the index")
|
568 |
continue
|
569 |
|
570 |
element = labels[i]
|
571 |
if element in {None, 7, 13, 14, 15}:
|
572 |
continue
|
573 |
|
574 |
-
|
575 |
-
if size == None:
|
576 |
element_width = boxes[i][2] - boxes[i][0]
|
577 |
element_height = boxes[i][3] - boxes[i][1]
|
578 |
else:
|
579 |
-
|
|
|
|
|
|
|
|
|
580 |
|
581 |
x, y = boxes[i][:2]
|
582 |
min_x = min(min_x, x)
|
|
|
181 |
|
182 |
#if there is no pool or lane, create a pool with all elements
|
183 |
if len(full_pred['pool_dict']) == 0 or (len(full_pred['pool_dict']) == 1 and len(next(iter(full_pred['pool_dict'].values()))) == len(full_pred['labels'])):
|
184 |
+
full_pred, text_mapping = create_big_pool(full_pred, text_mapping, size_elements)
|
185 |
|
186 |
#modify the boxes positions
|
187 |
old_boxes = copy.deepcopy(full_pred)
|
|
|
250 |
return pretty_xml_as_string
|
251 |
|
252 |
# Function that creates a single pool with all elements
|
253 |
+
def create_big_pool(full_pred, text_mapping, size_elements, marge=50):
|
254 |
# If no pools or lanes are detected, create a single pool with all elements
|
255 |
new_pool_index = 'pool_1'
|
256 |
size_elements = get_size_elements(st.session_state.size_scale)
|
257 |
elements_pool = list(range(len(full_pred['boxes'])))
|
258 |
min_x, min_y, max_x, max_y = calculate_pool_bounds(full_pred['boxes'],full_pred['labels'], elements_pool, size_elements)
|
259 |
+
box = [min_x - marge, min_y - marge//2, max_x + marge, max_y + marge//2]
|
260 |
full_pred['boxes'] = np.append(full_pred['boxes'], [box], axis=0)
|
261 |
full_pred['pool_dict'][new_pool_index] = elements_pool
|
262 |
full_pred['BPMN_id'].append('pool_1')
|
|
|
557 |
ET.SubElement(element, 'bpmn:timerEventDefinition', id=f'TimerEventDefinition_{i+1}')
|
558 |
add_diagram_elements(bpmnplane, element_id, x, y, size['timerEvent'][0], size['timerEvent'][1])
|
559 |
|
560 |
+
def calculate_pool_bounds(boxes, labels, keep_elements, size=None, class_dict=None):
|
|
|
561 |
min_x, min_y = float('inf'), float('inf')
|
562 |
max_x, max_y = float('-inf'), float('-inf')
|
563 |
|
564 |
for i in keep_elements:
|
565 |
if i >= len(labels):
|
566 |
+
print(f"Problem with the index: {i}")
|
567 |
continue
|
568 |
|
569 |
element = labels[i]
|
570 |
if element in {None, 7, 13, 14, 15}:
|
571 |
continue
|
572 |
|
573 |
+
if size is None or class_dict is None:
|
|
|
574 |
element_width = boxes[i][2] - boxes[i][0]
|
575 |
element_height = boxes[i][3] - boxes[i][1]
|
576 |
else:
|
577 |
+
if labels[i] in class_dict:
|
578 |
+
element_width, element_height = size[class_dict[labels[i]]]
|
579 |
+
else:
|
580 |
+
print(f"Class label {labels[i]} not found in class_dict.")
|
581 |
+
continue
|
582 |
|
583 |
x, y = boxes[i][:2]
|
584 |
min_x = min(min_x, x)
|