riksdagsmonitor 0.8.15 → 0.8.17

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.
Files changed (168) hide show
  1. package/dist/lib/dashboards/coalition-dashboard.d.ts.map +1 -1
  2. package/dist/lib/dashboards/coalition-dashboard.js +25 -9
  3. package/dist/lib/dashboards/coalition-dashboard.js.map +1 -1
  4. package/dist/lib/dashboards/coalition-loader.d.ts.map +1 -1
  5. package/dist/lib/dashboards/coalition-loader.js +68 -32
  6. package/dist/lib/dashboards/coalition-loader.js.map +1 -1
  7. package/dist/lib/shared/chart-factory.d.ts +9 -2
  8. package/dist/lib/shared/chart-factory.d.ts.map +1 -1
  9. package/dist/lib/shared/chart-factory.js +9 -2
  10. package/dist/lib/shared/chart-factory.js.map +1 -1
  11. package/dist/lib/shared/theme.d.ts +1 -18
  12. package/dist/lib/shared/theme.d.ts.map +1 -1
  13. package/dist/lib/shared/theme.js +1 -18
  14. package/dist/lib/shared/theme.js.map +1 -1
  15. package/dist/scripts/analysis-reader.d.ts +2 -1
  16. package/dist/scripts/analysis-reader.d.ts.map +1 -1
  17. package/dist/scripts/analysis-reader.js.map +1 -1
  18. package/dist/scripts/data-transformers/content-generators/index.d.ts +32 -4
  19. package/dist/scripts/data-transformers/content-generators/index.d.ts.map +1 -1
  20. package/dist/scripts/data-transformers/content-generators/index.js +4 -2
  21. package/dist/scripts/data-transformers/content-generators/index.js.map +1 -1
  22. package/dist/scripts/data-transformers/content-generators/shared.d.ts +44 -2
  23. package/dist/scripts/data-transformers/content-generators/shared.d.ts.map +1 -1
  24. package/dist/scripts/data-transformers/content-generators/shared.js +9 -3
  25. package/dist/scripts/data-transformers/content-generators/shared.js.map +1 -1
  26. package/dist/scripts/generate-news-enhanced/config.d.ts +5 -4
  27. package/dist/scripts/generate-news-enhanced/config.d.ts.map +1 -1
  28. package/dist/scripts/generate-news-enhanced/config.js +3 -3
  29. package/dist/scripts/generate-news-enhanced/config.js.map +1 -1
  30. package/dist/scripts/generate-news-enhanced/generators.d.ts.map +1 -1
  31. package/dist/scripts/generate-news-enhanced/generators.js +38 -15
  32. package/dist/scripts/generate-news-enhanced/generators.js.map +1 -1
  33. package/dist/scripts/generate-news-enhanced/helpers.d.ts +1 -2
  34. package/dist/scripts/generate-news-enhanced/helpers.d.ts.map +1 -1
  35. package/dist/scripts/generate-news-enhanced/helpers.js +31 -1
  36. package/dist/scripts/generate-news-enhanced/helpers.js.map +1 -1
  37. package/dist/scripts/generate-news-enhanced/swot-analyzer.d.ts +12 -1
  38. package/dist/scripts/generate-news-enhanced/swot-analyzer.d.ts.map +1 -1
  39. package/dist/scripts/generate-news-enhanced/swot-analyzer.js +48 -0
  40. package/dist/scripts/generate-news-enhanced/swot-analyzer.js.map +1 -1
  41. package/dist/scripts/news-types/breaking-news.d.ts.map +1 -1
  42. package/dist/scripts/news-types/breaking-news.js +5 -1
  43. package/dist/scripts/news-types/breaking-news.js.map +1 -1
  44. package/dist/scripts/pre-article-analysis/markdown-serializer.d.ts +64 -1
  45. package/dist/scripts/pre-article-analysis/markdown-serializer.d.ts.map +1 -1
  46. package/dist/scripts/pre-article-analysis/markdown-serializer.js.map +1 -1
  47. package/dist/scripts/pre-article-analysis.d.ts.map +1 -1
  48. package/dist/scripts/pre-article-analysis.js +20 -7
  49. package/dist/scripts/pre-article-analysis.js.map +1 -1
  50. package/dist/scripts/types/article.d.ts +33 -3
  51. package/dist/scripts/types/article.d.ts.map +1 -1
  52. package/package.json +3 -3
  53. package/dist/scripts/ai-analysis/coalition-detector.d.ts +0 -28
  54. package/dist/scripts/ai-analysis/coalition-detector.d.ts.map +0 -1
  55. package/dist/scripts/ai-analysis/coalition-detector.js +0 -242
  56. package/dist/scripts/ai-analysis/coalition-detector.js.map +0 -1
  57. package/dist/scripts/ai-analysis/dashboard-analyzer.d.ts +0 -73
  58. package/dist/scripts/ai-analysis/dashboard-analyzer.d.ts.map +0 -1
  59. package/dist/scripts/ai-analysis/dashboard-analyzer.js +0 -511
  60. package/dist/scripts/ai-analysis/dashboard-analyzer.js.map +0 -1
  61. package/dist/scripts/ai-analysis/document-analyzer.d.ts +0 -196
  62. package/dist/scripts/ai-analysis/document-analyzer.d.ts.map +0 -1
  63. package/dist/scripts/ai-analysis/document-analyzer.js +0 -1001
  64. package/dist/scripts/ai-analysis/document-analyzer.js.map +0 -1
  65. package/dist/scripts/ai-analysis/domains/index.d.ts +0 -15
  66. package/dist/scripts/ai-analysis/domains/index.d.ts.map +0 -1
  67. package/dist/scripts/ai-analysis/domains/index.js +0 -494
  68. package/dist/scripts/ai-analysis/domains/index.js.map +0 -1
  69. package/dist/scripts/ai-analysis/helpers.d.ts +0 -43
  70. package/dist/scripts/ai-analysis/helpers.d.ts.map +0 -1
  71. package/dist/scripts/ai-analysis/helpers.js +0 -79
  72. package/dist/scripts/ai-analysis/helpers.js.map +0 -1
  73. package/dist/scripts/ai-analysis/index.d.ts +0 -15
  74. package/dist/scripts/ai-analysis/index.d.ts.map +0 -1
  75. package/dist/scripts/ai-analysis/index.js +0 -14
  76. package/dist/scripts/ai-analysis/index.js.map +0 -1
  77. package/dist/scripts/ai-analysis/pipeline.d.ts +0 -41
  78. package/dist/scripts/ai-analysis/pipeline.d.ts.map +0 -1
  79. package/dist/scripts/ai-analysis/pipeline.js +0 -208
  80. package/dist/scripts/ai-analysis/pipeline.js.map +0 -1
  81. package/dist/scripts/ai-analysis/political-significance.d.ts +0 -63
  82. package/dist/scripts/ai-analysis/political-significance.d.ts.map +0 -1
  83. package/dist/scripts/ai-analysis/political-significance.js +0 -227
  84. package/dist/scripts/ai-analysis/political-significance.js.map +0 -1
  85. package/dist/scripts/ai-analysis/quality-assessor.d.ts +0 -104
  86. package/dist/scripts/ai-analysis/quality-assessor.d.ts.map +0 -1
  87. package/dist/scripts/ai-analysis/quality-assessor.js +0 -554
  88. package/dist/scripts/ai-analysis/quality-assessor.js.map +0 -1
  89. package/dist/scripts/ai-analysis/swot/index.d.ts +0 -56
  90. package/dist/scripts/ai-analysis/swot/index.d.ts.map +0 -1
  91. package/dist/scripts/ai-analysis/swot/index.js +0 -360
  92. package/dist/scripts/ai-analysis/swot/index.js.map +0 -1
  93. package/dist/scripts/ai-analysis/swot/placeholders.d.ts +0 -30
  94. package/dist/scripts/ai-analysis/swot/placeholders.d.ts.map +0 -1
  95. package/dist/scripts/ai-analysis/swot/placeholders.js +0 -381
  96. package/dist/scripts/ai-analysis/swot/placeholders.js.map +0 -1
  97. package/dist/scripts/ai-analysis/types.d.ts +0 -333
  98. package/dist/scripts/ai-analysis/types.d.ts.map +0 -1
  99. package/dist/scripts/ai-analysis/types.js +0 -19
  100. package/dist/scripts/ai-analysis/types.js.map +0 -1
  101. package/dist/scripts/ai-analysis/visualisation/index.d.ts +0 -15
  102. package/dist/scripts/ai-analysis/visualisation/index.d.ts.map +0 -1
  103. package/dist/scripts/ai-analysis/visualisation/index.js +0 -179
  104. package/dist/scripts/ai-analysis/visualisation/index.js.map +0 -1
  105. package/dist/scripts/analysis-framework/cross-reference.d.ts +0 -35
  106. package/dist/scripts/analysis-framework/cross-reference.d.ts.map +0 -1
  107. package/dist/scripts/analysis-framework/cross-reference.js +0 -290
  108. package/dist/scripts/analysis-framework/cross-reference.js.map +0 -1
  109. package/dist/scripts/analysis-framework/index.d.ts +0 -79
  110. package/dist/scripts/analysis-framework/index.d.ts.map +0 -1
  111. package/dist/scripts/analysis-framework/index.js +0 -133
  112. package/dist/scripts/analysis-framework/index.js.map +0 -1
  113. package/dist/scripts/analysis-framework/lenses/citizen.d.ts +0 -29
  114. package/dist/scripts/analysis-framework/lenses/citizen.d.ts.map +0 -1
  115. package/dist/scripts/analysis-framework/lenses/citizen.js +0 -220
  116. package/dist/scripts/analysis-framework/lenses/citizen.js.map +0 -1
  117. package/dist/scripts/analysis-framework/lenses/economic.d.ts +0 -29
  118. package/dist/scripts/analysis-framework/lenses/economic.d.ts.map +0 -1
  119. package/dist/scripts/analysis-framework/lenses/economic.js +0 -227
  120. package/dist/scripts/analysis-framework/lenses/economic.js.map +0 -1
  121. package/dist/scripts/analysis-framework/lenses/government.d.ts +0 -32
  122. package/dist/scripts/analysis-framework/lenses/government.d.ts.map +0 -1
  123. package/dist/scripts/analysis-framework/lenses/government.js +0 -265
  124. package/dist/scripts/analysis-framework/lenses/government.js.map +0 -1
  125. package/dist/scripts/analysis-framework/lenses/international.d.ts +0 -29
  126. package/dist/scripts/analysis-framework/lenses/international.d.ts.map +0 -1
  127. package/dist/scripts/analysis-framework/lenses/international.js +0 -248
  128. package/dist/scripts/analysis-framework/lenses/international.js.map +0 -1
  129. package/dist/scripts/analysis-framework/lenses/media.d.ts +0 -29
  130. package/dist/scripts/analysis-framework/lenses/media.d.ts.map +0 -1
  131. package/dist/scripts/analysis-framework/lenses/media.js +0 -211
  132. package/dist/scripts/analysis-framework/lenses/media.js.map +0 -1
  133. package/dist/scripts/analysis-framework/lenses/opposition.d.ts +0 -29
  134. package/dist/scripts/analysis-framework/lenses/opposition.d.ts.map +0 -1
  135. package/dist/scripts/analysis-framework/lenses/opposition.js +0 -234
  136. package/dist/scripts/analysis-framework/lenses/opposition.js.map +0 -1
  137. package/dist/scripts/analysis-framework/methodology-types.d.ts +0 -309
  138. package/dist/scripts/analysis-framework/methodology-types.d.ts.map +0 -1
  139. package/dist/scripts/analysis-framework/methodology-types.js +0 -38
  140. package/dist/scripts/analysis-framework/methodology-types.js.map +0 -1
  141. package/dist/scripts/analysis-framework/political-classification.d.ts +0 -58
  142. package/dist/scripts/analysis-framework/political-classification.d.ts.map +0 -1
  143. package/dist/scripts/analysis-framework/political-classification.js +0 -414
  144. package/dist/scripts/analysis-framework/political-classification.js.map +0 -1
  145. package/dist/scripts/analysis-framework/political-risk-assessment.d.ts +0 -74
  146. package/dist/scripts/analysis-framework/political-risk-assessment.d.ts.map +0 -1
  147. package/dist/scripts/analysis-framework/political-risk-assessment.js +0 -656
  148. package/dist/scripts/analysis-framework/political-risk-assessment.js.map +0 -1
  149. package/dist/scripts/analysis-framework/political-threat-analysis.d.ts +0 -65
  150. package/dist/scripts/analysis-framework/political-threat-analysis.d.ts.map +0 -1
  151. package/dist/scripts/analysis-framework/political-threat-analysis.js +0 -553
  152. package/dist/scripts/analysis-framework/political-threat-analysis.js.map +0 -1
  153. package/dist/scripts/analysis-framework/significance-scorer.d.ts +0 -62
  154. package/dist/scripts/analysis-framework/significance-scorer.d.ts.map +0 -1
  155. package/dist/scripts/analysis-framework/significance-scorer.js +0 -196
  156. package/dist/scripts/analysis-framework/significance-scorer.js.map +0 -1
  157. package/dist/scripts/analysis-framework/types.d.ts +0 -138
  158. package/dist/scripts/analysis-framework/types.d.ts.map +0 -1
  159. package/dist/scripts/analysis-framework/types.js +0 -12
  160. package/dist/scripts/analysis-framework/types.js.map +0 -1
  161. package/dist/scripts/data-transformers/content-generators/ai-swot-analyzer.d.ts +0 -89
  162. package/dist/scripts/data-transformers/content-generators/ai-swot-analyzer.d.ts.map +0 -1
  163. package/dist/scripts/data-transformers/content-generators/ai-swot-analyzer.js +0 -667
  164. package/dist/scripts/data-transformers/content-generators/ai-swot-analyzer.js.map +0 -1
  165. package/dist/scripts/data-transformers/content-generators/stakeholder-swot-section.d.ts +0 -83
  166. package/dist/scripts/data-transformers/content-generators/stakeholder-swot-section.d.ts.map +0 -1
  167. package/dist/scripts/data-transformers/content-generators/stakeholder-swot-section.js +0 -219
  168. package/dist/scripts/data-transformers/content-generators/stakeholder-swot-section.js.map +0 -1
@@ -1,667 +0,0 @@
1
- /**
2
- * @module data-transformers/content-generators/ai-swot-analyzer
3
- * @description Heuristic multi-perspective SWOT analysis builder.
4
- *
5
- * Produces substantive analytical SWOT entries for 6 stakeholder perspectives
6
- * using document-type-aware routing and rules-based reasoning over parliamentary
7
- * document metadata, summaries, and topics. Each entry carries justification,
8
- * trend direction, confidence scoring, and optional quantitative evidence —
9
- * rather than raw document title truncation.
10
- *
11
- * Note: the current implementation is deterministic and rules-based. It does
12
- * NOT invoke LLM or MCP prompts; "AI" in the public API names is aspirational
13
- * and kept for interface stability. A future iteration may wire the prompts
14
- * into the editorial-framework MCP pipeline.
15
- *
16
- * Analytical prose (entry text, justification) is English-only even though the
17
- * deep-inspection pipeline renders articles in all 14 language versions.
18
- * Stakeholder names, roles, and context metadata labels ("Confidence",
19
- * "Cross-references") are fully localised in all 14 supported languages.
20
- *
21
- * The six perspectives are:
22
- * 1. Government Coalition (M, KD, L + SD support)
23
- * 2. Social Democratic Opposition (S, V, C, MP)
24
- * 3. EU / International Actors
25
- * 4. Private Sector & Business
26
- * 5. Civil Society & NGOs
27
- * 6. Swedish Citizens / Voters
28
- *
29
- * @author Hack23 AB
30
- * @license Apache-2.0
31
- */
32
- // ---------------------------------------------------------------------------
33
- // Localised stakeholder names (all 14 languages)
34
- // ---------------------------------------------------------------------------
35
- export const STAKEHOLDER_NAMES = {
36
- 'government-coalition': {
37
- en: 'Government Coalition',
38
- sv: 'Regeringskoalitionen',
39
- da: 'Regeringskoalitionen',
40
- no: 'Regjeringskoalisjonen',
41
- fi: 'Hallituskoalitio',
42
- de: 'Regierungskoalition',
43
- fr: 'Coalition gouvernementale',
44
- es: 'Coalición gubernamental',
45
- nl: 'Regeringscoalitie',
46
- ar: 'الائتلاف الحكومي',
47
- he: 'הקואליציה הממשלתית',
48
- ja: '政府連立',
49
- ko: '정부 연립',
50
- zh: '执政联盟',
51
- },
52
- opposition: {
53
- en: 'Social Democratic Opposition',
54
- sv: 'Socialdemokratisk opposition',
55
- da: 'Socialdemokratisk opposition',
56
- no: 'Sosialdemokratisk opposisjon',
57
- fi: 'Sosiaalidemokraattinen oppositio',
58
- de: 'Sozialdemokratische Opposition',
59
- fr: 'Opposition sociale-démocrate',
60
- es: 'Oposición socialdemócrata',
61
- nl: 'Sociaaldemocratische oppositie',
62
- ar: 'المعارضة الاشتراكية الديمقراطية',
63
- he: 'האופוזיציה הסוציאל-דמוקרטית',
64
- ja: '社会民主主義野党',
65
- ko: '사회민주주의 야당',
66
- zh: '社会民主主义反对派',
67
- },
68
- 'eu-international': {
69
- en: 'EU & International Actors',
70
- sv: 'EU och internationella aktörer',
71
- da: 'EU og internationale aktører',
72
- no: 'EU og internasjonale aktører',
73
- fi: 'EU ja kansainväliset toimijat',
74
- de: 'EU & Internationale Akteure',
75
- fr: 'UE et acteurs internationaux',
76
- es: 'UE y actores internacionales',
77
- nl: 'EU & Internationale actoren',
78
- ar: 'الاتحاد الأوروبي والجهات الدولية',
79
- he: 'האיחוד האירופי וגורמים בינלאומיים',
80
- ja: 'EUと国際的アクター',
81
- ko: 'EU 및 국제 행위자',
82
- zh: '欧盟与国际行为者',
83
- },
84
- 'private-sector': {
85
- en: 'Private Sector & Business',
86
- sv: 'Privat sektor och näringsliv',
87
- da: 'Privat sektor og erhvervsliv',
88
- no: 'Privat sektor og næringsliv',
89
- fi: 'Yksityissektori ja elinkeinoelämä',
90
- de: 'Privatwirtschaft & Unternehmen',
91
- fr: 'Secteur privé & entreprises',
92
- es: 'Sector privado y empresas',
93
- nl: 'Private sector & bedrijfsleven',
94
- ar: 'القطاع الخاص والأعمال',
95
- he: 'המגזר הפרטי והעסקים',
96
- ja: '民間セクターとビジネス',
97
- ko: '민간 부문 및 기업',
98
- zh: '私营部门与商业',
99
- },
100
- 'civil-society': {
101
- en: 'Civil Society & NGOs',
102
- sv: 'Civilsamhälle och NGO:er',
103
- da: 'Civilsamfund og NGO\'er',
104
- no: 'Sivilsamfunn og frivillige organisasjoner',
105
- fi: 'Kansalaisyhteiskunta ja kansalaisjärjestöt',
106
- de: 'Zivilgesellschaft & NGOs',
107
- fr: 'Société civile & ONG',
108
- es: 'Sociedad civil y ONG',
109
- nl: 'Maatschappelijk middenveld & NGO\'s',
110
- ar: 'المجتمع المدني ومنظمات غير حكومية',
111
- he: 'החברה האזרחית וארגוני מלכ"ר',
112
- ja: '市民社会とNGO',
113
- ko: '시민 사회 및 NGO',
114
- zh: '公民社会与非政府组织',
115
- },
116
- 'citizens-voters': {
117
- en: 'Swedish Citizens & Voters',
118
- sv: 'Svenska medborgare och väljare',
119
- da: 'Svenske borgere og vælgere',
120
- no: 'Svenske borgere og velgere',
121
- fi: 'Ruotsalaiset kansalaiset ja äänestäjät',
122
- de: 'Schwedische Bürger & Wähler',
123
- fr: 'Citoyens & électeurs suédois',
124
- es: 'Ciudadanos y votantes suecos',
125
- nl: 'Zweedse burgers & kiezers',
126
- ar: 'المواطنون والناخبون السويديون',
127
- he: 'אזרחים ובוחרים שבדים',
128
- ja: 'スウェーデン市民と有権者',
129
- ko: '스웨덴 시민 및 유권자',
130
- zh: '瑞典公民与选民',
131
- },
132
- };
133
- // ---------------------------------------------------------------------------
134
- // Localised stakeholder roles (all 14 languages)
135
- // ---------------------------------------------------------------------------
136
- const STAKEHOLDER_ROLES = {
137
- 'government-coalition': {
138
- en: 'Tidö Agreement parties: M, KD, L with SD support',
139
- sv: 'Tidöavtalspartierna: M, KD, L med SD:s stöd',
140
- da: 'Tidö-aftalepartierne: M, KD, L med SD-støtte',
141
- no: 'Tidö-avtalepartiene: M, KD, L med SD-støtte',
142
- fi: 'Tidö-sopimuspuolueet: M, KD, L SD:n tuella',
143
- de: 'Tidö-Vereinbarungsparteien: M, KD, L mit SD-Unterstützung',
144
- fr: 'Partis de l\'accord Tidö: M, KD, L avec soutien SD',
145
- es: 'Partidos del acuerdo Tidö: M, KD, L con apoyo SD',
146
- nl: 'Tidö-akkoordpartijen: M, KD, L met SD-steun',
147
- ar: 'أحزاب اتفاقية تيدو: M وKD وL بدعم SD',
148
- he: 'מפלגות הסכם טידו: M, KD, L בתמיכת SD',
149
- ja: 'ティドー協定政党: M、KD、L(SD支持)',
150
- ko: '티도 협정 정당: M, KD, L (SD 지원)',
151
- zh: 'Tidö协议党派:M、KD、L,获SD支持',
152
- },
153
- opposition: {
154
- en: 'S, V, C, MP — alternative governance bloc',
155
- sv: 'S, V, C, MP — alternativt styrningsblock',
156
- da: 'S, V, C, MP — alternativ styringsblok',
157
- no: 'S, V, C, MP — alternativ styringsblokk',
158
- fi: 'S, V, C, MP — vaihtoehtoinen hallintoryhmä',
159
- de: 'S, V, C, MP — alternatives Regierungsblock',
160
- fr: 'S, V, C, MP — bloc de gouvernance alternatif',
161
- es: 'S, V, C, MP — bloque de gobierno alternativo',
162
- nl: 'S, V, C, MP — alternatief regeringsblok',
163
- ar: 'S وV وC وMP — كتلة حوكمة بديلة',
164
- he: 'S, V, C, MP — גוש שלטון חלופי',
165
- ja: 'S、V、C、MP — 代替統治ブロック',
166
- ko: 'S, V, C, MP — 대안 통치 블록',
167
- zh: 'S、V、C、MP — 替代执政集团',
168
- },
169
- 'eu-international': {
170
- en: 'European Union institutions, international bodies & diplomatic actors',
171
- sv: 'EU:s institutioner, internationella organ och diplomatiska aktörer',
172
- da: 'EU-institutioner, internationale organer og diplomatiske aktører',
173
- no: 'EU-institusjoner, internasjonale organer og diplomatiske aktører',
174
- fi: 'EU-instituutiot, kansainväliset elimet ja diplomaattiset toimijat',
175
- de: 'EU-Institutionen, internationale Gremien & diplomatische Akteure',
176
- fr: 'Institutions européennes, organismes internationaux & acteurs diplomatiques',
177
- es: 'Instituciones europeas, organismos internacionales y actores diplomáticos',
178
- nl: 'EU-instellingen, internationale organen & diplomatieke actoren',
179
- ar: 'المؤسسات الأوروبية والهيئات الدولية والجهات الدبلوماسية',
180
- he: 'מוסדות האיחוד האירופי, גופים בינלאומיים ושחקנים דיפלומטיים',
181
- ja: 'EU機関・国際機関・外交的行為者',
182
- ko: 'EU 기관, 국제 기구 및 외교적 행위자',
183
- zh: '欧盟机构、国际组织与外交行为者',
184
- },
185
- 'private-sector': {
186
- en: 'Companies, industry federations, employers & investors',
187
- sv: 'Företag, branschorganisationer, arbetsgivare och investerare',
188
- da: 'Virksomheder, brancheforeninger, arbejdsgivere og investorer',
189
- no: 'Bedrifter, bransjeforeninger, arbeidsgivere og investorer',
190
- fi: 'Yritykset, toimialajärjestöt, työnantajat ja sijoittajat',
191
- de: 'Unternehmen, Branchenverbände, Arbeitgeber & Investoren',
192
- fr: 'Entreprises, fédérations sectorielles, employeurs & investisseurs',
193
- es: 'Empresas, federaciones sectoriales, empleadores e inversores',
194
- nl: 'Bedrijven, brancheverenigingen, werkgevers & investeerders',
195
- ar: 'الشركات والاتحادات القطاعية وأصحاب العمل والمستثمرون',
196
- he: 'חברות, התאחדויות ענפיות, מעסיקים ומשקיעים',
197
- ja: '企業・業界団体・雇用主・投資家',
198
- ko: '기업, 업계 연합, 고용주 및 투자자',
199
- zh: '企业、行业协会、雇主与投资者',
200
- },
201
- 'civil-society': {
202
- en: 'Trade unions, advocacy groups, human rights organisations & media',
203
- sv: 'Fackföreningar, påtryckargrupper, människorättsorganisationer och media',
204
- da: 'Fagforeninger, interesseorganisationer, menneskerettighedsorganisationer og medier',
205
- no: 'Fagforeninger, interesseorganisasjoner, menneskerettighetsorganisasjoner og medier',
206
- fi: 'Ammattiliitot, edunvalvontaryhmät, ihmisoikeusjärjestöt ja media',
207
- de: 'Gewerkschaften, Interessengruppen, Menschenrechtsorganisationen & Medien',
208
- fr: 'Syndicats, groupes de plaidoyer, organisations de droits humains & médias',
209
- es: 'Sindicatos, grupos de defensa, organizaciones de derechos humanos y medios',
210
- nl: 'Vakbonden, belangengroepen, mensenrechtenorganisaties & media',
211
- ar: 'النقابات ومجموعات المناصرة ومنظمات حقوق الإنسان والإعلام',
212
- he: 'ועדי עובדים, קבוצות הסברה, ארגוני זכויות אדם ותקשורת',
213
- ja: '労働組合・権利擁護団体・人権団体・メディア',
214
- ko: '노동조합, 권익 단체, 인권 단체 및 미디어',
215
- zh: '工会、倡导团体、人权组织与媒体',
216
- },
217
- 'citizens-voters': {
218
- en: 'Electorate, public service users & democratic stakeholders',
219
- sv: 'Valmanskåren, användare av offentliga tjänster och demokratiska intressenter',
220
- da: 'Valgberettigede, brugere af offentlige tjenester og demokratiske interessenter',
221
- no: 'Stemmeberettigede, brukere av offentlige tjenester og demokratiske interessenter',
222
- fi: 'Äänioikeutetut, julkisten palvelujen käyttäjät ja demokraattiset sidosryhmät',
223
- de: 'Wählerschaft, Nutzer öffentlicher Dienste & demokratische Stakeholder',
224
- fr: 'Électorat, usagers des services publics & parties prenantes démocratiques',
225
- es: 'Electorado, usuarios de servicios públicos y partes interesadas democráticas',
226
- nl: 'Kiezers, gebruikers van overheidsdiensten & democratische stakeholders',
227
- ar: 'الناخبون ومستخدمو الخدمات العامة وأصحاب المصلحة الديمقراطية',
228
- he: 'מצביעים, משתמשי שירותים ציבוריים ובעלי עניין דמוקרטיים',
229
- ja: '有権者・公共サービス利用者・民主的利害関係者',
230
- ko: '유권자, 공공 서비스 이용자 및 민주적 이해 관계자',
231
- zh: '选民、公共服务用户与民主利益相关者',
232
- },
233
- };
234
- // ---------------------------------------------------------------------------
235
- // Localised context metadata labels (all 14 languages)
236
- // ---------------------------------------------------------------------------
237
- const CONTEXT_LABELS = {
238
- confidence: {
239
- en: 'Confidence',
240
- sv: 'Konfidens',
241
- da: 'Tillid',
242
- no: 'Tillit',
243
- fi: 'Luottamus',
244
- de: 'Konfidenz',
245
- fr: 'Confiance',
246
- es: 'Confianza',
247
- nl: 'Vertrouwen',
248
- ar: 'الثقة',
249
- he: 'ביטחון',
250
- ja: '信頼度',
251
- ko: '신뢰도',
252
- zh: '置信度',
253
- },
254
- crossReferences: {
255
- en: 'Cross-references',
256
- sv: 'Korsreferenser',
257
- da: 'Krydsreferencer',
258
- no: 'Kryssreferanser',
259
- fi: 'Ristiviittaukset',
260
- de: 'Querverweise',
261
- fr: 'Références croisées',
262
- es: 'Referencias cruzadas',
263
- nl: 'Kruisverwijzingen',
264
- ar: 'مراجع متقاطعة',
265
- he: 'הפניות צולבות',
266
- ja: '相互参照',
267
- ko: '교차 참조',
268
- zh: '交叉引用',
269
- },
270
- };
271
- // ---------------------------------------------------------------------------
272
- // Document classification helpers
273
- // ---------------------------------------------------------------------------
274
- /** Minimum summary length to prefer summary over title */
275
- const MIN_SUMMARY_LENGTH = 20;
276
- /** Maximum characters to use from a summary */
277
- const MAX_SUMMARY_CHARS = 120;
278
- /** Maximum characters to use from a document title */
279
- const MAX_TITLE_CHARS = 100;
280
- function titleOf(d) {
281
- return (d.titel || d.title || d.rubrik || d.dokumentnamn || d.dok_id || '').trim();
282
- }
283
- function summaryOf(d) {
284
- return (d.summary || d.notis || d.undertitel || '').trim();
285
- }
286
- /** Extract meaningful content for an AI entry — prefer summary over raw title */
287
- function contentOf(d, fallback) {
288
- const s = summaryOf(d);
289
- if (s.length > MIN_SUMMARY_LENGTH)
290
- return s.slice(0, MAX_SUMMARY_CHARS);
291
- const t = titleOf(d);
292
- return t.length > 0 ? t.slice(0, MAX_TITLE_CHARS) : fallback;
293
- }
294
- function makeAIEntry(text, impact, justification, relatedDocuments, trendDirection, quantitativeEvidence) {
295
- return { text, impact, justification, relatedDocuments, trendDirection, quantitativeEvidence };
296
- }
297
- function docEntry(d, defaultText, impact, justification, trendDirection, quantEvidence) {
298
- return makeAIEntry(contentOf(d, defaultText), impact, justification, [titleOf(d) || d.dok_id || ''].filter(Boolean), trendDirection, quantEvidence);
299
- }
300
- // ---------------------------------------------------------------------------
301
- // Topic-aware analytical statement builder
302
- // ---------------------------------------------------------------------------
303
- /** Build a topical statement: "X in {topic}" or strip %t placeholder when absent */
304
- function withTopic(template, topic) {
305
- if (!topic)
306
- return template.replaceAll('%t', '');
307
- return template.replaceAll('%t', topic);
308
- }
309
- /** Classify docs into type buckets in a single pass */
310
- function bucketDocs(docs) {
311
- const b = { prop: [], skr: [], sfs: [], pressm: [], bet: [], fpm: [], mot: [], ext: [], other: [] };
312
- for (const d of docs) {
313
- const t = d.doktyp || d.documentType || '';
314
- switch (t) {
315
- case 'prop':
316
- b.prop.push(d);
317
- break;
318
- case 'skr':
319
- b.skr.push(d);
320
- break;
321
- case 'sfs':
322
- b.sfs.push(d);
323
- break;
324
- case 'pressm':
325
- b.pressm.push(d);
326
- break;
327
- case 'bet':
328
- b.bet.push(d);
329
- break;
330
- case 'fpm':
331
- b.fpm.push(d);
332
- break;
333
- case 'mot':
334
- b.mot.push(d);
335
- break;
336
- case 'ext':
337
- b.ext.push(d);
338
- break;
339
- default:
340
- // SFS-by-name heuristic (doktyp missing but dokumentnamn starts with SFS)
341
- if ((d.dokumentnamn || '').startsWith('SFS')) {
342
- b.sfs.push(d);
343
- }
344
- else {
345
- b.other.push(d);
346
- }
347
- break;
348
- }
349
- }
350
- return b;
351
- }
352
- /** Sum of all bucketed documents */
353
- function bucketTotal(b) {
354
- return b.prop.length + b.skr.length + b.sfs.length + b.pressm.length
355
- + b.bet.length + b.fpm.length + b.mot.length + b.ext.length
356
- + b.other.length;
357
- }
358
- // ---------------------------------------------------------------------------
359
- // Per-stakeholder SWOT builders
360
- // ---------------------------------------------------------------------------
361
- function buildGovernmentSwot(b, topic, _lang) {
362
- const { prop: propDocs, skr: skrDocs, sfs: sfsDocs, pressm: pressmDocs, bet: betDocs, fpm: euDocs, mot: motDocs } = b;
363
- const topicStr = topic ? ` on ${topic}` : '';
364
- const total = bucketTotal(b);
365
- const docCount = `${total} parliamentary document${total !== 1 ? 's' : ''} examined`;
366
- const strengths = [];
367
- propDocs.slice(0, 2).forEach(d => {
368
- strengths.push(docEntry(d, withTopic('Government proposition%t', topicStr ? ` ${topic}` : null), 'high', `Government-initiated proposition demonstrates legislative agenda-setting capacity${topicStr}`, 'stable'));
369
- });
370
- sfsDocs.slice(0, 1).forEach(d => {
371
- strengths.push(docEntry(d, withTopic('Enacted law%t', topicStr ? ` ${topic}` : null), 'high', `Enacted statute indicates completed legislative cycle${topicStr}`, 'stable'));
372
- });
373
- pressmDocs.slice(0, 1).forEach(d => {
374
- strengths.push(docEntry(d, withTopic('Government communication%t', topicStr ? ` ${topic}` : null), 'medium', `Press communication signals proactive policy messaging${topicStr}`, 'stable'));
375
- });
376
- skrDocs.slice(0, 1).forEach(d => {
377
- strengths.push(docEntry(d, withTopic('Government written communication (skrivelse)%t', topicStr ? ` ${topic}` : null), 'medium', `Government skrivelse conveys policy position or report to parliament${topicStr}`, 'stable'));
378
- });
379
- if (strengths.length === 0) {
380
- strengths.push(makeAIEntry(withTopic('Policy initiative and agenda-setting%t', topicStr ? ` on ${topic}` : ''), 'medium', `Government holds exclusive right to introduce primary legislation${topicStr}`, [], 'stable'));
381
- }
382
- const weaknesses = [];
383
- betDocs.slice(0, 2).forEach(d => {
384
- weaknesses.push(docEntry(d, 'Implementation scrutiny in committee report', 'medium', `Committee scrutiny reveals implementation challenges${topicStr}`, 'stable'));
385
- });
386
- if (weaknesses.length === 0) {
387
- weaknesses.push(makeAIEntry(withTopic('Implementation timeline and resource prioritisation%t', topicStr ? ` for ${topic}` : ''), 'medium', `Complex legislation requires sustained administrative capacity${topicStr}`, [], 'stable'));
388
- }
389
- const opportunities = [];
390
- euDocs.slice(0, 2).forEach(d => {
391
- opportunities.push(docEntry(d, 'EU framework position paper', 'high', `EU/international alignment can strengthen domestic policy credibility${topicStr}`, 'improving'));
392
- });
393
- if (opportunities.length === 0) {
394
- opportunities.push(makeAIEntry(withTopic('EU and international cooperation%t', topicStr ? ` on ${topic}` : ''), 'high', `Multilateral frameworks provide legitimacy and co-funding for domestic reforms${topicStr}`, [], 'improving'));
395
- }
396
- const threats = [];
397
- motDocs.slice(0, 2).forEach(d => {
398
- threats.push(docEntry(d, 'Opposition motion challenging policy', 'medium', `Opposition motions create parliamentary counter-pressure${topicStr}`, 'stable'));
399
- });
400
- if (threats.length === 0) {
401
- threats.push(makeAIEntry(withTopic('Execution risks and political resistance%t', topicStr ? ` to ${topic} reform` : ''), 'medium', `Policy implementation faces stakeholder friction and opposition challenge${topicStr}`, [], 'stable', docCount));
402
- }
403
- return { strengths, weaknesses, opportunities, threats };
404
- }
405
- function buildOppositionSwot(b, topic, _lang) {
406
- const { bet: betDocs, mot: motDocs, prop: propDocs } = b;
407
- const topicStr = topic ? ` on ${topic}` : '';
408
- const strengths = [];
409
- betDocs.slice(0, 2).forEach(d => {
410
- strengths.push(docEntry(d, 'Committee oversight report', 'high', `Committee report enables structured parliamentary scrutiny${topicStr}`, 'stable'));
411
- });
412
- motDocs.slice(0, 2).forEach(d => {
413
- strengths.push(docEntry(d, 'Opposition motion for alternative policy', 'medium', `Tabling motions demonstrates alternative policy capacity and public positioning${topicStr}`, 'stable'));
414
- });
415
- if (strengths.length === 0) {
416
- strengths.push(makeAIEntry(withTopic('Parliamentary oversight and scrutiny%t', topicStr ? ` of ${topic} proposals` : ''), 'high', `Opposition fulfils democratic accountability function${topicStr}`, [], 'stable'));
417
- }
418
- const weaknesses = [
419
- makeAIEntry(withTopic('Limited access to implementation data%t', topicStr ? ` on ${topic}` : ''), 'medium', `Government controls executive information; opposition relies on public documents${topicStr}`, [], 'stable'),
420
- ];
421
- const opportunities = [
422
- makeAIEntry(withTopic('Cross-party consensus building%t', topicStr ? ` on ${topic}` : ''), 'high', `Issue salience creates openings for coalition with centrist defectors${topicStr}`, [], 'improving'),
423
- ];
424
- const threats = [];
425
- propDocs.slice(0, 1).forEach(d => {
426
- threats.push(docEntry(d, 'Government proposition limiting amendment scope', 'medium', `Government majority can pass legislation with minimal opposition amendments${topicStr}`, 'stable'));
427
- });
428
- if (threats.length === 0) {
429
- threats.push(makeAIEntry(withTopic('Government majority limiting amendment capacity%t', topicStr ? ` on ${topic}` : ''), 'medium', `Parliamentary arithmetic constrains opposition legislative influence${topicStr}`, [], 'stable'));
430
- }
431
- return { strengths, weaknesses, opportunities, threats };
432
- }
433
- function buildEUInternationalSwot(b, topic, _lang) {
434
- const { fpm: euDocs, ext: extDocs } = b;
435
- const topicStr = topic ? ` on ${topic}` : '';
436
- const strengths = [];
437
- euDocs.slice(0, 2).forEach(d => {
438
- strengths.push(docEntry(d, 'EU/international regulatory framework', 'high', `EU regulatory alignment provides Sweden with binding legal framework and co-funding${topicStr}`, 'improving'));
439
- });
440
- if (strengths.length === 0) {
441
- strengths.push(makeAIEntry(withTopic('EU regulatory frameworks and directives%t', topicStr ? ` for ${topic}` : ''), 'high', `EU membership provides supranational standards that shape national policy${topicStr}`, [], 'stable'));
442
- }
443
- extDocs.slice(0, 1).forEach(d => {
444
- strengths.push(docEntry(d, 'External expert input', 'medium', `International expertise validates and contextualises domestic policy positions${topicStr}`, 'stable'));
445
- });
446
- const weaknesses = [
447
- makeAIEntry(withTopic('Implementation variation across EU member states%t', topicStr ? ` regarding ${topic}` : ''), 'medium', `Divergent transposition timelines can create competitive disadvantages for Sweden${topicStr}`, [], 'stable'),
448
- ];
449
- const opportunities = [
450
- makeAIEntry(withTopic('Diplomatic leadership and norm-setting%t', topicStr ? ` on ${topic}` : ''), 'high', `Sweden can use international forums to shape standards and attract investment${topicStr}`, [], 'improving'),
451
- ];
452
- const threats = [
453
- makeAIEntry(withTopic('Geopolitical uncertainty impacting Swedish policy space%t', topicStr ? ` for ${topic}` : ''), 'high', `Shifting international dynamics can constrain or override domestic policy choices${topicStr}`, [], 'deteriorating'),
454
- ];
455
- return { strengths, weaknesses, opportunities, threats };
456
- }
457
- function buildPrivateSectorSwot(b, topic, _lang) {
458
- const { sfs: sfsDocs, ext: extDocs } = b;
459
- const topicStr = topic ? ` in ${topic}` : '';
460
- const strengths = [];
461
- extDocs.slice(0, 2).forEach(d => {
462
- strengths.push(docEntry(d, 'Industry input to policy process', 'high', `Industry representation in consultation demonstrates established influence channels${topicStr}`, 'stable'));
463
- });
464
- if (strengths.length === 0) {
465
- strengths.push(makeAIEntry(withTopic('Domain expertise and operational capacity%t', topicStr ? ` in ${topic}` : ''), 'high', `Private sector holds implementation knowledge critical to policy success${topicStr}`, [], 'stable'));
466
- }
467
- const weaknesses = [
468
- makeAIEntry(withTopic('Compliance costs from new regulatory requirements%t', topicStr ? ` in ${topic}` : ''), 'medium', `Legislative changes impose adaptation costs particularly on SMEs${topicStr}`, [], 'deteriorating'),
469
- ];
470
- sfsDocs.slice(0, 1).forEach(d => {
471
- weaknesses.push(docEntry(d, 'New regulatory requirement', 'medium', `Enacted statute creates compliance obligations for business${topicStr}`, 'stable'));
472
- });
473
- const opportunities = [
474
- makeAIEntry(withTopic('Investment and innovation from policy-driven market development%t', topicStr ? ` in ${topic}` : ''), 'high', `Government programmes create new markets and procurement opportunities${topicStr}`, [], 'improving'),
475
- ];
476
- const threats = [
477
- makeAIEntry(withTopic('Regulatory uncertainty during policy transition%t', topicStr ? ` on ${topic}` : ''), 'high', `Short implementation timelines and evolving rules hamper business planning${topicStr}`, [], 'deteriorating'),
478
- ];
479
- return { strengths, weaknesses, opportunities, threats };
480
- }
481
- function buildCivilSocietySwot(b, topic, _lang) {
482
- const { bet: betDocs, mot: motDocs } = b;
483
- const topicStr = topic ? ` on ${topic}` : '';
484
- const strengths = [];
485
- betDocs.slice(0, 1).forEach(d => {
486
- strengths.push(docEntry(d, 'Civil society input in committee process', 'high', `Committee consultation includes civil society perspectives that shape final legislation${topicStr}`, 'stable'));
487
- });
488
- if (strengths.length === 0) {
489
- strengths.push(makeAIEntry(withTopic('Democratic accountability and rights advocacy%t', topicStr ? ` regarding ${topic}` : ''), 'high', `Civil society provides independent oversight and public interest representation${topicStr}`, [], 'stable'));
490
- }
491
- const weaknesses = [
492
- makeAIEntry(withTopic('Resource constraints limiting monitoring capacity%t', topicStr ? ` for ${topic}` : ''), 'medium', `NGOs often lack funding to mount sustained campaigns on complex legislation${topicStr}`, [], 'stable'),
493
- ];
494
- const opportunities = [
495
- makeAIEntry(withTopic('Public mobilisation on rights-sensitive policies%t', topicStr ? ` related to ${topic}` : ''), 'high', `Heightened media attention creates window for civil society agenda-setting${topicStr}`, [], 'improving'),
496
- ];
497
- const threats = [];
498
- motDocs.slice(0, 1).forEach(d => {
499
- threats.push(docEntry(d, 'Restrictive legislative motion', 'medium', `Proposed legislation may restrict civic space or NGO operational freedoms${topicStr}`, 'deteriorating'));
500
- });
501
- if (threats.length === 0) {
502
- threats.push(makeAIEntry(withTopic('Legislative changes reducing civic freedoms%t', topicStr ? ` in ${topic}` : ''), 'medium', `Policy reforms can inadvertently curtail associational rights or protest space${topicStr}`, [], 'stable'));
503
- }
504
- return { strengths, weaknesses, opportunities, threats };
505
- }
506
- function buildCitizensSwot(b, topic, _lang) {
507
- const { prop: propDocs, sfs: sfsDocs, bet: betDocs } = b;
508
- const topicStr = topic ? ` on ${topic}` : '';
509
- const strengths = [];
510
- sfsDocs.slice(0, 1).forEach(d => {
511
- strengths.push(docEntry(d, 'Enacted welfare or service law', 'high', `Enacted legislation directly improves public service delivery${topicStr}`, 'stable'));
512
- });
513
- if (strengths.length === 0) {
514
- strengths.push(makeAIEntry(withTopic('Democratic representation through elected parliament%t', topicStr ? ` on ${topic}` : ''), 'high', `Citizens exercise electoral accountability over policy direction${topicStr}`, [], 'stable'));
515
- }
516
- const weaknesses = [
517
- makeAIEntry(withTopic('Information asymmetry on policy impacts%t', topicStr ? ` of ${topic}` : ''), 'medium', `Complex legislation is difficult for citizens to evaluate without expert analysis${topicStr}`, [], 'stable'),
518
- ];
519
- const opportunities = [];
520
- propDocs.slice(0, 1).forEach(d => {
521
- opportunities.push(docEntry(d, 'Government reform proposition', 'high', `Government reform proposals create opportunities for improved public services${topicStr}`, 'improving'));
522
- });
523
- betDocs.slice(0, 1).forEach(d => {
524
- opportunities.push(docEntry(d, 'Transparent committee review process', 'medium', `Open committee proceedings allow citizens to track legislative development${topicStr}`, 'stable'));
525
- });
526
- if (opportunities.length === 0) {
527
- opportunities.push(makeAIEntry(withTopic('Policy reforms improving public service quality%t', topicStr ? ` for ${topic}` : ''), 'high', `Parliamentary activity on this issue indicates political will to improve outcomes${topicStr}`, [], 'improving'));
528
- }
529
- const threats = [
530
- makeAIEntry(withTopic('Policy implementation gaps reducing service quality%t', topicStr ? ` in ${topic}` : ''), 'medium', `Distance between legislative intent and administrative execution affects citizen outcomes${topicStr}`, [], 'stable'),
531
- ];
532
- return { strengths, weaknesses, opportunities, threats };
533
- }
534
- // ---------------------------------------------------------------------------
535
- // Cross-reference generator
536
- // ---------------------------------------------------------------------------
537
- function buildCrossReferences(b, topic) {
538
- const refs = [];
539
- // Government strengths → Opposition threats
540
- if (b.prop.length > 0) {
541
- refs.push({
542
- fromStakeholder: 'government-coalition',
543
- fromQuadrant: 'strengths',
544
- toStakeholder: 'opposition',
545
- toQuadrant: 'threats',
546
- rationale: topic
547
- ? `Government propositions on ${topic} that reinforce coalition strengths simultaneously limit opposition influence`
548
- : 'Government legislative agenda that strengthens coalition simultaneously constrains opposition amendment capacity',
549
- });
550
- }
551
- // EU opportunities → Private sector opportunities
552
- if (b.fpm.length > 0) {
553
- refs.push({
554
- fromStakeholder: 'eu-international',
555
- fromQuadrant: 'opportunities',
556
- toStakeholder: 'private-sector',
557
- toQuadrant: 'opportunities',
558
- rationale: topic
559
- ? `EU directives on ${topic} open cross-border market opportunities for Swedish business`
560
- : 'EU regulatory harmonisation creates new market access opportunities for Swedish companies',
561
- });
562
- }
563
- // Private sector regulatory burden → Civil society accountability (only when evidence present)
564
- const hasRegulatoryDocs = b.sfs.length > 0 || b.skr.length > 0;
565
- if (hasRegulatoryDocs) {
566
- refs.push({
567
- fromStakeholder: 'private-sector',
568
- fromQuadrant: 'weaknesses',
569
- toStakeholder: 'civil-society',
570
- toQuadrant: 'opportunities',
571
- rationale: topic
572
- ? `Business compliance challenges on ${topic} create space for civil society to advocate proportionate regulation`
573
- : 'Regulatory compliance burdens provide civil society with advocacy leverage for balanced policy design',
574
- });
575
- }
576
- return refs;
577
- }
578
- // ---------------------------------------------------------------------------
579
- // Confidence score calculation
580
- // ---------------------------------------------------------------------------
581
- /** Baseline confidence with minimal document evidence */
582
- const BASE_CONFIDENCE = 0.55;
583
- /** Maximum bonus for document volume (capped at 10 extra documents) */
584
- const MAX_DOC_VOLUME_BONUS = 0.20;
585
- /** Confidence points added per document (capped by MAX_DOC_VOLUME_BONUS) */
586
- const CONFIDENCE_PER_DOC = 0.02;
587
- /** Maximum bonus for enriched full-text documents */
588
- const MAX_ENRICHMENT_BONUS = 0.15;
589
- /** Confidence points per enriched document (capped by MAX_ENRICHMENT_BONUS) */
590
- const CONFIDENCE_PER_ENRICHED_DOC = 0.03;
591
- /** Penalty when EU stakeholder has no fpm documents (low direct evidence) */
592
- const EU_NO_DATA_PENALTY = -0.05;
593
- /** Absolute maximum confidence (no analysis is 100% certain) */
594
- const MAX_CONFIDENCE = 0.95;
595
- /** Absolute minimum confidence (always some baseline reasoning possible) */
596
- const MIN_CONFIDENCE = 0.40;
597
- function computeConfidence(docs, b, perspective) {
598
- const docBonus = Math.min(MAX_DOC_VOLUME_BONUS, docs.length * CONFIDENCE_PER_DOC);
599
- const enriched = docs.filter(d => d.contentFetched).length;
600
- const enrichedBonus = Math.min(MAX_ENRICHMENT_BONUS, enriched * CONFIDENCE_PER_ENRICHED_DOC);
601
- // EU stakeholder gets slightly lower confidence when there are no fpm docs
602
- const euPenalty = perspective === 'eu-international' && b.fpm.length === 0 ? EU_NO_DATA_PENALTY : 0;
603
- return Math.min(MAX_CONFIDENCE, Math.max(MIN_CONFIDENCE, BASE_CONFIDENCE + docBonus + enrichedBonus + euPenalty));
604
- }
605
- // ---------------------------------------------------------------------------
606
- // Public API
607
- // ---------------------------------------------------------------------------
608
- /**
609
- * Build AI-driven SWOT analyses for all 6 stakeholder perspectives.
610
- *
611
- * Returns `StakeholderSwot[]` ready for `generateStakeholderSwotSection()`.
612
- * Each entry contains `AISwotEntry`-shaped data (which is compatible with the
613
- * base `SwotEntry` shape used by the renderer).
614
- *
615
- * @param docs - Parliamentary documents relevant to the analysis topic
616
- * @param topic - Focus topic for contextual framing (may be null)
617
- * @param lang - Target language for stakeholder names and roles
618
- */
619
- export function buildAISwotStakeholders(docs, topic, lang) {
620
- const perspectives = [
621
- 'government-coalition',
622
- 'opposition',
623
- 'eu-international',
624
- 'private-sector',
625
- 'civil-society',
626
- 'citizens-voters',
627
- ];
628
- // Pre-bucket documents by type in a single pass for efficiency
629
- const b = bucketDocs(docs);
630
- const builders = {
631
- 'government-coalition': () => buildGovernmentSwot(b, topic, lang),
632
- opposition: () => buildOppositionSwot(b, topic, lang),
633
- 'eu-international': () => buildEUInternationalSwot(b, topic, lang),
634
- 'private-sector': () => buildPrivateSectorSwot(b, topic, lang),
635
- 'civil-society': () => buildCivilSocietySwot(b, topic, lang),
636
- 'citizens-voters': () => buildCitizensSwot(b, topic, lang),
637
- };
638
- const crossRefs = buildCrossReferences(b, topic);
639
- // Localised labels for context metadata (with English fallback for safety)
640
- const confidenceLabel = CONTEXT_LABELS.confidence[lang] ?? CONTEXT_LABELS.confidence.en;
641
- const crossRefLabel = CONTEXT_LABELS.crossReferences[lang] ?? CONTEXT_LABELS.crossReferences.en;
642
- return perspectives.map(p => {
643
- const name = STAKEHOLDER_NAMES[p][lang];
644
- const role = STAKEHOLDER_ROLES[p][lang];
645
- const swotData = builders[p]();
646
- const confidence = computeConfidence(docs, b, p);
647
- // Attach confidence and crossReferences as context metadata (localised)
648
- const contextParts = [];
649
- contextParts.push(`${confidenceLabel}: ${Math.round(confidence * 100)}%`);
650
- const ownRefs = crossRefs.filter(r => r.fromStakeholder === p || r.toStakeholder === p);
651
- if (ownRefs.length > 0) {
652
- contextParts.push(`${crossRefLabel}: ${ownRefs.length}`);
653
- }
654
- return {
655
- name,
656
- role,
657
- swot: {
658
- strengths: swotData.strengths,
659
- weaknesses: swotData.weaknesses,
660
- opportunities: swotData.opportunities,
661
- threats: swotData.threats,
662
- context: contextParts.join(' | '),
663
- },
664
- };
665
- });
666
- }
667
- //# sourceMappingURL=ai-swot-analyzer.js.map