@tricoteuses/senat 3.1.11 → 3.1.12
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/config.d.ts +43 -0
- package/lib/src/config.js +37 -0
- package/lib/src/conversion_textes.d.ts +11 -0
- package/lib/src/conversion_textes.js +320 -0
- package/lib/src/databases_postgres.d.ts +4 -0
- package/lib/src/databases_postgres.js +23 -0
- package/lib/src/datasets.d.ts +38 -0
- package/lib/src/datasets.js +247 -0
- package/lib/src/git.d.ts +27 -0
- package/lib/src/git.js +251 -0
- package/lib/src/loaders.d.ts +52 -0
- package/lib/src/loaders.js +260 -0
- package/lib/src/model/agenda.d.ts +6 -0
- package/lib/src/model/agenda.js +148 -0
- package/lib/src/model/ameli.d.ts +67 -0
- package/lib/src/model/ameli.js +150 -0
- package/lib/src/model/commission.d.ts +19 -0
- package/lib/src/model/commission.js +269 -0
- package/lib/src/model/debats.d.ts +39 -0
- package/lib/src/model/debats.js +112 -0
- package/lib/src/model/documents.d.ts +32 -0
- package/lib/src/model/documents.js +182 -0
- package/lib/src/model/dosleg.d.ts +144 -0
- package/lib/src/model/dosleg.js +468 -0
- package/lib/src/model/index.d.ts +7 -0
- package/lib/src/model/index.js +7 -0
- package/lib/src/model/questions.d.ts +54 -0
- package/lib/src/model/questions.js +91 -0
- package/lib/src/model/scrutins.d.ts +48 -0
- package/lib/src/model/scrutins.js +121 -0
- package/lib/src/model/seance.d.ts +3 -0
- package/lib/src/model/seance.js +267 -0
- package/lib/src/model/sens.d.ts +112 -0
- package/lib/src/model/sens.js +385 -0
- package/lib/src/model/util.d.ts +1 -0
- package/lib/src/model/util.js +15 -0
- package/lib/src/raw_types/ameli.d.ts +1762 -0
- package/lib/src/raw_types/ameli.js +1074 -0
- package/lib/src/raw_types/debats.d.ts +380 -0
- package/lib/src/raw_types/debats.js +266 -0
- package/lib/src/raw_types/dosleg.d.ts +2954 -0
- package/lib/src/raw_types/dosleg.js +2005 -0
- package/lib/src/raw_types/questions.d.ts +699 -0
- package/lib/src/raw_types/questions.js +493 -0
- package/lib/src/raw_types/sens.d.ts +7843 -0
- package/lib/src/raw_types/sens.js +4691 -0
- package/lib/src/raw_types_schemats/ameli.d.ts +541 -0
- package/lib/src/raw_types_schemats/ameli.js +2 -0
- package/lib/src/raw_types_schemats/debats.d.ts +127 -0
- package/lib/src/raw_types_schemats/debats.js +2 -0
- package/lib/src/raw_types_schemats/dosleg.d.ts +977 -0
- package/lib/src/raw_types_schemats/dosleg.js +2 -0
- package/lib/src/raw_types_schemats/questions.d.ts +237 -0
- package/lib/src/raw_types_schemats/questions.js +2 -0
- package/lib/src/raw_types_schemats/sens.d.ts +2709 -0
- package/lib/src/raw_types_schemats/sens.js +2 -0
- package/lib/src/rich_types/dosleg.d.ts +74 -0
- package/lib/src/rich_types/dosleg.js +29 -11
- package/lib/src/scripts/debug_dosleg_query.d.ts +6 -0
- package/lib/src/scripts/debug_dosleg_query.js +50 -0
- package/lib/src/scripts/retrieve_agenda.js +6 -6
- package/lib/src/server/dosleg.js +170 -3
- package/lib/src/types/agenda.d.ts +45 -0
- package/lib/src/types/agenda.js +1 -0
- package/lib/src/types/ameli.d.ts +5 -0
- package/lib/src/types/ameli.js +1 -0
- package/lib/src/types/compte_rendu.d.ts +83 -0
- package/lib/src/types/compte_rendu.js +1 -0
- package/lib/src/types/debats.d.ts +2 -0
- package/lib/src/types/debats.js +1 -0
- package/lib/src/types/dosleg.d.ts +70 -0
- package/lib/src/types/dosleg.js +1 -0
- package/lib/src/types/questions.d.ts +2 -0
- package/lib/src/types/questions.js +1 -0
- package/lib/src/types/sens.d.ts +8 -0
- package/lib/src/types/sens.js +1 -0
- package/lib/src/types/sessions.d.ts +6 -0
- package/lib/src/types/sessions.js +19 -0
- package/lib/src/types/texte.d.ts +72 -0
- package/lib/src/types/texte.js +15 -0
- package/lib/src/utils/reunion_odj_building.d.ts +7 -1
- package/lib/src/utils/reunion_odj_building.js +144 -21
- package/lib/src/utils/reunion_parsing.d.ts +3 -2
- package/lib/src/utils/reunion_parsing.js +4 -4
- package/lib/src/validators/config.d.ts +9 -0
- package/lib/src/validators/config.js +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Ass, Aud as AudRaw, Auteur as AuteurRaw, DateSeance as DateSeanceRaw, Deccoc, Denrap, Docatt as DocattRaw, Ecr as EcrRaw, Etaloi, Lecass as LecassRaw, Lecassrap as LecassrapRaw, Lecture as LectureRaw, Loi as LoiRaw, Org, Oritxt, Qua, Rap as RapRaw, Raporg, Scr, Texte as TexteRaw, Typatt, Typlec, Typloi, Typtxt, Typurl } from "../raw_types/dosleg.js";
|
|
2
|
+
import { TxtAmeli } from "../raw_types/ameli.js";
|
|
3
|
+
import { Debats } from "../raw_types/debats.js";
|
|
4
|
+
import { TxtAmeliCustom } from "./ameli.js";
|
|
5
|
+
export type { Deccoc as DecCoc, Denrap as DenRap, Etaloi as EtaLoi, Oritxt as OriTxt, Raporg as RapOrg, Typatt as TypAtt, Typlec as TypLec, Typloi as TypLoi, Typtxt as TypTxt, Typurl as TypUrl, };
|
|
6
|
+
export interface Aud extends AudRaw {
|
|
7
|
+
org?: Org;
|
|
8
|
+
}
|
|
9
|
+
export interface Auteur extends AuteurRaw {
|
|
10
|
+
qua?: Qua;
|
|
11
|
+
}
|
|
12
|
+
export interface DateSeance extends DateSeanceRaw {
|
|
13
|
+
debat?: Debats;
|
|
14
|
+
scrids?: string[];
|
|
15
|
+
scrs?: Scr[];
|
|
16
|
+
}
|
|
17
|
+
export interface DocAtt extends DocattRaw {
|
|
18
|
+
rap?: RapRaw;
|
|
19
|
+
typatt?: Typatt;
|
|
20
|
+
}
|
|
21
|
+
export interface Ecr extends EcrRaw {
|
|
22
|
+
aut?: Auteur;
|
|
23
|
+
}
|
|
24
|
+
export interface LecAss extends LecassRaw {
|
|
25
|
+
ass?: Ass;
|
|
26
|
+
auds?: Aud[];
|
|
27
|
+
audcles?: AudRaw["audcle"][];
|
|
28
|
+
datesSeances?: DateSeance[];
|
|
29
|
+
datesSeancesCodes?: DateSeanceRaw["code"][];
|
|
30
|
+
debatdatseas: Date[];
|
|
31
|
+
lecassraps?: LecAssRap[];
|
|
32
|
+
lecassrapids?: string[];
|
|
33
|
+
org?: Org;
|
|
34
|
+
texcods: TexteRaw["texcod"][];
|
|
35
|
+
textes?: Texte[];
|
|
36
|
+
}
|
|
37
|
+
export interface LecAssRap extends LecassrapRaw {
|
|
38
|
+
rap?: RapRaw;
|
|
39
|
+
}
|
|
40
|
+
export interface Lecture extends LectureRaw {
|
|
41
|
+
typlec?: Typlec;
|
|
42
|
+
lecassidts?: LecassRaw["lecassidt"][];
|
|
43
|
+
lecasss?: LecAss[];
|
|
44
|
+
}
|
|
45
|
+
export interface Loi extends LoiRaw {
|
|
46
|
+
deccoc?: Deccoc;
|
|
47
|
+
etaloi?: Etaloi;
|
|
48
|
+
lecidts?: LectureRaw["lecidt"][];
|
|
49
|
+
lectures?: Lecture[];
|
|
50
|
+
typloi?: Typloi;
|
|
51
|
+
}
|
|
52
|
+
export interface Rap extends RapRaw {
|
|
53
|
+
denrap?: Denrap;
|
|
54
|
+
docattcles?: DocattRaw["docattcle"][];
|
|
55
|
+
docatts?: DocAtt[];
|
|
56
|
+
ecrnums?: EcrRaw["ecrnum"][];
|
|
57
|
+
ecrs?: Ecr[];
|
|
58
|
+
orgcods?: Raporg["orgcod"][];
|
|
59
|
+
orgs?: Org[];
|
|
60
|
+
}
|
|
61
|
+
export interface Texte extends TexteRaw {
|
|
62
|
+
ecrs?: Ecr[];
|
|
63
|
+
ecrnums?: EcrRaw["ecrnum"][];
|
|
64
|
+
libtypurl?: Typurl["libtypurl"];
|
|
65
|
+
org?: Org;
|
|
66
|
+
oritxt: Oritxt;
|
|
67
|
+
typtxt?: Typtxt;
|
|
68
|
+
txtAmeli?: TxtAmeliCustom;
|
|
69
|
+
txtAmeliId: TxtAmeli["id"];
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const UNDEFINED_SESSION = 0;
|
|
2
|
+
export declare const sessionOptions: readonly [1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026];
|
|
3
|
+
export declare const sessionOptionsOrAll: readonly [0, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026];
|
|
4
|
+
export type Session = (typeof sessionOptions)[number];
|
|
5
|
+
export type SessionOrAll = (typeof sessionOptionsOrAll)[number];
|
|
6
|
+
export declare function getSessionsFromStart(startSession: SessionOrAll): (1958 | 1959 | 1960 | 1961 | 1962 | 1963 | 1964 | 1965 | 1966 | 1967 | 1968 | 1969 | 1970 | 1971 | 1972 | 1973 | 1974 | 1975 | 1976 | 1977 | 1978 | 1979 | 1980 | 1981 | 1982 | 1983 | 1984 | 1985 | 1986 | 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | 1993 | 1994 | 1995 | 1996 | 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 | 2026)[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const UNDEFINED_SESSION = 0;
|
|
2
|
+
export const sessionOptions = [
|
|
3
|
+
1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976,
|
|
4
|
+
1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
|
|
5
|
+
1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
|
|
6
|
+
2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026,
|
|
7
|
+
// TO COMPLETE EVERY YEAR :)
|
|
8
|
+
];
|
|
9
|
+
export const sessionOptionsOrAll = [UNDEFINED_SESSION, ...sessionOptions];
|
|
10
|
+
export function getSessionsFromStart(startSession) {
|
|
11
|
+
if (startSession === UNDEFINED_SESSION) {
|
|
12
|
+
return Array.from(sessionOptions);
|
|
13
|
+
}
|
|
14
|
+
const sessionIndex = sessionOptions.findIndex((session) => startSession === session);
|
|
15
|
+
if (sessionIndex >= 0) {
|
|
16
|
+
return sessionOptions.slice(sessionIndex);
|
|
17
|
+
}
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DocumentResult } from "../loaders.js";
|
|
2
|
+
export declare enum DivisionType {
|
|
3
|
+
tome = 1,
|
|
4
|
+
part = 2,
|
|
5
|
+
book = 3,
|
|
6
|
+
title = 4,
|
|
7
|
+
subtitle = 5,
|
|
8
|
+
chapter = 6,
|
|
9
|
+
section = 7,
|
|
10
|
+
subsection = 8,
|
|
11
|
+
paragraph = 9,
|
|
12
|
+
article = 10,
|
|
13
|
+
alinea = 11,
|
|
14
|
+
division = 12
|
|
15
|
+
}
|
|
16
|
+
export type DivisionTag = keyof typeof DivisionType;
|
|
17
|
+
export interface DocumentMetadata {
|
|
18
|
+
name: string;
|
|
19
|
+
session: number | null | undefined;
|
|
20
|
+
date?: string | null;
|
|
21
|
+
url_expose_des_motifs?: URL;
|
|
22
|
+
url_xml?: URL;
|
|
23
|
+
url_html: URL;
|
|
24
|
+
url_pdf: URL;
|
|
25
|
+
}
|
|
26
|
+
export interface FlatTexte extends Partial<DocumentResult> {
|
|
27
|
+
titre: string | null;
|
|
28
|
+
titre_court: string | null;
|
|
29
|
+
url_dossier_senat: string | null;
|
|
30
|
+
url_dossier_assemblee: string | null;
|
|
31
|
+
date_presentation: Date | null;
|
|
32
|
+
date_depot: Date | null;
|
|
33
|
+
date_publication_xml: Date | null;
|
|
34
|
+
version: Version | null;
|
|
35
|
+
divisions: Division[];
|
|
36
|
+
expose_motifs?: ExposeDesMotifs | null;
|
|
37
|
+
}
|
|
38
|
+
export type Version = "RECT" | "RECT_BIS" | "RECT_TER" | "RECT_QUATER" | "RECT_QUINQUIES";
|
|
39
|
+
export interface Step {
|
|
40
|
+
eId: string;
|
|
41
|
+
date: Date | null;
|
|
42
|
+
type: string | null;
|
|
43
|
+
session: string | null;
|
|
44
|
+
numero: string | null;
|
|
45
|
+
version: Version | null;
|
|
46
|
+
outcome: string | null;
|
|
47
|
+
}
|
|
48
|
+
export interface Division {
|
|
49
|
+
index: number;
|
|
50
|
+
eId: string;
|
|
51
|
+
tag: DivisionTag;
|
|
52
|
+
level: number;
|
|
53
|
+
headings: DivisionContent[];
|
|
54
|
+
}
|
|
55
|
+
export interface Article extends Division {
|
|
56
|
+
alineas: Alinea[];
|
|
57
|
+
}
|
|
58
|
+
export interface DivisionContent {
|
|
59
|
+
text: string | null;
|
|
60
|
+
html?: string | null;
|
|
61
|
+
}
|
|
62
|
+
export interface Alinea extends DivisionContent {
|
|
63
|
+
eId: string;
|
|
64
|
+
heading: DivisionContent;
|
|
65
|
+
pastille: string | null;
|
|
66
|
+
text_avec_liens?: string | null;
|
|
67
|
+
html_avec_liens?: string | null;
|
|
68
|
+
}
|
|
69
|
+
export interface ExposeDesMotifs {
|
|
70
|
+
text: string | null;
|
|
71
|
+
html: string | null;
|
|
72
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export var DivisionType;
|
|
2
|
+
(function (DivisionType) {
|
|
3
|
+
DivisionType[DivisionType["tome"] = 1] = "tome";
|
|
4
|
+
DivisionType[DivisionType["part"] = 2] = "part";
|
|
5
|
+
DivisionType[DivisionType["book"] = 3] = "book";
|
|
6
|
+
DivisionType[DivisionType["title"] = 4] = "title";
|
|
7
|
+
DivisionType[DivisionType["subtitle"] = 5] = "subtitle";
|
|
8
|
+
DivisionType[DivisionType["chapter"] = 6] = "chapter";
|
|
9
|
+
DivisionType[DivisionType["section"] = 7] = "section";
|
|
10
|
+
DivisionType[DivisionType["subsection"] = 8] = "subsection";
|
|
11
|
+
DivisionType[DivisionType["paragraph"] = 9] = "paragraph";
|
|
12
|
+
DivisionType[DivisionType["article"] = 10] = "article";
|
|
13
|
+
DivisionType[DivisionType["alinea"] = 11] = "alinea";
|
|
14
|
+
DivisionType[DivisionType["division"] = 12] = "division";
|
|
15
|
+
})(DivisionType || (DivisionType = {}));
|
|
@@ -4,6 +4,12 @@ import { AgendaEvent, ReunionOdj } from "../other_types/agenda.js";
|
|
|
4
4
|
type DossierWithActes = DossierLegislatifResult & {
|
|
5
5
|
actes_legislatifs?: ActeLegislatif[] | null;
|
|
6
6
|
};
|
|
7
|
-
export
|
|
7
|
+
export type DoslegReunionIndex = Map<string, {
|
|
8
|
+
signet: string;
|
|
9
|
+
dossier: DossierWithActes;
|
|
10
|
+
reunionTitre?: string;
|
|
11
|
+
}[]>;
|
|
12
|
+
export declare function buildOdj(events: AgendaEvent[], dossierBySenatUrl: Record<string, DossierWithActes>, doslegReunionIndex?: DoslegReunionIndex): ReunionOdj | undefined;
|
|
8
13
|
export declare function buildSenatDossierIndex(options: commandLineArgs.CommandLineOptions): Record<string, DossierWithActes>;
|
|
14
|
+
export declare function buildDoslegReunionIndex(options: commandLineArgs.CommandLineOptions): DoslegReunionIndex;
|
|
9
15
|
export {};
|
|
@@ -1,34 +1,111 @@
|
|
|
1
1
|
import { getSessionsFromStart } from "../other_types/sessions.js";
|
|
2
2
|
import { iterLoadSenatDossiersLegislatifs } from "../server/loaders.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
function normalizeOrganeForMatch(text) {
|
|
4
|
+
return (text || "")
|
|
5
|
+
.toLowerCase()
|
|
6
|
+
.normalize("NFD")
|
|
7
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
8
|
+
.replace(/[^a-z0-9\s]/g, " ")
|
|
9
|
+
.replace(/\s+/g, " ")
|
|
10
|
+
.trim();
|
|
11
|
+
}
|
|
12
|
+
function buildReunionLookupKey(date, organe) {
|
|
13
|
+
const d = (date || "").split("T")[0];
|
|
14
|
+
const o = normalizeOrganeForMatch(organe || "");
|
|
15
|
+
return `${d}|${o}`;
|
|
16
|
+
}
|
|
17
|
+
export function buildOdj(events, dossierBySenatUrl, doslegReunionIndex) {
|
|
18
|
+
const byObjet = new Map();
|
|
19
|
+
let lastCodeEtape = null;
|
|
20
|
+
let lastFlatActes;
|
|
7
21
|
for (const ev of events) {
|
|
8
22
|
const objetKey = (ev.objet ?? "").trim();
|
|
9
23
|
const url = normalizeSenatUrl(ev.urlDossierSenat) ?? undefined;
|
|
10
|
-
dossier = url ? dossierBySenatUrl[url] : null;
|
|
24
|
+
let dossier = url ? (dossierBySenatUrl[url] ?? null) : null;
|
|
25
|
+
if (!dossier && doslegReunionIndex) {
|
|
26
|
+
dossier = lookupDossierFromDoslegReunion(ev, doslegReunionIndex);
|
|
27
|
+
}
|
|
11
28
|
const dossierUid = dossier ? pickDossierUid(dossier) : undefined;
|
|
12
|
-
|
|
13
|
-
|
|
29
|
+
if (dossier) {
|
|
30
|
+
lastFlatActes = buildFlatActes(dossier);
|
|
31
|
+
lastCodeEtape = computeCodeEtape(ev, dossier, lastFlatActes);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
lastFlatActes = undefined;
|
|
35
|
+
lastCodeEtape = null;
|
|
36
|
+
}
|
|
14
37
|
if (!objetKey && !dossierUid)
|
|
15
38
|
continue;
|
|
16
|
-
if (!byObjet.has(objetKey) && dossierUid) {
|
|
17
|
-
byObjet.set(objetKey, dossierUid);
|
|
39
|
+
if (!byObjet.has(objetKey) && dossierUid && dossier) {
|
|
40
|
+
byObjet.set(objetKey, { dossierUid, dossier, flatActes: lastFlatActes });
|
|
18
41
|
}
|
|
19
42
|
}
|
|
20
43
|
if (byObjet.size === 0)
|
|
21
44
|
return undefined;
|
|
22
45
|
const pointsOdj = [];
|
|
23
|
-
for (const [objetKey, dossierUid] of byObjet) {
|
|
46
|
+
for (const [objetKey, { dossierUid, dossier: matchedDossier, flatActes }] of byObjet) {
|
|
47
|
+
const matchedCodeEtape = lastCodeEtape ?? (matchedDossier ? computeCodeEtapeFromFirstAct(matchedDossier, flatActes) : null);
|
|
24
48
|
pointsOdj.push({
|
|
25
49
|
objet: objetKey || null,
|
|
26
50
|
dossierLegislatifRef: dossierUid || null,
|
|
27
|
-
codeEtape,
|
|
51
|
+
codeEtape: matchedCodeEtape,
|
|
28
52
|
});
|
|
29
53
|
}
|
|
30
54
|
return { pointsOdj };
|
|
31
55
|
}
|
|
56
|
+
function scoreCandidate(c, evText) {
|
|
57
|
+
let score = 0;
|
|
58
|
+
const dosSignet = c.signet.toLowerCase();
|
|
59
|
+
if (evText.includes(dosSignet))
|
|
60
|
+
score += 10;
|
|
61
|
+
if (c.reunionTitre) {
|
|
62
|
+
const reunionWords = normalizeOrganeForMatch(c.reunionTitre).split(/\s+/).filter((w) => w.length >= 5);
|
|
63
|
+
score += reunionWords.filter((w) => evText.includes(w)).length;
|
|
64
|
+
}
|
|
65
|
+
const dossierTitre = normalizeOrganeForMatch(c.dossier.titre ?? "");
|
|
66
|
+
if (dossierTitre && evText.includes(dossierTitre))
|
|
67
|
+
score += 5;
|
|
68
|
+
if (dossierTitre) {
|
|
69
|
+
const words = dossierTitre.split(/\s+/).filter((w) => w.length >= 5);
|
|
70
|
+
score += words.filter((w) => evText.includes(w)).length;
|
|
71
|
+
}
|
|
72
|
+
return score;
|
|
73
|
+
}
|
|
74
|
+
function bestMatch(candidates, evText) {
|
|
75
|
+
if (candidates.length === 0)
|
|
76
|
+
return null;
|
|
77
|
+
const scored = candidates.map((c) => ({ c, score: scoreCandidate(c, evText) }));
|
|
78
|
+
scored.sort((a, b) => b.score - a.score);
|
|
79
|
+
return scored[0].score >= 3 ? scored[0].c.dossier : null;
|
|
80
|
+
}
|
|
81
|
+
function lookupDossierFromDoslegReunion(ev, index) {
|
|
82
|
+
const evDate = (ev.date || "").split("T")[0];
|
|
83
|
+
const evOrgane = normalizeOrganeForMatch(ev.organe ?? "");
|
|
84
|
+
const key = buildReunionLookupKey(evDate, ev.organe);
|
|
85
|
+
const evText = normalizeOrganeForMatch((ev.titre ?? "") + " " + (ev.objet ?? ""));
|
|
86
|
+
const candidates = index.get(key);
|
|
87
|
+
if (candidates && candidates.length > 0) {
|
|
88
|
+
return bestMatch(candidates, evText);
|
|
89
|
+
}
|
|
90
|
+
const dateOnlyCandidates = [];
|
|
91
|
+
for (const [k, v] of index) {
|
|
92
|
+
if (k.startsWith(evDate + "|") && k !== key) {
|
|
93
|
+
const kOrgane = k.split("|")[1] || "";
|
|
94
|
+
if (evOrgane && kOrgane && (evOrgane.includes(kOrgane) || kOrgane.includes(evOrgane))) {
|
|
95
|
+
dateOnlyCandidates.push(...v);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return bestMatch(dateOnlyCandidates, evText);
|
|
100
|
+
}
|
|
101
|
+
function computeCodeEtapeFromFirstAct(dossier, flatActes) {
|
|
102
|
+
const flat = flatActes ?? buildFlatActes(dossier);
|
|
103
|
+
if (flat.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
const cleanCode = (code) => code.replace(/-SEANCE$/, "");
|
|
106
|
+
flat.sort((a, b) => a.date.localeCompare(b.date) || a.codeActe.length - b.codeActe.length);
|
|
107
|
+
return cleanCode(flat[0].codeActe);
|
|
108
|
+
}
|
|
32
109
|
function pickDossierUid(d) {
|
|
33
110
|
if (d["signet"] && d["signet"].trim())
|
|
34
111
|
return d["signet"].trim();
|
|
@@ -62,6 +139,44 @@ export function buildSenatDossierIndex(options) {
|
|
|
62
139
|
}
|
|
63
140
|
return index;
|
|
64
141
|
}
|
|
142
|
+
export function buildDoslegReunionIndex(options) {
|
|
143
|
+
const index = new Map();
|
|
144
|
+
const sessions = getSessionsFromStart(2015);
|
|
145
|
+
for (const session of sessions) {
|
|
146
|
+
for (const item of iterLoadSenatDossiersLegislatifs(options["dataDir"], session)) {
|
|
147
|
+
const dossier = item.item;
|
|
148
|
+
const signet = dossier.signet?.trim();
|
|
149
|
+
if (!signet)
|
|
150
|
+
continue;
|
|
151
|
+
const lectures = dossier.lectures ?? [];
|
|
152
|
+
for (const lecture of lectures) {
|
|
153
|
+
const lecturesAssemblee = lecture.lectures_assemblee ?? [];
|
|
154
|
+
for (const lecAss of lecturesAssemblee) {
|
|
155
|
+
if (lecAss.assemblee !== "Sénat")
|
|
156
|
+
continue;
|
|
157
|
+
const commissions = lecAss.commissions_saisies ?? [];
|
|
158
|
+
for (const commission of commissions) {
|
|
159
|
+
const reunions = commission.reunions ?? [];
|
|
160
|
+
for (const reunion of reunions) {
|
|
161
|
+
if (!reunion.date)
|
|
162
|
+
continue;
|
|
163
|
+
const key = buildReunionLookupKey(reunion.date, commission.libelle_organisme);
|
|
164
|
+
let entries = index.get(key);
|
|
165
|
+
if (!entries) {
|
|
166
|
+
entries = [];
|
|
167
|
+
index.set(key, entries);
|
|
168
|
+
}
|
|
169
|
+
if (!entries.some((e) => e.signet === signet)) {
|
|
170
|
+
entries.push({ signet, dossier, reunionTitre: reunion.titre ?? undefined });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return index;
|
|
179
|
+
}
|
|
65
180
|
function detectLecture(objet) {
|
|
66
181
|
objet = objet.toLowerCase();
|
|
67
182
|
if (objet.includes("première lecture"))
|
|
@@ -72,18 +187,17 @@ function detectLecture(objet) {
|
|
|
72
187
|
return 3;
|
|
73
188
|
return undefined;
|
|
74
189
|
}
|
|
75
|
-
function computeCodeEtape(ev, dossier) {
|
|
76
|
-
// In order to match with stage, we need to remove the '-SEANCE' suffix from the codeActe
|
|
190
|
+
function computeCodeEtape(ev, dossier, flatActes) {
|
|
77
191
|
const cleanCode = (code) => code.replace(/-SEANCE$/, "");
|
|
78
192
|
const lecture = detectLecture(ev.objet ?? "");
|
|
79
|
-
const
|
|
80
|
-
const nature =
|
|
193
|
+
const organeLower = (ev.organe ?? "").toLowerCase();
|
|
194
|
+
const nature = organeLower.includes("commission")
|
|
81
195
|
? "COM"
|
|
82
|
-
:
|
|
196
|
+
: organeLower.includes("séance publique")
|
|
83
197
|
? "DEBATS"
|
|
84
198
|
: "";
|
|
85
199
|
const evDate = ev.date.split("T")[0];
|
|
86
|
-
const flat = buildFlatActes(dossier);
|
|
200
|
+
const flat = flatActes ?? buildFlatActes(dossier);
|
|
87
201
|
// 1) Strict matching: same date + same nature
|
|
88
202
|
let candidates = flat.filter((a) => {
|
|
89
203
|
if (a.date !== evDate)
|
|
@@ -105,17 +219,26 @@ function computeCodeEtape(ev, dossier) {
|
|
|
105
219
|
return cleanCode(candidates[0].codeActe);
|
|
106
220
|
}
|
|
107
221
|
// 2) Fallback COM: If no exact date match for a commission event,
|
|
108
|
-
//
|
|
222
|
+
// find the nearest commission act for this lecture (before or after the event date).
|
|
109
223
|
if (nature === "COM") {
|
|
110
|
-
let comActs = flat.filter((a) => a.codeActe.includes("COM")
|
|
224
|
+
let comActs = flat.filter((a) => a.codeActe.includes("COM"));
|
|
111
225
|
if (lecture !== undefined) {
|
|
112
226
|
const byLecture = comActs.filter((a) => a.ordreLecture === lecture);
|
|
113
227
|
if (byLecture.length > 0)
|
|
114
228
|
comActs = byLecture;
|
|
115
229
|
}
|
|
116
230
|
if (comActs.length > 0) {
|
|
117
|
-
|
|
118
|
-
|
|
231
|
+
// Prefer acts on or before the event date (latest first), then closest after
|
|
232
|
+
const before = comActs.filter((a) => a.date <= evDate);
|
|
233
|
+
const after = comActs.filter((a) => a.date > evDate);
|
|
234
|
+
if (before.length > 0) {
|
|
235
|
+
before.sort((a, b) => b.date.localeCompare(a.date) || b.codeActe.length - a.codeActe.length);
|
|
236
|
+
return cleanCode(before[0].codeActe);
|
|
237
|
+
}
|
|
238
|
+
if (after.length > 0) {
|
|
239
|
+
after.sort((a, b) => a.date.localeCompare(b.date) || a.codeActe.length - b.codeActe.length);
|
|
240
|
+
return cleanCode(after[0].codeActe);
|
|
241
|
+
}
|
|
119
242
|
}
|
|
120
243
|
}
|
|
121
244
|
// 3) Fallback general lecture: if nothing else worked but a lecture is identified,
|
|
@@ -2,12 +2,13 @@ import { DateTime } from "luxon";
|
|
|
2
2
|
import type { AnyNode } from "domhandler";
|
|
3
3
|
import { AgendaEvent, Reunion } from "../other_types/agenda.js";
|
|
4
4
|
import { DossierLegislatifResult } from "../rich_types/dosleg.js";
|
|
5
|
+
import { DoslegReunionIndex } from "./reunion_odj_building.js";
|
|
5
6
|
import * as cheerio from "cheerio";
|
|
6
7
|
type KnownType = "SP" | "COM" | "MC" | "OD" | "ID";
|
|
7
|
-
type DossierBySenatUrl = Record<string, DossierLegislatifResult>;
|
|
8
8
|
type ReunionBucket = "IDS" | "IDC" | "IDM" | "IDO" | "IDI";
|
|
9
9
|
type ReunionsByBucket = Record<ReunionBucket, Reunion[]>;
|
|
10
|
-
|
|
10
|
+
type DossierBySenatUrl = Record<string, DossierLegislatifResult>;
|
|
11
|
+
export declare function buildReunionsByBucket(events: AgendaEvent[], dossierBySenatUrl: DossierBySenatUrl, doslegReunionIndex?: DoslegReunionIndex): ReunionsByBucket;
|
|
11
12
|
export declare function makeReunionUid(dateISO: string, kind: KnownType, agendaEventId: string, organe?: string | null): string;
|
|
12
13
|
export declare function formatYYYYMMDD(dateYYYYMMDD: string): string;
|
|
13
14
|
export declare function deriveTimesForEvent(ev: AgendaEvent): {
|
|
@@ -22,7 +22,7 @@ const STOPWORDS = new Set([
|
|
|
22
22
|
"a",
|
|
23
23
|
"aux",
|
|
24
24
|
]);
|
|
25
|
-
function toReunion(e, dossierBySenatUrl, uid) {
|
|
25
|
+
function toReunion(e, dossierBySenatUrl, uid, doslegReunionIndex) {
|
|
26
26
|
const date = norm(e.date) ?? e.date;
|
|
27
27
|
const { startISO, endISO } = deriveTimesForEvent(e);
|
|
28
28
|
const startTime = startISO ?? e.startTime ?? null;
|
|
@@ -39,11 +39,11 @@ function toReunion(e, dossierBySenatUrl, uid) {
|
|
|
39
39
|
titre: e.titre,
|
|
40
40
|
objet: e.objet || "",
|
|
41
41
|
events: [e], // TODO remove
|
|
42
|
-
odj: buildOdj([e], dossierBySenatUrl),
|
|
42
|
+
odj: buildOdj([e], dossierBySenatUrl, doslegReunionIndex),
|
|
43
43
|
lieu: e.lieu || undefined,
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
export function buildReunionsByBucket(events, dossierBySenatUrl) {
|
|
46
|
+
export function buildReunionsByBucket(events, dossierBySenatUrl, doslegReunionIndex) {
|
|
47
47
|
const out = { IDS: [], IDC: [], IDM: [], IDO: [], IDI: [] };
|
|
48
48
|
if (!events?.length)
|
|
49
49
|
return out;
|
|
@@ -55,7 +55,7 @@ export function buildReunionsByBucket(events, dossierBySenatUrl) {
|
|
|
55
55
|
}
|
|
56
56
|
const bucket = typeToSuffixStrict(kind);
|
|
57
57
|
const uid = makeReunionUid(e.date, kind, e.id, e.organe ?? null);
|
|
58
|
-
out[bucket].push(toReunion(e, dossierBySenatUrl, uid));
|
|
58
|
+
out[bucket].push(toReunion(e, dossierBySenatUrl, uid, doslegReunionIndex));
|
|
59
59
|
}
|
|
60
60
|
// Tri stable par bucket (date + heure, inconnus à la fin)
|
|
61
61
|
for (const k of Object.keys(out)) {
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
});
|