@tricoteuses/senat 3.2.1 → 3.2.3

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 (115) hide show
  1. package/LICENSE.md +32 -32
  2. package/README.md +326 -326
  3. package/lib/src/config.d.ts +43 -0
  4. package/lib/src/config.js +37 -0
  5. package/lib/src/conversion_textes.d.ts +11 -0
  6. package/lib/src/conversion_textes.js +320 -0
  7. package/lib/src/databases_postgres.d.ts +4 -0
  8. package/lib/src/databases_postgres.js +23 -0
  9. package/lib/src/datasets.d.ts +38 -0
  10. package/lib/src/datasets.js +247 -0
  11. package/lib/src/git.d.ts +27 -0
  12. package/lib/src/git.js +251 -0
  13. package/lib/src/index.d.ts +1 -1
  14. package/lib/src/loaders.d.ts +52 -0
  15. package/lib/src/loaders.js +260 -0
  16. package/lib/src/model/agenda.d.ts +6 -0
  17. package/lib/src/model/agenda.js +148 -0
  18. package/lib/src/model/ameli.d.ts +67 -0
  19. package/lib/src/model/ameli.js +150 -0
  20. package/lib/src/model/commission.d.ts +19 -0
  21. package/lib/src/model/commission.js +269 -0
  22. package/lib/src/model/debats.d.ts +39 -0
  23. package/lib/src/model/debats.js +112 -0
  24. package/lib/src/model/documents.d.ts +32 -0
  25. package/lib/src/model/documents.js +182 -0
  26. package/lib/src/model/dosleg.d.ts +144 -0
  27. package/lib/src/model/dosleg.js +468 -0
  28. package/lib/src/model/index.d.ts +7 -0
  29. package/lib/src/model/index.js +7 -0
  30. package/lib/src/model/questions.d.ts +54 -0
  31. package/lib/src/model/questions.js +91 -0
  32. package/lib/src/model/scrutins.d.ts +48 -0
  33. package/lib/src/model/scrutins.js +121 -0
  34. package/lib/src/model/seance.d.ts +3 -0
  35. package/lib/src/model/seance.js +267 -0
  36. package/lib/src/model/sens.d.ts +112 -0
  37. package/lib/src/model/sens.js +385 -0
  38. package/lib/src/model/util.d.ts +1 -0
  39. package/lib/src/model/util.js +15 -0
  40. package/lib/src/parsers/plf/amendement.d.ts +3 -0
  41. package/lib/src/parsers/plf/amendement.js +152 -0
  42. package/lib/src/raw_types/ameli.d.ts +1762 -0
  43. package/lib/src/raw_types/ameli.js +1074 -0
  44. package/lib/src/raw_types/debats.d.ts +380 -0
  45. package/lib/src/raw_types/debats.js +266 -0
  46. package/lib/src/raw_types/dosleg.d.ts +2954 -0
  47. package/lib/src/raw_types/dosleg.js +2005 -0
  48. package/lib/src/raw_types/questions.d.ts +699 -0
  49. package/lib/src/raw_types/questions.js +493 -0
  50. package/lib/src/raw_types/sens.d.ts +7843 -0
  51. package/lib/src/raw_types/sens.js +4691 -0
  52. package/lib/src/raw_types_schemats/ameli.d.ts +541 -0
  53. package/lib/src/raw_types_schemats/ameli.js +2 -0
  54. package/lib/src/raw_types_schemats/debats.d.ts +127 -0
  55. package/lib/src/raw_types_schemats/debats.js +2 -0
  56. package/lib/src/raw_types_schemats/dosleg.d.ts +977 -0
  57. package/lib/src/raw_types_schemats/dosleg.js +2 -0
  58. package/lib/src/raw_types_schemats/questions.d.ts +237 -0
  59. package/lib/src/raw_types_schemats/questions.js +2 -0
  60. package/lib/src/raw_types_schemats/sens.d.ts +2709 -0
  61. package/lib/src/raw_types_schemats/sens.js +2 -0
  62. package/lib/src/rich_types/ameli.d.ts +12 -0
  63. package/lib/src/scripts/data-download.js +13 -13
  64. package/lib/src/scripts/debug_dosleg_query.d.ts +6 -0
  65. package/lib/src/scripts/debug_dosleg_query.js +50 -0
  66. package/lib/src/scripts/retrieve_open_data.js +2 -1
  67. package/lib/src/scripts/shared/data_download.d.ts +43 -0
  68. package/lib/src/scripts/shared/data_download.js +175 -0
  69. package/lib/src/scripts/shared/incremental_import_sql.js +866 -866
  70. package/lib/src/scripts/shared/schema_version.js +85 -84
  71. package/lib/src/scripts/shared/staging_metadata_sql.js +214 -214
  72. package/lib/src/scripts/validate_prefixed_tables.js +12 -12
  73. package/lib/src/server/ameli.js +18 -11
  74. package/lib/src/server/conversion_textes.js +106 -106
  75. package/lib/src/server/datasets.js +1 -0
  76. package/lib/src/server/debats.js +10 -10
  77. package/lib/src/server/documents.js +22 -22
  78. package/lib/src/server/dosleg.js +33 -33
  79. package/lib/src/server/questions.js +10 -10
  80. package/lib/src/server/scrutins.js +3 -3
  81. package/lib/src/server/sens.js +19 -19
  82. package/lib/src/types/agenda.d.ts +45 -0
  83. package/lib/src/types/ameli.d.ts +5 -0
  84. package/lib/src/types/ameli.js +1 -0
  85. package/lib/src/types/compte_rendu.d.ts +83 -0
  86. package/lib/src/types/compte_rendu.js +1 -0
  87. package/lib/src/types/debats.d.ts +2 -0
  88. package/lib/src/types/debats.js +1 -0
  89. package/lib/src/types/dosleg.d.ts +70 -0
  90. package/lib/src/types/dosleg.js +1 -0
  91. package/lib/src/types/questions.d.ts +2 -0
  92. package/lib/src/types/questions.js +1 -0
  93. package/lib/src/types/sens.d.ts +8 -0
  94. package/lib/src/types/sens.js +1 -0
  95. package/lib/src/types/sessions.d.ts +6 -0
  96. package/lib/src/types/sessions.js +19 -0
  97. package/lib/src/types/texte.d.ts +72 -0
  98. package/lib/src/types/texte.js +15 -0
  99. package/lib/src/validators/config.d.ts +9 -0
  100. package/lib/src/validators/config.js +10 -0
  101. package/lib/src/videos/match.js +4 -4
  102. package/lib/tests/dataDownload.test.d.ts +1 -0
  103. package/lib/tests/dataDownload.test.js +217 -0
  104. package/lib/tests/parsers/plf/amendement.test.d.ts +1 -0
  105. package/lib/tests/parsers/plf/amendement.test.js +153 -0
  106. package/lib/tests/parsers/plf/credits.test.js +72 -72
  107. package/lib/tests/parsers/plf/test_utils.js +13 -13
  108. package/lib/tests/parsers/texte.test_utils.js +22 -22
  109. package/package.json +123 -123
  110. package/lib/src/scripts/extract_plf_fixtures.d.ts +0 -1
  111. package/lib/src/scripts/extract_plf_fixtures.js +0 -101
  112. package/lib/tests/plf/credits.test.js +0 -458
  113. package/lib/tests/plf/test_utils.d.ts +0 -20
  114. package/lib/tests/plf/test_utils.js +0 -85
  115. /package/lib/{tests/plf/credits.test.d.ts → src/types/agenda.js} +0 -0
