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.
@@ -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('<p>No instance loaded.</p>');
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
- $('#comp-current-log').attr('href', url).text(url.split('/').pop()).show();
31
- displayComplianceMessages(yaml);
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('<p>Could not load compliance log.</p>');
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 displayComplianceMessages(yaml) { //{{{
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 > requirements', att).text(reqj);
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
+ } //}}}