2018-01-08 15:07:26 +00:00
|
|
|
import {
|
2018-04-12 13:43:02 +00:00
|
|
|
add,
|
2018-01-08 15:07:26 +00:00
|
|
|
evolve,
|
2019-01-14 14:14:57 +00:00
|
|
|
filter,
|
2019-04-11 13:23:46 +00:00
|
|
|
fromPairs,
|
|
|
|
keys,
|
|
|
|
map,
|
|
|
|
mergeWith,
|
2020-05-19 16:01:16 +00:00
|
|
|
reduce,
|
|
|
|
dissoc
|
2018-01-08 15:07:26 +00:00
|
|
|
} from 'ramda'
|
2020-03-16 10:31:32 +00:00
|
|
|
import React from 'react'
|
2019-11-28 11:03:23 +00:00
|
|
|
import { typeWarning } from './error'
|
|
|
|
import { convertNodeToUnit, simplifyNodeUnit } from './nodeUnits'
|
2020-02-16 18:56:07 +00:00
|
|
|
import {
|
2020-02-24 15:41:19 +00:00
|
|
|
concatTemporals,
|
|
|
|
liftTemporalNode,
|
|
|
|
mapTemporal,
|
2020-02-24 17:34:38 +00:00
|
|
|
pureTemporal,
|
2020-03-18 14:04:49 +00:00
|
|
|
Temporal,
|
2020-02-24 17:34:38 +00:00
|
|
|
temporalAverage,
|
2020-02-24 15:41:19 +00:00
|
|
|
zipTemporals
|
2020-03-18 14:04:49 +00:00
|
|
|
} from './temporal'
|
2020-05-08 10:04:00 +00:00
|
|
|
import { EvaluatedNode, ParsedRule, ParsedRules } from './types'
|
2017-07-13 10:25:46 +00:00
|
|
|
|
2020-04-30 15:13:45 +00:00
|
|
|
export const makeJsx = (node: EvaluatedNode): JSX.Element => {
|
2020-04-29 16:12:26 +00:00
|
|
|
const Component = node.jsx
|
|
|
|
return <Component {...node} />
|
|
|
|
}
|
2017-07-13 19:55:59 +00:00
|
|
|
|
2020-04-30 15:13:45 +00:00
|
|
|
export const collectNodeMissing = node => node.missingVariables || {}
|
2018-04-12 13:43:02 +00:00
|
|
|
|
2020-04-30 15:13:45 +00:00
|
|
|
export const bonus = (missings, hasCondition = true) =>
|
2018-06-29 13:46:42 +00:00
|
|
|
hasCondition ? map(x => x + 0.0001, missings || {}) : missings
|
2020-04-30 15:13:45 +00:00
|
|
|
export const mergeAllMissing = missings =>
|
2018-06-29 13:46:42 +00:00
|
|
|
reduce(mergeWith(add), {}, map(collectNodeMissing, missings))
|
2020-04-30 15:13:45 +00:00
|
|
|
export const mergeMissing = (left, right) =>
|
2018-06-29 13:46:42 +00:00
|
|
|
mergeWith(add, left || {}, right || {})
|
2017-07-13 10:25:46 +00:00
|
|
|
|
2020-05-06 07:23:21 +00:00
|
|
|
export const evaluateNode = (cache, situation, parsedRules, node) => {
|
2020-04-30 15:13:45 +00:00
|
|
|
const evaluatedNode = node.evaluate
|
2020-05-06 07:23:21 +00:00
|
|
|
? node.evaluate(cache, situation, parsedRules, node)
|
2019-11-28 11:03:23 +00:00
|
|
|
: node
|
|
|
|
return evaluatedNode
|
|
|
|
}
|
2020-02-24 17:34:38 +00:00
|
|
|
|
|
|
|
function convertNodesToSameUnit(nodes, contextRule, mecanismName) {
|
|
|
|
const firstNodeWithUnit = nodes.find(node => !!node.unit)
|
2020-02-21 15:39:54 +00:00
|
|
|
if (!firstNodeWithUnit) {
|
2020-02-24 17:34:38 +00:00
|
|
|
return nodes
|
2020-02-21 15:39:54 +00:00
|
|
|
}
|
2020-02-24 17:34:38 +00:00
|
|
|
return nodes.map(node => {
|
2019-11-28 11:03:23 +00:00
|
|
|
try {
|
2020-02-24 17:34:38 +00:00
|
|
|
return convertNodeToUnit(firstNodeWithUnit.unit, node)
|
2019-11-28 11:03:23 +00:00
|
|
|
} catch (e) {
|
|
|
|
typeWarning(
|
|
|
|
contextRule,
|
2020-02-21 15:39:54 +00:00
|
|
|
`Dans le mécanisme ${mecanismName}, les unités des éléments suivants sont incompatibles entre elles : \n\t\t${node?.name ||
|
|
|
|
node?.rawNode}\n\t\t${firstNodeWithUnit?.name ||
|
|
|
|
firstNodeWithUnit?.rawNode}'`,
|
2019-11-28 11:03:23 +00:00
|
|
|
e
|
|
|
|
)
|
2020-02-24 17:34:38 +00:00
|
|
|
return node
|
2019-11-28 11:03:23 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2017-07-13 10:25:46 +00:00
|
|
|
|
2020-02-24 17:34:38 +00:00
|
|
|
export const evaluateArray = (reducer, start) => (
|
2018-01-03 15:54:19 +00:00
|
|
|
cache,
|
2020-05-06 07:23:21 +00:00
|
|
|
situation,
|
2018-01-03 15:54:19 +00:00
|
|
|
parsedRules,
|
|
|
|
node
|
|
|
|
) => {
|
2020-05-06 07:23:21 +00:00
|
|
|
const evaluate = evaluateNode.bind(null, cache, situation, parsedRules)
|
2020-02-24 17:34:38 +00:00
|
|
|
const evaluatedNodes = convertNodesToSameUnit(
|
|
|
|
node.explanation.map(evaluate),
|
|
|
|
cache._meta.contextRule,
|
|
|
|
node.name
|
2020-02-24 15:41:19 +00:00
|
|
|
)
|
2020-03-16 10:31:32 +00:00
|
|
|
|
2020-02-24 17:34:38 +00:00
|
|
|
const temporalValues = concatTemporals(
|
|
|
|
evaluatedNodes.map(
|
|
|
|
({ temporalValue, nodeValue }) => temporalValue ?? pureTemporal(nodeValue)
|
2020-02-24 15:41:19 +00:00
|
|
|
)
|
2020-02-24 17:34:38 +00:00
|
|
|
)
|
|
|
|
const temporalValue = mapTemporal(values => {
|
|
|
|
if (values.some(value => value === null)) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
return reduce(reducer, start, values)
|
|
|
|
}, temporalValues)
|
|
|
|
|
|
|
|
const baseEvaluation = {
|
|
|
|
...node,
|
2020-03-16 10:31:32 +00:00
|
|
|
missingVariables: mergeAllMissing(evaluatedNodes),
|
2020-02-24 17:34:38 +00:00
|
|
|
explanation: evaluatedNodes,
|
2020-04-23 16:56:16 +00:00
|
|
|
...(evaluatedNodes[0] && { unit: evaluatedNodes[0].unit })
|
2020-02-24 17:34:38 +00:00
|
|
|
}
|
|
|
|
if (temporalValue.length === 1) {
|
2020-02-24 15:41:19 +00:00
|
|
|
return {
|
2020-02-24 17:34:38 +00:00
|
|
|
...baseEvaluation,
|
|
|
|
nodeValue: temporalValue[0].value
|
2020-02-24 15:41:19 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-28 11:03:23 +00:00
|
|
|
return {
|
2020-02-24 17:34:38 +00:00
|
|
|
...baseEvaluation,
|
2020-02-24 15:41:19 +00:00
|
|
|
temporalValue,
|
2020-02-24 17:34:38 +00:00
|
|
|
nodeValue: temporalAverage(temporalValue)
|
2019-11-28 11:03:23 +00:00
|
|
|
}
|
2017-07-16 21:58:06 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 17:34:38 +00:00
|
|
|
export const evaluateArrayWithFilter = (evaluationFilter, reducer, start) => (
|
2018-01-03 15:54:19 +00:00
|
|
|
cache,
|
2020-05-06 07:23:21 +00:00
|
|
|
situation,
|
2018-01-03 15:54:19 +00:00
|
|
|
parsedRules,
|
|
|
|
node
|
|
|
|
) => {
|
2020-05-19 16:01:16 +00:00
|
|
|
return evaluateArray(reducer, start)(
|
|
|
|
cache,
|
|
|
|
dissoc('_meta.filter', situation),
|
|
|
|
parsedRules,
|
|
|
|
{
|
|
|
|
...node,
|
|
|
|
explanation: filter(evaluationFilter(situation), node.explanation)
|
|
|
|
}
|
|
|
|
)
|
2017-07-13 10:25:46 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 15:13:45 +00:00
|
|
|
export const defaultNode = (nodeValue: EvaluatedNode['nodeValue']) => ({
|
2019-02-14 17:00:31 +00:00
|
|
|
nodeValue,
|
|
|
|
// eslint-disable-next-line
|
2020-04-20 09:46:13 +00:00
|
|
|
jsx: ({ nodeValue }: EvaluatedNode) => (
|
|
|
|
<span className="value">{nodeValue}</span>
|
|
|
|
),
|
2019-02-14 17:00:31 +00:00
|
|
|
isDefault: true
|
|
|
|
})
|
|
|
|
|
2020-04-30 15:13:45 +00:00
|
|
|
export const parseObject = (recurse, objectShape, value) => {
|
|
|
|
const recurseOne = key => defaultValue => {
|
2020-03-09 15:51:10 +00:00
|
|
|
if (value[key] == null && !defaultValue)
|
2018-12-05 17:58:38 +00:00
|
|
|
throw new Error(
|
2020-02-25 18:47:54 +00:00
|
|
|
`Il manque une clé '${key}' dans ${JSON.stringify(value)} `
|
2018-12-05 17:58:38 +00:00
|
|
|
)
|
2020-03-09 15:51:10 +00:00
|
|
|
return value[key] != null ? recurse(value[key]) : defaultValue
|
2018-01-03 15:54:19 +00:00
|
|
|
}
|
2020-04-30 15:13:45 +00:00
|
|
|
const transforms = fromPairs(
|
2020-03-18 14:04:49 +00:00
|
|
|
map(k => [k, recurseOne(k)], keys(objectShape)) as any
|
|
|
|
)
|
|
|
|
return evolve(transforms as any, objectShape)
|
2017-07-13 10:25:46 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 15:13:45 +00:00
|
|
|
export const evaluateObject = (objectShape, effect) => (
|
2018-01-03 15:54:19 +00:00
|
|
|
cache,
|
2020-05-06 07:23:21 +00:00
|
|
|
situation,
|
2018-01-03 15:54:19 +00:00
|
|
|
parsedRules,
|
|
|
|
node
|
|
|
|
) => {
|
2020-05-06 07:23:21 +00:00
|
|
|
const evaluate = evaluateNode.bind(null, cache, situation, parsedRules)
|
2020-02-24 15:41:19 +00:00
|
|
|
const evaluations = map(evaluate, node.explanation)
|
|
|
|
const temporalExplanations = mapTemporal(
|
|
|
|
Object.fromEntries,
|
|
|
|
concatTemporals(
|
|
|
|
Object.entries(evaluations).map(([key, node]) =>
|
2020-02-24 17:34:38 +00:00
|
|
|
zipTemporals(pureTemporal(key), liftTemporalNode(node))
|
2020-02-24 15:41:19 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2020-02-24 17:34:38 +00:00
|
|
|
const temporalExplanation = mapTemporal(explanations => {
|
2020-05-06 07:23:21 +00:00
|
|
|
const evaluation = effect(explanations, cache, situation, parsedRules)
|
2020-02-24 17:34:38 +00:00
|
|
|
return {
|
|
|
|
...evaluation,
|
|
|
|
explanation: {
|
|
|
|
...explanations,
|
|
|
|
...evaluation.explanation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, temporalExplanations)
|
|
|
|
|
2020-03-18 14:04:49 +00:00
|
|
|
const sameUnitTemporalExplanation: Temporal<EvaluatedNode<
|
2020-04-23 07:30:03 +00:00
|
|
|
string,
|
2020-03-18 14:04:49 +00:00
|
|
|
number
|
|
|
|
>> = convertNodesToSameUnit(
|
2020-02-24 17:34:38 +00:00
|
|
|
temporalExplanation.map(x => x.value),
|
|
|
|
cache._meta.contextRule,
|
|
|
|
node.name
|
|
|
|
).map((node, i) => ({
|
|
|
|
...temporalExplanation[i],
|
|
|
|
value: simplifyNodeUnit(node)
|
|
|
|
}))
|
2020-02-24 15:41:19 +00:00
|
|
|
|
|
|
|
const temporalValue = mapTemporal(
|
2020-02-24 17:34:38 +00:00
|
|
|
({ nodeValue }) => nodeValue,
|
|
|
|
sameUnitTemporalExplanation
|
2020-02-24 15:41:19 +00:00
|
|
|
)
|
2020-02-24 17:34:38 +00:00
|
|
|
const nodeValue = temporalAverage(temporalValue)
|
|
|
|
const baseEvaluation = {
|
2019-11-28 11:03:23 +00:00
|
|
|
...node,
|
|
|
|
nodeValue,
|
2020-02-24 17:34:38 +00:00
|
|
|
unit: sameUnitTemporalExplanation[0].value.unit,
|
2020-03-16 10:31:32 +00:00
|
|
|
explanation: evaluations,
|
|
|
|
missingVariables: mergeAllMissing(Object.values(evaluations))
|
2020-02-24 17:34:38 +00:00
|
|
|
}
|
|
|
|
if (sameUnitTemporalExplanation.length === 1) {
|
|
|
|
return {
|
|
|
|
...baseEvaluation,
|
|
|
|
explanation: sameUnitTemporalExplanation[0].value.explanation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...baseEvaluation,
|
|
|
|
temporalValue,
|
|
|
|
temporalExplanation
|
|
|
|
}
|
2019-02-21 16:55:07 +00:00
|
|
|
}
|
2020-04-23 07:30:03 +00:00
|
|
|
|
2020-05-08 10:04:00 +00:00
|
|
|
type DefaultValues<Names extends string> = Partial<
|
|
|
|
Record<Names, number | string | object>
|
|
|
|
>
|
2020-04-23 07:30:03 +00:00
|
|
|
export function collectDefaults<Names extends string>(
|
|
|
|
parsedRules: ParsedRules<Names>
|
|
|
|
): DefaultValues<Names> {
|
2020-05-08 10:04:00 +00:00
|
|
|
const values: Array<ParsedRule<Names>> = Object.values(parsedRules)
|
|
|
|
return values.reduce(
|
|
|
|
(acc: DefaultValues<Names>, parsedRule: ParsedRule<Names>) => {
|
2020-04-23 07:30:03 +00:00
|
|
|
if (parsedRule?.['par défaut'] == null) {
|
|
|
|
return acc
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...acc,
|
|
|
|
[parsedRule.dottedName]: parsedRule['par défaut']
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{}
|
|
|
|
)
|
|
|
|
}
|