diff --git a/README.md b/README.md index 1c03b3b742972c50f2f2b05e15f5d4370fff6e9b..eb0693caa066a08d0a463968d24a2ecb6a27a407 100644 --- a/README.md +++ b/README.md @@ -233,12 +233,26 @@ $: console.log(parameterSmicMensuel, smicValue) ### Générer les YAML de test OpenFisca à partir des cas-type LexImpact ```bash -npx tsx src/scripts/generate_openfisca_tests_yaml.ts -y 2025 --openfisca_venv /path/to/openfisca-france/.venv ../../openfisca-france/tests/leximpact/ +npx tsx src/scripts/generate_openfisca_tests_yaml.ts \ +-y 2024 \ +--openfisca_venv /path/to/openfisca-france/.venv \ +--openfisca_json_model_path ../openfisca-json-model \ +--leximpact_openfisca_json_path ../leximpact-socio-fiscal-openfisca-json \ +../../openfisca-france/tests/leximpact/2024/ ``` -- Le paramètre `year` (-y) générera les variables d'output pour l'année `year` -- Le paramètre `openfisca_venv` (-e) doit correspondre au chemin d'un venv dans lequel openfisca-france est installé. Il doit être possible d'y exécuter `openfisca test`. -- L'argument par défaut `outdir` est le chemin où l'on veut exporter les YAML générés. +Vous aurez besoin : + +- d'une install fonctionnelle d'`openfisca-france` +- de clones locaux à jour des dépôts `openfisca-json-model` et `leximpact-socio-fiscal-openfisca-json` + +Paramètres attendus : + +- l'argument `year` (-y) générera les cas-types avec la valeur du SMIC au 01-01-`year`, les variables d'output pour l'année `year`, et des valeurs d'input pour les années `year`, `year`-1 et `year`-2 selon les données du cas-type. +- l'argument `openfisca_venv` (-e) doit correspondre au chemin d'un venv dans lequel openfisca-france est installé. Il doit être possible d'y exécuter `openfisca test`. +- l'argument `openfisca_json_model_path` doit contenir le chemin vers un clone à jour du dépôt "openfisca-json-model" +- l'argument `leximpact_openfisca_json_path` doit contenir le chemin vers un clone à jour du dépôt "leximpact-socio-fiscal-openfisca-json" +- L'argument par défaut `outdir` est le chemin où l'on veut exporter les YAML générés. Le répertoire fourni sera créé s'il n'existe pas. Les YAML générés contiennent, pour chaque cas-type, toutes les variables d'entrées du cas-type dans le fichier `test_cases.json`. Les valeurs sont mensualisées pour toutes les variables mensuelles. @@ -248,3 +262,5 @@ La section `output:` du YAML contient toutes les variables calculées, présente Les valeurs des variables de type Enum, sont remplacées par leur index dans les valeurs possibles de l'Enum. Les valeurs générées en sortie sont celles que retourne l'API d'openFisca-France **corrigées par les valeurs que retournent `openfisca test`** dans le cas où elles diffèrent. + +Toutes les valeurs sont arrondies au centime, sauf la valeur d'entrée du SMIC, arrondie à l'euro. diff --git a/src/scripts/generate_openfisca_tests_yaml.ts b/src/scripts/generate_openfisca_tests_yaml.ts index fae7ddefd727f1f9fc12e948ce6c5e656889b872..e4fb99ee2e3774e9aaf15ab23c5d0e1e486739d1 100644 --- a/src/scripts/generate_openfisca_tests_yaml.ts +++ b/src/scripts/generate_openfisca_tests_yaml.ts @@ -3,7 +3,6 @@ import commandLineArgs from "command-line-args" import fs from "fs-extra" import path from "path" import YAML from "js-yaml" -import testCasesCoreUnknown from "@leximpact/socio-fiscal-openfisca-json/test_cases.json" import { summaryCalculatedVariablesName, otherCalculatedVariablesName, @@ -41,6 +40,16 @@ const optionsDefinitions = [ name: "openfisca_venv", type: String, }, + { + help: "Path to openfisca-json-model. Be careful to clone the repo and git pull before.", + name: "openfisca_json_model_path", + type: String, + }, + { + help: "Path to leximpact-socio_fiscal-openfisca-json. Be careful to clone the repo and git pull before.", + name: "leximpact_openfisca_json_path", + type: String, + }, { alias: "t", help: "Single Test-Case to run", @@ -56,6 +65,64 @@ const optionsDefinitions = [ ] const options = commandLineArgs(optionsDefinitions) +function getSmicForYear( + smicValues: Record<string, number>, + year: number, +): number | null { + const targetDate = `${year}-01-01` + + const validDates = Object.keys(smicValues) + .filter((date) => date <= targetDate) + .sort() + + if (validDates.length === 0) { + return null + } + + const lastValidDate = validDates[validDates.length - 1] + return smicValues[lastValidDate] +} + +async function runMergeTestCases( + openFiscaJsonModelPath: string, + leximpactOpenFiscaJsonPath: string, + smicValue: number, + year: number, +): Promise<any> { + return new Promise<void>((resolve, reject) => { + const scriptPath = path.join( + openFiscaJsonModelPath, + "packages", + "tools", + "src", + "scripts", + "merge_test_cases.ts", + ) + const args = [ + scriptPath, + leximpactOpenFiscaJsonPath, + "--smic=" + smicValue.toString(), + "--year", + year.toString(), + ] + const processMergeTestCases = spawn("npx", ["tsx", ...args], { + stdio: "inherit", + shell: true, + }) + + processMergeTestCases.on("close", (code) => { + if (code !== 0) { + reject( + "Unable to generate new test_cases.json. Call to script merge_test_cases.ts failed with code : " + + code, + ) + } else { + resolve() + } + }) + }) +} + async function fetchWithRetries( url: string, options: RequestInit, @@ -513,6 +580,12 @@ async function main() { const outdir = options.outdir const singleTestCase = options.testcase const openFiscaVenvPath = options.openfisca_venv + const openFiscaJsonModelPath = options.openfisca_json_model_path + const leximpactOpenFiscaJsonPath = options.leximpact_openfisca_json_path + + if (!fs.existsSync(outdir)) { + fs.mkdirSync(outdir, { recursive: true }) + } const variablesFromDecompositions = [ ...summaryCalculatedVariablesName, @@ -537,13 +610,53 @@ async function main() { const openFiscaVariablesList = await openFiscaVariablesListApiCall.json() - const variablesToCalculate = variablesFromDecompositions.filter( - (variable: string) => openFiscaVariablesList.hasOwnProperty(variable), + const smicValuesApiCall = await fetch( + "https://api.fr.openfisca.org/latest/parameter/marche_travail/salaire_minimum/smic/smic_b_mensuel", + { + method: "GET", + }, + ) + if (!smicValuesApiCall.ok) { + console.error( + `Error ${smicValuesApiCall.status} while calling Openfisca France API`, + ) + return + } + + const smicValues = (await smicValuesApiCall.json()).values + const monthlySmicValueForYear = getSmicForYear(smicValues, year) + if (monthlySmicValueForYear === null) { + throw new Error("Unable to retrieve SMIC value for year", year) + } + const smicValueForYear = Math.round(monthlySmicValueForYear * 12) + console.info( + "SMIC value retrieved from openFisca API. Will be using", + smicValueForYear, ) - const testCasesCore = testCasesCoreUnknown as unknown as Situation[] + console.info("Generating test cases...") + try { + await runMergeTestCases( + openFiscaJsonModelPath, + leximpactOpenFiscaJsonPath, + smicValueForYear, + year, + ) + } catch (error) { + console.error("Error : ", error) + } + + const testCasesCoreData = await fs.readFile( + path.join(leximpactOpenFiscaJsonPath, "test_cases.json"), + "utf-8", + ) + const testCasesCore = JSON.parse(testCasesCoreData) const testCaseCount = testCasesCore.length + const variablesToCalculate = variablesFromDecompositions.filter( + (variable: string) => openFiscaVariablesList.hasOwnProperty(variable), + ) + let processedCounter = 0 for (const testCase of testCasesCore) { @@ -720,7 +833,10 @@ async function main() { ) } else { ok = true - console.info("YAML file written :", outdir, testCase.id + ".yml") + console.info( + "YAML file written :", + path.join(outdir, testCase.id + ".yml"), + ) } } catch (error) { console.error(error)