diff --git a/example.env b/example.env index 312615c539623859e2099553a9dac7c023814b4e..c5368cd77defe2d1ce34fae54367d41c23fbea3a 100644 --- a/example.env +++ b/example.env @@ -15,7 +15,7 @@ BASE_URL="http://localhost:3000" CHILDREN_KEY="enfants" # Path to file containing decompositions in JSON format -DECOMPOSITION_PATH="../openfisca-france-json/decompositions_customized.json" +DECOMPOSITION_PATH="../openfisca-france-json/decompositions/decompositions_customized.json" # Name of variable used as root of decomposition DECOMPOSITION_ROOT="revenu_disponible" @@ -61,3 +61,6 @@ PORTAL_URL="https://leximpact.an.fr/" PROXY=false TITLE="Simulateur socio-fiscal" + +# Path to file containing description of waterfalls in JSON format +WATERFALLS_PATH="../openfisca-france-json/custom/waterfalls.json" diff --git a/package-lock.json b/package-lock.json index 9a8578f1a77dfb5457558c5924bcb3297e4ba26a..56f58d5a5d374679929c8cf8141e3db7bb02008d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@auditors/core": "^0.1.7", "@fontsource/lato": "^4.3.0", "@fontsource/lora": "^4.3.0", - "@openfisca/ast": "^0.12.0", + "@openfisca/ast": "^0.12.2", "@sveltejs/adapter-node": "next", "@sveltejs/kit": "next", "@tailwindcss/forms": "^0.3.2", @@ -1907,9 +1907,9 @@ } }, "node_modules/@openfisca/ast": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.12.0.tgz", - "integrity": "sha512-ExicDxD58M+Rc7gpgmWJGTug1dmwgnb+uD4adr1EAyzjzFpBZbI0AcopNnpUu/bKKFGQ/yqSeOtzjZ9byr9Atw==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.12.2.tgz", + "integrity": "sha512-ZuCd8e7LkejDR8VgSp2Xwjmp2H2ypU9aF2vogt278XuxSMCs/mseMhPxSWXf+eMaaYnatDTfrDHBUcKKOYbbCA==", "dev": true, "dependencies": { "@auditors/core": "^0.1.11", @@ -8235,9 +8235,9 @@ } }, "@openfisca/ast": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.12.0.tgz", - "integrity": "sha512-ExicDxD58M+Rc7gpgmWJGTug1dmwgnb+uD4adr1EAyzjzFpBZbI0AcopNnpUu/bKKFGQ/yqSeOtzjZ9byr9Atw==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.12.2.tgz", + "integrity": "sha512-ZuCd8e7LkejDR8VgSp2Xwjmp2H2ypU9aF2vogt278XuxSMCs/mseMhPxSWXf+eMaaYnatDTfrDHBUcKKOYbbCA==", "dev": true, "requires": { "@auditors/core": "^0.1.11", diff --git a/package.json b/package.json index 1daa3bfef989718e040bd069983ab7b97c8ddda6..08c8d1b8ccc3ecc688e96fe4543a75067d403542 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@auditors/core": "^0.1.7", "@fontsource/lato": "^4.3.0", "@fontsource/lora": "^4.3.0", - "@openfisca/ast": "^0.12.0", + "@openfisca/ast": "^0.12.2", "@sveltejs/adapter-node": "next", "@sveltejs/kit": "next", "@tailwindcss/forms": "^0.3.2", diff --git a/src/hooks.ts b/src/hooks.ts index 5b9859773c1c16ef6e8777a5b966477ddd31fa70..19aa137e1d87a6b611e0764382a7d07d02bc1455 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -2,7 +2,7 @@ import type { GetSession, Handle } from "@sveltejs/kit" import fetch, { Response, Request, Headers } from "node-fetch" import config from "$lib/server/config" -import { decompositionCoreByName } from "$lib/server/decompositions" +import { decompositionCoreByName, waterfalls } from "$lib/server/decompositions" import { entityByKey, personEntityKey } from "$lib/server/entities" import { metadata } from "$lib/server/metadata" import { oauth2Authenticator } from "$lib/server/oauth2" @@ -64,10 +64,10 @@ export const getSession: GetSession<{}, Session> = async (request) => { }, personEntityKey, portalUrl: config.portalUrl, - rootDecompositionName: config.rootDecompositionName, testCases, title: config.title, user, + waterfalls, } } diff --git a/src/lib/auditors/config.ts b/src/lib/auditors/config.ts index 7db652ef2280e468c2cb12109c4daf5b83102ec0..08b6474024fad3bff63cf10b1c68f615f3b83cae 100644 --- a/src/lib/auditors/config.ts +++ b/src/lib/auditors/config.ts @@ -70,8 +70,8 @@ export function auditConfig( "decompositionsPath", "familyEntityKey", "jsonDir", - "rootDecompositionName", "title", + "waterfallsPath", ]) { audit.attribute( data, diff --git a/src/lib/components/latchkeys/Arrow.svelte b/src/lib/components/latchkeys/Arrow.svelte index c110a0ca0565d86935509cb35c104b92dc038ba0..4105a56c2069eb0129cbdc58fc78637f38969976 100644 --- a/src/lib/components/latchkeys/Arrow.svelte +++ b/src/lib/components/latchkeys/Arrow.svelte @@ -47,10 +47,11 @@ // const newSelfTargetAProps = getContext("newSelfTargetAProps") as ( // url: string, // ) => SelfTargetAProps - const rootDecompositionName = $session.rootDecompositionName // const showColoredRects = getContext("showColoredRects") as Writable<boolean> const testCaseIndex = getContext("testCaseIndex") as Writable<number> const verticalLineStrokeWidth = 2 + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root $: aggregate = item.aggregate diff --git a/src/lib/components/latchkeys/AxisY.svelte b/src/lib/components/latchkeys/AxisY.svelte index b2576c567b6f1d23ebacd18e2f6a492f5ab8daa0..3b49443d710a3ff051a38dbbaf083e9de8859ef0 100644 --- a/src/lib/components/latchkeys/AxisY.svelte +++ b/src/lib/components/latchkeys/AxisY.svelte @@ -18,8 +18,9 @@ "decompositionByNameArray", ) as Writable<DecompositionByName[]> const dispatch = createEventDispatcher() - const rootDecompositionName = $session.rootDecompositionName const testCaseIndex = getContext("testCaseIndex") as Writable<number> + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root $: yHalfBandwidth = $yScale.bandwidth() / 2 diff --git a/src/lib/components/latchkeys/Latchkey.svelte b/src/lib/components/latchkeys/Latchkey.svelte index f643eb645cb15a4d182bc2b69801d67acdc676e5..dd3d377c86b3fc08f37558574d4e2709bc62ff79 100644 --- a/src/lib/components/latchkeys/Latchkey.svelte +++ b/src/lib/components/latchkeys/Latchkey.svelte @@ -19,9 +19,10 @@ const decompositionByNameArray = getContext( "decompositionByNameArray", ) as Writable<DecompositionByName[]> - const rootDecompositionName = $session.rootDecompositionName const showNulls = getContext("showNulls") as Writable<boolean> const testCaseIndex = getContext("testCaseIndex") as Writable<number> + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root // const showColoredRects = writable(false) // setContext("showColoredRects", showColoredRects) diff --git a/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte b/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte index 5599d6cb1d6c9d5ea7041adfe8a30ada2d8e8943..8bbd58717fab08e949c8cf0ee7cdb6cf6d000c37 100644 --- a/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte +++ b/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte @@ -36,10 +36,11 @@ minimumFractionDigits: 0, style: "currency", }) - const rootDecompositionName = $session.rootDecompositionName const showNulls = getContext("showNulls") as Writable<boolean> const testCaseIndex = getContext("testCaseIndex") as Writable<number> let waterfallWidth = 100 + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root $: decompositionEvaluationAndDepthTriples = [ ...walkVisibleEvaluations( diff --git a/src/lib/components/test_cases/TestCaseView.svelte b/src/lib/components/test_cases/TestCaseView.svelte index c519be7950e2154ab038d9e924e8681ec5d980c4..cfce09960ed1e261d469029325f26c65b08b84f6 100644 --- a/src/lib/components/test_cases/TestCaseView.svelte +++ b/src/lib/components/test_cases/TestCaseView.svelte @@ -58,12 +58,6 @@ let inited = false const personEntity = entityByKey[$session.personEntityKey] const reform = getContext("reform") as Writable<ReformChange> - const rootDecompositionName = $session.rootDecompositionName - const firstDecompositionName = walkDecompositionsCoreName( - $session.decompositionCoreByName, - rootDecompositionName, - true, - ).next().value as string const variableDefinitionByName = { age: { allowSlider: true, @@ -85,6 +79,13 @@ ) let variableSummaryByName: { [name: string]: Variable } | undefined = undefined + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root + const firstDecompositionName = walkDecompositionsCoreName( + $session.decompositionCoreByName, + rootDecompositionName, + true, + ).next().value as string $: familySituation = situation[familyEntity.key_plural] diff --git a/src/lib/components/waterfalls/Waterfall.svelte b/src/lib/components/waterfalls/Waterfall.svelte index 16a87866f1b20daeeda5112347452a9c009d5897..a144d6ead1e556d2f54366567752c266681017dd 100644 --- a/src/lib/components/waterfalls/Waterfall.svelte +++ b/src/lib/components/waterfalls/Waterfall.svelte @@ -16,9 +16,10 @@ const decompositionByNameArray = getContext( "decompositionByNameArray", ) as Writable<DecompositionByName[]> - const rootDecompositionName = $session.rootDecompositionName const showNulls = getContext("showNulls") as Writable<boolean> const testCaseIndex = getContext("testCaseIndex") as Writable<number> + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root $: data = [ ...walkDecompositions( diff --git a/src/lib/server/config.ts b/src/lib/server/config.ts index 2e25941ecc0bda657336212ee1740d4c23f459f5..cbc0a482f94242697ce3a748b4d1ffecf2f00e5b 100644 --- a/src/lib/server/config.ts +++ b/src/lib/server/config.ts @@ -36,8 +36,8 @@ export interface Config { } portalUrl: string proxy: boolean - rootDecompositionName: string title: string + waterfallsPath: string } const [validConfig, error] = validateConfig({ @@ -78,7 +78,7 @@ const [validConfig, error] = validateConfig({ }, portalUrl: process.env["PORTAL_URL"], proxy: process.env["PROXY"], - rootDecompositionName: process.env["DECOMPOSITION_ROOT"], + waterfallsPath: process.env["WATERFALLS_PATH"], title: process.env["TITLE"], }) if (error !== null) { diff --git a/src/lib/server/decompositions.ts b/src/lib/server/decompositions.ts index 8757eda44786c7da90ec928647567d377ad8b677..414f03ac848ee61f7cc78fdd21e7eb355939f063 100644 --- a/src/lib/server/decompositions.ts +++ b/src/lib/server/decompositions.ts @@ -1,22 +1,51 @@ +import { + auditChain, + auditCleanArray, + auditFunction, + auditRequire, + strictAudit, +} from "@auditors/core" +import type { Waterfall } from "@openfisca/ast" +import { auditDecompositionByName, auditWaterfall } from "@openfisca/ast" import assert from "assert" import fs from "fs-extra" import type { DecompositionCoreByName } from "$lib/decompositions" -import { - decompositionByNameFromCore, - walkDecompositionsCore, -} from "$lib/decompositions" -import type { Variable, VariableByName } from "@openfisca/ast" +import { decompositionByNameFromCore } from "$lib/decompositions" import config from "$lib/server/config" -import { variableSummaryByName } from "$lib/server/variables" -const { decompositionsPath, rootDecompositionName } = config +const { decompositionsPath, waterfallsPath } = config + +const [decompositionByName, decompositionByNameError] = auditChain( + auditDecompositionByName, + auditRequire, +)(strictAudit, fs.readJsonSync(decompositionsPath)) as [ + DecompositionCoreByName, + unknown, +] +if (decompositionByNameError !== null) { + console.error(decompositionsPath) + console.error(JSON.stringify(decompositionByName, null, 2)) + console.error(JSON.stringify(decompositionByNameError, null, 2)) + process.exit(1) +} +export const decompositionCoreByName = decompositionByName -export const decompositionCoreByName = fs.readJsonSync( - decompositionsPath, -) as DecompositionCoreByName +const [validWaterfalls, waterfallsError] = auditChain( + auditCleanArray(auditWaterfall), + auditRequire, +)(strictAudit, fs.readJsonSync(waterfallsPath)) as [Waterfall[], unknown] +if (waterfallsError !== null) { + console.error(waterfallsPath) + console.error(JSON.stringify(validWaterfalls, null, 2)) + console.error(JSON.stringify(waterfallsError, null, 2)) + process.exit(1) +} +export const waterfalls = validWaterfalls -assert.notStrictEqual( - decompositionByNameFromCore(decompositionCoreByName, rootDecompositionName), - undefined, -) +for (const waterfall of waterfalls) { + assert.notStrictEqual( + decompositionByNameFromCore(decompositionCoreByName, waterfall.root), + undefined, + ) +} diff --git a/src/lib/sessions.ts b/src/lib/sessions.ts index 09d2ec09f9574932faeb9e28c820365abdaf5728..25bf1b1567a57fa95ed5e13996a26d42946571d2 100644 --- a/src/lib/sessions.ts +++ b/src/lib/sessions.ts @@ -1,4 +1,4 @@ -import type { EntityByKey, Metadata } from "@openfisca/ast" +import type { EntityByKey, Metadata, Waterfall } from "@openfisca/ast" import type { DecompositionCoreByName } from "$lib/decompositions" import type { Situation } from "$lib/situations" @@ -27,10 +27,10 @@ export interface Session { openfiscaRepository: SessionOpenFiscaRepository personEntityKey: string portalUrl: string - rootDecompositionName: string testCases: Situation[] title: string user?: User + waterfalls: Waterfall[] } export interface SessionOpenFiscaRepository { diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index 0b70dd0c1b756a500f95c870941bb5ec68e4120c..bbcf6be1398ba77d6136bf51b4217ac00c53b6b1 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -38,10 +38,12 @@ const vectorLength = writable(1) setContext("vectorLength", vectorLength) + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root const decompositionByName = writable( decompositionByNameFromCore( $session.decompositionCoreByName, - $session.rootDecompositionName, + rootDecompositionName, ), ) setContext("decompositionByName", decompositionByName) @@ -58,7 +60,7 @@ updateEvaluations( $decompositionByName, {}, - $session.rootDecompositionName, + rootDecompositionName, $vectorIndexes[situationIndex], $vectorLength, ), @@ -85,7 +87,7 @@ [ ...walkDecompositionsCore( $session.decompositionCoreByName, - $session.rootDecompositionName, + rootDecompositionName, true, ), ] @@ -112,8 +114,6 @@ > = writable({}) setContext("calculationTokenByName", calculationTokenByName) - const rootDecompositionName = $session.rootDecompositionName - let variableValuesByName: { [name: string]: VariableValues } = {} const webSocketByName: Writable<WebSocketByName | undefined> = diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 5035eb596ca12f02fccbeb7e3fdbc97b8b3162eb..664b8c67393aa32b94203de2586e2f20fe0a0c1b 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -84,7 +84,6 @@ }> setContext("newSelfTargetAProps", newSelfTargetAProps) const reform = getContext("reform") as Writable<ReformChange> - const rootDecompositionName = $session.rootDecompositionName const showNulls = writable(false) setContext("showNulls", showNulls) let showTutorial = true @@ -98,6 +97,8 @@ ) as Writable<{ [name in CalculationName]?: string }> const vectorIndexes = getContext("vectorIndexes") as Writable<number[]> const vectorLength = getContext("vectorLength") as Writable<number> + const waterfalls = $session.waterfalls + const rootDecompositionName = waterfalls[0].root const webSocketByName = getContext("webSocketByName") as Writable< WebSocketByName | undefined > diff --git a/src/routes/variables/[variable]/inputs/[date].json.ts b/src/routes/variables/[variable]/inputs/[date].json.ts index 2f8bb5a2798aa5893c9f7527755c140bc779d7e5..1a9330fce140ed4938ca9ad94c6d20e0d2fac940 100644 --- a/src/routes/variables/[variable]/inputs/[date].json.ts +++ b/src/routes/variables/[variable]/inputs/[date].json.ts @@ -15,10 +15,11 @@ import sanitizeFilename from "sanitize-filename" import { walkDecompositionsCoreName } from "$lib/decompositions" import config from "$lib/server/config" -import { decompositionCoreByName } from "$lib/server/decompositions" +import { decompositionCoreByName, waterfalls } from "$lib/server/decompositions" import { iterVariableInputVariables } from "$lib/server/variables" const { jsonDir } = config +const rootDecompositionName = waterfalls[0].root function auditVariableInputsQuery( audit: Audit, @@ -105,7 +106,7 @@ export const get: RequestHandler = async ({ ignoreVariablesName = new Set( walkDecompositionsCoreName( decompositionCoreByName, - config.rootDecompositionName, + rootDecompositionName, false, ), )