@tricoteuses/senat 2.23.0 → 3.0.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.
Files changed (84) hide show
  1. package/lib/add-js-extensions-v2.d.ts +1 -0
  2. package/lib/add-js-extensions-v2.js +23 -0
  3. package/lib/add-js-extensions.d.ts +1 -0
  4. package/lib/add-js-extensions.js +17 -0
  5. package/lib/src/databases_postgres.js +1 -1
  6. package/lib/src/index.d.ts +24 -13
  7. package/lib/src/index.js +6 -1
  8. package/lib/src/loaders.d.ts +12 -12
  9. package/lib/src/loaders.js +4 -4
  10. package/lib/src/model/agenda.d.ts +1 -1
  11. package/lib/src/model/agenda.js +1 -1
  12. package/lib/src/model/ameli.js +1 -1
  13. package/lib/src/model/commission.d.ts +2 -2
  14. package/lib/src/model/commission.js +4 -4
  15. package/lib/src/model/debats.js +1 -1
  16. package/lib/src/model/documents.js +1 -1
  17. package/lib/src/model/dosleg.js +1 -1
  18. package/lib/src/model/index.d.ts +7 -7
  19. package/lib/src/model/index.js +7 -7
  20. package/lib/src/model/questions.js +1 -1
  21. package/lib/src/model/scrutins.js +1 -1
  22. package/lib/src/model/seance.d.ts +1 -1
  23. package/lib/src/model/seance.js +4 -4
  24. package/lib/src/model/sens.js +1 -1
  25. package/lib/src/parsers/texte.d.ts +1 -1
  26. package/lib/src/parsers/texte.js +2 -2
  27. package/lib/src/raw_types/ameli.d.ts +1651 -803
  28. package/lib/src/raw_types/ameli.js +1074 -5
  29. package/lib/src/raw_types/debats.d.ts +353 -180
  30. package/lib/src/raw_types/debats.js +266 -5
  31. package/lib/src/raw_types/dosleg.d.ts +2862 -1527
  32. package/lib/src/raw_types/dosleg.js +2005 -5
  33. package/lib/src/raw_types/questions.d.ts +671 -395
  34. package/lib/src/raw_types/questions.js +493 -5
  35. package/lib/src/raw_types/sens.d.ts +7743 -8148
  36. package/lib/src/raw_types/sens.js +4691 -5
  37. package/lib/src/scripts/convert_data.js +10 -10
  38. package/lib/src/scripts/convert_xml_to_html.js +1 -1
  39. package/lib/src/scripts/datautil.d.ts +1 -1
  40. package/lib/src/scripts/retrieve_agenda.js +9 -9
  41. package/lib/src/scripts/retrieve_cr_commission.js +8 -8
  42. package/lib/src/scripts/retrieve_cr_seance.d.ts +1 -1
  43. package/lib/src/scripts/retrieve_cr_seance.js +8 -8
  44. package/lib/src/scripts/retrieve_documents.d.ts +1 -1
  45. package/lib/src/scripts/retrieve_documents.js +7 -7
  46. package/lib/src/scripts/retrieve_open_data.js +155 -49
  47. package/lib/src/scripts/retrieve_senateurs_photos.js +4 -4
  48. package/lib/src/scripts/retrieve_videos.js +9 -9
  49. package/lib/src/scripts/shared/incremental_import_sql.js +1 -1
  50. package/lib/src/scripts/shared/prefixed_tables.d.ts +6 -3
  51. package/lib/src/scripts/shared/prefixed_tables.js +22 -16
  52. package/lib/src/scripts/shared/staging_import.d.ts +1 -1
  53. package/lib/src/scripts/validate_prefixed_tables.js +8 -9
  54. package/lib/src/types/ameli.d.ts +4 -4
  55. package/lib/src/types/debats.d.ts +2 -2
  56. package/lib/src/types/dosleg.d.ts +39 -39
  57. package/lib/src/types/questions.d.ts +2 -2
  58. package/lib/src/types/sens.d.ts +0 -2
  59. package/lib/src/types/texte.d.ts +1 -1
  60. package/lib/src/utils/cr_spliting.d.ts +1 -1
  61. package/lib/src/utils/cr_spliting.js +4 -4
  62. package/lib/src/utils/nvs-parsing.js +2 -2
  63. package/lib/src/utils/reunion_odj_building.d.ts +2 -2
  64. package/lib/src/utils/reunion_odj_building.js +2 -2
  65. package/lib/src/utils/reunion_parsing.d.ts +2 -2
  66. package/lib/src/utils/reunion_parsing.js +2 -2
  67. package/lib/src/utils/scoring.d.ts +3 -3
  68. package/lib/src/utils/scoring.js +2 -2
  69. package/lib/src/videos/config.d.ts +1 -1
  70. package/lib/src/videos/index.d.ts +5 -5
  71. package/lib/src/videos/index.js +5 -5
  72. package/lib/src/videos/match.d.ts +2 -2
  73. package/lib/src/videos/match.js +5 -5
  74. package/lib/src/videos/pipeline.d.ts +2 -2
  75. package/lib/src/videos/pipeline.js +4 -4
  76. package/lib/src/videos/search.d.ts +2 -2
  77. package/lib/src/videos/search.js +2 -2
  78. package/lib/tests/incrementalImportSql.test.js +4 -4
  79. package/lib/tests/prefixedTables.test.js +8 -15
  80. package/lib/tests/schemaVersion.test.js +1 -1
  81. package/lib/tests/test_iter_load.test.js +1 -1
  82. package/lib/tests/validatePrefixedTables.test.js +4 -4
  83. package/lib/tests/videoMatching.test.js +4 -4
  84. package/package.json +6 -2
