diff --git a/.gitignore b/.gitignore index 351e74c381b04026dc2dbbb0fc157424d2accd33..ebc7c7a15cd76f5f19b2613f752ff29055b2ccda 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ node_modules !/example.env /.svelte-kit /build/ -/package +/src/lib/openfisca diff --git a/.prettierignore b/.prettierignore index 7dde8a0aed1f0aaeeae5e90d104331528e2d9950..5421174f88eb85ad3050ba62f557dbdcdffc0e08 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ .svelte-kit/** build/** node_modules/** +src/lib/openfisca/** static/** diff --git a/README.md b/README.md index 68efa4bb97c7ed8b42ccb325f98671720aff33f3..a999f7feee58b77ec19c09840fb60e7ac2e7fffb 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,13 @@ Pour fonctionner, ce simulateur socio-fiscal utilise un export des paramètres e git clone https://git.leximpact.dev/openfisca/openfisca-france-json ``` -Une fois ces différentes opérations effectuées, exécuter les commandes ci-dessous pour vous déplacer dans le répertoire du projet et installer les librairies nécessaires : +Une fois ces différentes opérations effectuées, exécuter les commandes ci-dessous pour vous déplacer dans le répertoire du projet, le lier aux paramètres et variables d'OpenFisca, puis installer les librairies nécessaires : ```shell git clone https://git.leximpact.dev/leximpact/leximpact-socio-fiscal-ui -cd leximpact-socio-fiscal-ui/ +cd leximpact-socio-fiscal-ui/src/lib/ +ln -s ../../../openfisca-france-json openfisca +cd ../../ npm install ``` diff --git a/example.env b/example.env index ed965d086cfeb7b32e9b505e7a9e3cb2947da210..c4b88768f24549d4412ab39e0c386fda6c6f1ee8 100644 --- a/example.env +++ b/example.env @@ -14,9 +14,6 @@ BASE_URL="http://localhost:3000" # Key for children in a family CHILDREN_KEY="enfants" -# Path to file containing decompositions in JSON format -DECOMPOSITION_PATH="../openfisca-france-json/decompositions/decompositions_customized.json" - # Key for family entity FAMILY_KEY="famille" @@ -28,9 +25,6 @@ GITHUB_PERSONAL_ACCESS_TOKEN=null # that should not be shown when editing test cases HIDDEN_ENTITIES=menages -# Directory containing JSON export of OpenFisca country package -JSON_DIR="../openfisca-france-json" - # Matomo analytics configuration # MATOMO_SITE_ID=123 # MATOMO_URL="https://MATOMO_SERVER/" @@ -61,6 +55,3 @@ PROXY=false SIMULATIONS_DIR="../simulations" 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/src/hooks.ts b/src/hooks.ts index c17459a8fa3297146c34575fd763698ecfee8aed..54357d651f1f37abd1ca8cb8edf769ce7b221541 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -2,16 +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, - decompositionsOptionsVariablesName, - waterfalls, -} from "$lib/server/decompositions" -import { entityByKey, personEntityKey } from "$lib/server/entities" -import { metadata } from "$lib/server/metadata" import { oauth2Authenticator } from "$lib/server/oauth2" -import { leafParametersName } from "$lib/server/parameters" -import { testCases } from "$lib/server/situations" import type { Session } from "$lib/sessions" // Globally declare fetch, because it is not (yet?) available in @sveltejs/adapter-node, @@ -51,15 +42,10 @@ export const getSession: GetSession<{}, Session> = async (request) => { authenticationEnabled: oauth2Authenticator !== undefined, baseUrl: config.baseUrl, childrenKey: config.childrenKey, - decompositionCoreByName, - decompositionsOptionsVariablesName, - entityByKey, familyEntityKey: config.familyEntityKey, hasGithubPersonalAccessToken: githubPersonalAccessToken !== undefined, hiddenEntitiesKeyPlural: config.hiddenEntitiesKeyPlural, - leafParametersName, matomo: config.matomo, - metadata, openfiscaRepository: { branch: openfiscaRepository.branch, group: openfiscaRepository.group, @@ -67,12 +53,9 @@ export const getSession: GetSession<{}, Session> = async (request) => { rawUrlTemplate: openfiscaRepository.rawUrlTemplate, urlTemplate: openfiscaRepository.urlTemplate, }, - personEntityKey, portalUrl: config.portalUrl, - testCases, title: config.title, user, - waterfalls, } } diff --git a/src/lib/auditors/config.ts b/src/lib/auditors/config.ts index 122f80aee7c17d12534225fc97f5e1129b7e2ab0..b2fc30b85e14c9ad675e4f996b614fb51d4ca0bc 100644 --- a/src/lib/auditors/config.ts +++ b/src/lib/auditors/config.ts @@ -67,12 +67,10 @@ export function auditConfig( } for (const key of [ "childrenKey", - "decompositionsPath", "familyEntityKey", - "jsonDir", + "openfiscaDir", "simulationsDir", "title", - "waterfallsPath", ]) { audit.attribute( data, diff --git a/src/lib/components/parameters/NodeEdit.svelte b/src/lib/components/parameters/NodeEdit.svelte index ebd57a69ba8df227b5582aacd10a84d4059b25eb..de3a10ae2035ff5277bbd5d5670db874ee01977f 100644 --- a/src/lib/components/parameters/NodeEdit.svelte +++ b/src/lib/components/parameters/NodeEdit.svelte @@ -3,9 +3,9 @@ import type { NodeParameter, Reference } from "@openfisca/ast" import { Unit } from "@openfisca/ast" - import { session } from "$app/stores" import ReferencesEdit from "$lib/components/parameters/ReferencesEdit.svelte" import { errorAsKeyValueDictionary, iterArrayWithErrors } from "$lib/errors" + import { metadata } from "$lib/metadata" import { labelFromUnit } from "$lib/parameters" let globalErrors: { [key: string]: unknown } @@ -21,8 +21,6 @@ let errors = globalErrors let instantReferencesArray = buildInstantReferencesArray(parameter) - $: metadata = $session.metadata - function addInstantReferences() { instantReferencesArray = [ { diff --git a/src/lib/components/parameters/ParameterPane.svelte b/src/lib/components/parameters/ParameterPane.svelte index 9feb72dc3dec10b6e33d6d5529df56da4e66bf58..9bf2a8be2a36ed9dbd8021e65c093648a0c015ed 100644 --- a/src/lib/components/parameters/ParameterPane.svelte +++ b/src/lib/components/parameters/ParameterPane.svelte @@ -1,31 +1,14 @@ <script lang="ts"> - import type { Parameter } from "@openfisca/ast" - import { improveParameterWithAncestors } from "@openfisca/ast" - - import { browser } from "$app/env" import ParameterView from "$lib/components/parameters/ParameterView.svelte" + import { getParameter } from "$lib/parameters" export let name: string // export let pane: string - let parameter: Parameter | undefined = undefined - - $: browser && retrieveParameter(name) + $: parameter = getParameter(name) - async function retrieveParameter(name: string): Promise<void> { - const url = `/parameters/${name}.json` - const res = await fetch(url) - if (res.ok) { - const parameterWithAncestors = await res.json() - parameter = improveParameterWithAncestors(parameterWithAncestors) - } else { - console.error( - `Error ${ - res.status - } while retrieving parameter "${name}" at ${url}\n\n${await res.text()}`, - ) - parameter = undefined - } + $: if (parameter === undefined) { + console.error(`Parameter "${name}" not found`) } </script> diff --git a/src/lib/components/parameters/ParameterView.svelte b/src/lib/components/parameters/ParameterView.svelte index 12c0230f82a694c44da39488ba5cbd80a6269f18..3a830cd3d2546a33fe1f03406af07e1778191c6b 100644 --- a/src/lib/components/parameters/ParameterView.svelte +++ b/src/lib/components/parameters/ParameterView.svelte @@ -15,8 +15,8 @@ } from "@openfisca/ast" import { getContext } from "svelte" - import { session } from "$app/stores" import ScaleView from "$lib/components/parameters/ScaleView.svelte" + import { metadata } from "$lib/metadata" import { buildInstantReferencesAndValueArray, labelFromScaleType, @@ -32,8 +32,6 @@ url: string, ) => SelfTargetAProps - $: metadata = $session.metadata - $: parameterRepositoryUrl = newParameterRepositoryUrl(metadata, parameter) function asAmountScaleParameter( diff --git a/src/lib/components/parameters/ScaleEdit.svelte b/src/lib/components/parameters/ScaleEdit.svelte index a77f3582fcfb47732175f4bc9236ed357570232e..907f9cafe57ce045715e8aee05c45d899d5c01ed 100644 --- a/src/lib/components/parameters/ScaleEdit.svelte +++ b/src/lib/components/parameters/ScaleEdit.svelte @@ -20,11 +20,11 @@ } from "@openfisca/ast" import { deepCopy } from "fast-deep-copy" - import { session } from "$app/stores" import ReferencesEdit from "$lib/components/parameters/ReferencesEdit.svelte" import ScaleAtInstantEdit from "$lib/components/parameters/ScaleAtInstantEdit.svelte" import { errorAsKeyValueDictionary, iterArrayWithErrors } from "$lib/errors" import { iterToLimit } from "$lib/iterators" + import { metadata } from "$lib/metadata" import { buildInstantReferencesAndScaleArray, labelFromScaleType, @@ -43,8 +43,6 @@ let showAll = false const useBase = false - $: metadata = $session.metadata - $: isAmountScale = isAmountScaleParameter(parameter) function addInstantReferencesAndScale() { diff --git a/src/lib/components/parameters/ValueAtInstantEdit.svelte b/src/lib/components/parameters/ValueAtInstantEdit.svelte index 23b4640baac69c9047067f46993a32b81401867a..3a8f2173db0a6365e7cefb7ab5bce822cda0b2ba 100644 --- a/src/lib/components/parameters/ValueAtInstantEdit.svelte +++ b/src/lib/components/parameters/ValueAtInstantEdit.svelte @@ -16,8 +16,8 @@ import { Unit, ValueType } from "@openfisca/ast" import { createEventDispatcher } from "svelte" - import { session } from "$app/stores" import { auditEditedAttribute } from "$lib/errors" + import { metadata } from "$lib/metadata" import { labelFromUnit } from "$lib/parameters" let globalErrors: { [key: string]: unknown } @@ -30,8 +30,6 @@ let errors = globalErrors const oldValueAtInstant = valueAtInstant - $: metadata = $session.metadata - function asMaybeNumberValue( value: | MaybeNumberValue diff --git a/src/lib/components/parameters/ValueEdit.svelte b/src/lib/components/parameters/ValueEdit.svelte index f5b96ab79e526c227218cd210281b5208e1597c8..6de28559f8c5febdadae94b8c06dba6aea391e39 100644 --- a/src/lib/components/parameters/ValueEdit.svelte +++ b/src/lib/components/parameters/ValueEdit.svelte @@ -7,11 +7,11 @@ } from "@openfisca/ast" import { Unit, ValueType } from "@openfisca/ast" - import { session } from "$app/stores" import ReferencesEdit from "$lib/components/parameters/ReferencesEdit.svelte" import ValueAtInstantEdit from "$lib/components/parameters/ValueAtInstantEdit.svelte" import { errorAsKeyValueDictionary, iterArrayWithErrors } from "$lib/errors" import { iterToLimit } from "$lib/iterators" + import { metadata } from "$lib/metadata" import { buildInstantReferencesAndValueArray, labelFromValueType, @@ -29,8 +29,6 @@ buildInstantReferencesAndValueArray(parameter) let showAll = false - $: metadata = $session.metadata - function addInstantReferencesAndValue() { instantReferencesAndValueArray = [ { diff --git a/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte b/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte index a73252f264a22cbe17f5a320dff16d952f967edd..6a72681900cf63357adea963e089896cb907ffb8 100644 --- a/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte +++ b/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte @@ -10,6 +10,7 @@ VisibleDecomposition, } from "$lib/decompositions" import { buildVisibleDecompositions } from "$lib/decompositions" + import { entityByKey } from "$lib/entities" import type { Situation } from "$lib/situations" export let decompositionByName: DecompositionByName @@ -44,7 +45,7 @@ $: visibleDecompositions = buildVisibleDecompositions( decompositionByName, - $session.entityByKey, + entityByKey, evaluationByName, situation, variableSummaryByName, diff --git a/src/lib/components/test_cases/TestCaseEdit.svelte b/src/lib/components/test_cases/TestCaseEdit.svelte index 1ec5cabba4e78d680f90e660be58c691ffbc4077..fd02fddac8c53cc9130b539c64cca8eb8168074c 100644 --- a/src/lib/components/test_cases/TestCaseEdit.svelte +++ b/src/lib/components/test_cases/TestCaseEdit.svelte @@ -4,7 +4,6 @@ GroupEntity, PersonEntity, Role, - Variable, } from "@openfisca/ast" import { getRolePersonsIdKey, isAdultRole, isChildRole } from "@openfisca/ast" import { createEventDispatcher } from "svelte" @@ -14,10 +13,11 @@ import PictoEnfant from "$lib/components/pictos/PictoEnfant.svelte" import RolePersonsEdit from "$lib/components/test_cases/RolePersonsEdit.svelte" import VariableInput from "$lib/components/variables/VariableInput.svelte" - import type { DecompositionCoreByName } from "$lib/decompositions" + import { decompositionCoreByName } from "$lib/decompositions" + import { entityByKey, personEntityKey } from "$lib/entities" import type { Axis, PopulationWithoutId, Situation } from "$lib/situations" import { iterSituationVariablesName } from "$lib/situations" - import { retrieveVariableSummaryByName } from "$lib/variables" + import { variableSummaryByName } from "$lib/variables" export let inputInstantsByVariableName: { [name: string]: Set<string> @@ -28,7 +28,6 @@ let currentInputInstantsByVariableName = inputInstantsByVariableName let currentSituation = situation const dispatch = createEventDispatcher() - const entityByKey = $session.entityByKey as EntityByKey const [, firstGroupEntity] = Object.entries(entityByKey).filter( ([, entity]) => !entity.is_person, )[0] as [string, GroupEntity] @@ -40,15 +39,12 @@ ].map((_, index) => `Enfant ${index + 1}`) const hiddenEntitiesKeyPlural = ($session.hiddenEntitiesKeyPlural ?? []) as string[] - const personEntityKey = $session.personEntityKey as string const personEntity = entityByKey[personEntityKey] as PersonEntity let variablesName = new Set( - Object.entries($session.decompositionCoreByName as DecompositionCoreByName) + Object.entries(decompositionCoreByName) .filter(([, decomposition]) => !decomposition.virtual) .map(([name]) => name), ) - let variableSummaryByName: { [name: string]: Variable } | undefined = - undefined $: persons = Object.entries( (situation[personEntity.key_plural] ?? {}) as { @@ -67,12 +63,6 @@ $: updateVariablesName(situation) - $: retrieveVariableSummaryByName($session.baseUrl, [...variablesName]).then( - (result) => { - variableSummaryByName = result - }, - ) - $: updateInputInstantsByVariableName(inputInstantsByVariableName) $: updateSituation(situation) @@ -548,22 +538,20 @@ {/each} </section> -{#if variableSummaryByName !== undefined} - <section class="border-t border-b-0 border-r-0 border-l-0 mt-5"> - <h1 class="font-bold mt-3 text-xl">Caractéristiques du cas type :</h1> - <ul> - {#each Object.values(variableSummaryByName) as variable} - {#if (inputInstantsByVariableName[variable.name] ?? new Set()).has(yearString)} - <li> - <VariableInput - bind:inputInstantsByVariableName - bind:situation - {variable} - {year} - /> - </li> - {/if} - {/each} - </ul> - </section> -{/if} +<section class="border-t border-b-0 border-r-0 border-l-0 mt-5"> + <h1 class="font-bold mt-3 text-xl">Caractéristiques du cas type :</h1> + <ul> + {#each [...variablesName] as variableName} + {#if (inputInstantsByVariableName[variableName] ?? new Set()).has(yearString)} + <li> + <VariableInput + bind:inputInstantsByVariableName + bind:situation + variable={variableSummaryByName[variableName]} + {year} + /> + </li> + {/if} + {/each} + </ul> +</section> diff --git a/src/lib/components/test_cases/TestCaseView.svelte b/src/lib/components/test_cases/TestCaseView.svelte index fa08bcaa6897b0ad0aaff5f3fb556393d3b5f345..35b8113bdde283450dd87cafd755059814f785b4 100644 --- a/src/lib/components/test_cases/TestCaseView.svelte +++ b/src/lib/components/test_cases/TestCaseView.svelte @@ -1,10 +1,5 @@ <script lang="ts"> - import type { - DecompositionReference, - EntityByKey, - Variable, - Waterfall, - } from "@openfisca/ast" + import type { DecompositionReference, Waterfall } from "@openfisca/ast" import { createEventDispatcher, getContext } from "svelte" import type { Writable } from "svelte/store" @@ -17,18 +12,18 @@ import type { CalculationName, DecompositionByName, - DecompositionCoreByName, EvaluationByName, EvaluationByNameArrayByCalculationName, } from "$lib/decompositions" + import { waterfalls } from "$lib/decompositions" + import { entityByKey, personEntityKey } from "$lib/entities" import type { Reform } from "$lib/reforms" import type { AxisDescription, Situation } from "$lib/situations" import { getSituationVariableValue, - iterSituationVariablesName, setSituationVariableValue, } from "$lib/situations" - import { retrieveVariableSummaryByName } from "$lib/variables" + import { variableSummaryByName } from "$lib/variables" export let calculationName: CalculationName export let decompositionByName: DecompositionByName @@ -46,7 +41,6 @@ const childrenKey = $session.childrenKey const dispatch = createEventDispatcher() const enfantVariablesName = ["age"] - const entityByKey = $session.entityByKey as EntityByKey const euroAmountFormatter = new Intl.NumberFormat("fr-FR", { currency: "EUR", maximumFractionDigits: 0, @@ -54,7 +48,7 @@ style: "currency", }) const familyEntity = entityByKey[$session.familyEntityKey] - const personEntity = entityByKey[$session.personEntityKey] + const personEntity = entityByKey[personEntityKey] const reform = getContext("reform") as Writable<Reform> const variableDefinitionByName = { age: { @@ -70,18 +64,7 @@ min: 0, }, } - let variablesName = new Set([ - ...$session.decompositionsOptionsVariablesName, - ...Object.entries( - $session.decompositionCoreByName as DecompositionCoreByName, - ) - .filter(([, decomposition]) => !decomposition.virtual) - .map(([name]) => name), - ]) - let variableSummaryByName: { [name: string]: Variable } | undefined = - undefined const waterfall = getContext("waterfall") as Writable<Waterfall> - const waterfalls = $session.waterfalls $: familySituation = situation[familyEntity.key_plural] @@ -98,14 +81,6 @@ $: adultsCount = Object.keys(personSituation).length - childrenCount - $: updateVariablesName(situation) - - $: retrieveVariableSummaryByName($session.baseUrl, [...variablesName]).then( - (result) => { - variableSummaryByName = result - }, - ) - // $: updateSituation(year, adultes, enfants) function calculateTotal( @@ -251,17 +226,6 @@ dispatch("changeVectorIndex", vectorIndex) dispatch("changeAxis", axisDescription) } - - function updateVariablesName(situation: Situation) { - const newVariablesName = [ - ...iterSituationVariablesName(entityByKey, situation), - ] - if ( - newVariablesName.some((variableName) => !variablesName.has(variableName)) - ) { - variablesName = new Set([...variablesName, ...newVariablesName]) - } - } </script> <div class="place-self-start shadow-md w-full rounded-t-sm bg-white mb-5 "> diff --git a/src/lib/components/test_cases/TestCasesPane.svelte b/src/lib/components/test_cases/TestCasesPane.svelte index 2d4f9f1a19e978bc67b18e45fb13af756ca65438..001d7a9fb146ce7d1d9b6a53344bab5ef1f31b25 100644 --- a/src/lib/components/test_cases/TestCasesPane.svelte +++ b/src/lib/components/test_cases/TestCasesPane.svelte @@ -1,14 +1,13 @@ <script lang="ts"> - import type { EntityByKey } from "@openfisca/ast" import { createEventDispatcher, getContext } from "svelte" import type { Writable } from "svelte/store" - import { session } from "$app/stores" import type { CalculationName, DecompositionByName, EvaluationByNameArrayByCalculationName, } from "$lib/decompositions" + import { entityByKey } from "$lib/entities" import type { Axis, AxisDescription, Situation } from "$lib/situations" import { indexOfSituationPopulationId } from "$lib/situations" @@ -24,7 +23,6 @@ "decompositionByName", ) as Writable<DecompositionByName> const dispatch = createEventDispatcher() - const entityByKey = $session.entityByKey as EntityByKey const evaluationByNameArrayByCalculationName = getContext( "evaluationByNameArrayByCalculationName", ) as Writable<EvaluationByNameArrayByCalculationName> diff --git a/src/lib/components/variables/FormulaView.svelte b/src/lib/components/variables/FormulaView.svelte index 74e591080836fdd3ce7acc3b70fa6c4995803ef9..1613d4e57791a1e6f52c6c01ad91ab9819e22647 100644 --- a/src/lib/components/variables/FormulaView.svelte +++ b/src/lib/components/variables/FormulaView.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import type { Formula, Variable } from "@openfisca/ast" + import type { Formula } from "@openfisca/ast" import { extractFromFormulaAst, newFormulaRepositoryUrl, @@ -8,10 +8,12 @@ import type { Writable } from "svelte/store" import { browser } from "$app/env" - import { session } from "$app/stores" import VariableInput from "$lib/components/variables/VariableInput.svelte" + import { entityByKey } from "$lib/entities" + import { metadata } from "$lib/metadata" + import { leafParametersName } from "$lib/parameters" import type { Situation } from "$lib/situations" - import { retrieveVariableSummaryByName } from "$lib/variables" + import { variableSummaryByName } from "$lib/variables" import type { WebSocketByName, WebSocketOpenByName } from "$lib/websockets" export let formula: Formula @@ -21,8 +23,6 @@ export let situation: Situation export let year: number - let variableSummaryByName: { [name: string]: Variable } | undefined = - undefined const webSocketByName = getContext("webSocketByName") as Writable< WebSocketByName | undefined > @@ -32,29 +32,24 @@ $: extraction = extractFromFormulaAst( formula.ast, - $session.entityByKey, - $session.leafParametersName, + entityByKey, + leafParametersName, ) - $: metadata = $session.metadata - $: if (browser && $webSocketOpenByName.law) { calculateVariables(extraction.openFiscaVariablesName) } async function calculateVariables(variablesName: Set<string>) { - variableSummaryByName = await retrieveVariableSummaryByName( - $session.baseUrl, - [...variablesName], - ) - - $webSocketByName.law.send( - JSON.stringify({ - calculate: true, - title: "law", - variables: Object.keys(variableSummaryByName), - }), - ) + if (variablesName.size > 0) { + $webSocketByName.law.send( + JSON.stringify({ + calculate: true, + title: "law", + variables: [...variablesName], + }), + ) + } } </script> @@ -68,19 +63,19 @@ <pre class="bg-gray-100 p-5 text-sm whitespace-pre-wrap">{formula.source_code}</pre> <!-- {#if formula.parameters !== undefined} - <section> - <h1>Paramètres référencés</h1> - <ul class="list-disc list-inside"> - {#each formula.parameters as parameterName} - <li> - <a class="link" href="/parameters/{parameterName}" - >{parameterName}</a - > - </li> - {/each} - </ul> - </section> - {/if} --> + <section> + <h1>Paramètres référencés</h1> + <ul class="list-disc list-inside"> + {#each formula.parameters as parameterName} + <li> + <a class="link" href="/parameters/{parameterName}" + >{parameterName}</a + > + </li> + {/each} + </ul> + </section> + {/if} --> </div> <p class="my-4"> <a @@ -124,19 +119,19 @@ </section> {/if} <!-- {#if formula.variables !== undefined} - <section> - <h1>Variables référencées</h1> - <ul class="list-disc list-inside"> - {#each formula.variables as variableName} - <li> - <a class="text-gray-900 hover:text-le-bleu underline" href="/variables/{variableName}" - >{variableName}</a - > - </li> - {/each} - </ul> - </section> - {/if} --> + <section> + <h1>Variables référencées</h1> + <ul class="list-disc list-inside"> + {#each formula.variables as variableName} + <li> + <a class="text-gray-900 hover:text-le-bleu underline" href="/variables/{variableName}" + >{variableName}</a + > + </li> + {/each} + </ul> + </section> + {/if} --> {#if variableSummaryByName !== undefined && Object.keys(variableSummaryByName).length > 0} <section class="mb-2"> <h3 class="mb-1 ">Variables :</h3> diff --git a/src/lib/components/variables/VariableInput.svelte b/src/lib/components/variables/VariableInput.svelte index 09fb71f1804fd17f8bfd1fc92f023bf53e937651..4ffaaea4bd5ec305e4b85ec4a0625bc07ceec568 100644 --- a/src/lib/components/variables/VariableInput.svelte +++ b/src/lib/components/variables/VariableInput.svelte @@ -4,9 +4,9 @@ import { getContext } from "svelte" import type { Writable } from "svelte/store" - import { session } from "$app/stores" import type { CalculationName } from "$lib/decompositions" import { calculationNames } from "$lib/decompositions" + import { entityByKey } from "$lib/entities" import type { Situation } from "$lib/situations" import { getSituationVariableValue, @@ -25,7 +25,7 @@ ) as Writable<Set<CalculationName>> let valueError = null - $: entity = $session.entityByKey[variable.entity] + $: entity = entityByKey[variable.entity] $: entitySituation = situation[entity.key_plural] @@ -77,7 +77,7 @@ break } const updatedSituation = setSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -138,7 +138,7 @@ on:blur={(event) => changeValue(event, populationId)} on:change={(event) => changeValue(event, populationId)} value={getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -154,7 +154,7 @@ <input checked={asBoolean( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -165,7 +165,7 @@ on:change={(event) => changeValue(event, populationId)} type="checkbox" /> - {#if asBoolean(getSituationVariableValue($session.entityByKey, situation, variable, populationId, year))}Oui{:else}Non{/if} + {#if asBoolean(getSituationVariableValue(entityByKey, situation, variable, populationId, year))}Oui{:else}Non{/if} </label> {:else if variable.value_type === "date"} <input @@ -174,7 +174,7 @@ type="date" value={asString( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -189,7 +189,7 @@ type="number" value={asNumber( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -204,7 +204,7 @@ type="text" value={asString( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, diff --git a/src/lib/components/variables/VariableReferredInputsPane.svelte b/src/lib/components/variables/VariableReferredInputsPane.svelte index f1692455daaebbc4226decd9a6886b0ac03fbcec..a41aea3656f137e97d47c1c9853a2ba74342e75b 100644 --- a/src/lib/components/variables/VariableReferredInputsPane.svelte +++ b/src/lib/components/variables/VariableReferredInputsPane.svelte @@ -3,9 +3,17 @@ import { createEventDispatcher, getContext } from "svelte" import type { Writable } from "svelte/store" - import { browser } from "$app/env" - import VariableReferredInputs from "./VariableReferredInputs.svelte" + import { + decompositionCoreByName, + walkDecompositionsCoreName, + } from "$lib/decompositions" import type { Situation } from "$lib/situations" + import { + iterVariableInputVariables, + variableSummaryByName, + } from "$lib/variables" + + import VariableReferredInputs from "./VariableReferredInputs.svelte" export let date: string export let inputInstantsByVariableName: { @@ -18,51 +26,31 @@ let currentInputInstantsByVariableName = inputInstantsByVariableName let currentSituation = situation const dispatch = createEventDispatcher() - let inputs: Variable[] | undefined = undefined - let variable: Variable | undefined = undefined const waterfall = getContext("waterfall") as Writable<Waterfall> - $: browser && retrieveVariable(name) + $: variable = variableSummaryByName[name] + + $: if (variable === undefined) { + console.error(`Variable "${name}" not found`) + } - $: browser && retrieveVariableReferredInputs(name, date) + $: inputs = getVariableReferredInputs(name, date) $: updateInputInstantsByVariableName(inputInstantsByVariableName) $: updateSituation(situation) - async function retrieveVariable(name: string): Promise<void> { - const url = `/variables/${name}.json` - const res = await fetch(url) - if (res.ok) { - variable = await res.json() - } else { - console.error( - `Error ${ - res.status - } while retrieving referred input variables of variable "${name}" at ${url}\n\n${await res.text()}`, - ) - variable = undefined - } - } - - async function retrieveVariableReferredInputs( - name: string, - date: string, - ): Promise<void> { - const url = `/variables/${name}/inputs/${date}.json?filter_decomposition=true&waterfall=${encodeURIComponent( - $waterfall.name, - )}` - const res = await fetch(url) - if (res.ok) { - inputs = await res.json() - } else { - console.error( - `Error ${ - res.status - } while retrieving referred input variables of variable "${name}" at ${url}\n\n${await res.text()}`, - ) - inputs = undefined - } + function getVariableReferredInputs(name: string, date: string): Variable[] { + const ignoreVariablesName = new Set( + walkDecompositionsCoreName( + decompositionCoreByName, + $waterfall.name, + $waterfall.root, + false, + ), + ) + ignoreVariablesName.delete(name) + return [...iterVariableInputVariables(variable, date, ignoreVariablesName)] } function updateInputInstantsByVariableName(inputInstantsByVariableName: { @@ -84,7 +72,7 @@ } </script> -{#if inputs !== undefined && variable !== undefined} +{#if variable !== undefined} <VariableReferredInputs {date} bind:inputInstantsByVariableName diff --git a/src/lib/components/variables/VariableReferredParameters.svelte b/src/lib/components/variables/VariableReferredParameters.svelte index 12ff6799cd107ae7b3369fb454757937e3974a79..366fb879dbc6021a38723548c381a4ca4d84179e 100644 --- a/src/lib/components/variables/VariableReferredParameters.svelte +++ b/src/lib/components/variables/VariableReferredParameters.svelte @@ -1,14 +1,11 @@ <script lang="ts"> import type { Parameter, Variable } from "@openfisca/ast" import { - getVariableFormula, getVariableLatestFormulaDate, mergeParameters, ParameterClass, } from "@openfisca/ast" - import { session } from "$app/stores" - import VariableReferredNodeParameter from "./VariableReferredNodeParameter.svelte" import VariableReferredScaleParameter from "./VariableReferredScaleParameter.svelte" import VariableReferredValueParameter from "./VariableReferredValueParameter.svelte" @@ -19,14 +16,8 @@ const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" }) - $: directParametersName = getVariableFormula(variable, date)?.parameters ?? [] - $: latestFormulaDate = getVariableLatestFormulaDate(variable) - $: metadata = $session.metadata - - $: openAllParameters = directParametersName.length === 0 - $: rootParameterById = mergeParameters(parameters) </script> diff --git a/src/lib/components/variables/VariableReferredParametersPane.svelte b/src/lib/components/variables/VariableReferredParametersPane.svelte index 9357a73035884f32eb1e55fc5f7a3873687439e9..f43d48b956480dc356aaaf0370d10abf11e87171 100644 --- a/src/lib/components/variables/VariableReferredParametersPane.svelte +++ b/src/lib/components/variables/VariableReferredParametersPane.svelte @@ -1,56 +1,21 @@ <script lang="ts"> - import type { Parameter, Variable } from "@openfisca/ast" - import { improveParameterWithAncestors } from "@openfisca/ast" + import { iterVariableParameters, variableSummaryByName } from "$lib/variables" - import { browser } from "$app/env" - import VariableReferredParameters from "$lib/components/variables/VariableReferredParameters.svelte" + import VariableReferredParameters from "./VariableReferredParameters.svelte" export let date: string export let name: string // export let pane: string - let variable: Variable | undefined = undefined - let parameters: Parameter[] | undefined = undefined + $: variable = variableSummaryByName[name] - $: browser && retrieveVariable(name) - - $: browser && retrieveVariableReferredParameters(name, date) - - async function retrieveVariable(name: string): Promise<void> { - const url = `/variables/${name}.json` - const res = await fetch(url) - if (res.ok) { - variable = await res.json() - } else { - console.error( - `Error ${ - res.status - } while retrieving referred input variables of variable "${name}" at ${url}\n\n${await res.text()}`, - ) - variable = undefined - } + $: if (variable === undefined) { + console.error(`Variable "${name}" not found`) } - async function retrieveVariableReferredParameters( - name: string, - date: string, - ): Promise<void> { - const url = `/variables/${name}/parameters/${date}.json` - const res = await fetch(url) - if (res.ok) { - const parametersWithAncestors = await res.json() - parameters = parametersWithAncestors.map(improveParameterWithAncestors) - } else { - console.error( - `Error ${ - res.status - } while retrieving referred parameters of variable "${name}" at ${url}\n\n${await res.text()}`, - ) - parameters = undefined - } - } + $: parameters = [...iterVariableParameters(variable, date)] </script> -{#if parameters !== undefined && variable !== undefined} +{#if variable !== undefined} <VariableReferredParameters {date} {parameters} {variable} /> {/if} diff --git a/src/lib/components/variables/VariableView.svelte b/src/lib/components/variables/VariableView.svelte index 97a3746a91ef28ff528a6a8c3d39d3a64733f53a..053307359609698a2fa5765934cbb710d434400b 100644 --- a/src/lib/components/variables/VariableView.svelte +++ b/src/lib/components/variables/VariableView.svelte @@ -5,10 +5,11 @@ import { getContext } from "svelte" import type { Writable } from "svelte/store" - import { session } from "$app/stores" import FormulaView from "$lib/components/variables/FormulaView.svelte" import type { CalculationName } from "$lib/decompositions" import { calculationNames } from "$lib/decompositions" + import { entityByKey } from "$lib/entities" + import { metadata } from "$lib/metadata" import type { Situation } from "$lib/situations" import { getSituationVariableValue, @@ -35,12 +36,10 @@ ) as Writable<Set<CalculationName>> let valueError = null - $: entity = $session.entityByKey[variable.entity] + $: entity = entityByKey[variable.entity] $: entitySituation = situation[entity.key_plural] - $: metadata = $session.metadata - function asBoolean(value: boolean | number | string): boolean { return value as boolean } @@ -89,7 +88,7 @@ break } const updatedSituation = setSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -268,7 +267,7 @@ on:blur={(event) => changeValue(event, populationId)} on:change={(event) => changeValue(event, populationId)} value={getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -284,7 +283,7 @@ <input checked={asBoolean( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -295,7 +294,7 @@ on:change={(event) => changeValue(event, populationId)} type="checkbox" /> - {#if asBoolean(getSituationVariableValue($session.entityByKey, situation, variable, populationId, year))}Oui{:else}Non{/if} + {#if asBoolean(getSituationVariableValue(entityByKey, situation, variable, populationId, year))}Oui{:else}Non{/if} </label> {:else if variable.value_type === "date"} <input @@ -304,7 +303,7 @@ type="date" value={asString( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -319,7 +318,7 @@ type="number" value={asNumber( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, @@ -334,7 +333,7 @@ type="text" value={asString( getSituationVariableValue( - $session.entityByKey, + entityByKey, situation, variable, populationId, diff --git a/src/lib/decompositions.ts b/src/lib/decompositions.ts index 4c4313cac0c6ec9b0ac765dab559a8afd00555eb..f8c6640ced10de731a779fec338b23b429777569 100644 --- a/src/lib/decompositions.ts +++ b/src/lib/decompositions.ts @@ -12,6 +12,8 @@ import type { WaterfallOptions, } from "@openfisca/ast" +import decompositionCoreByNameUnknown from "$lib/openfisca/decompositions.json" +import waterfallsUnknown from "$lib/openfisca/waterfalls.json" import type { Situation } from "$lib/situations" export type CalculationName = "amendment" | "bill" | "law" @@ -57,6 +59,29 @@ export interface VisibleDecomposition { export const calculationNames: CalculationName[] = ["law", "bill", "amendment"] +export const decompositionCoreByName: DecompositionCoreByName = + decompositionCoreByNameUnknown + +export const waterfalls: Waterfall[] = waterfallsUnknown + +export const decompositionsOptionsVariablesName = new Set<string>() +for (const decompositionCore of Object.values(decompositionCoreByName)) { + if (decompositionCore.options === undefined) { + continue + } + for (const options of decompositionCore.options) { + if (options.waterfall !== undefined) { + continue + } + for (const variableName of Object.keys(options)) { + if (["else", "then"].includes(variableName)) { + continue + } + decompositionsOptionsVariablesName.add(variableName) + } + } +} + export function buildDecompositionByNameFromCore( decompositionCoreByName: DecompositionCoreByName, ): DecompositionByName | undefined { diff --git a/src/lib/entities.ts b/src/lib/entities.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8080195ce15f033aa84ca8a24c2276d431fc45a --- /dev/null +++ b/src/lib/entities.ts @@ -0,0 +1,9 @@ +import type { EntityByKey } from "@openfisca/ast" + +import entityByKeyUnknown from "$lib/openfisca/entities.json" + +export const entityByKey = entityByKeyUnknown as EntityByKey + +export const personEntityKey = Object.entries(entityByKey) + .filter(([, entity]) => entity.is_person) + .map(([key]) => key)[0] diff --git a/src/lib/metadata.ts b/src/lib/metadata.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc7a6f4af34c26b62593b63ab150561ca8f38e34 --- /dev/null +++ b/src/lib/metadata.ts @@ -0,0 +1,5 @@ +import type { Metadata } from "@openfisca/ast" + +import metadataUnknown from "$lib/openfisca/metadata.json" + +export const metadata: Metadata = metadataUnknown diff --git a/src/lib/parameters.ts b/src/lib/parameters.ts index 75441af62bc3a1563b3fc49a6eb4a607e9ff5892..0447ea76a81dc0204a4a795cfd885099d9392ba7 100644 --- a/src/lib/parameters.ts +++ b/src/lib/parameters.ts @@ -1,7 +1,7 @@ -import { scaleByInstantFromBrackets } from "@openfisca/ast" - import type { Metadata, + NodeParameter, + Parameter, Reference, ScaleAtInstant, ScaleParameter, @@ -10,13 +10,18 @@ import type { } from "@openfisca/ast" import { AmountUnit, + improveParameter, ParameterClass, RateUnit, + scaleByInstantFromBrackets, ScaleType, Unit, ValueType, } from "@openfisca/ast" +import rootParameterUnknown from "$lib/openfisca/editable_processed_parameters.json" +import leafParametersNameUnknown from "$lib/openfisca/processed_parameters_names.json" + export interface InstantReferencesAndScale { instant: string references: Reference[] @@ -29,6 +34,11 @@ export interface InstantReferencesAndValue { valueAtInstant?: ValueAtInstant } +export const leafParametersName = new Set(leafParametersNameUnknown) + +improveParameter(null, rootParameterUnknown as NodeParameter) +export const rootParameter = rootParameterUnknown as NodeParameter + export function buildInstantReferencesAndScaleArray( parameter: ScaleParameter, ): InstantReferencesAndScale[] { @@ -86,6 +96,22 @@ export function buildInstantReferencesAndValueArray( .map(([, instantReferencesAndValue]) => instantReferencesAndValue) } +export function getParameter(name: string): Parameter | undefined { + let parameter = rootParameter as Parameter + for (const id of name.split(".")) { + const children = + parameter.class === ParameterClass.Node ? parameter.children : undefined + if (children === undefined) { + return undefined + } + parameter = children[id] + if (parameter === undefined) { + return undefined + } + } + return parameter +} + export function labelFromParameterClass( parameterClass: ParameterClass | string, ): string { diff --git a/src/lib/server/config.ts b/src/lib/server/config.ts index 5261d12110dc240b1b1a3857dda479dba50ac024..8ae023cc65d7bed03766df681e84dfdd56bbb29f 100644 --- a/src/lib/server/config.ts +++ b/src/lib/server/config.ts @@ -8,11 +8,9 @@ export interface Config { apiWebSocketBaseUrls: string[] baseUrl: string childrenKey: string - decompositionsPath: string familyEntityKey: string githubPersonalAccessToken?: string hiddenEntitiesKeyPlural?: string[] - jsonDir: string matomo?: { prependDomain?: boolean siteId: number @@ -27,6 +25,7 @@ export interface Config { jwtSecret: string profileUrl: string } + openfiscaDir: string openfiscaRepository: { branch: string group: string @@ -38,7 +37,6 @@ export interface Config { proxy: boolean simulationsDir: string title: string - waterfallsPath: string } const [validConfig, error] = validateConfig({ @@ -46,11 +44,9 @@ const [validConfig, error] = validateConfig({ apiBaseUrls: process.env["API_BASE_URLS"], baseUrl: process.env["BASE_URL"], childrenKey: process.env["CHILDREN_KEY"], - decompositionsPath: process.env["DECOMPOSITION_PATH"], familyEntityKey: process.env["FAMILY_KEY"], githubPersonalAccessToken: process.env["GITHUB_PERSONAL_ACCESS_TOKEN"], hiddenEntitiesKeyPlural: process.env["HIDDEN_ENTITIES"], - jsonDir: process.env["JSON_DIR"], matomo: process.env["MATOMO_SITE_ID"] && process.env["MATOMO_URL"] ? { @@ -70,6 +66,7 @@ const [validConfig, error] = validateConfig({ profileUrl: process.env["OAUTH2_PROFILE_URL"], } : null, + openfiscaDir: "src/lib/openfisca", openfiscaRepository: { branch: process.env["OPENFISCA_BRANCH"], group: process.env["OPENFISCA_GROUP"], @@ -80,7 +77,6 @@ const [validConfig, error] = validateConfig({ portalUrl: process.env["PORTAL_URL"], proxy: process.env["PROXY"], simulationsDir: process.env["SIMULATIONS_DIR"], - 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 deleted file mode 100644 index 92776a19f84387f9095bd0427217c7aa11d72a5a..0000000000000000000000000000000000000000 --- a/src/lib/server/decompositions.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - auditChain, - auditCleanArray, - auditRequire, - strictAudit, -} from "@auditors/core" -import type { Waterfall } from "@openfisca/ast" -import { auditDecompositionByName, auditWaterfall } from "@openfisca/ast" -import fs from "fs-extra" - -import type { DecompositionCoreByName } from "$lib/decompositions" -import config from "$lib/server/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 decompositionsOptionsVariablesName = new Set<string>() -for (const decompositionCore of Object.values(decompositionCoreByName)) { - if (decompositionCore.options === undefined) { - continue - } - for (const options of decompositionCore.options) { - if (options.waterfall !== undefined) { - continue - } - for (const variableName of Object.keys(options)) { - if (["else", "then"].includes(variableName)) { - continue - } - decompositionsOptionsVariablesName.add(variableName) - } - } -} - -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 diff --git a/src/lib/server/entities.ts b/src/lib/server/entities.ts deleted file mode 100644 index 3d2d7245a4484ec3d00349c0d49d6daadba182d0..0000000000000000000000000000000000000000 --- a/src/lib/server/entities.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { EntityByKey } from "@openfisca/ast" -import fs from "fs-extra" -import path from "path" - -import config from "$lib/server/config" - -const { jsonDir } = config - -let entitiesFilePath = path.join(jsonDir, "custom", "entities.json") -if (!fs.pathExistsSync(entitiesFilePath)) { - entitiesFilePath = path.join(jsonDir, "entities.json") -} -export const entityByKey = fs.readJsonSync(entitiesFilePath) as EntityByKey - -export const personEntityKey = Object.entries(entityByKey) - .filter(([, entity]) => entity.is_person) - .map(([key]) => key)[0] diff --git a/src/lib/server/metadata.ts b/src/lib/server/metadata.ts deleted file mode 100644 index 36023018062b34fca98a73afacb6b69d4a473ede..0000000000000000000000000000000000000000 --- a/src/lib/server/metadata.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Metadata } from "@openfisca/ast" -import fs from "fs-extra" -import path from "path" - -import config from "$lib/server/config" - -const { jsonDir } = config - -export const metadata = fs.readJsonSync( - path.join(jsonDir, "metadata.json"), -) as Metadata diff --git a/src/lib/server/parameters.ts b/src/lib/server/parameters.ts deleted file mode 100644 index ef1e887346007e0d3b986f6efa8b294e5dd5fc58..0000000000000000000000000000000000000000 --- a/src/lib/server/parameters.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { NodeParameter, Parameter } from "@openfisca/ast" -import { ParameterClass } from "@openfisca/ast" -import fs from "fs-extra" -import path from "path" - -import config from "$lib/server/config" - -const { jsonDir } = config - -export const leafParametersName = new Set( - fs.readJsonSync( - path.join(jsonDir, "processed_parameters_names.json"), - ) as string[], -) - -export const parameters = fs.readJsonSync( - path.join(jsonDir, "editable_processed_parameters.json"), -) as NodeParameter - -export function getParameterWithAncestors( - name: string, -): [Parameter | undefined, NodeParameter[]] { - const ancestors = [] - let parameter = parameters as Parameter - for (const id of name.split(".")) { - if (parameter.name) { - // Note: Skip root parameter. - ancestors.push(parameter) - } - const children = - parameter.class === ParameterClass.Node ? parameter.children : undefined - if (children === undefined) { - return [undefined, ancestors] - } - parameter = children[id] - if (parameter === undefined) { - return [undefined, ancestors] - } - } - return [parameter, ancestors] -} diff --git a/src/lib/server/situations.ts b/src/lib/server/situations.ts deleted file mode 100644 index 8c8800608f9a0154886800a360b4596a167b8950..0000000000000000000000000000000000000000 --- a/src/lib/server/situations.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Situation } from "$lib/situations" -import fs from "fs-extra" -import path from "path" - -import config from "$lib/server/config" - -const { jsonDir } = config - -export const testCases = fs.readJsonSync( - path.join(jsonDir, "test_cases.json"), -) as Situation[] diff --git a/src/lib/server/variables.ts b/src/lib/server/variables.ts deleted file mode 100644 index 1c6e10683756bdb0ce672ef4fcab33fef9c5d076..0000000000000000000000000000000000000000 --- a/src/lib/server/variables.ts +++ /dev/null @@ -1,120 +0,0 @@ -import type { - NodeParameter, - Parameter, - Variable, - VariableByName, -} from "@openfisca/ast" -import fs from "fs-extra" -import path from "path" - -import config from "$lib/server/config" -import { getParameterWithAncestors } from "$lib/server/parameters" - -const { jsonDir } = config - -export const variableSummaryByName = fs.readJsonSync( - path.join(jsonDir, "variables_summaries.json"), -) as VariableByName - -export function* iterVariableInputVariables( - variable: Variable, - date: string, - encounteredVariablesName: Set<string> = new Set(), -): Generator<Variable, void> { - const name = variable.name - if (encounteredVariablesName.has(name)) { - return - } - encounteredVariablesName.add(name) - - const formulas = variable.formulas - if (formulas === undefined) { - // Variable is an input variable. - yield variable - return - } - const dates = Object.keys(formulas).reverse() - let formula = undefined - for (const bestDate of dates) { - if (bestDate <= date) { - formula = formulas[bestDate] - break - } - } - if (formula == null) { - // No candidate date less than or equal to date found. - return - } - const referredVariablesName = formula.variables - if (referredVariablesName === undefined) { - return - } - for (const referredVariableName of referredVariablesName) { - const referredVariable = variableSummaryByName[referredVariableName] - yield* iterVariableInputVariables( - referredVariable, - date, - encounteredVariablesName, - ) - } -} - -export function* iterVariableParametersWithAncestors( - variable: Variable, - date: string, - encounteredParametersName: Set<string> = new Set(), - encounteredVariablesName: Set<string> = new Set(), -): Generator<[Parameter, NodeParameter[]], void> { - const name = variable.name - if (encounteredVariablesName.has(name)) { - return - } - encounteredVariablesName.add(name) - - const formulas = variable.formulas - if (formulas === undefined) { - return - } - const dates = Object.keys(formulas).reverse() - let formula = undefined - for (const bestDate of dates) { - if (bestDate <= date) { - formula = formulas[bestDate] - break - } - } - if (formula == null) { - // No candidate date less than or equal to date found. - return - } - - const referredVariablesName = formula.variables - if (referredVariablesName !== undefined) { - for (const referredVariableName of referredVariablesName) { - const referredVariable = variableSummaryByName[referredVariableName] - yield* iterVariableParametersWithAncestors( - referredVariable, - date, - encounteredParametersName, - encounteredVariablesName, - ) - } - } - - const referredParametersName = formula.parameters - if (referredParametersName !== undefined) { - for (const referredParameterName of referredParametersName) { - if (encounteredParametersName.has(referredParameterName)) { - continue - } - encounteredParametersName.add(referredParameterName) - - const [referredParameter, referredParameterAncestors] = - getParameterWithAncestors(referredParameterName) - if (referredParameter === undefined) { - continue - } - yield [referredParameter, referredParameterAncestors] - } - } -} diff --git a/src/lib/sessions.ts b/src/lib/sessions.ts index caa0e5e5554081c1b37d53ae5be871aa3eb6eed0..f4afccf6bf09d46f78ffeb8741f30fa311a02d16 100644 --- a/src/lib/sessions.ts +++ b/src/lib/sessions.ts @@ -1,7 +1,3 @@ -import type { EntityByKey, Metadata, Waterfall } from "@openfisca/ast" - -import type { DecompositionCoreByName } from "$lib/decompositions" -import type { Situation } from "$lib/situations" import type { User } from "$lib/users" export interface Session { @@ -11,27 +7,19 @@ export interface Session { baseUrl: string authenticationEnabled: boolean childrenKey: string - decompositionCoreByName: DecompositionCoreByName - decompositionsOptionsVariablesName: Set<string> familyEntityKey: string hasGithubPersonalAccessToken: boolean - entityByKey: EntityByKey hiddenEntitiesKeyPlural?: string[] - leafParametersName: Set<string> matomo?: { prependDomain?: boolean siteId: number subdomains?: string url: string } - metadata: Metadata openfiscaRepository: SessionOpenFiscaRepository - personEntityKey: string portalUrl: string - testCases: Situation[] title: string user?: User - waterfalls: Waterfall[] } export interface SessionOpenFiscaRepository { diff --git a/src/lib/situations.ts b/src/lib/situations.ts index ae4d2f44c878b6924ba899adb6c1f045194f52e5..441e4656c4553dff98054fc2ceabe1ca78a5fee8 100644 --- a/src/lib/situations.ts +++ b/src/lib/situations.ts @@ -1,6 +1,8 @@ import type { Entity, EntityByKey, GroupEntity, Variable } from "@openfisca/ast" import { getRolePersonsIdKey } from "@openfisca/ast" +import testCasesCoreUnknown from "$lib/openfisca/test_cases.json" + export interface Axis { count: number index?: number @@ -38,6 +40,8 @@ export type SituationWithAxes = Situation & { axes?: Axis[][] } +export const testCasesCore = testCasesCoreUnknown as unknown as Situation[] + export function buildTestCasesWithoutNonInputVariables( entityByKey: EntityByKey, inputInstantsByVariableNameArray: Array<{ diff --git a/src/lib/variables.ts b/src/lib/variables.ts index 63bda673ebf0b7c8517512ee9d149db9c78e5044..49aee773e62ac8af791a5877a41f603796bb9920 100644 --- a/src/lib/variables.ts +++ b/src/lib/variables.ts @@ -1,4 +1,13 @@ -import type { Formula, Reference, Variable } from "@openfisca/ast" +import type { + Formula, + Parameter, + Reference, + Variable, + VariableByName, +} from "@openfisca/ast" + +import variableSummaryByNameUnknown from "$lib/openfisca/variables_summaries.json" +import { getParameter } from "$lib/parameters" export type VariableValues = boolean[] | number[] | string[] @@ -8,6 +17,9 @@ export interface InstantFormulaAndReferences { references: Reference[] } +export const variableSummaryByName = + variableSummaryByNameUnknown as VariableByName + export function buildInstantFormulaAndReferencesArray( variable: Variable, ): InstantFormulaAndReferences[] { @@ -36,24 +48,104 @@ export function buildInstantFormulaAndReferencesArray( .map(([, instantFormulaAndReferences]) => instantFormulaAndReferences) } -export async function retrieveVariableSummaryByName( - baseUrl: string, - variablesName: string[], -): Promise<{ [name: string]: Variable } | undefined> { - if (variablesName.length === 0) { - return {} - } - const url = `/variables.json?${new URLSearchParams( - variablesName.map((name) => ["name", name]), - ).toString()}` - const res = await fetch(new URL(url, baseUrl).toString()) - if (!res.ok) { - console.error( - `Error ${ - res.status - } while retrieving variables at ${url}\n\n${await res.text()}`, +export function* iterVariableInputVariables( + variable: Variable, + date: string, + encounteredVariablesName: Set<string> = new Set(), +): Generator<Variable, void> { + const name = variable.name + if (encounteredVariablesName.has(name)) { + return + } + encounteredVariablesName.add(name) + + const formulas = variable.formulas + if (formulas === undefined) { + // Variable is an input variable. + yield variable + return + } + const dates = Object.keys(formulas).reverse() + let formula = undefined + for (const bestDate of dates) { + if (bestDate <= date) { + formula = formulas[bestDate] + break + } + } + if (formula == null) { + // No candidate date less than or equal to date found. + return + } + const referredVariablesName = formula.variables + if (referredVariablesName === undefined) { + return + } + for (const referredVariableName of referredVariablesName) { + const referredVariable = variableSummaryByName[referredVariableName] + yield* iterVariableInputVariables( + referredVariable, + date, + encounteredVariablesName, ) - return undefined } - return await res.json() +} + +export function* iterVariableParameters( + variable: Variable, + date: string, + encounteredParametersName: Set<string> = new Set(), + encounteredVariablesName: Set<string> = new Set(), +): Generator<Parameter, void> { + const name = variable.name + if (encounteredVariablesName.has(name)) { + return + } + encounteredVariablesName.add(name) + + const formulas = variable.formulas + if (formulas === undefined) { + return + } + const dates = Object.keys(formulas).reverse() + let formula = undefined + for (const bestDate of dates) { + if (bestDate <= date) { + formula = formulas[bestDate] + break + } + } + if (formula == null) { + // No candidate date less than or equal to date found. + return + } + + const referredVariablesName = formula.variables + if (referredVariablesName !== undefined) { + for (const referredVariableName of referredVariablesName) { + const referredVariable = variableSummaryByName[referredVariableName] + yield* iterVariableParameters( + referredVariable, + date, + encounteredParametersName, + encounteredVariablesName, + ) + } + } + + const referredParametersName = formula.parameters + if (referredParametersName !== undefined) { + for (const referredParameterName of referredParametersName) { + if (encounteredParametersName.has(referredParameterName)) { + continue + } + encounteredParametersName.add(referredParameterName) + + const referredParameter = getParameter(referredParameterName) + if (referredParameter === undefined) { + continue + } + yield referredParameter + } + } } diff --git a/src/routes/__error.svelte b/src/routes/__error.svelte index bdc20c06160a37d2383ec001af8f78b8806bb853..155ea60264bea61f113de62c52aed41a8ba3c1cf 100644 --- a/src/routes/__error.svelte +++ b/src/routes/__error.svelte @@ -1,5 +1,5 @@ <script context="module" lang="ts"> - import type { ErrorLoadInput, LoadOutput } from "@sveltejs/kit/types/page" + import type { ErrorLoadInput, LoadOutput } from "@sveltejs/kit" export function load({ error, status }: ErrorLoadInput): LoadOutput { return { diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index 65bb3c2c29fc613e31923383df886c5fc3c017dd..658ef441aa2347fa211bdc14c653d641bdb3af3f 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import "../app.css" - import type { EntityByKey, Waterfall } from "@openfisca/ast" + import type { Waterfall } from "@openfisca/ast" import { setContext } from "svelte" import type { Writable } from "svelte/store" import { writable } from "svelte/store" @@ -17,16 +17,20 @@ EvaluationByName, } from "$lib/decompositions" import { - calculationNames, buildDecompositionByNameFromCore, + calculationNames, + decompositionCoreByName, walkDecompositionsCore, updateEvaluations, + waterfalls, } from "$lib/decompositions" + import { entityByKey } from "$lib/entities" import type { Reform } from "$lib/reforms" import type { PopulationWithoutId, Situation } from "$lib/situations" import { buildTestCasesWithoutNonInputVariables, getPopulationReservedKeys, + testCasesCore, } from "$lib/situations" import type { VariableValues } from "$lib/variables" import type { WebSocketByName, WebSocketOpenByName } from "$lib/websockets" @@ -42,22 +46,19 @@ > = writable({}) setContext("calculationTokenByName", calculationTokenByName) - const entityByKey = $session.entityByKey as EntityByKey - const vectorIndexes: Writable<number[]> = writable( - new Array($session.testCases.length).fill(0), + new Array(testCasesCore.length).fill(0), ) setContext("vectorIndexes", vectorIndexes) const vectorLength = writable(1) setContext("vectorLength", vectorLength) - const waterfalls = $session.waterfalls let waterfall = writable(waterfalls[0]) setContext("waterfall", waterfall) const decompositionByName = writable( - buildDecompositionByNameFromCore($session.decompositionCoreByName), + buildDecompositionByNameFromCore(decompositionCoreByName), ) setContext("decompositionByName", decompositionByName) @@ -67,7 +68,7 @@ Object.fromEntries( calculationNames.map((calculationName) => [ calculationName, - new Array($session.testCases.length) + new Array(testCasesCore.length) .fill(null) .map((_, situationIndex) => updateEvaluations( @@ -90,7 +91,7 @@ Array<{ [name: string]: Set<string> }> - > = writable(extractInputInstantsFromTestCases($session.testCases)) + > = writable(extractInputInstantsFromTestCases(testCasesCore)) setContext( "inputInstantsByVariableNameArray", inputInstantsByVariableNameArray, @@ -101,7 +102,7 @@ // Note: Duplicates are removed from nonVirtualDecompositionsName, because a variable name // may appear more than once in decomposition. const nonVirtualDecompositionsName = computeNonVirtualDecompositionsName( - $session.decompositionCoreByName, + decompositionCoreByName, waterfalls, ) setContext("nonVirtualDecompositionsName", nonVirtualDecompositionsName) @@ -137,13 +138,13 @@ try { testCasesValue = JSON.parse(testCasesJson) } catch { - testCasesValue = $session.testCases + testCasesValue = testCasesCore } } else { - testCasesValue = $session.testCases + testCasesValue = testCasesCore } } else { - testCasesValue = $session.testCases + testCasesValue = testCasesCore } const testCases = writable(testCasesValue) setContext("testCases", testCases) @@ -180,7 +181,7 @@ "testCases", JSON.stringify( buildTestCasesWithoutNonInputVariables( - $session.entityByKey, + entityByKey, $inputInstantsByVariableNameArray, $testCases, ), diff --git a/src/routes/entities.svelte b/src/routes/entities.svelte index 8a31e670ec6ffe652968c1d33b9548cbf609cac2..59eb5fd31beaa729ba1cc7f250097999bccf3db7 100644 --- a/src/routes/entities.svelte +++ b/src/routes/entities.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import { session } from "$app/stores" - const entityByKey = $session.entityByKey + import { entityByKey } from "$lib/entities" </script> <svelte:head> diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 6939ccb7b3d97c68068d2b3156466d72efe13e1b..0c39b26576fa9204ed503fd10d3fbf97ee5bbddf 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -36,7 +36,9 @@ labelFromCalculationName, updateEvaluations, updateVectorIndex, + waterfalls, } from "$lib/decompositions" + import { entityByKey } from "$lib/entities" import type { Reform } from "$lib/reforms" import type { Axis, @@ -47,6 +49,7 @@ import { buildTestCasesWithoutNonInputVariables, getPopulationReservedKeys, + testCasesCore, } from "$lib/situations" import type { SelfTargetAProps } from "$lib/urls" import type { WebSocketByName, WebSocketOpenByName } from "$lib/websockets" @@ -112,7 +115,6 @@ ) 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 webSocketByName = getContext("webSocketByName") as Writable< WebSocketByName | undefined > @@ -381,7 +383,7 @@ function reset(): void { $reform = {} - $testCases = $session.testCases + $testCases = testCasesCore if ( calculationNames.some( (calculationName) => !$requestedCalculationsName.has(calculationName), @@ -417,7 +419,7 @@ body: JSON.stringify({ reform: $reform, testCases: buildTestCasesWithoutNonInputVariables( - $session.entityByKey, + entityByKey, $inputInstantsByVariableNameArray, $testCases, ), @@ -442,7 +444,7 @@ function submit(requestedCalculationsName: Set<CalculationName>) { // Aggregate every situations into a single one without calculated variables. const aggregatedSituation: SituationWithAxes = {} - const entities = Object.values($session.entityByKey as EntityByKey) + const entities = Object.values(entityByKey as EntityByKey) for (const [situationIndex, situation] of $testCases.entries()) { const inputInstantsByVariableName = $inputInstantsByVariableNameArray[situationIndex] diff --git a/src/routes/parameters/[parameter]/edit.svelte b/src/routes/parameters/[parameter]/edit.svelte index 75155a718f55f6366eb17b4bdd1b67490d902b6b..458df8c3b219c8c3fa72b5f5b922d3dcf4a56c13 100644 --- a/src/routes/parameters/[parameter]/edit.svelte +++ b/src/routes/parameters/[parameter]/edit.svelte @@ -3,14 +3,13 @@ import type { Metadata } from "@openfisca/ast" import { auditRawParameterToEditable, - improveParameterWithAncestors, iterParameterAncestors, ParameterClass, } from "@openfisca/ast" - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" + import type { LoadInput, LoadOutput } from "@sveltejs/kit" import yaml from "js-yaml" - import { labelFromParameterClass } from "$lib/parameters" + import { getParameter, labelFromParameterClass } from "$lib/parameters" import type { SessionOpenFiscaRepository } from "$lib/sessions" export async function load({ @@ -19,18 +18,13 @@ session, }: LoadInput): Promise<LoadOutput> { const { parameter: name } = page.params - const url = `/parameters/${name}.json` - const res = await fetch(url) - if (!res.ok) { + const processedParameter = getParameter(name) + if (processedParameter === undefined) { return { - status: res.status, - error: new Error(`Could not load ${url}`), + status: 404, + error: new Error(`Parameter "${name}" not found`), } } - const processedParameterWithAncestors = await res.json() - const processedParameter = improveParameterWithAncestors( - processedParameterWithAncestors, - ) let unprocessedParameterUrl = newParameterRepositoryRawUrl( session.metadata, diff --git a/src/routes/parameters/[parameter]/index.json.ts b/src/routes/parameters/[parameter]/index.json.ts index eddda26537f32e9491b4396f4739869020a94947..516457d8df94242421e8c9d339bf71fcb300972a 100644 --- a/src/routes/parameters/[parameter]/index.json.ts +++ b/src/routes/parameters/[parameter]/index.json.ts @@ -4,37 +4,17 @@ import { auditEditableParameter, convertEditableParameterToRaw, ParameterClass, - parameterWithoutChildren, yamlFromRawParameter, } from "@openfisca/ast" import type { RequestHandler } from "@sveltejs/kit" import { randomBytes } from "crypto" +import { metadata } from "$lib/metadata" +import { getParameter } from "$lib/parameters" import config from "$lib/server/config" -import { metadata } from "$lib/server/metadata" -import { getParameterWithAncestors } from "$lib/server/parameters" const { githubPersonalAccessToken, openfiscaRepository } = config -export const get: RequestHandler = ({ params }) => { - const { parameter: name } = params - const [parameter, ancestors] = getParameterWithAncestors(name) - if (parameter === undefined) { - return { body: null, status: 404 } - } - return { - body: { - // Remove children from ancestors, because we don't want to send - // the full tree. - ancestors: ancestors.map( - (ancestor) => - parameterWithoutChildren(ancestor) as unknown as JsonValue, - ), - parameter: parameter as unknown as JsonValue, - }, - } -} - export const put: RequestHandler = async ({ body, params }) => { if (githubPersonalAccessToken === undefined) { return { @@ -49,7 +29,7 @@ export const put: RequestHandler = async ({ body, params }) => { } const { parameter: name } = params - const [parameter] = getParameterWithAncestors(name) + const parameter = getParameter(name) if (parameter === undefined) { return { body: null, status: 404 } } diff --git a/src/routes/parameters/[parameter]/index.svelte b/src/routes/parameters/[parameter]/index.svelte index 5db78418ecef9905c0311e0d7cb56e148228465e..42433b6da9c280d784a54adbedd731170768ba12 100644 --- a/src/routes/parameters/[parameter]/index.svelte +++ b/src/routes/parameters/[parameter]/index.svelte @@ -1,21 +1,20 @@ <script context="module" lang="ts"> - import { improveParameterWithAncestors } from "@openfisca/ast" - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" + import type { LoadInput, LoadOutput } from "@sveltejs/kit" - export async function load({ fetch, page }: LoadInput): Promise<LoadOutput> { + import { getParameter } from "$lib/parameters" + + export function load({ page }: LoadInput): LoadOutput { const { parameter: name } = page.params - const url = `/parameters/${name}.json` - const res = await fetch(url) - if (!res.ok) { + const parameter = getParameter(name) + if (parameter === undefined) { return { - status: res.status, - error: new Error(`Could not load ${url}`), + status: 404, + error: new Error(`Parameter "${name}" not found`), } } - const parameterWithAncestors = await res.json() return { props: { - parameter: improveParameterWithAncestors(parameterWithAncestors), + parameter, }, } } @@ -40,8 +39,7 @@ <a href="/" - class="inline-flex items-center bg-gray-200 my-5 p-1 pr-2 text-xs rounded -text-black shadow-md hover:bg-gray-400" + class="inline-flex items-center bg-gray-200 my-5 p-1 pr-2 text-xs rounded text-black shadow-md hover:bg-gray-400" > <!-- material icons - Arrow Back --> <svg diff --git a/src/routes/parameters/index.json.ts b/src/routes/parameters/index.json.ts deleted file mode 100644 index 550cadf9346216f788dba8f8448e859e3d79d97a..0000000000000000000000000000000000000000 --- a/src/routes/parameters/index.json.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { JsonValue } from "@openfisca/ast" -import type { RequestHandler } from "@sveltejs/kit" - -import { parameters } from "$lib/server/parameters" - -export const get: RequestHandler = () => { - return { body: parameters as unknown as JsonValue } -} diff --git a/src/routes/parameters/index.svelte b/src/routes/parameters/index.svelte index 5cc0f12c57b917358e3c4071bac1520850d1f074..12887cf6d3fd7b80776984ae5249b676cc557a03 100644 --- a/src/routes/parameters/index.svelte +++ b/src/routes/parameters/index.svelte @@ -1,33 +1,10 @@ -<script context="module" lang="ts"> - import type { NodeParameter, Parameter } from "@openfisca/ast" - import { improveParameter } from "@openfisca/ast" - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" - - export async function load({ fetch }: LoadInput): Promise<LoadOutput> { - const url = "/parameters.json" - const res = await fetch(url) - if (!res.ok) { - return { - status: res.status, - error: new Error(`Could not load ${url}`), - } - } - const rootParameter = await res.json() - improveParameter(null, rootParameter) - return { - props: { - rootParameter, - }, - } - } -</script> - <script lang="ts"> + import type { Parameter } from "@openfisca/ast" + import { goto } from "$app/navigation" import { page, session } from "$app/stores" import ParametersSearch from "$lib/components/parameters/ParametersSearch.svelte" - - export let rootParameter: NodeParameter + import { rootParameter } from "$lib/parameters" let initialTerm: string | undefined = undefined diff --git a/src/routes/simulation.svelte b/src/routes/simulation.svelte index 655bd81a0e6b25775c1ea7248830702ba80f5f4e..1695385e0e26d01cdbee81c89324bfed261c6ea3 100644 --- a/src/routes/simulation.svelte +++ b/src/routes/simulation.svelte @@ -1,5 +1,5 @@ <script context="module" lang="ts"> - import type { LoadOutput } from "@sveltejs/kit/types/page" + import type { LoadOutput } from "@sveltejs/kit" export function load(): LoadOutput { return { redirect: "/", diff --git a/src/routes/simulations/[simulation].svelte b/src/routes/simulations/[simulation].svelte index 525c19c87d5c89ab481719fc66d59d6488522986..2912e896040c51a00363568f137eca8dc1461870 100644 --- a/src/routes/simulations/[simulation].svelte +++ b/src/routes/simulations/[simulation].svelte @@ -1,5 +1,5 @@ <script context="module" lang="ts"> - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" + import type { LoadInput, LoadOutput } from "@sveltejs/kit" export async function load({ fetch, page }: LoadInput): Promise<LoadOutput> { const { simulation: token } = page.params diff --git a/src/routes/variables/[variable]/index.json.ts b/src/routes/variables/[variable]/index.json.ts index 8dde6864e322599142e0c262531a169a6e85340e..8a897082b4040e3bd244fa2fe54c0d654f9c768e 100644 --- a/src/routes/variables/[variable]/index.json.ts +++ b/src/routes/variables/[variable]/index.json.ts @@ -5,12 +5,12 @@ import sanitizeFilename from "sanitize-filename" import config from "$lib/server/config" -const { jsonDir } = config +const { openfiscaDir } = config export const get: RequestHandler = async ({ params }) => { const { variable: name } = params const variableFilePath = path.join( - jsonDir, + openfiscaDir, "variables", `${sanitizeFilename(name)}.json`, ) diff --git a/src/routes/variables/[variable]/index.svelte b/src/routes/variables/[variable]/index.svelte index 341e85dbc55065afd9d30df47cc0af15da46f21f..6b049cb8124667ce45ab16116f5abd66a5302aac 100644 --- a/src/routes/variables/[variable]/index.svelte +++ b/src/routes/variables/[variable]/index.svelte @@ -1,5 +1,5 @@ <script context="module" lang="ts"> - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" + import type { LoadInput, LoadOutput } from "@sveltejs/kit" export async function load({ fetch, page }: LoadInput): Promise<LoadOutput> { const { variable: name } = page.params diff --git a/src/routes/variables/[variable]/inputs/[date].json.ts b/src/routes/variables/[variable]/inputs/[date].json.ts deleted file mode 100644 index 242be14afb044ab08f1b9c61deb4c2d581364ee8..0000000000000000000000000000000000000000 --- a/src/routes/variables/[variable]/inputs/[date].json.ts +++ /dev/null @@ -1,162 +0,0 @@ -import type { Audit } from "@auditors/core" -import { - auditArray, - auditFunction, - auditOptions, - auditRequire, - auditSetNullish, - auditStringToBoolean, - auditTest, - auditTrimString, - cleanAudit, -} from "@auditors/core" -import type { JsonValue } from "@openfisca/ast" -import type { RequestHandler } from "@sveltejs/kit" -import fs from "fs-extra" -import path from "path" -import sanitizeFilename from "sanitize-filename" - -import { walkDecompositionsCoreName } from "$lib/decompositions" -import config from "$lib/server/config" -import { decompositionCoreByName, waterfalls } from "$lib/server/decompositions" -import { iterVariableInputVariables } from "$lib/server/variables" - -const { jsonDir } = config - -function auditVariableInputsQuery( - audit: Audit, - query: URLSearchParams, -): [unknown, unknown] { - if (query == null) { - return [query, null] - } - if (!(query instanceof URLSearchParams)) { - return audit.unexpectedType(query, "URLSearchParams") - } - - const data: { [key: string]: unknown } = {} - // @ts-expect-error: urlSearchParams.entries() exists both on browsers & Node. - for (const [key, value] of query.entries()) { - let values = data[key] as string[] | undefined - if (values === undefined) { - values = data[key] = [] - } - values.push(value) - } - const errors: { [key: string]: unknown } = {} - const remainingKeys = new Set(Object.keys(data)) - - audit.attribute( - data, - "filter_decomposition", - true, - errors, - remainingKeys, - auditArray(auditTrimString, auditStringToBoolean), - auditTest( - (values) => values.length <= 1, - "Parameter must be present only once in query", - ), - auditFunction((value) => value[0]), - auditSetNullish(false), - ) - audit.attribute( - data, - "waterfall", - true, - errors, - remainingKeys, - auditArray(auditOptions(waterfalls.map(({ name }) => name))), - auditTest( - (values) => values.length <= 1, - "Parameter must be present only once in query", - ), - auditFunction((value) => value[0]), - ) - - if ( - errors.filter_decomposition === undefined && - data.filter_decomposition && - errors.waterfall === undefined - ) { - audit.attribute( - data, - "waterfall", - true, - errors, - remainingKeys, - auditRequire, - ) - } - - return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) -} - -export const get: RequestHandler = async ({ - path: requestPath, - query: requestQuery, - params, -}) => { - const [query, queryError] = auditVariableInputsQuery(cleanAudit, requestQuery) - if (queryError !== null) { - console.error( - `Error in ${requestPath} query:\n${JSON.stringify( - query, - null, - 2, - )}\n\nError:\n${JSON.stringify(queryError, null, 2)}`, - ) - return { - status: 400, - body: { - error: { - code: 400, - details: queryError as JsonValue, - message: "Invalid query", - path: requestPath, - }, - query: query as JsonValue, - }, - } - } - const { - filter_decomposition: filterDecomposition, - waterfall: waterfallName, - } = query as { - filter_decomposition: boolean - waterfall?: string - } - - const { date, variable: name } = params - const variableFilePath = path.join( - jsonDir, - "variables", - `${sanitizeFilename(name)}.json`, - ) - if (!(await fs.pathExists(variableFilePath))) { - return { body: null, status: 404 } - } - const variable = await fs.readJson(variableFilePath) - - let ignoreVariablesName: Set<string> - if (filterDecomposition) { - const waterfall = waterfalls.find(({ name }) => name === waterfallName) - ignoreVariablesName = new Set( - walkDecompositionsCoreName( - decompositionCoreByName, - waterfall.name, - waterfall.root, - false, - ), - ) - ignoreVariablesName.delete(variable.name) - } else { - ignoreVariablesName = new Set() - } - - return { - body: [ - ...iterVariableInputVariables(variable, date, ignoreVariablesName), - ] as unknown as JsonValue, - } -} diff --git a/src/routes/variables/[variable]/inputs/[date].svelte b/src/routes/variables/[variable]/inputs/[date].svelte index 387e9376ec3bbff273ec78745251a29599016753..0ec2089b3d4b9f49f2c750295d865daa909ec9d5 100644 --- a/src/routes/variables/[variable]/inputs/[date].svelte +++ b/src/routes/variables/[variable]/inputs/[date].svelte @@ -1,51 +1,25 @@ <script context="module" lang="ts"> - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" + import type { LoadInput, LoadOutput } from "@sveltejs/kit" - export async function load({ fetch, page }: LoadInput): Promise<LoadOutput> { + import { + iterVariableInputVariables, + variableSummaryByName, + } from "$lib/variables" + + export function load({ page }: LoadInput): LoadOutput { const { date, variable: name } = page.params - const results = await Promise.all([ - (async () => { - const url = `/variables/${name}.json` - const res = await fetch(url) - if (!res.ok) { - return { - status: res.status, - error: new Error(`Could not load ${url}`), - } - } - return { - props: { - variable: await res.json(), - }, - } - })(), - (async () => { - const url = `/variables/${name}/inputs/${date}.json` - const res = await fetch(url) - if (!res.ok) { - return { - status: res.status, - error: new Error(`Could not load ${url}`), - } - } - return { - props: { - inputs: await res.json(), - }, - } - })(), - ]) - const firstResultWithError = results.find( - ({ error }) => error !== undefined, - ) - if (firstResultWithError !== undefined) { - return firstResultWithError + const variable = variableSummaryByName[name] + if (variable === undefined) { + return { + status: 404, + error: new Error(`Variable "${name}" not found`), + } } + const inputs = [...iterVariableInputVariables(variable, date, new Set())] return { props: { - ...Object.fromEntries( - [].concat(...results.map(({ props }) => Object.entries(props))), - ), + inputs, + variable, }, } } diff --git a/src/routes/variables/[variable]/parameters/[date].json.ts b/src/routes/variables/[variable]/parameters/[date].json.ts deleted file mode 100644 index 9a41a2b9ef030842db3adec4bbb81f61234e63c6..0000000000000000000000000000000000000000 --- a/src/routes/variables/[variable]/parameters/[date].json.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { JsonValue } from "@openfisca/ast" -import { parameterWithoutChildren } from "@openfisca/ast" -import type { RequestHandler } from "@sveltejs/kit" -import fs from "fs-extra" -import path from "path" -import sanitizeFilename from "sanitize-filename" - -import config from "$lib/server/config" -import { iterVariableParametersWithAncestors } from "$lib/server/variables" - -const { jsonDir } = config - -export const get: RequestHandler = async ({ params }) => { - const { date, variable: name } = params - const variableFilePath = path.join( - jsonDir, - "variables", - `${sanitizeFilename(name)}.json`, - ) - if (!(await fs.pathExists(variableFilePath))) { - return { body: null, status: 404 } - } - const variable = await fs.readJson(variableFilePath) - return { - body: [...iterVariableParametersWithAncestors(variable, date)].map( - ([parameter, ancestors]) => ({ - // Remove children from ancestors, because we don't want to send - // the full tree. - ancestors: ancestors.map( - (ancestor) => - parameterWithoutChildren(ancestor) as unknown as JsonValue, - ), - parameter: parameter as unknown as JsonValue, - }), - ), - } -} diff --git a/src/routes/variables/[variable]/parameters/[date].svelte b/src/routes/variables/[variable]/parameters/[date].svelte index 3c2f1470b55a413803356dc3c772475f76ddd0e0..09dd88b73f91c93cbbdf83148431af452a3ba0d9 100644 --- a/src/routes/variables/[variable]/parameters/[date].svelte +++ b/src/routes/variables/[variable]/parameters/[date].svelte @@ -1,55 +1,23 @@ <script context="module" lang="ts"> - import { improveParameterWithAncestors } from "@openfisca/ast" - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" + import type { LoadInput, LoadOutput } from "@sveltejs/kit" - export async function load({ fetch, page }: LoadInput): Promise<LoadOutput> { + import { iterVariableParameters, variableSummaryByName } from "$lib/variables" + + export function load({ page }: LoadInput): LoadOutput { const { date, variable: name } = page.params - const results = await Promise.all([ - (async () => { - const url = `/variables/${name}.json` - const res = await fetch(url) - if (!res.ok) { - return { - status: res.status, - error: new Error(`Could not load ${url}`), - } - } - return { - props: { - variable: await res.json(), - }, - } - })(), - (async () => { - const url = `/variables/${name}/parameters/${date}.json` - const res = await fetch(url) - if (!res.ok) { - return { - status: res.status, - error: new Error(`Could not load ${url}`), - } - } - const parametersWithAncestors = await res.json() - return { - props: { - parameters: parametersWithAncestors.map( - improveParameterWithAncestors, - ), - }, - } - })(), - ]) - const firstResultWithError = results.find( - ({ error }) => error !== undefined, - ) - if (firstResultWithError !== undefined) { - return firstResultWithError + const variable = variableSummaryByName[name] + if (variable === undefined) { + return { + status: 404, + error: new Error(`Variable "${name}" not found`), + } } + const parameters = [...iterVariableParameters(variable, date)] + return { props: { - ...Object.fromEntries( - [].concat(...results.map(({ props }) => Object.entries(props))), - ), + variable, + parameters, }, } } diff --git a/src/routes/variables/index.json.ts b/src/routes/variables/index.json.ts deleted file mode 100644 index 031721909778f10ef008a347f6df479b38501664..0000000000000000000000000000000000000000 --- a/src/routes/variables/index.json.ts +++ /dev/null @@ -1,91 +0,0 @@ -import type { Audit } from "@auditors/core" -import { - auditArray, - auditOptions, - auditSetNullish, - auditTrimString, - cleanAudit, -} from "@auditors/core" -import type { JsonValue } from "@openfisca/ast" -import type { RequestHandler } from "@sveltejs/kit" - -import { variableSummaryByName } from "$lib/server/variables" - -const variablesName = Object.keys(variableSummaryByName) - -function auditVariablesQuery( - audit: Audit, - query: URLSearchParams, -): [unknown, unknown] { - if (query == null) { - return [query, null] - } - if (!(query instanceof URLSearchParams)) { - return audit.unexpectedType(query, "URLSearchParams") - } - - const data: { [key: string]: unknown } = {} - // @ts-expect-error: urlSearchParams.entries() exists both on browsers & Node. - for (const [key, value] of query.entries()) { - let values = data[key] as string[] | undefined - if (values === undefined) { - values = data[key] = [] - } - values.push(value) - } - const errors: { [key: string]: unknown } = {} - const remainingKeys = new Set(Object.keys(data)) - - audit.attribute( - data, - "name", - true, - errors, - remainingKeys, - auditArray(auditTrimString, auditOptions(variablesName)), - ) - - return audit.reduceRemaining(data, errors, remainingKeys, auditSetNullish({})) -} - -export const get: RequestHandler = async ({ - path: requestPath, - query: requestQuery, -}) => { - const [query, queryError] = auditVariablesQuery(cleanAudit, requestQuery) - if (queryError !== null) { - console.error( - `Error in ${requestPath} query:\n${JSON.stringify( - query, - null, - 2, - )}\n\nError:\n${JSON.stringify(queryError, null, 2)}`, - ) - return { - status: 400, - body: { - error: { - code: 400, - details: queryError as JsonValue, - message: "Invalid query", - path: requestPath, - }, - query: query as JsonValue, - }, - } - } - const { name: names } = query as { - name?: string[] - } - - if (names === undefined) { - return { body: variableSummaryByName as unknown as JsonValue } - } - - const requestedVariableSummaryByName = Object.fromEntries( - Object.entries(variableSummaryByName).filter(([name]) => - names.includes(name), - ), - ) - return { body: requestedVariableSummaryByName as unknown as JsonValue } -} diff --git a/src/routes/variables/index.svelte b/src/routes/variables/index.svelte index 0dde097aa5bb536f89f2d41cbbc07ca8c4ec6ca3..9f2d46a57cd8181c7ac3f64a1fe6ebc6b487ad60 100644 --- a/src/routes/variables/index.svelte +++ b/src/routes/variables/index.svelte @@ -1,29 +1,6 @@ -<script context="module" lang="ts"> - import type { LoadInput, LoadOutput } from "@sveltejs/kit/types/page" - - export async function load({ fetch }: LoadInput): Promise<LoadOutput> { - const url = "/variables.json" - const res = await fetch(url) - if (!res.ok) { - return { - status: res.status, - error: new Error(`Could not load ${url}`), - } - } - return { - props: { - variableSummaryByName: await res.json(), - }, - } - } -</script> - <script lang="ts"> - import type { VariableByName } from "@openfisca/ast" - import { session } from "$app/stores" - - export let variableSummaryByName: VariableByName + import { variableSummaryByName } from "$lib/variables" </script> <svelte:head> diff --git a/svelte.config.js b/svelte.config.js index 070e68dff95e9d7ca4e2952a019aababfb5eba85..5165f6763b1301bff9a0e66957dad7c1f904a021 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -15,6 +15,11 @@ const config = { target: "#svelte", vite: { + build: { + // Increase size of chunks to 3 MB, to be able to import + // JSON files extracted from OpenFisca country package. + chunkSizeWarningLimit: 3072, + }, optimizeDeps: { // See https://svelte-modals.mattjennings.io/ // and https://github.com/sveltejs/vite-plugin-svelte/issues/124.