From ab0b64da129b91457cb7d1e7f67a02671d46ae8d Mon Sep 17 00:00:00 2001 From: Mael Date: Mon, 22 Jul 2019 12:36:50 +0200 Subject: [PATCH] =?UTF-8?q?:sparkles:=20D=C3=A9placement=20des=20Variation?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/engine/mecanisms.js | 155 +-------------------- source/engine/mecanisms/barème-linéaire.js | 6 +- source/engine/mecanisms/barème.js | 4 +- source/engine/mecanisms/utils.js | 14 -- source/engine/mecanisms/variations.js | 109 +++++++++++++++ source/engine/parse.js | 11 +- 6 files changed, 122 insertions(+), 177 deletions(-) create mode 100644 source/engine/mecanisms/variations.js diff --git a/source/engine/mecanisms.js b/source/engine/mecanisms.js index dd27b1afa..5c7000f1c 100644 --- a/source/engine/mecanisms.js +++ b/source/engine/mecanisms.js @@ -1,9 +1,7 @@ -import { desugarScale } from 'Engine/mecanisms/barème' -import { decompose, devariateExplanation } from 'Engine/mecanisms/utils' +import { decompose } from 'Engine/mecanisms/utils' import { add, any, - aperture, curry, equals, evolve, @@ -12,9 +10,7 @@ import { head, is, isEmpty, - isNil, keys, - last, map, max, mergeWith, @@ -23,11 +19,7 @@ import { pipe, pluck, prop, - propEq, reduce, - reduced, - reject, - sort, subtract, toPairs } from 'ramda' @@ -48,100 +40,15 @@ import { rewriteNode } from './evaluation' import Allègement from './mecanismViews/Allègement' -import Barème from './mecanismViews/Barème' -import BarèmeContinu from './mecanismViews/BarèmeContinu' import { Node, SimpleRuleLink } from './mecanismViews/common' import InversionNumérique from './mecanismViews/InversionNumérique' import Product from './mecanismViews/Product' import Somme from './mecanismViews/Somme' -import Variations from './mecanismViews/Variations' import { disambiguateRuleReference, findRuleByDottedName } from './rules' import { anyNull, val } from './traverse-common-functions' import uniroot from './uniroot' import { inferUnit } from 'Engine/units' - -/* @devariate = true => This function will produce variations of a same mecanism (e.g. product) that share some common properties */ -export let mecanismVariations = (recurse, k, v, devariate) => { - let explanation = devariate - ? devariateExplanation(recurse, k, v) - : v.map(({ si, alors, sinon }) => - sinon !== undefined - ? { consequence: recurse(sinon), condition: undefined } - : { consequence: recurse(alors), condition: recurse(si) } - ) - - let evaluate = (cache, situationGate, parsedRules, node) => { - let evaluateVariationProp = prop => - prop === undefined - ? undefined - : evaluateNode(cache, situationGate, parsedRules, prop), - // mark the satisfied variation if any in the explanation - [, resolvedExplanation] = reduce( - ([resolved, result], variation) => { - if (resolved) return [true, [...result, variation]] - - // evaluate the condition - let evaluatedCondition = evaluateVariationProp(variation.condition) - - if (evaluatedCondition == undefined) { - // We've reached the eventual defaut case - let evaluatedVariation = { - consequence: evaluateVariationProp(variation.consequence), - satisfied: true - } - return [true, [...result, evaluatedVariation]] - } - - if (evaluatedCondition.nodeValue === null) - // one case has missing variables => we can't go further - return [true, [...result, { condition: evaluatedCondition }]] - - if (evaluatedCondition.nodeValue === true) { - let evaluatedVariation = { - condition: evaluatedCondition, - consequence: evaluateVariationProp(variation.consequence), - satisfied: true - } - return [true, [...result, evaluatedVariation]] - } - return [false, [...result, variation]] - }, - [false, []] - )(node.explanation), - satisfiedVariation = resolvedExplanation.find(v => v.satisfied), - nodeValue = satisfiedVariation - ? satisfiedVariation.consequence.nodeValue - : null - - let leftMissing = mergeAllMissing( - reject(isNil, pluck('condition', resolvedExplanation)) - ), - candidateVariations = filter( - node => !node.condition || node.condition.nodeValue !== false, - resolvedExplanation - ), - rightMissing = mergeAllMissing( - reject(isNil, pluck('consequence', candidateVariations)) - ), - missingVariables = satisfiedVariation - ? collectNodeMissing(satisfiedVariation.consequence) - : mergeMissing(bonus(leftMissing), rightMissing) - - return rewriteNode(node, nodeValue, resolvedExplanation, missingVariables) - } - - // TODO - find an appropriate representation - - return { - explanation, - evaluate, - jsx: Variations, - category: 'mecanism', - name: 'variations', - type: 'numeric', - unit: inferUnit('+', explanation.map(r => r.consequence.unit)) - } -} +import variations from 'Engine/mecanisms/variations' export let mecanismOneOf = (recurse, k, v) => { if (!is(Array, v)) throw new Error('should be array') @@ -576,7 +483,7 @@ export let mecanismProduct = (recurse, k, v) => { return decompose(recurse, k, v) } if (v.variations) { - return mecanismVariations(recurse, k, v, true) + return variations(recurse, k, v, true) } let objectShape = { @@ -621,62 +528,6 @@ export let mecanismProduct = (recurse, k, v) => { } } -export let mecanismContinuousScale = (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' - } -} - export let mecanismMax = (recurse, k, v) => { let explanation = v.map(recurse) diff --git a/source/engine/mecanisms/barème-linéaire.js b/source/engine/mecanisms/barème-linéaire.js index 41e7437aa..d4543c600 100644 --- a/source/engine/mecanisms/barème-linéaire.js +++ b/source/engine/mecanisms/barème-linéaire.js @@ -1,6 +1,6 @@ import { defaultNode, evaluateObject } from 'Engine/evaluation' import Barème from 'Engine/mecanismViews/Barème' -import { mecanismVariations } from 'Engine/mecanisms' +import variations from 'Engine/mecanisms/variations' import { decompose } from 'Engine/mecanisms/utils' import { val } from 'Engine/traverse-common-functions' import { inferUnit, parseUnit } from 'Engine/units' @@ -21,7 +21,7 @@ export default (recurse, k, v) => { return decompose(recurse, k, v) } if (v.variations) { - return mecanismVariations(recurse, k, v, true) + return variations(recurse, k, v, true) } let tranches = desugarScale(recurse)(v['tranches']), objectShape = { @@ -52,8 +52,6 @@ export default (recurse, k, v) => { }, evaluate = evaluateObject(objectShape, effect) - console.log('explanation', explanation) - return { evaluate, jsx: Barème('linéaire'), diff --git a/source/engine/mecanisms/barème.js b/source/engine/mecanisms/barème.js index 270389a70..4176232e0 100644 --- a/source/engine/mecanisms/barème.js +++ b/source/engine/mecanisms/barème.js @@ -1,5 +1,5 @@ import { defaultNode, E, rewriteNode } from 'Engine/evaluation' -import { mecanismVariations } from 'Engine/mecanisms' +import variations from 'Engine/mecanisms/variations' import { decompose } from 'Engine/mecanisms/utils' import Barème from 'Engine/mecanismViews/Barème' import { val } from 'Engine/traverse-common-functions' @@ -39,7 +39,7 @@ export default (recurse, k, v) => { return decompose(recurse, k, v) } if (v.variations) { - return mecanismVariations(recurse, k, v, true) + return variations(recurse, k, v, true) } let { assiette, multiplicateur } = v, diff --git a/source/engine/mecanisms/utils.js b/source/engine/mecanisms/utils.js index cf986ee25..60e499994 100644 --- a/source/engine/mecanisms/utils.js +++ b/source/engine/mecanisms/utils.js @@ -34,17 +34,3 @@ export let decompose = (recurse, k, v) => { } } -export let devariateExplanation = (recurse, mecanismKey, v) => { - let fixedProps = dissoc('variations')(v), - explanation = v.variations.map(({ si, alors, sinon }) => ({ - consequence: recurse({ - [mecanismKey]: { - ...fixedProps, - ...(sinon || alors) - } - }), - condition: sinon ? undefined : recurse(si) - })) - - return explanation -} diff --git a/source/engine/mecanisms/variations.js b/source/engine/mecanisms/variations.js new file mode 100644 index 000000000..b8887c23c --- /dev/null +++ b/source/engine/mecanisms/variations.js @@ -0,0 +1,109 @@ +import { inferUnit } from 'Engine/units' +import { + bonus, + collectNodeMissing, + evaluateNode, + mergeAllMissing, + mergeMissing, + rewriteNode +} from 'Engine/evaluation' +import { reject, pluck, isNil, filter, dissoc, reduce } from 'ramda' +import Variations from 'Engine/mecanismViews/Variations' + +/* @devariate = true => This function will produce variations of a same mecanism (e.g. product) that share some common properties */ +export default (recurse, k, v, devariate) => { + let explanation = devariate + ? devariateExplanation(recurse, k, v) + : v.map(({ si, alors, sinon }) => + sinon !== undefined + ? { consequence: recurse(sinon), condition: undefined } + : { consequence: recurse(alors), condition: recurse(si) } + ) + + let evaluate = (cache, situationGate, parsedRules, node) => { + let evaluateVariationProp = prop => + prop === undefined + ? undefined + : evaluateNode(cache, situationGate, parsedRules, prop), + // mark the satisfied variation if any in the explanation + [, resolvedExplanation] = reduce( + ([resolved, result], variation) => { + if (resolved) return [true, [...result, variation]] + + // evaluate the condition + let evaluatedCondition = evaluateVariationProp(variation.condition) + + if (evaluatedCondition == undefined) { + // We've reached the eventual defaut case + let evaluatedVariation = { + consequence: evaluateVariationProp(variation.consequence), + satisfied: true + } + return [true, [...result, evaluatedVariation]] + } + + if (evaluatedCondition.nodeValue === null) + // one case has missing variables => we can't go further + return [true, [...result, { condition: evaluatedCondition }]] + + if (evaluatedCondition.nodeValue === true) { + let evaluatedVariation = { + condition: evaluatedCondition, + consequence: evaluateVariationProp(variation.consequence), + satisfied: true + } + return [true, [...result, evaluatedVariation]] + } + return [false, [...result, variation]] + }, + [false, []] + )(node.explanation), + satisfiedVariation = resolvedExplanation.find(v => v.satisfied), + nodeValue = satisfiedVariation + ? satisfiedVariation.consequence.nodeValue + : null + + let leftMissing = mergeAllMissing( + reject(isNil, pluck('condition', resolvedExplanation)) + ), + candidateVariations = filter( + node => !node.condition || node.condition.nodeValue !== false, + resolvedExplanation + ), + rightMissing = mergeAllMissing( + reject(isNil, pluck('consequence', candidateVariations)) + ), + missingVariables = satisfiedVariation + ? collectNodeMissing(satisfiedVariation.consequence) + : mergeMissing(bonus(leftMissing), rightMissing) + + return rewriteNode(node, nodeValue, resolvedExplanation, missingVariables) + } + + // TODO - find an appropriate representation + + return { + explanation, + evaluate, + jsx: Variations, + category: 'mecanism', + name: 'variations', + type: 'numeric', + unit: inferUnit('+', explanation.map(r => r.consequence.unit)) + } +} + +export let devariateExplanation = (recurse, mecanismKey, v) => { + let fixedProps = dissoc('variations')(v), + explanation = v.variations.map(({ si, alors, sinon }) => ({ + consequence: recurse({ + [mecanismKey]: { + ...fixedProps, + ...(sinon || alors) + } + }), + condition: sinon ? undefined : recurse(si) + })) + + return explanation +} diff --git a/source/engine/parse.js b/source/engine/parse.js index 663196bb4..12e3e862f 100644 --- a/source/engine/parse.js +++ b/source/engine/parse.js @@ -2,10 +2,12 @@ // In a specific file // 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 barème from 'Engine/mecanisms/barème' +import barèmeContinu from 'Engine/mecanisms/barème-continu' +import barèmeLinéaire from 'Engine/mecanisms/barème-linéaire' +import variations from 'Engine/mecanisms/variations' import { Parser } from 'nearley' + import { add, curry, @@ -42,7 +44,6 @@ import { mecanismReduction, mecanismSum, mecanismSynchronisation, - mecanismVariations, mecanismOnePossibility } from './mecanisms' import { Node } from './mecanismViews/common' @@ -145,7 +146,7 @@ export let parseObject = (rules, rule, parsedRules) => rawNode => { 'une possibilité': mecanismOnePossibility(rule.dottedName), 'inversion numérique': mecanismInversion(rule.dottedName), allègement: mecanismReduction, - variations: mecanismVariations, + variations, synchronisation: mecanismSynchronisation, ...operationDispatch, filter: () =>