@tricoteuses/senat 3.1.0 → 3.1.2
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 +3 -3
- package/lib/src/loaders.js +1 -1
- package/lib/src/model/agenda.d.ts +1 -1
- package/lib/src/model/commission.d.ts +2 -2
- package/lib/src/model/seance.d.ts +1 -1
- package/lib/src/types/ameli.d.ts +4 -1761
- package/lib/src/types/ameli.js +1 -1074
- package/lib/src/types/debats.d.ts +2 -380
- package/lib/src/types/debats.js +1 -266
- package/lib/src/types/dosleg.d.ts +69 -2953
- package/lib/src/types/dosleg.js +1 -2005
- package/lib/src/types/questions.d.ts +2 -699
- package/lib/src/types/questions.js +1 -493
- package/lib/src/types/sens.d.ts +7 -7842
- package/lib/src/types/sens.js +1 -4691
- package/lib/src/utils/nvs-parsing.d.ts +1 -1
- package/lib/src/utils/nvs-parsing.js +9 -1
- package/lib/src/videos/pipeline.d.ts +3 -3
- package/lib/src/videos/pipeline.js +2 -2
- package/package.json +1 -1
- package/lib/add-js-extensions-v2.d.ts +0 -1
- package/lib/add-js-extensions-v2.js +0 -23
- package/lib/add-js-extensions.d.ts +0 -1
- package/lib/add-js-extensions.js +0 -17
- package/lib/aggregates.d.ts +0 -52
- package/lib/aggregates.js +0 -930
- package/lib/aggregates.mjs +0 -713
- package/lib/aggregates.ts +0 -833
- package/lib/config.d.ts +0 -10
- package/lib/config.js +0 -16
- package/lib/config.mjs +0 -16
- package/lib/config.ts +0 -26
- package/lib/databases.d.ts +0 -2
- package/lib/databases.js +0 -26
- package/lib/databases.mjs +0 -57
- package/lib/databases.ts +0 -71
- package/lib/datasets.d.ts +0 -34
- package/lib/datasets.js +0 -233
- package/lib/datasets.mjs +0 -78
- package/lib/datasets.ts +0 -118
- package/lib/fields.d.ts +0 -10
- package/lib/fields.js +0 -68
- package/lib/fields.mjs +0 -22
- package/lib/fields.ts +0 -29
- 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/index.mjs +0 -7
- package/lib/index.ts +0 -64
- package/lib/inserters.d.ts +0 -98
- package/lib/inserters.js +0 -500
- package/lib/inserters.mjs +0 -360
- package/lib/inserters.ts +0 -521
- package/lib/legislatures.json +0 -38
- package/lib/loaders.d.ts +0 -58
- package/lib/loaders.js +0 -286
- package/lib/loaders.mjs +0 -158
- package/lib/loaders.ts +0 -271
- 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 -149
- package/lib/model/ameli.mjs +0 -84
- package/lib/model/ameli.ts +0 -100
- 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/debats.mjs +0 -43
- package/lib/model/debats.ts +0 -68
- package/lib/model/documents.d.ts +0 -12
- package/lib/model/documents.js +0 -151
- package/lib/model/dosleg.d.ts +0 -7
- package/lib/model/dosleg.js +0 -326
- package/lib/model/dosleg.mjs +0 -196
- package/lib/model/dosleg.ts +0 -240
- package/lib/model/index.d.ts +0 -7
- package/lib/model/index.js +0 -7
- package/lib/model/index.mjs +0 -5
- package/lib/model/index.ts +0 -15
- package/lib/model/questions.d.ts +0 -45
- package/lib/model/questions.js +0 -89
- package/lib/model/questions.mjs +0 -71
- package/lib/model/questions.ts +0 -93
- 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/sens.mjs +0 -415
- package/lib/model/sens.ts +0 -516
- package/lib/model/texte.d.ts +0 -7
- package/lib/model/texte.js +0 -256
- package/lib/model/texte.mjs +0 -208
- package/lib/model/texte.ts +0 -229
- package/lib/model/util.d.ts +0 -9
- package/lib/model/util.js +0 -38
- package/lib/model/util.mjs +0 -19
- package/lib/model/util.ts +0 -32
- 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/ameli.mjs +0 -163
- package/lib/raw_types/debats.d.ts +0 -207
- package/lib/raw_types/debats.js +0 -5
- package/lib/raw_types/debats.mjs +0 -58
- package/lib/raw_types/dosleg.d.ts +0 -1619
- package/lib/raw_types/dosleg.js +0 -5
- package/lib/raw_types/dosleg.mjs +0 -438
- package/lib/raw_types/questions.d.ts +0 -419
- package/lib/raw_types/questions.js +0 -5
- package/lib/raw_types/questions.mjs +0 -11
- package/lib/raw_types/senat.d.ts +0 -11368
- 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/sens.mjs +0 -508
- package/lib/raw_types_kysely/ameli.d.ts +0 -915
- package/lib/raw_types_kysely/ameli.js +0 -7
- package/lib/raw_types_kysely/ameli.mjs +0 -5
- package/lib/raw_types_kysely/ameli.ts +0 -951
- package/lib/raw_types_kysely/debats.d.ts +0 -207
- package/lib/raw_types_kysely/debats.js +0 -7
- package/lib/raw_types_kysely/debats.mjs +0 -5
- package/lib/raw_types_kysely/debats.ts +0 -222
- package/lib/raw_types_kysely/dosleg.d.ts +0 -3532
- package/lib/raw_types_kysely/dosleg.js +0 -7
- package/lib/raw_types_kysely/dosleg.mjs +0 -5
- package/lib/raw_types_kysely/dosleg.ts +0 -3621
- package/lib/raw_types_kysely/questions.d.ts +0 -414
- package/lib/raw_types_kysely/questions.js +0 -7
- package/lib/raw_types_kysely/questions.mjs +0 -5
- package/lib/raw_types_kysely/questions.ts +0 -426
- package/lib/raw_types_kysely/sens.d.ts +0 -4394
- package/lib/raw_types_kysely/sens.js +0 -7
- package/lib/raw_types_kysely/sens.mjs +0 -5
- package/lib/raw_types_kysely/sens.ts +0 -4499
- package/lib/raw_types_schemats/ameli.d.ts +0 -539
- package/lib/raw_types_schemats/ameli.js +0 -2
- package/lib/raw_types_schemats/ameli.mjs +0 -2
- package/lib/raw_types_schemats/ameli.ts +0 -601
- package/lib/raw_types_schemats/debats.d.ts +0 -127
- package/lib/raw_types_schemats/debats.js +0 -2
- package/lib/raw_types_schemats/debats.mjs +0 -2
- package/lib/raw_types_schemats/debats.ts +0 -145
- package/lib/raw_types_schemats/dosleg.d.ts +0 -977
- package/lib/raw_types_schemats/dosleg.js +0 -2
- package/lib/raw_types_schemats/dosleg.mjs +0 -2
- package/lib/raw_types_schemats/dosleg.ts +0 -2193
- package/lib/raw_types_schemats/questions.d.ts +0 -235
- package/lib/raw_types_schemats/questions.js +0 -2
- package/lib/raw_types_schemats/questions.mjs +0 -2
- package/lib/raw_types_schemats/questions.ts +0 -249
- package/lib/raw_types_schemats/sens.d.ts +0 -6915
- package/lib/raw_types_schemats/sens.js +0 -2
- package/lib/raw_types_schemats/sens.mjs +0 -2
- package/lib/raw_types_schemats/sens.ts +0 -2907
- package/lib/scripts/convert_data.d.ts +0 -1
- package/lib/scripts/convert_data.js +0 -354
- package/lib/scripts/convert_data.mjs +0 -181
- package/lib/scripts/convert_data.ts +0 -243
- 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/datautil.mjs +0 -16
- package/lib/scripts/datautil.ts +0 -19
- package/lib/scripts/images/transparent_150x192.jpg +0 -0
- package/lib/scripts/images/transparent_155x225.jpg +0 -0
- package/lib/scripts/parse_textes.d.ts +0 -1
- package/lib/scripts/parse_textes.js +0 -44
- package/lib/scripts/parse_textes.mjs +0 -46
- package/lib/scripts/parse_textes.ts +0 -65
- 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_documents.mjs +0 -249
- package/lib/scripts/retrieve_documents.ts +0 -298
- package/lib/scripts/retrieve_open_data.d.ts +0 -1
- package/lib/scripts/retrieve_open_data.js +0 -315
- package/lib/scripts/retrieve_open_data.mjs +0 -217
- package/lib/scripts/retrieve_open_data.ts +0 -268
- package/lib/scripts/retrieve_senateurs_photos.d.ts +0 -1
- package/lib/scripts/retrieve_senateurs_photos.js +0 -147
- package/lib/scripts/retrieve_senateurs_photos.mjs +0 -147
- package/lib/scripts/retrieve_senateurs_photos.ts +0 -177
- 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/cli_helpers.ts +0 -36
- package/lib/scripts/shared/util.d.ts +0 -4
- package/lib/scripts/shared/util.js +0 -35
- package/lib/scripts/shared/util.ts +0 -33
- package/lib/scripts/test_iter_load.d.ts +0 -1
- package/lib/scripts/test_iter_load.js +0 -12
- package/lib/src/ameli.d.ts +0 -66
- package/lib/src/ameli.js +0 -1
- package/lib/src/databases.d.ts +0 -3
- package/lib/src/databases.js +0 -26
- package/lib/src/db_types/ameli.d.ts +0 -1762
- package/lib/src/db_types/ameli.js +0 -1074
- package/lib/src/db_types/debats.d.ts +0 -380
- package/lib/src/db_types/debats.js +0 -266
- package/lib/src/db_types/dosleg.d.ts +0 -2954
- package/lib/src/db_types/dosleg.js +0 -2005
- package/lib/src/db_types/questions.d.ts +0 -699
- package/lib/src/db_types/questions.js +0 -493
- package/lib/src/db_types/sens.d.ts +0 -7843
- package/lib/src/db_types/sens.js +0 -4691
- package/lib/src/debats.d.ts +0 -38
- package/lib/src/debats.js +0 -1
- package/lib/src/dosleg.d.ts +0 -142
- package/lib/src/dosleg.js +0 -193
- package/lib/src/model/ameli_postgres.d.ts +0 -67
- package/lib/src/model/ameli_postgres.js +0 -150
- package/lib/src/other_types/questions.d.ts +0 -2
- package/lib/src/other_types/questions.js +0 -1
- package/lib/src/questions.d.ts +0 -53
- package/lib/src/questions.js +0 -1
- package/lib/src/raw_types/senat.d.ts +0 -11372
- package/lib/src/raw_types/senat.js +0 -5
- package/lib/src/rich_types/agenda.d.ts +0 -45
- package/lib/src/rich_types/agenda.js +0 -1
- package/lib/src/rich_types/compte_rendu.d.ts +0 -83
- package/lib/src/rich_types/compte_rendu.js +0 -1
- package/lib/src/rich_types/sessions.d.ts +0 -6
- package/lib/src/rich_types/sessions.js +0 -19
- package/lib/src/rich_types/texte.d.ts +0 -72
- package/lib/src/rich_types/texte.js +0 -15
- package/lib/src/scripts/test_iter_load.d.ts +0 -1
- package/lib/src/scripts/test_iter_load.js +0 -12
- package/lib/src/sens.d.ts +0 -104
- package/lib/src/sens.js +0 -1
- package/lib/strings.d.ts +0 -1
- package/lib/strings.js +0 -18
- package/lib/strings.mjs +0 -18
- package/lib/strings.ts +0 -26
- package/lib/tsconfig.tsbuildinfo +0 -1
- 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/ameli.mjs +0 -13
- package/lib/types/ameli.ts +0 -21
- 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/debats.mjs +0 -2
- package/lib/types/debats.ts +0 -6
- package/lib/types/dosleg.d.ts +0 -70
- package/lib/types/dosleg.js +0 -1
- package/lib/types/dosleg.mjs +0 -151
- package/lib/types/dosleg.ts +0 -284
- package/lib/types/questions.d.ts +0 -2
- package/lib/types/questions.js +0 -1
- package/lib/types/questions.mjs +0 -1
- package/lib/types/questions.ts +0 -3
- package/lib/types/sens.d.ts +0 -10
- package/lib/types/sens.js +0 -1
- package/lib/types/sens.mjs +0 -1
- package/lib/types/sens.ts +0 -12
- package/lib/types/sessions.d.ts +0 -5
- package/lib/types/sessions.js +0 -84
- package/lib/types/sessions.mjs +0 -43
- package/lib/types/sessions.ts +0 -42
- package/lib/types/texte.d.ts +0 -74
- package/lib/types/texte.js +0 -16
- package/lib/types/texte.mjs +0 -16
- package/lib/types/texte.ts +0 -76
- package/lib/typings/windows-1252.d.js +0 -2
- package/lib/typings/windows-1252.d.mjs +0 -2
- package/lib/typings/windows-1252.d.ts +0 -11
- 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 -9
- package/lib/utils/reunion_grouping.js +0 -361
- 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/validators/config.mjs +0 -54
- package/lib/validators/config.ts +0 -79
- package/lib/validators/senat.d.ts +0 -0
- package/lib/validators/senat.js +0 -28
- package/lib/validators/senat.mjs +0 -24
- package/lib/validators/senat.ts +0 -26
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { getSessionsFromStart } from "../types/sessions";
|
|
2
|
-
import { iterLoadSenatDossiersLegislatifs } from "../loaders";
|
|
3
|
-
export function buildOdj(events, dossierBySenatUrl) {
|
|
4
|
-
const byObjet = new Map(); // objet -> set de dossier uids
|
|
5
|
-
let codeEtape = null;
|
|
6
|
-
let dossier = null;
|
|
7
|
-
for (const ev of events) {
|
|
8
|
-
const objetKey = (ev.objet ?? "").trim();
|
|
9
|
-
const url = normalizeSenatUrl(ev.urlDossierSenat) ?? undefined;
|
|
10
|
-
dossier = url ? dossierBySenatUrl[url] : null;
|
|
11
|
-
const dossierUid = dossier ? pickDossierUid(dossier) : undefined;
|
|
12
|
-
codeEtape = dossier ? computeCodeEtape(ev, dossier) : null;
|
|
13
|
-
// si on n’a ni objet ni dossier, ça ne sert à rien de créer un point
|
|
14
|
-
if (!objetKey && !dossierUid)
|
|
15
|
-
continue;
|
|
16
|
-
if (!byObjet.has(objetKey) && dossierUid) {
|
|
17
|
-
byObjet.set(objetKey, dossierUid);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
if (byObjet.size === 0)
|
|
21
|
-
return undefined;
|
|
22
|
-
const pointsOdj = [];
|
|
23
|
-
for (const [objetKey, dossierUid] of byObjet) {
|
|
24
|
-
pointsOdj.push({
|
|
25
|
-
objet: objetKey || null,
|
|
26
|
-
dossierLegislatifRef: dossierUid || null,
|
|
27
|
-
codeEtape,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return { pointsOdj };
|
|
31
|
-
}
|
|
32
|
-
function pickDossierUid(d) {
|
|
33
|
-
if (d["signet"] && d["signet"].trim())
|
|
34
|
-
return d["signet"].trim();
|
|
35
|
-
if (d["code"] && String(d["code"]).trim())
|
|
36
|
-
return String(d["code"]).trim();
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
function normalizeSenatUrl(url) {
|
|
40
|
-
if (!url)
|
|
41
|
-
return null;
|
|
42
|
-
let u = url.trim();
|
|
43
|
-
if (!u)
|
|
44
|
-
return null;
|
|
45
|
-
if (!/^https?:\/\//i.test(u))
|
|
46
|
-
return u;
|
|
47
|
-
// force https://
|
|
48
|
-
u = u.replace(/^http:\/\//i, "https://");
|
|
49
|
-
u = u.replace(/\/+$/, "");
|
|
50
|
-
return u;
|
|
51
|
-
}
|
|
52
|
-
export function buildSenatDossierIndex(options) {
|
|
53
|
-
const index = {};
|
|
54
|
-
const sessions = getSessionsFromStart(2015);
|
|
55
|
-
for (const session of sessions) {
|
|
56
|
-
for (const item of iterLoadSenatDossiersLegislatifs(options["dataDir"], session)) {
|
|
57
|
-
const dossier = item.item;
|
|
58
|
-
const url = dossier["url"] ? normalizeSenatUrl(dossier["url"]) : undefined;
|
|
59
|
-
if (url)
|
|
60
|
-
index[url] = dossier;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return index;
|
|
64
|
-
}
|
|
65
|
-
function detectLecture(objet) {
|
|
66
|
-
objet = objet.toLowerCase();
|
|
67
|
-
if (objet.includes("première lecture"))
|
|
68
|
-
return 1;
|
|
69
|
-
if (objet.includes("deuxième lecture") || objet.includes("2ème"))
|
|
70
|
-
return 2;
|
|
71
|
-
if (objet.includes("troisième lecture") || objet.includes("3ème"))
|
|
72
|
-
return 3;
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
function computeCodeEtape(ev, dossier) {
|
|
76
|
-
// In order to match with stage, we need to remove the '-SEANCE' suffix from the codeActe
|
|
77
|
-
const cleanCode = (code) => code.replace(/-SEANCE$/, "");
|
|
78
|
-
const lecture = detectLecture(ev.objet ?? "");
|
|
79
|
-
const organe = ev.organe ?? "";
|
|
80
|
-
const nature = organe.toLowerCase().includes("commission")
|
|
81
|
-
? "COM"
|
|
82
|
-
: organe.toLowerCase().includes("séance publique")
|
|
83
|
-
? "DEBATS"
|
|
84
|
-
: "";
|
|
85
|
-
const evDate = ev.date.split("T")[0];
|
|
86
|
-
const flat = buildFlatActes(dossier);
|
|
87
|
-
// 1) Strict matching: same date + same nature
|
|
88
|
-
let candidates = flat.filter((a) => {
|
|
89
|
-
if (a.date !== evDate)
|
|
90
|
-
return false;
|
|
91
|
-
if (nature && !a.codeActe.includes(nature))
|
|
92
|
-
return false;
|
|
93
|
-
return true;
|
|
94
|
-
});
|
|
95
|
-
// If a specific lecture is detected in the agenda event, refine the candidates
|
|
96
|
-
if (lecture !== undefined && candidates.length > 0) {
|
|
97
|
-
const withLecture = candidates.filter((c) => c.ordreLecture === lecture);
|
|
98
|
-
if (withLecture.length > 0) {
|
|
99
|
-
candidates = withLecture;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (candidates.length > 0) {
|
|
103
|
-
// Multiple candidates: pick the most specific one (longest code string)
|
|
104
|
-
candidates.sort((a, b) => b.codeActe.length - a.codeActe.length);
|
|
105
|
-
return cleanCode(candidates[0].codeActe);
|
|
106
|
-
}
|
|
107
|
-
// 2) Fallback COM: If no exact date match for a commission event,
|
|
108
|
-
// take the latest commission act for this lecture on or before the event date.
|
|
109
|
-
if (nature === "COM") {
|
|
110
|
-
let comActs = flat.filter((a) => a.codeActe.includes("COM") && a.date <= evDate);
|
|
111
|
-
if (lecture !== undefined) {
|
|
112
|
-
const byLecture = comActs.filter((a) => a.ordreLecture === lecture);
|
|
113
|
-
if (byLecture.length > 0)
|
|
114
|
-
comActs = byLecture;
|
|
115
|
-
}
|
|
116
|
-
if (comActs.length > 0) {
|
|
117
|
-
comActs.sort((a, b) => b.date.localeCompare(a.date) || b.codeActe.length - a.codeActe.length);
|
|
118
|
-
return cleanCode(comActs[0].codeActe);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
// 3) Fallback general lecture: if nothing else worked but a lecture is identified,
|
|
122
|
-
// find any act belonging to that lecture (e.g., SN1-DEPOT).
|
|
123
|
-
if (lecture !== undefined) {
|
|
124
|
-
const genericActe = flat.find((a) => a.ordreLecture === lecture);
|
|
125
|
-
if (genericActe) {
|
|
126
|
-
return cleanCode(genericActe.codeActe);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
console.log(`✖ No stage code found for ev=${ev.id} (Date: ${evDate}, Nature: ${nature}, Lecture: ${lecture})`, {
|
|
130
|
-
totalActsInDossier: dossier["actes_legislatifs"]?.length || 0,
|
|
131
|
-
firstActDate: flat[0]?.date,
|
|
132
|
-
});
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
function buildFlatActes(dossier) {
|
|
136
|
-
const actes = dossier["actes_legislatifs"] ?? [];
|
|
137
|
-
const res = [];
|
|
138
|
-
for (const acte of actes) {
|
|
139
|
-
if (acte["chambre"] !== "SN")
|
|
140
|
-
continue;
|
|
141
|
-
const codeActe = acte.code_acte;
|
|
142
|
-
const dateActe = acte.date?.split("T")[0];
|
|
143
|
-
if (!codeActe || !dateActe)
|
|
144
|
-
continue;
|
|
145
|
-
const match = codeActe.match(/^(?:SN|AN)(\d+)/);
|
|
146
|
-
const ordreLecture = match ? parseInt(match[1], 10) : undefined;
|
|
147
|
-
res.push({
|
|
148
|
-
codeActe,
|
|
149
|
-
date: dateActe,
|
|
150
|
-
ordreLecture,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
return res;
|
|
154
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { DateTime } from "luxon";
|
|
2
|
-
import { AgendaEvent, Reunion } from "../types/agenda";
|
|
3
|
-
import { DossierLegislatifResult } from "../model/dosleg";
|
|
4
|
-
import * as cheerio from "cheerio";
|
|
5
|
-
type KnownType = "SP" | "COM" | "MC" | "OD" | "ID";
|
|
6
|
-
type DossierBySenatUrl = Record<string, DossierLegislatifResult>;
|
|
7
|
-
type ReunionBucket = "IDS" | "IDC" | "IDM" | "IDO" | "IDI";
|
|
8
|
-
type ReunionsByBucket = Record<ReunionBucket, Reunion[]>;
|
|
9
|
-
export declare function buildReunionsByBucket(events: AgendaEvent[], dossierBySenatUrl: DossierBySenatUrl): ReunionsByBucket;
|
|
10
|
-
export declare function makeReunionUid(dateISO: string, kind: KnownType, agendaEventId: string, organe?: string | null): string;
|
|
11
|
-
export declare function formatYYYYMMDD(dateYYYYMMDD: string): string;
|
|
12
|
-
export declare function deriveTimesForEvent(ev: AgendaEvent): {
|
|
13
|
-
startISO: string | null;
|
|
14
|
-
endISO: string | null;
|
|
15
|
-
};
|
|
16
|
-
export type SommaireBlock = {
|
|
17
|
-
text: string;
|
|
18
|
-
startIndex: number;
|
|
19
|
-
targetId?: string | null;
|
|
20
|
-
};
|
|
21
|
-
export declare function extractSommaireBlocks($: cheerio.CheerioAPI, idx: Map<any, number>): SommaireBlock[];
|
|
22
|
-
export declare function parseISO(iso: string | null | undefined): DateTime | null;
|
|
23
|
-
export {};
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { DateTime } from "luxon";
|
|
2
|
-
import { buildOdj } from "./reunion_odj_building";
|
|
3
|
-
import { norm } from "./string_cleaning";
|
|
4
|
-
const PARIS = "Europe/Paris";
|
|
5
|
-
const STOPWORDS = new Set([
|
|
6
|
-
"de",
|
|
7
|
-
"du",
|
|
8
|
-
"des",
|
|
9
|
-
"la",
|
|
10
|
-
"le",
|
|
11
|
-
"les",
|
|
12
|
-
"l",
|
|
13
|
-
"d",
|
|
14
|
-
"et",
|
|
15
|
-
"en",
|
|
16
|
-
"au",
|
|
17
|
-
"aux",
|
|
18
|
-
"pour",
|
|
19
|
-
"sur",
|
|
20
|
-
"sous",
|
|
21
|
-
"à",
|
|
22
|
-
"a",
|
|
23
|
-
"aux",
|
|
24
|
-
]);
|
|
25
|
-
function toReunion(e, dossierBySenatUrl, uid) {
|
|
26
|
-
const date = norm(e.date) ?? e.date;
|
|
27
|
-
const { startISO, endISO } = deriveTimesForEvent(e);
|
|
28
|
-
const startTime = startISO ?? e.startTime ?? null;
|
|
29
|
-
const endTime = endISO ?? e.endTime ?? null;
|
|
30
|
-
return {
|
|
31
|
-
uid,
|
|
32
|
-
chambre: "SN",
|
|
33
|
-
date,
|
|
34
|
-
type: e.type || "",
|
|
35
|
-
organe: e.organe || undefined,
|
|
36
|
-
startTime,
|
|
37
|
-
endTime,
|
|
38
|
-
captationVideo: e.captationVideo === true,
|
|
39
|
-
titre: e.titre,
|
|
40
|
-
objet: e.objet || "",
|
|
41
|
-
events: [e], // TODO remove
|
|
42
|
-
odj: buildOdj([e], dossierBySenatUrl),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
export function buildReunionsByBucket(events, dossierBySenatUrl) {
|
|
46
|
-
const out = { IDS: [], IDC: [], IDM: [], IDO: [], IDI: [] };
|
|
47
|
-
if (!events?.length)
|
|
48
|
-
return out;
|
|
49
|
-
for (const e of events) {
|
|
50
|
-
const kind = classifyAgendaType(e?.type);
|
|
51
|
-
if (!kind) {
|
|
52
|
-
continue;
|
|
53
|
-
console.warn("Can't determine type of reunion");
|
|
54
|
-
}
|
|
55
|
-
const bucket = typeToSuffixStrict(kind);
|
|
56
|
-
const uid = makeReunionUid(e.date, kind, e.id, e.organe ?? null);
|
|
57
|
-
out[bucket].push(toReunion(e, dossierBySenatUrl, uid));
|
|
58
|
-
}
|
|
59
|
-
// Tri stable par bucket (date + heure, inconnus à la fin)
|
|
60
|
-
for (const k of Object.keys(out)) {
|
|
61
|
-
out[k].sort((a, b) => {
|
|
62
|
-
const da = DateTime.fromISO(`${a.date}T${a.startTime || "23:59:59.999+02:00"}`, { zone: PARIS }).toMillis();
|
|
63
|
-
const db = DateTime.fromISO(`${b.date}T${b.startTime || "23:59:59.999+02:00"}`, { zone: PARIS }).toMillis();
|
|
64
|
-
return da - db || (a.organe || "").localeCompare(b.organe || "") || (a.titre || "").localeCompare(b.titre || "");
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
return out;
|
|
68
|
-
}
|
|
69
|
-
function normalizeNoAccents(s) {
|
|
70
|
-
return (s || "")
|
|
71
|
-
.trim()
|
|
72
|
-
.normalize("NFKD")
|
|
73
|
-
.replace(/[\u0300-\u036f]/g, "");
|
|
74
|
-
}
|
|
75
|
-
function classifyAgendaType(typeLabel) {
|
|
76
|
-
const s = normalizeNoAccents(typeLabel || "").toLowerCase();
|
|
77
|
-
if (/\bseance\b.*\bpublique\b/.test(s))
|
|
78
|
-
return "SP";
|
|
79
|
-
if (/\bcommissions\b/.test(s))
|
|
80
|
-
return "COM";
|
|
81
|
-
if (/\bmission\b.*\bcontrole\b/.test(s))
|
|
82
|
-
return "MC";
|
|
83
|
-
if (/\boffices\b|\bdelegations\b/.test(s))
|
|
84
|
-
return "OD";
|
|
85
|
-
if (/\instances\b|\decisionelles\b/.test(s))
|
|
86
|
-
return "ID";
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
function typeToSuffixStrict(kind) {
|
|
90
|
-
switch (kind) {
|
|
91
|
-
case "SP":
|
|
92
|
-
return "IDS";
|
|
93
|
-
case "COM":
|
|
94
|
-
return "IDC";
|
|
95
|
-
case "MC":
|
|
96
|
-
return "IDM";
|
|
97
|
-
case "OD":
|
|
98
|
-
return "IDO";
|
|
99
|
-
case "ID":
|
|
100
|
-
return "IDI";
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function organeInitials(input, maxLen = 8) {
|
|
104
|
-
if (!input)
|
|
105
|
-
return "";
|
|
106
|
-
const clean = normalizeNoAccents(input)
|
|
107
|
-
.replace(/['’]/g, " ")
|
|
108
|
-
.replace(/[^A-Za-z0-9\s]/g, " ")
|
|
109
|
-
.replace(/\s+/g, " ")
|
|
110
|
-
.trim();
|
|
111
|
-
if (!clean)
|
|
112
|
-
return "";
|
|
113
|
-
const parts = clean.split(" ");
|
|
114
|
-
const letters = [];
|
|
115
|
-
for (const raw of parts) {
|
|
116
|
-
const w = raw.toLowerCase();
|
|
117
|
-
if (!w)
|
|
118
|
-
continue;
|
|
119
|
-
if (STOPWORDS.has(w))
|
|
120
|
-
continue;
|
|
121
|
-
// Take two first letter if alphanumeric
|
|
122
|
-
const two = raw.slice(0, 2);
|
|
123
|
-
if (/[A-Za-z0-9]/.test(two))
|
|
124
|
-
letters.push(two.toUpperCase());
|
|
125
|
-
}
|
|
126
|
-
const out = letters.join("");
|
|
127
|
-
return out.slice(0, maxLen);
|
|
128
|
-
}
|
|
129
|
-
export function makeReunionUid(dateISO, kind, agendaEventId, organe) {
|
|
130
|
-
const ymd = dateISO ? formatYYYYMMDD(dateISO) : "00000000";
|
|
131
|
-
const suffix = typeToSuffixStrict(kind);
|
|
132
|
-
const org = organe && suffix !== "IDS" ? organeInitials(organe) : "";
|
|
133
|
-
let base = `RUSN${ymd}${suffix}${org}${agendaEventId}`;
|
|
134
|
-
return base;
|
|
135
|
-
}
|
|
136
|
-
export function formatYYYYMMDD(dateYYYYMMDD) {
|
|
137
|
-
const [y, m, d] = dateYYYYMMDD.split("-");
|
|
138
|
-
return `${y}${m}${d}`;
|
|
139
|
-
}
|
|
140
|
-
// Extract hours/minutes from French text like "à 10 h 30", "de 10 h à 12 h", etc.
|
|
141
|
-
function parseTimeOriginalFR(timeOriginal) {
|
|
142
|
-
if (!timeOriginal)
|
|
143
|
-
return { start: null, end: null };
|
|
144
|
-
const txt = (timeOriginal || "")
|
|
145
|
-
.replace(/\u00A0/g, " ") // nbsp → space
|
|
146
|
-
.replace(/\s+/g, " ") // espaces multiples
|
|
147
|
-
.toLowerCase()
|
|
148
|
-
.trim();
|
|
149
|
-
// 1) "de 10 h 30 à 12 heures", "de 10h30 à 12h", "de 9 h à 11 h 15", etc.
|
|
150
|
-
const reRange = /\bde\s+(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?\s*à\s*(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?/i;
|
|
151
|
-
const mRange = txt.match(reRange);
|
|
152
|
-
if (mRange) {
|
|
153
|
-
const h1 = clampHour(+mRange[1]), m1 = clampMinute(mRange[2] ? +mRange[2] : 0);
|
|
154
|
-
const h2 = clampHour(+mRange[3]), m2 = clampMinute(mRange[4] ? +mRange[4] : 0);
|
|
155
|
-
return { start: toIsoTime(h1, m1), end: toIsoTime(h2, m2) };
|
|
156
|
-
}
|
|
157
|
-
// 2) "à 10 h 30", "à 10h", "A 10h30", "A 9 heures", etc.
|
|
158
|
-
const reAt = /\b(?:a|à)\s*(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?/i;
|
|
159
|
-
const mAt = txt.match(reAt);
|
|
160
|
-
if (mAt) {
|
|
161
|
-
const h = clampHour(+mAt[1]), m = clampMinute(mAt[2] ? +mAt[2] : 0);
|
|
162
|
-
return { start: toIsoTime(h, m), end: null };
|
|
163
|
-
}
|
|
164
|
-
// 3) "10 h 30", "15h", "9 heures" sans 'à' / 'de ... à ...'
|
|
165
|
-
const reBare = /\b(\d{1,2})\s*(?:h|:)?\s*(\d{1,2})?\s*(?:heures?)?\b/;
|
|
166
|
-
const mBare = txt.match(reBare);
|
|
167
|
-
if (mBare) {
|
|
168
|
-
const h = clampHour(+mBare[1]), m = clampMinute(mBare[2] ? +mBare[2] : 0);
|
|
169
|
-
return { start: toIsoTime(h, m), end: null };
|
|
170
|
-
}
|
|
171
|
-
return { start: null, end: null };
|
|
172
|
-
}
|
|
173
|
-
function clampHour(h) {
|
|
174
|
-
return Math.max(0, Math.min(23, h));
|
|
175
|
-
}
|
|
176
|
-
function clampMinute(m) {
|
|
177
|
-
return Math.max(0, Math.min(59, m));
|
|
178
|
-
}
|
|
179
|
-
function toIsoTime(h, m) {
|
|
180
|
-
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:00.000+02:00`;
|
|
181
|
-
}
|
|
182
|
-
export function deriveTimesForEvent(ev) {
|
|
183
|
-
const directStart = ev.startTime ?? null;
|
|
184
|
-
const directEnd = ev.endTime ?? null;
|
|
185
|
-
const fromText = parseTimeOriginalFR(ev.timeOriginal);
|
|
186
|
-
const startISO = directStart ?? fromText.start ?? null;
|
|
187
|
-
const endISO = directEnd ?? fromText.end ?? null;
|
|
188
|
-
return { startISO, endISO };
|
|
189
|
-
}
|
|
190
|
-
export function extractSommaireBlocks($, idx) {
|
|
191
|
-
const blocks = [];
|
|
192
|
-
// lignes du sommaire avec lien
|
|
193
|
-
$("cri\\:tm5 a[href^='#'], cri\\:tm3 a[href^='#'], p.tm5 a[href^='#'], p.tm3 a[href^='#']").each((_, a) => {
|
|
194
|
-
const href = $(a).attr("href") || "";
|
|
195
|
-
const targetId = href.startsWith("#") ? href.slice(1) : null;
|
|
196
|
-
const text = norm($(a).text() || "");
|
|
197
|
-
const startIndex = idx.get(a) ?? idx.get($(a).closest("p")[0]) ?? null;
|
|
198
|
-
if (!text || startIndex == null)
|
|
199
|
-
return;
|
|
200
|
-
blocks.push({ text, startIndex, targetId });
|
|
201
|
-
});
|
|
202
|
-
return blocks;
|
|
203
|
-
}
|
|
204
|
-
export function parseISO(iso) {
|
|
205
|
-
if (!iso)
|
|
206
|
-
return null;
|
|
207
|
-
const dt = DateTime.fromISO(iso, { setZone: true, zone: PARIS });
|
|
208
|
-
return dt.isValid ? dt : null;
|
|
209
|
-
}
|
package/lib/utils/scoring.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { AgendaEvent, Reunion } from "../types/agenda";
|
|
2
|
-
export declare function jaccard(a: Set<string>, b: Set<string>): number;
|
|
3
|
-
export declare function jaccardTokenSim(a: string, b: string): number;
|
|
4
|
-
export declare function isNoiseBlock(text: string): boolean;
|
|
5
|
-
/**
|
|
6
|
-
* Score robuste pour "bloc court vs event long":
|
|
7
|
-
* - coverageBloc = |A∩B| / |A| (critère principal)
|
|
8
|
-
* - jaccard en secondaire (utile quand les 2 sont longs)
|
|
9
|
-
* - bonus time optionnel (déjà chez toi)
|
|
10
|
-
*/
|
|
11
|
-
export declare function scoreSommaireBlockForEvent(blockText: string, ev: AgendaEvent): number;
|
|
12
|
-
export declare function scoreVideo(agenda: Reunion, agendaTs: number | null, sameOrg: boolean, videoTitle?: string, videoEpoch?: number, videoOrganes?: string[], timeAmbigious?: boolean): number;
|
|
13
|
-
export declare function dice(a: string, b: string): number;
|
|
14
|
-
export declare function normalize(s?: string | null): string;
|
package/lib/utils/scoring.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { parseISO } from "./reunion_parsing";
|
|
2
|
-
import { normalizeText } from "./string_cleaning";
|
|
3
|
-
export function jaccard(a, b) {
|
|
4
|
-
if (!a.size || !b.size)
|
|
5
|
-
return 0;
|
|
6
|
-
let inter = 0;
|
|
7
|
-
for (const t of a)
|
|
8
|
-
if (b.has(t))
|
|
9
|
-
inter++;
|
|
10
|
-
return inter / (a.size + b.size - inter);
|
|
11
|
-
}
|
|
12
|
-
export function jaccardTokenSim(a, b) {
|
|
13
|
-
const A = new Set(normalizeText(a).split(" ").filter(Boolean));
|
|
14
|
-
const B = new Set(normalizeText(b).split(" ").filter(Boolean));
|
|
15
|
-
if (A.size === 0 || B.size === 0)
|
|
16
|
-
return 0;
|
|
17
|
-
let inter = 0;
|
|
18
|
-
for (const x of A)
|
|
19
|
-
if (B.has(x))
|
|
20
|
-
inter++;
|
|
21
|
-
return inter / (A.size + B.size - inter);
|
|
22
|
-
}
|
|
23
|
-
export function isNoiseBlock(text) {
|
|
24
|
-
const t = normalizeText(text);
|
|
25
|
-
// très génériques / institutionnels / bruit
|
|
26
|
-
if (!t)
|
|
27
|
-
return true;
|
|
28
|
-
if (t.length < 6)
|
|
29
|
-
return true;
|
|
30
|
-
// Ex: "article 78", "ord re du jour", "organisation des travaux"
|
|
31
|
-
if (/^article\s+\d+/.test(t))
|
|
32
|
-
return true;
|
|
33
|
-
if (/\b(organisation des travaux|ordre du jour|suspension|reprise de la seance)\b/.test(t))
|
|
34
|
-
return true;
|
|
35
|
-
if (/\b(vice presidente|president|secretaire|ministre|rapporteur)\b/.test(t))
|
|
36
|
-
return true;
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Score robuste pour "bloc court vs event long":
|
|
41
|
-
* - coverageBloc = |A∩B| / |A| (critère principal)
|
|
42
|
-
* - jaccard en secondaire (utile quand les 2 sont longs)
|
|
43
|
-
* - bonus time optionnel (déjà chez toi)
|
|
44
|
-
*/
|
|
45
|
-
export function scoreSommaireBlockForEvent(blockText, ev) {
|
|
46
|
-
const evText = `${ev.titre ?? ""} ${ev.objet ?? ""}`;
|
|
47
|
-
const A = tokens(blockText); // tokens du bloc
|
|
48
|
-
const B = tokens(evText); // tokens de l'event
|
|
49
|
-
if (A.size === 0 || B.size === 0)
|
|
50
|
-
return 0;
|
|
51
|
-
const inter = intersectionSize(A, B);
|
|
52
|
-
const coverageBloc = inter / A.size; // 🔥 clé
|
|
53
|
-
const jac = jaccard(A, B);
|
|
54
|
-
// bonus inclusion brut (utile sur des expressions exactes)
|
|
55
|
-
const bNorm = normalizeText(blockText);
|
|
56
|
-
const eNorm = normalizeText(evText);
|
|
57
|
-
const inclusion = bNorm.length >= 10 && eNorm.includes(bNorm) ? 0.12 : 0;
|
|
58
|
-
// bonus heure (si tu l'as)
|
|
59
|
-
const timeBonus = timeProximityBonus(ev.startTime ?? null, blockText);
|
|
60
|
-
// combine: coverage domine
|
|
61
|
-
const score = Math.max(coverageBloc, jac) * 0.85 + Math.min(1, jac) * 0.1 + inclusion + timeBonus;
|
|
62
|
-
return Math.max(0, Math.min(1, score));
|
|
63
|
-
}
|
|
64
|
-
function intersectionSize(a, b) {
|
|
65
|
-
let inter = 0;
|
|
66
|
-
for (const x of a)
|
|
67
|
-
if (b.has(x))
|
|
68
|
-
inter++;
|
|
69
|
-
return inter;
|
|
70
|
-
}
|
|
71
|
-
function tokens(s) {
|
|
72
|
-
const stop = new Set(["de", "du", "des", "la", "le", "les", "et", "au", "aux", "sur", "en", "d", "l", "un", "une"]);
|
|
73
|
-
return new Set(normalizeText(s)
|
|
74
|
-
.split(" ")
|
|
75
|
-
.filter((w) => w.length >= 3 && !stop.has(w)));
|
|
76
|
-
}
|
|
77
|
-
function timeProximityBonus(eventStartISO, blockText) {
|
|
78
|
-
if (!eventStartISO)
|
|
79
|
-
return 0;
|
|
80
|
-
const dt = parseISO(eventStartISO);
|
|
81
|
-
if (!dt)
|
|
82
|
-
return 0;
|
|
83
|
-
const eventMin = dt.hour * 60 + dt.minute;
|
|
84
|
-
const hints = extractHourHints(blockText);
|
|
85
|
-
if (!hints.length)
|
|
86
|
-
return 0;
|
|
87
|
-
let best = Infinity;
|
|
88
|
-
for (const x of hints)
|
|
89
|
-
best = Math.min(best, Math.abs(x.h * 60 + x.m - eventMin));
|
|
90
|
-
if (best <= 30)
|
|
91
|
-
return 0.1;
|
|
92
|
-
if (best <= 60)
|
|
93
|
-
return 0.06;
|
|
94
|
-
if (best <= 120)
|
|
95
|
-
return 0.03;
|
|
96
|
-
return 0;
|
|
97
|
-
}
|
|
98
|
-
function extractHourHints(text) {
|
|
99
|
-
const t = (text || "").toLowerCase();
|
|
100
|
-
const out = [];
|
|
101
|
-
const re = /\b(\d{1,2})\s*h\s*(\d{2})\b/g;
|
|
102
|
-
let m;
|
|
103
|
-
while ((m = re.exec(t)))
|
|
104
|
-
out.push({ h: Number(m[1]), m: Number(m[2]) });
|
|
105
|
-
return out;
|
|
106
|
-
}
|
|
107
|
-
export function scoreVideo(agenda, agendaTs, sameOrg, videoTitle, videoEpoch, videoOrganes, timeAmbigious = false) {
|
|
108
|
-
const objetS = dice(agenda.objet || "", videoTitle || "");
|
|
109
|
-
const titleS = dice(agenda.titre || "", videoTitle || "");
|
|
110
|
-
const titleScore = Math.max(objetS, titleS);
|
|
111
|
-
let timeScore = 0;
|
|
112
|
-
if (agendaTs && videoEpoch) {
|
|
113
|
-
const deltaMin = Math.abs(videoEpoch - agendaTs) / 60;
|
|
114
|
-
timeScore = Math.exp(-deltaMin / 60);
|
|
115
|
-
}
|
|
116
|
-
let orgScore = 0;
|
|
117
|
-
if (agenda.organe && videoOrganes && videoOrganes.length) {
|
|
118
|
-
const agendaOrg = agenda.organe;
|
|
119
|
-
orgScore = Math.max(...videoOrganes.map((v) => dice(agendaOrg, v)));
|
|
120
|
-
}
|
|
121
|
-
if (timeAmbigious) {
|
|
122
|
-
return 0.6 * titleScore + (sameOrg ? 0.4 : orgScore * 0.4);
|
|
123
|
-
}
|
|
124
|
-
return 0.3 * titleScore + 0.3 * timeScore + (sameOrg ? 0.4 : orgScore * 0.4);
|
|
125
|
-
}
|
|
126
|
-
function tokensDice(s) {
|
|
127
|
-
return normalize(s).split(" ").filter(Boolean);
|
|
128
|
-
}
|
|
129
|
-
export function dice(a, b) {
|
|
130
|
-
const A = new Set(tokensDice(a)), B = new Set(tokensDice(b));
|
|
131
|
-
if (!A.size || !B.size)
|
|
132
|
-
return 0;
|
|
133
|
-
let inter = 0;
|
|
134
|
-
for (const t of A)
|
|
135
|
-
if (B.has(t))
|
|
136
|
-
inter++;
|
|
137
|
-
return (2 * inter) / (A.size + B.size);
|
|
138
|
-
}
|
|
139
|
-
export function normalize(s) {
|
|
140
|
-
return (s ?? "")
|
|
141
|
-
.toLowerCase()
|
|
142
|
-
.normalize("NFD")
|
|
143
|
-
.replace(/[\u0300-\u036f]/g, "")
|
|
144
|
-
.replace(/[^\p{L}\p{N}\s-]/gu, " ")
|
|
145
|
-
.replace(/\s+/g, " ")
|
|
146
|
-
.trim();
|
|
147
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare function normalizeText(t: string): string;
|
|
2
|
-
export declare function decodeHtmlEntities(s?: string | null): string;
|
|
3
|
-
export declare function stripTrailingPunct(s: string): string;
|
|
4
|
-
export declare function dedupeSpeaker(raw: string): string;
|
|
5
|
-
export declare function fixApostrophes(s: string): string;
|
|
6
|
-
export declare function norm(s?: string | null): string;
|
|
7
|
-
export declare function normalizeSpaces(s: string): string;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export function normalizeText(t) {
|
|
2
|
-
return (t || "")
|
|
3
|
-
.toLowerCase()
|
|
4
|
-
.normalize("NFD")
|
|
5
|
-
.replace(/\p{Diacritic}/gu, "")
|
|
6
|
-
.replace(/[^a-z0-9\s]/g, " ")
|
|
7
|
-
.replace(/\s+/g, " ")
|
|
8
|
-
.trim();
|
|
9
|
-
}
|
|
10
|
-
export function decodeHtmlEntities(s) {
|
|
11
|
-
if (!s)
|
|
12
|
-
return "";
|
|
13
|
-
return s
|
|
14
|
-
.replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))
|
|
15
|
-
.replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))
|
|
16
|
-
.replace(/&/g, "&")
|
|
17
|
-
.replace(/</g, "<")
|
|
18
|
-
.replace(/>/g, ">")
|
|
19
|
-
.replace(/"/g, '"')
|
|
20
|
-
.replace(/'/g, "'");
|
|
21
|
-
}
|
|
22
|
-
export function stripTrailingPunct(s) {
|
|
23
|
-
return s.replace(/\s*([:,.;])\s*$/u, "").trim();
|
|
24
|
-
}
|
|
25
|
-
export function dedupeSpeaker(raw) {
|
|
26
|
-
let s = norm(raw);
|
|
27
|
-
s = stripTrailingPunct(s);
|
|
28
|
-
const dupPatterns = [/^(.+?)\s*[.]\s*\1$/u, /^(.+?)\s*,\s*\1,?$/u, /^(.+?)\s+\1$/u];
|
|
29
|
-
for (const re of dupPatterns) {
|
|
30
|
-
const m = s.match(re);
|
|
31
|
-
if (m) {
|
|
32
|
-
s = m[1];
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return s.replace(/\.\s*$/, "");
|
|
37
|
-
}
|
|
38
|
-
export function fixApostrophes(s) {
|
|
39
|
-
let out = s;
|
|
40
|
-
out = out.replace(/\s*’\s*/g, "’");
|
|
41
|
-
out = out.replace(/\b([dljctmsn])\s*’/gi, (_, m) => m + "’");
|
|
42
|
-
out = out.replace(/’\s+([A-Za-zÀ-ÖØ-öø-ÿ])/g, "’$1");
|
|
43
|
-
out = out.replace(/\s+([,;:.!?])/g, "$1");
|
|
44
|
-
return out;
|
|
45
|
-
}
|
|
46
|
-
export function norm(s) {
|
|
47
|
-
return (s || "")
|
|
48
|
-
.replace(/\u00A0/g, " ")
|
|
49
|
-
.replace(/\s+/g, " ")
|
|
50
|
-
.trim();
|
|
51
|
-
}
|
|
52
|
-
export function normalizeSpaces(s) {
|
|
53
|
-
return s
|
|
54
|
-
.replace(/\u00A0/g, " ")
|
|
55
|
-
.replace(/\s+/g, " ")
|
|
56
|
-
.trim();
|
|
57
|
-
}
|
package/lib/validators/config.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
const dbSchema = z.object({
|
|
3
|
-
host: z.string().trim().min(1, "Must not be empty"),
|
|
4
|
-
password: z.string().trim().min(1, "Must not be empty"),
|
|
5
|
-
user: z.string().trim().min(1, "Must not be empty"),
|
|
6
|
-
port: z.coerce.number().int().min(0).max(65535),
|
|
7
|
-
});
|
|
8
|
-
export const configSchema = z.object({
|
|
9
|
-
db: dbSchema,
|
|
10
|
-
});
|