From 6606bb4f34ba7db0b5827fc4825cf26afb31d2a5 Mon Sep 17 00:00:00 2001 From: Mael Date: Thu, 11 Jul 2019 18:25:08 +0200 Subject: [PATCH] =?UTF-8?q?Unit=C3=A9s=20des=20bar=C3=A8mes=20et=20composa?= =?UTF-8?q?ntes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/components/Value.js | 1 + source/engine/mecanisms.js | 56 ------------------ source/engine/mecanisms/barème-continu.js | 63 ++++++++++++++++++++ source/engine/mecanisms/barème-linéaire.js | 68 ++++++++++++++++++++++ source/engine/mecanisms/barème.js | 4 +- source/engine/mecanisms/utils.js | 4 +- source/engine/parse.js | 13 ++--- source/engine/units.js | 1 + test/mecanisms.test.js | 1 + test/mécanismes/barème-continu.yaml | 12 ++-- test/mécanismes/barème-linéaire.yaml | 2 + test/mécanismes/barème.yaml | 9 ++- test/mécanismes/composantes.yaml | 7 ++- 13 files changed, 166 insertions(+), 75 deletions(-) create mode 100644 source/engine/mecanisms/barème-continu.js create mode 100644 source/engine/mecanisms/barème-linéaire.js diff --git a/source/components/Value.js b/source/components/Value.js index 431a48805..6942ef0e5 100644 --- a/source/components/Value.js +++ b/source/components/Value.js @@ -77,6 +77,7 @@ export default withLanguage( return nodeValue == undefined ? null : ( + unit: {unitText} {negative ? '-' : ''} {formattedValue} diff --git a/source/engine/mecanisms.js b/source/engine/mecanisms.js index c7c006fe4..aa4441fa9 100644 --- a/source/engine/mecanisms.js +++ b/source/engine/mecanisms.js @@ -620,62 +620,6 @@ export let mecanismProduct = (recurse, k, v) => { } } -/* on réécrit en une syntaxe plus bas niveau mais plus régulière les tranches : - `en-dessous de: 1` - devient - ``` - de: 0 - à: 1 - ``` - */ - -export let mecanismLinearScale = (recurse, k, v) => { - if (v.composantes) { - //mécanisme de composantes. Voir known-mecanisms.md/composantes - return decompose(recurse, k, v) - } - if (v.variations) { - return mecanismVariations(recurse, k, v, true) - } - let tranches = desugarScale(recurse)(v['tranches']), - objectShape = { - assiette: false, - multiplicateur: defaultNode(1) - } - - let effect = ({ assiette, multiplicateur, tranches }) => { - if (val(assiette) === null) return null - - let roundedAssiette = Math.round(val(assiette)) - - let matchedTranche = tranches.find( - ({ de: min, à: max }) => - roundedAssiette >= val(multiplicateur) * min && - roundedAssiette <= max * val(multiplicateur) - ) - - if (!matchedTranche) return 0 - if (matchedTranche.taux) - return matchedTranche.taux.nodeValue * val(assiette) - return matchedTranche.montant - } - - let explanation = { - ...parseObject(recurse, objectShape, v), - tranches - }, - evaluate = evaluateObject(objectShape, effect) - - return { - evaluate, - jsx: Barème('linéaire'), - explanation, - category: 'mecanism', - name: 'barème linéaire', - barème: 'en taux', - type: 'numeric' - } -} export let mecanismContinuousScale = (recurse, k, v) => { let objectShape = { diff --git a/source/engine/mecanisms/barème-continu.js b/source/engine/mecanisms/barème-continu.js new file mode 100644 index 000000000..4f9546d2a --- /dev/null +++ b/source/engine/mecanisms/barème-continu.js @@ -0,0 +1,63 @@ +import { defaultNode, evaluateObject } from 'Engine/evaluation' +import BarèmeContinu from 'Engine/mecanismViews/BarèmeContinu' +import { val, anyNull } from 'Engine/traverse-common-functions' +import { parseUnit } from 'Engine/units' +import { parseObject } from 'Engine/evaluation' +import { reduce, toPairs, sort, aperture, pipe, reduced, last } from 'ramda' + +export default (recurse, k, v) => { + let objectShape = { + assiette: false, + multiplicateur: defaultNode(1) + } + + let returnRate = v['retourne seulement le taux'] === 'oui' + let effect = ({ assiette, multiplicateur, points }) => { + if (anyNull([assiette, multiplicateur])) return null + //We'll build a linear function given the two constraints that must be respected + let result = pipe( + toPairs, + // we don't rely on the sorting of objects + sort(([k1], [k2]) => k1 - k2), + points => [...points, [Infinity, last(points)[1]]], + aperture(2), + reduce((_, [[lowerLimit, lowerRate], [upperLimit, upperRate]]) => { + let x1 = val(multiplicateur) * lowerLimit, + x2 = val(multiplicateur) * upperLimit, + y1 = val(assiette) * val(recurse(lowerRate)), + y2 = val(assiette) * val(recurse(upperRate)) + if (val(assiette) > x1 && val(assiette) <= x2) { + // Outside of these 2 limits, it's a linear function a * x + b + let a = (y2 - y1) / (x2 - x1), + b = y1 - x1 * a, + nodeValue = a * val(assiette) + b, + taux = nodeValue / val(assiette) + return reduced({ + nodeValue: returnRate ? taux : nodeValue, + additionalExplanation: { + seuil: val(assiette) / val(multiplicateur), + taux + } + }) + } + }, 0) + )(points) + + return result + } + let explanation = { + ...parseObject(recurse, objectShape, v), + points: v.points, + returnRate + }, + evaluate = evaluateObject(objectShape, effect) + return { + evaluate, + jsx: BarèmeContinu, + explanation, + category: 'mecanism', + name: 'barème continu', + type: 'numeric', + unit: returnRate ? parseUnit('%') : explanation.assiette.unit + } +} diff --git a/source/engine/mecanisms/barème-linéaire.js b/source/engine/mecanisms/barème-linéaire.js new file mode 100644 index 000000000..41e7437aa --- /dev/null +++ b/source/engine/mecanisms/barème-linéaire.js @@ -0,0 +1,68 @@ +import { defaultNode, evaluateObject } from 'Engine/evaluation' +import Barème from 'Engine/mecanismViews/Barème' +import { mecanismVariations } from 'Engine/mecanisms' +import { decompose } from 'Engine/mecanisms/utils' +import { val } from 'Engine/traverse-common-functions' +import { inferUnit, parseUnit } from 'Engine/units' +import { parseObject } from 'Engine/evaluation' +import { desugarScale } from './barème' +/* on réécrit en une syntaxe plus bas niveau mais plus régulière les tranches : + `en-dessous de: 1` + devient + ``` + de: 0 + à: 1 + ``` + */ + +export default (recurse, k, v) => { + if (v.composantes) { + //mécanisme de composantes. Voir known-mecanisms.md/composantes + return decompose(recurse, k, v) + } + if (v.variations) { + return mecanismVariations(recurse, k, v, true) + } + let tranches = desugarScale(recurse)(v['tranches']), + objectShape = { + assiette: false, + multiplicateur: defaultNode(1) + } + + let effect = ({ assiette, multiplicateur, tranches }) => { + if (val(assiette) === null) return null + + let roundedAssiette = Math.round(val(assiette)) + + let matchedTranche = tranches.find( + ({ de: min, à: max }) => + roundedAssiette >= val(multiplicateur) * min && + roundedAssiette <= max * val(multiplicateur) + ) + + if (!matchedTranche) return 0 + if (matchedTranche.taux) + return matchedTranche.taux.nodeValue * val(assiette) + return matchedTranche.montant + } + + let explanation = { + ...parseObject(recurse, objectShape, v), + tranches + }, + evaluate = evaluateObject(objectShape, effect) + + console.log('explanation', explanation) + + return { + evaluate, + jsx: Barème('linéaire'), + explanation, + category: 'mecanism', + name: 'barème linéaire', + barème: 'en taux', + type: 'numeric', + + unit: explanation.assiette.unit + } +} diff --git a/source/engine/mecanisms/barème.js b/source/engine/mecanisms/barème.js index a7b7c6754..270389a70 100644 --- a/source/engine/mecanisms/barème.js +++ b/source/engine/mecanisms/barème.js @@ -4,6 +4,7 @@ import { decompose } from 'Engine/mecanisms/utils' import Barème from 'Engine/mecanismViews/Barème' import { val } from 'Engine/traverse-common-functions' import { evolve, has, pluck, sum } from 'ramda' +import { inferUnit, parseUnit } from 'Engine/units' export let desugarScale = recurse => tranches => tranches @@ -85,6 +86,7 @@ export default (recurse, k, v) => { jsx: Barème('marginal'), category: 'mecanism', name: 'barème', - barème: 'marginal' + barème: 'marginal', + unit: inferUnit('*', [explanation.assiette.unit, parseUnit('%')]) } } diff --git a/source/engine/mecanisms/utils.js b/source/engine/mecanisms/utils.js index 88362872a..cf986ee25 100644 --- a/source/engine/mecanisms/utils.js +++ b/source/engine/mecanisms/utils.js @@ -1,6 +1,7 @@ import Composantes from 'Engine/mecanismViews/Composantes' import { add, dissoc, objOf } from 'ramda' import { evaluateArrayWithFilter } from 'Engine/evaluation' +import { inferUnit } from 'Engine/units' export let decompose = (recurse, k, v) => { let subProps = dissoc('composantes')(v), @@ -28,7 +29,8 @@ export let decompose = (recurse, k, v) => { evaluate: evaluateArrayWithFilter(filter, add, 0), category: 'mecanism', name: 'composantes', - type: 'numeric' + type: 'numeric', + unit: inferUnit('+', explanation.map(e => e.unit)) } } diff --git a/source/engine/parse.js b/source/engine/parse.js index daba3a3fa..7eebe2488 100644 --- a/source/engine/parse.js +++ b/source/engine/parse.js @@ -3,6 +3,8 @@ // TODO import them automatically // TODO convert the legacy functions to new files import barème from 'Engine/mecanisms/barème.js' +import barèmeContinu from 'Engine/mecanisms/barème-continu.js' +import barèmeLinéaire from 'Engine/mecanisms/barème-linéaire.js' import { Parser } from 'nearley' import { add, @@ -30,10 +32,8 @@ import Grammar from './grammar.ne' import { mecanismAllOf, mecanismComplement, - mecanismContinuousScale, mecanismError, mecanismInversion, - mecanismLinearScale, mecanismMax, mecanismMin, mecanismNumericalSwitch, @@ -46,10 +46,7 @@ import { mecanismOnePossibility } from './mecanisms' import { Node } from './mecanismViews/common' -import { - parseReference, - parseReferenceTransforms -} from './parseReference' +import { parseReferenceTransforms } from './parseReference' import { inferUnit } from 'Engine/units' export let parse = (rules, rule, parsedRules) => rawNode => { @@ -140,8 +137,8 @@ export let parseObject = (rules, rule, parsedRules) => rawNode => { somme: mecanismSum, multiplication: mecanismProduct, barème, - 'barème linéaire': mecanismLinearScale, - 'barème continu': mecanismContinuousScale, + 'barème linéaire': barèmeLinéaire, + 'barème continu': barèmeContinu, 'le maximum de': mecanismMax, 'le minimum de': mecanismMin, complément: mecanismComplement, diff --git a/source/engine/units.js b/source/engine/units.js index 5c7ce6e29..295a742f8 100644 --- a/source/engine/units.js +++ b/source/engine/units.js @@ -23,6 +23,7 @@ export let serialiseUnit = rawUnit => { ? `/${denominators.join('')}` : `${numerators.join('')} / ${denominators.join('')}` + // the unit '%' is only displayed when it is the only unit if (string.length > 1) return string.replace(/%/g, '') return string } diff --git a/test/mecanisms.test.js b/test/mecanisms.test.js index d4f7b366c..ddbbd63c9 100644 --- a/test/mecanisms.test.js +++ b/test/mecanisms.test.js @@ -54,6 +54,7 @@ describe('Mécanismes', () => } if (unit) { + expect(target.unit).not.to.be.equal(undefined) expect(serialiseUnit(target.unit)).to.eql(unit) } }) diff --git a/test/mécanismes/barème-continu.yaml b/test/mécanismes/barème-continu.yaml index 2931b671c..b204d2047 100644 --- a/test/mécanismes/barème-continu.yaml +++ b/test/mécanismes/barème-continu.yaml @@ -1,9 +1,9 @@ - nom: base - unité: _ + unité: £ formule: 300 - nom: assiette - unité: _ + unité: £ - test: Simple formule: @@ -14,7 +14,7 @@ 0: 0% 0.4: 3.16% 1.1: 6.35% - + unité attendue: £ exemples: - nom: Premier point situation: @@ -39,11 +39,12 @@ - nom: base deux - unité: _ + unité: µ formule: 300 - nom: assiette deux - unité: _ + unité: µ + - test: Retour de taux, pas d'assiette formule: barème continu: @@ -54,6 +55,7 @@ 0.75: 100% 1: 0% retourne seulement le taux: oui + unité attendue: '%' exemples: - nom: Premier point situation: diff --git a/test/mécanismes/barème-linéaire.yaml b/test/mécanismes/barème-linéaire.yaml index 97a55fb17..f2f2e9424 100644 --- a/test/mécanismes/barème-linéaire.yaml +++ b/test/mécanismes/barème-linéaire.yaml @@ -14,6 +14,7 @@ taux: 10% - au-dessus de: 2000 taux: 15% + unité attendue: € exemples: - nom: "petite assiette" @@ -51,6 +52,7 @@ - au-dessus de: 2000 montant: 400 + unité attendue: € exemples: - nom: "petite assiette" situation: diff --git a/test/mécanismes/barème.yaml b/test/mécanismes/barème.yaml index e9f589cee..5bc50b860 100644 --- a/test/mécanismes/barème.yaml +++ b/test/mécanismes/barème.yaml @@ -17,8 +17,7 @@ taux: 3% - au-dessus de: 3 taux: 1% - - + unité attendue: € exemples: - nom: 'petite assiette' situation: @@ -52,6 +51,7 @@ taux: 9% - au-dessus de: 2 taux: 29% + unité attendue: € exemples: - nom: @@ -62,12 +62,14 @@ - nom: ma condition -- nom: taux variable +- test: taux variable formule: variations: - si: ma condition alors: 29% - sinon: 56% + unité attendue: '%' + exemples: [] - nom: deuxième barème test: Barème à taux variable @@ -80,6 +82,7 @@ taux: taux variable - au-dessus de: 1 taux: 90% + unité attendue: € exemples: - nom: taux faible diff --git a/test/mécanismes/composantes.yaml b/test/mécanismes/composantes.yaml index db0f82701..270fd2f7d 100644 --- a/test/mécanismes/composantes.yaml +++ b/test/mécanismes/composantes.yaml @@ -1,12 +1,17 @@ - test: Composantes formule: multiplication: - assiette: 100 + assiette: richesse composantes: - taux: 8% - taux: 2% + unité attendue: crédits exemples: - nom: situation: valeur attendue: 10 + +- nom: richesse + unité: crédits + formule: 100