Skip to content

Add tests for Risk Assessment Dashboard and improve SEO#163

Merged
pethers merged 11 commits intomainfrom
indexupdate
Feb 14, 2026
Merged

Add tests for Risk Assessment Dashboard and improve SEO#163
pethers merged 11 commits intomainfrom
indexupdate

Conversation

@pethers
Copy link
Member

@pethers pethers commented Feb 14, 2026

Enhance the Risk Assessment & Anomaly Detection Dashboard with comprehensive tests for various functionalities, including risk scoring and data loading. Update meta tags and links for better SEO and navigation on dashboard and news pages. Refactor code for improved readability and maintainability.

…oard

- Implement tests for risk scoring, heat map configuration, and Chart.js visualizations.
- Validate CIA CSV data loading and mock data generation.
- Ensure proper risk classification logic and color mapping.
- Test DOM structure, filter controls, and early warning system functionality.
- Verify data attribution and last updated timestamp handling.
'stat-total-votes': 'view_riksdagen_vote_data_ballot_politician_summary',
'stat-total-documents': 'document_data',
'stat-rule-violations': 'rule_violation',
'stat-government-proposals': 'view_riksdagen_goverment_proposals',
fetchCIAData(CIA_DATA_URLS.crisisResilience)
]);

const [riskLevels, riskByParty, crisisResilience] = datasets;
fetchCIAData(CIA_DATA_URLS.crisisResilience)
]);

const [riskLevels, riskByParty, crisisResilience] = datasets;
… URL

- Changed Open Graph and Twitter image URLs from "https://cia.sourceforge.io/cia-logo.png" to "https://hack23.com/cia-icon-140.webp" in:
  - news/index_he.html
  - news/index_ja.html
  - news/index_ko.html
  - news/index_nl.html
  - news/index_no.html
  - news/index_sv.html
  - news/index_zh.html
  - politician-dashboard.html
  - scripts/article-template.js
  - scripts/generate-news-indexes.js
  - sitemap.html
  - sitemap_ar.html
  - sitemap_da.html
  - sitemap_de.html
  - sitemap_es.html
  - sitemap_fi.html
  - sitemap_fr.html
  - sitemap_he.html
  - sitemap_ja.html
  - sitemap_ko.html
  - sitemap_nl.html
  - sitemap_no.html
  - sitemap_sv.html
  - sitemap_zh.html
…index_zh.html for better clarity and accuracy
# Conflicts:
#	tests/back-to-top.test.js
#	tests/stats-loader.test.js
@pethers pethers requested a review from Copilot February 14, 2026 01:43
@github-actions
Copy link
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@pethers pethers merged commit 70763a6 into main Feb 14, 2026
18 checks passed
@pethers pethers deleted the indexupdate branch February 14, 2026 01:46
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Risk Assessment & Anomaly Detection dashboard (D3 heatmap + Chart.js charts) and updates stats loading/SEO metadata across dashboards, sitemaps, and news pages, alongside expanded Vitest coverage for UI helpers.

Changes:

  • Introduces js/risk-dashboard.js and tests/risk-dashboard.test.js for risk-scoring/dashboard behaviors.
  • Refactors js/stats-loader.js to use cia-data/extraction_summary_report.csv as the single source of truth (and updates related CIA stats tooling/data files).
  • Updates OpenGraph/Twitter metadata and icons across sitemaps, dashboards, news index pages, and several news articles.

Reviewed changes

