import { add, any, equals, evolve, filter, fromPairs, is, keys, map, mergeWith, reduce, values } from 'ramda' import React from 'react' import { typeWarning } from './error' import { convertNodeToUnit, simplifyNodeUnit } from './nodeUnits' export let makeJsx = node => typeof node.jsx == 'function' ? node.jsx(node.nodeValue, node.explanation, node.lazyEval, node.unit) : node.jsx export let collectNodeMissing = node => node.missingVariables || {} export let bonus = (missings, hasCondition = true) => hasCondition ? map(x => x + 0.0001, missings || {}) : missings export let mergeAllMissing = missings => reduce(mergeWith(add), {}, map(collectNodeMissing, missings)) export let mergeMissing = (left, right) => mergeWith(add, left || {}, right || {}) export let evaluateNode = (cache, situationGate, parsedRules, node) => { let evaluatedNode = node.evaluate ? node.evaluate(cache, situationGate, parsedRules, node) : node if (typeof evaluatedNode.nodeValue !== 'number') { return evaluatedNode } evaluatedNode = node.unité ? convertNodeToUnit(node.unit, evaluatedNode) : simplifyNodeUnit(evaluatedNode) return evaluatedNode } const sameUnitValues = (explanation, contextRule, mecanismName) => { const unit = explanation.map(n => n.unit).find(Boolean) const values = explanation.map(node => { try { return convertNodeToUnit(unit, node).nodeValue } catch (e) { typeWarning( contextRule, `'${node.name}' a une unité incompatible avec celle du mécanisme ${mecanismName}`, e ) return node.nodeValue } }) return [unit, values] } export let evaluateArray = (reducer, start) => ( cache, situationGate, parsedRules, node ) => { let evaluateOne = child => evaluateNode(cache, situationGate, parsedRules, child), explanation = map(evaluateOne, node.explanation), [unit, values] = sameUnitValues( explanation, cache._meta.contextRule, node.name ), nodeValue = values.some(value => value === null) ? null : reduce(reducer, start, values), missingVariables = node.nodeValue == null ? mergeAllMissing(explanation) : {} return { ...node, nodeValue, explanation, missingVariables, unit } } export let evaluateArrayWithFilter = (evaluationFilter, reducer, start) => ( cache, situationGate, parsedRules, node ) => { let evaluateOne = child => evaluateNode(cache, situationGate, parsedRules, child), explanation = map( evaluateOne, filter(evaluationFilter(situationGate), node.explanation) ), [unit, values] = sameUnitValues( explanation, cache._meta.contextRule, node.name ), nodeValue = any(equals(null), values) ? null : reduce(reducer, start, values), missingVariables = node.nodeValue == null ? mergeAllMissing(explanation) : {} return { ...node, nodeValue, explanation, missingVariables, unit } } export let defaultNode = nodeValue => ({ nodeValue, // eslint-disable-next-line jsx: nodeValue => {nodeValue}, isDefault: true }) export let parseObject = (recurse, objectShape, value) => { let recurseOne = key => defaultValue => { if (!value[key] && !defaultValue) throw new Error( `Il manque une valeur '${key}' dans ${JSON.stringify(value)} ` ) return value[key] ? recurse(value[key]) : defaultValue } let transforms = fromPairs(map(k => [k, recurseOne(k)], keys(objectShape))) return evolve(transforms, objectShape) } export let evaluateObject = (objectShape, effect) => ( cache, situationGate, parsedRules, node ) => { let evaluateOne = child => evaluateNode(cache, situationGate, parsedRules, child) let transforms = map(k => [k, evaluateOne], keys(objectShape)), automaticExplanation = evolve(fromPairs(transforms))(node.explanation) // the result of effect can either be just a nodeValue, or an object {additionalExplanation, nodeValue}. The latter is useful for a richer JSX visualisation of the mecanism : the view should not duplicate code to recompute intermediate values (e.g. for a marginal 'barème', the marginal 'tranche') let evaluated = effect(automaticExplanation, cache, situationGate, parsedRules), explanation = is(Object, evaluated) ? { ...automaticExplanation, ...evaluated.additionalExplanation } : automaticExplanation, nodeValue = is(Object, evaluated) ? evaluated.nodeValue : evaluated, missingVariables = mergeAllMissing(values(explanation)) return simplifyNodeUnit({ ...node, nodeValue, explanation, missingVariables, unit: explanation.unit }) }