@tricoteuses/senat 3.1.22 → 3.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/LICENSE.md +32 -32
  2. package/README.md +326 -326
  3. package/lib/src/other_types/collaborateurs.d.ts +5 -0
  4. package/lib/src/parsers/collaborateurs.d.ts +44 -0
  5. package/lib/src/parsers/collaborateurs.js +157 -0
  6. package/lib/src/scripts/retrieve_collaborateurs.js +166 -0
  7. package/lib/src/scripts/retrieve_cr_seance.js +34 -25
  8. package/lib/src/scripts/retrieve_videos.js +13 -8
  9. package/lib/src/scripts/shared/incremental_import_sql.js +866 -859
  10. package/lib/src/scripts/shared/schema_version.js +84 -84
  11. package/lib/src/scripts/shared/staging_metadata_sql.js +214 -214
  12. package/lib/src/scripts/validate_prefixed_tables.js +12 -12
  13. package/lib/src/server/ameli.js +14 -13
  14. package/lib/src/server/conversion_textes.js +106 -106
  15. package/lib/src/server/debats.js +10 -10
  16. package/lib/src/server/documents.js +22 -22
  17. package/lib/src/server/dosleg.js +33 -33
  18. package/lib/src/server/questions.js +10 -10
  19. package/lib/src/server/scrutins.js +3 -3
  20. package/lib/src/server/sens.js +19 -19
  21. package/lib/src/utils/reunion_odj_building.js +0 -4
  22. package/lib/src/videos/match.js +7 -5
  23. package/lib/src/videos/pipeline.js +26 -19
  24. package/lib/tests/collaborateurs.test.js +115 -0
  25. package/lib/tests/incrementalImportSql.test.js +4 -1
  26. package/package.json +119 -119
  27. package/lib/add-js-extensions-v2.js +0 -23
  28. package/lib/add-js-extensions.js +0 -17
  29. package/lib/aggregates.d.ts +0 -52
  30. package/lib/aggregates.js +0 -930
  31. package/lib/aggregates.mjs +0 -713
  32. package/lib/aggregates.ts +0 -833
  33. package/lib/config.d.ts +0 -10
  34. package/lib/config.js +0 -16
  35. package/lib/config.mjs +0 -16
  36. package/lib/config.ts +0 -26
  37. package/lib/databases.d.ts +0 -2
  38. package/lib/databases.js +0 -26
  39. package/lib/databases.mjs +0 -57
  40. package/lib/databases.ts +0 -71
  41. package/lib/datasets.d.ts +0 -34
  42. package/lib/datasets.js +0 -233
  43. package/lib/datasets.mjs +0 -78
  44. package/lib/datasets.ts +0 -118
  45. package/lib/fields.d.ts +0 -10
  46. package/lib/fields.js +0 -68
  47. package/lib/fields.mjs +0 -22
  48. package/lib/fields.ts +0 -29
  49. package/lib/git.d.ts +0 -26
  50. package/lib/git.js +0 -167
  51. package/lib/index.d.ts +0 -13
  52. package/lib/index.js +0 -1
  53. package/lib/index.mjs +0 -7
  54. package/lib/index.ts +0 -64
  55. package/lib/inserters.d.ts +0 -98
  56. package/lib/inserters.js +0 -500
  57. package/lib/inserters.mjs +0 -360
  58. package/lib/inserters.ts +0 -521
  59. package/lib/legislatures.json +0 -38
  60. package/lib/loaders.d.ts +0 -58
  61. package/lib/loaders.js +0 -286
  62. package/lib/loaders.mjs +0 -158
  63. package/lib/loaders.ts +0 -271
  64. package/lib/model/agenda.d.ts +0 -6
  65. package/lib/model/agenda.js +0 -148
  66. package/lib/model/ameli.d.ts +0 -51
  67. package/lib/model/ameli.js +0 -149
  68. package/lib/model/ameli.mjs +0 -84
  69. package/lib/model/ameli.ts +0 -100
  70. package/lib/model/commission.d.ts +0 -18
  71. package/lib/model/commission.js +0 -269
  72. package/lib/model/debats.d.ts +0 -67
  73. package/lib/model/debats.js +0 -95
  74. package/lib/model/debats.mjs +0 -43
  75. package/lib/model/debats.ts +0 -68
  76. package/lib/model/documents.d.ts +0 -12
  77. package/lib/model/documents.js +0 -151
  78. package/lib/model/dosleg.d.ts +0 -7
  79. package/lib/model/dosleg.js +0 -326
  80. package/lib/model/dosleg.mjs +0 -196
  81. package/lib/model/dosleg.ts +0 -240
  82. package/lib/model/index.d.ts +0 -7
  83. package/lib/model/index.js +0 -7
  84. package/lib/model/index.mjs +0 -5
  85. package/lib/model/index.ts +0 -15
  86. package/lib/model/questions.d.ts +0 -45
  87. package/lib/model/questions.js +0 -89
  88. package/lib/model/questions.mjs +0 -71
  89. package/lib/model/questions.ts +0 -93
  90. package/lib/model/scrutins.d.ts +0 -13
  91. package/lib/model/scrutins.js +0 -114
  92. package/lib/model/seance.d.ts +0 -3
  93. package/lib/model/seance.js +0 -267
  94. package/lib/model/sens.d.ts +0 -146
  95. package/lib/model/sens.js +0 -454
  96. package/lib/model/sens.mjs +0 -415
  97. package/lib/model/sens.ts +0 -516
  98. package/lib/model/texte.d.ts +0 -7
  99. package/lib/model/texte.js +0 -256
  100. package/lib/model/texte.mjs +0 -208
  101. package/lib/model/texte.ts +0 -229
  102. package/lib/model/util.d.ts +0 -9
  103. package/lib/model/util.js +0 -38
  104. package/lib/model/util.mjs +0 -19
  105. package/lib/model/util.ts +0 -32
  106. package/lib/parsers/texte.d.ts +0 -7
  107. package/lib/parsers/texte.js +0 -228
  108. package/lib/raw_types/ameli.d.ts +0 -914
  109. package/lib/raw_types/ameli.js +0 -5
  110. package/lib/raw_types/ameli.mjs +0 -163
  111. package/lib/raw_types/debats.d.ts +0 -207
  112. package/lib/raw_types/debats.js +0 -5
  113. package/lib/raw_types/debats.mjs +0 -58
  114. package/lib/raw_types/dosleg.d.ts +0 -1619
  115. package/lib/raw_types/dosleg.js +0 -5
  116. package/lib/raw_types/dosleg.mjs +0 -438
  117. package/lib/raw_types/questions.d.ts +0 -419
  118. package/lib/raw_types/questions.js +0 -5
  119. package/lib/raw_types/questions.mjs +0 -11
  120. package/lib/raw_types/senat.d.ts +0 -11368
  121. package/lib/raw_types/senat.js +0 -5
  122. package/lib/raw_types/sens.d.ts +0 -8248
  123. package/lib/raw_types/sens.js +0 -5
  124. package/lib/raw_types/sens.mjs +0 -508
  125. package/lib/raw_types_kysely/ameli.d.ts +0 -915
  126. package/lib/raw_types_kysely/ameli.js +0 -7
  127. package/lib/raw_types_kysely/ameli.mjs +0 -5
  128. package/lib/raw_types_kysely/ameli.ts +0 -951
  129. package/lib/raw_types_kysely/debats.d.ts +0 -207
  130. package/lib/raw_types_kysely/debats.js +0 -7
  131. package/lib/raw_types_kysely/debats.mjs +0 -5
  132. package/lib/raw_types_kysely/debats.ts +0 -222
  133. package/lib/raw_types_kysely/dosleg.d.ts +0 -3532
  134. package/lib/raw_types_kysely/dosleg.js +0 -7
  135. package/lib/raw_types_kysely/dosleg.mjs +0 -5
  136. package/lib/raw_types_kysely/dosleg.ts +0 -3621
  137. package/lib/raw_types_kysely/questions.d.ts +0 -414
  138. package/lib/raw_types_kysely/questions.js +0 -7
  139. package/lib/raw_types_kysely/questions.mjs +0 -5
  140. package/lib/raw_types_kysely/questions.ts +0 -426
  141. package/lib/raw_types_kysely/sens.d.ts +0 -4394
  142. package/lib/raw_types_kysely/sens.js +0 -7
  143. package/lib/raw_types_kysely/sens.mjs +0 -5
  144. package/lib/raw_types_kysely/sens.ts +0 -4499
  145. package/lib/raw_types_schemats/ameli.d.ts +0 -539
  146. package/lib/raw_types_schemats/ameli.js +0 -2
  147. package/lib/raw_types_schemats/ameli.mjs +0 -2
  148. package/lib/raw_types_schemats/ameli.ts +0 -601
  149. package/lib/raw_types_schemats/debats.d.ts +0 -127
  150. package/lib/raw_types_schemats/debats.js +0 -2
  151. package/lib/raw_types_schemats/debats.mjs +0 -2
  152. package/lib/raw_types_schemats/debats.ts +0 -145
  153. package/lib/raw_types_schemats/dosleg.d.ts +0 -977
  154. package/lib/raw_types_schemats/dosleg.js +0 -2
  155. package/lib/raw_types_schemats/dosleg.mjs +0 -2
  156. package/lib/raw_types_schemats/dosleg.ts +0 -2193
  157. package/lib/raw_types_schemats/questions.d.ts +0 -235
  158. package/lib/raw_types_schemats/questions.js +0 -2
  159. package/lib/raw_types_schemats/questions.mjs +0 -2
  160. package/lib/raw_types_schemats/questions.ts +0 -249
  161. package/lib/raw_types_schemats/sens.d.ts +0 -6915
  162. package/lib/raw_types_schemats/sens.js +0 -2
  163. package/lib/raw_types_schemats/sens.mjs +0 -2
  164. package/lib/raw_types_schemats/sens.ts +0 -2907
  165. package/lib/scripts/convert_data.js +0 -354
  166. package/lib/scripts/convert_data.mjs +0 -181
  167. package/lib/scripts/convert_data.ts +0 -243
  168. package/lib/scripts/data-download.d.ts +0 -1
  169. package/lib/scripts/data-download.js +0 -12
  170. package/lib/scripts/datautil.d.ts +0 -8
  171. package/lib/scripts/datautil.js +0 -34
  172. package/lib/scripts/datautil.mjs +0 -16
  173. package/lib/scripts/datautil.ts +0 -19
  174. package/lib/scripts/images/transparent_150x192.jpg +0 -0
  175. package/lib/scripts/images/transparent_155x225.jpg +0 -0
  176. package/lib/scripts/parse_textes.d.ts +0 -1
  177. package/lib/scripts/parse_textes.js +0 -44
  178. package/lib/scripts/parse_textes.mjs +0 -46
  179. package/lib/scripts/parse_textes.ts +0 -65
  180. package/lib/scripts/retrieve_agenda.d.ts +0 -1
  181. package/lib/scripts/retrieve_agenda.js +0 -132
  182. package/lib/scripts/retrieve_cr_commission.d.ts +0 -1
  183. package/lib/scripts/retrieve_cr_commission.js +0 -364
  184. package/lib/scripts/retrieve_cr_seance.d.ts +0 -6
  185. package/lib/scripts/retrieve_cr_seance.js +0 -347
  186. package/lib/scripts/retrieve_documents.d.ts +0 -3
  187. package/lib/scripts/retrieve_documents.js +0 -219
  188. package/lib/scripts/retrieve_documents.mjs +0 -249
  189. package/lib/scripts/retrieve_documents.ts +0 -298
  190. package/lib/scripts/retrieve_open_data.d.ts +0 -1
  191. package/lib/scripts/retrieve_open_data.js +0 -315
  192. package/lib/scripts/retrieve_open_data.mjs +0 -217
  193. package/lib/scripts/retrieve_open_data.ts +0 -268
  194. package/lib/scripts/retrieve_senateurs_photos.d.ts +0 -1
  195. package/lib/scripts/retrieve_senateurs_photos.js +0 -147
  196. package/lib/scripts/retrieve_senateurs_photos.mjs +0 -147
  197. package/lib/scripts/retrieve_senateurs_photos.ts +0 -177
  198. package/lib/scripts/retrieve_videos.d.ts +0 -1
  199. package/lib/scripts/retrieve_videos.js +0 -461
  200. package/lib/scripts/shared/cli_helpers.d.ts +0 -95
  201. package/lib/scripts/shared/cli_helpers.js +0 -91
  202. package/lib/scripts/shared/cli_helpers.ts +0 -36
  203. package/lib/scripts/shared/util.d.ts +0 -4
  204. package/lib/scripts/shared/util.js +0 -35
  205. package/lib/scripts/shared/util.ts +0 -33
  206. package/lib/scripts/test_iter_load.d.ts +0 -1
  207. package/lib/scripts/test_iter_load.js +0 -12
  208. package/lib/src/ameli.d.ts +0 -66
  209. package/lib/src/ameli.js +0 -1
  210. package/lib/src/config.d.ts +0 -43
  211. package/lib/src/config.js +0 -37
  212. package/lib/src/conversion_textes.d.ts +0 -11
  213. package/lib/src/conversion_textes.js +0 -320
  214. package/lib/src/databases.d.ts +0 -3
  215. package/lib/src/databases.js +0 -26
  216. package/lib/src/databases_postgres.d.ts +0 -4
  217. package/lib/src/databases_postgres.js +0 -23
  218. package/lib/src/datasets.d.ts +0 -38
  219. package/lib/src/datasets.js +0 -247
  220. package/lib/src/db_types/ameli.d.ts +0 -1762
  221. package/lib/src/db_types/ameli.js +0 -1074
  222. package/lib/src/db_types/debats.d.ts +0 -380
  223. package/lib/src/db_types/debats.js +0 -266
  224. package/lib/src/db_types/dosleg.d.ts +0 -2954
  225. package/lib/src/db_types/dosleg.js +0 -2005
  226. package/lib/src/db_types/questions.d.ts +0 -699
  227. package/lib/src/db_types/questions.js +0 -493
  228. package/lib/src/db_types/sens.d.ts +0 -7843
  229. package/lib/src/db_types/sens.js +0 -4691
  230. package/lib/src/debats.d.ts +0 -38
  231. package/lib/src/debats.js +0 -1
  232. package/lib/src/dosleg.d.ts +0 -142
  233. package/lib/src/dosleg.js +0 -193
  234. package/lib/src/git.d.ts +0 -27
  235. package/lib/src/git.js +0 -251
  236. package/lib/src/loaders.d.ts +0 -52
  237. package/lib/src/loaders.js +0 -260
  238. package/lib/src/model/agenda.d.ts +0 -6
  239. package/lib/src/model/agenda.js +0 -148
  240. package/lib/src/model/ameli.d.ts +0 -67
  241. package/lib/src/model/ameli.js +0 -150
  242. package/lib/src/model/ameli_postgres.d.ts +0 -67
  243. package/lib/src/model/ameli_postgres.js +0 -150
  244. package/lib/src/model/commission.d.ts +0 -19
  245. package/lib/src/model/commission.js +0 -269
  246. package/lib/src/model/debats.d.ts +0 -39
  247. package/lib/src/model/debats.js +0 -112
  248. package/lib/src/model/documents.d.ts +0 -32
  249. package/lib/src/model/documents.js +0 -182
  250. package/lib/src/model/dosleg.d.ts +0 -144
  251. package/lib/src/model/dosleg.js +0 -468
  252. package/lib/src/model/index.d.ts +0 -7
  253. package/lib/src/model/index.js +0 -7
  254. package/lib/src/model/questions.d.ts +0 -54
  255. package/lib/src/model/questions.js +0 -91
  256. package/lib/src/model/scrutins.d.ts +0 -48
  257. package/lib/src/model/scrutins.js +0 -121
  258. package/lib/src/model/seance.d.ts +0 -3
  259. package/lib/src/model/seance.js +0 -267
  260. package/lib/src/model/sens.d.ts +0 -112
  261. package/lib/src/model/sens.js +0 -385
  262. package/lib/src/model/util.d.ts +0 -1
  263. package/lib/src/model/util.js +0 -15
  264. package/lib/src/other_types/questions.d.ts +0 -2
  265. package/lib/src/other_types/questions.js +0 -1
  266. package/lib/src/questions.d.ts +0 -53
  267. package/lib/src/questions.js +0 -1
  268. package/lib/src/raw_types/ameli.d.ts +0 -1762
  269. package/lib/src/raw_types/ameli.js +0 -1074
  270. package/lib/src/raw_types/debats.d.ts +0 -380
  271. package/lib/src/raw_types/debats.js +0 -266
  272. package/lib/src/raw_types/dosleg.d.ts +0 -2954
  273. package/lib/src/raw_types/dosleg.js +0 -2005
  274. package/lib/src/raw_types/questions.d.ts +0 -699
  275. package/lib/src/raw_types/questions.js +0 -493
  276. package/lib/src/raw_types/senat.d.ts +0 -11372
  277. package/lib/src/raw_types/senat.js +0 -5
  278. package/lib/src/raw_types/sens.d.ts +0 -7843
  279. package/lib/src/raw_types/sens.js +0 -4691
  280. package/lib/src/raw_types_schemats/ameli.d.ts +0 -541
  281. package/lib/src/raw_types_schemats/ameli.js +0 -2
  282. package/lib/src/raw_types_schemats/debats.d.ts +0 -127
  283. package/lib/src/raw_types_schemats/debats.js +0 -2
  284. package/lib/src/raw_types_schemats/dosleg.d.ts +0 -977
  285. package/lib/src/raw_types_schemats/dosleg.js +0 -2
  286. package/lib/src/raw_types_schemats/questions.d.ts +0 -237
  287. package/lib/src/raw_types_schemats/questions.js +0 -2
  288. package/lib/src/raw_types_schemats/sens.d.ts +0 -2709
  289. package/lib/src/raw_types_schemats/sens.js +0 -2
  290. package/lib/src/rich_types/agenda.d.ts +0 -45
  291. package/lib/src/rich_types/agenda.js +0 -1
  292. package/lib/src/rich_types/compte_rendu.d.ts +0 -83
  293. package/lib/src/rich_types/compte_rendu.js +0 -1
  294. package/lib/src/rich_types/sessions.d.ts +0 -6
  295. package/lib/src/rich_types/sessions.js +0 -19
  296. package/lib/src/rich_types/texte.d.ts +0 -72
  297. package/lib/src/rich_types/texte.js +0 -15
  298. package/lib/src/scripts/test_iter_load.d.ts +0 -1
  299. package/lib/src/scripts/test_iter_load.js +0 -12
  300. package/lib/src/sens.d.ts +0 -104
  301. package/lib/src/sens.js +0 -1
  302. package/lib/src/types/agenda.d.ts +0 -45
  303. package/lib/src/types/agenda.js +0 -1
  304. package/lib/src/types/ameli.d.ts +0 -1762
  305. package/lib/src/types/ameli.js +0 -1074
  306. package/lib/src/types/compte_rendu.d.ts +0 -83
  307. package/lib/src/types/compte_rendu.js +0 -1
  308. package/lib/src/types/debats.d.ts +0 -380
  309. package/lib/src/types/debats.js +0 -266
  310. package/lib/src/types/dosleg.d.ts +0 -2954
  311. package/lib/src/types/dosleg.js +0 -2005
  312. package/lib/src/types/questions.d.ts +0 -699
  313. package/lib/src/types/questions.js +0 -493
  314. package/lib/src/types/sens.d.ts +0 -7843
  315. package/lib/src/types/sens.js +0 -4691
  316. package/lib/src/types/sessions.d.ts +0 -6
  317. package/lib/src/types/sessions.js +0 -19
  318. package/lib/src/types/texte.d.ts +0 -72
  319. package/lib/src/types/texte.js +0 -15
  320. package/lib/src/validators/config.d.ts +0 -9
  321. package/lib/src/validators/config.js +0 -10
  322. package/lib/strings.d.ts +0 -1
  323. package/lib/strings.js +0 -18
  324. package/lib/strings.mjs +0 -18
  325. package/lib/strings.ts +0 -26
  326. package/lib/tsconfig.tsbuildinfo +0 -1
  327. package/lib/types/agenda.d.ts +0 -44
  328. package/lib/types/agenda.js +0 -1
  329. package/lib/types/ameli.d.ts +0 -5
  330. package/lib/types/ameli.js +0 -1
  331. package/lib/types/ameli.mjs +0 -13
  332. package/lib/types/ameli.ts +0 -21
  333. package/lib/types/compte_rendu.d.ts +0 -83
  334. package/lib/types/compte_rendu.js +0 -1
  335. package/lib/types/debats.d.ts +0 -2
  336. package/lib/types/debats.js +0 -1
  337. package/lib/types/debats.mjs +0 -2
  338. package/lib/types/debats.ts +0 -6
  339. package/lib/types/dosleg.d.ts +0 -70
  340. package/lib/types/dosleg.js +0 -1
  341. package/lib/types/dosleg.mjs +0 -151
  342. package/lib/types/dosleg.ts +0 -284
  343. package/lib/types/questions.d.ts +0 -2
  344. package/lib/types/questions.js +0 -1
  345. package/lib/types/questions.mjs +0 -1
  346. package/lib/types/questions.ts +0 -3
  347. package/lib/types/sens.d.ts +0 -10
  348. package/lib/types/sens.js +0 -1
  349. package/lib/types/sens.mjs +0 -1
  350. package/lib/types/sens.ts +0 -12
  351. package/lib/types/sessions.d.ts +0 -5
  352. package/lib/types/sessions.js +0 -84
  353. package/lib/types/sessions.mjs +0 -43
  354. package/lib/types/sessions.ts +0 -42
  355. package/lib/types/texte.d.ts +0 -74
  356. package/lib/types/texte.js +0 -16
  357. package/lib/types/texte.mjs +0 -16
  358. package/lib/types/texte.ts +0 -76
  359. package/lib/typings/windows-1252.d.js +0 -2
  360. package/lib/typings/windows-1252.d.mjs +0 -2
  361. package/lib/typings/windows-1252.d.ts +0 -11
  362. package/lib/utils/cr_spliting.d.ts +0 -28
  363. package/lib/utils/cr_spliting.js +0 -265
  364. package/lib/utils/date.d.ts +0 -10
  365. package/lib/utils/date.js +0 -100
  366. package/lib/utils/nvs-timecode.d.ts +0 -7
  367. package/lib/utils/nvs-timecode.js +0 -79
  368. package/lib/utils/reunion_grouping.d.ts +0 -9
  369. package/lib/utils/reunion_grouping.js +0 -361
  370. package/lib/utils/reunion_odj_building.d.ts +0 -5
  371. package/lib/utils/reunion_odj_building.js +0 -154
  372. package/lib/utils/reunion_parsing.d.ts +0 -23
  373. package/lib/utils/reunion_parsing.js +0 -209
  374. package/lib/utils/scoring.d.ts +0 -14
  375. package/lib/utils/scoring.js +0 -147
  376. package/lib/utils/string_cleaning.d.ts +0 -7
  377. package/lib/utils/string_cleaning.js +0 -57
  378. package/lib/validators/config.d.ts +0 -9
  379. package/lib/validators/config.js +0 -10
  380. package/lib/validators/config.mjs +0 -54
  381. package/lib/validators/config.ts +0 -79
  382. package/lib/validators/senat.d.ts +0 -0
  383. package/lib/validators/senat.js +0 -28
  384. package/lib/validators/senat.mjs +0 -24
  385. package/lib/validators/senat.ts +0 -26
  386. /package/lib/{add-js-extensions-v2.d.ts → src/other_types/collaborateurs.js} +0 -0
  387. /package/lib/{add-js-extensions.d.ts → src/scripts/retrieve_collaborateurs.d.ts} +0 -0
  388. /package/lib/{scripts/convert_data.d.ts → tests/collaborateurs.test.d.ts} +0 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Fonctions pures de parsing du PDF DRH des collaborateurs de sénateurs