Copilot reviewed 79 out of 85 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
tests/risk-dashboard.test.js Adds a large Vitest suite for risk dashboard logic/DOM structure and CSV fetch handling.
tests/back-to-top.test.js Expands tests around back-to-top button behavior and accessibility preferences.
styles.css Adds dashboard loader/error styles plus filter-group and chart container helpers.
sitemap.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_ar.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_da.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_de.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_es.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_fi.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_fr.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_he.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_ja.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_ko.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_nl.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_no.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_sv.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
sitemap_zh.html Updates OG/Twitter image and favicon URL for SEO/social sharing.
scripts/load-cia-stats.js Extends extracted stats (tables + views) and bumps version to 1.2.0.
scripts/generate-news-indexes.js Updates social image URLs in generated news index HTML/JSON-LD.
scripts/article-template.js Updates social image URLs in generated article templates/JSON-LD.
politician-dashboard.html Updates OG image URL.
news/index.html Updates OG/Twitter image and adjusts hreflang for Norwegian.
news/index_ar.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_da.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_de.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_es.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_fi.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_fr.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_he.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_ja.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_ko.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_nl.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_no.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_sv.html Updates OG/Twitter image and JSON-LD org logo URL.
news/index_zh.html Updates OG/Twitter image and JSON-LD org logo URL.
news/2026-02-week-ahead-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-week-ahead-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-parliament-agenda-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-parliament-agenda-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-opposition-motions-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-opposition-motions-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-government-propositions-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-government-propositions-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-committee-reports-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-committee-reports-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-10-week-ahead-feb-10-17-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-10-week-ahead-feb-10-17-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-10-pm-eu-summit-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-10-pm-eu-summit-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-10-biodiversity-citizenship-en.html Updates OG/Twitter/JSON-LD image/logo URLs.
news/2026-02-10-biodiversity-citizenship-sv.html Updates OG/Twitter/JSON-LD image/logo URLs.
js/stats-loader.js Switches stats loading to extraction_summary_report.csv and updates DOM update logic.
js/risk-dashboard.js Adds new D3/Chart.js-driven risk assessment dashboard implementation.
js/back-to-top.js Adds an accessible back-to-top IIFE (scroll threshold + reduced-motion handling).
dashboard/index.html SEO + navigation + JSON-LD enhancements, local Chart.js, footer rebuild, hreflang change.
dashboard/index_ar.html Updates OG/Twitter image and favicon URL.
dashboard/index_da.html Updates OG/Twitter image and favicon URL.
dashboard/index_de.html Updates OG/Twitter image and favicon URL.
dashboard/index_es.html Updates OG/Twitter image and favicon URL.
dashboard/index_fi.html Updates OG/Twitter image and favicon URL.
dashboard/index_fr.html Updates OG/Twitter image and favicon URL.
dashboard/index_he.html Updates OG/Twitter image and favicon URL.
dashboard/index_ja.html Updates OG/Twitter image and favicon URL.
dashboard/index_ko.html Updates OG/Twitter image and favicon URL.
dashboard/index_nl.html Updates OG/Twitter image and favicon URL.
dashboard/index_no.html Updates OG/Twitter image and favicon URL.
dashboard/index_sv.html Updates OG/Twitter image and favicon URL.
dashboard/index_zh.html Updates OG/Twitter image and favicon URL.
cia-data/production-stats.json Updates generated metadata and expands counts to include views/tables.
cia-data/extraction_summary_report.csv Adds the extraction summary CSV used as stats source of truth.
cia-data/download-csv.sh Downloads extraction summary CSV as part of CIA data sync.

<!-- Skip to Main Content Link -->
<a href="#main-dashboard" class="skip-link">Skip to dashboard content</a>
<!-- Skip to Content Link for Accessibility -->
<a href="#main-dashboard" class="skip-to-content">Skip to dashboard content</a>
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skip link class was changed to skip-to-content, but the primary styling in styles.css is defined for .skip-link. Without matching styles, the skip link may be permanently visible or not positioned correctly. Either keep the .skip-link class or add equivalent base styles for .skip-to-content.

Suggested change
<a href="#main-dashboard" class="skip-to-content">Skip to dashboard content</a>
<a href="#main-dashboard" class="skip-link">Skip to dashboard content</a>

