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