@@ -2,16 +2,16 @@ import commandLineArgs from "command-line-args";
2
2
  import fs from "fs-extra";
3
3
  import path from "path";
4
4
  import pLimit from "p-limit";
5
- import * as git from "../git";
6
- import { datasets, EnabledDatasets, getEnabledDatasets } from "../datasets";
7
- import { DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER, DOCUMENT_METADATA_FILE, DOSLEG_DOSSIERS_FOLDER, SCRUTINS_FOLDER, RAPPORT_FOLDER, SENS_CIRCONSCRIPTIONS_FOLDER, SENS_ORGANISMES_FOLDER, SENS_SENATEURS_FOLDER, TEXTE_FOLDER, ENRICHED_TEXTE_FOLDER, } from "../loaders";
8
- import { findAllAmendements, findAllCirconscriptions, findAllDebats, findAllDossiers, findAllScrutins, findAllOrganismes, findAllQuestions, findAllSens, findAllTextes, findAllRapports, } from "../model";
9
- import { processRapport, processTexte } from "./retrieve_documents";
10
- import { buildActesLegislatifs } from "../model/dosleg";
11
- import { UNDEFINED_SESSION } from "../types/sessions";
12
- import { getSessionFromDate, getSessionFromSignet } from "./datautil";
13
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
14
- import { ensureAndClearDir } from "./shared/util";
5
+ import * as git from "../git.js";
6
+ import { datasets, EnabledDatasets, getEnabledDatasets } from "../datasets.js";
7
+ import { DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER, DOCUMENT_METADATA_FILE, DOSLEG_DOSSIERS_FOLDER, SCRUTINS_FOLDER, RAPPORT_FOLDER, SENS_CIRCONSCRIPTIONS_FOLDER, SENS_ORGANISMES_FOLDER, SENS_SENATEURS_FOLDER, TEXTE_FOLDER, ENRICHED_TEXTE_FOLDER, } from "../loaders.js";
8
+ import { findAllAmendements, findAllCirconscriptions, findAllDebats, findAllDossiers, findAllScrutins, findAllOrganismes, findAllQuestions, findAllSens, findAllTextes, findAllRapports, } from "../model/index.js";
9
+ import { processRapport, processTexte } from "./retrieve_documents.js";
10
+ import { buildActesLegislatifs } from "../model/dosleg.js";
11
+ import { UNDEFINED_SESSION } from "../types/sessions.js";
12
+ import { getSessionFromDate, getSessionFromSignet } from "./datautil.js";
13
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
14
+ import { ensureAndClearDir } from "./shared/util.js";
15
15
  let exitCode = 10; // 0: some data changed, 10: no modification
16
16
  const optionsDefinitions = [...commonOptions];
17
17
  const options = commandLineArgs(optionsDefinitions);
@@ -1,7 +1,7 @@
1
1
  import fs from "fs-extra";
2
2
  import path from "path";
3
3
  import commandLineArgs from "command-line-args";
