@tricoteuses/senat 2.10.5 → 2.11.1
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/databases.d.ts +1 -28
- package/lib/databases.js +0 -6
- package/lib/datasets.d.ts +6 -0
- package/lib/datasets.js +233 -0
- package/lib/loaders.d.ts +5 -0
- package/lib/loaders.js +14 -9
- package/lib/model/ameli.d.ts +31 -143
- package/lib/model/ameli.js +102 -95
- package/lib/model/commission.d.ts +5 -0
- package/lib/model/commission.js +263 -0
- package/lib/model/debats.d.ts +13 -51
- package/lib/model/documents.d.ts +2 -0
- package/lib/model/documents.js +37 -0
- package/lib/model/dosleg.d.ts +9 -104
- package/lib/model/dosleg.js +76 -108
- package/lib/model/index.d.ts +4 -2
- package/lib/model/index.js +4 -2
- package/lib/model/questions.d.ts +10 -458
- package/lib/model/scrutins.d.ts +3 -0
- package/lib/model/scrutins.js +74 -0
- package/lib/model/{compte_rendu.js → seance.js} +47 -28
- package/lib/model/sens.d.ts +28 -1002
- package/lib/model/sens.js +65 -33
- package/lib/model/util.d.ts +1 -0
- package/lib/model/util.js +19 -1
- package/lib/raw_types/ameli.d.ts +778 -1521
- package/lib/raw_types/ameli.js +5 -345
- package/lib/raw_types/debats.d.ts +163 -306
- package/lib/raw_types/debats.js +5 -84
- package/lib/raw_types/dosleg.d.ts +1349 -2293
- package/lib/raw_types/dosleg.js +5 -550
- package/lib/raw_types/questions.d.ts +374 -519
- package/lib/raw_types/questions.js +5 -84
- package/lib/raw_types/senat.d.ts +11389 -0
- package/lib/raw_types/senat.js +5 -0
- package/lib/raw_types/sens.d.ts +6729 -12571
- package/lib/raw_types/sens.js +5 -2944
- package/lib/raw_types_schemats/ameli.d.ts +2 -2
- package/lib/raw_types_schemats/debats.d.ts +2 -2
- package/lib/raw_types_schemats/dosleg.d.ts +2 -2
- package/lib/raw_types_schemats/questions.d.ts +2 -2
- package/lib/raw_types_schemats/sens.d.ts +2 -2
- package/lib/scripts/convert_data.js +37 -31
- package/lib/scripts/retrieve_cr_commission.d.ts +1 -0
- package/lib/scripts/retrieve_cr_commission.js +291 -0
- package/lib/scripts/{retrieve_comptes_rendus.js → retrieve_cr_seance.js} +1 -1
- package/lib/scripts/retrieve_open_data.js +35 -1
- package/lib/utils/cr_spliting.d.ts +22 -1
- package/lib/utils/cr_spliting.js +273 -12
- package/lib/utils/reunion_grouping.d.ts +3 -0
- package/lib/utils/reunion_grouping.js +1 -1
- package/package.json +12 -11
- /package/lib/model/{compte_rendu.d.ts → seance.d.ts} +0 -0
- /package/lib/scripts/{retrieve_comptes_rendus.d.ts → retrieve_cr_seance.d.ts} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AUTO-GENERATED FILE - DO NOT EDIT!
|
|
3
3
|
*
|
|
4
|
-
* This file was automatically generated by schemats v.2.
|
|
5
|
-
* $ schemats generate -c postgres://username:password@localhost:5433/
|
|
4
|
+
* This file was automatically generated by schemats v.2.10.5
|
|
5
|
+
* $ schemats generate -c postgres://username:password@localhost:5433/senat -t amd -t amdsen -t avicom -t avigvt -t cab -t com_ameli -t ent -t etatxt -t fbu -t grppol_ameli -t gvt -t intora -t irr -t lec_ameli -t mot -t nat -t orarol -t sai -t saisen -t sea -t sen_ameli -t ses -t sor -t sub -t txt_ameli -t typrect -t typses -t typsub -t w_nivrec -s ameli
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
export declare namespace amdFields {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AUTO-GENERATED FILE - DO NOT EDIT!
|
|
3
3
|
*
|
|
4
|
-
* This file was automatically generated by schemats v.2.
|
|
5
|
-
* $ schemats generate -c postgres://username:password@localhost:5433/
|
|
4
|
+
* This file was automatically generated by schemats v.2.10.5
|
|
5
|
+
* $ schemats generate -c postgres://username:password@localhost:5433/senat -t debats -t intdivers -t intpjl -t lecassdeb -t secdis -t secdivers -t syndeb -t typsec -s debats
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
export declare namespace debatsFields {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AUTO-GENERATED FILE - DO NOT EDIT!
|
|
3
3
|
*
|
|
4
|
-
* This file was automatically generated by schemats v.2.
|
|
5
|
-
* $ schemats generate -c postgres://username:password@localhost:5433/
|
|
4
|
+
* This file was automatically generated by schemats v.2.10.5
|
|
5
|
+
* $ schemats generate -c postgres://username:password@localhost:5433/senat -t amescr -t ass -t aud -t auteur -t ble -t catrap -t corscr -t date_seance -t deccoc -t denrap -t doc -t docatt -t docsea -t ecr -t etaloi -t evtsea -t forpub -t gen -t lecass -t lecassrap -t lecture -t lnkrap -t loi -t loithe -t natloi -t org -t orgnomhis -t orippr -t oritxt -t posvot -t qua -t rap -t raporg -t rapthe -t rolsig -t scr -t ses -t stavot -t texte -t texte_ancien -t the -t titsen -t typatt -t typaut -t typdoc -t typevtsea -t typlec -t typloi -t typorg -t typrap -t typtxt -t typurl -t votsen -s dosleg
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
export declare namespace amescrFields {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AUTO-GENERATED FILE - DO NOT EDIT!
|
|
3
3
|
*
|
|
4
|
-
* This file was automatically generated by schemats v.2.
|
|
5
|
-
* $ schemats generate -c postgres://username:password@localhost:5433/
|
|
4
|
+
* This file was automatically generated by schemats v.2.10.5
|
|
5
|
+
* $ schemats generate -c postgres://username:password@localhost:5433/senat -t etatquestion -t legquestion -t naturequestion -t sortquestion -t tam_ministeres -t tam_questions -t tam_reponses -t the -s questions
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
export declare namespace etatquestionFields {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AUTO-GENERATED FILE - DO NOT EDIT!
|
|
3
3
|
*
|
|
4
|
-
* This file was automatically generated by schemats v.2.
|
|
5
|
-
* $ schemats generate -c postgres://username:password@localhost:5433/
|
|
4
|
+
* This file was automatically generated by schemats v.2.10.5
|
|
5
|
+
* $ schemats generate -c postgres://username:password@localhost:5433/senat -t acr -t activite -t activite_audit -t activite_delegation -t activite_delegation_audit -t activite_loi -t activite_loi_audit -t activite_obligatoire -t activite_participant -t activite_participant_audit -t activite_senateur -t activite_senateur_audit -t activite_senateur_params -t activite_senateur_params_audit -t activites_liees -t activites_liees_audit -t actpro -t adhgrpsen -t adr -t adresse -t adrsen -t app -t assparint -t asster -t autgrpsen -t autorisation_profil -t autorisations -t avis_nomination_art13 -t basdes -t bur -t bur3r -t bur4r -t cad -t candid -t candidat -t candtodelete -t categorie_activite -t catpro -t catpro2e -t catterrit -t cible_categorie_periode -t cirdep -t com -t con -t cotgip -t csp -t cspfam -t databasechangelog -t databasechangeloglock -t delega -t derogation -t derogation_audit -t derogation_senateur -t derogation_senateur_audit -t design -t designoep -t designorg -t discou -t div -t dpt -t dpt_seuil_presence -t dptele -t dptele_files -t dptele_processing -t dptele_processing_type -t dpttypman -t droits_acces -t droits_acces_audit -t droits_type_derogation -t ele -t eleloc -t elucan -t eludep -t eludiv -t elueur -t elueur_apf -t elumet -t elureg -t elusen -t elusen2e -t elusen3r -t elusen4r -t elusencommu -t elusenpair -t eluter -t elutit -t eluvil -t etadebman -t etadebman3r -t etadebman4r -t etafinman -t etafinman3r -t etafinman4r -t etaprr -t etarpm -t etasen -t ext2e_bio -t ext2e_csp -t ext2e_mandats -t ext2e_minist -t extsencom_identite -t extsencom_mandat -t fonact_participant -t foncandid -t foncom -t fondelega -t fongrppol -t fongrpsen -t fonmemcom -t fonmemdelega -t fonmemextpar -t fonmemgrppol -t fonmemgrpsen -t fonmemorg -t fonorg -t grppol -t grppol4r -t grpsenami -t grpsenamiadh -t grpsenamiadhreq -t grpsenamiadhreqeta -t grpsenamiunadh -t grpsim -t gvt -t insee_pays2008 -t jhi_authority -t jhi_user -t jhi_user_authority -t lanetr -t libcom -t libdelega -t libgrppol -t libgrpsen -t liborg -t lisdptele -t mel -t memcom -t memcomsea -t memdelega -t memextpar -t memgrppol -t memgrpsen -t memorg -t met -t minind -t minist -t mis -t misetafin -t mismin -t misrapeta -t missen -t moddes -t mode_acces_elusenpair -t nation -t nationgrpsen -t nivlan -t org -t orgext -t orgextpres -t orgthe -t pairie_elusenpair -t parpol -t parpolglo -t participa -t pcs -t pcs24 -t pcs42 -t pcs8 -t pcscatpro -t per -t per_sen -t perapp -t periode_presence -t perpolglo -t perrol -t pj_justificatif -t pj_justificatif_audit -t plaind -t plan_table -t plsql_profiler_runs -t plsql_profiler_units -t poicon -t posvot -t presences_scrutin_surcharge -t presencesrevisionentity -t profil_applicatif -t qua -t rap_the -t reg -t reladr -t requetes_profil -t reslis -t resultat -t reu -t revchanges -t rne_mandat -t rne_mandat_diff -t rne_sen -t rne_sen_diff -t rne_type_mandat -t rol -t sal -t scr -t scrusoldelega -t sea -t sec -t sec2e -t secexe -t sen -t senbur -t senbur3r -t senbur4r -t sennom -t senpj -t sensim -t sentablenom -t senurl -t seuil_presence -t sirpas_elusen -t sirpas_fonmemcom -t sirpas_fonmemdelega -t sirpas_fonmemgrppol -t sirpas_memcom -t sirpas_memdelega -t sirpas_memgrppol -t sirpas_mvt -t sirpas_mvtcm -t sirpas_mvttri -t sirpas_sen -t sirpas_senbur -t sirpas_trf -t srv -t stajur -t stavot -t suspensiontravaux -t suspensiontravaux_audit -t sysage -t syscognos -t sysevt -t sysvar -t sysvar_sendev -t sysvar_senprod -t tapsenrevchanges -t tapsenrevisionentity -t telephone -t temval -t tenpol -t territ -t testoracle -t titele -t titelerne -t titmin -t titnob -t tmpsd -t toutes -t turelu -t typadr -t typapppol -t typbister -t typcandid -t type_activite -t type_activite_participant -t type_activite_rol -t type_activite_senateur -t type_categorie -t type_derogation -t type_droit_acces -t type_pj_justificatif -t type_rne_diff -t type_type_derogation -t typele -t typgrpsen -t typman -t typmin -t typmoddes -t typorg -t typorgext -t typparpol -t typpoicon -t typprs -t typprssta -t typscr -t typtel -t typurl -t typvoi -t uploaded_file -t uploaded_file_type -t validation -t validation_defview_profil -t validation_profil -t vercand -t verres -t votes -t zongeo -s sens
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
export declare namespace acrFields {
|
|
@@ -21,14 +21,14 @@ async function convertData() {
|
|
|
21
21
|
const enabledDatasets = getEnabledDatasets(options["categories"]);
|
|
22
22
|
console.time("data transformation time");
|
|
23
23
|
if (enabledDatasets & EnabledDatasets.Ameli) {
|
|
24
|
-
await convertDatasetAmeli(dataDir);
|
|
24
|
+
await convertDatasetAmeli(dataDir, options);
|
|
25
25
|
}
|
|
26
26
|
if (enabledDatasets & EnabledDatasets.Debats) {
|
|
27
|
-
await convertDatasetDebats(dataDir);
|
|
27
|
+
await convertDatasetDebats(dataDir, options);
|
|
28
28
|
}
|
|
29
29
|
if (enabledDatasets & EnabledDatasets.DosLeg) {
|
|
30
|
-
await convertDatasetDosLeg(dataDir);
|
|
31
|
-
await convertDatasetScrutins(dataDir);
|
|
30
|
+
await convertDatasetDosLeg(dataDir, options);
|
|
31
|
+
await convertDatasetScrutins(dataDir, options);
|
|
32
32
|
}
|
|
33
33
|
if (enabledDatasets & EnabledDatasets.Questions) {
|
|
34
34
|
await convertDatasetQuestions(dataDir);
|
|
@@ -40,27 +40,27 @@ async function convertData() {
|
|
|
40
40
|
console.timeEnd("data transformation time");
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
async function convertDatasetAmeli(dataDir) {
|
|
43
|
+
async function convertDatasetAmeli(dataDir, options) {
|
|
44
44
|
const dataset = datasets.ameli;
|
|
45
45
|
if (!options["silent"]) {
|
|
46
46
|
console.log(`Converting database ${dataset.database} data into files…`);
|
|
47
47
|
}
|
|
48
48
|
const ameliReorganizedRootDir = path.join(dataDir, dataset.database);
|
|
49
49
|
ensureAndClearDir(ameliReorganizedRootDir);
|
|
50
|
-
for await (const amendement of findAllAmendements()) {
|
|
50
|
+
for await (const amendement of findAllAmendements(options["fromSession"])) {
|
|
51
51
|
if (options["verbose"]) {
|
|
52
|
-
console.log(`Converting ${amendement
|
|
52
|
+
console.log(`Converting ${amendement["numero"]} file…`);
|
|
53
53
|
}
|
|
54
|
-
const session = String(amendement
|
|
55
|
-
const signetDossierLegislatif = amendement
|
|
56
|
-
`${amendement
|
|
54
|
+
const session = String(amendement["session"]) || UNDEFINED_SESSION;
|
|
55
|
+
const signetDossierLegislatif = amendement["signet_dossier_legislatif"] ||
|
|
56
|
+
`${amendement["nature_texte"]}-${amendement["numero_texte"]}`.toLowerCase();
|
|
57
57
|
const ameliReorganizedDir = path.join(ameliReorganizedRootDir, String(session), signetDossierLegislatif);
|
|
58
58
|
fs.ensureDirSync(ameliReorganizedDir);
|
|
59
|
-
const amendementFileName = `${amendement
|
|
59
|
+
const amendementFileName = `${amendement["numero"]}.json`;
|
|
60
60
|
fs.writeJSONSync(path.join(ameliReorganizedDir, amendementFileName), amendement, { spaces: 2 });
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
async function convertDatasetDebats(dataDir) {
|
|
63
|
+
async function convertDatasetDebats(dataDir, options) {
|
|
64
64
|
const dataset = datasets.debats;
|
|
65
65
|
if (!options["silent"]) {
|
|
66
66
|
console.log(`Converting database ${dataset.database} data into files…`);
|
|
@@ -74,6 +74,9 @@ async function convertDatasetDebats(dataDir) {
|
|
|
74
74
|
}
|
|
75
75
|
const enrichedDebat = await enrichDebat(debat, allAuteurs);
|
|
76
76
|
const session = getSessionFromDate(enrichedDebat.date_seance);
|
|
77
|
+
if (options["fromSession"] && session < options["fromSession"]) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
77
80
|
const debatsReorganizedDir = path.join(debatsReorganizedRootDir, String(session));
|
|
78
81
|
fs.ensureDirSync(debatsReorganizedDir);
|
|
79
82
|
const debatFileName = `${enrichedDebat.id}.json`;
|
|
@@ -84,12 +87,12 @@ async function enrichDebat(debat, auteurs) {
|
|
|
84
87
|
const enrichedDebat = { ...debat };
|
|
85
88
|
for (const section of enrichedDebat.sections) {
|
|
86
89
|
for (const intervention of section.interventions) {
|
|
87
|
-
intervention.auteur = findAuteur(intervention
|
|
90
|
+
intervention.auteur = findAuteur(intervention["auteur_code"], auteurs);
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
for (const section of enrichedDebat.sections_divers) {
|
|
91
94
|
for (const intervention of section.interventions) {
|
|
92
|
-
intervention.auteur = findAuteur(intervention
|
|
95
|
+
intervention.auteur = findAuteur(intervention["auteur_code"], auteurs);
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
98
|
return enrichedDebat;
|
|
@@ -97,7 +100,7 @@ async function enrichDebat(debat, auteurs) {
|
|
|
97
100
|
function findAuteur(auteurCode, auteurs) {
|
|
98
101
|
return auteurs.find(auteur => auteur.code === auteurCode);
|
|
99
102
|
}
|
|
100
|
-
async function convertDatasetDosLeg(dataDir) {
|
|
103
|
+
async function convertDatasetDosLeg(dataDir, options) {
|
|
101
104
|
const dataset = datasets.dosleg;
|
|
102
105
|
if (!options["silent"]) {
|
|
103
106
|
console.log(`Converting database ${dataset.database} data into files…`);
|
|
@@ -112,6 +115,9 @@ async function convertDatasetDosLeg(dataDir) {
|
|
|
112
115
|
}
|
|
113
116
|
let loiReorganizedDir = path.join(dossiersReorganizedDir, String(UNDEFINED_SESSION));
|
|
114
117
|
const session = getSessionFromSignet(loi["signet"]) || UNDEFINED_SESSION;
|
|
118
|
+
if (options["fromSession"] && session < options["fromSession"]) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
115
121
|
loiReorganizedDir = path.join(dossiersReorganizedDir, String(session));
|
|
116
122
|
fs.ensureDirSync(loiReorganizedDir);
|
|
117
123
|
const scrutinFileName = `${loi["signet"]}.json`;
|
|
@@ -122,22 +128,22 @@ async function convertDatasetDosLeg(dataDir) {
|
|
|
122
128
|
await convertTexteUrls(dataDir);
|
|
123
129
|
await convertRapportUrls(dataDir);
|
|
124
130
|
}
|
|
125
|
-
async function convertDatasetScrutins(dataDir) {
|
|
131
|
+
async function convertDatasetScrutins(dataDir, options) {
|
|
126
132
|
const dataset = datasets.dosleg;
|
|
127
133
|
if (!options["silent"]) {
|
|
128
|
-
console.log(`Converting database ${dataset.database}
|
|
134
|
+
console.log(`Converting database scrutins (${dataset.database}) data into files…`);
|
|
129
135
|
}
|
|
130
136
|
const scrutinsReorganizedDir = path.join(dataDir, SCRUTINS_FOLDER);
|
|
131
137
|
ensureAndClearDir(scrutinsReorganizedDir);
|
|
132
|
-
for await (const scrutin of findAllScrutins()) {
|
|
138
|
+
for await (const scrutin of findAllScrutins(options["fromSession"])) {
|
|
133
139
|
if (options["verbose"]) {
|
|
134
|
-
console.log(`Converting ${scrutin
|
|
140
|
+
console.log(`Converting ${scrutin["numero"]} file…`);
|
|
135
141
|
}
|
|
136
142
|
let scrutinReorganizedDir = path.join(scrutinsReorganizedDir, String(UNDEFINED_SESSION));
|
|
137
|
-
const session = scrutin
|
|
143
|
+
const session = scrutin["session"] || UNDEFINED_SESSION;
|
|
138
144
|
scrutinReorganizedDir = path.join(scrutinsReorganizedDir, String(session));
|
|
139
145
|
fs.ensureDirSync(scrutinReorganizedDir);
|
|
140
|
-
const scrutinFileName = `${scrutin
|
|
146
|
+
const scrutinFileName = `${scrutin["numero"]}.json`;
|
|
141
147
|
fs.writeJSONSync(path.join(scrutinReorganizedDir, scrutinFileName), scrutin, {
|
|
142
148
|
spaces: 2,
|
|
143
149
|
});
|
|
@@ -152,12 +158,12 @@ async function convertDatasetQuestions(dataDir) {
|
|
|
152
158
|
ensureAndClearDir(questionsReorganizedRootDir);
|
|
153
159
|
for await (const question of findAllQuestions()) {
|
|
154
160
|
if (options["verbose"]) {
|
|
155
|
-
console.log(`Converting ${question
|
|
161
|
+
console.log(`Converting ${question["reference"]} file…`);
|
|
156
162
|
}
|
|
157
|
-
const legislature = question
|
|
163
|
+
const legislature = question["legislature"] ? question["legislature"] : 0;
|
|
158
164
|
const questionReorganizedDir = path.join(questionsReorganizedRootDir, String(legislature));
|
|
159
165
|
fs.ensureDirSync(questionReorganizedDir);
|
|
160
|
-
const questionFileName = `${question
|
|
166
|
+
const questionFileName = `${question["reference"]}.json`;
|
|
161
167
|
fs.writeJSONSync(path.join(questionReorganizedDir, questionFileName), question, { spaces: 2 });
|
|
162
168
|
}
|
|
163
169
|
}
|
|
@@ -229,26 +235,26 @@ async function convertDatasetSens(dataDir) {
|
|
|
229
235
|
ensureAndClearDir(organismesReorganizedDir);
|
|
230
236
|
for await (const sen of findAllSens()) {
|
|
231
237
|
if (options["verbose"]) {
|
|
232
|
-
console.log(`Converting ${sen
|
|
238
|
+
console.log(`Converting ${sen["matricule"]} file…`);
|
|
233
239
|
}
|
|
234
|
-
const senFileName = `${sen
|
|
240
|
+
const senFileName = `${sen["matricule"]}.json`;
|
|
235
241
|
fs.writeJSONSync(path.join(senateursReorganizedDir, senFileName), sen, {
|
|
236
242
|
spaces: 2,
|
|
237
243
|
});
|
|
238
244
|
}
|
|
239
245
|
for await (const circonscription of findAllCirconscriptions()) {
|
|
240
246
|
if (options["verbose"]) {
|
|
241
|
-
console.log(`Converting ${circonscription
|
|
247
|
+
console.log(`Converting ${circonscription["identifiant"]} file…`);
|
|
242
248
|
}
|
|
243
|
-
const circonscriptionFileName = `${circonscription
|
|
249
|
+
const circonscriptionFileName = `${circonscription["identifiant"]}.json`;
|
|
244
250
|
fs.writeJSONSync(path.join(circonscriptionsReorganizedDir, circonscriptionFileName), circonscription, { spaces: 2 });
|
|
245
251
|
}
|
|
246
252
|
for await (const organisme of findAllOrganismes()) {
|
|
247
253
|
if (options["verbose"]) {
|
|
248
|
-
console.log(`Converting ${organisme
|
|
254
|
+
console.log(`Converting ${organisme["code"]} file…`);
|
|
249
255
|
}
|
|
250
|
-
const organismeFileName = `${organisme
|
|
251
|
-
const organismeDir = path.join(organismesReorganizedDir, organisme
|
|
256
|
+
const organismeFileName = `${organisme["code"]}.json`;
|
|
257
|
+
const organismeDir = path.join(organismesReorganizedDir, organisme["type_code"]);
|
|
252
258
|
fs.ensureDirSync(organismeDir);
|
|
253
259
|
fs.writeJSONSync(path.join(organismeDir, organismeFileName), organisme, { spaces: 2 });
|
|
254
260
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import assert from "assert";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import * as cheerio from "cheerio";
|
|
5
|
+
import { COMMISSION_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders";
|
|
6
|
+
import { createCommissionGroupIfMissing, loadCommissionAgendaForDate, parseCommissionMetadataFromHtml, } from "../utils/cr_spliting";
|
|
7
|
+
import { parseCommissionCRFromFile } from "../model/commission";
|
|
8
|
+
import commandLineArgs from "command-line-args";
|
|
9
|
+
import { commonOptions } from "./shared/cli_helpers";
|
|
10
|
+
import { sessionStartYearFromDate } from "../model/seance";
|
|
11
|
+
import { getSessionsFromStart } from "../types/sessions";
|
|
12
|
+
import { ensureAndClearDir, fetchWithRetry } from "./shared/util";
|
|
13
|
+
class CommissionCRDownloadError extends Error {
|
|
14
|
+
constructor(message, url) {
|
|
15
|
+
super(`An error occurred while retrieving Commission CR ${url}: ${message}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const optionsDefinitions = [
|
|
19
|
+
...commonOptions,
|
|
20
|
+
{ name: "concurrency", type: Number, defaultValue: 6, help: "Max parallel downloads" },
|
|
21
|
+
{ name: "politenessMs", type: Number, defaultValue: 150, help: "Delay per worker (ms)" },
|
|
22
|
+
{
|
|
23
|
+
help: "parse and convert comptes-rendus des débats into JSON",
|
|
24
|
+
name: "parseDebats",
|
|
25
|
+
type: Boolean,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
const options = commandLineArgs(optionsDefinitions);
|
|
29
|
+
const COMMISSION_HUBS = {
|
|
30
|
+
"affaires-etrangeres": [
|
|
31
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-etrangeres.html",
|
|
32
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-etrangeres_archives.html",
|
|
33
|
+
],
|
|
34
|
+
"affaires-economiques": [
|
|
35
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-economiques.html",
|
|
36
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-economiques_archives.html",
|
|
37
|
+
],
|
|
38
|
+
"amenagement-developpement-durable": [
|
|
39
|
+
"https://www.senat.fr/compte-rendu-commissions/cadre-de-vie-et-developpement-durable.html",
|
|
40
|
+
"https://www.senat.fr/compte-rendu-commissions/cadre-de-vie-et-developpement-durable_archives.html",
|
|
41
|
+
],
|
|
42
|
+
culture: [
|
|
43
|
+
"https://www.senat.fr/compte-rendu-commissions/culture.html",
|
|
44
|
+
"https://www.senat.fr/compte-rendu-commissions/culture_archives.html",
|
|
45
|
+
],
|
|
46
|
+
finances: [
|
|
47
|
+
"https://www.senat.fr/compte-rendu-commissions/finances.html",
|
|
48
|
+
"https://www.senat.fr/compte-rendu-commissions/finances_archives.html",
|
|
49
|
+
],
|
|
50
|
+
lois: [
|
|
51
|
+
"https://www.senat.fr/compte-rendu-commissions/lois.html",
|
|
52
|
+
"https://www.senat.fr/compte-rendu-commissions/lois_archives.html",
|
|
53
|
+
],
|
|
54
|
+
"affaires-sociales": [
|
|
55
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-sociales.html",
|
|
56
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-sociales_archives.html",
|
|
57
|
+
],
|
|
58
|
+
"affaires-europeennes": [
|
|
59
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-europeennes.html",
|
|
60
|
+
"https://www.senat.fr/compte-rendu-commissions/affaires-europeennes_archives.html",
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
async function harvestWeeklyLinksFromHub(hubUrl) {
|
|
64
|
+
const res = await fetchWithRetry(hubUrl);
|
|
65
|
+
if (!res.ok)
|
|
66
|
+
return [];
|
|
67
|
+
const html = await res.text();
|
|
68
|
+
const $ = cheerio.load(html);
|
|
69
|
+
const out = [];
|
|
70
|
+
$("a[href]").each((_, a) => {
|
|
71
|
+
const href = ($(a).attr("href") || "").trim();
|
|
72
|
+
const m = href.match(/\/compte-rendu-commissions\/(\d{8})\/([a-z0-9\-]+)\.html$/i);
|
|
73
|
+
if (m) {
|
|
74
|
+
const url = href.startsWith("http") ? href : new URL(href, hubUrl).toString();
|
|
75
|
+
out.push(url);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return Array.from(new Set(out));
|
|
79
|
+
}
|
|
80
|
+
async function discoverCommissionWeeklyPages(fromSession) {
|
|
81
|
+
const results = [];
|
|
82
|
+
for (const [commissionKey, hubs] of Object.entries(COMMISSION_HUBS)) {
|
|
83
|
+
for (const hubUrl of hubs) {
|
|
84
|
+
try {
|
|
85
|
+
const links = await harvestWeeklyLinksFromHub(hubUrl);
|
|
86
|
+
for (const url of links) {
|
|
87
|
+
const m = url.match(/\/compte-rendu-commissions\/(\d{8})\/([a-z0-9\-]+)\.html$/i);
|
|
88
|
+
if (!m)
|
|
89
|
+
continue;
|
|
90
|
+
const yyyymmdd = m[1];
|
|
91
|
+
const year = Number(yyyymmdd.slice(0, 4));
|
|
92
|
+
const month = Number(yyyymmdd.slice(4, 6));
|
|
93
|
+
const session = month >= 10 ? year : year - 1;
|
|
94
|
+
if (session < fromSession)
|
|
95
|
+
continue;
|
|
96
|
+
results.push({ url, yyyymmdd, commissionKey });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
console.warn(`[COM-CR][hub-fail] ${hubUrl} → ${e?.message ?? e}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return results.sort((a, b) => a.yyyymmdd.localeCompare(b.yyyymmdd));
|
|
105
|
+
}
|
|
106
|
+
function toHourShort(hhmm) {
|
|
107
|
+
if (!hhmm)
|
|
108
|
+
return null;
|
|
109
|
+
const m = hhmm.match(/^(\d{2}):(\d{2})$/);
|
|
110
|
+
return m ? `${m[1]}${m[2]}` : null;
|
|
111
|
+
}
|
|
112
|
+
function timeToMinutes(hhmm) {
|
|
113
|
+
const [h, m] = hhmm.split(":").map((n) => parseInt(n, 10));
|
|
114
|
+
return (h || 0) * 60 + (m || 0);
|
|
115
|
+
}
|
|
116
|
+
async function tryDownload(url) {
|
|
117
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
118
|
+
if (res.status === 404)
|
|
119
|
+
return null;
|
|
120
|
+
if (!res.ok)
|
|
121
|
+
throw new CommissionCRDownloadError(String(res.status), url);
|
|
122
|
+
const ab = await res.arrayBuffer();
|
|
123
|
+
return Buffer.from(ab);
|
|
124
|
+
}
|
|
125
|
+
async function retrieveCommissionCRs(options = {}) {
|
|
126
|
+
const dataDir = options["dataDir"];
|
|
127
|
+
const fromSession = Number(options["fromSession"]);
|
|
128
|
+
const concurrency = Number(options["concurrency"] ?? 6);
|
|
129
|
+
const politenessMs = Number(options["politenessMs"] ?? 150);
|
|
130
|
+
const commissionsRootDir = path.join(dataDir, COMMISSION_FOLDER);
|
|
131
|
+
const originalRoot = path.join(commissionsRootDir, DATA_ORIGINAL_FOLDER);
|
|
132
|
+
ensureAndClearDir(originalRoot);
|
|
133
|
+
const discovered = await discoverCommissionWeeklyPages(fromSession);
|
|
134
|
+
console.log(`[COM-CR][discover] ${discovered.length} links (>= session ${fromSession})`);
|
|
135
|
+
const jobs = discovered.map(({ url, yyyymmdd }) => {
|
|
136
|
+
const d = new Date(Number(yyyymmdd.slice(0, 4)), Number(yyyymmdd.slice(4, 6)) - 1, Number(yyyymmdd.slice(6, 8)));
|
|
137
|
+
const session = sessionStartYearFromDate(d);
|
|
138
|
+
const dir = path.join(originalRoot, String(session));
|
|
139
|
+
fs.ensureDirSync(dir);
|
|
140
|
+
const slug = url.replace(/^.*\/(\d{8})\/([^\/]+)\.html$/i, "$2");
|
|
141
|
+
const outPath = path.join(dir, `${yyyymmdd}.${slug}.html`);
|
|
142
|
+
return { url, outPath, yyyymmdd };
|
|
143
|
+
});
|
|
144
|
+
console.log(`[COM-CR] Downloading ${jobs.length} links → ${path.relative(process.cwd(), originalRoot)}`);
|
|
145
|
+
let completed = 0, saved = 0, skipped = 0, notFound = 0;
|
|
146
|
+
const workers = Array.from({ length: Math.max(1, concurrency) }, async () => {
|
|
147
|
+
while (true) {
|
|
148
|
+
const job = jobs.shift();
|
|
149
|
+
if (!job)
|
|
150
|
+
break;
|
|
151
|
+
const { url, outPath, yyyymmdd } = job;
|
|
152
|
+
try {
|
|
153
|
+
if (await fs.pathExists(outPath)) {
|
|
154
|
+
skipped++;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
const buf = await tryDownload(url);
|
|
158
|
+
if (!buf) {
|
|
159
|
+
notFound++;
|
|
160
|
+
console.warn(`[COM-CR][404] ${url} → week=${yyyymmdd}`);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
await fs.writeFile(outPath, buf);
|
|
164
|
+
saved++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
console.error(`[COM-CR][err] ${url} → ${e?.message || e}`);
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
completed++;
|
|
173
|
+
if (politenessMs > 0)
|
|
174
|
+
await new Promise((r) => setTimeout(r, politenessMs));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
await Promise.all(workers);
|
|
179
|
+
console.log(`[COM-CR] done: saved=${saved} | skipped=${skipped} | 404=${notFound} | total=${completed}`);
|
|
180
|
+
const sessions = getSessionsFromStart(options["fromSession"]);
|
|
181
|
+
const comRoot = path.join(dataDir, COMMISSION_FOLDER);
|
|
182
|
+
const transformedRoot = path.join(comRoot, DATA_TRANSFORMED_FOLDER);
|
|
183
|
+
if (options["parseDebats"])
|
|
184
|
+
ensureAndClearDir(transformedRoot);
|
|
185
|
+
for (const session of sessions) {
|
|
186
|
+
const originalSessionDir = path.join(originalRoot, String(session));
|
|
187
|
+
const transformedSessionDir = path.join(transformedRoot, String(session));
|
|
188
|
+
fs.ensureDirSync(transformedSessionDir);
|
|
189
|
+
if (!(await fs.pathExists(originalSessionDir)))
|
|
190
|
+
continue;
|
|
191
|
+
const htmlFiles = (await fs.readdir(originalSessionDir)).filter((f) => /\.html?$/i.test(f)).sort();
|
|
192
|
+
for (const f of htmlFiles) {
|
|
193
|
+
const htmlPath = path.join(originalSessionDir, f);
|
|
194
|
+
let meta;
|
|
195
|
+
try {
|
|
196
|
+
const raw = await fs.readFile(htmlPath, "utf8");
|
|
197
|
+
meta = parseCommissionMetadataFromHtml(raw, f);
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
console.warn(`[COM-CR][PRE][${session}] Cannot read/parse ${f}:`, e);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const organeKeywords = (meta.organeDetected ?? meta.organeTitleRaw ?? "")
|
|
204
|
+
.toLowerCase()
|
|
205
|
+
.replace(/[’']/g, "'")
|
|
206
|
+
.split(/\W+/)
|
|
207
|
+
.filter((x) => x.length >= 3 && !["commission", "des", "de", "du", "d", "la", "le", "les", "et"].includes(x));
|
|
208
|
+
const MAX_TIME_DELTA_MIN = 120;
|
|
209
|
+
for (let i = 0; i < meta.days.length; i++) {
|
|
210
|
+
const day = meta.days[i];
|
|
211
|
+
const yyyymmdd = day.date.replace(/-/g, "");
|
|
212
|
+
const dt = new Date(Number(day.date.slice(0, 4)), Number(day.date.slice(5, 7)) - 1, Number(day.date.slice(8, 10)));
|
|
213
|
+
const daySession = sessionStartYearFromDate(dt);
|
|
214
|
+
const hits = await loadCommissionAgendaForDate(dataDir, yyyymmdd, daySession);
|
|
215
|
+
let best = null;
|
|
216
|
+
let reason = "fallback-none";
|
|
217
|
+
let deltaMin;
|
|
218
|
+
// a) score by title and organe keywords
|
|
219
|
+
if (organeKeywords.length && hits.length) {
|
|
220
|
+
const scored = hits
|
|
221
|
+
.map((h) => {
|
|
222
|
+
const t = (h.titre ?? "").toLowerCase();
|
|
223
|
+
const s = organeKeywords.reduce((acc, kw) => acc + (t.includes(kw) ? 1 : 0), 0);
|
|
224
|
+
return { h, s };
|
|
225
|
+
})
|
|
226
|
+
.sort((a, b) => b.s - a.s);
|
|
227
|
+
if (scored[0]?.s > 0) {
|
|
228
|
+
best = scored[0].h;
|
|
229
|
+
reason = "title";
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// b) otherwise score by time proximity
|
|
233
|
+
if (!best && day.openTime && hits.length) {
|
|
234
|
+
const candidates = hits
|
|
235
|
+
.map((h) => ({ h, hhmm: h.startTime ?? null }))
|
|
236
|
+
.filter((x) => !!x.hhmm)
|
|
237
|
+
.map((x) => ({
|
|
238
|
+
h: x.h,
|
|
239
|
+
d: Math.abs(timeToMinutes(x.hhmm) - timeToMinutes(day.openTime)),
|
|
240
|
+
}))
|
|
241
|
+
.sort((a, b) => a.d - b.d);
|
|
242
|
+
if (candidates[0] && candidates[0].d <= MAX_TIME_DELTA_MIN) {
|
|
243
|
+
best = candidates[0].h;
|
|
244
|
+
reason = "time";
|
|
245
|
+
deltaMin = candidates[0].d;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (best) {
|
|
249
|
+
const cr = parseCommissionCRFromFile(htmlPath, best);
|
|
250
|
+
if (!cr) {
|
|
251
|
+
console.warn(`[COM-CR][TRANSFORM] parse failed for ${f} → ${best.uid}`);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const fileUid = cr.uid;
|
|
255
|
+
const outPath = path.join(transformedSessionDir, `${fileUid}.json`);
|
|
256
|
+
await fs.writeJSON(outPath, cr, { spaces: 2 });
|
|
257
|
+
const npts = Array.isArray(cr.contenu.point) ? cr.contenu.point.length : cr.contenu.point ? 1 : 0;
|
|
258
|
+
if (!options["silent"]) {
|
|
259
|
+
console.log(`[COM-CR][TRANSFORM] saved ${path.basename(outPath)} (points=${npts})`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const hourShort = toHourShort(day.openTime) ?? "NA";
|
|
265
|
+
const titreGuess = meta.organeDetected || meta.organeTitleRaw || "Commission";
|
|
266
|
+
const { uid, filePath } = await createCommissionGroupIfMissing(dataDir, day.date, meta.organeDetected ?? null, hourShort, titreGuess);
|
|
267
|
+
if (!options["silent"]) {
|
|
268
|
+
console.log(`[COM-CR][PRE-SPLIT][${session}] ${f} | ${day.date}` +
|
|
269
|
+
(day.openTime ? ` ${day.openTime}` : ``) +
|
|
270
|
+
` → NO-MATCH → CREATED uid=${uid} file=${path.basename(filePath)}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async function main() {
|
|
278
|
+
const dataDir = options["dataDir"];
|
|
279
|
+
assert(dataDir, "Missing argument: data directory");
|
|
280
|
+
console.time("CRI processing time");
|
|
281
|
+
await retrieveCommissionCRs(options);
|
|
282
|
+
if (!options["silent"]) {
|
|
283
|
+
console.timeEnd("CRI processing time");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
main()
|
|
287
|
+
.then(() => process.exit(0))
|
|
288
|
+
.catch((error) => {
|
|
289
|
+
console.error(error);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
});
|
|
@@ -11,7 +11,7 @@ import StreamZip from "node-stream-zip";
|
|
|
11
11
|
import * as cheerio from "cheerio";
|
|
12
12
|
import { AGENDA_FOLDER, COMPTES_RENDUS_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER, } from "../loaders";
|
|
13
13
|
import { commonOptions } from "./shared/cli_helpers";
|
|
14
|
-
import { deriveTitreObjetFromSommaire, parseCompteRenduSlotFromFile, parseYYYYMMDD, sessionStartYearFromDate } from "../model/
|
|
14
|
+
import { deriveTitreObjetFromSommaire, parseCompteRenduSlotFromFile, parseYYYYMMDD, sessionStartYearFromDate } from "../model/seance";
|
|
15
15
|
import { makeGroupUid } from "../utils/reunion_grouping";
|
|
16
16
|
import { getSessionsFromStart } from "../types/sessions";
|
|
17
17
|
import { ensureAndClearDir, fetchWithRetry } from "./shared/util";
|
|
@@ -202,6 +202,30 @@ async function retrieveDataset(dataDir, dataset) {
|
|
|
202
202
|
console.log(`Importing ${dataset.title}: ${sqlFilename}…`);
|
|
203
203
|
}
|
|
204
204
|
await copyToSenat(dataset, dataDir, options);
|
|
205
|
+
// Create indexes programmatically after import
|
|
206
|
+
if (dataset.indexes) {
|
|
207
|
+
for (const [table, indexes] of Object.entries(dataset.indexes)) {
|
|
208
|
+
for (const index of indexes) {
|
|
209
|
+
const indexName = index.name;
|
|
210
|
+
const columns = index.columns.join(", ");
|
|
211
|
+
const schema = dataset.database;
|
|
212
|
+
const sql = `CREATE INDEX IF NOT EXISTS ${indexName} ON ${schema}.${table} (${columns});`;
|
|
213
|
+
try {
|
|
214
|
+
execSync(`${options["sudo"] ? `sudo -u ${options["sudo"]} ` : ""}psql --quiet -d senat -c "${sql}"`, {
|
|
215
|
+
env: process.env,
|
|
216
|
+
encoding: "utf-8",
|
|
217
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
218
|
+
});
|
|
219
|
+
if (!options["silent"]) {
|
|
220
|
+
console.log(`Created index: ${indexName} on ${schema}.${table} (${columns})`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
console.error(`Failed to create index ${indexName} on ${schema}.${table}:`, err);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
205
229
|
}
|
|
206
230
|
if (options["schema"]) {
|
|
207
231
|
let definitionsDir = path.resolve("src", "raw_types_schemats");
|
|
@@ -224,7 +248,7 @@ async function retrieveDataset(dataDir, dataset) {
|
|
|
224
248
|
fs.writeFileSync(definitionFilePath, definitionRepaired);
|
|
225
249
|
definitionsDir = path.resolve("src", "raw_types");
|
|
226
250
|
definitionFilePath = path.join(definitionsDir, `${dataset.database}.ts`);
|
|
227
|
-
execSync(`npx
|
|
251
|
+
execSync(`npx kysely-codegen --url '${dbConnectionString}' --default-schema ${dataset.database} --include-pattern '${dataset.database}.*' --out-file ${definitionFilePath}`, {
|
|
228
252
|
env: process.env,
|
|
229
253
|
encoding: "utf-8",
|
|
230
254
|
// stdio: ["ignore", "ignore", "pipe"],
|
|
@@ -261,6 +285,16 @@ async function retrieveOpenData() {
|
|
|
261
285
|
for (const dataset of chosenDatasets) {
|
|
262
286
|
await retrieveDataset(dataDir, dataset);
|
|
263
287
|
}
|
|
288
|
+
if (options["schema"]) {
|
|
289
|
+
const dbConnectionString = `postgres://${process.env["PGUSER"]}:${process.env["PGPASSWORD"]}@${process.env["PGHOST"]}:${process.env["PGPORT"]}/senat`;
|
|
290
|
+
const definitionsDir = path.resolve("src", "raw_types");
|
|
291
|
+
const definitionFilePath = path.join(definitionsDir, `senat.ts`);
|
|
292
|
+
execSync(`npx kysely-codegen --url '${dbConnectionString}' --out-file ${definitionFilePath}`, {
|
|
293
|
+
env: process.env,
|
|
294
|
+
encoding: "utf-8",
|
|
295
|
+
// stdio: ["ignore", "ignore", "pipe"],
|
|
296
|
+
});
|
|
297
|
+
}
|
|
264
298
|
if (!options["silent"]) {
|
|
265
299
|
console.timeEnd("data extraction time");
|
|
266
300
|
}
|
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
import { TimeSlot } from "../types/agenda";
|
|
1
|
+
import { GroupedReunion, TimeSlot } from "../types/agenda";
|
|
2
2
|
import * as cheerio from "cheerio";
|
|
3
3
|
export declare function computeIntervalsBySlot($: cheerio.CheerioAPI, idx: Map<any, number>, firstSlotOfDay?: TimeSlot): {
|
|
4
4
|
slot: TimeSlot;
|
|
5
5
|
start: number;
|
|
6
6
|
end: number;
|
|
7
7
|
}[];
|
|
8
|
+
export declare function parseCommissionMetadataFromHtml(html: string, sourceFileName?: string): {
|
|
9
|
+
sourceFile: string | null;
|
|
10
|
+
organeTitleRaw: string | null;
|
|
11
|
+
organeDetected: string | null;
|
|
12
|
+
organeCode: string | null;
|
|
13
|
+
weekStart: string | null;
|
|
14
|
+
days: {
|
|
15
|
+
date: string;
|
|
16
|
+
openTime?: string;
|
|
17
|
+
h2Index: number;
|
|
18
|
+
}[];
|
|
19
|
+
};
|
|
20
|
+
export declare function loadCommissionAgendaForDate(dataDir: string, yyyymmdd: string, session: number): Promise<GroupedReunion[]>;
|
|
21
|
+
export declare function createCommissionGroupIfMissing(dataDir: string, dateISO: string, // "YYYY-MM-DD"
|
|
22
|
+
organeDetected: string | null, // ex. "Commission des finances"
|
|
23
|
+
hourShort: string | null, // "HHMM" | "NA"
|
|
24
|
+
titreGuess?: string | null): Promise<{
|
|
25
|
+
uid: string;
|
|
26
|
+
filePath: string;
|
|
27
|
+
created: boolean;
|
|
28
|
+
}>;
|