Unités des barèmes et composantes

pull/603/head
Mael 2019-07-11 18:25:08 +02:00
parent 0d0fcabe1b
commit 6606bb4f34
13 changed files with 166 additions and 75 deletions

View File

@ -77,6 +77,7 @@ export default withLanguage(
return nodeValue == undefined ? null : (
<span css={style(customCSS)} className="value">
unit: {unitText}
{negative ? '-' : ''}
{formattedValue}
</span>

View File

@ -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 = {

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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('%')])
}
}

View File

@ -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))
}
}

View File

@ -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,

View File

@ -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
}

View File

@ -54,6 +54,7 @@ describe('Mécanismes', () =>
}
if (unit) {
expect(target.unit).not.to.be.equal(undefined)
expect(serialiseUnit(target.unit)).to.eql(unit)
}
})

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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