Kea2-python 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

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.

Potentially problematic release.


This version of Kea2-python might be problematic. Click here for more details.

@@ -118,7 +118,7 @@
118
118
  }
119
119
 
120
120
  .table-custom thead {
121
- background-color: var(--primary-color);
121
+ background-color: #495057;
122
122
  color: white;
123
123
  }
124
124
 
@@ -182,7 +182,6 @@
182
182
  border-radius: 3px;
183
183
  }
184
184
 
185
- /* 增加板块间距 */
186
185
  .section-block {
187
186
  margin-bottom: 70px;
188
187
  }
@@ -232,7 +231,7 @@
232
231
  }
233
232
 
234
233
  .activity-list {
235
- height: 300px; /* 固定高度 */
234
+ height: 550px;
236
235
  overflow-y: auto;
237
236
  border-radius: 8px;
238
237
  border: 1px solid rgba(0,0,0,0.1);
@@ -240,7 +239,7 @@
240
239
  background-color: rgba(255,255,255,0.5);
241
240
  scrollbar-width: thin;
242
241
  scrollbar-color: var(--primary-color) #eee;
243
- margin-bottom: 15px; /* 为翻页按钮留出空间 */
242
+ margin-bottom: 15px;
244
243
  }
245
244
 
246
245
  .activity-list::-webkit-scrollbar {
@@ -260,7 +259,7 @@
260
259
  .activities-container {
261
260
  display: flex;
262
261
  flex-direction: column;
263
- height: 400px; /* 容器总高度 */
262
+ height: 650px;
264
263
  }
265
264
 
266
265
  .pagination-container {
@@ -287,7 +286,101 @@
287
286
  transform: translateX(3px);
288
287
  }
289
288
 
290
- /* Responsive adjustments */
289
+ .nav-tabs .nav-link {
290
+ color: #666;
291
+ border: 1px solid transparent;
292
+ border-radius: 6px 6px 0 0;
293
+ font-weight: 500;
294
+ transition: all 0.3s ease;
295
+ }
296
+
297
+ .nav-tabs .nav-link:hover {
298
+ color: var(--primary-color);
299
+ border-color: rgba(52, 152, 219, 0.2);
300
+ background-color: rgba(52, 152, 219, 0.05);
301
+ }
302
+
303
+ .nav-tabs .nav-link.active {
304
+ color: var(--primary-color);
305
+ background-color: white;
306
+ border-color: #dee2e6 #dee2e6 #fff;
307
+ font-weight: 600;
308
+ }
309
+
310
+ .tab-content {
311
+ border: 1px solid #dee2e6;
312
+ border-top: none;
313
+ border-radius: 0 0 8px 8px;
314
+ padding: 20px;
315
+ background-color: #fafafa;
316
+ }
317
+
318
+ .sorting-controls {
319
+ background-color: #f8f9fa;
320
+ border: 1px solid #e9ecef;
321
+ border-radius: 8px;
322
+ padding: 15px;
323
+ margin-bottom: 20px;
324
+ }
325
+
326
+ .sorting-controls .form-select {
327
+ min-width: 140px;
328
+ }
329
+
330
+ .sorting-controls .btn {
331
+ transition: all 0.3s ease;
332
+ }
333
+
334
+ .sorting-controls .btn:hover {
335
+ transform: translateY(-1px);
336
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
337
+ }
338
+
339
+ .sort-icon {
340
+ margin-left: 8px;
341
+ transition: all 0.3s ease;
342
+ font-size: 1.4rem;
343
+ color: #ffffff !important;
344
+ opacity: 0.6;
345
+ text-shadow: 0 0 3px rgba(0,0,0,0.3);
346
+ }
347
+
348
+ .sort-icon:hover {
349
+ opacity: 1;
350
+ transform: scale(1.2);
351
+ text-shadow: 0 0 5px rgba(0,0,0,0.5);
352
+ }
353
+
354
+ .sort-icon.active {
355
+ opacity: 1;
356
+ font-weight: bold;
357
+ text-shadow: 0 0 5px rgba(0,0,0,0.5);
358
+ }
359
+
360
+ .sort-icon.asc.active {
361
+ color: #40e0d0 !important;
362
+ }
363
+
364
+ .sort-icon.desc.active {
365
+ color: #ff6b6b !important;
366
+ }
367
+
368
+ .table-violations thead {
369
+ background-color: #dc3545 !important;
370
+ color: white;
371
+ }
372
+
373
+ .table-violations .sort-icon {
374
+ color: #ffffff !important;
375
+ text-shadow: 0 0 3px rgba(0,0,0,0.4);
376
+ }
377
+
378
+ .table-violations .sort-icon:hover {
379
+ opacity: 1;
380
+ transform: scale(1.2);
381
+ text-shadow: 0 0 5px rgba(0,0,0,0.6);
382
+ }
383
+
291
384
  @media (max-width: 768px) {
292
385
  .stat-value {
293
386
  font-size: 1.5rem;
@@ -321,66 +414,46 @@
321
414
  <div class="summary-card">
322
415
  <h2 class="section-title">Test Summary</h2>
323
416
  <div class="row g-4">
324
- <div class="col-md-3 col-sm-6">
417
+ <div class="col-md-2 col-sm-6">
325
418
  <div class="text-center">
326
419
  <i class="bi bi-bug text-danger" style="font-size: 2rem;"></i>
327
420
  <span class="stat-value value-danger">{{ bugs_found }}</span>
328
421
  <span class="stat-label">Bugs Found</span>
329
422
  </div>
330
423
  </div>
331
- <div class="col-md-3 col-sm-6">
424
+ <div class="col-md-2 col-sm-6">
332
425
  <div class="text-center">
333
426
  <i class="bi bi-clock-history text-primary" style="font-size: 2rem;"></i>
334
- <span class="stat-value value-highlight">{{ (total_testing_time / 60)|int }} min: {{ (total_testing_time % 60)|int }} sec</span>
427
+ <span class="stat-value value-highlight">{{ total_testing_time }}</span>
335
428
  <span class="stat-label">Total Testing Time</span>
336
429
  </div>
337
430
  </div>
338
- <div class="col-md-3 col-sm-6">
431
+ <div class="col-md-2 col-sm-6">
339
432
  <div class="text-center">
340
433
  <i class="bi bi-activity text-success" style="font-size: 2rem;"></i>
341
434
  <span class="stat-value value-success">{{ executed_events }}</span>
342
435
  <span class="stat-label">Executed Events</span>
343
436
  </div>
344
437
  </div>
345
- <div class="col-md-3 col-sm-6">
438
+ <div class="col-md-2 col-sm-6">
346
439
  <div class="text-center">
347
440
  <i class="bi bi-pie-chart text-warning" style="font-size: 2rem;"></i>
348
441
  <span class="stat-value value-warning">{{ coverage_percent }}%</span>
349
442
  <span class="stat-label">Activity Coverage</span>
350
443
  </div>
351
444
  </div>
352
- </div>
353
- </div>
354
- </div>
355
- </div>
356
-
357
- <!-- Key Statistics -->
358
- <div class="row g-4 mb-4">
359
- <div class="col-12">
360
- <div class="stats-card">
361
- <div class="card-header bg-success text-white">
362
- <i class="bi bi-bar-chart"></i> Coverage Statistics
363
- </div>
364
- <div class="card-body">
365
- <div class="mb-3">
366
- <h5 class="d-flex justify-content-between">
367
- <span>Activity Coverage:</span>
368
- <span class="value-warning">{{ coverage_percent }}%</span>
369
- </h5>
370
- <div class="progress">
371
- <div class="progress-bar bg-warning" role="progressbar"
372
- style="width: {{ coverage_percent }}%;"
373
- aria-valuenow="{{ coverage_percent }}" aria-valuemin="0" aria-valuemax="100"></div>
445
+ <div class="col-md-2 col-sm-6">
446
+ <div class="text-center">
447
+ <i class="bi bi-list-check text-info" style="font-size: 2rem;"></i>
448
+ <span class="stat-value value-highlight">{{ all_properties_count }}</span>
449
+ <span class="stat-label">All Properties</span>
374
450
  </div>
375
451
  </div>
376
- <div class="row mt-4">
377
- <div class="col-6 text-center">
378
- <div class="stat-value value-primary">{{ total_activities_count }}</div>
379
- <div class="stat-label">Total Activities</div>
380
- </div>
381
- <div class="col-6 text-center">
382
- <div class="stat-value value-success">{{ tested_activities_count }}</div>
383
- <div class="stat-label">Tested Activities</div>
452
+ <div class="col-md-2 col-sm-6">
453
+ <div class="text-center">
454
+ <i class="bi bi-check-square text-secondary" style="font-size: 2rem;"></i>
455
+ <span class="stat-value value-success">{{ executed_properties_count }}</span>
456
+ <span class="stat-label">Executed Properties</span>
384
457
  </div>
385
458
  </div>
386
459
  </div>
@@ -388,21 +461,56 @@
388
461
  </div>
389
462
  </div>
390
463
 
464
+ <!-- Coverage Trend Chart -->
465
+ <div class="section-block">
466
+ <h2 class="section-title">Coverage Trend</h2>
467
+ <div class="chart-container">
468
+ <canvas id="coverageChart"></canvas>
469
+ </div>
470
+ </div>
471
+
391
472
  <!-- Tested Activities List -->
392
473
  <div class="section-block">
393
474
  <h2 class="section-title">Activities Coverage</h2>
394
475
 
395
- <div class="row g-4">
396
- <!-- Tested Activities Panel -->
397
- <div class="col-md-6">
398
- <div class="card">
399
- <div class="card-header bg-warning text-white">
400
- <div class="d-flex justify-content-between align-items-center">
401
- <span><i class="bi bi-check-circle"></i> Tested Activities ({{ tested_activities|length }})</span>
402
- <span class="badge bg-light text-dark">{{ tested_activities|length }} / {{ total_activities_count }}</span>
476
+ <div class="card">
477
+ <div class="card-header bg-primary text-white">
478
+ <div class="d-flex justify-content-between align-items-center">
479
+ <span><i class="bi bi-app"></i> Activities Coverage Overview</span>
480
+ <span class="badge bg-light text-dark">Coverage: {{ coverage_percent }}%</span>
481
+ </div>
482
+ </div>
483
+ <div class="card-body">
484
+ <!-- Navigation Tabs -->
485
+ <ul class="nav nav-tabs mb-3" id="activitiesTabs" role="tablist">
486
+ <li class="nav-item" role="presentation">
487
+ <button class="nav-link active" id="tested-tab" data-bs-toggle="tab"
488
+ data-bs-target="#tested-activities" type="button" role="tab"
489
+ aria-controls="tested-activities" aria-selected="true">
490
+ <i class="bi bi-check-circle"></i> Tested Activities ({{ tested_activities|length }})
491
+ </button>
492
+ </li>
493
+ <li class="nav-item" role="presentation">
494
+ <button class="nav-link" id="all-tab" data-bs-toggle="tab"
495
+ data-bs-target="#all-activities" type="button" role="tab"
496
+ aria-controls="all-activities" aria-selected="false">
497
+ <i class="bi bi-app"></i> All Activities ({{ total_activities|length }})
498
+ </button>
499
+ </li>
500
+ </ul>
501
+
502
+ <!-- Tab Content -->
503
+ <div class="tab-content" id="activitiesTabContent">
504
+ <!-- Tested Activities Tab -->
505
+ <div class="tab-pane fade show active" id="tested-activities" role="tabpanel"
506
+ aria-labelledby="tested-tab">
507
+ <div class="d-flex justify-content-between align-items-center mb-3">
508
+ <h5 class="mb-0 text-success">
509
+ <i class="bi bi-check-circle-fill"></i> Tested Activities
510
+ </h5>
511
+ <span class="badge bg-success">{{ tested_activities|length }} / {{ total_activities_count }}</span>
403
512
  </div>
404
- </div>
405
- <div class="card-body">
513
+
406
514
  <div class="activities-container">
407
515
  <div class="activity-list">
408
516
  {% if tested_activities|length > 0 %}
@@ -421,28 +529,36 @@
421
529
  {% endif %}
422
530
  </div>
423
531
  <!-- Pagination for Tested Activities -->
424
- <div class="pagination-container d-flex justify-content-center">
532
+ <div class="pagination-container d-flex justify-content-between align-items-center">
533
+ <div class="d-flex align-items-center">
534
+ <label for="tested-page-size" class="form-label me-2 mb-0">Show:</label>
535
+ <select class="form-select form-select-sm" id="tested-page-size" style="width: auto;">
536
+ <option value="5">5</option>
537
+ <option value="10" selected>10</option>
538
+ <option value="20">20</option>
539
+ <option value="50">50</option>
540
+ <option value="100">100</option>
541
+ </select>
542
+ </div>
425
543
  <nav aria-label="Tested Activities Pagination">
426
- <ul class="pagination pagination-sm" id="tested-pagination">
544
+ <ul class="pagination pagination-sm mb-0" id="tested-pagination">
427
545
  <!-- Pagination will be generated by JavaScript -->
428
546
  </ul>
429
547
  </nav>
430
548
  </div>
431
549
  </div>
432
550
  </div>
433
- </div>
434
- </div>
435
-
436
- <!-- All Activities Panel -->
437
- <div class="col-md-6">
438
- <div class="card">
439
- <div class="card-header bg-primary text-white">
440
- <div class="d-flex justify-content-between align-items-center">
441
- <span><i class="bi bi-app"></i> All Activities ({{ total_activities|length }})</span>
442
- <span class="badge bg-light text-dark">Coverage: {{ coverage_percent }}%</span>
551
+
552
+ <!-- All Activities Tab -->
553
+ <div class="tab-pane fade" id="all-activities" role="tabpanel"
554
+ aria-labelledby="all-tab">
555
+ <div class="d-flex justify-content-between align-items-center mb-3">
556
+ <h5 class="mb-0 text-primary">
557
+ <i class="bi bi-app"></i> All Activities Overview
558
+ </h5>
559
+ <span class="badge bg-primary">Total: {{ total_activities|length }}</span>
443
560
  </div>
444
- </div>
445
- <div class="card-body">
561
+
446
562
  <div class="activities-container">
447
563
  <div class="activity-list">
448
564
  {% if total_activities|length > 0 %}
@@ -465,9 +581,19 @@
465
581
  {% endif %}
466
582
  </div>
467
583
  <!-- Pagination for All Activities -->
468
- <div class="pagination-container d-flex justify-content-center">
584
+ <div class="pagination-container d-flex justify-content-between align-items-center">
585
+ <div class="d-flex align-items-center">
586
+ <label for="all-page-size" class="form-label me-2 mb-0">Show:</label>
587
+ <select class="form-select form-select-sm" id="all-page-size" style="width: auto;">
588
+ <option value="5">5</option>
589
+ <option value="10" selected>10</option>
590
+ <option value="20">20</option>
591
+ <option value="50">50</option>
592
+ <option value="100">100</option>
593
+ </select>
594
+ </div>
469
595
  <nav aria-label="All Activities Pagination">
470
- <ul class="pagination pagination-sm" id="all-pagination">
596
+ <ul class="pagination pagination-sm mb-0" id="all-pagination">
471
597
  <!-- Pagination will be generated by JavaScript -->
472
598
  </ul>
473
599
  </nav>
@@ -500,20 +626,12 @@
500
626
  </div>
501
627
  {% endif %}
502
628
 
503
- <!-- Coverage Trend Chart -->
504
- <div class="section-block">
505
- <h2 class="section-title">Coverage Trend</h2>
506
- <div class="chart-container">
507
- <canvas id="coverageChart"></canvas>
508
- </div>
509
- </div>
510
-
511
629
  <!-- Property Violations List -->
512
630
  {% if take_screenshots %}
513
631
  <div class="section-block">
514
632
  <h2 class="section-title">Property Violations</h2>
515
633
  <div class="table-responsive">
516
- <table class="table table-custom">
634
+ <table class="table table-custom table-violations">
517
635
  <thead>
518
636
  <tr>
519
637
  <th>Index</th>
@@ -537,9 +655,19 @@
537
655
  </table>
538
656
 
539
657
  <!-- Pagination for Property Violations -->
540
- <div class="d-flex justify-content-center mt-3">
658
+ <div class="d-flex justify-content-between align-items-center mt-3">
659
+ <div class="d-flex align-items-center">
660
+ <label for="violations-page-size" class="form-label me-2 mb-0">Show:</label>
661
+ <select class="form-select form-select-sm" id="violations-page-size" style="width: auto;">
662
+ <option value="5">5</option>
663
+ <option value="10" selected>10</option>
664
+ <option value="20">20</option>
665
+ <option value="50">50</option>
666
+ <option value="100">100</option>
667
+ </select>
668
+ </div>
541
669
  <nav aria-label="Property Violations Pagination">
542
- <ul class="pagination pagination-sm" id="violations-pagination">
670
+ <ul class="pagination pagination-sm mb-0" id="violations-pagination">
543
671
  <!-- Pagination will be generated by JavaScript -->
544
672
  </ul>
545
673
  </nav>
@@ -551,6 +679,7 @@
551
679
  <!-- Property Checking Statistics -->
552
680
  <div class="section-block">
553
681
  <h2 class="section-title">Property Checking Statistics</h2>
682
+
554
683
  <div class="table-responsive">
555
684
  <table class="table table-custom">
556
685
  <thead>
@@ -559,28 +688,158 @@
559
688
  <th>Property Name</th>
560
689
  <th>Precondition Satisfied</th>
561
690
  <th>Executed</th>
562
- <th>Fails</th>
563
- <th>Errors</th>
691
+ <th>Fails <i class="bi bi-arrow-down-up text-muted sort-icon" id="fails-sort" data-column="fails" data-order="none" style="cursor: pointer;"></i></th>
692
+ <th>Errors <i class="bi bi-arrow-down-up text-muted sort-icon" id="errors-sort" data-column="errors" data-order="none" style="cursor: pointer;"></i></th>
693
+ <th>Error Details</th>
564
694
  </tr>
565
695
  </thead>
566
696
  <tbody id="property-stats-container">
567
697
  {% for property_name, test_result in property_stats.items() %}
568
- <tr class="property-stat-row" data-page="1">
698
+ <tr class="property-stat-row" data-page="1"
699
+ data-index="{{ loop.index }}"
700
+ data-property-name="{{ property_name }}"
701
+ data-precond-satisfied="{{ test_result.precond_satisfied|default(0) }}"
702
+ data-executed="{{ test_result.executed|default(0) }}"
703
+ data-fails="{{ test_result.fail|default(0) }}"
704
+ data-errors="{{ test_result.error|default(0) }}">
569
705
  <td>{{ loop.index }}</td>
570
706
  <td><span class="badge bg-light text-dark badge-custom">{{ property_name }}</span></td>
571
707
  <td>{{ test_result.precond_satisfied|default(0) }}</td>
572
708
  <td>{{ test_result.executed|default(0) }}</td>
573
709
  <td><span class="badge bg-danger text-white">{{ test_result.fail|default(0) }}</span></td>
574
710
  <td><span class="badge bg-warning text-dark">{{ test_result.error|default(0) }}</span></td>
711
+ <td>
712
+ {% if (test_result.fail|default(0) > 0 or test_result.error|default(0) > 0) and property_name in property_error_details %}
713
+ {% set error_list = property_error_details[property_name] %}
714
+ {% set property_index = loop.index %}
715
+ {% if error_list|length == 1 %}
716
+ <!-- Single error - simple display -->
717
+ <button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
718
+ data-bs-target="#single-error-detail-{{ property_index }}" aria-expanded="false"
719
+ aria-controls="single-error-detail-{{ property_index }}">
720
+ <i class="bi bi-exclamation-triangle"></i> View Error
721
+ </button>
722
+ <div class="collapse mt-2" id="single-error-detail-{{ property_index }}">
723
+ <div class="card card-body bg-light">
724
+ <div class="mb-2">
725
+ <span class="badge bg-{{ 'danger' if error_list[0].state == 'fail' else 'warning' }}">
726
+ {{ error_list[0].state|upper }}
727
+ </span>
728
+ {% if error_list[0].occurrence_count > 1 %}
729
+ <span class="badge bg-info ms-2">
730
+ Occurred {{ error_list[0].occurrence_count }} times
731
+ </span>
732
+ {% endif %}
733
+ </div>
734
+ {% if error_list[0].short_description %}
735
+ <div class="mb-2">
736
+ <strong>Error:</strong> <code>{{ error_list[0].short_description }}</code>
737
+ </div>
738
+ {% endif %}
739
+ <details>
740
+ <summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Traceback</summary>
741
+ <pre class="text-danger mb-0" style="font-size: 0.85rem; white-space: pre-wrap;">{{ error_list[0].traceback }}</pre>
742
+ </details>
743
+ </div>
744
+ </div>
745
+ {% else %}
746
+ <!-- Multiple errors - tabbed display -->
747
+ <button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
748
+ data-bs-target="#multi-error-detail-{{ property_index }}" aria-expanded="false"
749
+ aria-controls="multi-error-detail-{{ property_index }}">
750
+ <i class="bi bi-exclamation-triangle"></i> View {{ error_list|length }} Errors
751
+ </button>
752
+ <div class="collapse mt-2" id="multi-error-detail-{{ property_index }}">
753
+ <div class="card card-body bg-light">
754
+ <!-- Error summary -->
755
+ <div class="mb-3">
756
+ <h6 class="text-danger">Multiple Errors Detected</h6>
757
+ <div class="d-flex flex-wrap gap-1">
758
+ {% for error in error_list %}
759
+ <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
760
+ {{ error.state|upper }} #{{ loop.index }}
761
+ {% if error.occurrence_count > 1 %} ({{ error.occurrence_count }}x){% endif %}
762
+ </span>
763
+ {% endfor %}
764
+ </div>
765
+ </div>
766
+
767
+ <!-- Error tabs -->
768
+ <ul class="nav nav-pills nav-fill mb-3" id="multi-error-tabs-{{ property_index }}" role="tablist">
769
+ {% for error in error_list %}
770
+ <li class="nav-item" role="presentation">
771
+ <button class="nav-link {{ 'active' if loop.first else '' }} btn-sm"
772
+ id="multi-error-tab-{{ property_index }}-{{ loop.index }}"
773
+ data-bs-toggle="pill"
774
+ data-bs-target="#multi-error-content-{{ property_index }}-{{ loop.index }}"
775
+ type="button" role="tab"
776
+ aria-controls="multi-error-content-{{ property_index }}-{{ loop.index }}"
777
+ aria-selected="{{ 'true' if loop.first else 'false' }}">
778
+ <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }} me-1">
779
+ {{ error.state|upper }}
780
+ </span>
781
+ #{{ loop.index }}
782
+ </button>
783
+ </li>
784
+ {% endfor %}
785
+ </ul>
786
+
787
+ <!-- Error content -->
788
+ <div class="tab-content" id="multi-error-content-{{ property_index }}">
789
+ {% for error in error_list %}
790
+ <div class="tab-pane fade {{ 'show active' if loop.first else '' }}"
791
+ id="multi-error-content-{{ property_index }}-{{ loop.index }}"
792
+ role="tabpanel"
793
+ aria-labelledby="multi-error-tab-{{ property_index }}-{{ loop.index }}">
794
+ <div class="mb-2">
795
+ <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
796
+ {{ error.state|upper }} #{{ loop.index }}
797
+ </span>
798
+ <small class="text-muted ms-2">Error {{ loop.index }} of {{ loop.length }}</small>
799
+ {% if error.occurrence_count > 1 %}
800
+ <span class="badge bg-info ms-2">
801
+ {{ error.occurrence_count }} occurrences
802
+ </span>
803
+ {% endif %}
804
+ </div>
805
+ {% if error.short_description %}
806
+ <div class="mb-2">
807
+ <strong>Error:</strong> <code>{{ error.short_description }}</code>
808
+ </div>
809
+ {% endif %}
810
+ <details open="{{ loop.first }}">
811
+ <summary class="btn btn-sm btn-outline-secondary mb-2">{{ 'Hide' if loop.first else 'Show' }} Full Traceback</summary>
812
+ <pre class="text-danger mb-0" style="font-size: 0.85rem; white-space: pre-wrap;">{{ error.traceback }}</pre>
813
+ </details>
814
+ </div>
815
+ {% endfor %}
816
+ </div>
817
+ </div>
818
+ </div>
819
+ {% endif %}
820
+ {% else %}
821
+ <span class="text-muted">-</span>
822
+ {% endif %}
823
+ </td>
575
824
  </tr>
576
825
  {% endfor %}
577
826
  </tbody>
578
827
  </table>
579
828
 
580
829
  <!-- Pagination for Property Checking Statistics -->
581
- <div class="d-flex justify-content-center mt-3">
830
+ <div class="d-flex justify-content-between align-items-center mt-3">
831
+ <div class="d-flex align-items-center">
832
+ <label for="stats-page-size" class="form-label me-2 mb-0">Show:</label>
833
+ <select class="form-select form-select-sm" id="stats-page-size" style="width: auto;">
834
+ <option value="5">5</option>
835
+ <option value="10" selected>10</option>
836
+ <option value="20">20</option>
837
+ <option value="50">50</option>
838
+ <option value="100">100</option>
839
+ </select>
840
+ </div>
582
841
  <nav aria-label="Property Stats Pagination">
583
- <ul class="pagination pagination-sm" id="stats-pagination">
842
+ <ul class="pagination pagination-sm mb-0" id="stats-pagination">
584
843
  <!-- Pagination will be generated by JavaScript -->
585
844
  </ul>
586
845
  </nav>
@@ -605,12 +864,12 @@
605
864
  console.log("Coverage data points:", coverageData.length);
606
865
 
607
866
  coverageData.sort(function(a, b) {
608
- return a.steps - b.steps;
867
+ return a.stepsCount - b.stepsCount;
609
868
  });
610
869
 
611
- var steps = coverageData.map(function(item) { return item.steps; });
870
+ var steps = coverageData.map(function(item) { return item.stepsCount; });
612
871
  var coverages = coverageData.map(function(item) { return item.coverage; });
613
- var testedActivities = coverageData.map(function(item) { return item.tested_activities_count; });
872
+ var testedActivities = coverageData.map(function(item) { return item.testedActivitiesCount; });
614
873
 
615
874
  if (steps.length > 0 && steps[0] > 0) {
616
875
  steps.unshift(0);
@@ -711,7 +970,7 @@
711
970
  stepSize: steps.length > 0 ? Math.max(1, Math.ceil(Math.max(...steps) / 10)) : 1,
712
971
  callback: function(value) {
713
972
  if (Number.isInteger(value) && value <= (steps.length > 0 ? Math.max(...steps) : 10)) {
714
- return value;
973
+ return value;
715
974
  }
716
975
  return null;
717
976
  }
@@ -761,81 +1020,81 @@
761
1020
  });