Copilot uses AI. Check for mistakes.
Comment on lines +318 to +322
.on('keydown', function(event, d) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
showRiskDetails(d, this);
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showRiskDetails is referenced in the heatmap cell keydown handler but is not defined anywhere in this script, so pressing Enter/Space on a focused cell will throw a ReferenceError. Define showRiskDetails or reuse the existing click handler logic for keyboard activation.

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +160
// Map risk level to scores
let baseScore;
if (level === 'HIGH') baseScore = 7 + Math.random() * 2;
else if (level === 'MEDIUM') baseScore = 4 + Math.random() * 3;
else baseScore = 1 + Math.random() * 3;
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CIA risk-level transformation assigns baseScore for HIGH/MEDIUM/else, but any risk_level other than HIGH/MEDIUM (e.g. CRITICAL) will fall into the else branch and get a low score (1–4) while still having level=CRITICAL. This makes level inconsistent with score (colors/classification) and can trigger incorrect early warnings.

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +213
if (criticalMPs.length > 0) {
const uniqueMPs = [...new Set(criticalMPs.map(d => d.politician))];
warningBanner.className = 'alert-banner critical';

// Build banner content safely using DOM methods
const strong = document.createElement('strong');
strong.textContent = '⚠️ CRITICAL:';
warningBanner.appendChild(strong);
warningBanner.appendChild(document.createTextNode(` ${uniqueMPs.length} MPs with risk level ≥8.0 detected `));

const detailsSpan = document.createElement('span');
detailsSpan.className = 'alert-details';
detailsSpan.textContent = 'Immediate review recommended';
warningBanner.appendChild(detailsSpan);

Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The critical-alert branch appends nodes to #earlyWarnings without clearing existing content first. If updateEarlyWarnings runs more than once, the banner will accumulate duplicate text/labels; also it won’t remove any initial placeholder markup. Clear the container (e.g., textContent = '') before appending.

Copilot uses AI. Check for mistakes.
Comment on lines +421 to +455
.text(d => String(d || '').replace('Rule_', 'R'));

// Add Y axis labels (politicians) - Sample every 10th
g.append('g')
.selectAll('text')
.data(politicians.filter((_, i) => i % 10 === 0))
.enter()
.append('text')
.attr('x', -10)
.attr('y', d => yScale(d) + yScale.bandwidth() / 2)
.attr('text-anchor', 'end')
.attr('alignment-baseline', 'middle')
.attr('font-size', '10px')
.attr('fill', 'currentColor')
.text(d => d);

// Create legend
createLegend();

// Filter functionality
document.getElementById('filterHighRisk').addEventListener('change', function(e) {
if (e.target.checked) {
cells.style('opacity', d => d.score >= 6.0 ? 1 : 0.1);
} else {
cells.style('opacity', 1);
}
});

// Rule filter
const ruleFilter = document.getElementById('riskRuleFilter');
rules.forEach(rule => {
const option = document.createElement('option');
option.value = rule;
option.textContent = String(rule || '').replace('Rule_', 'Risk Rule ');
ruleFilter.appendChild(option);
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heatmap rule label generation uses String(d || ''), which turns numeric rule values like 0 into an empty string. In your CIA-transformed data rule is numeric (ruleIdx), so rule 0 will render as a blank label and also collide with the "All Rules" option when building the select. Avoid || '' here and handle numeric rules explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines 28 to +31
<link rel="alternate" hreflang="en" href="https://riksdagsmonitor.com/news/index.html">
<link rel="alternate" hreflang="sv" href="https://riksdagsmonitor.com/news/index_sv.html">
<link rel="alternate" hreflang="da" href="https://riksdagsmonitor.com/news/index_da.html">
<link rel="alternate" hreflang="nb" href="https://riksdagsmonitor.com/news/index_no.html">
<link rel="alternate" hreflang="no" href="https://riksdagsmonitor.com/news/index_no.html">
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hreflang was changed to no here, but the other news index pages (news/index_sv.html, etc.) still declare the Norwegian variant as hreflang="nb". Keep this consistent across the whole news index set (prefer nb for Bokmål) to avoid SEO inconsistencies.

Copilot uses AI. Check for mistakes.
Comment on lines 31 to +34
<!-- Additional Meta Tags -->
<meta name="theme-color" content="#0a0e27">
<meta name="color-scheme" content="dark light">
<link rel="icon" type="image/png" href="https://cia.sourceforge.io/cia-logo.png">
<link rel="icon" type="image/png" href="https://hack23.com/cia-icon-140.webp">
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The favicon link now points to a .webp file but still declares type="image/png". This MIME type mismatch can cause incorrect icon handling in some clients. Update the type to image/webp (or omit the type attribute).

Copilot uses AI. Check for mistakes.
<meta name="theme-color" content="#0a0e27">
<meta name="color-scheme" content="dark light">
<link rel="icon" type="image/png" href="https://cia.sourceforge.io/cia-logo.png">
<link rel="icon" type="image/png" href="https://hack23.com/cia-icon-140.webp">
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The favicon link now points to a .webp file but still declares type="image/png". This MIME type mismatch can cause incorrect icon handling in some clients. Update the type to image/webp (or omit the type attribute).

Suggested change
<link rel="icon" type="image/png" href="https://hack23.com/cia-icon-140.webp">
<link rel="icon" type="image/webp" href="https://hack23.com/cia-icon-140.webp">

Copilot uses AI. Check for mistakes.
Comment on lines +324 to +332
.on('mouseover', function(event, d) {
tooltip.style('visibility', 'visible')
.html(`
<strong>${d.politician}</strong> (${d.party})<br>
<strong>${d.ruleName}</strong><br>
Risk Score: <strong>${d.score.toFixed(2)}</strong><br>
Level: <strong>${d.level}</strong>
`);
d3.select(this).attr('stroke', '#000').attr('stroke-width', 2);
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tooltip content is built with tooltip.html(...) using values sourced from remote CSV data (politician, party, ruleName). This is an XSS risk if the CSV content is ever malformed/compromised. Prefer building the tooltip with DOM nodes / textContent (or sanitize before inserting HTML).

Copilot uses AI. Check for mistakes.
Comment on lines +149 to 152
for (const row of rows) {
if (row.status === 'success' && row.object_name && row.row_count) {
lookup[row.object_name] = parseInt(row.row_count, 10);
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the CSV lookup build, the condition row.row_count will be false for legitimate zero counts ('0'), so those objects will never be added to lookup and stats won’t update to 0 when appropriate. Check for row.row_count !== undefined/!== '' instead of truthiness.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant