@tricoteuses/senat 2.22.13 → 2.22.14
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/lib/src/loaders.d.ts +2 -8
- package/lib/src/loaders.js +7 -25
- package/lib/tests/test_iter_load.test.js +17 -0
- package/package.json +2 -2
- package/lib/config.d.ts +0 -21
- package/lib/config.js +0 -27
- package/lib/databases.d.ts +0 -2
- package/lib/databases.js +0 -26
- package/lib/datasets.d.ts +0 -34
- package/lib/datasets.js +0 -233
- package/lib/git.d.ts +0 -26
- package/lib/git.js +0 -167
- package/lib/index.d.ts +0 -13
- package/lib/index.js +0 -1
- package/lib/loaders.d.ts +0 -58
- package/lib/loaders.js +0 -286
- package/lib/model/agenda.d.ts +0 -6
- package/lib/model/agenda.js +0 -148
- package/lib/model/ameli.d.ts +0 -51
- package/lib/model/ameli.js +0 -147
- package/lib/model/commission.d.ts +0 -18
- package/lib/model/commission.js +0 -269
- package/lib/model/debats.d.ts +0 -67
- package/lib/model/debats.js +0 -95
- package/lib/model/documents.d.ts +0 -12
- package/lib/model/documents.js +0 -138
- package/lib/model/dosleg.d.ts +0 -7
- package/lib/model/dosleg.js +0 -326
- package/lib/model/index.d.ts +0 -7
- package/lib/model/index.js +0 -7
- package/lib/model/questions.d.ts +0 -45
- package/lib/model/questions.js +0 -89
- package/lib/model/scrutins.d.ts +0 -13
- package/lib/model/scrutins.js +0 -114
- package/lib/model/seance.d.ts +0 -3
- package/lib/model/seance.js +0 -267
- package/lib/model/sens.d.ts +0 -146
- package/lib/model/sens.js +0 -454
- package/lib/model/texte.d.ts +0 -7
- package/lib/model/texte.js +0 -228
- package/lib/model/util.d.ts +0 -9
- package/lib/model/util.js +0 -38
- package/lib/parsers/texte.d.ts +0 -7
- package/lib/parsers/texte.js +0 -228
- package/lib/raw_types/ameli.d.ts +0 -914
- package/lib/raw_types/ameli.js +0 -5
- package/lib/raw_types/debats.d.ts +0 -207
- package/lib/raw_types/debats.js +0 -5
- package/lib/raw_types/dosleg.d.ts +0 -1619
- package/lib/raw_types/dosleg.js +0 -5
- package/lib/raw_types/questions.d.ts +0 -423
- package/lib/raw_types/questions.js +0 -5
- package/lib/raw_types/senat.d.ts +0 -11372
- package/lib/raw_types/senat.js +0 -5
- package/lib/raw_types/sens.d.ts +0 -8248
- package/lib/raw_types/sens.js +0 -5
- package/lib/raw_types_schemats/ameli.d.ts +0 -539
- package/lib/raw_types_schemats/ameli.js +0 -2
- package/lib/raw_types_schemats/debats.d.ts +0 -127
- package/lib/raw_types_schemats/debats.js +0 -2
- package/lib/raw_types_schemats/dosleg.d.ts +0 -977
- package/lib/raw_types_schemats/dosleg.js +0 -2
- package/lib/raw_types_schemats/questions.d.ts +0 -237
- package/lib/raw_types_schemats/questions.js +0 -2
- package/lib/raw_types_schemats/sens.d.ts +0 -6915
- package/lib/raw_types_schemats/sens.js +0 -2
- package/lib/scripts/convert_data.js +0 -354
- package/lib/scripts/data-download.d.ts +0 -1
- package/lib/scripts/data-download.js +0 -12
- package/lib/scripts/datautil.d.ts +0 -8
- package/lib/scripts/datautil.js +0 -34
- package/lib/scripts/parse_textes.d.ts +0 -1
- package/lib/scripts/parse_textes.js +0 -44
- package/lib/scripts/retrieve_agenda.d.ts +0 -1
- package/lib/scripts/retrieve_agenda.js +0 -132
- package/lib/scripts/retrieve_cr_commission.d.ts +0 -1
- package/lib/scripts/retrieve_cr_commission.js +0 -364
- package/lib/scripts/retrieve_cr_seance.d.ts +0 -6
- package/lib/scripts/retrieve_cr_seance.js +0 -347
- package/lib/scripts/retrieve_documents.d.ts +0 -3
- package/lib/scripts/retrieve_documents.js +0 -219
- package/lib/scripts/retrieve_open_data.d.ts +0 -1
- package/lib/scripts/retrieve_open_data.js +0 -316
- package/lib/scripts/retrieve_senateurs_photos.d.ts +0 -1
- package/lib/scripts/retrieve_senateurs_photos.js +0 -147
- package/lib/scripts/retrieve_videos.d.ts +0 -1
- package/lib/scripts/retrieve_videos.js +0 -461
- package/lib/scripts/shared/cli_helpers.d.ts +0 -95
- package/lib/scripts/shared/cli_helpers.js +0 -91
- package/lib/scripts/shared/util.d.ts +0 -4
- package/lib/scripts/shared/util.js +0 -35
- package/lib/scripts/test_iter_load.d.ts +0 -1
- package/lib/scripts/test_iter_load.js +0 -12
- package/lib/src/utils/nvs-timecode.d.ts +0 -17
- package/lib/src/utils/nvs-timecode.js +0 -79
- package/lib/src/utils/weights_scoring_config.d.ts +0 -2
- package/lib/src/utils/weights_scoring_config.js +0 -15
- package/lib/strings.d.ts +0 -1
- package/lib/strings.js +0 -18
- package/lib/types/agenda.d.ts +0 -44
- package/lib/types/agenda.js +0 -1
- package/lib/types/ameli.d.ts +0 -5
- package/lib/types/ameli.js +0 -1
- package/lib/types/compte_rendu.d.ts +0 -83
- package/lib/types/compte_rendu.js +0 -1
- package/lib/types/debats.d.ts +0 -2
- package/lib/types/debats.js +0 -1
- package/lib/types/dosleg.d.ts +0 -70
- package/lib/types/dosleg.js +0 -1
- package/lib/types/questions.d.ts +0 -2
- package/lib/types/questions.js +0 -1
- package/lib/types/sens.d.ts +0 -10
- package/lib/types/sens.js +0 -1
- package/lib/types/sessions.d.ts +0 -5
- package/lib/types/sessions.js +0 -84
- package/lib/types/texte.d.ts +0 -74
- package/lib/types/texte.js +0 -16
- package/lib/utils/cr_spliting.d.ts +0 -28
- package/lib/utils/cr_spliting.js +0 -265
- package/lib/utils/date.d.ts +0 -10
- package/lib/utils/date.js +0 -100
- package/lib/utils/nvs-timecode.d.ts +0 -7
- package/lib/utils/nvs-timecode.js +0 -79
- package/lib/utils/reunion_grouping.d.ts +0 -11
- package/lib/utils/reunion_grouping.js +0 -337
- package/lib/utils/reunion_odj_building.d.ts +0 -5
- package/lib/utils/reunion_odj_building.js +0 -154
- package/lib/utils/reunion_parsing.d.ts +0 -23
- package/lib/utils/reunion_parsing.js +0 -209
- package/lib/utils/scoring.d.ts +0 -14
- package/lib/utils/scoring.js +0 -147
- package/lib/utils/string_cleaning.d.ts +0 -7
- package/lib/utils/string_cleaning.js +0 -57
- package/lib/validators/config.d.ts +0 -9
- package/lib/validators/config.js +0 -10
- /package/lib/{scripts/convert_data.d.ts → tests/test_iter_load.test.d.ts} +0 -0
package/lib/utils/cr_spliting.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import * as cheerio from "cheerio";
|
|
3
|
-
import { AGENDA_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders";
|
|
4
|
-
import fs from "fs-extra";
|
|
5
|
-
import { sessionStartYearFromDate } from "../model/seance";
|
|
6
|
-
import { frDateToISO, hourShortToStartTime } from "./date";
|
|
7
|
-
import { normalizeSpaces } from "./string_cleaning";
|
|
8
|
-
// Convert "quinze heures trente", "15 heures 30", "dix-sept heures moins le quart", etc. en "HHMM"
|
|
9
|
-
function parseFrenchClockToHHMM(input) {
|
|
10
|
-
const s = (input || "")
|
|
11
|
-
.toLowerCase()
|
|
12
|
-
.normalize("NFKD")
|
|
13
|
-
.replace(/[\u0300-\u036f]/g, "")
|
|
14
|
-
.trim();
|
|
15
|
-
if (!s)
|
|
16
|
-
return undefined;
|
|
17
|
-
const digitMatch = s.match(/(\d{1,2})\s*heures?(?:\s*(\d{1,2}))?/);
|
|
18
|
-
if (digitMatch) {
|
|
19
|
-
const h = Math.min(24, Math.max(0, parseInt(digitMatch[1], 10)));
|
|
20
|
-
const m = digitMatch[2] ? Math.min(59, Math.max(0, parseInt(digitMatch[2], 10))) : 0;
|
|
21
|
-
return `${String(h).padStart(2, "0")}${String(m).padStart(2, "0")}`;
|
|
22
|
-
}
|
|
23
|
-
const NUM = new Map([
|
|
24
|
-
["zero", 0],
|
|
25
|
-
["une", 1],
|
|
26
|
-
["un", 1],
|
|
27
|
-
["deux", 2],
|
|
28
|
-
["trois", 3],
|
|
29
|
-
["quatre", 4],
|
|
30
|
-
["cinq", 5],
|
|
31
|
-
["six", 6],
|
|
32
|
-
["sept", 7],
|
|
33
|
-
["huit", 8],
|
|
34
|
-
["neuf", 9],
|
|
35
|
-
["dix", 10],
|
|
36
|
-
["onze", 11],
|
|
37
|
-
["douze", 12],
|
|
38
|
-
["treize", 13],
|
|
39
|
-
["quatorze", 14],
|
|
40
|
-
["quinze", 15],
|
|
41
|
-
["seize", 16],
|
|
42
|
-
["dix-sept", 17],
|
|
43
|
-
["dix sept", 17],
|
|
44
|
-
["dix-huit", 18],
|
|
45
|
-
["dix huit", 18],
|
|
46
|
-
["dix-neuf", 19],
|
|
47
|
-
["dix neuf", 19],
|
|
48
|
-
["vingt", 20],
|
|
49
|
-
["vingt et une", 21],
|
|
50
|
-
["vingt-et-une", 21],
|
|
51
|
-
["vingt et un", 21],
|
|
52
|
-
["vingt-et-un", 21],
|
|
53
|
-
["vingt-deux", 22],
|
|
54
|
-
["vingt deux", 22],
|
|
55
|
-
["vingt-trois", 23],
|
|
56
|
-
["vingt trois", 23],
|
|
57
|
-
["vingt-quatre", 24],
|
|
58
|
-
["vingt quatre", 24],
|
|
59
|
-
]);
|
|
60
|
-
const hourWordMatch = s.match(/([a-z\- ]+?)\s*heures?/);
|
|
61
|
-
if (!hourWordMatch)
|
|
62
|
-
return undefined;
|
|
63
|
-
const hourWord = hourWordMatch[1].trim();
|
|
64
|
-
let hour = NUM.get(hourWord);
|
|
65
|
-
if (hour == null) {
|
|
66
|
-
const cleaned = hourWord.replace(/\s+/g, " ");
|
|
67
|
-
hour = NUM.get(cleaned);
|
|
68
|
-
}
|
|
69
|
-
if (hour == null)
|
|
70
|
-
return undefined;
|
|
71
|
-
let minutes = 0;
|
|
72
|
-
if (/\bet (demie|demi)\b/.test(s))
|
|
73
|
-
minutes = 30;
|
|
74
|
-
else if (/\bet quart\b/.test(s))
|
|
75
|
-
minutes = 15;
|
|
76
|
-
else if (/\bmoins le quart\b/.test(s)) {
|
|
77
|
-
hour = (hour + 23) % 24;
|
|
78
|
-
minutes = 45;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
const MIN = new Map([
|
|
82
|
-
["cinq", 5],
|
|
83
|
-
["dix", 10],
|
|
84
|
-
["quinze", 15],
|
|
85
|
-
["vingt", 20],
|
|
86
|
-
["vingt-cinq", 25],
|
|
87
|
-
["vingt cinq", 25],
|
|
88
|
-
["trente", 30],
|
|
89
|
-
["trente-cinq", 35],
|
|
90
|
-
["trente cinq", 35],
|
|
91
|
-
["quarante", 40],
|
|
92
|
-
["quarante-cinq", 45],
|
|
93
|
-
["quarante cinq", 45],
|
|
94
|
-
["cinquante", 50],
|
|
95
|
-
["cinquante-cinq", 55],
|
|
96
|
-
["cinquante cinq", 55],
|
|
97
|
-
]);
|
|
98
|
-
const minWordMatch = s.match(/heures?\s+([a-z\- ]+?)(?:[).,;]|$)/);
|
|
99
|
-
if (minWordMatch) {
|
|
100
|
-
const mw = minWordMatch[1].trim();
|
|
101
|
-
const m1 = MIN.get(mw);
|
|
102
|
-
if (m1 != null)
|
|
103
|
-
minutes = m1;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return `${String(hour).padStart(2, "0")}${String(minutes).padStart(2, "0")}`;
|
|
107
|
-
}
|
|
108
|
-
function extractWeekStartFromHead($) {
|
|
109
|
-
const og = $('meta[property="og:title"]').attr("content") || $("title").text();
|
|
110
|
-
const m = (og ?? "").toLowerCase().match(/semaine du\s+(\d{1,2}\s+\w+\s+\d{4})/i);
|
|
111
|
-
if (m)
|
|
112
|
-
return frDateToISO(m[1]);
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
function detectOrganeFromTitle(s) {
|
|
116
|
-
const t = (s ?? "").trim();
|
|
117
|
-
if (!t)
|
|
118
|
-
return { organeTitleRaw: undefined, organeDetected: undefined };
|
|
119
|
-
const lower = t.toLowerCase();
|
|
120
|
-
const m = lower.match(/commission(?:\s+des|\s+de|)\s+([^:]+)$/i);
|
|
121
|
-
let organeDetected;
|
|
122
|
-
if (m && m[1]) {
|
|
123
|
-
organeDetected = ("Commission " + m[1])
|
|
124
|
-
.replace(/\s+/g, " ")
|
|
125
|
-
.replace(/\s+:? comptes? rendus?$/i, "")
|
|
126
|
-
.trim();
|
|
127
|
-
organeDetected = organeDetected[0].toUpperCase() + organeDetected.slice(1);
|
|
128
|
-
}
|
|
129
|
-
return { organeTitleRaw: t, organeDetected };
|
|
130
|
-
}
|
|
131
|
-
function extractDaysAndOpenings($) {
|
|
132
|
-
const days = [];
|
|
133
|
-
const h2s = $("h2").toArray();
|
|
134
|
-
for (let i = 0; i < h2s.length; i++) {
|
|
135
|
-
const h = h2s[i];
|
|
136
|
-
const txt = normalizeSpaces($(h).text());
|
|
137
|
-
const m = txt.match(/^(?:Lundi|Mardi|Mercredi|Jeudi|Vendredi|Samedi|Dimanche)\s+(.+?)$/i);
|
|
138
|
-
if (!m)
|
|
139
|
-
continue;
|
|
140
|
-
const iso = frDateToISO(m[1]);
|
|
141
|
-
if (!iso)
|
|
142
|
-
continue;
|
|
143
|
-
let openTime;
|
|
144
|
-
let cur = $(h).next();
|
|
145
|
-
while (cur.length && cur[0].tagName !== "h2") {
|
|
146
|
-
const t = normalizeSpaces(cur.text());
|
|
147
|
-
const mt = t.match(/La réunion est ouverte à\s+(\d{1,2})\s*h(?:\s*(\d{2}))?/i);
|
|
148
|
-
if (mt) {
|
|
149
|
-
openTime = `${mt[1].padStart(2, "0")}:${(mt[2] ?? "00").padStart(2, "0")}`;
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
cur = cur.next();
|
|
153
|
-
}
|
|
154
|
-
days.push({ date: iso, openTime, h2Index: i });
|
|
155
|
-
}
|
|
156
|
-
return days;
|
|
157
|
-
}
|
|
158
|
-
function extractOrganeCode($) {
|
|
159
|
-
const names = $("a[name]")
|
|
160
|
-
.toArray()
|
|
161
|
-
.map((a) => ($(a).attr("name") || "").trim());
|
|
162
|
-
return names.find((n) => /^[A-Z]{3,6}$/.test(n));
|
|
163
|
-
}
|
|
164
|
-
export function parseCommissionMetadataFromHtml(html, sourceFileName) {
|
|
165
|
-
const $ = cheerio.load(html);
|
|
166
|
-
const h1 = $("h1.page-title").first().text().trim() || undefined;
|
|
167
|
-
const headTitle = $('meta[property="og:title"]').attr("content") || $("title").text() || undefined;
|
|
168
|
-
const { organeTitleRaw, organeDetected } = detectOrganeFromTitle(h1 || headTitle);
|
|
169
|
-
let weekStart = extractWeekStartFromHead($);
|
|
170
|
-
const days = extractDaysAndOpenings($);
|
|
171
|
-
if (!weekStart && days.length > 0)
|
|
172
|
-
weekStart = days[0].date;
|
|
173
|
-
const organeCode = extractOrganeCode($);
|
|
174
|
-
return {
|
|
175
|
-
sourceFile: sourceFileName ?? null,
|
|
176
|
-
organeTitleRaw: organeTitleRaw ?? null,
|
|
177
|
-
organeDetected: organeDetected ?? null,
|
|
178
|
-
organeCode: organeCode ?? null,
|
|
179
|
-
weekStart: weekStart ?? null,
|
|
180
|
-
days, // [{date, openTime?, h2Index}]
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
function isGroupedReunion(o) {
|
|
184
|
-
return o && typeof o === "object" && typeof o.uid === "string" && typeof o.date === "string";
|
|
185
|
-
}
|
|
186
|
-
export async function loadAgendaForDate(dataDir, yyyymmdd, session) {
|
|
187
|
-
const baseDir = path.join(dataDir, AGENDA_FOLDER, DATA_TRANSFORMED_FOLDER, String(session));
|
|
188
|
-
if (!(await fs.pathExists(baseDir)))
|
|
189
|
-
return [];
|
|
190
|
-
const files = (await fs.readdir(baseDir)).filter((f) => f.startsWith(`RUSN${yyyymmdd}IDC`) && f.toLowerCase().endsWith(".json"));
|
|
191
|
-
const out = [];
|
|
192
|
-
for (const f of files) {
|
|
193
|
-
const p = path.join(baseDir, f);
|
|
194
|
-
try {
|
|
195
|
-
const raw = await fs.readFile(p, "utf8");
|
|
196
|
-
const obj = JSON.parse(raw);
|
|
197
|
-
if (!isGroupedReunion(obj)) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (!obj.uid.startsWith(`RUSN${yyyymmdd}IDC`)) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
out.push(obj);
|
|
204
|
-
}
|
|
205
|
-
catch {
|
|
206
|
-
// ignore
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return out;
|
|
210
|
-
}
|
|
211
|
-
export async function linkCRtoCommissionGroup(opts) {
|
|
212
|
-
const { dataDir, dateISO, organeDetected, hourShort, crUid, titreGuess, groupUid } = opts;
|
|
213
|
-
const computedUid = crUid.replace(/^CRC/, "RU");
|
|
214
|
-
const uid = groupUid ?? computedUid;
|
|
215
|
-
const session = sessionStartYearFromDate(new Date(dateISO));
|
|
216
|
-
const groupedDir = path.join(dataDir, AGENDA_FOLDER, DATA_TRANSFORMED_FOLDER, String(session));
|
|
217
|
-
await fs.ensureDir(groupedDir);
|
|
218
|
-
const filePath = path.join(groupedDir, `${uid}.json`);
|
|
219
|
-
let group = null;
|
|
220
|
-
let created = false;
|
|
221
|
-
let updated = false;
|
|
222
|
-
try {
|
|
223
|
-
if (await fs.pathExists(filePath)) {
|
|
224
|
-
group = await fs.readJSON(filePath);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
catch (e) {
|
|
228
|
-
console.warn(`[AGENDA][COM] Unreadable JSON → ${filePath} (${e?.message}) → will recreate`);
|
|
229
|
-
}
|
|
230
|
-
if (!group) {
|
|
231
|
-
group = {
|
|
232
|
-
uid,
|
|
233
|
-
chambre: "SN",
|
|
234
|
-
date: dateISO,
|
|
235
|
-
type: "Commission",
|
|
236
|
-
startTime: hourShortToStartTime(hourShort),
|
|
237
|
-
endTime: null,
|
|
238
|
-
captationVideo: false,
|
|
239
|
-
titre: titreGuess ?? "",
|
|
240
|
-
organe: organeDetected ?? "Commission",
|
|
241
|
-
objet: titreGuess ?? "",
|
|
242
|
-
events: [],
|
|
243
|
-
compteRenduRefUid: crUid,
|
|
244
|
-
};
|
|
245
|
-
created = true;
|
|
246
|
-
console.log(`[AGENDA][COM] Created new group uid=${uid} for CR uid=${crUid}`);
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
group.compteRenduRefUid = crUid;
|
|
250
|
-
updated = true;
|
|
251
|
-
console.log(`[AGENDA][COM] Updated group uid=${uid} for CR uid=${crUid}`);
|
|
252
|
-
}
|
|
253
|
-
// Lien CR
|
|
254
|
-
// Enrichir depuis CR si vide
|
|
255
|
-
// const sommaire = cr?.metadonnees?.sommaire as Sommaire | undefined;
|
|
256
|
-
// if (sommaire) {
|
|
257
|
-
// const { titre: dTitre, objet: dObjet } = deriveTitreObjetFromSommaire(sommaire, undefined);
|
|
258
|
-
// if (!group.titre && dTitre) group.titre = dTitre;
|
|
259
|
-
// if ((!group.objet || !group.objet.trim()) && dObjet) group.objet = dObjet;
|
|
260
|
-
// } else if (!group.titre && titreGuess) {
|
|
261
|
-
// group.titre = titreGuess;
|
|
262
|
-
// }
|
|
263
|
-
await fs.writeJSON(filePath, group, { spaces: 2 });
|
|
264
|
-
return { uid, filePath, created, updated };
|
|
265
|
-
}
|
package/lib/utils/date.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare function yyyymmddFromPath(xmlFilePath: string): string;
|
|
2
|
-
export declare function parseYYYYMMDD(yyyymmdd: string): Date | null;
|
|
3
|
-
export declare function frDateToISO(s?: string): string | undefined;
|
|
4
|
-
export declare function hourShortToStartTime(hourShort: string | null): string | null;
|
|
5
|
-
export declare function epochToParisDateTime(epochSec: number): {
|
|
6
|
-
date: string;
|
|
7
|
-
startTime: string;
|
|
8
|
-
} | null;
|
|
9
|
-
export declare function toTargetEpoch(time: string | null, date?: string | null): number | null;
|
|
10
|
-
export declare function toFRDate(dateYYYYMMDD: string): string;
|
package/lib/utils/date.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { DateTime } from "luxon";
|
|
2
|
-
export function yyyymmddFromPath(xmlFilePath) {
|
|
3
|
-
return xmlFilePath.replace(/^.*?(\d{8}).*$/i, "$1");
|
|
4
|
-
}
|
|
5
|
-
export function parseYYYYMMDD(yyyymmdd) {
|
|
6
|
-
if (!/^\d{8}$/.test(yyyymmdd))
|
|
7
|
-
return null;
|
|
8
|
-
const y = Number(yyyymmdd.slice(0, 4));
|
|
9
|
-
const m = Number(yyyymmdd.slice(4, 6)) - 1;
|
|
10
|
-
const d = Number(yyyymmdd.slice(6, 8));
|
|
11
|
-
const dt = new Date(y, m, d);
|
|
12
|
-
return Number.isFinite(dt.getTime()) ? dt : null;
|
|
13
|
-
}
|
|
14
|
-
export function frDateToISO(s) {
|
|
15
|
-
if (!s)
|
|
16
|
-
return;
|
|
17
|
-
const months = {
|
|
18
|
-
janvier: 1,
|
|
19
|
-
février: 2,
|
|
20
|
-
fevrier: 2,
|
|
21
|
-
mars: 3,
|
|
22
|
-
avril: 4,
|
|
23
|
-
mai: 5,
|
|
24
|
-
juin: 6,
|
|
25
|
-
juillet: 7,
|
|
26
|
-
août: 8,
|
|
27
|
-
aout: 8,
|
|
28
|
-
septembre: 9,
|
|
29
|
-
octobre: 10,
|
|
30
|
-
novembre: 11,
|
|
31
|
-
décembre: 12,
|
|
32
|
-
decembre: 12,
|
|
33
|
-
};
|
|
34
|
-
const cleaned = s
|
|
35
|
-
.trim()
|
|
36
|
-
.replace(/\u00A0/g, " ")
|
|
37
|
-
.replace(/ +/g, " ");
|
|
38
|
-
const m = cleaned.match(/^(\d{1,2})(?:er)?\s+([a-zéèêîïôûùç]+)\s+(\d{4})$/i);
|
|
39
|
-
if (!m)
|
|
40
|
-
return;
|
|
41
|
-
const d = String(parseInt(m[1], 10)).padStart(2, "0");
|
|
42
|
-
const mon = months[m[2].toLowerCase()];
|
|
43
|
-
if (!mon)
|
|
44
|
-
return;
|
|
45
|
-
const y = m[3];
|
|
46
|
-
return `${y}-${String(mon).padStart(2, "0")}-${d}`;
|
|
47
|
-
}
|
|
48
|
-
export function hourShortToStartTime(hourShort) {
|
|
49
|
-
if (!hourShort || hourShort === "NA")
|
|
50
|
-
return null;
|
|
51
|
-
if (!/^\d{4}$/.test(hourShort))
|
|
52
|
-
return null;
|
|
53
|
-
const hh = hourShort.slice(0, 2);
|
|
54
|
-
const mm = hourShort.slice(2, 4);
|
|
55
|
-
return `${hh}:${mm}`;
|
|
56
|
-
}
|
|
57
|
-
export function epochToParisDateTime(epochSec) {
|
|
58
|
-
if (!Number.isFinite(epochSec))
|
|
59
|
-
return null;
|
|
60
|
-
const dUtc = new Date(epochSec * 1000);
|
|
61
|
-
// Offset heuristic (same logique que parisOffsetForDate)
|
|
62
|
-
const m = dUtc.getUTCMonth() + 1; // 1..12
|
|
63
|
-
const offsetHours = m >= 4 && m <= 10 ? 2 : 1;
|
|
64
|
-
const offsetStr = offsetHours === 2 ? "+02:00" : "+01:00";
|
|
65
|
-
// Applique l'offset pour obtenir la date/heure locales Paris
|
|
66
|
-
const localMs = dUtc.getTime() + offsetHours * 3600 * 1000;
|
|
67
|
-
const dl = new Date(localMs);
|
|
68
|
-
const yyyy = String(dl.getUTCFullYear());
|
|
69
|
-
const mm = String(dl.getUTCMonth() + 1).padStart(2, "0");
|
|
70
|
-
const dd = String(dl.getUTCDate()).padStart(2, "0");
|
|
71
|
-
const hh = String(dl.getUTCHours()).padStart(2, "0");
|
|
72
|
-
const mi = String(dl.getUTCMinutes()).padStart(2, "0");
|
|
73
|
-
const ss = String(dl.getUTCSeconds()).padStart(2, "0");
|
|
74
|
-
const ms = String(dl.getUTCMilliseconds()).padStart(3, "0");
|
|
75
|
-
return {
|
|
76
|
-
date: `${yyyy}-${mm}-${dd}`,
|
|
77
|
-
startTime: `${hh}:${mi}:${ss}.${ms}${offsetStr}`,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
export function toTargetEpoch(time, date) {
|
|
81
|
-
if (!time)
|
|
82
|
-
return null;
|
|
83
|
-
let dtLocal;
|
|
84
|
-
if (time.includes("T")) {
|
|
85
|
-
dtLocal = DateTime.fromISO(time, { zone: "Europe/Paris" });
|
|
86
|
-
}
|
|
87
|
-
else if (date) {
|
|
88
|
-
dtLocal = DateTime.fromISO(`${date}T${time}`, { zone: "Europe/Paris" });
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
if (!dtLocal.isValid)
|
|
94
|
-
return null;
|
|
95
|
-
return Math.floor(dtLocal.toUTC().toSeconds());
|
|
96
|
-
}
|
|
97
|
-
export function toFRDate(dateYYYYMMDD) {
|
|
98
|
-
const [y, m, d] = dateYYYYMMDD.split("-");
|
|
99
|
-
return `${d}/${m}/${y}`; // DD/MM/YYYY
|
|
100
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { XMLParser } from "fast-xml-parser";
|
|
2
|
-
import { dice, normalize } from "./scoring";
|
|
3
|
-
import { decodeHtmlEntities } from "./string_cleaning";
|
|
4
|
-
const CHAPTER_MATCH_THRESHOLD = 0.5;
|
|
5
|
-
const xmlParser = new XMLParser({
|
|
6
|
-
ignoreAttributes: false,
|
|
7
|
-
attributeNamePrefix: "@_",
|
|
8
|
-
});
|
|
9
|
-
function getTimecodeForChapterId(finalPlayerNvs, chapterId) {
|
|
10
|
-
const xml = xmlParser.parse(finalPlayerNvs);
|
|
11
|
-
const synchros = xml?.player?.synchro;
|
|
12
|
-
if (!synchros)
|
|
13
|
-
return null;
|
|
14
|
-
const synchsArray = Array.isArray(synchros) ? synchros : [synchros];
|
|
15
|
-
const match = synchsArray.find((s) => String(s["@_id"]) === String(chapterId));
|
|
16
|
-
if (!match)
|
|
17
|
-
return null;
|
|
18
|
-
const rawTimecode = match["@_timecode"];
|
|
19
|
-
if (rawTimecode == null)
|
|
20
|
-
return null;
|
|
21
|
-
const ms = Number(rawTimecode);
|
|
22
|
-
if (Number.isNaN(ms))
|
|
23
|
-
return null;
|
|
24
|
-
return Math.floor(ms / 1000);
|
|
25
|
-
}
|
|
26
|
-
function toArray(v) {
|
|
27
|
-
if (!v)
|
|
28
|
-
return [];
|
|
29
|
-
return Array.isArray(v) ? v : [v];
|
|
30
|
-
}
|
|
31
|
-
function getLevel1Chapters(dataNvs) {
|
|
32
|
-
const xml = xmlParser.parse(dataNvs);
|
|
33
|
-
const root = xml?.data?.chapters?.chapter ?? xml?.chapters?.chapter;
|
|
34
|
-
const roots = toArray(root);
|
|
35
|
-
return roots
|
|
36
|
-
.map((ch, i) => {
|
|
37
|
-
const id = ch?.id ?? ch?.["@_id"];
|
|
38
|
-
const labelRaw = ch?.label ?? ch?.["@_label"] ?? "";
|
|
39
|
-
return {
|
|
40
|
-
id: String(id),
|
|
41
|
-
label: decodeHtmlEntities(String(labelRaw)).trim(),
|
|
42
|
-
index: i,
|
|
43
|
-
};
|
|
44
|
-
})
|
|
45
|
-
.filter((c) => c.id && c.label);
|
|
46
|
-
}
|
|
47
|
-
function pickBestLevel1ChapterForAgenda(chapters, agendaTitle) {
|
|
48
|
-
const q = normalize(agendaTitle);
|
|
49
|
-
let best = null;
|
|
50
|
-
for (const ch of chapters) {
|
|
51
|
-
const s = dice(q, ch.label);
|
|
52
|
-
if (!best || s > best.score)
|
|
53
|
-
best = { chapter: ch, score: s };
|
|
54
|
-
}
|
|
55
|
-
if (!best || best.score < CHAPTER_MATCH_THRESHOLD)
|
|
56
|
-
return { chapter: chapters[0], score: 0 };
|
|
57
|
-
return best;
|
|
58
|
-
}
|
|
59
|
-
export function getAgendaSegmentTimecodes(dataNvs, finalPlayerNvs, agendaTitleOrObjet) {
|
|
60
|
-
const l1 = getLevel1Chapters(dataNvs);
|
|
61
|
-
if (!l1.length)
|
|
62
|
-
return null;
|
|
63
|
-
const best = pickBestLevel1ChapterForAgenda(l1, agendaTitleOrObjet);
|
|
64
|
-
if (!best)
|
|
65
|
-
return null;
|
|
66
|
-
const chapter = best.chapter;
|
|
67
|
-
const next = l1[chapter.index + 1] ?? null;
|
|
68
|
-
const start = getTimecodeForChapterId(finalPlayerNvs, chapter.id);
|
|
69
|
-
if (start == null)
|
|
70
|
-
return null;
|
|
71
|
-
const end = next ? getTimecodeForChapterId(finalPlayerNvs, next.id) : null;
|
|
72
|
-
return {
|
|
73
|
-
start,
|
|
74
|
-
end,
|
|
75
|
-
chapterId: chapter.id,
|
|
76
|
-
nextChapterId: next?.id ?? null,
|
|
77
|
-
score: best.score,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { AgendaEvent, GroupedReunion, TimeSlot } from "../types/agenda";
|
|
2
|
-
import { DossierLegislatifResult } from "../model/dosleg";
|
|
3
|
-
type KnownType = "SP" | "COM" | "MC" | "OD" | "ID";
|
|
4
|
-
type DossierBySenatUrl = Record<string, DossierLegislatifResult>;
|
|
5
|
-
export declare function groupNonSPByTypeOrganeHour(events: AgendaEvent[], DossierBySenatUrl: DossierBySenatUrl): Record<"IDC" | "IDM" | "IDO" | "IDI", GroupedReunion[]>;
|
|
6
|
-
export declare function groupSeancePubliqueBySlot(events: AgendaEvent[], dossierBySenatUrl: DossierBySenatUrl): Record<TimeSlot, GroupedReunion[]>;
|
|
7
|
-
export declare function makeTypeGroupUid(dateISO: string, kind: KnownType, agendaEventId: string, organe?: string | null): string;
|
|
8
|
-
export declare function makeGroupUid(date: string, slot: TimeSlot): string;
|
|
9
|
-
export declare function formatYYYYMMDD(dateYYYYMMDD: string): string;
|
|
10
|
-
export declare function makeReunionUid(agenda: AgendaEvent): string;
|
|
11
|
-
export {};
|