diff --git a/package-lock.json b/package-lock.json index 56f58d5a5d374679929c8cf8141e3db7bb02008d..7428f0973bae37ea56396cf01344b01eb2055983 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.2", + "@openfisca/ast": "^0.13.0", "@sveltejs/adapter-node": "next", "@sveltejs/kit": "next", "@tailwindcss/forms": "^0.3.2", @@ -1907,9 +1907,9 @@ } }, "node_modules/@openfisca/ast": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.12.2.tgz", - "integrity": "sha512-ZuCd8e7LkejDR8VgSp2Xwjmp2H2ypU9aF2vogt278XuxSMCs/mseMhPxSWXf+eMaaYnatDTfrDHBUcKKOYbbCA==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.13.0.tgz", + "integrity": "sha512-zrpmi75kcngmmZj0hSy4TvYEcVlrf5LxpQtBXD6Lv+PH0HFyK+bMG75sbSOUb7Txnix8WDSP45NY6QlVHyXojw==", "dev": true, "dependencies": { "@auditors/core": "^0.1.11", @@ -8235,9 +8235,9 @@ } }, "@openfisca/ast": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.12.2.tgz", - "integrity": "sha512-ZuCd8e7LkejDR8VgSp2Xwjmp2H2ypU9aF2vogt278XuxSMCs/mseMhPxSWXf+eMaaYnatDTfrDHBUcKKOYbbCA==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@openfisca/ast/-/ast-0.13.0.tgz", + "integrity": "sha512-zrpmi75kcngmmZj0hSy4TvYEcVlrf5LxpQtBXD6Lv+PH0HFyK+bMG75sbSOUb7Txnix8WDSP45NY6QlVHyXojw==", "dev": true, "requires": { "@auditors/core": "^0.1.11", diff --git a/package.json b/package.json index 08c8d1b8ccc3ecc688e96fe4543a75067d403542..e51786f25fe795e164b5c4cd46bff9c72836c468 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.2", + "@openfisca/ast": "^0.13.0", "@sveltejs/adapter-node": "next", "@sveltejs/kit": "next", "@tailwindcss/forms": "^0.3.2", diff --git a/src/lib/components/latchkeys/Arrow.svelte b/src/lib/components/latchkeys/Arrow.svelte index 4105a56c2069eb0129cbdc58fc78637f38969976..0e55561a2eb4e7f9390a54f22b3d27acb9a30745 100644 --- a/src/lib/components/latchkeys/Arrow.svelte +++ b/src/lib/components/latchkeys/Arrow.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import type { Waterfall } from "@openfisca/ast" import { getContext } from "svelte" import type { Writable } from "svelte/store" @@ -50,8 +51,7 @@ // 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 + const waterfall = getContext("waterfall") as Writable<Waterfall> $: aggregate = item.aggregate @@ -88,6 +88,8 @@ aggregate !== undefined && (aggregate === leaf || aggregate.values.some(([x0]) => x0 !== 0)) + $: rootDecompositionName = $waterfall.root + $: triangleHalfHeight = (width * chevronHeightWidthRatio) / 2 $: x = x0 < x1 ? x0 : x1 diff --git a/src/lib/components/latchkeys/AxisY.svelte b/src/lib/components/latchkeys/AxisY.svelte index 3b49443d710a3ff051a38dbbaf083e9de8859ef0..ad14dc849df76e004e13c65c549dfb1b14d93a13 100644 --- a/src/lib/components/latchkeys/AxisY.svelte +++ b/src/lib/components/latchkeys/AxisY.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import type { Waterfall } from "@openfisca/ast" import { createEventDispatcher, getContext } from "svelte" import type { Writable } from "svelte/store" @@ -19,8 +20,9 @@ ) as Writable<DecompositionByName[]> const dispatch = createEventDispatcher() const testCaseIndex = getContext("testCaseIndex") as Writable<number> - const waterfalls = $session.waterfalls - const rootDecompositionName = waterfalls[0].root + const waterfall = getContext("waterfall") as Writable<Waterfall> + + $: rootDecompositionName = $waterfall.root $: yHalfBandwidth = $yScale.bandwidth() / 2 diff --git a/src/lib/components/latchkeys/Latchkey.svelte b/src/lib/components/latchkeys/Latchkey.svelte index dd3d377c86b3fc08f37558574d4e2709bc62ff79..a24fdc0e53aaa23d174c506a2fbc91d4f1e4ad59 100644 --- a/src/lib/components/latchkeys/Latchkey.svelte +++ b/src/lib/components/latchkeys/Latchkey.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import type { Waterfall } from "@openfisca/ast" import { scaleBand } from "d3-scale" import { getContext } from "svelte" import type { Writable } from "svelte/store" @@ -21,12 +22,13 @@ ) as Writable<DecompositionByName[]> const showNulls = getContext("showNulls") as Writable<boolean> const testCaseIndex = getContext("testCaseIndex") as Writable<number> - const waterfalls = $session.waterfalls - const rootDecompositionName = waterfalls[0].root + const waterfall = getContext("waterfall") as Writable<Waterfall> // const showColoredRects = writable(false) // setContext("showColoredRects", showColoredRects) + $: rootDecompositionName = $waterfall.root + $: data = aggregateDecomposition( $decompositionByNameArray[$testCaseIndex], rootDecompositionName, diff --git a/src/lib/components/parameters/ValueAtInstantEdit.svelte b/src/lib/components/parameters/ValueAtInstantEdit.svelte index 1c869c62dd9082e85c285dee08cee7a33612bd01..23b4640baac69c9047067f46993a32b81401867a 100644 --- a/src/lib/components/parameters/ValueAtInstantEdit.svelte +++ b/src/lib/components/parameters/ValueAtInstantEdit.svelte @@ -43,7 +43,7 @@ } function changeUnit({ target }: Event) { - const { value } = target as HTMLInputElement + const { value } = target as HTMLSelectElement ;[valueAtInstant, errors] = auditEditedAttribute( laxAudit, valueAtInstant === "expected" ? { value: null } : valueAtInstant, diff --git a/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte b/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte index b010c5348f4c1984d8d45a54bc6af516b1dcc172..1cdbf6267d2f2a79c2c038e27722ae79b685d52b 100644 --- a/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte +++ b/src/lib/components/scholar_waterfalls/ScholarWaterfall.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import type { Waterfall } from "@openfisca/ast" import { createEventDispatcher, getContext } from "svelte" import type { Writable } from "svelte/store" @@ -38,14 +39,16 @@ }) const showNulls = getContext("showNulls") as Writable<boolean> const testCaseIndex = getContext("testCaseIndex") as Writable<number> + const waterfall = getContext("waterfall") as Writable<Waterfall> let waterfallWidth = 100 - const waterfalls = $session.waterfalls - const rootDecompositionName = waterfalls[0].root + + $: rootDecompositionName = $waterfall.root $: decompositionEvaluationAndDepthTriples = [ ...walkVisibleEvaluations( decompositionByName, evaluationByName, + $waterfall.name, rootDecompositionName, $showNulls, ), @@ -126,6 +129,7 @@ export function* walkVisibleEvaluations( decompositionByName: DecompositionByName, evaluationByName: EvaluationByName, + waterfallName: string, name: string, showNulls: boolean, depth: number = 0, @@ -138,8 +142,25 @@ if (evaluation === undefined) { return } - const showNode = - (showNulls && !decomposition.hidden) || !evaluation.deltaIs0Array + + let hidden = decomposition.hidden + // for (const options of decomposition.options ?? []) { + // if ( + // options.then?.hidden !== undefined && + // (options.waterfall === undefined || + // options.waterfall.includes(waterfallName)) + // ) { + // hidden = options.then.hidden ?? undefined + // } else if ( + // options.else?.hidden !== undefined && + // options.waterfall !== undefined && + // !options.waterfall.includes(waterfallName) + // ) { + // hidden = options.else.hidden ?? undefined + // } + // } + + const showNode = (showNulls && !hidden) || !evaluation.deltaIs0Array if (showNode) { let childrenDepth = depth if (!decomposition.trunk) { @@ -151,6 +172,7 @@ yield* walkVisibleEvaluations( decompositionByName, evaluationByName, + waterfallName, childName, showNulls, childrenDepth, diff --git a/src/lib/components/test_cases/TestCaseView.svelte b/src/lib/components/test_cases/TestCaseView.svelte index cfce09960ed1e261d469029325f26c65b08b84f6..a7928ab5e05f2514786e6cfa4b34140f555eec35 100644 --- a/src/lib/components/test_cases/TestCaseView.svelte +++ b/src/lib/components/test_cases/TestCaseView.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import type { EntityByKey, Variable } from "@openfisca/ast" + import type { EntityByKey, Variable, Waterfall } from "@openfisca/ast" import { createEventDispatcher, getContext, tick } from "svelte" import type { Writable } from "svelte/store" @@ -79,13 +79,7 @@ ) 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 + const waterfall = getContext("waterfall") as Writable<Waterfall> $: familySituation = situation[familyEntity.key_plural] @@ -98,6 +92,15 @@ $: evaluationByName = evaluationByNameArrayByCalculationName[calculationName][situationIndex] + $: rootDecompositionName = $waterfall.root + + $: firstDecompositionName = walkDecompositionsCoreName( + $session.decompositionCoreByName, + $waterfall.name, + rootDecompositionName, + true, + ).next().value[0] as string + $: personSituation = situation[personEntity.key_plural] $: adultsCount = Object.keys(personSituation).length - childrenCount @@ -472,15 +475,15 @@ ? undefined : evaluationByNameArrayByCalculationName.amendment[situationIndex][ rootDecompositionName - ].deltaAtVectorIndex ?? 0} + ]?.deltaAtVectorIndex ?? 0} billValue={$billName === undefined ? undefined : evaluationByNameArrayByCalculationName.bill[situationIndex][ rootDecompositionName - ].deltaAtVectorIndex ?? 0} + ]?.deltaAtVectorIndex ?? 0} lawValue={evaluationByNameArrayByCalculationName.law[situationIndex][ rootDecompositionName - ].deltaAtVectorIndex ?? 0} + ]?.deltaAtVectorIndex ?? 0} /> </p> </div> @@ -501,15 +504,15 @@ ? undefined : evaluationByNameArrayByCalculationName.amendment[situationIndex][ firstDecompositionName - ].deltaAtVectorIndex ?? 0} + ]?.deltaAtVectorIndex ?? 0} billValue={$billName === undefined ? undefined : evaluationByNameArrayByCalculationName.bill[situationIndex][ firstDecompositionName - ].deltaAtVectorIndex ?? 0} + ]?.deltaAtVectorIndex ?? 0} lawValue={evaluationByNameArrayByCalculationName.law[situationIndex][ firstDecompositionName - ].deltaAtVectorIndex ?? 0} + ]?.deltaAtVectorIndex ?? 0} /> </p> </div> @@ -532,24 +535,24 @@ ? undefined : (evaluationByNameArrayByCalculationName.amendment[situationIndex][ rootDecompositionName - ].deltaAtVectorIndex ?? 0) - + ]?.deltaAtVectorIndex ?? 0) - (evaluationByNameArrayByCalculationName.amendment[situationIndex][ firstDecompositionName - ].deltaAtVectorIndex ?? 0)} + ]?.deltaAtVectorIndex ?? 0)} billValue={$billName === undefined ? undefined : (evaluationByNameArrayByCalculationName.bill[situationIndex][ rootDecompositionName - ].deltaAtVectorIndex ?? 0) - + ]?.deltaAtVectorIndex ?? 0) - (evaluationByNameArrayByCalculationName.bill[situationIndex][ firstDecompositionName - ].deltaAtVectorIndex ?? 0)} + ]?.deltaAtVectorIndex ?? 0)} lawValue={(evaluationByNameArrayByCalculationName.law[situationIndex][ rootDecompositionName - ].deltaAtVectorIndex ?? 0) - + ]?.deltaAtVectorIndex ?? 0) - (evaluationByNameArrayByCalculationName.law[situationIndex][ firstDecompositionName - ].deltaAtVectorIndex ?? 0)} + ]?.deltaAtVectorIndex ?? 0)} /> </p> </div> diff --git a/src/lib/components/variables/VariableReferredInputsPane.svelte b/src/lib/components/variables/VariableReferredInputsPane.svelte index 48c4104268b0b06ae95f76012b46ff3c37da8191..2f3104cee4c7e9beb15fa42d46cd429e6e119120 100644 --- a/src/lib/components/variables/VariableReferredInputsPane.svelte +++ b/src/lib/components/variables/VariableReferredInputsPane.svelte @@ -1,5 +1,7 @@ <script lang="ts"> - import type { Variable } from "@openfisca/ast" + import type { Variable, Waterfall } from "@openfisca/ast" + import { getContext } from "svelte" + import type { Writable } from "svelte/store" import { browser } from "$app/env" import VariableReferredInputs from "./VariableReferredInputs.svelte" @@ -12,6 +14,7 @@ let inputs: Variable[] | undefined = undefined let variable: Variable | undefined = undefined + const waterfall = getContext("waterfall") as Writable<Waterfall> $: browser && retrieveVariable(name) @@ -36,7 +39,9 @@ name: string, date: string, ): Promise<void> { - const url = `/variables/${name}/inputs/${date}.json?filter_decomposition=true` + 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() diff --git a/src/lib/components/waterfalls/Waterfall.svelte b/src/lib/components/waterfalls/Waterfall.svelte index a144d6ead1e556d2f54366567752c266681017dd..92e6d7395928d3277f61987b76cfbfc70f13ba7d 100644 --- a/src/lib/components/waterfalls/Waterfall.svelte +++ b/src/lib/components/waterfalls/Waterfall.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import type { Waterfall } from "@openfisca/ast" import { scaleBand } from "d3-scale" import { getContext } from "svelte" import type { Writable } from "svelte/store" @@ -18,8 +19,9 @@ ) as Writable<DecompositionByName[]> const showNulls = getContext("showNulls") as Writable<boolean> const testCaseIndex = getContext("testCaseIndex") as Writable<number> - const waterfalls = $session.waterfalls - const rootDecompositionName = waterfalls[0].root + const waterfall = getContext("waterfall") as Writable<Waterfall> + + $: rootDecompositionName = $waterfall.root $: data = [ ...walkDecompositions( diff --git a/src/lib/decompositions.ts b/src/lib/decompositions.ts index 213e52c769d3d706ff33338a69449352bf4b8948..2480f745f49e777e26bc8021e323c1283a2d962d 100644 --- a/src/lib/decompositions.ts +++ b/src/lib/decompositions.ts @@ -5,7 +5,7 @@ export type { import type { Decomposition as DecompositionCore, DecompositionByName as DecompositionCoreByName, - DecompositionReference, + DecompositionOptions, } from "@openfisca/ast" export type CalculationName = "amendment" | "bill" | "law" @@ -21,6 +21,7 @@ export interface Decomposition { negate?: boolean obsolete?: boolean open?: boolean + options?: DecompositionOptions[] parentName?: string previousChildStack?: string[] trunk: boolean @@ -54,6 +55,7 @@ export const calculationNames: CalculationName[] = ["law", "bill", "amendment"] export function decompositionByNameFromCore( decompositionCoreByName: DecompositionCoreByName, + waterfallName: string, name: string, parentName: string | undefined = undefined, index = 0, @@ -72,8 +74,24 @@ export function decompositionByNameFromCore( console.error(`Error: Variable "${name}" is missing from decomposition.`) return undefined } + + let hidden = decompositionCore.hidden + for (const options of decompositionCore.options ?? []) { + if (options.waterfall === undefined) { + continue + } + if ( + options.waterfall.includes(waterfallName) && + options.then?.hidden !== undefined + ) { + hidden = options.then.hidden ?? undefined + } else if (options.else?.hidden !== undefined) { + hidden = options.else.hidden ?? undefined + } + } + const decomposition: Decomposition = { - hidden: decompositionCore.hidden, + hidden, index, involve: decompositionCore.involve, label: decompositionCore.label, @@ -81,6 +99,7 @@ export function decompositionByNameFromCore( name, negate, obsolete: decompositionCore.obsolete, + options: decompositionCore.options, trunk, ux_name: decompositionCore.ux_name, virtual: decompositionCore.virtual, @@ -89,10 +108,23 @@ export function decompositionByNameFromCore( if (parentName !== undefined) { decomposition.parentName = parentName } - if (decompositionCore.children !== undefined) { - decomposition.childrenName = decompositionCore.children.map( - ({ name }) => name, - ) + + let children = decompositionCore.children + for (const options of decompositionCore.options ?? []) { + if (options.waterfall === undefined) { + continue + } + if ( + options.waterfall.includes(waterfallName) && + options.then?.children !== undefined + ) { + children = options.then.children ?? undefined + } else if (options.else?.children !== undefined) { + children = options.else.children ?? undefined + } + } + if (children !== undefined) { + decomposition.childrenName = children.map(({ name }) => name) let ancestor: Decomposition | undefined = undefined let depth = 0 for (const ancestorName of iterDecompositionAncestorsName( @@ -115,12 +147,10 @@ export function decompositionByNameFromCore( if (depth < 1) { decomposition.open = true } - for (const [ - childIndex, - childReference, - ] of decompositionCore.children.entries()) { + for (const [childIndex, childReference] of children.entries()) { decompositionByNameFromCore( decompositionCoreByName, + waterfallName, childReference.name, name, childIndex, @@ -182,9 +212,12 @@ export function updateEvaluations( name: string, vectorIndex: number, vectorLength: number, - newEvaluationByName: EvaluationByName = {}, + newEvaluationByName: EvaluationByName | undefined = undefined, valuesPrevious = undefined, ): EvaluationByName { + if (newEvaluationByName === undefined) { + newEvaluationByName = { ...evaluationByName } + } const decomposition = decompositionByName[name] if (decomposition === undefined) { return newEvaluationByName @@ -206,6 +239,11 @@ export function updateEvaluations( childValuesPrevious, ) const childEvaluation = newEvaluationByName[childName] + if (childEvaluation === undefined) { + console.error( + `Undefined evaluation for child "${childName}" of "${name}"`, + ) + } childValuesPrevious = childEvaluation.values.map( (itemValue) => itemValue[1], ) @@ -322,6 +360,7 @@ export function* walkDecompositions( export function* walkDecompositionsCore( decompositionCoreByName: DecompositionCoreByName, + waterfallName: string, name: string, depthFirst: boolean, ): Generator<[string, DecompositionCore], void, unknown> { @@ -332,15 +371,32 @@ export function* walkDecompositionsCore( if (!depthFirst) { yield [name, decompositionCore] } - if (decompositionCore.children !== undefined) { - for (const childReference of decompositionCore.children) { + + let children = decompositionCore.children + for (const options of decompositionCore.options ?? []) { + if (options.waterfall === undefined) { + continue + } + if ( + options.waterfall.includes(waterfallName) && + options.then?.children !== undefined + ) { + children = options.then.children ?? undefined + } else if (options.else?.children !== undefined) { + children = options.else.children ?? undefined + } + } + if (children !== undefined) { + for (const childReference of children) { yield* walkDecompositionsCore( decompositionCoreByName, + waterfallName, childReference.name, depthFirst, ) } } + if (depthFirst) { yield [name, decompositionCore] } @@ -348,26 +404,16 @@ export function* walkDecompositionsCore( export function* walkDecompositionsCoreName( decompositionCoreByName: DecompositionCoreByName, + waterfallName: string, name: string, depthFirst: boolean, ): Generator<string, void, unknown> { - const decompositionCore = decompositionCoreByName[name] - if (decompositionCore === undefined) { - return - } - if (!depthFirst) { - yield name - } - if (decompositionCore.children !== undefined) { - for (const childReference of decompositionCore.children) { - yield* walkDecompositionsCoreName( - decompositionCoreByName, - childReference.name, - depthFirst, - ) - } - } - if (depthFirst) { - yield name + for (const [decompositionName, _decompositionCore] of walkDecompositionsCore( + decompositionCoreByName, + waterfallName, + name, + depthFirst, + )) { + yield decompositionName } } diff --git a/src/lib/server/decompositions.ts b/src/lib/server/decompositions.ts index 414f03ac848ee61f7cc78fdd21e7eb355939f063..a74f9a9220a1b9c62451d370febb9369fb3850ba 100644 --- a/src/lib/server/decompositions.ts +++ b/src/lib/server/decompositions.ts @@ -45,7 +45,11 @@ export const waterfalls = validWaterfalls for (const waterfall of waterfalls) { assert.notStrictEqual( - decompositionByNameFromCore(decompositionCoreByName, waterfall.root), + decompositionByNameFromCore( + decompositionCoreByName, + waterfall.name, + waterfall.root, + ), undefined, ) } diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index bbcf6be1398ba77d6136bf51b4217ac00c53b6b1..a8745b43c9db2a513c5d6f73dd5e4387a81ec605 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import "../app.css" - import type { EntityByKey } from "@openfisca/ast" + import type { EntityByKey, Waterfall } from "@openfisca/ast" import { setContext } from "svelte" import type { Writable } from "svelte/store" import { writable } from "svelte/store" @@ -10,7 +10,11 @@ import { browser } from "$app/env" import { page, session } from "$app/stores" import NavBar from "$lib/components/NavBar.svelte" - import type { CalculationName, EvaluationByName } from "$lib/decompositions" + import type { + CalculationName, + DecompositionCoreByName, + EvaluationByName, + } from "$lib/decompositions" import { calculationNames, decompositionByNameFromCore, @@ -28,6 +32,11 @@ const calculationName: Writable<CalculationName> = writable("law") setContext("calculationName", calculationName) + const calculationTokenByName: Writable< + { [name in CalculationName]?: string } + > = writable({}) + setContext("calculationTokenByName", calculationTokenByName) + const entityByKey = $session.entityByKey as EntityByKey const vectorIndexes: Writable<number[]> = writable( @@ -39,11 +48,15 @@ setContext("vectorLength", vectorLength) const waterfalls = $session.waterfalls - const rootDecompositionName = waterfalls[0].root + let waterfall = writable(waterfalls[0]) + setContext("waterfall", waterfall) + let waterfallName = $waterfall.name // Used to track change of waterfall. + const decompositionByName = writable( decompositionByNameFromCore( $session.decompositionCoreByName, - rootDecompositionName, + $waterfall.name, + $waterfall.root, ), ) setContext("decompositionByName", decompositionByName) @@ -60,7 +73,7 @@ updateEvaluations( $decompositionByName, {}, - rootDecompositionName, + $waterfall.root, $vectorIndexes[situationIndex], $vectorLength, ), @@ -82,19 +95,10 @@ // Note: Duplicates are removed from nonVirtualDecompositionsName, because a variable name // may appear more than once in decomposition. - const nonVirtualDecompositionsName = [ - ...new Set( - [ - ...walkDecompositionsCore( - $session.decompositionCoreByName, - rootDecompositionName, - true, - ), - ] - .filter(([name, decomposition]) => !decomposition.virtual) - .map(([name, _decomposition]) => name), - ), - ] + const nonVirtualDecompositionsName = computeNonVirtualDecompositionsName( + $session.decompositionCoreByName, + waterfalls, + ) setContext("nonVirtualDecompositionsName", nonVirtualDecompositionsName) const reform = writable({}) @@ -109,11 +113,6 @@ const testCases = writable($session.testCases as Situation[]) setContext("testCases", testCases) - const calculationTokenByName: Writable< - { [name in CalculationName]?: string } - > = writable({}) - setContext("calculationTokenByName", calculationTokenByName) - let variableValuesByName: { [name: string]: VariableValues } = {} const webSocketByName: Writable<WebSocketByName | undefined> = @@ -126,6 +125,34 @@ const year = writable(2021) setContext("year", year) + $: rootDecompositionName = $waterfall.root + + $: if ($waterfall.name !== waterfallName) { + // Another waterfall has been selected. + waterfallName = $waterfall.name + $decompositionByName = decompositionByNameFromCore( + $session.decompositionCoreByName, + $waterfall.name, + $waterfall.root, + ) + $evaluationByNameArrayByCalculationName = Object.fromEntries( + Object.entries($evaluationByNameArrayByCalculationName).map( + ([calculationName, evaluationByNameArray]) => [ + calculationName, + evaluationByNameArray.map((evaluationByName, situationIndex) => + updateEvaluations( + $decompositionByName, + evaluationByName, + $waterfall.root, + $vectorIndexes[situationIndex], + $vectorLength, + ), + ), + ], + ), + ) + } + if (browser && matomoConfig !== undefined) { function matomo(id, url) { // @ts-expect-error @@ -186,6 +213,29 @@ _paq.push(["trackPageView"]) } + function computeNonVirtualDecompositionsName( + decompositionCoreByName: DecompositionCoreByName, + waterfalls: Waterfall[], + ): string[] { + const nonVirtualDecompositionsName: string[] = [] + for (const { name: waterfallName, root } of waterfalls) { + for (const [name, decomposition] of walkDecompositionsCore( + decompositionCoreByName, + waterfallName, + root, + true, + )) { + if ( + !decomposition.virtual && + !nonVirtualDecompositionsName.includes(name) + ) { + nonVirtualDecompositionsName.push(name) + } + } + } + return nonVirtualDecompositionsName + } + function extractInputInstantsFromTestCases(testCases: Situation[]): { [name: string]: Set<string> } { @@ -323,7 +373,9 @@ ...evaluationByName, [result.name]: { ...evaluationByName[result.name], - delta: $decompositionByName[result.name].negate + delta: $session.decompositionCoreByName[ + result.name + ].negate ? sum.map((sumItem) => -sumItem) : sum, fromOpenFisca: true, diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 664b8c67393aa32b94203de2586e2f20fe0a0c1b..8fcaa2d0915b95b964bdfb68737f372732d9ae1b 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import type { EntityByKey, GroupEntity } from "@openfisca/ast" + import type { EntityByKey, GroupEntity, Waterfall } from "@openfisca/ast" import { getRolePersonsIdKey } from "@openfisca/ast" import { getContext, setContext } from "svelte" import type { Writable } from "svelte/store" @@ -97,8 +97,8 @@ ) as Writable<{ [name in CalculationName]?: string }> const vectorIndexes = getContext("vectorIndexes") as Writable<number[]> const vectorLength = getContext("vectorLength") as Writable<number> + const waterfall = getContext("waterfall") as Writable<Waterfall> const waterfalls = $session.waterfalls - const rootDecompositionName = waterfalls[0].root const webSocketByName = getContext("webSocketByName") as Writable< WebSocketByName | undefined > @@ -109,9 +109,12 @@ $: query = ensureValidQuery($page.query) + $: rootDecompositionName = $waterfall.root + $: $action = query.action $: if ($simulationRequested) { + console.log("Simulation requested") $simulationRequested = false submit() } @@ -202,6 +205,11 @@ } } + function changeWaterfall({ target }: Event) { + const { value } = target as HTMLSelectElement + $waterfall = waterfalls.find(({ name }) => name === value) + } + function closeEditionPane() { if (editionMode.mode === "tutorial") { editionMode = null @@ -333,6 +341,7 @@ } function submit() { + console.log("submit called") // Aggregate every situations into a single one without calculated variables. const aggregatedSituation: SituationWithAxes = {} const entities = Object.values($session.entityByKey as EntityByKey) @@ -679,8 +688,7 @@ <div class="w-full {editionMode !== null ? 'hidden md:block' : ''}"> <div class="mx-1 my-2 px-4"> <div class="flex justify-between"> - <!-- - <label + <!-- <label ><input bind:checked={$adaptAmountsScale} type="checkbox" /> <span class="hidden text-xs lg:inline text-gray-600"> Échelle identique pour tous les cas types @@ -688,8 +696,7 @@ <span class="lg:hidden text-xs inline text-gray-600 leading-none "> Échelle unique </span></label - > - --> + > --> <select class=" rounded border-1 text-xs " required @@ -700,6 +707,18 @@ {/each} </select> + <select + class=" rounded border-1 text-xs " + on:blur={changeWaterfall} + on:change={changeWaterfall} + required + value={$waterfall.name} + > + {#each waterfalls as { label, name }} + <option value={name}>{label}</option> + {/each} + </select> + <div class="flex"> <button class="bg-gray-200 text-gray-900 shadow-md hover:bg-gray-400 px-5 mr-2 rounded p-2 uppercase text-sm" diff --git a/src/routes/variables/[variable]/inputs/[date].json.ts b/src/routes/variables/[variable]/inputs/[date].json.ts index 1a9330fce140ed4938ca9ad94c6d20e0d2fac940..b4a31cf70d1a88b901b241b855ac4333eca64d1b 100644 --- a/src/routes/variables/[variable]/inputs/[date].json.ts +++ b/src/routes/variables/[variable]/inputs/[date].json.ts @@ -2,6 +2,8 @@ import { Audit, auditArray, auditFunction, + auditOptions, + auditRequire, auditSetNullish, auditStringToBoolean, auditTrimString, @@ -19,7 +21,6 @@ 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, @@ -55,6 +56,29 @@ function auditVariableInputsQuery( auditFunction((value) => value[value.length - 1]), auditSetNullish(false), ) + audit.attribute( + data, + "waterfall", + true, + errors, + remainingKeys, + auditOptions(waterfalls.map(({ name }) => name)), + ) + + 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({})) } @@ -86,8 +110,12 @@ export const get: RequestHandler = async ({ }, } } - const { filter_decomposition: filterDecomposition } = query as { + const { + filter_decomposition: filterDecomposition, + waterfall: waterfallName, + } = query as { filter_decomposition: boolean + waterfall?: string } const { date, variable: name } = params @@ -103,10 +131,12 @@ export const get: RequestHandler = async ({ let ignoreVariablesName: Set<string> if (filterDecomposition) { + const waterfall = waterfalls.find(({ name }) => name === waterfallName) ignoreVariablesName = new Set( walkDecompositionsCoreName( decompositionCoreByName, - rootDecompositionName, + waterfall.name, + waterfall.root, false, ), )