3
+ * (https://www.senat.fr/pubagas/liste_senateurs_collaborateurs.pdf).
4
+ *
5
+ * Le PDF est un tableau à deux colonnes par page :
6
+ * - "Employeur" (le sénateur) à gauche, ex. "Mme AESCHLIMANN Marie-Do" ;
7
+ * - "Nom Collaborateur" à droite, ex. "M. CHAREF Dahmane".
8
+ * Une ligne sans employeur prolonge le sénateur précédent.
9
+ *
10
+ * Pièges traités ici : aucun matricule dans le PDF, prénoms de sénateurs tronqués
11
+ * ("Marie-Do"), noms composés ("DI FOLCO", "MAZENOT CHAPPUY"), accents.
12
+ */
13
+ /** Élément de texte positionné extrait du PDF (x croissant vers la droite, y croissant vers le haut). */
14
+ export type PdfTextItem = {
15
+ str: string;
16
+ x: number;
17
+ y: number;
18
+ page: number;
19
+ };
20
+ export type PersonneNom = {
21
+ civilite: string;
22
+ nom: string;
23
+ prenom: string;
24
+ };
25
+ export type LigneCollaborateur = {
26
+ senateur: PersonneNom;
27
+ collaborateur: PersonneNom;
28
+ };
29
+ /** Normalise un texte pour la comparaison : majuscules, sans accents, espaces compactés. */
30
+ export declare function normaliserPourComparaison(valeur: string): string;
31
+ /**
32
+ * Sépare une cellule "Civilité NOM(S) Prénom" en {civilite, nom, prenom}.
33
+ * Les jetons en majuscules de tête forment le nom (gère les noms composés), le reste le prénom.
34
+ * Renvoie null si la cellule n'est pas un nom de personne exploitable.
35
+ */
36
+ export declare function parseCellulePersonne(cellule: string): PersonneNom | null;
37
+ /** Extrait la date d'édition imprimée en pied de page ("Edition du JJ/MM/AAAA"). */
38
+ export declare function extraireDateEdition(strings: string[]): Date | null;
39
+ /**
40
+ * Reconstruit la liste {sénateur, collaborateur} à partir des éléments de texte positionnés.
41
+ * Parcourt les pages puis les lignes de haut en bas ; mémorise le sénateur courant et lui
42
+ * rattache les collaborateurs des lignes suivantes tant qu'aucun nouvel employeur n'apparaît.
43
+ */
44
+ export declare function construireLignesCollaborateurs(items: PdfTextItem[]): LigneCollaborateur[];
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Fonctions pures de parsing du PDF DRH des collaborateurs de sénateurs
3
+ * (https://www.senat.fr/pubagas/liste_senateurs_collaborateurs.pdf).
4
+ *
5
+ * Le PDF est un tableau à deux colonnes par page :
6
+ * - "Employeur" (le sénateur) à gauche, ex. "Mme AESCHLIMANN Marie-Do" ;
7
+ * - "Nom Collaborateur" à droite, ex. "M. CHAREF Dahmane".
8
+ * Une ligne sans employeur prolonge le sénateur précédent.
9
+ *
10
+ * Pièges traités ici : aucun matricule dans le PDF, prénoms de sénateurs tronqués
11
+ * ("Marie-Do"), noms composés ("DI FOLCO", "MAZENOT CHAPPUY"), accents.
12
+ */
13
+ // Bandes horizontales des deux colonnes (mesurées sur le PDF : employeur ≈ 147, collaborateur ≈ 285).
14
+ const COLONNE_EMPLOYEUR = { min: 130, max: 225 };
15
+ const COLONNE_COLLABORATEUR = { min: 255, max: 360 };
16
+ const CIVILITES = ["Mme", "M.", "Mlle", "M"];
17
+ // Particules nobiliaires/patronymiques en minuscules, qui font partie du nom quand elles le précèdent
18
+ // (ex. "de CIDRAC", "de LA GONTRIE", "de LEGGE"). Le référentiel Sénat les conserve dans Nom_usuel.
19
+ const PARTICULES = new Set([
20
+ "de",
21
+ "du",
22
+ "des",
23
+ "d'",
24
+ "le",
25
+ "la",
26
+ "les",
27
+ "von",
28
+ "van",
29
+ "der",
30
+ "den",
31
+ "da",
32
+ "di",
33
+ "del",
34
+ "dos",
35
+ ]);
36
+ // Lignes d'en-tête / pied de page à ignorer.
37
+ const LIGNES_IGNOREES = [
38
+ /liste des collaborateurs/i,
39
+ /^employeur$/i,
40
+ /^nom collaborateur$/i,
41
+ /^a\.?g\.?a\.?s\.?/i,
42
+ /edition du/i,
43
+ /congé non rémunéré/i,
44
+ /^-\s*\d+\s*-$/,
45
+ ];
46
+ /** Normalise un texte pour la comparaison : majuscules, sans accents, espaces compactés. */
47
+ export function normaliserPourComparaison(valeur) {
48
+ return valeur
49
+ .normalize("NFD")
50
+ .replace(/\p{Diacritic}/gu, "")
51
+ .toUpperCase()
52
+ .replace(/['']/g, "'")
53
+ .replace(/\s+/g, " ")
54
+ .trim();
55
+ }
56
+ function estIgnoree(str) {
57
+ const t = str.trim();
58
+ if (!t)
59
+ return true;
60
+ return LIGNES_IGNOREES.some((re) => re.test(t));
61
+ }
62
+ /**
63
+ * Sépare une cellule "Civilité NOM(S) Prénom" en {civilite, nom, prenom}.
64
+ * Les jetons en majuscules de tête forment le nom (gère les noms composés), le reste le prénom.
65
+ * Renvoie null si la cellule n'est pas un nom de personne exploitable.
66
+ */
67
+ export function parseCellulePersonne(cellule) {
68
+ const brut = cellule
69
+ .replace(/\(\*\)/g, "")
70
+ .replace(/\s+/g, " ")
71
+ .trim();
72
+ if (!brut)
73
+ return null;
74
+ let civilite = "";
75
+ let reste = brut;
76
+ for (const civ of CIVILITES) {
77
+ if (brut === civ)
78
+ return null;
79
+ if (brut.startsWith(`${civ} `)) {
80
+ civilite = civ;
81
+ reste = brut.slice(civ.length + 1).trim();
82
+ break;
83
+ }
84
+ }
85
+ if (!reste)
86
+ return null;
87
+ const jetons = reste.split(" ");
88
+ const estMajuscule = (jeton) => {
89
+ const lettres = jeton.replace(/[^A-Za-zÀ-ÿ]/g, "");
90
+ return lettres.length > 0 && lettres === lettres.toUpperCase();
91
+ };
92
+ const jetonsNom = [];
93
+ let i = 0;
94
+ // Particules de tête en minuscules (ex. "de", "de la") faisant partie du nom.
95
+ while (i < jetons.length && PARTICULES.has(jetons[i].toLowerCase())) {
96
+ jetonsNom.push(jetons[i]);
97
+ i++;
98
+ }
99
+ // Puis les jetons du nom en majuscules.
100
+ while (i < jetons.length && estMajuscule(jetons[i])) {
101
+ jetonsNom.push(jetons[i]);
102
+ i++;
103
+ }
104
+ // Une particule seule sans nom en majuscules n'est pas un nom valide : on rétablit.
105
+ if (jetonsNom.length > 0 && jetonsNom.every((j) => PARTICULES.has(j.toLowerCase()))) {
106
+ jetonsNom.length = 0;
107
+ i = 0;
108
+ }
109
+ // Si aucun jeton majuscule (cas inattendu), on prend le premier comme nom.
110
+ if (jetonsNom.length === 0 && jetons.length > 0) {
111
+ jetonsNom.push(jetons[0]);
112
+ i = 1;
113
+ }
114
+ const nom = jetonsNom.join(" ");
115
+ const prenom = jetons.slice(i).join(" ");
116
+ if (!nom)
117
+ return null;
118
+ return { civilite, nom, prenom };
119
+ }
120
+ /** Extrait la date d'édition imprimée en pied de page ("Edition du JJ/MM/AAAA"). */
121
+ export function extraireDateEdition(strings) {
122
+ for (const s of strings) {
123
+ const m = s.match(/Edition du\s+(\d{2})\/(\d{2})\/(\d{4})/i);
124
+ if (m) {
125
+ const [, jj, mm, aaaa] = m;
126
+ return new Date(Date.UTC(Number(aaaa), Number(mm) - 1, Number(jj)));
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+ /**
132
+ * Reconstruit la liste {sénateur, collaborateur} à partir des éléments de texte positionnés.
133
+ * Parcourt les pages puis les lignes de haut en bas ; mémorise le sénateur courant et lui
134
+ * rattache les collaborateurs des lignes suivantes tant qu'aucun nouvel employeur n'apparaît.
135
+ */
136
+ export function construireLignesCollaborateurs(items) {
137
+ const utiles = items.filter((it) => !estIgnoree(it.str));
138
+ // Tri lecture : page croissante, puis y décroissant (haut → bas), puis x croissant (gauche → droite).
139
+ const tries = [...utiles].sort((a, b) => a.page - b.page || b.y - a.y || a.x - b.x);
140
+ const lignes = [];
141
+ let senateurCourant = null;
142
+ for (const it of tries) {
143
+ const dansEmployeur = it.x >= COLONNE_EMPLOYEUR.min && it.x <= COLONNE_EMPLOYEUR.max;
144
+ const dansCollaborateur = it.x >= COLONNE_COLLABORATEUR.min && it.x <= COLONNE_COLLABORATEUR.max;
145
+ if (dansEmployeur) {
146
+ const personne = parseCellulePersonne(it.str);
147
+ if (personne)
148
+ senateurCourant = personne;
149
+ }
150
+ else if (dansCollaborateur && senateurCourant) {
151
+ const collaborateur = parseCellulePersonne(it.str);
152
+ if (collaborateur)
153
+ lignes.push({ senateur: senateurCourant, collaborateur });
154
+ }
155
+ }
156
+ return lignes;
157
+ }
@@ -0,0 +1,166 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { getDocumentProxy } from "unpdf";
4
+ import { construireLignesCollaborateurs, extraireDateEdition, normaliserPourComparaison, } from "../parsers/collaborateurs.js";
5
+ import { assertExistingDirectory } from "./shared/cli_helpers.js";
6
+ import { iterFilePaths } from "../server/loaders.js";
7
+ const URL_PDF_DEFAUT = "https://www.senat.fr/pubagas/liste_senateurs_collaborateurs.pdf";
8
+ const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36";
9
+ async function telechargerPdf(url) {
10
+ const cookies = new Map();
11
+ let courant = url;
12
+ for (let i = 0; i < 8; i += 1) {
13
+ const headers = { "User-Agent": USER_AGENT };
14
+ if (cookies.size > 0) {
15
+ headers["Cookie"] = [...cookies].map(([k, v]) => `${k}=${v}`).join("; ");
16
+ }
17
+ const reponse = await fetch(courant, { redirect: "manual", headers });
18
+ for (const brut of reponse.headers.getSetCookie?.() ?? []) {
19
+ const [paire] = brut.split(";");
20
+ const idx = paire.indexOf("=");
21
+ if (idx > 0)
22
+ cookies.set(paire.slice(0, idx).trim(), paire.slice(idx + 1).trim());
23
+ }
24
+ if (reponse.status >= 300 && reponse.status < 400) {
25
+ const location = reponse.headers.get("location");
26
+ if (!location)
27
+ throw new Error(`Redirection sans en-tête Location (HTTP ${reponse.status})`);
28
+ courant = new URL(location, courant).toString();
29
+ continue;
30
+ }
31
+ if (!reponse.ok)
32
+ throw new Error(`Téléchargement du PDF échoué : HTTP ${reponse.status}`);
33
+ const buffer = Buffer.from(await reponse.arrayBuffer());
34
+ if (buffer.subarray(0, 5).toString("latin1") !== "%PDF-") {
35
+ throw new Error("Le contenu téléchargé n'est pas un PDF");
36
+ }
37
+ return buffer;
38
+ }
39
+ throw new Error("Trop de redirections lors du téléchargement du PDF DRH");
40
+ }
41
+ async function extraireItemsPdf(buffer) {
42
+ const pdf = await getDocumentProxy(new Uint8Array(buffer));
43
+ const items = [];
44
+ const strings = [];
45
+ for (let p = 1; p <= pdf.numPages; p += 1) {
46
+ const page = await pdf.getPage(p);
47
+ const contenu = await page.getTextContent();
48
+ for (const item of contenu.items) {
49
+ if (typeof item.str !== "string" || !item.str.trim() || !item.transform)
50
+ continue;
51
+ items.push({ str: item.str, x: item.transform[4], y: item.transform[5], page: p });
52
+ strings.push(item.str);
53
+ }
54
+ }
55
+ return { items, strings };
56
+ }
57
+ function chargerSenateurs(dataDir) {
58
+ const senateursDir = path.join(dataDir, "sens", "senateurs");
59
+ const files = new Map();
60
+ const indexParNom = new Map();
61
+ if (!fs.existsSync(senateursDir))
62
+ return { files, indexParNom };
63
+ for (const filePath of iterFilePaths(senateursDir)) {
64
+ const senator = JSON.parse(fs.readFileSync(filePath, "utf-8"));
65
+ files.set(senator.matricule, senator);
66
+ const cle = normaliserPourComparaison(senator.nom_usuel);
67
+ const liste = indexParNom.get(cle);
68
+ if (liste)
69
+ liste.push(senator);
70
+ else
71
+ indexParNom.set(cle, [senator]);
72
+ }
73
+ return { files, indexParNom };
74
+ }
75
+ async function main() {
76
+ const dataDir = process.argv[2];
77
+ assertExistingDirectory(dataDir, "dataDir");
78
+ // 1. Téléchargement + parsing du PDF
79
+ console.log(`Téléchargement du PDF DRH : ${URL_PDF_DEFAUT}`);
80
+ const buffer = await telechargerPdf(URL_PDF_DEFAUT);
81
+ const { items, strings } = await extraireItemsPdf(buffer);
82
+ const lignes = construireLignesCollaborateurs(items);
83
+ const dateEdition = extraireDateEdition(strings);
84
+ console.log(`PDF lu : ${lignes.length} lignes collaborateur, édition ${dateEdition?.toISOString().slice(0, 10) ?? "inconnue"}`);
85
+ if (lignes.length === 0) {
86
+ throw new Error("Aucune ligne collaborateur extraite du PDF : format inattendu, abandon sans modification.");
87
+ }
88
+ // 2. Charger les fichiers sénateurs existants
89
+ const { indexParNom } = chargerSenateurs(dataDir);
90
+ if (indexParNom.size === 0) {
91
+ throw new Error("Aucun fichier sénateur trouvé. Lancez d'abord data:download pour générer les fichiers.");
92
+ }
93
+ // 3. Résoudre les sénateurs du PDF → fichiers
94
+ let nbEnrichis = 0;
95
+ let nbNonResolus = 0;
96
+ // Grouper les collaborateurs par matricule de sénateur
97
+ const collabsParMatricule = new Map();
98
+ const nomsNonResolus = new Set();
99
+ for (const ligne of lignes) {
100
+ const cleNom = normaliserPourComparaison(ligne.senateur.nom);
101
+ const candidats = indexParNom.get(cleNom);
102
+ if (!candidats || candidats.length === 0) {
103
+ nomsNonResolus.add(`${ligne.senateur.civilite} ${ligne.senateur.nom} ${ligne.senateur.prenom}`.trim());
104
+ nbNonResolus += 1;
105
+ continue;
106
+ }
107
+ // Désambiguïsation par préfixe de prénom
108
+ const prenomCible = normaliserPourComparaison(ligne.senateur.prenom);
109
+ const parPrenom = candidats.filter((c) => normaliserPourComparaison(c.prenom_usuel).startsWith(prenomCible));
110
+ let senator = null;
111
+ if (candidats.length === 1) {
112
+ senator = candidats[0];
113
+ }
114
+ else if (parPrenom.length === 1) {
115
+ senator = parPrenom[0];
116
+ }
117
+ else {
118
+ nomsNonResolus.add(`${ligne.senateur.civilite} ${ligne.senateur.nom} ${ligne.senateur.prenom}`.trim());
119
+ nbNonResolus += 1;
120
+ continue;
121
+ }
122
+ let collabs = collabsParMatricule.get(senator.matricule);
123
+ if (!collabs) {
124
+ collabs = [];
125
+ collabsParMatricule.set(senator.matricule, collabs);
126
+ }
127
+ // Dédoublonnage
128
+ const nNom = normaliserPourComparaison(ligne.collaborateur.nom);
129
+ const nPrenom = normaliserPourComparaison(ligne.collaborateur.prenom);
130
+ if (!collabs.some((c) => normaliserPourComparaison(c.nom) === nNom && normaliserPourComparaison(c.prenom) === nPrenom)) {
131
+ collabs.push({
132
+ civilite: ligne.collaborateur.civilite,
133
+ nom: ligne.collaborateur.nom,
134
+ prenom: ligne.collaborateur.prenom,
135
+ });
136
+ }
137
+ }
138
+ // 4. Écrire les collaborateurs dans les fichiers sénateurs
139
+ for (const [matricule, collaborateurs] of collabsParMatricule) {
140
+ const cheminFichier = path.join(dataDir, "sens", "senateurs", `${matricule}.json`);
141
+ if (!fs.existsSync(cheminFichier))
142
+ continue;
143
+ const senator = JSON.parse(fs.readFileSync(cheminFichier, "utf-8"));
144
+ senator.collaborateurs = collaborateurs;
145
+ fs.writeFileSync(cheminFichier, JSON.stringify(senator, null, 2) + "\n");
146
+ nbEnrichis += 1;
147
+ }
148
+ // 5. Nettoyer les collaborateurs des sénateurs absents du PDF
149
+ const matriculesEnrichis = new Set(collabsParMatricule.keys());
150
+ const { files: tousLesFichiers } = chargerSenateurs(dataDir);
151
+ for (const [matricule, senator] of tousLesFichiers) {
152
+ if (!matriculesEnrichis.has(matricule) && senator.collaborateurs) {
153
+ delete senator.collaborateurs;
154
+ const cheminFichier = path.join(dataDir, "sens", "senateurs", `${matricule}.json`);
155
+ fs.writeFileSync(cheminFichier, JSON.stringify(senator, null, 2) + "\n");
156
+ }
157
+ }
158
+ console.log(`${nbEnrichis} sénateur(s) enrichi(s) avec leurs collaborateurs`);
159
+ if (nomsNonResolus.size > 0) {
160
+ console.warn(`${nbNonResolus} sénateur(s) du PDF non rattaché(s) : ${[...nomsNonResolus].slice(0, 10).join(", ")}${nomsNonResolus.size > 10 ? "…" : ""}`);
161
+ }
162
+ }
163
+ main().catch((error) => {
164
+ console.error(error);
165
+ process.exit(1);
166
+ });
@@ -28,18 +28,25 @@ const optionsDefinitions = [
28
28
  const options = commandLineArgs(optionsDefinitions);
29
29
  const CRI_ZIP_URL = "https://data.senat.fr/data/debats/cri.zip";
30
30
  let exitCode = 10; // 0: some data changed, 10: no modification
31
+ function log(options, ...args) {
32
+ if (!options["silent"])
33
+ console.log(...args);
34
+ }
35
+ function warn(options, ...args) {
36
+ if (!options["silent"])
37
+ console.warn(...args);
38
+ }
31
39
  class CompteRenduError extends Error {
32
40
  constructor(message, url) {
33
41
  super(`An error occurred while retrieving ${url}: ${message}`);
34
42
  }
35
43
  }
36
- async function downloadCriZip(zipPath) {
37
- if (!options["silent"])
38
- console.log(`Downloading CRI zip ${CRI_ZIP_URL}…`);
44
+ async function downloadCriZip(zipPath, options) {
45
+ log(options, `Downloading CRI zip ${CRI_ZIP_URL}…`);
39
46
  const response = await fetchWithRetry(CRI_ZIP_URL);
40
47
  if (!response.ok) {
41
48
  if (response.status === 404) {
42
- console.warn(`CRI zip ${CRI_ZIP_URL} not found`);
49
+ warn(options, `CRI zip ${CRI_ZIP_URL} not found`);
43
50
  return;
44
51
  }
45
52
  throw new CompteRenduError(String(response.status), CRI_ZIP_URL);
@@ -48,7 +55,7 @@ async function downloadCriZip(zipPath) {
48
55
  await fs.writeFile(zipPath, buf);
49
56
  if (!options["silent"]) {
50
57
  const mb = (buf.length / (1024 * 1024)).toFixed(1);
51
- console.log(`[CRI] Downloaded ${mb} MB → ${zipPath}`);
58
+ log(options, `[CRI] Downloaded ${mb} MB → ${zipPath}`);
52
59
  }
53
60
  }
54
61
  async function extractAndDistributeXmlBySession(zipPath, originalRoot) {
@@ -97,9 +104,9 @@ export async function retrieveCriXmlDump(dataDir, options = {}) {
97
104
  const sessions = getSessionsFromStart((options["fromSession"] ?? UNDEFINED_SESSION));
98
105
  // 1) Download ZIP global + distribut by session
99
106
  const zipPath = path.join(dataDir, "cri.zip");
100
- console.log("[CRI] Downloading global CRI zip…");
101
- await downloadCriZip(zipPath);
102
- console.log("[CRI] Extracting + distributing XMLs by session…");
107
+ log(options, "[CRI] Downloading global CRI zip…");
108
+ await downloadCriZip(zipPath, options);
109
+ log(options, "[CRI] Extracting + distributing XMLs by session…");
103
110
  for (const session of sessions) {
104
111
  const dir = path.join(originalRoot, String(session));
105
112
  if (await fs.pathExists(dir)) {
@@ -110,13 +117,13 @@ export async function retrieveCriXmlDump(dataDir, options = {}) {
110
117
  }
111
118
  const n = await extractAndDistributeXmlBySession(zipPath, originalRoot);
112
119
  if (n === 0) {
113
- console.warn("[CRI] No XML extracted. Archive empty or layout changed?");
120
+ warn(options, "[CRI] No XML extracted. Archive empty or layout changed?");
114
121
  }
115
122
  else {
116
- console.log(`[CRI] Distributed ${n} XML file(s) into session folders.`);
123
+ log(options, `[CRI] Distributed ${n} XML file(s) into session folders.`);
117
124
  }
118
125
  if (!options["parseDebats"]) {
119
- console.log("[CRI] parseDebats not requested → done.");
126
+ log(options, "[CRI] parseDebats not requested → done.");
120
127
  return;
121
128
  }
122
129
  for (const session of sessions) {
@@ -147,10 +154,10 @@ export async function retrieveCriXmlDump(dataDir, options = {}) {
147
154
  const crPath = path.join(transformedSessionDir, fn);
148
155
  try {
149
156
  const cr = await fs.readJSON(crPath);
150
- await linkCriEventIntoAgenda(dataDir, yyyymmdd, eventId, cr.uid, cr, session);
157
+ await linkCriEventIntoAgenda(dataDir, yyyymmdd, eventId, cr.uid, cr, session, options);
151
158
  }
152
159
  catch (e) {
153
- console.warn(`[CR] [${session}] Could not relink existing CR into a reunion for ${yyyymmdd} event=${eventId}:`, e);
160
+ warn(options, `[CR] [${session}] Could not relink existing CR into a reunion for ${yyyymmdd} event=${eventId}:`, e);
154
161
  }
155
162
  }
156
163
  continue;
@@ -160,7 +167,7 @@ export async function retrieveCriXmlDump(dataDir, options = {}) {
160
167
  // === Charger les events SP du jour depuis les agendas groupés ===
161
168
  const dayEvents = await loadAgendaSpEventsForDate(dataDir, yyyymmdd, session);
162
169
  if (dayEvents.length === 0) {
163
- console.warn(`[CRI] [${session}] No agenda SP events found for ${yyyymmdd} → skip split/link`);
170
+ warn(options, `[CRI] [${session}] No agenda SP events found for ${yyyymmdd} → skip split/link`);
164
171
  continue;
165
172
  }
166
173
  // === Lire XML + construire index DOM ===
@@ -175,14 +182,14 @@ export async function retrieveCriXmlDump(dataDir, options = {}) {
175
182
  idx = new Map(order.map((el, i) => [el, i]));
176
183
  }
177
184
  catch (e) {
178
- console.warn(`[CRI] [${session}] Cannot read/parse ${f}:`, e);
185
+ warn(options, `[CRI] [${session}] Cannot read/parse ${f}:`, e);
179
186
  continue;
180
187
  }
181
188
  // === Extraire sommaire + matcher vers events agenda ===
182
189
  const blocks = extractSommaireBlocks($, idx);
183
190
  const intervals = buildIntervalsByAgendaEvents($, idx, order, blocks, dayEvents);
184
191
  if (!intervals.length) {
185
- console.warn(`[CRI] [${session}] No confident split intervals for ${yyyymmdd} → skip`);
192
+ warn(options, `[CRI] [${session}] No confident split intervals for ${yyyymmdd} → skip`);
186
193
  continue;
187
194
  }
188
195
  // === Parser / écrire / linker chaque segment par event ===
@@ -191,16 +198,16 @@ export async function retrieveCriXmlDump(dataDir, options = {}) {
191
198
  const outPath = path.join(transformedSessionDir, outName);
192
199
  const cr = await parseCompteRenduIntervalFromFile(xmlPath, iv.startIndex, iv.endIndex, iv.agendaEventId);
193
200
  if (!cr) {
194
- console.warn(`[CRI] [${session}] Empty or no points for ${yyyymmdd} event=${iv.agendaEventId} → skip`);
201
+ warn(options, `[CRI] [${session}] Empty or no points for ${yyyymmdd} event=${iv.agendaEventId} → skip`);
195
202
  continue;
196
203
  }
197
204
  await fs.ensureDir(transformedSessionDir);
198
205
  await fs.writeJSON(outPath, cr, { spaces: 2 });
199
206
  try {
200
- await linkCriEventIntoAgenda(dataDir, yyyymmdd, iv.agendaEventId, cr.uid, cr, session);
207
+ await linkCriEventIntoAgenda(dataDir, yyyymmdd, iv.agendaEventId, cr.uid, cr, session, options);
201
208
  }
202
209
  catch (e) {
203
- console.warn(`[CR] [${session}] Could not link CR into agenda for ${yyyymmdd} event=${iv.agendaEventId}:`, e);
210
+ warn(options, `[CR] [${session}] Could not link CR into agenda for ${yyyymmdd} event=${iv.agendaEventId}:`, e);
204
211
  }
205
212
  }
206
213
  }
@@ -218,7 +225,7 @@ function commitAndPushGit(datasetDir, options) {
218
225
  }
219
226
  }
220
227
  }
221
- async function linkCriEventIntoAgenda(dataDir, yyyymmdd, agendaEventId, crUid, cr, session) {
228
+ async function linkCriEventIntoAgenda(dataDir, yyyymmdd, agendaEventId, crUid, cr, session, options) {
222
229
  const agendadDir = path.join(dataDir, AGENDA_FOLDER, DATA_TRANSFORMED_FOLDER, session.toString());
223
230
  fs.ensureDirSync(agendadDir);
224
231
  const dateISO = `${yyyymmdd.slice(0, 4)}-${yyyymmdd.slice(4, 6)}-${yyyymmdd.slice(6, 8)}`;
@@ -230,17 +237,17 @@ async function linkCriEventIntoAgenda(dataDir, yyyymmdd, agendaEventId, crUid, c
230
237
  agenda = await fs.readJSON(agendaPath);
231
238
  }
232
239
  catch (e) {
233
- console.warn(`[CR] unreadable reunion JSON → ${agendaPath} (${e})`);
240
+ warn(options, `[CR] unreadable reunion JSON → ${agendaPath} (${e})`);
234
241
  agenda = null;
235
242
  }
236
243
  }
237
244
  if (!agenda) {
238
- console.warn(`[CR] Missing reunion file for SP event=${agendaEventId}: ${agendaPath}`);
245
+ warn(options, `[CR] Missing reunion file for SP event=${agendaEventId}: ${agendaPath}`);
239
246
  return;
240
247
  }
241
248
  agenda.compteRenduRefUid = crUid;
242
249
  await fs.writeJSON(agendaPath, agenda, { spaces: 2 });
243
- console.log(`[CR] Linked CR ${crUid} → ${path.basename(agendaPath)} (event=${agendaEventId})`);
250
+ log(options, `[CR] Linked CR ${crUid} → ${path.basename(agendaPath)} (event=${agendaEventId})`);
244
251
  }
245
252
  function buildIntervalsByAgendaEvents($, idx, order, blocks, dayEvents) {
246
253
  const MIN_SCORE = 0.65;
@@ -348,9 +355,11 @@ function resolveTargetIndex($, idx, targetId) {
348
355
  }
349
356
  async function main() {
350
357
  const dataDir = assertExistingDirectory(options["dataDir"], "data directory");
351
- console.time("CRI processing time");
358
+ if (!options["silent"])
359
+ console.time("CRI processing time");
352
360
  await retrieveCriXmlDump(dataDir, options);
353
- console.timeEnd("CRI processing time");
361
+ if (!options["silent"])
362
+ console.timeEnd("CRI processing time");
354
363
  }
355
364
  main()
356
365
  .then(() => process.exit(exitCode))
@@ -17,6 +17,10 @@ import { processBisIfNeeded, processOneReunionMatch, writeIfChanged } from "../v
17
17
  const optionsDefinitions = [...commonOptions];
18
18
  const options = commandLineArgs(optionsDefinitions);
19
19
  let exitCode = 10; // 0: some data changed, 10: no modification
20
+ function log(...args) {
21
+ if (!options["silent"])
22
+ console.log(...args);
23
+ }
20
24
  function shouldSkipAgenda(agenda) {
21
25
  if (!agenda.date || !agenda.startTime)
22
26
  return true;
@@ -113,12 +117,12 @@ async function processGroupedReunion(agenda, session, dataDir, lastByVideo) {
113
117
  STATS.total++;
114
118
  const candidates = await fetchCandidatesForAgenda(agenda, options);
115
119
  if (!candidates) {
116
- console.log(`[warn] ${agenda.uid} No candidate found for this reunion. Probably VOD not published yet.`);
120
+ log(`[warn] ${agenda.uid} No candidate found for this reunion. Probably VOD not published yet.`);
117
121
  return;
118
122
  }
119
123
  const match = await matchAgendaToVideo({ agenda, agendaTs: ctx.agendaTs, candidates, options });
120
124
  if (!match) {
121
- console.log(`[miss] ${agenda.uid} No match found for this reunion`);
125
+ log(`[miss] ${agenda.uid} No match found for this reunion`);
122
126
  return;
123
127
  }
124
128
  ;
@@ -127,8 +131,7 @@ async function processGroupedReunion(agenda, session, dataDir, lastByVideo) {
127
131
  await writeMatchArtifacts({ agenda, ctx, best, secondBest });
128
132
  }
129
133
  if (best && isAmbiguousTimeOriginal(agenda.events[0].timeOriginal)) {
130
- if (!options["silent"])
131
- console.log("If the time is ambiguous, update agenda startTime from matched video");
134
+ log("If the time is ambiguous, update agenda startTime from matched video");
132
135
  agenda = { ...agenda, startTime: epochToParisDateTime(best.epoch)?.startTime ?? agenda.startTime };
133
136
  }
134
137
  // 3) Always update BEST agenda JSON from local NVS
@@ -158,7 +161,7 @@ async function processGroupedReunion(agenda, session, dataDir, lastByVideo) {
158
161
  });
159
162
  }
160
163
  async function processAll(dataDir, sessions) {
161
- console.log("Process all Agendas and fetch video's url");
164
+ log("Process all Agendas and fetch video's url");
162
165
  for (const session of sessions) {
163
166
  const lastByVideo = new Map();
164
167
  for (const { item: agenda } of iterLoadSenatAgendas(dataDir, session)) {
@@ -186,12 +189,14 @@ async function main() {
186
189
  const dataDir = assertExistingDirectory(options["dataDir"], "data directory");
187
190
  const sessions = getSessionsFromStart((options["fromSession"] ?? UNDEFINED_SESSION));
188
191
  const TIMER = "senat-agendas→videos processing time";
189
- console.time(TIMER);
192
+ if (!options["silent"])
193
+ console.time(TIMER);
190
194
  await processAll(dataDir, sessions);
191
- console.timeEnd(TIMER);
195
+ if (!options["silent"])
196
+ console.timeEnd(TIMER);
192
197
  const { total, accepted } = STATS;
193
198
  const ratio = total ? ((accepted / total) * 100).toFixed(1) : "0.0";
194
- console.log(`[summary] accepted=${accepted} / total=${total} (${ratio}%)`);
199
+ log(`[summary] accepted=${accepted} / total=${total} (${ratio}%)`);
195
200
  }
196
201
  if (import.meta.url === pathToFileURL(process.argv[1]).href) {
197
202
  main()