4
- import { convertSenatXmlToHtml } from "../conversion_textes";
4
+ import { convertSenatXmlToHtml } from "../conversion_textes.js";
5
5
  const optionDefinitions = [
6
6
  { name: "input", alias: "i", type: String, defaultOption: true },
7
7
  { name: "output", alias: "o", type: String },
@@ -1,4 +1,4 @@
1
- import { Session } from "../types/sessions";
1
+ import { Session } from "../types/sessions.js";
2
2
  export declare const STANDARD_DATE_FORMAT = "yyyy-MM-dd";
3
3
  export declare const ID_DATE_FORMAT = "yyyyMMdd";
4
4
  export declare const AKN_IDENTIFICATION_STRUCTURE_REGEXP: RegExp;
@@ -2,15 +2,15 @@ import commandLineArgs from "command-line-args";
2
2
  import fs from "fs-extra";
3
3
  import { DateTime } from "luxon";
4
4
  import path from "path";
5
- import * as git from "../git";
6
- import { AGENDA_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders";
7
- import { parseAgendaFromFile } from "../model/agenda";
8
- import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions";
9
- import { ID_DATE_FORMAT } from "./datautil";
10
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
11
- import { fetchWithRetry } from "./shared/util";
12
- import { buildReunionsByBucket } from "../utils/reunion_parsing";
13
- import { buildSenatDossierIndex } from "../utils/reunion_odj_building";
5
+ import * as git from "../git.js";
6
+ import { AGENDA_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders.js";
7
+ import { parseAgendaFromFile } from "../model/agenda.js";
8
+ import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions.js";
9
+ import { ID_DATE_FORMAT } from "./datautil.js";
10
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
11
+ import { fetchWithRetry } from "./shared/util.js";
12
+ import { buildReunionsByBucket } from "../utils/reunion_parsing.js";
13
+ import { buildSenatDossierIndex } from "../utils/reunion_odj_building.js";
14
14
  const optionsDefinitions = [
15
15
  ...commonOptions,
16
16
  {
@@ -1,15 +1,15 @@
1
1
  import fs, { ensureDir } from "fs-extra";
2
2
  import path from "path";
3
3
  import * as cheerio from "cheerio";
4
- import { COMMISSION_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders";
5
- import { loadAgendaForDate, parseCommissionMetadataFromHtml, linkCRtoCommissionGroup } from "../utils/cr_spliting";
6
- import { cleanTitle, extractDayH3Sections, parseCommissionCRSectionFromDom } from "../model/commission";
4
+ import { COMMISSION_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders.js";
5
+ import { loadAgendaForDate, parseCommissionMetadataFromHtml, linkCRtoCommissionGroup } from "../utils/cr_spliting.js";
6
+ import { cleanTitle, extractDayH3Sections, parseCommissionCRSectionFromDom } from "../model/commission.js";
7
7
  import commandLineArgs from "command-line-args";
8
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
9
- import { sessionStartYearFromDate } from "../model/seance";
10
- import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions";
11
- import { ensureAndClearDir, fetchWithRetry } from "./shared/util";
12
- import { jaccard, jaccardTokenSim } from "../utils/scoring";
8
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
9
+ import { sessionStartYearFromDate } from "../model/seance.js";
10
+ import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions.js";
11
+ import { ensureAndClearDir, fetchWithRetry } from "./shared/util.js";
12
+ import { jaccard, jaccardTokenSim } from "../utils/scoring.js";
13
13
  import * as git from "../git.js";
14
14
  class CommissionCRDownloadError extends Error {
15
15
  constructor(message, url) {
@@ -3,7 +3,7 @@
3
3
  * - downloads the ZIP of comptes-rendus des débats (CRI) from data.senat.fr
4
4
  * - extracts XML files, distributes them by session/year
5
5
  */
6
- import { Session } from "../types/sessions";
6
+ import { Session } from "../types/sessions.js";
7
7
  import { CommandLineOptions } from "command-line-args";
8
8
  type RetrieveCriOptions = CommandLineOptions & {
9
9
  commit?: boolean;
@@ -8,14 +8,14 @@ import fs, { ensureDirSync } from "fs-extra";
8
8
  import path from "path";
9
9
  import StreamZip from "node-stream-zip";
10
10
  import * as cheerio from "cheerio";
11
- import { AGENDA_FOLDER, COMPTES_RENDUS_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders";
12
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
13
- import { parseCompteRenduIntervalFromFile, sessionStartYearFromDate } from "../model/seance";
14
- import { extractSommaireBlocks, makeReunionUid } from "../utils/reunion_parsing";
15
- import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions";
16
- import { ensureAndClearDir, fetchWithRetry } from "./shared/util";
17
- import { isNoiseBlock, scoreSommaireBlockForEvent } from "../utils/scoring";
18
- import { parseYYYYMMDD } from "../utils/date";
11
+ import { AGENDA_FOLDER, COMPTES_RENDUS_FOLDER, DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER } from "../loaders.js";
12
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
13
+ import { parseCompteRenduIntervalFromFile, sessionStartYearFromDate } from "../model/seance.js";
14
+ import { extractSommaireBlocks, makeReunionUid } from "../utils/reunion_parsing.js";
15
+ import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions.js";
16
+ import { ensureAndClearDir, fetchWithRetry } from "./shared/util.js";
17
+ import { isNoiseBlock, scoreSommaireBlockForEvent } from "../utils/scoring.js";
18
+ import { parseYYYYMMDD } from "../utils/date.js";
19
19
  import * as git from "../git.js";
20
20
  const optionsDefinitions = [
21
21
  ...commonOptions,
@@ -1,4 +1,4 @@
1
- import { DocumentMetadata } from "../types/texte";
1
+ import { DocumentMetadata } from "../types/texte.js";
2
2
  type DownloadOptions = {
3
3
  force?: boolean;
4
4
  formats?: string[];
@@ -2,13 +2,13 @@ import commandLineArgs from "command-line-args";
2
2
  import fs from "fs-extra";
3
3
  import { DateTime } from "luxon";
4
4
  import path from "path";
5
- import { convertSenatXmlToHtml } from "../conversion_textes";
6
- import * as git from "../git";
7
- import { DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER, ENRICHED_TEXTE_FOLDER, iterLoadSenatRapportUrls, iterLoadSenatTexteUrls, RAPPORT_FOLDER, TEXTE_FOLDER, } from "../loaders";
8
- import { parseExposeDesMotifs, parseTexte, parseTexteFromFile } from "../parsers/texte";
9
- import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions";
10
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
11
- import { ensureAndClearDir, fetchWithRetry, isOptionEmptyOrHasValue } from "./shared/util";
5
+ import { convertSenatXmlToHtml } from "../conversion_textes.js";
6
+ import * as git from "../git.js";
7
+ import { DATA_ORIGINAL_FOLDER, DATA_TRANSFORMED_FOLDER, ENRICHED_TEXTE_FOLDER, iterLoadSenatRapportUrls, iterLoadSenatTexteUrls, RAPPORT_FOLDER, TEXTE_FOLDER, } from "../loaders.js";
8
+ import { parseExposeDesMotifs, parseTexte, parseTexteFromFile } from "../parsers/texte.js";
9
+ import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions.js";
10
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
11
+ import { ensureAndClearDir, fetchWithRetry, isOptionEmptyOrHasValue } from "./shared/util.js";
12
12
  let exitCode = 10; // 0: some data changed, 10: no modification
13
13
  const optionsDefinitions = [
14
14
  ...commonOptions,
@@ -1,21 +1,23 @@
1
1
  import assert from "assert";
2
- import { execSync } from "child_process";
2
+ import { execFileSync } from "child_process";
3
3
  import commandLineArgs from "command-line-args";
4
4
  import fs from "fs-extra";
5
+ import { formatWithPrettier, makePgTsGenerator, markAsGenerated, processDatabase } from "kanel";
6
+ import { makeGenerateZodSchemas } from "kanel-zod";
5
7
  import path from "path";
6
8
  import StreamZip from "node-stream-zip";
7
9
  import readline from "readline";
8
10
  import { pipeline, Readable } from "stream";
9
11
  import { promisify } from "util";
10
12
  import * as windows1252 from "windows-1252";
11
- import config from "../config";
12
- import { getChosenDatasets, getEnabledDatasets } from "../datasets";
13
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
14
- import { buildIncrementalDatasetImportSql, buildNormalizeStagingSchemaSql } from "./shared/incremental_import_sql";
15
- import { normalizeGeneratedDefinition, prefixedName, senatSchemaName, stagingSchemaName, } from "./shared/prefixed_tables";
16
- import { buildEnsureSchemaVersionTableSql, buildIncrementSchemaVersionSql, buildSchemaStructureFingerprintQuery, } from "./shared/schema_version";
17
- import { buildExportStagingMetadataStatementsQuery } from "./shared/staging_metadata_sql";
18
- import { isCopyFromStdinLine, rewriteLineForStagingImport } from "./shared/staging_import";
13
+ import config from "../config.js";
14
+ import { getChosenDatasets, getEnabledDatasets } from "../datasets.js";
15
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
16
+ import { buildIncrementalDatasetImportSql, buildNormalizeStagingSchemaSql } from "./shared/incremental_import_sql.js";
17
+ import { buildGeneratedTableManifest, getGeneratedDefinitionPath, getGeneratedTableManifestPath, prefixedName, rawTypesDir, senatSchemaName, stagingSchemaName, stripDatasetPrefix, } from "./shared/prefixed_tables.js";
18
+ import { buildEnsureSchemaVersionTableSql, buildIncrementSchemaVersionSql, buildSchemaStructureFingerprintQuery, } from "./shared/schema_version.js";
19
+ import { buildExportStagingMetadataStatementsQuery } from "./shared/staging_metadata_sql.js";
20
+ import { isCopyFromStdinLine, rewriteLineForStagingImport } from "./shared/staging_import.js";
19
21
  const badWindows1252CharacterRegex = /[\u0080-\u009f]/g;
20
22
  const optionsDefinitions = [
21
23
  ...commonOptions,
@@ -27,7 +29,7 @@ const optionsDefinitions = [
27
29
  },
28
30
  {
29
31
  alias: "c",
30
- help: "create TypeScript interfaces from database schemas into src/raw_types_* directories",
32
+ help: "create TypeScript interfaces and Zod schemas from database tables into src/raw_types",
31
33
  name: "schema",
32
34
  type: Boolean,
33
35
  },
@@ -73,9 +75,6 @@ const stagingServerName = "staging_server";
73
75
  function isIncrementalImport(options) {
74
76
  return options["incremental"] === true;
75
77
  }
76
- function shellQuote(value) {
77
- return `'${value.replace(/'/g, `'"'"'`)}'`;
78
- }
79
78
  function connectionEnv(connection) {
80
79
  return {
81
80
  ...process.env,
@@ -124,7 +123,8 @@ async function runWithRetry(operation, options, retryOptions) {
124
123
  throw error;
125
124
  }
126
125
  if (!options["silent"]) {
127
- console.warn(`${retryOptions.label} hit a transient PostgreSQL lock error on attempt ${attempt}/${retryOptions.attempts}; retrying in ${delayMs}ms...`);
126
+ console.warn(`${retryOptions.label} hit a transient PostgreSQL lock error ` +
127
+ `on attempt ${attempt}/${retryOptions.attempts}; retrying in ${delayMs}ms...`);
128
128
  }
129
129
  await sleep(delayMs);
130
130
  attempt += 1;
@@ -132,18 +132,30 @@ async function runWithRetry(operation, options, retryOptions) {
132
132
  }
133
133
  }
134
134
  }
135
- function buildPsqlCommand(baseArgs, connection, options) {
136
- const sudoPrefix = options["sudo"] ? `sudo -u ${options["sudo"]} ` : "";
137
- return (`${sudoPrefix}psql --quiet ` +
138
- `-h ${shellQuote(connection.host)} ` +
139
- `-p ${shellQuote(String(connection.port))} ` +
140
- `-U ${shellQuote(connection.user)} ` +
141
- `-d ${shellQuote(connection.name)} ` +
142
- baseArgs);
135
+ function buildPsqlInvocation(baseArgs, connection, options) {
136
+ const psqlArgs = [
137
+ "--quiet",
138
+ "-h",
139
+ connection.host,
140
+ "-p",
141
+ String(connection.port),
142
+ "-U",
143
+ connection.user,
144
+ "-d",
145
+ connection.name,
146
+ ...baseArgs,
147
+ ];
148
+ if (!options["sudo"]) {
149
+ return { command: "psql", args: psqlArgs };
150
+ }
151
+ return {
152
+ command: "sudo",
153
+ args: ["-u", options["sudo"], "psql", ...psqlArgs],
154
+ };
143
155
  }
144
156
  function runPsqlFile(sqlFilePath, dataDir, options, connection, stopOnError = true) {
145
- const onErrorFlag = stopOnError ? "-v ON_ERROR_STOP=1 " : "";
146
- execSync(buildPsqlCommand(`${onErrorFlag}-f ${shellQuote(sqlFilePath)}`, connection, options), {
157
+ const { command, args } = buildPsqlInvocation([...(stopOnError ? ["-v", "ON_ERROR_STOP=1"] : []), "-f", sqlFilePath], connection, options);
158
+ execFileSync(command, args, {
147
159
  cwd: dataDir,
148
160
  env: connectionEnv(connection),
149
161
  encoding: "utf-8",
@@ -151,8 +163,9 @@ function runPsqlFile(sqlFilePath, dataDir, options, connection, stopOnError = tr
151
163
  });
152
164
  }
153
165
  function runPsqlCommand(command, dataDir, options, connection, stopOnError = true) {
154
- const onErrorFlag = stopOnError ? "-v ON_ERROR_STOP=1 " : "";
155
- execSync(buildPsqlCommand(`${onErrorFlag}-c ${shellQuote(command)}`, connection, options), {
166
+ const psqlCommand = command;
167
+ const { command: binary, args } = buildPsqlInvocation([...(stopOnError ? ["-v", "ON_ERROR_STOP=1"] : []), "-c", psqlCommand], connection, options);
168
+ execFileSync(binary, args, {
156
169
  cwd: dataDir,
157
170
  env: connectionEnv(connection),
158
171
  encoding: "utf-8",
@@ -160,8 +173,9 @@ function runPsqlCommand(command, dataDir, options, connection, stopOnError = tru
160
173
  });
161
174
  }
162
175
  function runPsqlQuery(command, dataDir, options, connection, stopOnError = true) {
163
- const onErrorFlag = stopOnError ? "-v ON_ERROR_STOP=1 " : "";
164
- return execSync(buildPsqlCommand(`${onErrorFlag}-At -c ${shellQuote(command)}`, connection, options), {
176
+ const psqlCommand = command;
177
+ const { command: binary, args } = buildPsqlInvocation([...(stopOnError ? ["-v", "ON_ERROR_STOP=1"] : []), "-At", "-c", psqlCommand], connection, options);
178
+ return execFileSync(binary, args, {
165
179
  cwd: dataDir,
166
180
  env: connectionEnv(connection),
167
181
  encoding: "utf-8",
@@ -171,7 +185,8 @@ function runPsqlQuery(command, dataDir, options, connection, stopOnError = true)
171
185
  function ensureStagingSchemaHasTables(dataset, dataDir, options, connection) {
172
186
  const stagingSchema = stagingSchemaName(dataset.database);
173
187
  const tableCount = Number.parseInt(runPsqlQuery(`SELECT count(*) FROM pg_tables WHERE schemaname = '${escapeSqlLiteral(stagingSchema)}'`, dataDir, options, connection).trim(), 10);
174
- assert(tableCount > 0, `Staging schema ${stagingSchema} is empty after importing ${dataset.database}. Aborting incremental merge to protect ${senatSchemaName}.`);
188
+ assert(tableCount > 0, `Staging schema ${stagingSchema} is empty after importing ${dataset.database}. ` +
189
+ `Aborting incremental merge to protect ${senatSchemaName}.`);
175
190
  }
176
191
  function ensureStagingDatabase(dataDir, options, runtime) {
177
192
  const maintenanceDb = process.env["PGDATABASE"] || "postgres";
@@ -207,16 +222,27 @@ function ensureDatabaseExists(connection, dataDir, options) {
207
222
  runPsqlCommand(`CREATE DATABASE ${connection.name} WITH OWNER ${connection.user}`, dataDir, options, maintenanceConnection);
208
223
  }
209
224
  function ensureForeignStagingServer(dataDir, options, runtime) {
210
- runPsqlCommand(`CREATE EXTENSION IF NOT EXISTS postgres_fdw`, dataDir, options, runtime.target);
225
+ runPsqlCommand("CREATE EXTENSION IF NOT EXISTS postgres_fdw", dataDir, options, runtime.target);
211
226
  runPsqlCommand(`DROP SERVER IF EXISTS ${stagingServerName} CASCADE`, dataDir, options, runtime.target, false);
212
227
  runPsqlCommand([
213
228
  `CREATE SERVER ${stagingServerName}`,
214
- `FOREIGN DATA WRAPPER postgres_fdw`,
215
- `OPTIONS (host '${escapeSqlLiteral(runtime.staging.host)}', dbname '${escapeSqlLiteral(runtime.staging.name)}', port '${escapeSqlLiteral(String(runtime.staging.port))}')`,
229
+ "FOREIGN DATA WRAPPER postgres_fdw",
230
+ [
231
+ "OPTIONS (",
232
+ `host '${escapeSqlLiteral(runtime.staging.host)}', `,
233
+ `dbname '${escapeSqlLiteral(runtime.staging.name)}', `,
234
+ `port '${escapeSqlLiteral(String(runtime.staging.port))}'`,
235
+ ")",
236
+ ].join(""),
216
237
  ].join(" "), dataDir, options, runtime.target);
217
238
  runPsqlCommand([
218
239
  `CREATE USER MAPPING FOR CURRENT_USER SERVER ${stagingServerName}`,
219
- `OPTIONS (user '${escapeSqlLiteral(runtime.staging.user)}', password '${escapeSqlLiteral(runtime.staging.password)}')`,
240
+ [
241
+ "OPTIONS (",
242
+ `user '${escapeSqlLiteral(runtime.staging.user)}', `,
243
+ `password '${escapeSqlLiteral(runtime.staging.password)}'`,
244
+ ")",
245
+ ].join(""),
220
246
  ].join(" "), dataDir, options, runtime.target);
221
247
  }
222
248
  function cleanupForeignStagingServer(dataDir, options, runtime) {
@@ -224,7 +250,7 @@ function cleanupForeignStagingServer(dataDir, options, runtime) {
224
250
  }
225
251
  function mountForeignStagingSchema(dataset, dataDir, options, runtime) {
226
252
  const stagingSchema = stagingSchemaName(dataset.database);
227
- const importForeignSchemaCommand = `IMPORT FOREIGN SCHEMA ${stagingSchema} FROM SERVER ${stagingServerName} INTO ${stagingSchema}`;
253
+ const importForeignSchemaCommand = `IMPORT FOREIGN SCHEMA ${stagingSchema} ` + `FROM SERVER ${stagingServerName} INTO ${stagingSchema}`;
228
254
  runPsqlCommand(`DROP SCHEMA IF EXISTS ${stagingSchema} CASCADE`, dataDir, options, runtime.target, false);
229
255
  runPsqlCommand(`CREATE SCHEMA ${stagingSchema}`, dataDir, options, runtime.target);
230
256
  try {
@@ -342,6 +368,97 @@ function listPrefixedTables(dataset, dataDir, options, runtime) {
342
368
  .map((tableName) => tableName.trim())
343
369
  .filter((tableName) => tableName.length > 0);
344
370
  }
371
+ function toPascalCase(text) {
372
+ return text.replace(/(^|_)([a-z0-9])/gi, (_match, _separator, letter) => letter.toUpperCase());
373
+ }
374
+ function toIdentifierName(tableName, columnName) {
375
+ return `${toPascalCase(tableName)}${toPascalCase(columnName)}`;
376
+ }
377
+ function trimComment(comment) {
378
+ const trimmed = comment?.trim();
379
+ return trimmed ? trimmed : undefined;
380
+ }
381
+ async function generateRawTypes(dataset, runtime, prefixedTables) {
382
+ await fs.ensureDir(rawTypesDir);
383
+ const definitionFilePath = getGeneratedDefinitionPath(dataset.database);
384
+ const manifestFilePath = getGeneratedTableManifestPath(dataset.database);
385
+ const datasetPrefix = `${dataset.database}_`;
386
+ const datasetOutputPath = path.join(rawTypesDir, dataset.database);
387
+ const generateZodSchemas = makeGenerateZodSchemas({
388
+ castToSchema: true,
389
+ getZodIdentifierMetadata: (column, details) => ({
390
+ name: `${toIdentifierName(stripDatasetPrefix(details.name, dataset.database), column.name)}Schema`,
391
+ }),
392
+ getZodSchemaMetadata: (details, generateFor) => {
393
+ const baseName = toPascalCase(stripDatasetPrefix(details.name, dataset.database));
394
+ const suffix = generateFor === "selector" || generateFor === undefined ? "" : toPascalCase(generateFor);
395
+ return {
396
+ name: `${baseName}${suffix}Schema`,
397
+ path: datasetOutputPath,
398
+ };
399
+ },
400
+ });
401
+ await fs.remove(definitionFilePath);
402
+ await fs.remove(manifestFilePath);
403
+ await processDatabase({
404
+ connection: {
405
+ database: runtime.target.name,
406
+ host: runtime.target.host,
407
+ password: runtime.target.password,
408
+ port: runtime.target.port,
409
+ user: runtime.target.user,
410
+ },
411
+ filter: (pgType) => pgType.schemaName === senatSchemaName && pgType.name.startsWith(datasetPrefix),
412
+ generators: [
413
+ makePgTsGenerator({
414
+ filter: (pgType) => pgType.schemaName === senatSchemaName && pgType.name.startsWith(datasetPrefix),
415
+ generateIdentifierType: (column, details, builtinType) => {
416
+ const tableName = stripDatasetPrefix(details.name, dataset.database);
417
+ return {
418
+ ...builtinType,
419
+ comment: undefined,
420
+ name: toIdentifierName(tableName, column.name),
421
+ typeDefinition: builtinType.typeDefinition.map((typeDefinition) => typeDefinition.replace(/ & \{ __flavor\?: '[^']+' \}/g, "").replace(/ & \{ __brand: '[^']+' \}/g, "")),
422
+ };
423
+ },
424
+ getMetadata: (details, generateFor, builtinMetadata) => {
425
+ const baseName = toPascalCase(stripDatasetPrefix(details.name, dataset.database));
426
+ const suffix = generateFor === "selector" || generateFor === undefined ? "" : toPascalCase(generateFor);
427
+ const tableComment = trimComment(details.comment);
428
+ return {
429
+ ...builtinMetadata,
430
+ comment: tableComment ? [tableComment] : undefined,
431
+ exportAs: "named",
432
+ name: `${baseName}${suffix}`,
433
+ path: datasetOutputPath,
434
+ };
435
+ },
436
+ getPropertyMetadata: (property, _details, generateFor, builtinMetadata) => {
437
+ const comment = trimComment(property.comment);
438
+ const defaultComment = generateFor === "initializer" && property.defaultValue !== null && property.defaultValue !== undefined
439
+ ? `Default value: ${property.defaultValue}`
440
+ : undefined;
441
+ const comments = [comment, defaultComment].filter((value) => value !== undefined);
442
+ return {
443
+ ...builtinMetadata,
444
+ comment: comments.length > 0 ? comments : undefined,
445
+ };
446
+ },
447
+ preRenderHooks: [generateZodSchemas],
448
+ }),
449
+ ],
450
+ outputPath: rawTypesDir,
451
+ postRenderHooks: [markAsGenerated, formatWithPrettier],
452
+ schemaNames: [senatSchemaName],
453
+ typescriptConfig: {
454
+ enumStyle: "literal-union",
455
+ tsModuleFormat: "esm",
456
+ },
457
+ });
458
+ const generatedDefinition = await fs.readFile(definitionFilePath, { encoding: "utf8" });
459
+ await fs.writeFile(definitionFilePath, generatedDefinition.replace(/\r\n/g, "\n"));
460
+ await fs.writeFile(manifestFilePath, buildGeneratedTableManifest(dataset.database, prefixedTables));
461
+ }
345
462
  async function downloadFile(url, dest) {
346
463
  const response = await fetch(url);
347
464
  if (!response.ok) {
@@ -474,7 +591,7 @@ async function retrieveDataset(dataDir, dataset, options, runtime) {
474
591
  repairedSqlWriter.end();
475
592
  await fs.move(repairedSqlFilePath, sqlFilePath, { overwrite: true });
476
593
  }
477
- if (options["all"] || options["import"] || options["schema"]) {
594
+ if (options["all"] || options["import"]) {
478
595
  if (!options["silent"]) {
479
596
  console.log(`Merging ${dataset.title}: ${sqlFilename} into ${runtime.target.name}.${senatSchemaName}...`);
480
597
  }
@@ -485,24 +602,13 @@ async function retrieveDataset(dataDir, dataset, options, runtime) {
485
602
  }
486
603
  }
487
604
  if (options["schema"]) {
488
- const definitionsDir = path.resolve("src", "raw_types_schemats");
489
- assert(fs.statSync(definitionsDir).isDirectory());
605
+ await fs.ensureDir(rawTypesDir);
490
606
  if (!options["silent"]) {
491
607
  console.log(`Creating TypeScript definitions from prefixed ${senatSchemaName} tables ` +
492
608
  `for '${dataset.database}' in database '${runtime.target.name}'...`);
493
609
  }
494
- const dbConnectionString = `postgres://${runtime.target.user}:${runtime.target.password}` +
495
- `@${runtime.target.host}:${runtime.target.port}/${runtime.target.name}`;
496
- const definitionFilePath = path.join(definitionsDir, `${dataset.database}.ts`);
497
- const tables = listPrefixedTables(dataset, dataDir, options, runtime);
498
- const tableOptions = tables.map((tableName) => `-t ${tableName}`).join(" ");
499
- execSync(`npx schemats generate -c ${dbConnectionString} -s ${senatSchemaName} ${tableOptions} -o ${definitionFilePath}`, {
500
- env: process.env,
501
- encoding: "utf-8",
502
- });
503
- const definition = fs.readFileSync(definitionFilePath, { encoding: "utf8" });
504
- const definitionRepaired = normalizeGeneratedDefinition(definition, dataset.database);
505
- fs.writeFileSync(definitionFilePath, definitionRepaired);
610
+ const prefixedTables = listPrefixedTables(dataset, dataDir, options, runtime);
611
+ await generateRawTypes(dataset, runtime, prefixedTables);
506
612
  }
507
613
  }
508
614
  function buildRuntimeContext() {
@@ -6,10 +6,10 @@ import path from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  // import stream from "stream"
8
8
  // import util from "util"
9
- import * as git from "../git";
10
- import { findActif as findActifSenateurs } from "../model/sens";
11
- import { slugify } from "../strings";
12
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
9
+ import * as git from "../git.js";
10
+ import { findActif as findActifSenateurs } from "../model/sens.js";
11
+ import { slugify } from "../strings.js";
12
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
13
13
  const optionsDefinitions = [
14
14
  ...commonOptions,
15
15
  {
@@ -4,16 +4,16 @@ import fs from "fs-extra";
4
4
  import fsp from "fs/promises";
5
5
  import path from "path";
6
6
  import * as git from "../git.js";
7
- import { AGENDA_FOLDER, iterLoadSenatAgendas } from "../loaders";
8
- import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions";
9
- import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
10
- import { getAgendaSegmentTimecodes, buildSenatVodMasterM3u8FromNvs } from "../utils/nvs-parsing";
11
- import { epochToParisDateTime, isAmbiguousTimeOriginal, toTargetEpoch } from "../utils/date";
7
+ import { AGENDA_FOLDER, iterLoadSenatAgendas } from "../loaders.js";
8
+ import { getSessionsFromStart, UNDEFINED_SESSION } from "../types/sessions.js";
9
+ import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers.js";
10
+ import { getAgendaSegmentTimecodes, buildSenatVodMasterM3u8FromNvs } from "../utils/nvs-parsing.js";
11
+ import { epochToParisDateTime, isAmbiguousTimeOriginal, toTargetEpoch } from "../utils/date.js";
12
12
  import { pathToFileURL } from "url";
13
- import { fetchCandidatesForAgenda, fetchText } from "../videos/search";
14
- import { matchAgendaToVideo } from "../videos/match";
15
- import { SENAT_DATAS_ROOT, STATS, VIDEOS_ROOT_FOLDER, weights } from "../videos/config";
16
- import { processBisIfNeeded, processOneReunionMatch, writeIfChanged } from "../videos";
13
+ import { fetchCandidatesForAgenda, fetchText } from "../videos/search.js";
14
+ import { matchAgendaToVideo } from "../videos/match.js";
15
+ import { SENAT_DATAS_ROOT, STATS, VIDEOS_ROOT_FOLDER, weights } from "../videos/config.js";
16
+ import { processBisIfNeeded, processOneReunionMatch, writeIfChanged } from "../videos/index.js";
17
17
  const optionsDefinitions = [...commonOptions];
18
18
  const options = commandLineArgs(optionsDefinitions);
19
19
  let exitCode = 10; // 0: some data changed, 10: no modification
@@ -1,4 +1,4 @@
1
- import { prefixedName, senatSchemaName, stagingSchemaName } from "./prefixed_tables";
1
+ import { prefixedName, senatSchemaName, stagingSchemaName } from "./prefixed_tables.js";
2
2
  function escapeSqlLiteral(value) {
3
3
  return value.replace(/'/g, "''");
4
4
  }
@@ -1,7 +1,10 @@
1
1
  export declare const senatSchemaName = "senat";
2
+ export declare const rawTypesDir: string;
2
3
  export declare function prefixedName(datasetName: string, relationName: string): string;
3
4
  export declare function stagingSchemaName(datasetName: string): string;
4
5
  export declare function escapeRegExp(text: string): string;
5
- export declare function normalizeGeneratedDefinition(definition: string, datasetName: string): string;
6
- export declare function extractTableNamesFromGeneratedDefinition(definition: string): string[];
7
- export declare function extractPrefixedTableNamesFromGeneratedDefinition(definition: string, datasetName: string): string[];
6
+ export declare function stripDatasetPrefix(relationName: string, datasetName: string): string;
7
+ export declare function getGeneratedDefinitionPath(datasetName: string): string;
8
+ export declare function getGeneratedTableManifestPath(datasetName: string): string;
9
+ export declare function buildGeneratedTableManifest(datasetName: string, prefixedTables: string[]): string;
10
+ export declare function extractPrefixedTableNamesFromGeneratedManifest(manifest: string, datasetName: string): string[];
@@ -1,4 +1,6 @@
1
+ import path from "path";
1
2
  export const senatSchemaName = "senat";
3
+ export const rawTypesDir = path.resolve("src", "raw_types");
2
4
  export function prefixedName(datasetName, relationName) {
3
5
  return `${datasetName}_${relationName}`;
4
6
  }
@@ -8,23 +10,27 @@ export function stagingSchemaName(datasetName) {
8
10
  export function escapeRegExp(text) {
9
11
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
12
  }
11
- export function normalizeGeneratedDefinition(definition, datasetName) {
13
+ export function stripDatasetPrefix(relationName, datasetName) {
12
14
  const datasetPrefix = `${datasetName}_`;
13
- return definition
14
- .replace(/\r\n/g, "\n")
15
- .replace(/AUTO-GENERATED FILE @ \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, "AUTO-GENERATED FILE")
16
- .replace(new RegExp(`\\b${escapeRegExp(datasetPrefix)}([A-Za-z0-9_]+Fields)\\b`, "g"), "$1")
17
- .replace(new RegExp(`\\b${escapeRegExp(datasetPrefix)}([A-Za-z0-9_]+)\\b`, "g"), "$1");
18
- }
19
- export function extractTableNamesFromGeneratedDefinition(definition) {
20
- const flattenedDefinition = definition.replace(/\n \*/g, " ");
21
- const match = flattenedDefinition.match(/\$ schemats generate .*?((?: -t [^ ]+)+) -s [^\s]+/s);
22
- if (!match) {
15
+ return relationName.startsWith(datasetPrefix) ? relationName.slice(datasetPrefix.length) : relationName;
16
+ }
17
+ export function getGeneratedDefinitionPath(datasetName) {
18
+ return path.join(rawTypesDir, `${datasetName}.ts`);
19
+ }
20
+ export function getGeneratedTableManifestPath(datasetName) {
21
+ return path.join(rawTypesDir, `${datasetName}.tables.json`);
22
+ }
23
+ export function buildGeneratedTableManifest(datasetName, prefixedTables) {
24
+ const manifest = {
25
+ dataset: datasetName,
26
+ prefixedTables: [...new Set(prefixedTables)],
27
+ };
28
+ return JSON.stringify(manifest, null, 2) + "\n";
29
+ }
30
+ export function extractPrefixedTableNamesFromGeneratedManifest(manifest, datasetName) {
31
+ const parsed = JSON.parse(manifest);
32
+ if (parsed.dataset !== datasetName || !Array.isArray(parsed.prefixedTables)) {
23
33
  return [];
24
34
  }
25
- const tables = Array.from(match[1].matchAll(/-t ([^ ]+)/g), ([, tableName]) => tableName);
26
- return [...new Set(tables)];
27
- }
28
- export function extractPrefixedTableNamesFromGeneratedDefinition(definition, datasetName) {
29
- return extractTableNamesFromGeneratedDefinition(definition).map((tableName) => prefixedName(datasetName, tableName));
35
+ return [...new Set(parsed.prefixedTables.filter((tableName) => typeof tableName === "string"))];
30
36
  }
@@ -1,3 +1,3 @@
1
- import type { Dataset } from "../../datasets";
1
+ import type { Dataset } from "../../datasets.js";
2
2
  export declare function isCopyFromStdinLine(line: string): boolean;
3
3
  export declare function rewriteLineForStagingImport(line: string, dataset: Dataset, stagingSchema: string, inCopyData?: boolean): string;
@@ -1,10 +1,9 @@
1
1
  import commandLineArgs from "command-line-args";
2
2
  import fs from "fs-extra";
3
- import path from "path";
4
- import { sql } from "../databases_postgres";
5
- import { getChosenDatasets, getEnabledDatasets } from "../datasets";
6
- import { categoriesOption, silentOption } from "./shared/cli_helpers";
7
- import { extractPrefixedTableNamesFromGeneratedDefinition, senatSchemaName } from "./shared/prefixed_tables";
3
+ import { sql } from "../databases_postgres.js";
4
+ import { getChosenDatasets, getEnabledDatasets } from "../datasets.js";
5
+ import { categoriesOption, silentOption } from "./shared/cli_helpers.js";
6
+ import { extractPrefixedTableNamesFromGeneratedManifest, getGeneratedTableManifestPath, senatSchemaName, } from "./shared/prefixed_tables.js";
8
7
  const optionsDefinitions = [categoriesOption, silentOption];
9
8
  const options = commandLineArgs(optionsDefinitions);
10
9
  async function listPrefixedSenatTables(dataset) {
@@ -19,12 +18,12 @@ async function listPrefixedSenatTables(dataset) {
19
18
  return rows.map(({ relation_name }) => relation_name);
20
19
  }
21
20
  function getGeneratedDefinitionPath(dataset) {
22
- return path.resolve("src", "raw_types_schemats", `${dataset.database}.ts`);
21
+ return getGeneratedTableManifestPath(dataset.database);
23
22
  }
24
23
  function listExpectedPrefixedSenatTables(dataset) {
25
- const definitionFilePath = getGeneratedDefinitionPath(dataset);
26
- const definition = fs.readFileSync(definitionFilePath, { encoding: "utf8" });
27
- return extractPrefixedTableNamesFromGeneratedDefinition(definition, dataset.database);
24
+ const manifestFilePath = getGeneratedDefinitionPath(dataset);
25
+ const manifest = fs.readFileSync(manifestFilePath, { encoding: "utf8" });
26
+ return extractPrefixedTableNamesFromGeneratedManifest(manifest, dataset.database);
28
27
  }
29
28
  async function listColumns(schema, relationName) {
30
29
  const rows = await sql `