762
1021
 
763
1022
  // Initialize pagination for Activities lists
764
- initPagination('tested-activities-container', 'tested-activity', 'tested-pagination', {{ items_per_page }});
765
- initPagination('all-activities-container', 'all-activity', 'all-pagination', {{ items_per_page }});
1023
+ initPagination('tested-activities-container', 'tested-activity', 'tested-pagination', 'tested-page-size');
1024
+ initPagination('all-activities-container', 'all-activity', 'all-pagination', 'all-page-size');
766
1025
 
767
1026
  // Initialize pagination for Property tables
768
- initPagination('property-violations-container', 'property-violation-row', 'violations-pagination', {{ items_per_page }});
769
- initPagination('property-stats-container', 'property-stat-row', 'stats-pagination', {{ items_per_page }});
1027
+ initPagination('property-violations-container', 'property-violation-row', 'violations-pagination', 'violations-page-size');
1028
+ initPagination('property-stats-container', 'property-stat-row', 'stats-pagination', 'stats-page-size');
770
1029
 
771
- // Pagination function
772
- function initPagination(containerId, itemClass, paginationId, itemsPerPage) {
773
- const container = document.getElementById(containerId);
774
- if (!container) return;
775
-
776
- const items = container.getElementsByClassName(itemClass);
777
- const totalItems = items.length;
778
- const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage));
779
-
780
- // Create pagination
781
- const paginationElement = document.getElementById(paginationId);
782
- if (!paginationElement) return;
783
-
784
- // Clear pagination
785
- paginationElement.innerHTML = '';
786
-
787
- // Previous button
788
- const prevLi = document.createElement('li');
789
- prevLi.className = 'page-item disabled';
790
- prevLi.innerHTML = '<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>';
791
- paginationElement.appendChild(prevLi);
792
-
793
- // Add page numbers
794
- for (let i = 1; i <= totalPages; i++) {
795
- const pageLi = document.createElement('li');
796
- pageLi.className = i === 1 ? 'page-item active' : 'page-item';
797
- pageLi.innerHTML = `<a class="page-link" href="#">${i}</a>`;
798
- pageLi.addEventListener('click', function(e) {
799
- e.preventDefault();
800
- goToPage(i, containerId, itemClass, itemsPerPage, paginationId);
801
- });
802
- paginationElement.appendChild(pageLi);
803
- }
804
-
805
- // Next button
806
- const nextLi = document.createElement('li');
807
- nextLi.className = totalPages <= 1 ? 'page-item disabled' : 'page-item';
808
- nextLi.innerHTML = '<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>';
809
- paginationElement.appendChild(nextLi);
810
-
811
- // Initialize page 1
812
- goToPage(1, containerId, itemClass, itemsPerPage, paginationId);
1030
+ // Initialize sorting for Property Checking Statistics
1031
+ initSorting();
1032
+
1033
+ // Simplified sorting function for Fails and Errors columns
1034
+ function initSorting() {
1035
+ const sortIcons = document.querySelectorAll('.sort-icon');
813
1036
 
814
- // Add event listeners to prev/next buttons
815
- if (paginationElement.firstChild) {
816
- paginationElement.firstChild.addEventListener('click', function(e) {
817
- e.preventDefault();
818
- const activePage = paginationElement.querySelector('.active');
819
- if (activePage && activePage.previousElementSibling && activePage.previousElementSibling.previousElementSibling) {
820
- const pageNum = parseInt(activePage.textContent) - 1;
821
- if (pageNum >= 1) {
822
- goToPage(pageNum, containerId, itemClass, itemsPerPage, paginationId);
1037
+ sortIcons.forEach(function(icon) {
1038
+ icon.addEventListener('click', function() {
1039
+ const column = this.dataset.column;
1040
+ const currentOrder = this.dataset.order;
1041
+
1042
+ // Reset all other sort icons
1043
+ sortIcons.forEach(function(otherIcon) {
1044
+ if (otherIcon !== icon) {
1045
+ otherIcon.dataset.order = 'none';
1046
+ otherIcon.className = 'bi bi-arrow-down-up sort-icon';
1047
+ otherIcon.style.color = '#6c757d';
823
1048
  }
1049
+ });
1050
+
1051
+ // Toggle current sort order
1052
+ let newOrder;
1053
+ if (currentOrder === 'none' || currentOrder === 'desc') {
1054
+ newOrder = 'asc';
1055
+ this.className = 'bi bi-arrow-up sort-icon active asc';
1056
+ this.style.color = '#28a745';
1057
+ } else {
1058
+ newOrder = 'desc';
1059
+ this.className = 'bi bi-arrow-down sort-icon active desc';
1060
+ this.style.color = '#dc3545';
824
1061
  }
1062
+
1063
+ this.dataset.order = newOrder;
1064
+ sortTable(column, newOrder);
825
1065
  });
826
- }
1066
+ });
827
1067
 
828
- if (paginationElement.lastChild) {
829
- paginationElement.lastChild.addEventListener('click', function(e) {
830
- e.preventDefault();
831
- const activePage = paginationElement.querySelector('.active');
832
- if (activePage && activePage.nextElementSibling && activePage.nextElementSibling.nextElementSibling) {
833
- const pageNum = parseInt(activePage.textContent) + 1;
834
- if (pageNum <= totalPages) {
835
- goToPage(pageNum, containerId, itemClass, itemsPerPage, paginationId);
836
- }
1068
+ function sortTable(column, order) {
1069
+ const container = document.getElementById('property-stats-container');
1070
+ const rows = Array.from(container.getElementsByClassName('property-stat-row'));
1071
+
1072
+ rows.sort(function(a, b) {
1073
+ let valueA, valueB;
1074
+
1075
+ if (column === 'fails') {
1076
+ valueA = parseInt(a.dataset.fails);
1077
+ valueB = parseInt(b.dataset.fails);
1078
+ } else if (column === 'errors') {
1079
+ valueA = parseInt(a.dataset.errors);
1080
+ valueB = parseInt(b.dataset.errors);
837
1081
  }
1082
+
1083
+ if (order === 'asc') {
1084
+ return valueA - valueB;
1085
+ } else {
1086
+ return valueB - valueA;
1087
+ }
1088
+ });
1089
+
1090
+ // Clear container and append sorted rows
1091
+ container.innerHTML = '';
1092
+ rows.forEach(function(row) {
1093
+ container.appendChild(row);
838
1094
  });
1095
+
1096
+ // Re-initialize pagination after sorting
1097
+ initPagination('property-stats-container', 'property-stat-row', 'stats-pagination', 'stats-page-size');
839
1098
  }
840
1099
  }
841
1100
 
@@ -884,6 +1143,94 @@
884
1143
  }
885
1144
  }
886
1145
 
1146
+ // Pagination function
1147
+ function initPagination(containerId, itemClass, paginationId, pageSizeSelectId) {
1148
+ const container = document.getElementById(containerId);
1149
+ const pageSizeSelect = document.getElementById(pageSizeSelectId);
1150
+ if (!container) return;
1151
+
1152
+ // Get initial page size
1153
+ let itemsPerPage = pageSizeSelect ? parseInt(pageSizeSelect.value) : 10;
1154
+
1155
+ // Add event listener for page size changes
1156
+ if (pageSizeSelect) {
1157
+ pageSizeSelect.addEventListener('change', function() {
1158
+ itemsPerPage = parseInt(this.value);
1159
+ renderPagination();
1160
+ goToPage(1, containerId, itemClass, itemsPerPage, paginationId);
1161
+ });
1162
+ }
1163
+
1164
+ function renderPagination() {
1165
+ const items = container.getElementsByClassName(itemClass);
1166
+ const totalItems = items.length;
1167
+ const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage));
1168
+
1169
+ // Create pagination
1170
+ const paginationElement = document.getElementById(paginationId);
1171
+ if (!paginationElement) return;
1172
+
1173
+ // Clear pagination
1174
+ paginationElement.innerHTML = '';
1175
+
1176
+ // Previous button
1177
+ const prevLi = document.createElement('li');
1178
+ prevLi.className = 'page-item disabled';
1179
+ prevLi.innerHTML = '<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>';
1180
+ paginationElement.appendChild(prevLi);
1181
+
1182
+ // Add page numbers
1183
+ for (let i = 1; i <= totalPages; i++) {
1184
+ const pageLi = document.createElement('li');
1185
+ pageLi.className = i === 1 ? 'page-item active' : 'page-item';
1186
+ pageLi.innerHTML = `<a class="page-link" href="#">${i}</a>`;
1187
+ pageLi.addEventListener('click', function(e) {
1188
+ e.preventDefault();
1189
+ goToPage(i, containerId, itemClass, itemsPerPage, paginationId);
1190
+ });
1191
+ paginationElement.appendChild(pageLi);
1192
+ }
1193
+
1194
+ // Next button
1195
+ const nextLi = document.createElement('li');
1196
+ nextLi.className = totalPages <= 1 ? 'page-item disabled' : 'page-item';
1197
+ nextLi.innerHTML = '<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>';
1198
+ paginationElement.appendChild(nextLi);
1199
+
1200
+ // Add event listeners to prev/next buttons
1201
+ if (paginationElement.firstChild) {
1202
+ paginationElement.firstChild.addEventListener('click', function(e) {
1203
+ e.preventDefault();
1204
+ const activePage = paginationElement.querySelector('.active');
1205
+ if (activePage && activePage.previousElementSibling && activePage.previousElementSibling.previousElementSibling) {
1206
+ const pageNum = parseInt(activePage.textContent) - 1;
1207
+ if (pageNum >= 1) {
1208
+ goToPage(pageNum, containerId, itemClass, itemsPerPage, paginationId);
1209
+ }
1210
+ }
1211
+ });
1212
+ }
1213
+
1214
+ if (paginationElement.lastChild) {
1215
+ paginationElement.lastChild.addEventListener('click', function(e) {
1216
+ e.preventDefault();
1217
+ const activePage = paginationElement.querySelector('.active');
1218
+ if (activePage && activePage.nextElementSibling && activePage.nextElementSibling.nextElementSibling) {
1219
+ const pageNum = parseInt(activePage.textContent) + 1;
1220
+ const totalPages = Math.ceil(container.getElementsByClassName(itemClass).length / itemsPerPage);
1221
+ if (pageNum <= totalPages) {
1222
+ goToPage(pageNum, containerId, itemClass, itemsPerPage, paginationId);
1223
+ }
1224
+ }
1225
+ });
1226
+ }
1227
+ }
1228
+
1229
+ // Initial render
1230
+ renderPagination();
1231
+ goToPage(1, containerId, itemClass, itemsPerPage, paginationId);
1232
+ }
1233
+
887
1234
  // Scroll functionality
888
1235
  const screenshots = document.getElementById('screenshots');
889
1236
  if (screenshots) {