2018-01-08 15:07:26 +00:00
|
|
|
import {
|
2018-04-10 12:27:25 +00:00
|
|
|
flatten,
|
2018-04-12 13:43:02 +00:00
|
|
|
mergeAll,
|
2018-04-10 12:27:25 +00:00
|
|
|
pluck,
|
2018-01-08 15:07:26 +00:00
|
|
|
groupBy,
|
|
|
|
toPairs,
|
|
|
|
sort,
|
|
|
|
map,
|
|
|
|
length,
|
|
|
|
descend,
|
|
|
|
head,
|
|
|
|
unless,
|
|
|
|
is,
|
|
|
|
prop,
|
2018-02-06 17:01:20 +00:00
|
|
|
pick,
|
2018-01-08 15:07:26 +00:00
|
|
|
path,
|
|
|
|
reject,
|
|
|
|
identity
|
|
|
|
} from 'ramda'
|
2018-02-06 16:02:13 +00:00
|
|
|
import React from 'react'
|
2017-07-02 17:12:02 +00:00
|
|
|
import Question from 'Components/conversation/Question'
|
|
|
|
import Input from 'Components/conversation/Input'
|
2017-09-19 14:55:23 +00:00
|
|
|
import Select from 'Components/conversation/select/Select'
|
2017-09-24 13:26:07 +00:00
|
|
|
import SelectAtmp from 'Components/conversation/select/SelectTauxRisque'
|
2017-07-02 17:12:02 +00:00
|
|
|
import formValueTypes from 'Components/conversation/formValueTypes'
|
2018-02-07 16:51:55 +00:00
|
|
|
import InversionInput from '../components/conversation/InversionInput'
|
2017-07-02 17:12:02 +00:00
|
|
|
|
2018-02-06 16:02:13 +00:00
|
|
|
import {
|
|
|
|
findRuleByDottedName,
|
|
|
|
disambiguateRuleReference,
|
|
|
|
queryRule
|
|
|
|
} from './rules'
|
2017-04-28 15:03:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
COLLECTE DES VARIABLES MANQUANTES
|
|
|
|
*********************************
|
|
|
|
on collecte les variables manquantes : celles qui sont nécessaires pour
|
|
|
|
remplir les objectifs de la simulation (calculer des cotisations) mais qui n'ont pas
|
|
|
|
encore été renseignées
|
|
|
|
|
|
|
|
TODO perf : peut-on le faire en même temps que l'on traverse l'AST ?
|
|
|
|
Oui sûrement, cette liste se complète en remontant l'arbre. En fait, on le fait déjà pour nodeValue,
|
|
|
|
et quand nodeValue vaut null, c'est qu'il y a des missingVariables ! Il suffit donc de remplacer les
|
|
|
|
null par un tableau, et d'ailleurs utiliser des fonction d'aide pour mutualiser ces tests.
|
|
|
|
|
|
|
|
missingVariables: {variable: [objectives]}
|
|
|
|
*/
|
2017-07-07 08:35:40 +00:00
|
|
|
|
2018-04-12 13:43:02 +00:00
|
|
|
export let collectMissingVariables = targets => mergeAll(pluck('missingVariables', targets))
|
2017-11-07 18:46:40 +00:00
|
|
|
|
2017-11-28 11:45:06 +00:00
|
|
|
export let getNextSteps = (situationGate, analysis) => {
|
2018-04-12 13:43:02 +00:00
|
|
|
let impact = ([, count]) => count
|
2017-04-28 15:03:34 +00:00
|
|
|
|
2017-11-07 18:46:40 +00:00
|
|
|
let missingVariables = collectMissingVariables(analysis.targets),
|
2018-01-08 15:07:26 +00:00
|
|
|
pairs = toPairs(missingVariables),
|
|
|
|
sortedPairs = sort(descend(impact), pairs)
|
|
|
|
return map(head, sortedPairs)
|
2017-11-04 11:09:58 +00:00
|
|
|
}
|
2017-04-24 18:03:38 +00:00
|
|
|
|
2018-02-06 17:01:20 +00:00
|
|
|
let isVariant = rule => queryRule(rule.raw)('formule . une possibilité')
|
2017-04-24 18:03:38 +00:00
|
|
|
|
2017-09-07 20:22:16 +00:00
|
|
|
let buildVariantTree = (allRules, path) => {
|
2017-04-24 18:03:38 +00:00
|
|
|
let rec = path => {
|
2017-07-01 09:17:54 +00:00
|
|
|
let node = findRuleByDottedName(allRules, path),
|
2017-04-27 18:08:52 +00:00
|
|
|
variant = isVariant(node),
|
2018-01-08 15:07:26 +00:00
|
|
|
variants = variant && unless(is(Array), prop('possibilités'))(variant),
|
|
|
|
shouldBeExpanded = variant && true, //variants.find( v => relevantPaths.find(rp => contains(path + ' . ' + v)(rp) )),
|
2017-04-27 18:08:52 +00:00
|
|
|
canGiveUp = variant && !variant['choix obligatoire']
|
2017-04-24 18:03:38 +00:00
|
|
|
|
|
|
|
return Object.assign(
|
|
|
|
node,
|
2017-11-15 13:46:48 +00:00
|
|
|
shouldBeExpanded
|
|
|
|
? {
|
2018-01-03 15:54:19 +00:00
|
|
|
canGiveUp,
|
|
|
|
children: variants.map(v => rec(path + ' . ' + v))
|
2018-02-06 15:17:58 +00:00
|
|
|
}
|
2017-11-15 13:46:48 +00:00
|
|
|
: null
|
2017-04-24 18:03:38 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
return rec(path)
|
|
|
|
}
|
|
|
|
|
2018-02-06 16:02:13 +00:00
|
|
|
let buildPossibleInversion = (rule, rules, targetNames) => {
|
|
|
|
let query = queryRule(rule),
|
2018-02-06 17:01:20 +00:00
|
|
|
inversion = query('formule . inversion')
|
2018-02-06 16:02:13 +00:00
|
|
|
|
2018-02-06 17:01:20 +00:00
|
|
|
if (!inversion) return null
|
|
|
|
let inversionObjects = query('formule . inversion . avec').map(i =>
|
2018-02-06 16:02:13 +00:00
|
|
|
findRuleByDottedName(rules, disambiguateRuleReference(rules, rule, i))
|
2017-11-24 10:04:29 +00:00
|
|
|
),
|
2018-02-26 12:36:08 +00:00
|
|
|
inversions = reject(({ name }) => targetNames.includes(name))(
|
2018-01-03 15:54:19 +00:00
|
|
|
[rule].concat(inversionObjects)
|
|
|
|
)
|
2017-11-24 10:04:29 +00:00
|
|
|
|
2017-11-24 17:45:28 +00:00
|
|
|
return {
|
2018-02-26 12:36:08 +00:00
|
|
|
inversions,
|
2018-02-06 16:02:13 +00:00
|
|
|
question: query('formule . inversion . question')
|
2017-11-24 17:45:28 +00:00
|
|
|
}
|
2017-11-24 10:04:29 +00:00
|
|
|
}
|
|
|
|
|
2018-02-07 11:10:49 +00:00
|
|
|
// This function takes the unknown rule and finds which React component should be displayed to get a user input through successive if statements
|
|
|
|
// That's not great, but we won't invest more time until we have more diverse input components and a better type system.
|
2018-02-06 15:17:58 +00:00
|
|
|
export let getInputComponent = ({ unfolded }) => (
|
|
|
|
rules,
|
2018-02-07 17:16:23 +00:00
|
|
|
targetNames
|
2018-02-06 15:17:58 +00:00
|
|
|
) => dottedName => {
|
|
|
|
let rule = findRuleByDottedName(rules, dottedName)
|
|
|
|
|
|
|
|
let commonProps = {
|
2018-02-06 17:01:20 +00:00
|
|
|
key: dottedName,
|
2018-02-06 15:17:58 +00:00
|
|
|
unfolded,
|
2018-02-06 16:02:13 +00:00
|
|
|
fieldName: dottedName,
|
2018-02-06 17:01:20 +00:00
|
|
|
...pick(['dottedName', 'title', 'question', 'defaultValue'], rule)
|
2018-02-06 15:17:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isVariant(rule))
|
|
|
|
return (
|
|
|
|
<Question
|
|
|
|
{...{
|
|
|
|
...commonProps,
|
|
|
|
choices: buildVariantTree(rules, dottedName)
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
|
|
|
if (rule.format == null)
|
|
|
|
return (
|
|
|
|
<Question
|
|
|
|
{...{
|
|
|
|
...commonProps,
|
|
|
|
choices: [
|
|
|
|
{ value: 'non', label: 'Non' },
|
|
|
|
{ value: 'oui', label: 'Oui' }
|
|
|
|
]
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
|
|
|
if (rule.suggestions == 'atmp-2017')
|
|
|
|
return (
|
|
|
|
<SelectAtmp
|
|
|
|
{...{
|
|
|
|
...commonProps,
|
|
|
|
valueType: formValueTypes[rule.format],
|
|
|
|
suggestions: rule.suggestions
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
|
|
|
if (typeof rule.suggestions == 'string')
|
|
|
|
return (
|
|
|
|
<Select
|
|
|
|
{...{
|
|
|
|
...commonProps,
|
|
|
|
valueType: formValueTypes[rule.format],
|
|
|
|
suggestions: rule.suggestions
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
2018-02-07 11:10:49 +00:00
|
|
|
// Now the numeric input case
|
|
|
|
|
|
|
|
// Check for inversions
|
2018-02-07 17:16:23 +00:00
|
|
|
let inversion = buildPossibleInversion(rule, rules, targetNames)
|
2018-02-06 16:02:13 +00:00
|
|
|
|
2018-02-07 11:10:49 +00:00
|
|
|
/* In the case of an inversion, display a RadioInput component.
|
|
|
|
On click on one of the radios, display the corresponding input.
|
|
|
|
If only one inversion is possible, don't show the radio but show the documentation icon.
|
|
|
|
Else just display the Input component.
|
|
|
|
*/
|
|
|
|
|
2018-02-07 14:09:42 +00:00
|
|
|
if (inversion)
|
|
|
|
return (
|
2018-02-07 16:51:55 +00:00
|
|
|
<InversionInput
|
2018-02-07 14:09:42 +00:00
|
|
|
{...{
|
|
|
|
...commonProps,
|
2018-02-08 10:33:57 +00:00
|
|
|
valueType: formValueTypes[rule.format],
|
|
|
|
suggestions: rule.suggestions,
|
2018-02-07 17:16:23 +00:00
|
|
|
inversion
|
2018-02-07 14:09:42 +00:00
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
|
2018-02-06 15:17:58 +00:00
|
|
|
return (
|
|
|
|
<Input
|
|
|
|
{...{
|
|
|
|
...commonProps,
|
|
|
|
valueType: formValueTypes[rule.format],
|
2018-02-07 17:16:23 +00:00
|
|
|
suggestions: rule.suggestions
|
2018-02-06 15:17:58 +00:00
|
|
|
}}
|
|
|
|
/>
|
2017-09-07 20:22:16 +00:00
|
|
|
)
|
|
|
|
}
|