Spaces:
Running
Running
Update templates/result.html
Browse files- templates/result.html +89 -344
templates/result.html
CHANGED
@@ -8,8 +8,10 @@
|
|
8 |
table { border-collapse: collapse; width: 60%; margin-top: 15px; }
|
9 |
th, td { border: 1px solid #ddd; padding: 8px; }
|
10 |
th { background-color: #f4f4f4; }
|
11 |
-
|
12 |
-
.
|
|
|
|
|
13 |
.improvement { color: #2c3e50; background-color: #ecf0f1; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
|
14 |
.improvement-value { color: #27ae60; font-weight: bold; }
|
15 |
.ai-badge { background-color: #3498db; color: white; padding: 3px 8px; border-radius: 3px; font-size: 0.8em; margin-left: 10px; }
|
@@ -462,45 +464,37 @@
|
|
462 |
<div class="collapsible-content">
|
463 |
<div class="aibom-property">
|
464 |
<div class="property-name">Generated:</div>
|
465 |
-
<div class="property-value">{{ aibom.metadata.timestamp if aibom.metadata.timestamp else 'Not specified' }}</div>
|
466 |
</div>
|
467 |
|
468 |
-
{% if aibom.metadata.
|
469 |
<div class="aibom-property">
|
470 |
-
<div class="property-name">
|
471 |
<div class="property-value">
|
472 |
-
{% for
|
473 |
-
<div>{{
|
474 |
{% endfor %}
|
475 |
</div>
|
476 |
</div>
|
477 |
{% endif %}
|
478 |
|
479 |
-
{% if aibom.metadata.
|
480 |
<div class="aibom-property">
|
481 |
-
<div class="property-name">
|
482 |
<div class="property-value">
|
483 |
-
{% for
|
484 |
-
<div>{{
|
485 |
{% endfor %}
|
486 |
</div>
|
487 |
</div>
|
488 |
{% endif %}
|
489 |
|
490 |
-
{% if aibom.metadata.properties %}
|
491 |
<div class="aibom-property">
|
492 |
<div class="property-name">Properties:</div>
|
493 |
<div class="property-value">
|
494 |
{% for prop in aibom.metadata.properties %}
|
495 |
-
{
|
496 |
-
<div><strong>Tags:</strong>
|
497 |
-
<!-- Display tags without using split filter -->
|
498 |
-
{% set tag_value = prop.value|replace('[', '')|replace(']', '')|replace('"', '') %}
|
499 |
-
<span class="tag">{{ tag_value }}</span>
|
500 |
-
</div>
|
501 |
-
{% elif prop.name not in ['aibom:quality-score', 'aibom:quality-breakdown', 'aibom:max-scores', 'aibom:completeness-profile', 'aibom:completeness-description', 'aibom:ai-enhanced', 'aibom:ai-model', 'aibom:original-score', 'aibom:score-improvement'] %}
|
502 |
-
<div><strong>{{ prop.name }}:</strong> {{ prop.value }}</div>
|
503 |
-
{% endif %}
|
504 |
{% endfor %}
|
505 |
</div>
|
506 |
</div>
|
@@ -515,68 +509,22 @@
|
|
515 |
<div class="collapsible-content">
|
516 |
{% for ref in aibom.components[0].externalReferences %}
|
517 |
<div class="aibom-property">
|
518 |
-
<div class="property-name">{{ ref.type|
|
519 |
-
<div class="property-value"><a href="{{ ref.url }}" target="_blank">{{ ref.url }}</a></div>
|
520 |
-
</div>
|
521 |
-
{% endfor %}
|
522 |
-
</div>
|
523 |
-
</div>
|
524 |
-
{% endif %}
|
525 |
-
|
526 |
-
<!-- Dependencies Section -->
|
527 |
-
{% if aibom.dependencies %}
|
528 |
-
<div class="aibom-section">
|
529 |
-
<h4 class="collapsible" onclick="toggleCollapsible(this)">Dependencies</h4>
|
530 |
-
<div class="collapsible-content">
|
531 |
-
{% for dep in aibom.dependencies %}
|
532 |
-
<div class="aibom-property">
|
533 |
-
<div class="property-name">{{ dep.ref }}:</div>
|
534 |
<div class="property-value">
|
535 |
-
{
|
536 |
-
|
537 |
-
<div>{{ dependency }}</div>
|
538 |
-
{% endfor %}
|
539 |
-
{% else %}
|
540 |
-
No dependencies specified
|
541 |
-
{% endif %}
|
542 |
</div>
|
543 |
</div>
|
544 |
{% endfor %}
|
545 |
</div>
|
546 |
</div>
|
547 |
{% endif %}
|
548 |
-
|
549 |
-
<!-- SBOM Information Section -->
|
550 |
-
<div class="aibom-section">
|
551 |
-
<h4 class="collapsible" onclick="toggleCollapsible(this)">SBOM Information</h4>
|
552 |
-
<div class="collapsible-content">
|
553 |
-
<div class="aibom-property">
|
554 |
-
<div class="property-name">Format:</div>
|
555 |
-
<div class="property-value">{{ aibom.bomFormat }}</div>
|
556 |
-
</div>
|
557 |
-
<div class="aibom-property">
|
558 |
-
<div class="property-name">Spec Version:</div>
|
559 |
-
<div class="property-value">{{ aibom.specVersion }}</div>
|
560 |
-
</div>
|
561 |
-
<div class="aibom-property">
|
562 |
-
<div class="property-name">Serial Number:</div>
|
563 |
-
<div class="property-value">{{ aibom.serialNumber }}</div>
|
564 |
-
</div>
|
565 |
-
<div class="aibom-property">
|
566 |
-
<div class="property-name">Version:</div>
|
567 |
-
<div class="property-value">{{ aibom.version }}</div>
|
568 |
-
</div>
|
569 |
-
</div>
|
570 |
-
</div>
|
571 |
</div>
|
572 |
</div>
|
573 |
|
574 |
<div id="json-view" class="tab-content">
|
575 |
<div class="json-view">
|
576 |
-
<pre
|
577 |
-
</div>
|
578 |
-
<div style="margin-top: 15px;">
|
579 |
-
<button onclick="downloadJSON()">Download JSON</button>
|
580 |
</div>
|
581 |
</div>
|
582 |
|
@@ -586,19 +534,22 @@
|
|
586 |
<!-- Field Tier Legend -->
|
587 |
<div class="tier-legend">
|
588 |
<div class="tier-legend-item">
|
589 |
-
<span class="field-tier tier-critical"></span>
|
|
|
590 |
</div>
|
591 |
<div class="tier-legend-item">
|
592 |
-
<span class="field-tier tier-important"></span>
|
|
|
593 |
</div>
|
594 |
<div class="tier-legend-item">
|
595 |
-
<span class="field-tier tier-supplementary"></span>
|
|
|
596 |
</div>
|
597 |
</div>
|
598 |
|
599 |
<ul>
|
600 |
{% for field, status in completeness_score.field_checklist.items() %}
|
601 |
-
{% if
|
602 |
<li class="present">{{ status }} {{ field }}
|
603 |
{% if completeness_score.field_tiers and field in completeness_score.field_tiers %}
|
604 |
<span class="field-tier tier-{{ completeness_score.field_tiers[field] }}"></span>
|
@@ -854,297 +805,77 @@
|
|
854 |
<li>
|
855 |
<strong>Fix Validation Issues</strong> (removes validation penalty):
|
856 |
<ul>
|
857 |
-
{% for
|
858 |
-
<li>{{
|
859 |
{% endfor %}
|
860 |
</ul>
|
861 |
</li>
|
862 |
{% endif %}
|
863 |
|
864 |
-
{% if completeness_score.completeness_profile and completeness_score.completeness_profile.next_level %}
|
865 |
-
{% set has_recommendations = true %}
|
866 |
-
<li>
|
867 |
-
<strong>Upgrade to {{ completeness_score.completeness_profile.next_level.name }} Profile</strong>:
|
868 |
-
<ul>
|
869 |
-
{% for field in completeness_score.completeness_profile.next_level.missing_fields[:5] %}
|
870 |
-
<li>Add {{ field }}</li>
|
871 |
-
{% endfor %}
|
872 |
-
{% if completeness_score.completeness_profile.next_level.missing_fields|length > 5 %}
|
873 |
-
<li>... and {{ completeness_score.completeness_profile.next_level.missing_fields|length - 5 }} more fields</li>
|
874 |
-
{% endif %}
|
875 |
-
</ul>
|
876 |
-
</li>
|
877 |
-
{% endif %}
|
878 |
-
|
879 |
{% if not has_recommendations %}
|
880 |
<li>Your AI SBOM is already well-documented. Great job!</li>
|
881 |
{% endif %}
|
882 |
</ol>
|
883 |
</div>
|
884 |
|
885 |
-
<!-- Scoring Rubric
|
886 |
<div class="scoring-rubric">
|
887 |
-
<h4
|
888 |
-
<
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
<
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
<
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
<
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
<
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
<
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
<table>
|
922 |
-
<tr>
|
923 |
-
<th>Field</th>
|
924 |
-
<th>Points</th>
|
925 |
-
<th>Criteria</th>
|
926 |
-
</tr>
|
927 |
-
<tr>
|
928 |
-
<td>timestamp</td>
|
929 |
-
<td>5</td>
|
930 |
-
<td>Must be a valid ISO 8601 timestamp</td>
|
931 |
-
</tr>
|
932 |
-
<tr>
|
933 |
-
<td>tools</td>
|
934 |
-
<td>5</td>
|
935 |
-
<td>Must include at least one tool with name, vendor, and version</td>
|
936 |
-
</tr>
|
937 |
-
<tr>
|
938 |
-
<td>authors</td>
|
939 |
-
<td>5</td>
|
940 |
-
<td>Must include at least one author with name</td>
|
941 |
-
</tr>
|
942 |
-
<tr>
|
943 |
-
<td>component</td>
|
944 |
-
<td>5</td>
|
945 |
-
<td>Must include component metadata</td>
|
946 |
-
</tr>
|
947 |
-
</table>
|
948 |
-
|
949 |
-
<h5>Component Basic Info (20% of total score)</h5>
|
950 |
-
<table>
|
951 |
-
<tr>
|
952 |
-
<th>Field</th>
|
953 |
-
<th>Points</th>
|
954 |
-
<th>Criteria</th>
|
955 |
-
</tr>
|
956 |
-
<tr>
|
957 |
-
<td>type</td>
|
958 |
-
<td>2</td>
|
959 |
-
<td>Must be "machine-learning-model"</td>
|
960 |
-
</tr>
|
961 |
-
<tr>
|
962 |
-
<td>name</td>
|
963 |
-
<td>4</td>
|
964 |
-
<td>Must be present</td>
|
965 |
-
</tr>
|
966 |
-
<tr>
|
967 |
-
<td>bom-ref</td>
|
968 |
-
<td>2</td>
|
969 |
-
<td>Must be present</td>
|
970 |
-
</tr>
|
971 |
-
<tr>
|
972 |
-
<td>purl</td>
|
973 |
-
<td>4</td>
|
974 |
-
<td>Must start with "pkg:huggingface/"</td>
|
975 |
-
</tr>
|
976 |
-
<tr>
|
977 |
-
<td>description</td>
|
978 |
-
<td>4</td>
|
979 |
-
<td>Must be present and longer than 20 characters</td>
|
980 |
-
</tr>
|
981 |
-
<tr>
|
982 |
-
<td>licenses</td>
|
983 |
-
<td>4</td>
|
984 |
-
<td>Must use valid SPDX license identifiers</td>
|
985 |
-
</tr>
|
986 |
-
</table>
|
987 |
-
|
988 |
-
<h5>Model Card (30% of total score)</h5>
|
989 |
-
<table>
|
990 |
-
<tr>
|
991 |
-
<th>Field</th>
|
992 |
-
<th>Points</th>
|
993 |
-
<th>Criteria</th>
|
994 |
-
</tr>
|
995 |
-
<tr>
|
996 |
-
<td>modelParameters</td>
|
997 |
-
<td>10</td>
|
998 |
-
<td>Must include model parameters</td>
|
999 |
-
</tr>
|
1000 |
-
<tr>
|
1001 |
-
<td>quantitativeAnalysis</td>
|
1002 |
-
<td>10</td>
|
1003 |
-
<td>Must include quantitative analysis</td>
|
1004 |
-
</tr>
|
1005 |
-
<tr>
|
1006 |
-
<td>considerations</td>
|
1007 |
-
<td>10</td>
|
1008 |
-
<td>Must be present and longer than 50 characters</td>
|
1009 |
-
</tr>
|
1010 |
-
</table>
|
1011 |
-
|
1012 |
-
<h5>External References (10% of total score)</h5>
|
1013 |
-
<table>
|
1014 |
-
<tr>
|
1015 |
-
<th>Reference Type</th>
|
1016 |
-
<th>Points</th>
|
1017 |
-
<th>Maximum</th>
|
1018 |
-
</tr>
|
1019 |
-
<tr>
|
1020 |
-
<td>Model Card URL</td>
|
1021 |
-
<td>4 per reference</td>
|
1022 |
-
<td rowspan="3">10</td>
|
1023 |
-
</tr>
|
1024 |
-
<tr>
|
1025 |
-
<td>Hugging Face or GitHub URL</td>
|
1026 |
-
<td>3 per reference</td>
|
1027 |
-
</tr>
|
1028 |
-
<tr>
|
1029 |
-
<td>Dataset URL</td>
|
1030 |
-
<td>3 per reference</td>
|
1031 |
-
</tr>
|
1032 |
-
</table>
|
1033 |
-
</div>
|
1034 |
-
</div>
|
1035 |
-
|
1036 |
-
<!-- Validation Summary if available -->
|
1037 |
-
{% if completeness_score.validation %}
|
1038 |
-
<div class="aibom-section">
|
1039 |
-
<h4>Validation Results</h4>
|
1040 |
-
<div class="aibom-property">
|
1041 |
-
<div class="property-name">Status:</div>
|
1042 |
-
<div class="property-value">
|
1043 |
-
{% if completeness_score.validation.valid %}
|
1044 |
-
<span style="color: green;">✓ Valid</span>
|
1045 |
-
{% else %}
|
1046 |
-
<span style="color: red;">✗ Invalid</span>
|
1047 |
-
{% endif %}
|
1048 |
-
</div>
|
1049 |
-
</div>
|
1050 |
-
|
1051 |
-
<div class="aibom-property">
|
1052 |
-
<div class="property-name">Issues:</div>
|
1053 |
-
<div class="property-value">
|
1054 |
-
<span style="color: red;">Errors: {{ completeness_score.validation.summary.error_count }}</span><br>
|
1055 |
-
<span style="color: orange;">Warnings: {{ completeness_score.validation.summary.warning_count }}</span><br>
|
1056 |
-
<span style="color: blue;">Info: {{ completeness_score.validation.summary.info_count }}</span>
|
1057 |
-
</div>
|
1058 |
-
</div>
|
1059 |
-
|
1060 |
-
{% if not completeness_score.validation.valid and completeness_score.validation.issues %}
|
1061 |
-
<div class="aibom-property">
|
1062 |
-
<div class="property-name">Top Issues:</div>
|
1063 |
-
<div class="property-value">
|
1064 |
-
<ul>
|
1065 |
-
{% for issue in completeness_score.validation.issues[:3] %}
|
1066 |
-
<li>
|
1067 |
-
{% if issue.severity == 'error' %}
|
1068 |
-
<span style="color: red;">[ERROR]</span>
|
1069 |
-
{% elif issue.severity == 'warning' %}
|
1070 |
-
<span style="color: orange;">[WARNING]</span>
|
1071 |
-
{% else %}
|
1072 |
-
<span style="color: blue;">[INFO]</span>
|
1073 |
-
{% endif %}
|
1074 |
-
{{ issue.message }}
|
1075 |
-
</li>
|
1076 |
-
{% endfor %}
|
1077 |
-
{% if completeness_score.validation.issues|length > 3 %}
|
1078 |
-
<li>... and {{ completeness_score.validation.issues|length - 3 }} more issues</li>
|
1079 |
-
{% endif %}
|
1080 |
-
</ul>
|
1081 |
-
</div>
|
1082 |
-
</div>
|
1083 |
-
{% endif %}
|
1084 |
-
|
1085 |
-
{% if completeness_score.validation.recommendations %}
|
1086 |
-
<div class="aibom-property">
|
1087 |
-
<div class="property-name">Recommendations:</div>
|
1088 |
-
<div class="property-value">
|
1089 |
-
<ul>
|
1090 |
-
{% for rec in completeness_score.validation.recommendations[:3] %}
|
1091 |
-
<li>{{ rec }}</li>
|
1092 |
-
{% endfor %}
|
1093 |
-
{% if completeness_score.validation.recommendations|length > 3 %}
|
1094 |
-
<li>... and {{ completeness_score.validation.recommendations|length - 3 }} more recommendations</li>
|
1095 |
-
{% endif %}
|
1096 |
-
</ul>
|
1097 |
-
</div>
|
1098 |
-
</div>
|
1099 |
-
{% endif %}
|
1100 |
-
</div>
|
1101 |
-
{% endif %}
|
1102 |
-
|
1103 |
-
<!-- Score Calculation Explanation -->
|
1104 |
-
<div class="aibom-section">
|
1105 |
-
<h4>Score Calculation</h4>
|
1106 |
-
<p>The total score is calculated as a weighted sum of section scores:</p>
|
1107 |
-
<ul>
|
1108 |
-
<li>Required Fields: {{ completeness_score.section_scores.required_fields }}/{{ completeness_score.max_scores.required_fields }} × 20% = {{ (completeness_score.section_scores.required_fields / completeness_score.max_scores.required_fields * 20)|round(2) }}</li>
|
1109 |
-
<li>Metadata: {{ completeness_score.section_scores.metadata }}/{{ completeness_score.max_scores.metadata }} × 20% = {{ (completeness_score.section_scores.metadata / completeness_score.max_scores.metadata * 20)|round(2) }}</li>
|
1110 |
-
<li>Component Basic: {{ completeness_score.section_scores.component_basic }}/{{ completeness_score.max_scores.component_basic }} × 20% = {{ (completeness_score.section_scores.component_basic / completeness_score.max_scores.component_basic * 20)|round(2) }}</li>
|
1111 |
-
<li>Model Card: {{ completeness_score.section_scores.component_model_card }}/{{ completeness_score.max_scores.component_model_card }} × 30% = {{ (completeness_score.section_scores.component_model_card / completeness_score.max_scores.component_model_card * 30)|round(2) }}</li>
|
1112 |
-
<li>External References: {{ completeness_score.section_scores.external_references }}/{{ completeness_score.max_scores.external_references }} × 10% = {{ (completeness_score.section_scores.external_references / completeness_score.max_scores.external_references * 10)|round(2) }}</li>
|
1113 |
-
</ul>
|
1114 |
-
|
1115 |
-
{% if completeness_score.validation_penalty %}
|
1116 |
-
<p>Validation penalty: {{ completeness_score.validation_penalty }}</p>
|
1117 |
-
{% endif %}
|
1118 |
</div>
|
1119 |
</div>
|
1120 |
-
|
|
|
|
|
1121 |
<script>
|
1122 |
-
function downloadJSON() {
|
1123 |
-
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(document.getElementById('aibom-json').textContent);
|
1124 |
-
const downloadAnchorNode = document.createElement('a');
|
1125 |
-
downloadAnchorNode.setAttribute("href", dataStr);
|
1126 |
-
downloadAnchorNode.setAttribute("download", "{{ model_id }}-aibom.json");
|
1127 |
-
document.body.appendChild(downloadAnchorNode);
|
1128 |
-
downloadAnchorNode.click();
|
1129 |
-
downloadAnchorNode.remove();
|
1130 |
-
}
|
1131 |
-
|
1132 |
function switchTab(tabId) {
|
1133 |
// Hide all tab contents
|
1134 |
-
|
1135 |
-
for (
|
1136 |
tabContents[i].classList.remove('active');
|
1137 |
}
|
1138 |
|
1139 |
// Deactivate all tabs
|
1140 |
-
|
1141 |
-
for (
|
1142 |
tabs[i].classList.remove('active');
|
1143 |
}
|
1144 |
|
1145 |
// Activate selected tab and content
|
1146 |
document.getElementById(tabId).classList.add('active');
|
1147 |
-
|
1148 |
if (selectedTab) {
|
1149 |
selectedTab.classList.add('active');
|
1150 |
}
|
@@ -1152,14 +883,28 @@
|
|
1152 |
|
1153 |
function toggleCollapsible(element) {
|
1154 |
element.classList.toggle('active');
|
1155 |
-
|
1156 |
-
content.classList.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1157 |
}
|
1158 |
|
1159 |
// Initialize collapsible sections
|
1160 |
document.addEventListener('DOMContentLoaded', function() {
|
1161 |
-
|
1162 |
-
for (
|
1163 |
collapsibles[i].nextElementSibling.classList.add('active');
|
1164 |
collapsibles[i].classList.add('active');
|
1165 |
}
|
|
|
8 |
table { border-collapse: collapse; width: 60%; margin-top: 15px; }
|
9 |
th, td { border: 1px solid #ddd; padding: 8px; }
|
10 |
th { background-color: #f4f4f4; }
|
11 |
+
/* Fixed color styling for field checklist items */
|
12 |
+
.missing { color: #e74c3c; } /* Red color for missing fields */
|
13 |
+
.present { color: #27ae60; } /* Green color for present fields */
|
14 |
+
.unknown { color: #f39c12; } /* Orange color for unknown status */
|
15 |
.improvement { color: #2c3e50; background-color: #ecf0f1; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
|
16 |
.improvement-value { color: #27ae60; font-weight: bold; }
|
17 |
.ai-badge { background-color: #3498db; color: white; padding: 3px 8px; border-radius: 3px; font-size: 0.8em; margin-left: 10px; }
|
|
|
464 |
<div class="collapsible-content">
|
465 |
<div class="aibom-property">
|
466 |
<div class="property-name">Generated:</div>
|
467 |
+
<div class="property-value">{{ aibom.metadata.timestamp if aibom.metadata and aibom.metadata.timestamp else 'Not specified' }}</div>
|
468 |
</div>
|
469 |
|
470 |
+
{% if aibom.metadata and aibom.metadata.tools %}
|
471 |
<div class="aibom-property">
|
472 |
+
<div class="property-name">Generated By:</div>
|
473 |
<div class="property-value">
|
474 |
+
{% for tool in aibom.metadata.tools %}
|
475 |
+
<div>{{ tool.name }} {{ tool.version }}</div>
|
476 |
{% endfor %}
|
477 |
</div>
|
478 |
</div>
|
479 |
{% endif %}
|
480 |
|
481 |
+
{% if aibom.metadata and aibom.metadata.authors %}
|
482 |
<div class="aibom-property">
|
483 |
+
<div class="property-name">Authors:</div>
|
484 |
<div class="property-value">
|
485 |
+
{% for author in aibom.metadata.authors %}
|
486 |
+
<div>{{ author.name }} {% if author.email %}({{ author.email }}){% endif %}</div>
|
487 |
{% endfor %}
|
488 |
</div>
|
489 |
</div>
|
490 |
{% endif %}
|
491 |
|
492 |
+
{% if aibom.metadata and aibom.metadata.properties %}
|
493 |
<div class="aibom-property">
|
494 |
<div class="property-name">Properties:</div>
|
495 |
<div class="property-value">
|
496 |
{% for prop in aibom.metadata.properties %}
|
497 |
+
<div><strong>{{ prop.name }}:</strong> {{ prop.value }}</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
498 |
{% endfor %}
|
499 |
</div>
|
500 |
</div>
|
|
|
509 |
<div class="collapsible-content">
|
510 |
{% for ref in aibom.components[0].externalReferences %}
|
511 |
<div class="aibom-property">
|
512 |
+
<div class="property-name">{{ ref.type|title if ref.type else 'Reference' }}:</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
<div class="property-value">
|
514 |
+
<a href="{{ ref.url }}" target="_blank">{{ ref.url }}</a>
|
515 |
+
{% if ref.comment %}<div>{{ ref.comment }}</div>{% endif %}
|
|
|
|
|
|
|
|
|
|
|
516 |
</div>
|
517 |
</div>
|
518 |
{% endfor %}
|
519 |
</div>
|
520 |
</div>
|
521 |
{% endif %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
522 |
</div>
|
523 |
</div>
|
524 |
|
525 |
<div id="json-view" class="tab-content">
|
526 |
<div class="json-view">
|
527 |
+
<pre>{{ aibom | tojson(indent=2) }}</pre>
|
|
|
|
|
|
|
528 |
</div>
|
529 |
</div>
|
530 |
|
|
|
534 |
<!-- Field Tier Legend -->
|
535 |
<div class="tier-legend">
|
536 |
<div class="tier-legend-item">
|
537 |
+
<span class="field-tier tier-critical"></span>
|
538 |
+
<span>Critical</span>
|
539 |
</div>
|
540 |
<div class="tier-legend-item">
|
541 |
+
<span class="field-tier tier-important"></span>
|
542 |
+
<span>Important</span>
|
543 |
</div>
|
544 |
<div class="tier-legend-item">
|
545 |
+
<span class="field-tier tier-supplementary"></span>
|
546 |
+
<span>Supplementary</span>
|
547 |
</div>
|
548 |
</div>
|
549 |
|
550 |
<ul>
|
551 |
{% for field, status in completeness_score.field_checklist.items() %}
|
552 |
+
{% if "✔" in status %}
|
553 |
<li class="present">{{ status }} {{ field }}
|
554 |
{% if completeness_score.field_tiers and field in completeness_score.field_tiers %}
|
555 |
<span class="field-tier tier-{{ completeness_score.field_tiers[field] }}"></span>
|
|
|
805 |
<li>
|
806 |
<strong>Fix Validation Issues</strong> (removes validation penalty):
|
807 |
<ul>
|
808 |
+
{% for issue in completeness_score.validation.issues %}
|
809 |
+
<li>{{ issue.message }}</li>
|
810 |
{% endfor %}
|
811 |
</ul>
|
812 |
</li>
|
813 |
{% endif %}
|
814 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
815 |
{% if not has_recommendations %}
|
816 |
<li>Your AI SBOM is already well-documented. Great job!</li>
|
817 |
{% endif %}
|
818 |
</ol>
|
819 |
</div>
|
820 |
|
821 |
+
<!-- Scoring Rubric -->
|
822 |
<div class="scoring-rubric">
|
823 |
+
<h4>Scoring Rubric</h4>
|
824 |
+
<p>The completeness score is calculated based on the following categories:</p>
|
825 |
+
<table>
|
826 |
+
<tr>
|
827 |
+
<th>Category</th>
|
828 |
+
<th>Weight</th>
|
829 |
+
<th>Description</th>
|
830 |
+
</tr>
|
831 |
+
<tr>
|
832 |
+
<td>Required Fields</td>
|
833 |
+
<td>20%</td>
|
834 |
+
<td>Basic SBOM fields required by the CycloneDX specification</td>
|
835 |
+
</tr>
|
836 |
+
<tr>
|
837 |
+
<td>Metadata</td>
|
838 |
+
<td>20%</td>
|
839 |
+
<td>Information about the AI SBOM itself</td>
|
840 |
+
</tr>
|
841 |
+
<tr>
|
842 |
+
<td>Component Basic</td>
|
843 |
+
<td>20%</td>
|
844 |
+
<td>Basic information about the AI model</td>
|
845 |
+
</tr>
|
846 |
+
<tr>
|
847 |
+
<td>Model Card</td>
|
848 |
+
<td>30%</td>
|
849 |
+
<td>Detailed information about the model</td>
|
850 |
+
</tr>
|
851 |
+
<tr>
|
852 |
+
<td>External References</td>
|
853 |
+
<td>10%</td>
|
854 |
+
<td>Links to external resources</td>
|
855 |
+
</tr>
|
856 |
+
</table>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
857 |
</div>
|
858 |
</div>
|
859 |
+
|
860 |
+
<pre id="aibom-json" style="display: none;">{{ aibom | tojson(indent=2) }}</pre>
|
861 |
+
|
862 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
863 |
function switchTab(tabId) {
|
864 |
// Hide all tab contents
|
865 |
+
var tabContents = document.getElementsByClassName('tab-content');
|
866 |
+
for (var i = 0; i < tabContents.length; i++) {
|
867 |
tabContents[i].classList.remove('active');
|
868 |
}
|
869 |
|
870 |
// Deactivate all tabs
|
871 |
+
var tabs = document.getElementsByClassName('aibom-tab');
|
872 |
+
for (var i = 0; i < tabs.length; i++) {
|
873 |
tabs[i].classList.remove('active');
|
874 |
}
|
875 |
|
876 |
// Activate selected tab and content
|
877 |
document.getElementById(tabId).classList.add('active');
|
878 |
+
var selectedTab = document.querySelector('.aibom-tab[onclick="switchTab(\'' + tabId + '\')"]');
|
879 |
if (selectedTab) {
|
880 |
selectedTab.classList.add('active');
|
881 |
}
|
|
|
883 |
|
884 |
function toggleCollapsible(element) {
|
885 |
element.classList.toggle('active');
|
886 |
+
var content = element.nextElementSibling;
|
887 |
+
if (content.classList.contains('active')) {
|
888 |
+
content.classList.remove('active');
|
889 |
+
} else {
|
890 |
+
content.classList.add('active');
|
891 |
+
}
|
892 |
+
}
|
893 |
+
|
894 |
+
function downloadJSON() {
|
895 |
+
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(document.getElementById('aibom-json').textContent);
|
896 |
+
const downloadAnchorNode = document.createElement('a');
|
897 |
+
downloadAnchorNode.setAttribute("href", dataStr);
|
898 |
+
downloadAnchorNode.setAttribute("download", "{{ model_id }}-aibom.json");
|
899 |
+
document.body.appendChild(downloadAnchorNode);
|
900 |
+
downloadAnchorNode.click();
|
901 |
+
downloadAnchorNode.remove();
|
902 |
}
|
903 |
|
904 |
// Initialize collapsible sections
|
905 |
document.addEventListener('DOMContentLoaded', function() {
|
906 |
+
var collapsibles = document.getElementsByClassName('collapsible');
|
907 |
+
for (var i = 0; i < collapsibles.length; i++) {
|
908 |
collapsibles[i].nextElementSibling.classList.add('active');
|
909 |
collapsibles[i].classList.add('active');
|
910 |
}
|