euparliamentmonitor 0.8.4
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.
- package/LICENSE +201 -0
- package/README.md +1005 -0
- package/SECURITY.md +151 -0
- package/package.json +131 -0
- package/scripts/constants/committee-indicator-map.d.ts +199 -0
- package/scripts/constants/committee-indicator-map.d.ts.map +1 -0
- package/scripts/constants/committee-indicator-map.js +1224 -0
- package/scripts/constants/committee-indicator-map.js.map +1 -0
- package/scripts/constants/config.d.ts +38 -0
- package/scripts/constants/config.d.ts.map +1 -0
- package/scripts/constants/config.js +66 -0
- package/scripts/constants/config.js.map +1 -0
- package/scripts/constants/language-articles.d.ts +84 -0
- package/scripts/constants/language-articles.d.ts.map +1 -0
- package/scripts/constants/language-articles.js +6771 -0
- package/scripts/constants/language-articles.js.map +1 -0
- package/scripts/constants/language-core.d.ts +38 -0
- package/scripts/constants/language-core.d.ts.map +1 -0
- package/scripts/constants/language-core.js +90 -0
- package/scripts/constants/language-core.js.map +1 -0
- package/scripts/constants/language-ui.d.ts +82 -0
- package/scripts/constants/language-ui.d.ts.map +1 -0
- package/scripts/constants/language-ui.js +889 -0
- package/scripts/constants/language-ui.js.map +1 -0
- package/scripts/constants/languages.d.ts +14 -0
- package/scripts/constants/languages.d.ts.map +1 -0
- package/scripts/constants/languages.js +15 -0
- package/scripts/constants/languages.js.map +1 -0
- package/scripts/generators/analysis-builders.d.ts +266 -0
- package/scripts/generators/analysis-builders.d.ts.map +1 -0
- package/scripts/generators/analysis-builders.js +2903 -0
- package/scripts/generators/analysis-builders.js.map +1 -0
- package/scripts/generators/breaking-content.d.ts +45 -0
- package/scripts/generators/breaking-content.d.ts.map +1 -0
- package/scripts/generators/breaking-content.js +530 -0
- package/scripts/generators/breaking-content.js.map +1 -0
- package/scripts/generators/committee-helpers.d.ts +54 -0
- package/scripts/generators/committee-helpers.d.ts.map +1 -0
- package/scripts/generators/committee-helpers.js +154 -0
- package/scripts/generators/committee-helpers.js.map +1 -0
- package/scripts/generators/dashboard-content.d.ts +95 -0
- package/scripts/generators/dashboard-content.d.ts.map +1 -0
- package/scripts/generators/dashboard-content.js +630 -0
- package/scripts/generators/dashboard-content.js.map +1 -0
- package/scripts/generators/deep-analysis-content.d.ts +23 -0
- package/scripts/generators/deep-analysis-content.d.ts.map +1 -0
- package/scripts/generators/deep-analysis-content.js +831 -0
- package/scripts/generators/deep-analysis-content.js.map +1 -0
- package/scripts/generators/mindmap-content.d.ts +55 -0
- package/scripts/generators/mindmap-content.d.ts.map +1 -0
- package/scripts/generators/mindmap-content.js +512 -0
- package/scripts/generators/mindmap-content.js.map +1 -0
- package/scripts/generators/motions-content.d.ts +50 -0
- package/scripts/generators/motions-content.d.ts.map +1 -0
- package/scripts/generators/motions-content.js +391 -0
- package/scripts/generators/motions-content.js.map +1 -0
- package/scripts/generators/news-enhanced.d.ts +14 -0
- package/scripts/generators/news-enhanced.d.ts.map +1 -0
- package/scripts/generators/news-enhanced.js +169 -0
- package/scripts/generators/news-enhanced.js.map +1 -0
- package/scripts/generators/news-indexes.d.ts +31 -0
- package/scripts/generators/news-indexes.d.ts.map +1 -0
- package/scripts/generators/news-indexes.js +410 -0
- package/scripts/generators/news-indexes.js.map +1 -0
- package/scripts/generators/pipeline/fetch-stage.d.ts +352 -0
- package/scripts/generators/pipeline/fetch-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/fetch-stage.js +1522 -0
- package/scripts/generators/pipeline/fetch-stage.js.map +1 -0
- package/scripts/generators/pipeline/generate-stage.d.ts +43 -0
- package/scripts/generators/pipeline/generate-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/generate-stage.js +204 -0
- package/scripts/generators/pipeline/generate-stage.js.map +1 -0
- package/scripts/generators/pipeline/output-stage.d.ts +48 -0
- package/scripts/generators/pipeline/output-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/output-stage.js +145 -0
- package/scripts/generators/pipeline/output-stage.js.map +1 -0
- package/scripts/generators/pipeline/transform-stage.d.ts +57 -0
- package/scripts/generators/pipeline/transform-stage.d.ts.map +1 -0
- package/scripts/generators/pipeline/transform-stage.js +111 -0
- package/scripts/generators/pipeline/transform-stage.js.map +1 -0
- package/scripts/generators/propositions-content.d.ts +29 -0
- package/scripts/generators/propositions-content.d.ts.map +1 -0
- package/scripts/generators/propositions-content.js +90 -0
- package/scripts/generators/propositions-content.js.map +1 -0
- package/scripts/generators/sankey-content.d.ts +45 -0
- package/scripts/generators/sankey-content.d.ts.map +1 -0
- package/scripts/generators/sankey-content.js +227 -0
- package/scripts/generators/sankey-content.js.map +1 -0
- package/scripts/generators/sitemap.d.ts +66 -0
- package/scripts/generators/sitemap.d.ts.map +1 -0
- package/scripts/generators/sitemap.js +562 -0
- package/scripts/generators/sitemap.js.map +1 -0
- package/scripts/generators/strategies/article-strategy.d.ts +146 -0
- package/scripts/generators/strategies/article-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/article-strategy.js +4 -0
- package/scripts/generators/strategies/article-strategy.js.map +1 -0
- package/scripts/generators/strategies/breaking-news-strategy.d.ts +64 -0
- package/scripts/generators/strategies/breaking-news-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/breaking-news-strategy.js +246 -0
- package/scripts/generators/strategies/breaking-news-strategy.js.map +1 -0
- package/scripts/generators/strategies/committee-reports-strategy.d.ts +93 -0
- package/scripts/generators/strategies/committee-reports-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/committee-reports-strategy.js +447 -0
- package/scripts/generators/strategies/committee-reports-strategy.js.map +1 -0
- package/scripts/generators/strategies/month-ahead-strategy.d.ts +60 -0
- package/scripts/generators/strategies/month-ahead-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/month-ahead-strategy.js +175 -0
- package/scripts/generators/strategies/month-ahead-strategy.js.map +1 -0
- package/scripts/generators/strategies/monthly-review-strategy.d.ts +66 -0
- package/scripts/generators/strategies/monthly-review-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/monthly-review-strategy.js +204 -0
- package/scripts/generators/strategies/monthly-review-strategy.js.map +1 -0
- package/scripts/generators/strategies/motions-strategy.d.ts +61 -0
- package/scripts/generators/strategies/motions-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/motions-strategy.js +215 -0
- package/scripts/generators/strategies/motions-strategy.js.map +1 -0
- package/scripts/generators/strategies/propositions-strategy.d.ts +60 -0
- package/scripts/generators/strategies/propositions-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/propositions-strategy.js +257 -0
- package/scripts/generators/strategies/propositions-strategy.js.map +1 -0
- package/scripts/generators/strategies/week-ahead-strategy.d.ts +57 -0
- package/scripts/generators/strategies/week-ahead-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/week-ahead-strategy.js +178 -0
- package/scripts/generators/strategies/week-ahead-strategy.js.map +1 -0
- package/scripts/generators/strategies/weekly-review-strategy.d.ts +63 -0
- package/scripts/generators/strategies/weekly-review-strategy.d.ts.map +1 -0
- package/scripts/generators/strategies/weekly-review-strategy.js +211 -0
- package/scripts/generators/strategies/weekly-review-strategy.js.map +1 -0
- package/scripts/generators/swot-content.d.ts +42 -0
- package/scripts/generators/swot-content.d.ts.map +1 -0
- package/scripts/generators/swot-content.js +366 -0
- package/scripts/generators/swot-content.js.map +1 -0
- package/scripts/generators/week-ahead-content.d.ts +103 -0
- package/scripts/generators/week-ahead-content.d.ts.map +1 -0
- package/scripts/generators/week-ahead-content.js +610 -0
- package/scripts/generators/week-ahead-content.js.map +1 -0
- package/scripts/index.d.ts +40 -0
- package/scripts/index.d.ts.map +1 -0
- package/scripts/index.js +53 -0
- package/scripts/index.js.map +1 -0
- package/scripts/mcp/ep-mcp-client.d.ts +471 -0
- package/scripts/mcp/ep-mcp-client.d.ts.map +1 -0
- package/scripts/mcp/ep-mcp-client.js +734 -0
- package/scripts/mcp/ep-mcp-client.js.map +1 -0
- package/scripts/mcp/mcp-connection.d.ts +264 -0
- package/scripts/mcp/mcp-connection.d.ts.map +1 -0
- package/scripts/mcp/mcp-connection.js +790 -0
- package/scripts/mcp/mcp-connection.js.map +1 -0
- package/scripts/mcp/mcp-health.d.ts +75 -0
- package/scripts/mcp/mcp-health.d.ts.map +1 -0
- package/scripts/mcp/mcp-health.js +78 -0
- package/scripts/mcp/mcp-health.js.map +1 -0
- package/scripts/mcp/mcp-retry.d.ts +94 -0
- package/scripts/mcp/mcp-retry.d.ts.map +1 -0
- package/scripts/mcp/mcp-retry.js +127 -0
- package/scripts/mcp/mcp-retry.js.map +1 -0
- package/scripts/mcp/wb-mcp-client.d.ts +38 -0
- package/scripts/mcp/wb-mcp-client.d.ts.map +1 -0
- package/scripts/mcp/wb-mcp-client.js +112 -0
- package/scripts/mcp/wb-mcp-client.js.map +1 -0
- package/scripts/templates/article-template.d.ts +9 -0
- package/scripts/templates/article-template.d.ts.map +1 -0
- package/scripts/templates/article-template.js +378 -0
- package/scripts/templates/article-template.js.map +1 -0
- package/scripts/templates/section-builders.d.ts +28 -0
- package/scripts/templates/section-builders.d.ts.map +1 -0
- package/scripts/templates/section-builders.js +142 -0
- package/scripts/templates/section-builders.js.map +1 -0
- package/scripts/types/analysis.d.ts +115 -0
- package/scripts/types/analysis.d.ts.map +1 -0
- package/scripts/types/analysis.js +4 -0
- package/scripts/types/analysis.js.map +1 -0
- package/scripts/types/common.d.ts +584 -0
- package/scripts/types/common.d.ts.map +1 -0
- package/scripts/types/common.js +96 -0
- package/scripts/types/common.js.map +1 -0
- package/scripts/types/generation.d.ts +104 -0
- package/scripts/types/generation.d.ts.map +1 -0
- package/scripts/types/generation.js +4 -0
- package/scripts/types/generation.js.map +1 -0
- package/scripts/types/index.d.ts +24 -0
- package/scripts/types/index.d.ts.map +1 -0
- package/scripts/types/index.js +16 -0
- package/scripts/types/index.js.map +1 -0
- package/scripts/types/intelligence.d.ts +129 -0
- package/scripts/types/intelligence.d.ts.map +1 -0
- package/scripts/types/intelligence.js +4 -0
- package/scripts/types/intelligence.js.map +1 -0
- package/scripts/types/mcp.d.ts +418 -0
- package/scripts/types/mcp.d.ts.map +1 -0
- package/scripts/types/mcp.js +4 -0
- package/scripts/types/mcp.js.map +1 -0
- package/scripts/types/parliament.d.ts +388 -0
- package/scripts/types/parliament.d.ts.map +1 -0
- package/scripts/types/parliament.js +4 -0
- package/scripts/types/parliament.js.map +1 -0
- package/scripts/types/quality.d.ts +114 -0
- package/scripts/types/quality.d.ts.map +1 -0
- package/scripts/types/quality.js +4 -0
- package/scripts/types/quality.js.map +1 -0
- package/scripts/types/stakeholder.d.ts +88 -0
- package/scripts/types/stakeholder.d.ts.map +1 -0
- package/scripts/types/stakeholder.js +16 -0
- package/scripts/types/stakeholder.js.map +1 -0
- package/scripts/types/visualization.d.ts +708 -0
- package/scripts/types/visualization.d.ts.map +1 -0
- package/scripts/types/visualization.js +4 -0
- package/scripts/types/visualization.js.map +1 -0
- package/scripts/types/world-bank.d.ts +85 -0
- package/scripts/types/world-bank.d.ts.map +1 -0
- package/scripts/types/world-bank.js +4 -0
- package/scripts/types/world-bank.js.map +1 -0
- package/scripts/utils/article-category.d.ts +18 -0
- package/scripts/utils/article-category.d.ts.map +1 -0
- package/scripts/utils/article-category.js +49 -0
- package/scripts/utils/article-category.js.map +1 -0
- package/scripts/utils/article-quality-scorer.d.ts +87 -0
- package/scripts/utils/article-quality-scorer.d.ts.map +1 -0
- package/scripts/utils/article-quality-scorer.js +1048 -0
- package/scripts/utils/article-quality-scorer.js.map +1 -0
- package/scripts/utils/content-metadata.d.ts +34 -0
- package/scripts/utils/content-metadata.d.ts.map +1 -0
- package/scripts/utils/content-metadata.js +249 -0
- package/scripts/utils/content-metadata.js.map +1 -0
- package/scripts/utils/content-validator.d.ts +94 -0
- package/scripts/utils/content-validator.d.ts.map +1 -0
- package/scripts/utils/content-validator.js +489 -0
- package/scripts/utils/content-validator.js.map +1 -0
- package/scripts/utils/copy-test-reports.d.ts +9 -0
- package/scripts/utils/copy-test-reports.d.ts.map +1 -0
- package/scripts/utils/copy-test-reports.js +508 -0
- package/scripts/utils/copy-test-reports.js.map +1 -0
- package/scripts/utils/file-utils.d.ts +144 -0
- package/scripts/utils/file-utils.d.ts.map +1 -0
- package/scripts/utils/file-utils.js +374 -0
- package/scripts/utils/file-utils.js.map +1 -0
- package/scripts/utils/fix-articles.d.ts +27 -0
- package/scripts/utils/fix-articles.d.ts.map +1 -0
- package/scripts/utils/fix-articles.js +510 -0
- package/scripts/utils/fix-articles.js.map +1 -0
- package/scripts/utils/generate-docs-index.d.ts +8 -0
- package/scripts/utils/generate-docs-index.d.ts.map +1 -0
- package/scripts/utils/generate-docs-index.js +275 -0
- package/scripts/utils/generate-docs-index.js.map +1 -0
- package/scripts/utils/html-sanitize.d.ts +18 -0
- package/scripts/utils/html-sanitize.d.ts.map +1 -0
- package/scripts/utils/html-sanitize.js +57 -0
- package/scripts/utils/html-sanitize.js.map +1 -0
- package/scripts/utils/intelligence-analysis.d.ts +173 -0
- package/scripts/utils/intelligence-analysis.d.ts.map +1 -0
- package/scripts/utils/intelligence-analysis.js +936 -0
- package/scripts/utils/intelligence-analysis.js.map +1 -0
- package/scripts/utils/intelligence-index.d.ts +126 -0
- package/scripts/utils/intelligence-index.d.ts.map +1 -0
- package/scripts/utils/intelligence-index.js +731 -0
- package/scripts/utils/intelligence-index.js.map +1 -0
- package/scripts/utils/metadata-utils.d.ts +14 -0
- package/scripts/utils/metadata-utils.d.ts.map +1 -0
- package/scripts/utils/metadata-utils.js +18 -0
- package/scripts/utils/metadata-utils.js.map +1 -0
- package/scripts/utils/news-metadata.d.ts +47 -0
- package/scripts/utils/news-metadata.d.ts.map +1 -0
- package/scripts/utils/news-metadata.js +259 -0
- package/scripts/utils/news-metadata.js.map +1 -0
- package/scripts/utils/validate-articles.d.ts +2 -0
- package/scripts/utils/validate-articles.d.ts.map +1 -0
- package/scripts/utils/validate-articles.js +284 -0
- package/scripts/utils/validate-articles.js.map +1 -0
- package/scripts/utils/validate-ep-api.d.ts +51 -0
- package/scripts/utils/validate-ep-api.d.ts.map +1 -0
- package/scripts/utils/validate-ep-api.js +160 -0
- package/scripts/utils/validate-ep-api.js.map +1 -0
- package/scripts/utils/world-bank-data.d.ts +84 -0
- package/scripts/utils/world-bank-data.d.ts.map +1 -0
- package/scripts/utils/world-bank-data.js +311 -0
- package/scripts/utils/world-bank-data.js.map +1 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* @module Utils/ValidateArticles
|
|
5
|
+
* @description CI-ready article validation tool that checks all news articles
|
|
6
|
+
* for quality, structural correctness, language consistency, and meta tag alignment.
|
|
7
|
+
*
|
|
8
|
+
* Can be run standalone or integrated into CI pipelines as a quality gate.
|
|
9
|
+
* Exits with code 1 if any article has validation errors.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* - Validate all articles: `npx tsx src/utils/validate-articles.ts`
|
|
13
|
+
* - Validate specific date: `npx tsx src/utils/validate-articles.ts --date=2026-03-04`
|
|
14
|
+
* - Strict mode (warnings are errors): `npx tsx src/utils/validate-articles.ts --strict`
|
|
15
|
+
* - Dry run (no exit code): `npx tsx src/utils/validate-articles.ts --dry-run`
|
|
16
|
+
* - Quality scoring: `npx tsx src/utils/validate-articles.ts --quality`
|
|
17
|
+
* - JSON output: `npx tsx src/utils/validate-articles.ts --quality --output=json`
|
|
18
|
+
*/
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { NEWS_DIR, ARTICLE_FILENAME_PATTERN } from '../constants/config.js';
|
|
22
|
+
import { validateArticleContent } from './content-validator.js';
|
|
23
|
+
import { scoreArticleQuality } from './article-quality-scorer.js';
|
|
24
|
+
// āāā CLI argument parsing āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
/**
|
|
27
|
+
* Extract a CLI flag value: --flag=value
|
|
28
|
+
*
|
|
29
|
+
* @param name - Flag name to extract
|
|
30
|
+
* @returns Flag value or undefined if not found
|
|
31
|
+
*/
|
|
32
|
+
function getArg(name) {
|
|
33
|
+
const prefix = `--${name}=`;
|
|
34
|
+
const arg = args.find((a) => a.startsWith(prefix));
|
|
35
|
+
return arg?.slice(prefix.length);
|
|
36
|
+
}
|
|
37
|
+
const filterDate = getArg('date');
|
|
38
|
+
const strictMode = args.includes('--strict');
|
|
39
|
+
const dryRun = args.includes('--dry-run');
|
|
40
|
+
const qualityMode = args.includes('--quality');
|
|
41
|
+
const outputFormat = getArg('output');
|
|
42
|
+
// āāā Slug-to-article-type mapping āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
43
|
+
/**
|
|
44
|
+
* Map article slug to article type for validator word-count thresholds.
|
|
45
|
+
* The slug is the middle portion of the filename: {date}-{slug}-{lang}.html
|
|
46
|
+
*
|
|
47
|
+
* @param slug - Article slug (e.g. "week-ahead", "breaking", "committee-reports")
|
|
48
|
+
* @returns Article type string matching ArticleCategory values
|
|
49
|
+
*/
|
|
50
|
+
function slugToArticleType(slug) {
|
|
51
|
+
const mapping = {
|
|
52
|
+
'week-ahead': 'week-ahead',
|
|
53
|
+
'month-ahead': 'month-ahead',
|
|
54
|
+
breaking: 'breaking',
|
|
55
|
+
'committee-reports': 'committee-reports',
|
|
56
|
+
propositions: 'propositions',
|
|
57
|
+
motions: 'motions',
|
|
58
|
+
'week-in-review': 'week-in-review',
|
|
59
|
+
'month-in-review': 'month-in-review',
|
|
60
|
+
'weekly-review': 'week-in-review',
|
|
61
|
+
'monthly-review': 'month-in-review',
|
|
62
|
+
};
|
|
63
|
+
return mapping[slug] ?? slug;
|
|
64
|
+
}
|
|
65
|
+
// āāā Main validation logic āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
66
|
+
/**
|
|
67
|
+
* Validate a single article file and return a summary.
|
|
68
|
+
*
|
|
69
|
+
* @param filename - Filename of the article to validate
|
|
70
|
+
* @returns Article validation summary or null if the filename does not match
|
|
71
|
+
*/
|
|
72
|
+
function validateSingleFile(filename) {
|
|
73
|
+
const match = ARTICLE_FILENAME_PATTERN.exec(filename);
|
|
74
|
+
if (!match)
|
|
75
|
+
return null;
|
|
76
|
+
const date = match[1] ?? '';
|
|
77
|
+
const slug = match[2] ?? '';
|
|
78
|
+
const lang = match[3] ?? '';
|
|
79
|
+
const filePath = path.join(NEWS_DIR, filename);
|
|
80
|
+
const html = fs.readFileSync(filePath, 'utf-8');
|
|
81
|
+
const articleType = slugToArticleType(slug);
|
|
82
|
+
const result = validateArticleContent(html, lang, articleType);
|
|
83
|
+
const summary = {
|
|
84
|
+
filename,
|
|
85
|
+
lang,
|
|
86
|
+
slug,
|
|
87
|
+
date,
|
|
88
|
+
valid: strictMode ? result.valid && result.warnings.length === 0 : result.valid,
|
|
89
|
+
errors: [...result.errors],
|
|
90
|
+
warnings: [...result.warnings],
|
|
91
|
+
wordCount: result.metrics.wordCount,
|
|
92
|
+
};
|
|
93
|
+
if (strictMode && result.warnings.length > 0) {
|
|
94
|
+
summary.errors.push(...result.warnings.map((w) => `[strict] ${w}`));
|
|
95
|
+
}
|
|
96
|
+
if (qualityMode) {
|
|
97
|
+
summary.qualityReport = scoreArticleQuality(html, `${date}-${slug}`, lang, articleType);
|
|
98
|
+
}
|
|
99
|
+
return summary;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate all news articles in the news directory.
|
|
103
|
+
*
|
|
104
|
+
* @returns Validation report with per-article summaries
|
|
105
|
+
*/
|
|
106
|
+
function validateAllArticles() {
|
|
107
|
+
if (!fs.existsSync(NEWS_DIR)) {
|
|
108
|
+
console.error(`ā News directory not found: ${NEWS_DIR}`);
|
|
109
|
+
return { totalArticles: 0, passed: 0, failed: 0, warnings: 0, articles: [] };
|
|
110
|
+
}
|
|
111
|
+
const files = fs
|
|
112
|
+
.readdirSync(NEWS_DIR)
|
|
113
|
+
.filter((f) => f.endsWith('.html'))
|
|
114
|
+
.filter((f) => ARTICLE_FILENAME_PATTERN.test(f))
|
|
115
|
+
.filter((f) => (filterDate ? f.startsWith(filterDate) : true))
|
|
116
|
+
.sort();
|
|
117
|
+
const articles = [];
|
|
118
|
+
let passed = 0;
|
|
119
|
+
let failed = 0;
|
|
120
|
+
let warningCount = 0;
|
|
121
|
+
for (const filename of files) {
|
|
122
|
+
const summary = validateSingleFile(filename);
|
|
123
|
+
if (!summary)
|
|
124
|
+
continue;
|
|
125
|
+
if (summary.valid) {
|
|
126
|
+
passed++;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
failed++;
|
|
130
|
+
}
|
|
131
|
+
if (summary.warnings.length > 0) {
|
|
132
|
+
warningCount++;
|
|
133
|
+
}
|
|
134
|
+
articles.push(summary);
|
|
135
|
+
}
|
|
136
|
+
const report = {
|
|
137
|
+
totalArticles: files.length,
|
|
138
|
+
passed,
|
|
139
|
+
failed,
|
|
140
|
+
warnings: warningCount,
|
|
141
|
+
articles,
|
|
142
|
+
};
|
|
143
|
+
if (qualityMode) {
|
|
144
|
+
report.gradeDistribution = buildGradeDistribution(articles);
|
|
145
|
+
}
|
|
146
|
+
return report;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Build a grade distribution map from article quality reports.
|
|
150
|
+
*
|
|
151
|
+
* @param articles - Array of article validation summaries
|
|
152
|
+
* @returns Grade distribution counts
|
|
153
|
+
*/
|
|
154
|
+
function buildGradeDistribution(articles) {
|
|
155
|
+
const distribution = { A: 0, B: 0, C: 0, D: 0, F: 0 };
|
|
156
|
+
for (const article of articles) {
|
|
157
|
+
if (article.qualityReport) {
|
|
158
|
+
distribution[article.qualityReport.grade]++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return distribution;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Print a formatted validation report to the console.
|
|
165
|
+
*
|
|
166
|
+
* @param report - Validation report to print
|
|
167
|
+
*/
|
|
168
|
+
function printReport(report) {
|
|
169
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
170
|
+
console.log(' EU Parliament Monitor ā Article Validation Report');
|
|
171
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
172
|
+
if (filterDate) {
|
|
173
|
+
console.log(` Filter: articles from ${filterDate}`);
|
|
174
|
+
}
|
|
175
|
+
if (strictMode) {
|
|
176
|
+
console.log(' Mode: STRICT (warnings treated as errors)');
|
|
177
|
+
}
|
|
178
|
+
if (qualityMode) {
|
|
179
|
+
console.log(' Mode: QUALITY scoring enabled');
|
|
180
|
+
}
|
|
181
|
+
console.log(` Total articles: ${report.totalArticles}`);
|
|
182
|
+
console.log(` ā
Passed: ${report.passed}`);
|
|
183
|
+
console.log(` ā Failed: ${report.failed}`);
|
|
184
|
+
console.log(` ā ļø With warnings: ${report.warnings}\n`);
|
|
185
|
+
printFailures(report);
|
|
186
|
+
printWarnings(report);
|
|
187
|
+
if (qualityMode) {
|
|
188
|
+
printQualityScores(report);
|
|
189
|
+
printGradeDistribution(report);
|
|
190
|
+
}
|
|
191
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Print failure details from the validation report.
|
|
195
|
+
*
|
|
196
|
+
* @param report - Validation report
|
|
197
|
+
*/
|
|
198
|
+
function printFailures(report) {
|
|
199
|
+
const failures = report.articles.filter((a) => !a.valid);
|
|
200
|
+
if (failures.length === 0)
|
|
201
|
+
return;
|
|
202
|
+
console.log('āā FAILURES āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
203
|
+
for (const article of failures) {
|
|
204
|
+
console.log(` ā ${article.filename} (${article.wordCount} words)`);
|
|
205
|
+
for (const error of article.errors) {
|
|
206
|
+
console.log(` ERROR: ${error}`);
|
|
207
|
+
}
|
|
208
|
+
for (const warning of article.warnings) {
|
|
209
|
+
console.log(` WARN: ${warning}`);
|
|
210
|
+
}
|
|
211
|
+
console.log('');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Print warning details from the validation report.
|
|
216
|
+
*
|
|
217
|
+
* @param report - Validation report
|
|
218
|
+
*/
|
|
219
|
+
function printWarnings(report) {
|
|
220
|
+
const withWarnings = report.articles.filter((a) => a.valid && a.warnings.length > 0);
|
|
221
|
+
if (withWarnings.length === 0)
|
|
222
|
+
return;
|
|
223
|
+
console.log('āā WARNINGS āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
224
|
+
for (const article of withWarnings) {
|
|
225
|
+
console.log(` ā ļø ${article.filename} (${article.wordCount} words)`);
|
|
226
|
+
for (const warning of article.warnings) {
|
|
227
|
+
console.log(` WARN: ${warning}`);
|
|
228
|
+
}
|
|
229
|
+
console.log('');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Print quality scores for all articles (only in --quality mode).
|
|
234
|
+
*
|
|
235
|
+
* @param report - Validation report
|
|
236
|
+
*/
|
|
237
|
+
function printQualityScores(report) {
|
|
238
|
+
const articlesWithQuality = report.articles.filter((a) => a.qualityReport);
|
|
239
|
+
if (articlesWithQuality.length === 0)
|
|
240
|
+
return;
|
|
241
|
+
console.log('āā QUALITY SCORES āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
242
|
+
for (const article of articlesWithQuality) {
|
|
243
|
+
const qr = article.qualityReport;
|
|
244
|
+
if (!qr)
|
|
245
|
+
continue;
|
|
246
|
+
const gate = qr.passesQualityGate ? 'ā
' : 'ā ļø ';
|
|
247
|
+
console.log(` ${gate} ${article.filename} ā Grade: ${qr.grade} (${qr.overallScore}/100, ${qr.wordCount} words)`);
|
|
248
|
+
if (!qr.passesQualityGate && qr.recommendations.length > 0) {
|
|
249
|
+
for (const rec of qr.recommendations.slice(0, 3)) {
|
|
250
|
+
console.log(` š” ${rec}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
console.log('');
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Print the grade distribution summary (only in --quality mode).
|
|
258
|
+
*
|
|
259
|
+
* @param report - Validation report
|
|
260
|
+
*/
|
|
261
|
+
function printGradeDistribution(report) {
|
|
262
|
+
if (!report.gradeDistribution)
|
|
263
|
+
return;
|
|
264
|
+
const dist = report.gradeDistribution;
|
|
265
|
+
console.log('āā GRADE DISTRIBUTION āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
266
|
+
console.log(` A (ā„80): ${dist['A']} B (ā„65): ${dist['B']} C (ā„40): ${dist['C']} D (ā„25): ${dist['D']} F (<25): ${dist['F']}`);
|
|
267
|
+
console.log('');
|
|
268
|
+
}
|
|
269
|
+
// āāā CLI execution āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
270
|
+
const report = validateAllArticles();
|
|
271
|
+
printReport(report);
|
|
272
|
+
if (qualityMode && outputFormat === 'json') {
|
|
273
|
+
const outputPath = path.join(process.cwd(), 'quality-report.json');
|
|
274
|
+
fs.writeFileSync(outputPath, JSON.stringify(report, null, 2), 'utf-8');
|
|
275
|
+
console.log(`š Quality report written to ${outputPath}`);
|
|
276
|
+
}
|
|
277
|
+
if (!dryRun && report.failed > 0) {
|
|
278
|
+
console.error(`ā Validation failed: ${report.failed} article(s) have errors`);
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
else if (report.failed === 0) {
|
|
282
|
+
console.log('ā
All articles passed validation');
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=validate-articles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-articles.js","sourceRoot":"","sources":["../../src/utils/validate-articles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Result of validating a single committee endpoint */
|
|
2
|
+
export interface CommitteeValidationResult {
|
|
3
|
+
/** Committee abbreviation code (e.g. `"ENVI"`) */
|
|
4
|
+
abbreviation: string;
|
|
5
|
+
/** Whether the endpoint returned usable real data */
|
|
6
|
+
success: boolean;
|
|
7
|
+
/** Whether a human-readable name was found */
|
|
8
|
+
hasName: boolean;
|
|
9
|
+
/** Whether a label/abbreviation code was found */
|
|
10
|
+
hasLabel: boolean;
|
|
11
|
+
/** Whether the classification identifies a standing committee */
|
|
12
|
+
hasClassification: boolean;
|
|
13
|
+
/** English name of the committee, or null when unavailable */
|
|
14
|
+
name: string | null;
|
|
15
|
+
/** Error message when the request failed, or null on success */
|
|
16
|
+
error: string | null;
|
|
17
|
+
/** Round-trip time in milliseconds */
|
|
18
|
+
responseTimeMs: number;
|
|
19
|
+
}
|
|
20
|
+
/** Overall validation summary for a batch of committee checks */
|
|
21
|
+
export interface EPAPIValidationSummary {
|
|
22
|
+
/** ISO 8601 timestamp when validation was performed */
|
|
23
|
+
timestamp: string;
|
|
24
|
+
/** Base URL of the EP API that was validated */
|
|
25
|
+
apiBase: string;
|
|
26
|
+
/** Number of committees checked */
|
|
27
|
+
totalChecked: number;
|
|
28
|
+
/** Number of committees that returned valid data */
|
|
29
|
+
totalPassed: number;
|
|
30
|
+
/** Number of committees that failed validation */
|
|
31
|
+
totalFailed: number;
|
|
32
|
+
/** Per-committee validation results */
|
|
33
|
+
results: CommitteeValidationResult[];
|
|
34
|
+
/** Whether all checked committees passed validation */
|
|
35
|
+
allValid: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate a single committee endpoint on the EP v2 API.
|
|
39
|
+
*
|
|
40
|
+
* @param abbreviation - Committee code (e.g. `"ENVI"`)
|
|
41
|
+
* @returns Validation result
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateCommitteeEndpoint(abbreviation: string): Promise<CommitteeValidationResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Validate all specified committee endpoints and return a summary.
|
|
46
|
+
*
|
|
47
|
+
* @param committees - Array of committee abbreviations to validate
|
|
48
|
+
* @returns Validation summary
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateEPAPI(committees?: readonly string[]): Promise<EPAPIValidationSummary>;
|
|
51
|
+
//# sourceMappingURL=validate-ep-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-ep-api.d.ts","sourceRoot":"","sources":["../../src/utils/validate-ep-api.ts"],"names":[],"mappings":"AAmCA,uDAAuD;AACvD,MAAM,WAAW,yBAAyB;IACxC,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gEAAgE;IAChE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,iEAAiE;AACjE,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACrC,uDAAuD;IACvD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,yBAAyB,CAAC,CA4EpC;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,UAAU,GAAE,SAAS,MAAM,EAAuB,GACjD,OAAO,CAAC,sBAAsB,CAAC,CAYjC"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* @module Utils/ValidateEPAPI
|
|
5
|
+
* @description Validates that the European Parliament v2 API returns real data
|
|
6
|
+
* for committee endpoints. Can be run standalone as a pre-flight check or
|
|
7
|
+
* imported as a library for integration tests.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx tsx src/utils/validate-ep-api.ts
|
|
11
|
+
* npx tsx src/utils/validate-ep-api.ts --committees=ENVI,ECON
|
|
12
|
+
*/
|
|
13
|
+
import { resolve } from 'node:path';
|
|
14
|
+
import { pathToFileURL } from 'node:url';
|
|
15
|
+
/** Base URL for the EP Open Data Portal v2 API */
|
|
16
|
+
const EP_API_V2_BASE = 'https://data.europarl.europa.eu/api/v2';
|
|
17
|
+
/** Default timeout for API requests (ms) */
|
|
18
|
+
const REQUEST_TIMEOUT_MS = 20_000;
|
|
19
|
+
/** Default committees to validate */
|
|
20
|
+
const DEFAULT_COMMITTEES = ['ENVI', 'ECON', 'AFET', 'LIBE', 'AGRI'];
|
|
21
|
+
/**
|
|
22
|
+
* Validate a single committee endpoint on the EP v2 API.
|
|
23
|
+
*
|
|
24
|
+
* @param abbreviation - Committee code (e.g. `"ENVI"`)
|
|
25
|
+
* @returns Validation result
|
|
26
|
+
*/
|
|
27
|
+
export async function validateCommitteeEndpoint(abbreviation) {
|
|
28
|
+
const url = `${EP_API_V2_BASE}/corporate-bodies/${encodeURIComponent(abbreviation)}?format=application%2Fld%2Bjson`;
|
|
29
|
+
const start = Date.now();
|
|
30
|
+
try {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
33
|
+
let response;
|
|
34
|
+
try {
|
|
35
|
+
response = await fetch(url, {
|
|
36
|
+
signal: controller.signal,
|
|
37
|
+
headers: { Accept: 'application/ld+json' },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
}
|
|
43
|
+
const responseTimeMs = Date.now() - start;
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
return {
|
|
46
|
+
abbreviation,
|
|
47
|
+
success: false,
|
|
48
|
+
hasName: false,
|
|
49
|
+
hasLabel: false,
|
|
50
|
+
hasClassification: false,
|
|
51
|
+
name: null,
|
|
52
|
+
error: `HTTP ${String(response.status)}`,
|
|
53
|
+
responseTimeMs,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const body = (await response.json());
|
|
57
|
+
const items = body.data;
|
|
58
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
59
|
+
return {
|
|
60
|
+
abbreviation,
|
|
61
|
+
success: false,
|
|
62
|
+
hasName: false,
|
|
63
|
+
hasLabel: false,
|
|
64
|
+
hasClassification: false,
|
|
65
|
+
name: null,
|
|
66
|
+
error: 'No data items in response',
|
|
67
|
+
responseTimeMs,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const item = items[0];
|
|
71
|
+
const name = item?.prefLabel?.['en'] ?? item?.altLabel?.['en'] ?? null;
|
|
72
|
+
const label = item?.label ?? null;
|
|
73
|
+
const classification = item?.classification ?? null;
|
|
74
|
+
const hasName = name !== null && name.length > 0;
|
|
75
|
+
const hasLabel = label !== null && label.length > 0;
|
|
76
|
+
const hasClassification = classification?.includes('COMMITTEE') ?? false;
|
|
77
|
+
return {
|
|
78
|
+
abbreviation,
|
|
79
|
+
success: hasName && hasLabel && hasClassification,
|
|
80
|
+
hasName,
|
|
81
|
+
hasLabel,
|
|
82
|
+
hasClassification,
|
|
83
|
+
name,
|
|
84
|
+
error: null,
|
|
85
|
+
responseTimeMs,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return {
|
|
90
|
+
abbreviation,
|
|
91
|
+
success: false,
|
|
92
|
+
hasName: false,
|
|
93
|
+
hasLabel: false,
|
|
94
|
+
hasClassification: false,
|
|
95
|
+
name: null,
|
|
96
|
+
error: err instanceof Error ? err.message : String(err),
|
|
97
|
+
responseTimeMs: Date.now() - start,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate all specified committee endpoints and return a summary.
|
|
103
|
+
*
|
|
104
|
+
* @param committees - Array of committee abbreviations to validate
|
|
105
|
+
* @returns Validation summary
|
|
106
|
+
*/
|
|
107
|
+
export async function validateEPAPI(committees = DEFAULT_COMMITTEES) {
|
|
108
|
+
const results = await Promise.all(committees.map((abbr) => validateCommitteeEndpoint(abbr)));
|
|
109
|
+
const totalPassed = results.filter((r) => r.success).length;
|
|
110
|
+
return {
|
|
111
|
+
timestamp: new Date().toISOString(),
|
|
112
|
+
apiBase: EP_API_V2_BASE,
|
|
113
|
+
totalChecked: results.length,
|
|
114
|
+
totalPassed,
|
|
115
|
+
totalFailed: results.length - totalPassed,
|
|
116
|
+
results,
|
|
117
|
+
allValid: totalPassed === results.length,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// āāā CLI entry point āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
121
|
+
/* c8 ignore start */
|
|
122
|
+
async function main() {
|
|
123
|
+
const args = process.argv.slice(2);
|
|
124
|
+
const committeesArg = args.find((a) => a.startsWith('--committees='));
|
|
125
|
+
const parsedCommittees = committeesArg
|
|
126
|
+
? committeesArg
|
|
127
|
+
.replace('--committees=', '')
|
|
128
|
+
.split(',')
|
|
129
|
+
.map((committee) => committee.trim())
|
|
130
|
+
.filter((committee) => committee.length > 0)
|
|
131
|
+
: [];
|
|
132
|
+
const committees = parsedCommittees.length > 0 ? parsedCommittees : [...DEFAULT_COMMITTEES];
|
|
133
|
+
console.log(`\nš Validating EP v2 API for committees: ${committees.join(', ')}\n`);
|
|
134
|
+
const summary = await validateEPAPI(committees);
|
|
135
|
+
for (const r of summary.results) {
|
|
136
|
+
const icon = r.success ? 'ā
' : 'ā';
|
|
137
|
+
const detail = r.success
|
|
138
|
+
? `${r.name} (${String(r.responseTimeMs)}ms)`
|
|
139
|
+
: `Error: ${r.error ?? 'unknown'} (${String(r.responseTimeMs)}ms)`;
|
|
140
|
+
console.log(` ${icon} ${r.abbreviation}: ${detail}`);
|
|
141
|
+
}
|
|
142
|
+
console.log(`\nš Summary: ${String(summary.totalPassed)}/${String(summary.totalChecked)} passed`);
|
|
143
|
+
if (!summary.allValid) {
|
|
144
|
+
console.error('\nā EP v2 API validation failed ā some endpoints returned no real data');
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.log('\nā
All EP v2 API committee endpoints returned real data');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Only run if executed directly (not imported)
|
|
152
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
153
|
+
main().catch((err) => {
|
|
154
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
155
|
+
console.error('Fatal error:', message);
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/* c8 ignore stop */
|
|
160
|
+
//# sourceMappingURL=validate-ep-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-ep-api.js","sourceRoot":"","sources":["../../src/utils/validate-ep-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Utils/WorldBankData
|
|
3
|
+
* @description Utility functions for World Bank economic data integration.
|
|
4
|
+
*
|
|
5
|
+
* Provides EU member state ā World Bank country code mapping,
|
|
6
|
+
* CSV parsing for MCP responses, indicator formatting, and economic
|
|
7
|
+
* context building for EU Parliament article enrichment.
|
|
8
|
+
*
|
|
9
|
+
* Functions in this module are designed to be stateless and avoid observable
|
|
10
|
+
* side effects, with the exception of explicitly recording metadata such as
|
|
11
|
+
* data timestamps in returned objects.
|
|
12
|
+
*/
|
|
13
|
+
import type { EconomicContext, PolicyRelevantIndicators, WorldBankIndicator } from '../types/world-bank.js';
|
|
14
|
+
/**
|
|
15
|
+
* Maps EU member state ISO 3166-1 alpha-2 codes to World Bank alpha-3 codes.
|
|
16
|
+
* Covers all 27 EU member states.
|
|
17
|
+
*/
|
|
18
|
+
export declare const EU_COUNTRY_CODES: Readonly<Record<string, string>>;
|
|
19
|
+
/** EU aggregate code in World Bank (European Union) */
|
|
20
|
+
export declare const EU_AGGREGATE_CODE = "EUU";
|
|
21
|
+
/**
|
|
22
|
+
* World Bank indicator IDs relevant to EU Parliament policy analysis.
|
|
23
|
+
* These indicators map to common policy areas discussed in EP legislation.
|
|
24
|
+
*/
|
|
25
|
+
export declare const POLICY_INDICATORS: PolicyRelevantIndicators;
|
|
26
|
+
/**
|
|
27
|
+
* Parse CSV data from World Bank MCP response into structured indicator objects.
|
|
28
|
+
* The World Bank MCP server returns indicator data as CSV text via pandas.
|
|
29
|
+
* Handles quoted fields that may contain commas (e.g., indicator names).
|
|
30
|
+
*
|
|
31
|
+
* @param csvText - Raw CSV text from the MCP tool response (accepts null/undefined for convenience)
|
|
32
|
+
* @returns Array of parsed World Bank indicator data points
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseWorldBankCSV(csvText: string | null | undefined): WorldBankIndicator[];
|
|
35
|
+
/**
|
|
36
|
+
* Format a numeric value for display based on the indicator type.
|
|
37
|
+
*
|
|
38
|
+
* @param value - The numeric value to format
|
|
39
|
+
* @param indicatorId - The World Bank indicator ID (determines formatting style)
|
|
40
|
+
* @returns Formatted display string
|
|
41
|
+
*/
|
|
42
|
+
export declare function formatIndicatorValue(value: number | null, indicatorId: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Extract the most recent non-null data point from a series of World Bank indicators.
|
|
45
|
+
*
|
|
46
|
+
* @param indicators - Array of indicator data points
|
|
47
|
+
* @returns The most recent data point with a non-null value, or null if none found
|
|
48
|
+
*/
|
|
49
|
+
export declare function getMostRecentValue(indicators: readonly WorldBankIndicator[]): WorldBankIndicator | null;
|
|
50
|
+
/**
|
|
51
|
+
* Build an economic context summary from raw World Bank indicator data.
|
|
52
|
+
*
|
|
53
|
+
* @param countryCode - EU member state ISO2 code
|
|
54
|
+
* @param countryName - Country display name
|
|
55
|
+
* @param indicatorData - Map of indicator ID to parsed data points
|
|
56
|
+
* @returns Structured economic context for article enrichment
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildEconomicContext(countryCode: string, countryName: string, indicatorData: ReadonlyMap<string, readonly WorldBankIndicator[]>): EconomicContext;
|
|
59
|
+
/**
|
|
60
|
+
* Get the World Bank country code for an EU member state.
|
|
61
|
+
*
|
|
62
|
+
* @param iso2Code - EU member state ISO 3166-1 alpha-2 code (e.g., 'DE', 'FR')
|
|
63
|
+
* @returns World Bank alpha-3 code or null if not an EU member state
|
|
64
|
+
*/
|
|
65
|
+
export declare function getWorldBankCountryCode(iso2Code: string): string | null;
|
|
66
|
+
/**
|
|
67
|
+
* Check if a country code corresponds to an EU member state.
|
|
68
|
+
*
|
|
69
|
+
* @param iso2Code - ISO 3166-1 alpha-2 country code
|
|
70
|
+
* @returns True if the country is an EU member state
|
|
71
|
+
*/
|
|
72
|
+
export declare function isEUMemberState(iso2Code: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Generate an HTML section with economic context data for article embedding.
|
|
75
|
+
*
|
|
76
|
+
* Note: UI strings are currently in English. A future enhancement should accept
|
|
77
|
+
* a `lang` parameter and use localized string maps (similar to `WEEK_AHEAD_STRINGS`)
|
|
78
|
+
* to support all 14 article languages.
|
|
79
|
+
*
|
|
80
|
+
* @param context - Economic context data
|
|
81
|
+
* @returns Sanitized HTML string for the economic context section
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildEconomicContextHTML(context: EconomicContext): string;
|
|
84
|
+
//# sourceMappingURL=world-bank-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-bank-data.d.ts","sourceRoot":"","sources":["../../src/utils/world-bank-data.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,eAAe,EAEf,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAKhC;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA4BpD,CAAC;AAEX,uDAAuD;AACvD,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,wBAStB,CAAC;AAsEX;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,kBAAkB,EAAE,CAqC1F;AAID;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CA+CtF;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,kBAAkB,EAAE,GACxC,kBAAkB,GAAG,IAAI,CAO3B;AAID;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC,GAChE,eAAe,CAiCjB;AAID;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGvE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAID;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAuBzE"}
|