cpee 2.1.130 → 2.1.131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/FEATURES.md +9 -19
- data/cockpit/compliance.html +34 -23
- data/cockpit/css/compliance.css +70 -2
- data/cockpit/css/ui.css +1 -0
- data/cockpit/js/compliance.js +813 -10
- data/cockpit/js/instance.js +7 -7
- data/cockpit/js/wfadaptor.js +4 -5
- data/cockpit/llm.html +20 -22
- data/cockpit/llmmodel.html +18 -18
- data/cockpit/themes/THEMES.MD +419 -0
- data/cockpit/themes/preset_model/symbols/critical.svg +13 -3
- data/cpee.gemspec +1 -1
- metadata +2 -1
data/cockpit/js/compliance.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
var latestComplianceResult = null;
|
|
2
|
+
|
|
1
3
|
$(document).ready(function() { //{{{
|
|
2
4
|
$.ajax({
|
|
3
5
|
type: "GET",
|
|
@@ -5,21 +7,223 @@ $(document).ready(function() { //{{{
|
|
|
5
7
|
dataType: "xml",
|
|
6
8
|
success: function(rng){
|
|
7
9
|
save['requirements'] = new RelaxNGui(rng,$('#dat_requirements'));
|
|
10
|
+
initialize_nl_requirements_from_saved_requirements();
|
|
8
11
|
}
|
|
9
12
|
});
|
|
10
13
|
|
|
14
|
+
initialize_nl_requirements_tab();
|
|
15
|
+
|
|
16
|
+
function loadComplianceLog(uuid, options) {
|
|
17
|
+
var renderOptions = options || {};
|
|
18
|
+
var baseUrl = 'https://cpee.org/comp-log/';
|
|
19
|
+
var currentUrl = baseUrl + uuid + '.xes.yaml';
|
|
20
|
+
var allUrl = baseUrl + uuid + '.all.xes.yaml';
|
|
21
|
+
|
|
22
|
+
$('#comp-verify-all-log').attr('href', allUrl);
|
|
23
|
+
$('#areacomp .yourlog').show();
|
|
24
|
+
$('#areacomp .yourlog [style]').show();
|
|
25
|
+
|
|
26
|
+
function tryLoad(url, fallback) {
|
|
27
|
+
$.ajax({
|
|
28
|
+
type: 'GET',
|
|
29
|
+
url: url,
|
|
30
|
+
dataType: 'text',
|
|
31
|
+
success: function(yaml) {
|
|
32
|
+
$('#comp-verify-current-log').attr('href', url).text(url.split('/').pop()).show();
|
|
33
|
+
displayComplianceMessages(yaml, renderOptions);
|
|
34
|
+
},
|
|
35
|
+
error: function() {
|
|
36
|
+
if (fallback) {
|
|
37
|
+
tryLoad(fallback, null);
|
|
38
|
+
} else {
|
|
39
|
+
$('#comp_log').html('Could not load compliance log.');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
tryLoad(currentUrl, baseUrl + uuid + '.current.xes.yaml');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getCurrentTestsetXml(onSuccess, onError) {
|
|
49
|
+
if (typeof get_testset !== 'function') {
|
|
50
|
+
onError('Could not collect the current testset XML.');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var deferred = new $.Deferred();
|
|
55
|
+
|
|
56
|
+
deferred.done(function(_name, testset) {
|
|
57
|
+
if (!testset || typeof testset.serializePrettyXML !== 'function') {
|
|
58
|
+
onError('Could not serialize testset XML.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onSuccess(testset.serializePrettyXML());
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
deferred.fail(function() {
|
|
66
|
+
onError('Could not load the current testset.');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
get_testset(deferred);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function extractDescriptionXmlFromTestset(testsetXml) {
|
|
73
|
+
var xmlDoc = $.parseXML(testsetXml);
|
|
74
|
+
var descriptionContainer = xmlDoc.getElementsByTagName('description')[0];
|
|
75
|
+
|
|
76
|
+
if (!descriptionContainer) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var descriptionNode = null;
|
|
81
|
+
for (var i = 0; i < descriptionContainer.childNodes.length; i++) {
|
|
82
|
+
var child = descriptionContainer.childNodes[i];
|
|
83
|
+
if (child.nodeType === 1) {
|
|
84
|
+
descriptionNode = child;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!descriptionNode) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return new XMLSerializer().serializeToString(descriptionNode);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function extractAttributesFromTestset(testsetXml) {
|
|
97
|
+
var attributes = {};
|
|
98
|
+
var xmlDoc = $.parseXML(testsetXml);
|
|
99
|
+
var attributesContainer = xmlDoc.getElementsByTagName('attributes')[0];
|
|
100
|
+
|
|
101
|
+
if (!attributesContainer) {
|
|
102
|
+
return attributes;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (var i = 0; i < attributesContainer.childNodes.length; i++) {
|
|
106
|
+
var child = attributesContainer.childNodes[i];
|
|
107
|
+
if (child.nodeType !== 1) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
attributes[child.localName || child.nodeName] = (child.textContent || '').trim();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return attributes;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractInstanceId() {
|
|
117
|
+
var currentInstanceUrl = $('body').attr('current-instance') || '';
|
|
118
|
+
var match = currentInstanceUrl.match(/\/(\d+)\/?$/);
|
|
119
|
+
return match ? parseInt(match[1], 10) : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function buildSubscriptionLikeNotification(testsetXml, uuid) {
|
|
123
|
+
var attributes = extractAttributesFromTestset(testsetXml);
|
|
124
|
+
var descriptionXml = extractDescriptionXmlFromTestset(testsetXml);
|
|
125
|
+
|
|
126
|
+
if (!descriptionXml) {
|
|
127
|
+
throw new Error('Could not extract description XML from testset.');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
var instanceId = extractInstanceId();
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
cpee: $('body').attr('current-base') || '',
|
|
134
|
+
'instance-url': $('body').attr('current-instance') || '',
|
|
135
|
+
instance: instanceId,
|
|
136
|
+
topic: 'description',
|
|
137
|
+
type: 'event',
|
|
138
|
+
name: 'change',
|
|
139
|
+
timestamp: new Date().toISOString(),
|
|
140
|
+
content: {
|
|
141
|
+
attributes: attributes,
|
|
142
|
+
description: descriptionXml,
|
|
143
|
+
testset: testsetXml,
|
|
144
|
+
},
|
|
145
|
+
'instance-uuid': uuid,
|
|
146
|
+
'instance-name': (attributes.info || 'semantic-verify'),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
11
150
|
$("#verify").click(function(){
|
|
12
151
|
var uuid = save.attributes_raw && save.attributes_raw.uuid;
|
|
13
152
|
if (!uuid) {
|
|
14
|
-
$('#comp_log').html('
|
|
153
|
+
$('#comp_log').html('No instance loaded.');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
loadComplianceLog(uuid);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
$("#semantic_verify").click(function(){
|
|
161
|
+
var uuid = save.attributes_raw && save.attributes_raw.uuid;
|
|
162
|
+
if (!uuid) {
|
|
163
|
+
$('#comp_log').html('No instance loaded.');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
$('#comp_log').html('Preparing semantic verification payload...');
|
|
168
|
+
|
|
169
|
+
getCurrentTestsetXml(
|
|
170
|
+
function(testsetXml) {
|
|
171
|
+
$('#comp_log').html('Running semantic verification...');
|
|
172
|
+
|
|
173
|
+
var notification;
|
|
174
|
+
try {
|
|
175
|
+
notification = buildSubscriptionLikeNotification(testsetXml, uuid);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
$('#comp_log').html(escapeHtml(error.message || 'Failed to build semantic verification payload.'));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
var formData = new FormData();
|
|
182
|
+
formData.append('notification', JSON.stringify(notification));
|
|
183
|
+
formData.append('type', 'event');
|
|
184
|
+
formData.append('topic', 'description');
|
|
185
|
+
formData.append('event', 'change');
|
|
186
|
+
|
|
187
|
+
$.ajax({
|
|
188
|
+
method: 'POST',
|
|
189
|
+
type: 'POST',
|
|
190
|
+
url: 'https://power.bpm.cit.tum.de/compliance/SubscriberSemantic',
|
|
191
|
+
data: formData,
|
|
192
|
+
processData: false,
|
|
193
|
+
contentType: false,
|
|
194
|
+
dataType: 'text',
|
|
195
|
+
success: function(_response, _textStatus, xhr) {
|
|
196
|
+
if (xhr && xhr.status === 200) {
|
|
197
|
+
loadComplianceLog(uuid, { semantic: true });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
var body = xhr && xhr.responseText ? xhr.responseText : 'Semantic verification returned an unexpected response.';
|
|
202
|
+
$('#comp_log').html('<pre>' + escapeHtml(body) + '</pre>');
|
|
203
|
+
},
|
|
204
|
+
error: function(xhr) {
|
|
205
|
+
var body = xhr && xhr.responseText ? xhr.responseText : 'Semantic verification failed.';
|
|
206
|
+
$('#comp_log').html('<pre>' + escapeHtml(body) + '</pre>');
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
function(message) {
|
|
211
|
+
$('#comp_log').html(escapeHtml(message));
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
$("#identify-violations").click(function(){
|
|
217
|
+
var uuid = save.attributes_raw && save.attributes_raw.uuid;
|
|
218
|
+
if (!uuid) {
|
|
219
|
+
$('#comp_log').html('No instance loaded.');
|
|
15
220
|
return;
|
|
16
221
|
}
|
|
17
222
|
|
|
223
|
+
$('#comp_log').html('Loading compliance log...');
|
|
224
|
+
|
|
18
225
|
var baseUrl = 'https://cpee.org/comp-log/';
|
|
19
226
|
var currentUrl = baseUrl + uuid + '.xes.yaml';
|
|
20
|
-
var allUrl = baseUrl + uuid + '.all.xes.yaml';
|
|
21
|
-
|
|
22
|
-
$('#comp-all-log').attr('href', allUrl).show();
|
|
23
227
|
|
|
24
228
|
function tryLoad(url, fallback) {
|
|
25
229
|
$.ajax({
|
|
@@ -27,14 +231,38 @@ $(document).ready(function() { //{{{
|
|
|
27
231
|
url: url,
|
|
28
232
|
dataType: 'text',
|
|
29
233
|
success: function(yaml) {
|
|
30
|
-
|
|
31
|
-
|
|
234
|
+
var formData = new FormData();
|
|
235
|
+
var fileName = uuid + '.xes.yaml';
|
|
236
|
+
formData.append(
|
|
237
|
+
'file',
|
|
238
|
+
new Blob([yaml], { type: 'text/yaml' }),
|
|
239
|
+
fileName
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
$('#comp_log').html('Identifying violations...');
|
|
243
|
+
|
|
244
|
+
$.ajax({
|
|
245
|
+
type: 'POST',
|
|
246
|
+
url: 'https://power.bpm.cit.tum.de/comprepair/violations',
|
|
247
|
+
data: formData,
|
|
248
|
+
processData: false,
|
|
249
|
+
contentType: false,
|
|
250
|
+
dataType: 'json',
|
|
251
|
+
success: function(result) {
|
|
252
|
+
latestComplianceResult = result;
|
|
253
|
+
renderViolationMessages(result);
|
|
254
|
+
},
|
|
255
|
+
error: function(xhr) {
|
|
256
|
+
var detail = xhr && xhr.responseText ? xhr.responseText : 'Unknown error';
|
|
257
|
+
$('#comp_log').html('Violation identification failed: ' + detail);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
32
260
|
},
|
|
33
261
|
error: function() {
|
|
34
262
|
if (fallback) {
|
|
35
263
|
tryLoad(fallback, null);
|
|
36
264
|
} else {
|
|
37
|
-
$('#comp_log').html('
|
|
265
|
+
$('#comp_log').html('Could not load compliance log.');
|
|
38
266
|
}
|
|
39
267
|
}
|
|
40
268
|
});
|
|
@@ -43,6 +271,61 @@ $(document).ready(function() { //{{{
|
|
|
43
271
|
tryLoad(currentUrl, baseUrl + uuid + '.current.xes.yaml');
|
|
44
272
|
});
|
|
45
273
|
|
|
274
|
+
$("#repair-violations").click(function(){
|
|
275
|
+
var uuid = save.attributes_raw && save.attributes_raw.uuid;
|
|
276
|
+
if (!uuid) {
|
|
277
|
+
$('#comp_log').html('No instance loaded.');
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!latestComplianceResult) {
|
|
282
|
+
$('#comp_log').html('No compliance result available. Run Identify Violations first.');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
var originalPstXml = getCurrentProcessXml();
|
|
287
|
+
if (!originalPstXml) {
|
|
288
|
+
$('#comp_log').html('Could not extract the current process XML.');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
var formData = new FormData();
|
|
293
|
+
formData.append(
|
|
294
|
+
'original_pst',
|
|
295
|
+
new Blob([originalPstXml], { type: 'application/xml' }),
|
|
296
|
+
'original_pst.xml'
|
|
297
|
+
);
|
|
298
|
+
formData.append(
|
|
299
|
+
'compliance_result',
|
|
300
|
+
new Blob([JSON.stringify(latestComplianceResult)], { type: 'application/json' }),
|
|
301
|
+
'compliance_result.json'
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
$('#comp_log').html('Repairing violations...');
|
|
305
|
+
|
|
306
|
+
$.ajax({
|
|
307
|
+
type: 'POST',
|
|
308
|
+
url: 'https://power.bpm.cit.tum.de/comprepair/repair_full',
|
|
309
|
+
data: formData,
|
|
310
|
+
processData: false,
|
|
311
|
+
contentType: false,
|
|
312
|
+
dataType: 'json',
|
|
313
|
+
success: function(result) {
|
|
314
|
+
if (hasResolutionStrategies(result)) {
|
|
315
|
+
renderResolutionStrategies(result);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
var pretty = JSON.stringify(result, null, 2);
|
|
320
|
+
$('#comp_log').html('<pre>' + escapeHtml(pretty) + '</pre>');
|
|
321
|
+
},
|
|
322
|
+
error: function(xhr) {
|
|
323
|
+
var detail = xhr && xhr.responseText ? xhr.responseText : 'Unknown error';
|
|
324
|
+
$('#comp_log').html('Repair failed: ' + escapeHtml(detail));
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
46
329
|
document.addEventListener('attributes:changed', function (e) {
|
|
47
330
|
let req = $X("<requirements xmlns='http://cpee.org/ns/properties/2.0'/>");
|
|
48
331
|
let reqs;
|
|
@@ -59,6 +342,11 @@ $(document).ready(function() { //{{{
|
|
|
59
342
|
req.append(ele);
|
|
60
343
|
}
|
|
61
344
|
save['requirements'].content(req);
|
|
345
|
+
sync_nl_rows_to_requirement_keys(reqj);
|
|
346
|
+
|
|
347
|
+
let nlReqj = parse_saved_object(save.attributes_raw['NL-requirements']);
|
|
348
|
+
sync_nl_rows_to_requirement_keys(nlReqj);
|
|
349
|
+
apply_nl_requirements_values(nlReqj);
|
|
62
350
|
});
|
|
63
351
|
|
|
64
352
|
var timer;
|
|
@@ -69,6 +357,8 @@ $(document).ready(function() { //{{{
|
|
|
69
357
|
});
|
|
70
358
|
$(document).on('relaxngui_remove', '#dat_requirements', function(event){
|
|
71
359
|
clearTimeout(timer);
|
|
360
|
+
sync_nl_rows_to_requirement_keys(read_requirements_object(), true);
|
|
361
|
+
do_nl_requirements_save(event);
|
|
72
362
|
do_requirements_save(event);
|
|
73
363
|
});
|
|
74
364
|
$(document).on('relaxngui_move', '#dat_requirements', function(event){
|
|
@@ -79,9 +369,342 @@ $(document).ready(function() { //{{{
|
|
|
79
369
|
clearTimeout(timer);
|
|
80
370
|
do_requirements_save(event);
|
|
81
371
|
});
|
|
372
|
+
|
|
373
|
+
var nlTimer;
|
|
374
|
+
$(document).on('input', '#dat_nlrequirements .nlreq-text', function(event){
|
|
375
|
+
clearTimeout(nlTimer);
|
|
376
|
+
nlTimer = setTimeout(function(){ do_nl_requirements_save(event); }, 2000);
|
|
377
|
+
});
|
|
82
378
|
}); //}}}
|
|
83
379
|
|
|
84
|
-
function
|
|
380
|
+
function initialize_nl_requirements_tab() { //{{{
|
|
381
|
+
$('#nlreq_add').on('click', function() {
|
|
382
|
+
add_nl_requirement_row();
|
|
383
|
+
do_nl_requirements_save();
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
$(document).on('click', '.nlreq-extract', function() {
|
|
387
|
+
var row = $(this).closest('.nlreq-row');
|
|
388
|
+
var requirementId = row.attr('data-requirement-id');
|
|
389
|
+
var naturalText = row.find('.nlreq-text').val().trim();
|
|
390
|
+
var status = row.find('.nlreq-status');
|
|
391
|
+
var button = $(this);
|
|
392
|
+
|
|
393
|
+
if (!naturalText) {
|
|
394
|
+
status.text('Enter text').removeClass('ok').addClass('error');
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (!save['requirements']) {
|
|
398
|
+
status.text('Not ready').removeClass('ok').addClass('error');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
status.text('Extracting...').removeClass('ok error');
|
|
403
|
+
button.prop('disabled', true);
|
|
404
|
+
|
|
405
|
+
extract_ast_from_natural_language(
|
|
406
|
+
naturalText,
|
|
407
|
+
function(ast) {
|
|
408
|
+
upsert_requirement_ast(requirementId, ast);
|
|
409
|
+
do_nl_requirements_save();
|
|
410
|
+
status.text('Added').removeClass('error').addClass('ok');
|
|
411
|
+
button.prop('disabled', false);
|
|
412
|
+
},
|
|
413
|
+
function(message) {
|
|
414
|
+
status.text(message || 'Failed').removeClass('ok').addClass('error');
|
|
415
|
+
button.prop('disabled', false);
|
|
416
|
+
}
|
|
417
|
+
);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
if ($('#dat_nlrequirements .nlreq-row').length === 0) {
|
|
421
|
+
add_nl_requirement_row();
|
|
422
|
+
}
|
|
423
|
+
} //}}}
|
|
424
|
+
|
|
425
|
+
function initialize_nl_requirements_from_saved_requirements() { //{{{
|
|
426
|
+
if (!save['requirements']) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
var reqObject = read_requirements_object();
|
|
430
|
+
sync_nl_rows_to_requirement_keys(reqObject);
|
|
431
|
+
} //}}}
|
|
432
|
+
|
|
433
|
+
function add_nl_requirement_row(requirementId, textValue) { //{{{
|
|
434
|
+
var rid = requirementId || next_requirement_id();
|
|
435
|
+
if ($('#dat_nlrequirements .nlreq-row[data-requirement-id="' + rid + '"]').length > 0) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
var row = $('<div class="nlreq-row"></div>').attr('data-requirement-id', rid);
|
|
440
|
+
row.append('<button type="button" class="nlreq-extract">Extract</button>');
|
|
441
|
+
row.append($('<span class="nlreq-id"></span>').text(rid));
|
|
442
|
+
row.append(
|
|
443
|
+
$('<input type="text" class="nlreq-text" placeholder="Write natural-language requirement"/>')
|
|
444
|
+
.val(textValue || '')
|
|
445
|
+
);
|
|
446
|
+
row.append('<span class="nlreq-status"></span>');
|
|
447
|
+
$('#dat_nlrequirements').append(row);
|
|
448
|
+
} //}}}
|
|
449
|
+
|
|
450
|
+
function sync_nl_rows_to_requirement_keys(requirementsObject, pruneMissing) { //{{{
|
|
451
|
+
if (!requirementsObject || typeof requirementsObject !== 'object') {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
var keys = Object.keys(requirementsObject);
|
|
456
|
+
|
|
457
|
+
if (pruneMissing) {
|
|
458
|
+
$('#dat_nlrequirements .nlreq-row').each(function() {
|
|
459
|
+
var rid = $(this).attr('data-requirement-id');
|
|
460
|
+
if (rid && keys.indexOf(rid) === -1) {
|
|
461
|
+
$(this).remove();
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
keys.forEach(function(key) {
|
|
467
|
+
if ($('#dat_nlrequirements .nlreq-row[data-requirement-id="' + key + '"]').length === 0) {
|
|
468
|
+
add_nl_requirement_row(key, '');
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
} //}}}
|
|
472
|
+
|
|
473
|
+
function next_requirement_id() { //{{{
|
|
474
|
+
var maxId = 0;
|
|
475
|
+
$('#dat_nlrequirements .nlreq-row').each(function() {
|
|
476
|
+
var rid = $(this).attr('data-requirement-id') || '';
|
|
477
|
+
var m = rid.match(/^R(\d+)$/);
|
|
478
|
+
if (m) {
|
|
479
|
+
maxId = Math.max(maxId, parseInt(m[1], 10));
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
var currentReqs = read_requirements_object();
|
|
484
|
+
Object.keys(currentReqs).forEach(function(key) {
|
|
485
|
+
var m = key.match(/^R(\d+)$/);
|
|
486
|
+
if (m) {
|
|
487
|
+
maxId = Math.max(maxId, parseInt(m[1], 10));
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
return 'R' + (maxId + 1);
|
|
492
|
+
} //}}}
|
|
493
|
+
|
|
494
|
+
function upsert_requirement_ast(requirementId, astText) { //{{{
|
|
495
|
+
var reqObject = read_requirements_object();
|
|
496
|
+
reqObject[requirementId] = astText;
|
|
497
|
+
write_requirements_object(reqObject);
|
|
498
|
+
} //}}}
|
|
499
|
+
|
|
500
|
+
function read_requirements_object() { //{{{
|
|
501
|
+
if (!save['requirements']) {
|
|
502
|
+
return {};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
var raw = save['requirements'].save_json();
|
|
506
|
+
if (raw == null || raw === '') {
|
|
507
|
+
return {};
|
|
508
|
+
}
|
|
509
|
+
if (typeof raw === 'string') {
|
|
510
|
+
try {
|
|
511
|
+
return JSON.parse(raw);
|
|
512
|
+
} catch (e) {
|
|
513
|
+
return {};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (typeof raw === 'object') {
|
|
517
|
+
return raw;
|
|
518
|
+
}
|
|
519
|
+
return {};
|
|
520
|
+
} //}}}
|
|
521
|
+
|
|
522
|
+
function parse_saved_object(raw) { //{{{
|
|
523
|
+
if (raw == null || raw === '') {
|
|
524
|
+
return {};
|
|
525
|
+
}
|
|
526
|
+
if (typeof raw === 'object') {
|
|
527
|
+
return raw;
|
|
528
|
+
}
|
|
529
|
+
if (typeof raw === 'string') {
|
|
530
|
+
var normalized = raw;
|
|
531
|
+
if (/\"\s*=>\s*"/g.test(normalized)) {
|
|
532
|
+
normalized = normalized.replace(/\"\s*=>\s*"/g, '\":\"');
|
|
533
|
+
}
|
|
534
|
+
try {
|
|
535
|
+
return JSON.parse(normalized);
|
|
536
|
+
} catch (e) {
|
|
537
|
+
return {};
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return {};
|
|
541
|
+
} //}}}
|
|
542
|
+
|
|
543
|
+
function apply_nl_requirements_values(nlReqObject) { //{{{
|
|
544
|
+
if (!nlReqObject || typeof nlReqObject !== 'object') {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
Object.keys(nlReqObject).forEach(function(key) {
|
|
549
|
+
var row = $('#dat_nlrequirements .nlreq-row[data-requirement-id="' + key + '"]');
|
|
550
|
+
if (row.length > 0) {
|
|
551
|
+
row.find('.nlreq-text').val(nlReqObject[key] || '');
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
} //}}}
|
|
555
|
+
|
|
556
|
+
function read_nl_requirements_object_from_ui() { //{{{
|
|
557
|
+
var data = {};
|
|
558
|
+
$('#dat_nlrequirements .nlreq-row').each(function() {
|
|
559
|
+
var requirementId = $(this).attr('data-requirement-id');
|
|
560
|
+
if (!requirementId) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
var text = $(this).find('.nlreq-text').val();
|
|
564
|
+
data[requirementId] = text == null ? '' : String(text);
|
|
565
|
+
});
|
|
566
|
+
return data;
|
|
567
|
+
} //}}}
|
|
568
|
+
|
|
569
|
+
function write_requirements_object(reqObject) { //{{{
|
|
570
|
+
let req = $X("<requirements xmlns='http://cpee.org/ns/properties/2.0'/>");
|
|
571
|
+
Object.keys(reqObject)
|
|
572
|
+
.sort(sort_requirement_ids)
|
|
573
|
+
.forEach(function(key) {
|
|
574
|
+
let ele = $X("<" + key + " xmlns='http://cpee.org/ns/properties/2.0'/>");
|
|
575
|
+
ele.text(reqObject[key]);
|
|
576
|
+
req.append(ele);
|
|
577
|
+
});
|
|
578
|
+
save['requirements'].content(req);
|
|
579
|
+
do_requirements_save({ target: $('#dat_requirements')[0] });
|
|
580
|
+
} //}}}
|
|
581
|
+
|
|
582
|
+
function sort_requirement_ids(a, b) { //{{{
|
|
583
|
+
var am = String(a).match(/^R(\d+)$/);
|
|
584
|
+
var bm = String(b).match(/^R(\d+)$/);
|
|
585
|
+
if (am && bm) {
|
|
586
|
+
return parseInt(am[1], 10) - parseInt(bm[1], 10);
|
|
587
|
+
}
|
|
588
|
+
return String(a).localeCompare(String(b));
|
|
589
|
+
} //}}}
|
|
590
|
+
|
|
591
|
+
function extract_ast_from_natural_language(naturalText, onSuccess, onError) { //{{{
|
|
592
|
+
var configuredEndpoint = $('body').attr('data-singlerule-endpoint') || '/compextract/singlerule';
|
|
593
|
+
var url = absolute_extract_url(configuredEndpoint);
|
|
594
|
+
var fallbackUrl = absolute_extract_url(configuredEndpoint.replace(/\/?singlerule\/?$/, '/text'));
|
|
595
|
+
|
|
596
|
+
var attempts = [
|
|
597
|
+
{ url: url, payload: { rule: naturalText } },
|
|
598
|
+
{ url: url, payload: { text: naturalText } }
|
|
599
|
+
];
|
|
600
|
+
if (fallbackUrl !== url) {
|
|
601
|
+
attempts.push({ url: fallbackUrl, payload: { text: naturalText } });
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
run_extract_attempt(attempts, 0, onSuccess, onError);
|
|
605
|
+
} //}}}
|
|
606
|
+
|
|
607
|
+
function run_extract_attempt(attempts, index, onSuccess, onError) { //{{{
|
|
608
|
+
if (index >= attempts.length) {
|
|
609
|
+
onError('Extract failed');
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
var attempt = attempts[index];
|
|
614
|
+
$.ajax({
|
|
615
|
+
type: 'POST',
|
|
616
|
+
url: attempt.url,
|
|
617
|
+
contentType: 'application/json',
|
|
618
|
+
dataType: 'json',
|
|
619
|
+
data: JSON.stringify(attempt.payload),
|
|
620
|
+
success: function(response) {
|
|
621
|
+
var ast = parse_extracted_ast(response);
|
|
622
|
+
if (ast) {
|
|
623
|
+
onSuccess(ast);
|
|
624
|
+
} else {
|
|
625
|
+
run_extract_attempt(attempts, index + 1, onSuccess, onError);
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
error: function() {
|
|
629
|
+
run_extract_attempt(attempts, index + 1, onSuccess, onError);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
} //}}}
|
|
633
|
+
|
|
634
|
+
function parse_extracted_ast(response) { //{{{
|
|
635
|
+
if (!response) {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (typeof response.rule === 'string' && response.rule.trim()) {
|
|
640
|
+
return response.rule.trim();
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
var payload = response.asts != null ? response.asts : response.ast;
|
|
644
|
+
if (payload == null) {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (typeof payload === 'string') {
|
|
649
|
+
var trimmed = payload.trim();
|
|
650
|
+
if (!trimmed) {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
try {
|
|
654
|
+
var parsed = JSON.parse(trimmed);
|
|
655
|
+
return first_rule_from_payload(parsed) || trimmed;
|
|
656
|
+
} catch (e) {
|
|
657
|
+
return trimmed;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return first_rule_from_payload(payload);
|
|
662
|
+
} //}}}
|
|
663
|
+
|
|
664
|
+
function first_rule_from_payload(payload) { //{{{
|
|
665
|
+
if (!payload) {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
if (typeof payload.rule === 'string' && payload.rule.trim()) {
|
|
669
|
+
return payload.rule.trim();
|
|
670
|
+
}
|
|
671
|
+
if (payload.ast && typeof payload.ast === 'object') {
|
|
672
|
+
var nested = first_rule_from_payload(payload.ast);
|
|
673
|
+
if (nested) {
|
|
674
|
+
return nested;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (Array.isArray(payload)) {
|
|
678
|
+
if (payload.length === 0) {
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
return typeof payload[0] === 'string' ? payload[0] : JSON.stringify(payload[0]);
|
|
682
|
+
}
|
|
683
|
+
if (payload.rules && Array.isArray(payload.rules) && payload.rules.length > 0) {
|
|
684
|
+
return typeof payload.rules[0] === 'string' ? payload.rules[0] : JSON.stringify(payload.rules[0]);
|
|
685
|
+
}
|
|
686
|
+
if (payload.rules && typeof payload.rules === 'string') {
|
|
687
|
+
return payload.rules;
|
|
688
|
+
}
|
|
689
|
+
if (typeof payload === 'object') {
|
|
690
|
+
return JSON.stringify(payload);
|
|
691
|
+
}
|
|
692
|
+
return null;
|
|
693
|
+
} //}}}
|
|
694
|
+
|
|
695
|
+
function absolute_extract_url(endpoint) { //{{{
|
|
696
|
+
if (/^https?:\/\//.test(endpoint)) {
|
|
697
|
+
return endpoint;
|
|
698
|
+
}
|
|
699
|
+
if (endpoint.charAt(0) !== '/') {
|
|
700
|
+
endpoint = '/' + endpoint;
|
|
701
|
+
}
|
|
702
|
+
return window.location.origin + endpoint;
|
|
703
|
+
} //}}}
|
|
704
|
+
|
|
705
|
+
function displayComplianceMessages(yaml, options) { //{{{
|
|
706
|
+
var renderOptions = options || {};
|
|
707
|
+
var semanticMode = !!renderOptions.semantic;
|
|
85
708
|
var messages = [];
|
|
86
709
|
var parts = yaml.split(/\n---\s*\n/);
|
|
87
710
|
|
|
@@ -107,12 +730,26 @@ function displayComplianceMessages(yaml) { //{{{
|
|
|
107
730
|
messages.push(val);
|
|
108
731
|
}
|
|
109
732
|
|
|
733
|
+
if (semanticMode) {
|
|
734
|
+
var firstRequirementIndex = -1;
|
|
735
|
+
for (var k = 0; k < messages.length; k++) {
|
|
736
|
+
if (/^Verifying Requirement R/.test(messages[k])) {
|
|
737
|
+
firstRequirementIndex = k;
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (firstRequirementIndex >= 0) {
|
|
743
|
+
messages = messages.slice(firstRequirementIndex);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
110
747
|
if (messages.length === 0) {
|
|
111
748
|
$('#comp_log').html('<div>No messages found.</div>');
|
|
112
749
|
return;
|
|
113
750
|
}
|
|
114
751
|
|
|
115
|
-
var html = '<div>Pre-Tests</div>';
|
|
752
|
+
var html = semanticMode ? '' : '<div>Pre-Tests</div>';
|
|
116
753
|
for (var j = 0; j < messages.length; j++) {
|
|
117
754
|
var msg = messages[j];
|
|
118
755
|
var isHeader = /^Verifying Requirement R/.test(msg) || /^Requirement R/.test(msg);
|
|
@@ -122,13 +759,179 @@ function displayComplianceMessages(yaml) { //{{{
|
|
|
122
759
|
$('#comp_log').html(html);
|
|
123
760
|
} //}}}
|
|
124
761
|
|
|
762
|
+
function renderViolationMessages(result) { //{{{
|
|
763
|
+
var violations = Array.isArray(result && result.violations)
|
|
764
|
+
? result.violations
|
|
765
|
+
: [];
|
|
766
|
+
|
|
767
|
+
var html = '<div>Violations:</div>';
|
|
768
|
+
|
|
769
|
+
if (violations.length === 0) {
|
|
770
|
+
html += '<div class="indent">None</div>';
|
|
771
|
+
$('#comp_log').html(html);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
for (var i = 0; i < violations.length; i++) {
|
|
776
|
+
var violation = violations[i] || {};
|
|
777
|
+
var requirementId = violation.requirement_id || 'unknown';
|
|
778
|
+
|
|
779
|
+
html += '<div class="indent">' + escapeHtml(requirementId) + '</div>';
|
|
780
|
+
|
|
781
|
+
var evidenceList = Array.isArray(violation.evidence)
|
|
782
|
+
? violation.evidence
|
|
783
|
+
: [];
|
|
784
|
+
|
|
785
|
+
for (var j = 0; j < evidenceList.length; j++) {
|
|
786
|
+
html += '<div class="indent indent-level-2">'
|
|
787
|
+
+ escapeHtml(String(evidenceList[j]))
|
|
788
|
+
+ '</div>';
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
$('#comp_log').html(html);
|
|
793
|
+
} //}}}
|
|
794
|
+
|
|
795
|
+
function hasResolutionStrategies(result) { //{{{
|
|
796
|
+
return !!(
|
|
797
|
+
result
|
|
798
|
+
&& Array.isArray(result.resolution_strategies)
|
|
799
|
+
);
|
|
800
|
+
} //}}}
|
|
801
|
+
|
|
802
|
+
function renderResolutionStrategies(result) { //{{{
|
|
803
|
+
var strategies = result.resolution_strategies;
|
|
804
|
+
|
|
805
|
+
if (!Array.isArray(strategies)) {
|
|
806
|
+
var pretty = JSON.stringify(result, null, 2);
|
|
807
|
+
$('#comp_log').html('<pre>' + escapeHtml(pretty) + '</pre>');
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
var html = '';
|
|
812
|
+
|
|
813
|
+
for (var i = 0; i < strategies.length; i++) {
|
|
814
|
+
var strategy = strategies[i] || {};
|
|
815
|
+
var requirementId = strategy.requirement_id || 'unknown';
|
|
816
|
+
var changeDescription = strategy.change_description || strategy.resolution_strategy || 'N/A';
|
|
817
|
+
var riskValue = extractRiskValue(strategy.change_risk);
|
|
818
|
+
var status = typeof strategy.status === 'string' ? strategy.status.toLowerCase() : '';
|
|
819
|
+
var pstXml = typeof strategy.pst_xml === 'string' ? strategy.pst_xml : '';
|
|
820
|
+
|
|
821
|
+
html += '<div>Resolution ' + escapeHtml(requirementId) + ':</div>';
|
|
822
|
+
html += '<div class="indent">Repair Action: ' + escapeHtml(changeDescription) + '</div>';
|
|
823
|
+
html += '<div class="indent">Risk: ' + escapeHtml(riskValue) + '</div>';
|
|
824
|
+
|
|
825
|
+
if (status === 'success' && pstXml) {
|
|
826
|
+
html += '<div class="indent">PST: ' + buildPstDownloadLink(pstXml, requirementId, i) + '</div>';
|
|
827
|
+
} else {
|
|
828
|
+
html += '<div class="indent error">Error</div>';
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (html === '') {
|
|
833
|
+
html = '<pre>' + escapeHtml(JSON.stringify(result, null, 2)) + '</pre>';
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
$('#comp_log').html(html);
|
|
837
|
+
} //}}}
|
|
838
|
+
|
|
839
|
+
function extractRiskValue(changeRisk) { //{{{
|
|
840
|
+
if (changeRisk && typeof changeRisk === 'object') {
|
|
841
|
+
if (typeof changeRisk.value === 'string' && changeRisk.value.trim()) {
|
|
842
|
+
return changeRisk.value.trim();
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (typeof changeRisk === 'string' && changeRisk.trim()) {
|
|
847
|
+
return changeRisk.trim();
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return 'N/A';
|
|
851
|
+
} //}}}
|
|
852
|
+
|
|
853
|
+
function buildPstDownloadLink(pstXml, requirementId, index) { //{{{
|
|
854
|
+
var blob = new Blob([pstXml], { type: 'application/xml' });
|
|
855
|
+
var url = URL.createObjectURL(blob);
|
|
856
|
+
var fileName = 'resolution_' + requirementId + '_' + index + '.xml';
|
|
857
|
+
return '<a href="' + escapeHtml(url) + '" download="' + escapeHtml(fileName) + '">Download the PST</a>';
|
|
858
|
+
} //}}}
|
|
859
|
+
|
|
860
|
+
function escapeHtml(value) { //{{{
|
|
861
|
+
return $('<span>').text(value == null ? '' : String(value)).html();
|
|
862
|
+
} //}}}
|
|
863
|
+
|
|
864
|
+
function getCurrentProcessXml() { //{{{
|
|
865
|
+
if (
|
|
866
|
+
save['graph_adaptor']
|
|
867
|
+
&& typeof save['graph_adaptor'].get_description === 'function'
|
|
868
|
+
) {
|
|
869
|
+
return save['graph_adaptor'].get_description();
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (
|
|
873
|
+
save['graph']
|
|
874
|
+
&& typeof save['graph'].serializePrettyXML === 'function'
|
|
875
|
+
) {
|
|
876
|
+
return save['graph'].serializePrettyXML();
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return null;
|
|
880
|
+
} //}}}
|
|
881
|
+
|
|
125
882
|
function do_requirements_save(event) { //{{{
|
|
126
883
|
if (save['requirements'].has_changed()) {
|
|
127
884
|
save['requirements'].set_checkpoint();
|
|
128
885
|
var url = $('body').attr('current-instance');
|
|
129
886
|
let reqj = save['requirements'].save_json();
|
|
130
887
|
let att = save['attributes'].save();
|
|
131
|
-
$(' > attributes
|
|
888
|
+
let attributesNode = $(' > attributes', att);
|
|
889
|
+
if (attributesNode.length === 0) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
let requirementsNode = attributesNode.children('requirements');
|
|
893
|
+
if (requirementsNode.length === 0) {
|
|
894
|
+
requirementsNode = $X("<requirements xmlns='http://cpee.org/ns/properties/2.0'/>");
|
|
895
|
+
attributesNode.append(requirementsNode);
|
|
896
|
+
}
|
|
897
|
+
requirementsNode.text(reqj);
|
|
132
898
|
do_parameters_save_part('attributes',$(att).serializePrettyXML());
|
|
133
899
|
}
|
|
134
900
|
} //}}}
|
|
901
|
+
|
|
902
|
+
function do_nl_requirements_save(event) { //{{{
|
|
903
|
+
if (!save['attributes']) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Keep regular requirements in sync when NL requirements are persisted.
|
|
908
|
+
if (save['requirements']) {
|
|
909
|
+
do_requirements_save({ target: $('#dat_requirements')[0] });
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
let nlReqj = JSON.stringify(read_nl_requirements_object_from_ui());
|
|
913
|
+
let att = save['attributes'].save();
|
|
914
|
+
let attributesNode = $(' > attributes', att);
|
|
915
|
+
if (attributesNode.length === 0) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
let nlReqNode = attributesNode.children('NL-requirements');
|
|
920
|
+
if (nlReqNode.length === 0) {
|
|
921
|
+
nlReqNode = $X("<NL-requirements xmlns='http://cpee.org/ns/properties/2.0'/>");
|
|
922
|
+
attributesNode.append(nlReqNode);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (save['requirements']) {
|
|
926
|
+
let reqj = save['requirements'].save_json();
|
|
927
|
+
let requirementsNode = attributesNode.children('requirements');
|
|
928
|
+
if (requirementsNode.length === 0) {
|
|
929
|
+
requirementsNode = $X("<requirements xmlns='http://cpee.org/ns/properties/2.0'/>");
|
|
930
|
+
attributesNode.append(requirementsNode);
|
|
931
|
+
}
|
|
932
|
+
requirementsNode.text(reqj);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
nlReqNode.text(nlReqj);
|
|
936
|
+
do_parameters_save_part('attributes',$(att).serializePrettyXML());
|
|
937
|
+
} //}}}
|