jzxiao commited on
Commit
46bd053
·
verified ·
1 Parent(s): a2954c4

Delete dbp-audit.js

Browse files
Files changed (1) hide show
  1. dbp-audit.js +0 -218
dbp-audit.js DELETED
@@ -1,218 +0,0 @@
1
- const $ = (id) => document.getElementById(id);
2
- const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, (character) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[character]));
3
- const finiteNumber = (value) => value !== null && value !== '' && Number.isFinite(Number(value)) ? Number(value) : null;
4
- const integer = (value) => finiteNumber(value) === null ? null : Math.trunc(Number(value));
5
- const countFormat = (value) => finiteNumber(value) === null ? 'Not reported' : Number(value).toLocaleString('en-US',{maximumFractionDigits:0});
6
- const metricFormat = (value,digits=3) => finiteNumber(value) === null ? 'N/A' : Number(value).toLocaleString('en-US',{minimumFractionDigits:digits,maximumFractionDigits:digits});
7
- const runtimeLabel = (runtime) => ({cpp:'C++',java:'Java',python:'Python'}[runtime] || String(runtime || 'Unknown'));
8
- const successfulStatus = (value) => ['ok','complete','passed'].includes(normalizeStatus(value));
9
- const PAGE_SIZE = 100;
10
- const EXPECTED_RUNTIMES = ['cpp','java','python'];
11
- const EXPECTED_COMPLETE_SUMMARY = {dataset_receipt_count:169,measured_dataset_count:162,n_a_dataset_count:7,schema_aggregate_count:4190,nonempty_aggregate_count:4096,n_a_aggregate_count:94};
12
- const REQUIRED_EVIDENCE_SHA_FIELDS = ['scope_sha256','source_column_inventory_sha256','runtime_artifact_manifest_sha256','campaign_binding_sha256','frame_verification_receipt_sha256','compact_collection_receipt_sha256','input_scope_receipt_sha256','campaign_run_receipt_sha256','campaign_progress_sha256','environment_receipt_sha256','source_evidence_chain_sha256'];
13
- const REQUIRED_COLUMN_SHA_FIELDS = ['frame_sequence_sha256','expected_sequence_chain_sha256','result_sha256','receipt_sha256','source_manifest_receipt_sha256','source_aggregates_sha256','source_blocks_sha256','source_block_catalog_sha256','runtime_artifact_manifest_sha256','source_column_inventory_sha256','scope_sha256'];
14
- const SHA256_PATTERN = /^[0-9a-f]{64}$/;
15
-
16
- let audit=null;
17
- let columns=[];
18
- let datasetReceipts=[];
19
- let filteredColumns=[];
20
- let page=1;
21
-
22
- function normalizeStatus(value) {
23
- const text=String(value??'').trim().toLowerCase().replace(/[\s/-]+/g,'_');
24
- return text||'unknown';
25
- }
26
- function statusLabel(value) {
27
- const status=normalizeStatus(value);
28
- if(status==='n_a'||status==='na')return 'N/A';
29
- return status.replaceAll('_',' ').replace(/\b\w/g,letter=>letter.toUpperCase());
30
- }
31
- function statusChip(value) {
32
- const status=normalizeStatus(value),css=status.replaceAll('_','-');
33
- return `<span class="status-chip status-${escapeHtml(css)}">${escapeHtml(statusLabel(status))}</span>`;
34
- }
35
- function normalizeDtype(value,width) {
36
- const text=String(value??'').toLowerCase();
37
- if(text.includes('32')||Number(width)===4)return 'float32';
38
- if(text.includes('64')||Number(width)===8)return 'float64';
39
- return text||'unknown';
40
- }
41
- function normalizeColumn(row) {
42
- const valid=integer(row.valid_count??row.verified_values),nativeBytes=integer(row.native_bytes??row.original_bytes),frameBytes=integer(row.frame_bytes??row.compressed_bytes??row.codec_wire_bytes);
43
- const encodeTotal=finiteNumber(row.encode_ns),decodeTotal=finiteNumber(row.decode_ns);
44
- return {...row,
45
- status:normalizeStatus(row.status),runtime:String(row.runtime??row.implementation_language??''),dataset:String(row.dataset??row.dataset_id??''),column:String(row.column??row.source_column??row.aggregate_id??''),aggregate_id:String(row.aggregate_id??''),dtype:normalizeDtype(row.dtype,row.width),
46
- rows:integer(row.rows??row.row_count),valid_count:valid,missing_count:integer(row.missing_count),native_bytes:nativeBytes,frame_bytes:frameBytes,
47
- compression_rate:finiteNumber(row.compression_rate??(nativeBytes>0&&frameBytes!==null?frameBytes/nativeBytes:null)),
48
- encode_ns_per_value:finiteNumber(row.encode_ns_per_value??row.compression_time_ns_per_point??(valid>0&&encodeTotal!==null?encodeTotal/valid:null)),
49
- decode_ns_per_value:finiteNumber(row.decode_ns_per_value??row.decompression_time_ns_per_point??(valid>0&&decodeTotal!==null?decodeTotal/valid:null)),
50
- blocks:integer(row.blocks),scaled_blocks:integer(row.scaled_blocks),raw_blocks:integer(row.raw_blocks),validity_bytes:integer(row.validity_bytes),header_bytes:integer(row.header_bytes),verified_values:integer(row.verified_values),
51
- frame_sequence_sha256:String(row.frame_sequence_sha256??''),receipt_sha256:String(row.receipt_sha256??''),message:String(row.message??'')};
52
- }
53
- function normalizeDatasetReceipt(row) {
54
- return {...row,dataset:String(row.dataset??row.dataset_id??''),runtime:String(row.runtime??''),status:normalizeStatus(row.status),reason:String(row.reason??row.message??''),receipt_sha256:String(row.receipt_sha256??'')};
55
- }
56
- function uniqueAggregateKey(row) {
57
- return row.aggregate_id||`${row.dataset}\0${row.column}\0${row.dtype}`;
58
- }
59
- function groupBy(items,keyFn) {
60
- const groups=new Map();
61
- for(const item of items){const key=keyFn(item);if(!groups.has(key))groups.set(key,[]);groups.get(key).push(item);}
62
- return groups;
63
- }
64
- function summaryObservation(columnRows=columns,receiptRows=datasetReceipts) {
65
- const datasetGroups=groupBy(receiptRows.filter(row=>row.dataset),row=>row.dataset);
66
- const aggregateGroups=groupBy(columnRows.filter(row=>uniqueAggregateKey(row)),uniqueAggregateKey);
67
- return {
68
- dataset_receipt_count:datasetGroups.size,
69
- measured_dataset_count:[...datasetGroups.values()].filter(rows=>rows.some(row=>successfulStatus(row.status))).length,
70
- n_a_dataset_count:[...datasetGroups.values()].filter(rows=>rows.length&&rows.every(row=>row.status==='n_a')).length,
71
- schema_aggregate_count:aggregateGroups.size,
72
- nonempty_aggregate_count:[...aggregateGroups.values()].filter(rows=>rows.some(row=>(row.valid_count??0)>0&&successfulStatus(row.status))).length,
73
- n_a_aggregate_count:[...aggregateGroups.values()].filter(rows=>rows.length&&rows.every(row=>row.status==='n_a')).length,
74
- };
75
- }
76
- function renderSummary() {
77
- const declared=audit.summary||{},observed=summaryObservation();
78
- const definitions=[['Dataset receipts','dataset_receipt_count'],['Measured datasets','measured_dataset_count'],['Dataset N/A','n_a_dataset_count'],['Schema aggregates','schema_aggregate_count'],['Nonempty aggregates','nonempty_aggregate_count'],['Aggregate N/A','n_a_aggregate_count']];
79
- $('audit-summary').innerHTML=definitions.map(([label,key])=>{const detail=integer(observed[key]),reported=integer(declared[key]),value=detail??reported,suffix=detail!==null&&reported!==null&&detail!==reported?` · JSON summary ${countFormat(reported)}`:'';return `<div><strong>${countFormat(value)}</strong><span>${escapeHtml(label+suffix)}</span></div>`;}).join('');
80
- }
81
- function auditValidationErrors(auditData,columnRows,receiptRows) {
82
- const errors=[],add=(message)=>{if(errors.length<500)errors.push(message);},declared=auditData.summary||{},observed=summaryObservation(columnRows,receiptRows),complete=normalizeStatus(auditData.status)==='complete';
83
- if(auditData.schema_version!=='dbp-native-float-audit-v1')add('schema_version is not dbp-native-float-audit-v1');
84
- for(const key of Object.keys(EXPECTED_COMPLETE_SUMMARY)){const expected=integer(declared[key]),actual=integer(observed[key]);if(expected!==null&&actual!==null&&expected!==actual)add(`${key}: summary ${expected}, detail ${actual}`);}
85
- if(!complete)return errors;
86
- for(const [key,value] of Object.entries(EXPECTED_COMPLETE_SUMMARY)){if(integer(declared[key])!==value)add(`${key}: complete scope requires ${value}`);}
87
- const runtimeRows=Array.isArray(auditData.runtimes)?auditData.runtimes:[],runtimeIds=runtimeRows.map(row=>String(row.runtime??''));
88
- if(runtimeRows.length!==3||new Set(runtimeIds).size!==3||!EXPECTED_RUNTIMES.every(runtime=>runtimeIds.includes(runtime)))add('complete audit requires exactly C++, Java, and Python runtime receipts');
89
- for(const row of runtimeRows){if(normalizeStatus(row.status)!=='complete')add(`${runtimeLabel(row.runtime)} runtime status is not complete`);if(!SHA256_PATTERN.test(String(row.result_sha256??'')))add(`${runtimeLabel(row.runtime)} runtime result chain SHA is absent`);}
90
- const byDataset=groupBy(receiptRows.filter(row=>row.dataset),row=>row.dataset);
91
- if(receiptRows.length!==507||byDataset.size!==169)add('complete audit requires 507 runtime receipts for 169 unique datasets');
92
- for(const [dataset,rows] of byDataset){const ids=rows.map(row=>row.runtime);if(rows.length!==3||new Set(ids).size!==3||!EXPECTED_RUNTIMES.every(runtime=>ids.includes(runtime)))add(`${dataset}: dataset receipts are not exactly C++, Java, and Python`);if(rows.some(row=>!['ok','n_a'].includes(row.status)))add(`${dataset}: dataset receipt has a nonfinal status`);if(new Set(rows.map(row=>row.status)).size!==1)add(`${dataset}: runtime dataset statuses differ`);if(rows.some(row=>!SHA256_PATTERN.test(row.receipt_sha256)))add(`${dataset}: dataset run receipt SHA is absent`);}
93
- const aggregateGroups=groupBy(columnRows.filter(row=>row.aggregate_id),row=>row.aggregate_id);
94
- if(columnRows.length!==12570||aggregateGroups.size!==4190)add('complete audit requires 12,570 runtime rows for 4,190 unique aggregates');
95
- const exactFields=['status','dataset','column','dtype','width','max_point','rows','valid_count','missing_count','native_bytes','frame_bytes','validity_bytes','header_bytes','blocks','scaled_blocks','raw_blocks','verified_values','frame_sequence_sha256','expected_sequence_chain_sha256','receipt_sha256','source_manifest_receipt_sha256','source_aggregates_sha256','source_blocks_sha256','source_block_catalog_sha256','runtime_artifact_manifest_sha256','source_column_inventory_sha256','scope_sha256'];
96
- for(const row of columnRows){
97
- const id=row.aggregate_id||'(missing aggregate_id)',width=row.dtype==='float32'?4:row.dtype==='float64'?8:null,expectedStatus=(row.valid_count??0)>0?'ok':'n_a';
98
- if(!row.aggregate_id)add('a complete column row is missing aggregate_id');
99
- if(!byDataset.has(row.dataset))add(`${id}: column dataset is absent from dataset receipts`);
100
- for(const field of ['width','max_point','rows','valid_count','missing_count','native_bytes','frame_bytes','validity_bytes','header_bytes','encode_ns','decode_ns','blocks','scaled_blocks','raw_blocks','verified_values'])if(finiteNumber(row[field])===null)add(`${id} ${runtimeLabel(row.runtime)}: ${field} is absent or invalid`);
101
- if(row.status!==expectedStatus)add(`${id} ${runtimeLabel(row.runtime)}: status does not match valid_count`);
102
- if(!width||row.native_bytes!==width*row.valid_count)add(`${id} ${runtimeLabel(row.runtime)}: native-byte accounting differs`);
103
- if(row.rows!==row.valid_count+row.missing_count)add(`${id} ${runtimeLabel(row.runtime)}: row accounting differs`);
104
- if(row.header_bytes!==10*row.blocks)add(`${id} ${runtimeLabel(row.runtime)}: header-byte accounting differs`);
105
- if(row.verified_values!==row.valid_count)add(`${id} ${runtimeLabel(row.runtime)}: verified value count differs`);
106
- if(row.scaled_blocks+row.raw_blocks!==row.blocks)add(`${id} ${runtimeLabel(row.runtime)}: mode counts differ`);
107
- if(row.valid_count>0&&(finiteNumber(row.compression_rate)===null||finiteNumber(row.encode_ns_per_value)===null||finiteNumber(row.decode_ns_per_value)===null))add(`${id} ${runtimeLabel(row.runtime)}: measured rate or timing is absent`);
108
- for(const field of REQUIRED_COLUMN_SHA_FIELDS)if(!SHA256_PATTERN.test(String(row[field]??'')))add(`${id} ${runtimeLabel(row.runtime)}: ${field} is absent or invalid`);
109
- }
110
- for(const [aggregateId,rows] of aggregateGroups){const ids=rows.map(row=>row.runtime);if(rows.length!==3||new Set(ids).size!==3||!EXPECTED_RUNTIMES.every(runtime=>ids.includes(runtime)))add(`${aggregateId}: result rows are not exactly C++, Java, and Python`);for(const field of exactFields)if(new Set(rows.map(row=>JSON.stringify(row[field]??null))).size!==1)add(`${aggregateId}: ${field} differs across runtimes`);}
111
- const evidence=auditData.evidence||{};
112
- for(const field of REQUIRED_EVIDENCE_SHA_FIELDS)if(!SHA256_PATTERN.test(String(evidence[field]??'')))add(`evidence.${field} is absent or invalid`);
113
- const runtimeChains=evidence.runtime_result_chain_sha256||{};for(const runtime of EXPECTED_RUNTIMES)if(!SHA256_PATTERN.test(String(runtimeChains[runtime]??'')))add(`evidence.runtime_result_chain_sha256.${runtime} is absent or invalid`);
114
- if(integer(auditData.measurement?.repeats)!==1||integer(auditData.measurement?.warmup_blocks)!==20||integer(auditData.measurement?.header_bytes_per_block)!==10)add('complete audit measurement contract differs');
115
- if(auditData.environment?.shared_server!==true||integer(auditData.environment?.cpu?.passed_receipts_at_capture)!==169)add('complete audit environment receipt does not cover all 169 dataset receipts');
116
- return errors;
117
- }
118
- function validateScope() {
119
- const errors=auditValidationErrors(audit,columns,datasetReceipts);
120
- const complete=normalizeStatus(audit.status)==='complete';
121
- $('scope-validation').className=`documentation-status ${errors.length&&complete?'audit-validation-error':'audit-validation-ok'}`;
122
- $('scope-validation').textContent=errors.length?(complete?`Completed receipt has ${errors.length} consistency issue${errors.length===1?'':'s'}. ${errors.slice(0,3).join(' · ')}${errors.length>3?' · …':''}`:`Audit status is ${statusLabel(audit.status)}; ${errors.length} completeness check${errors.length===1?'':'s'} remain unresolved.`):'Loaded summary, runtime, dataset, aggregate, and cross-runtime frame-hash receipts are internally consistent.';
123
- const rawPageStatus=normalizeStatus(audit.status),state=$('audit-state');state.textContent=errors.length&&complete?'Failed · inconsistent':successfulStatus(rawPageStatus)&&!complete?'Incomplete · invalid status':statusLabel(rawPageStatus);state.className=`status-chip status-${errors.length&&complete||rawPageStatus==='failed'?'failed':complete?'complete':'incomplete'}`;
124
- }
125
- function metadataValue(value) {
126
- if(value===null||value===undefined||value==='')return 'Not reported';
127
- if(Array.isArray(value))return value.join(', ');
128
- if(typeof value==='object')return JSON.stringify(value);
129
- return String(value);
130
- }
131
- function renderDefinitionList(target,entries) {
132
- target.innerHTML=entries.map(([label,value])=>`<dt>${escapeHtml(label)}</dt><dd>${escapeHtml(metadataValue(value))}</dd>`).join('');
133
- }
134
- function renderMetadata() {
135
- const measurement=audit.measurement||{},environment=audit.environment||{};
136
- renderDefinitionList($('measurement-meta'),[
137
- ['Context',measurement.context],['Repetitions',measurement.repetitions??measurement.repeats],['Warmup blocks',measurement.warmup_blocks],['Header bytes per block',measurement.header_bytes_per_block],['Clock',measurement.clock??environment.clock],['Encode timing',measurement.encode_boundary],['Decode timing',measurement.decode_boundary],['Ranking integration',measurement.ranking_integration],['Notes',measurement.notes]
138
- ]);
139
- renderDefinitionList($('environment-meta'),[
140
- ['Hardware',environment.hardware],['CPU',environment.cpu],['CPU affinity',environment.cpu_affinity],['Operating system',environment.os],['Concurrent load',environment.concurrent_load??environment.load],['Shared server',environment.shared_server===true?'Yes':environment.shared_server===false?'No':null],['Captured at',environment.captured_at]
141
- ]);
142
- }
143
- function renderRuntimeStatus() {
144
- const runtimes=Array.isArray(audit.runtimes)?audit.runtimes:[];
145
- $('runtime-status').innerHTML=runtimes.length?runtimes.map(row=>`<article class="runtime-status-card"><header><h3>${escapeHtml(runtimeLabel(row.runtime))}</h3>${statusChip(row.status)}</header>${row.version?`<p>${escapeHtml(row.version)}</p>`:''}${row.message?`<p>${escapeHtml(row.message)}</p>`:''}${row.result_sha256?`<p class="source-code">Result ${escapeHtml(String(row.result_sha256).slice(0,16))}…</p>`:''}</article>`).join(''):'<p class="documentation-empty">No runtime status receipts are available.</p>';
146
- }
147
- function populateFilters() {
148
- const runtimeIds=[...new Set([...(audit.runtimes||[]).map(row=>row.runtime),...columns.map(row=>row.runtime)].filter(Boolean))];
149
- $('audit-runtime').innerHTML='<option value="all">All runtimes</option>'+runtimeIds.map(runtime=>`<option value="${escapeHtml(runtime)}">${escapeHtml(runtimeLabel(runtime))}</option>`).join('');
150
- const statuses=[...new Set(columns.map(row=>row.status).filter(status=>['ok','n_a'].includes(status)))].sort();
151
- $('audit-status').innerHTML='<option value="all">All statuses</option>'+statuses.map(status=>`<option value="${escapeHtml(status)}">${escapeHtml(statusLabel(status))}</option>`).join('');
152
- }
153
- function filterState() {
154
- return {runtime:$('audit-runtime').value,dtype:$('audit-dtype').value,status:$('audit-status').value,search:$('audit-search').value.trim().toLowerCase()};
155
- }
156
- function applyFilters(resetPage=true) {
157
- const filters=filterState();
158
- filteredColumns=columns.filter(row=>(filters.runtime==='all'||row.runtime===filters.runtime)&&(filters.dtype==='all'||row.dtype===filters.dtype)&&(filters.status==='all'||row.status===filters.status)&&(!filters.search||`${row.dataset} ${row.column} ${row.aggregate_id}`.toLowerCase().includes(filters.search)));
159
- if(resetPage)page=1;
160
- renderColumnTable();renderDatasetReceipts();
161
- }
162
- function renderColumnTable() {
163
- const totalPages=Math.max(1,Math.ceil(filteredColumns.length/PAGE_SIZE));if(page>totalPages)page=totalPages;
164
- const start=(page-1)*PAGE_SIZE,visible=filteredColumns.slice(start,start+PAGE_SIZE),tbody=$('audit-table').querySelector('tbody');
165
- tbody.innerHTML=visible.length?visible.map(row=>`<tr><td>${statusChip(row.status)}</td><td>${escapeHtml(runtimeLabel(row.runtime))}</td><td>${escapeHtml(row.dataset||'Unknown')}</td><td>${escapeHtml(row.column||row.aggregate_id||'Unknown')}</td><td class="numeric">${escapeHtml(row.dtype)}</td><td class="numeric">${countFormat(row.native_bytes)}</td><td class="numeric">${countFormat(row.frame_bytes)}</td><td class="numeric">${metricFormat(row.compression_rate,4)}</td><td class="numeric">${metricFormat(row.encode_ns_per_value,2)}</td><td class="numeric">${metricFormat(row.decode_ns_per_value,2)}</td><td class="numeric">${countFormat(row.scaled_blocks)} scaled<span class="mode-count">${countFormat(row.raw_blocks)} raw · ${countFormat(row.blocks)} total</span></td><td class="numeric">${countFormat(row.verified_values)}</td></tr>`).join(''):'<tr><td colspan="12" class="empty-cell">No column result matches these filters.</td></tr>';
166
- $('column-result-count').textContent=`${countFormat(filteredColumns.length)} of ${countFormat(columns.length)} runtime / column rows shown${filteredColumns.length?` · rows ${countFormat(start+1)}–${countFormat(Math.min(start+PAGE_SIZE,filteredColumns.length))}`:''}`;
167
- $('audit-page').textContent=`Page ${page} of ${totalPages}`;$('audit-prev').disabled=page<=1;$('audit-next').disabled=page>=totalPages;$('download-column-json').disabled=!filteredColumns.length;$('download-column-csv').disabled=!filteredColumns.length;
168
- }
169
- function renderDatasetReceipts() {
170
- const filters=filterState();
171
- const visible=datasetReceipts.filter(row=>(filters.runtime==='all'||row.runtime===filters.runtime)&&(filters.status==='all'||row.status===filters.status)&&(!filters.search||`${row.dataset} ${row.label??''} ${row.reason}`.toLowerCase().includes(filters.search)));
172
- $('dataset-receipt-count').textContent=`${countFormat(visible.length)} of ${countFormat(datasetReceipts.length)} runtime receipts · ${countFormat(new Set(visible.map(row=>row.dataset)).size)} datasets`;
173
- $('dataset-receipts').innerHTML=visible.length?visible.map(row=>`<article class="dataset-receipt-card"><header><div><strong>${escapeHtml(row.label||row.dataset)}</strong><small>${escapeHtml(runtimeLabel(row.runtime))}${row.label&&row.label!==row.dataset?` · ${escapeHtml(row.dataset)}`:''}</small></div>${statusChip(row.status)}</header>${row.reason?`<p>${escapeHtml(row.reason)}</p>`:''}<p>${integer(row.schema_aggregate_count)!==null?`${countFormat(row.schema_aggregate_count)} schema · `:''}${integer(row.nonempty_aggregate_count)!==null?`${countFormat(row.nonempty_aggregate_count)} nonempty · `:''}${integer(row.n_a_aggregate_count)!==null?`${countFormat(row.n_a_aggregate_count)} N/A`:''}</p>${row.receipt_sha256?`<p class="source-code">Receipt ${escapeHtml(row.receipt_sha256.slice(0,16))}…</p>`:''}</article>`).join(''):'<p class="documentation-empty">No dataset receipt matches these filters.</p>';
174
- }
175
- function csvCell(value) {
176
- let text=String(value??'');if(/^[=+@-]/.test(text))text=`'${text}`;return `"${text.replaceAll('"','""')}"`;
177
- }
178
- function downloadBlob(filename,type,text) {
179
- const url=URL.createObjectURL(new Blob([text],{type})),anchor=document.createElement('a');anchor.href=url;anchor.download=filename;anchor.click();setTimeout(()=>URL.revokeObjectURL(url),1000);
180
- }
181
- const DOWNLOAD_FIELDS=[
182
- 'status','runtime','dataset','column','source_column','aggregate_id','dtype','width','max_point','rows','valid_count','missing_count',
183
- 'native_bytes','frame_bytes','validity_bytes','header_bytes','compression_rate','encode_ns','decode_ns','encode_ns_per_value','decode_ns_per_value',
184
- 'blocks','scaled_blocks','raw_blocks','verified_values','frame_sequence_sha256','expected_sequence_chain_sha256','result_sha256','receipt_sha256',
185
- 'source_manifest_receipt_sha256','source_aggregates_sha256','source_blocks_sha256','source_block_catalog_sha256','runtime_artifact_manifest_sha256',
186
- 'source_column_inventory_sha256','scope_sha256','message'
187
- ];
188
- function downloadCsv() {
189
- const rows=filteredColumns.map(row=>DOWNLOAD_FIELDS.map(field=>csvCell(row[field])).join(','));downloadBlob('THULab-DBP-audit-columns-filtered.csv','text/csv;charset=utf-8','\ufeff'+[DOWNLOAD_FIELDS.join(','),...rows].join('\r\n'));
190
- }
191
- function downloadJson() {
192
- downloadBlob('THULab-DBP-audit-columns-filtered.json','application/json;charset=utf-8',JSON.stringify({schema_version:audit.schema_version,status:audit.status,filters:filterState(),columns:filteredColumns.map(row=>Object.fromEntries(DOWNLOAD_FIELDS.map(field=>[field,row[field]??null])))},null,2)+'\n');
193
- }
194
- function setupEvents() {
195
- for(const id of ['audit-runtime','audit-dtype','audit-status'])$(id).addEventListener('change',()=>applyFilters(true));$('audit-search').addEventListener('input',()=>applyFilters(true));
196
- $('audit-prev').addEventListener('click',()=>{if(page>1){page--;renderColumnTable();}});$('audit-next').addEventListener('click',()=>{if(page*PAGE_SIZE<filteredColumns.length){page++;renderColumnTable();}});
197
- $('download-column-csv').addEventListener('click',downloadCsv);$('download-column-json').addEventListener('click',downloadJson);
198
- }
199
- function setupTheme() {
200
- let stored;try{stored=localStorage.getItem('thulab-theme');}catch{}
201
- const query=new URLSearchParams(location.search).get('__theme'),theme=stored||(['dark','light'].includes(query)?query:matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light');document.documentElement.dataset.theme=theme;$('theme').textContent=theme==='dark'?'Light mode':'Dark mode';
202
- $('theme').addEventListener('click',()=>{const next=document.documentElement.dataset.theme==='dark'?'light':'dark';document.documentElement.dataset.theme=next;$('theme').textContent=next==='dark'?'Light mode':'Dark mode';try{localStorage.setItem('thulab-theme',next);}catch{}});
203
- }
204
- function initialize() {
205
- columns=(Array.isArray(audit.columns)?audit.columns:Array.isArray(audit.results)?audit.results:[]).map(normalizeColumn);datasetReceipts=(Array.isArray(audit.datasets)?audit.datasets:[]).map(normalizeDatasetReceipt);
206
- $('audit-generated').textContent=audit.generated_at?`Generated ${String(audit.generated_at).replace('T',' ').replace('Z',' UTC')}`:'Generation time not reported';
207
- renderSummary();renderMetadata();renderRuntimeStatus();populateFilters();setupEvents();applyFilters(true);validateScope();document.documentElement.dataset.ready='true';
208
- }
209
-
210
- async function loadAudit() {
211
- try {
212
- const response=await fetch('./dbp-audit.json?v=20261009-dbp-audit-v1',{cache:'no-cache'});if(!response.ok)throw new Error(`Audit request returned ${response.status}`);audit=await response.json();if(!audit||typeof audit!=='object'||!audit.summary||!audit.measurement||!audit.environment)throw new Error('The audit JSON does not match the required top-level schema.');initialize();
213
- } catch(error) {
214
- $('load-error').hidden=false;$('load-error').textContent=`The independent DBP audit is not available yet. No completed result is inferred. ${error.message}`;$('audit-state').textContent='Unavailable';$('audit-state').className='status-chip status-incomplete';$('audit-generated').textContent='dbp-audit.json was not loaded';$('audit-table').querySelector('tbody').innerHTML='<tr><td colspan="12" class="empty-cell">Audit results unavailable.</td></tr>';$('scope-validation').textContent='The historical leaderboard remains unchanged.';
215
- }
216
- }
217
- if(typeof document!=='undefined'){setupTheme();loadAudit();}
218
- if(typeof module!=='undefined'&&module.exports)module.exports={auditValidationErrors,normalizeColumn,normalizeDatasetReceipt,summaryObservation};