@@ -0,0 +1,2 @@
1
+ /* tslint:disable */
2
+ export {};
@@ -11,6 +11,14 @@ export interface AmendementAuteurRow {
11
11
  qualite: string | null;
12
12
  rang: string | null;
13
13
  }
14
+ export interface LigneCreditPlf {
15
+ autorisation_engagement: string | null;
16
+ credit_paiement: string | null;
17
+ libelle: string;
18
+ }
19
+ export interface ProgrammePlf extends LigneCreditPlf {
20
+ lignes_credits?: LigneCreditPlf[];
21
+ }
14
22
  export interface AmendementResult {
15
23
  accepte_gouvernement: string | null;
16
24
  alinea: number | null;
@@ -32,6 +40,9 @@ export interface AmendementResult {
32
40
  intitule_texte: string | null;
33
41
  lecture: string | null;
34
42
  motion_libelle: string | null;
43
+ mission_plf_id: number | null;
44
+ mission_plf_libelle: string | null;
45
+ mission_plf_libelle_court: string | null;
35
46
  nature: string;
36
47
  nature_texte: string | null;
37
48
  nature_texte_libelle: string | null;
@@ -44,6 +55,7 @@ export interface AmendementResult {
44
55
  observations_additionnelles: string | null;
45
56
  ordre: number | null;
46
57
  parent_id: number | null;
58
+ programmes_plf: ProgrammePlf[];
47
59
  revision: string | null;
48
60
  session: string;
49
61
  session_libelle: string;
@@ -1,21 +1,21 @@
1
1
  import { execSync } from "child_process";
2
- const args = process.argv.slice(2).join(" ");
3
- function runScript(command) {
2
+ import { runDataDownload } from "./shared/data_download.js";
3
+ function exec(command) {
4
4
  try {
5
5
  execSync(command, { stdio: "inherit" });
6
+ return { status: 0 };
6
7
  }
7
8
  catch (error) {
8
9
  const execError = error;
9
- if (execError.status !== 10) {
10
- console.error(`Error during: ${command}`, error);
11
- process.exit(execError.status || 1);
12
- }
10
+ return { status: execError.status ?? null, error };
13
11
  }
14
12
  }
15
- runScript(`tsx src/scripts/retrieve_open_data.ts --all ${args}`);
16
- runScript(`tsx src/scripts/convert_data.ts ${args}`);
17
- runScript(`cross-env TZ='Etc/UTC' tsx src/scripts/retrieve_agenda.ts ${args} --parseAgenda --silent`);
18
- runScript(`tsx src/scripts/retrieve_cr_seance.ts ${args} --parseDebats --silent`);
19
- runScript(`tsx src/scripts/retrieve_cr_commission.ts ${args} --parseDebats --silent`);
20
- runScript(`tsx src/scripts/retrieve_videos.ts ${args} --silent`);
21
- runScript(`tsx src/scripts/retrieve_collaborateurs.ts ${args} --silent`);
13
+ const exitCode = runDataDownload({
14
+ exec,
15
+ now: () => new Date(),
16
+ rawArgs: process.argv.slice(2),
17
+ log: console.log,
18
+ warn: console.warn,
19
+ error: (message, error) => error === undefined ? console.error(message) : console.error(message, error),
20
+ });
21
+ process.exit(exitCode);
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Debug script to check the DosLeg query compilation
4
+ * Helps identify the "column undefined" error
5
+ */
6
+ export {};
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Debug script to check the DosLeg query compilation
4
+ * Helps identify the "column undefined" error
5
+ */
6
+ import { db } from "../server/databases_postgres.js";
7
+ import { jsonArrayFrom } from "kysely/helpers/postgres";
8
+ import { sql } from "kysely";
9
+ async function debugDoslegQuery() {
10
+ console.log("=== Debugging DosLeg Query ===\n");
11
+ try {
12
+ // Build the same query as findAllDossiers but capture SQL
13
+ const query = db
14
+ .withSchema("senat")
15
+ .selectFrom("dosleg_loi as loi")
16
+ .leftJoin("dosleg_typloi as typloi", "typloi.typloicod", "loi.typloicod")
17
+ .leftJoin("dosleg_etaloi as etaloi", "etaloi.etaloicod", "loi.etaloicod")
18
+ .leftJoin("dosleg_deccoc as deccoc", "deccoc.deccoccod", "loi.deccoccod")
19
+ .select((eb) => [
20
+ sql `rtrim(${eb.ref("loi.loicod")})`.as("code"),
21
+ "loi.numero as numero",
22
+ "loi.signet as signet",
23
+ ]);
24
+ const compiled = query.compile();
25
+ console.log("✓ Query compiled successfully (basic columns)\n");
26
+ console.log("SQL:", compiled.sql);
27
+ console.log("\n");
28
+ // Try with simple lectures first
29
+ const queryWithLectures = db
30
+ .withSchema("senat")
31
+ .selectFrom("dosleg_loi as loi")
32
+ .select((eb) => [
33
+ "loi.signet as signet",
34
+ jsonArrayFrom(eb.withSchema("senat").selectFrom("dosleg_lecture as lecture").select(["lecture.lecidt as id"])).as("lectures"),
35
+ ]);
36
+ const compiledWithLectures = queryWithLectures.compile();
37
+ console.log("✓ Query with simple lectures compiled successfully\n");
38
+ console.log("SQL:", compiledWithLectures.sql);
39
+ console.log("\n");
40
+ console.log("✅ Basic query structures seem valid");
41
+ console.log("\nThe issue is likely in one of the nested subqueries with cross-references.");
42
+ console.log("Try running findAllDossiers with the verbose flag to see exactly where it fails.");
43
+ }
44
+ catch (error) {
45
+ console.error("❌ Error during query compilation:");
46
+ console.error(error instanceof Error ? error.message : String(error));
47
+ process.exit(1);
48
+ }
49
+ }
50
+ debugDoslegQuery().catch(console.error);
@@ -273,7 +273,8 @@ function ensureForeignStagingServer(dataDir, options, runtime) {
273
273
  "OPTIONS (",
274
274
  `host '${escapeSqlLiteral(runtime.staging.host)}', `,
275
275
  `dbname '${escapeSqlLiteral(runtime.staging.name)}', `,
276
- `port '${escapeSqlLiteral(String(runtime.staging.port))}'`,
276
+ `port '${escapeSqlLiteral(String(runtime.staging.port))}', `,
277
+ "fetch_size '10000'",
277
278
  ")",
278
279
  ].join(""),
279
280
  ].join(" "), dataDir, options, runtime.target);
@@ -0,0 +1,43 @@
1
+ import { type Dataset } from "../../server/datasets.js";
2
+ export declare const STATUS_FILENAME = "dataset_status.json";
3
+ export type DatasetStatusEntry = {
4
+ status: "ok" | "failed";
5
+ lastRunAt: string;
6
+ lastSuccessAt?: string;
7
+ lastFailureAt?: string;
8
+ lastError?: string;
9
+ consecutiveFailures: number;
10
+ firstFailureAt?: string;
11
+ };
12
+ export type DatasetStatusFile = Record<string, DatasetStatusEntry>;
13
+ export type ExecResult = {
14
+ status: number | null;
15
+ error?: unknown;
16
+ };
17
+ export type ExecFn = (command: string) => ExecResult;
18
+ export type DataDownloadLogger = {
19
+ log?: (message: string) => void;
20
+ warn?: (message: string) => void;
21
+ error?: (message: string, error?: unknown) => void;
22
+ };
23
+ export type DataDownloadDeps = DataDownloadLogger & {
24
+ rawArgs: string[];
25
+ exec: ExecFn;
26
+ now: () => Date;
27
+ };
28
+ export declare const CATEGORY_DATASETS: Array<{
29
+ category: string;
30
+ dataset: Dataset;
31
+ }>;
32
+ export type ParsedArgs = {
33
+ categories: string[];
34
+ dataDir?: string;
35
+ rest: string[];
36
+ };
37
+ export declare function parseArgs(rawArgs: string[]): ParsedArgs;
38
+ export declare function forwardArgs(parsedArgs: ParsedArgs, categories: string[]): string;
39
+ export declare function readStatusFile(statusFilePath: string): DatasetStatusFile;
40
+ export declare function writeStatusFile(statusFilePath: string, statuses: DatasetStatusFile): void;
41
+ export declare function recordSuccess(statuses: DatasetStatusFile, datasetName: string, now: Date): void;
42
+ export declare function recordFailure(statuses: DatasetStatusFile, datasetName: string, now: Date, errorMessage: string): void;
43
+ export declare function runDataDownload(deps: DataDownloadDeps): number;
@@ -0,0 +1,175 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { datasets } from "../../server/datasets.js";
4
+ export const STATUS_FILENAME = "dataset_status.json";
5
+ const MAX_ERROR_LENGTH = 2000;
6
+ export const CATEGORY_DATASETS = [
7
+ { category: "Ameli", dataset: datasets.ameli },
8
+ { category: "Debats", dataset: datasets.debats },
9
+ { category: "DosLeg", dataset: datasets.dosleg },
10
+ { category: "Questions", dataset: datasets.questions },
11
+ { category: "Sens", dataset: datasets.sens },
12
+ ];
13
+ // Options of retrieve_open_data whose next token is a value. They must not be
14
+ // mistaken for the dataDir positional argument.
15
+ const valueOptions = new Set([
16
+ "categories",
17
+ "k",
18
+ "sudo",
19
+ "clone",
20
+ "C",
21
+ "remote",
22
+ "r",
23
+ "fromSession",
24
+ "only-recent",
25
+ ]);
26
+ export function parseArgs(rawArgs) {
27
+ const categories = [];
28
+ let dataDir;
29
+ const rest = [];
30
+ for (let index = 0; index < rawArgs.length; index++) {
31
+ const token = rawArgs[index];
32
+ const name = token.replace(/^-+/, "").split("=")[0];
33
+ if (name === "categories" || name === "k") {
34
+ const rawValues = token.includes("=") ? [token.slice(token.indexOf("=") + 1)] : [rawArgs[++index]];
35
+ for (const rawValue of rawValues) {
36
+ if (rawValue === undefined) {
37
+ continue;
38
+ }
39
+ for (const value of rawValue.split(",")) {
40
+ if (value.length > 0) {
41
+ categories.push(value);
42
+ }
43
+ }
44
+ }
45
+ continue;
46
+ }
47
+ if (token.startsWith("-") && valueOptions.has(name) && !token.includes("=")) {
48
+ rest.push(token);
49
+ const value = rawArgs[++index];
50
+ if (value !== undefined) {
51
+ rest.push(value);
52
+ }
53
+ continue;
54
+ }
55
+ if (!token.startsWith("-") && dataDir === undefined) {
56
+ dataDir = token;
57
+ continue;
58
+ }
59
+ rest.push(token);
60
+ }
61
+ return { categories: categories.length > 0 ? categories : ["All"], dataDir, rest };
62
+ }
63
+ export function forwardArgs(parsedArgs, categories) {
64
+ const parts = [...parsedArgs.rest];
65
+ if (parsedArgs.dataDir !== undefined) {
66
+ parts.push(parsedArgs.dataDir);
67
+ }
68
+ for (const category of categories) {
69
+ parts.push("--categories", category);
70
+ }
71
+ return parts.join(" ");
72
+ }
73
+ export function readStatusFile(statusFilePath) {
74
+ try {
75
+ const content = fs.readJsonSync(statusFilePath);
76
+ return content && typeof content === "object" ? content : {};
77
+ }
78
+ catch {
79
+ return {};
80
+ }
81
+ }
82
+ export function writeStatusFile(statusFilePath, statuses) {
83
+ fs.writeJsonSync(statusFilePath, statuses, { spaces: 2 });
84
+ }
85
+ export function recordSuccess(statuses, datasetName, now) {
86
+ statuses[datasetName] = {
87
+ consecutiveFailures: 0,
88
+ lastRunAt: now.toISOString(),
89
+ lastSuccessAt: now.toISOString(),
90
+ status: "ok",
91
+ };
92
+ }
93
+ export function recordFailure(statuses, datasetName, now, errorMessage) {
94
+ const previous = statuses[datasetName];
95
+ statuses[datasetName] = {
96
+ consecutiveFailures: (previous?.consecutiveFailures ?? 0) + 1,
97
+ firstFailureAt: previous?.firstFailureAt ?? now.toISOString(),
98
+ lastError: errorMessage.slice(0, MAX_ERROR_LENGTH),
99
+ lastFailureAt: now.toISOString(),
100
+ lastRunAt: now.toISOString(),
101
+ lastSuccessAt: previous?.lastSuccessAt,
102
+ status: "failed",
103
+ };
104
+ }
105
+ function execNormalized(deps, command) {
106
+ try {
107
+ return deps.exec(command);
108
+ }
109
+ catch (error) {
110
+ const execError = error;
111
+ return { status: execError.status ?? null, error };
112
+ }
113
+ }
114
+ function isSuccessfulStatus(result) {
115
+ return result.status === 0 || result.status === 10;
116
+ }
117
+ function runDownstreamScript(deps, command) {
118
+ const result = execNormalized(deps, command);
119
+ if (isSuccessfulStatus(result)) {
120
+ return null;
121
+ }
122
+ deps.error?.(`Error during: ${command}`, result.error);
123
+ return result.status || 1;
124
+ }
125
+ export function runDataDownload(deps) {
126
+ const parsedArgs = parseArgs(deps.rawArgs);
127
+ const chosen = parsedArgs.categories.includes("All")
128
+ ? CATEGORY_DATASETS
129
+ : CATEGORY_DATASETS.filter(({ category }) => parsedArgs.categories.includes(category));
130
+ if (chosen.length === 0) {
131
+ deps.error?.(`No known dataset among categories: ${parsedArgs.categories.join(", ")}`);
132
+ return 1;
133
+ }
134
+ const statusFilePath = parsedArgs.dataDir !== undefined ? path.join(parsedArgs.dataDir, STATUS_FILENAME) : null;
135
+ const statuses = statusFilePath !== null ? readStatusFile(statusFilePath) : {};
136
+ const okCategories = [];
137
+ const failedCategories = [];
138
+ for (const { category, dataset } of chosen) {
139
+ // --all: fetch, unzip, repair encoding and import (same as the original script).
140
+ const command = `tsx src/scripts/retrieve_open_data.ts --all ${forwardArgs(parsedArgs, [category])}`.trim();
141
+ deps.log?.(`\n=== Retrieving dataset ${dataset.title} (${category}) ===`);
142
+ const result = execNormalized(deps, command);
143
+ if (!isSuccessfulStatus(result)) {
144
+ const errorMessage = result.error instanceof Error ? result.error.message : String(result.error);
145
+ recordFailure(statuses, dataset.database, deps.now(), errorMessage);
146
+ failedCategories.push(category);
147
+ deps.error?.(`Error during: ${command}`, result.error);
148
+ }
149
+ else {
150
+ recordSuccess(statuses, dataset.database, deps.now());
151
+ okCategories.push(category);
152
+ }
153
+ if (statusFilePath !== null) {
154
+ writeStatusFile(statusFilePath, statuses);
155
+ }
156
+ }
157
+ if (failedCategories.length > 0) {
158
+ deps.warn?.(`\n⚠️ Warning: Some datasets failed to retrieve: ${failedCategories.join(", ")}. ` +
159
+ "Continuing with available datasets...\n");
160
+ }
161
+ if (okCategories.length === 0) {
162
+ deps.error?.("All datasets failed to retrieve; skipping data conversion.");
163
+ return 1;
164
+ }
165
+ const exitCode = runDownstreamScript(deps, `tsx src/scripts/convert_data.ts ${forwardArgs(parsedArgs, okCategories)}`) ??
166
+ runDownstreamScript(deps, `cross-env TZ='Etc/UTC' tsx src/scripts/retrieve_agenda.ts ${deps.rawArgs.join(" ")} --parseAgenda --silent`) ??
167
+ runDownstreamScript(deps, "cross-env TZ='Etc/UTC' tsx src/scripts/retrieve_cr_seance.ts " +
168
+ `${deps.rawArgs.join(" ")} --parseDebats --silent`) ??
169
+ runDownstreamScript(deps, "cross-env TZ='Etc/UTC' tsx src/scripts/retrieve_cr_commission.ts " +
170
+ `${deps.rawArgs.join(" ")} --parseDebats --silent`) ??
171
+ runDownstreamScript(deps, `tsx src/scripts/retrieve_videos.ts ${deps.rawArgs.join(" ")} --silent`) ??
172
+ runDownstreamScript(deps, `tsx src/scripts/retrieve_collaborateurs.ts ${deps.rawArgs.join(" ")} --silent`) ??
173
+ 0;
174
+ return exitCode;
175
+ }