⚙️ Analyse top-down, correction de collect/buildNextSteps
parent
5f3d4f386a
commit
1e3398f050
|
@ -6,7 +6,7 @@ import Question from 'Components/conversation/Question'
|
|||
import Input from 'Components/conversation/Input'
|
||||
import formValueTypes from 'Components/conversation/formValueTypes'
|
||||
|
||||
import {analyseSituation} from './traverse'
|
||||
import {analyseSituation, evaluateNode} from './traverse'
|
||||
import {formValueSelector} from 'redux-form'
|
||||
import {rules, findRuleByDottedName, findVariantsAndRecords} from './rules'
|
||||
|
||||
|
@ -42,7 +42,7 @@ export let analyse = rootVariable => R.pipe(
|
|||
|
||||
// On peut travailler sur une somme, les objectifs sont alors les variables de cette somme.
|
||||
// Ou sur une variable unique ayant une formule, elle est elle-même le seul objectif
|
||||
export let getObjectives = (root, parsedRules) => {
|
||||
export let getObjectives = (situationGate, root, parsedRules) => {
|
||||
let formuleType = R.path(["formule", "explanation", "name"])(
|
||||
root
|
||||
)
|
||||
|
@ -55,37 +55,17 @@ export let getObjectives = (root, parsedRules) => {
|
|||
: formuleType ? [root] : null,
|
||||
names = targets ? R.reject(R.isNil)(targets) : []
|
||||
|
||||
return R.map(R.curry(findRuleByDottedName)(parsedRules),names)
|
||||
let findAndEvaluate = name => evaluateNode(situationGate,parsedRules,findRuleByDottedName(parsedRules,name))
|
||||
return R.map(findAndEvaluate,names)
|
||||
}
|
||||
|
||||
// FIXME - this relies on side-effects and the recursion is grossly indiscriminate
|
||||
let collectNodeMissingVariables = (root, source=root, results=[]) => {
|
||||
if (root == source) console.log("cNMV:",root)
|
||||
if (
|
||||
source.nodeValue != null ||
|
||||
source.shortCircuit && source.shortCircuit(root)
|
||||
) {
|
||||
// console.log('nodev or shortcircuit root, source', root, source)
|
||||
return []
|
||||
}
|
||||
|
||||
if (source['missingVariables']) {
|
||||
// console.log('root, source', root, source)
|
||||
results.push(source['missingVariables'])
|
||||
}
|
||||
|
||||
for (var prop in source) {
|
||||
if (R.is(Object)(source[prop])) {
|
||||
collectNodeMissingVariables(root, source[prop], results)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
let collectNodeMissingVariables = (root) => {
|
||||
return root.collectMissing ? root.collectMissing(root) : []
|
||||
}
|
||||
|
||||
export let collectMissingVariables = (groupMethod='groupByMissingVariable') => ({root, parsedRules}) => {
|
||||
export let collectMissingVariables = (groupMethod='groupByMissingVariable') => (situationGate, {root, parsedRules}) => {
|
||||
return R.pipe(
|
||||
getObjectives,
|
||||
R.curry(getObjectives)(situationGate),
|
||||
R.chain( v =>
|
||||
R.pipe(
|
||||
collectNodeMissingVariables,
|
||||
|
@ -101,9 +81,9 @@ export let collectMissingVariables = (groupMethod='groupByMissingVariable') => (
|
|||
)(root, parsedRules)
|
||||
}
|
||||
|
||||
export let buildNextSteps = (allRules, analysedSituation) => {
|
||||
export let buildNextSteps = (situationGate, flatRules, analysedSituation) => {
|
||||
let missingVariables = collectMissingVariables('groupByMissingVariable')(
|
||||
analysedSituation
|
||||
situationGate, analysedSituation
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -142,11 +122,11 @@ export let buildNextSteps = (allRules, analysedSituation) => {
|
|||
|
||||
return R.pipe(
|
||||
R.keys,
|
||||
R.curry(findVariantsAndRecords)(allRules),
|
||||
R.curry(findVariantsAndRecords)(flatRules),
|
||||
// on va maintenant construire la liste des composants React qui afficheront les questions à l'utilisateur pour que l'on obtienne les variables manquantes
|
||||
R.evolve({
|
||||
variantGroups: generateGridQuestions(allRules, missingVariables),
|
||||
recordGroups: generateSimpleQuestions(allRules, missingVariables),
|
||||
variantGroups: generateGridQuestions(analysedSituation.parsedRules, missingVariables),
|
||||
recordGroups: generateSimpleQuestions(analysedSituation.parsedRules, missingVariables),
|
||||
}),
|
||||
R.values,
|
||||
R.unnest,
|
||||
|
|
|
@ -2,7 +2,7 @@ import R from 'ramda'
|
|||
import React from 'react'
|
||||
import {anyNull, val} from './traverse-common-functions'
|
||||
import {Node, Leaf} from './traverse-common-jsx'
|
||||
import {evaluateNode} from './traverse'
|
||||
import {evaluateNode, collectNodeMissing} from './traverse'
|
||||
|
||||
let transformPercentage = s =>
|
||||
R.contains('%')(s) ?
|
||||
|
@ -266,13 +266,18 @@ export let mecanismPercentage = (recurse,k,v) => {
|
|||
|
||||
export let mecanismSum = (recurse,k,v) => {
|
||||
let evaluate = (situationGate, parsedRules, node) => {
|
||||
let evaluateOne = child => evaluateNode(situationGate, parsedRules, child).nodeValue,
|
||||
values = R.map(evaluateOne, node.explanation),
|
||||
let evaluateOne = child => evaluateNode(situationGate, parsedRules, child),
|
||||
explanation = R.map(evaluateOne, node.explanation),
|
||||
values = R.pluck("nodeValue",explanation),
|
||||
nodeValue = R.any(R.equals(null),values) ? null : R.reduce(R.add,0,values)
|
||||
|
||||
let collectMissing = node => R.chain(collectNodeMissing,node.explanation)
|
||||
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
collectMissing,
|
||||
explanation,
|
||||
jsx: {
|
||||
...node.jsx,
|
||||
value: nodeValue
|
||||
|
|
|
@ -112,6 +112,7 @@ export let findVariantsAndRecords = (allRules, names) => {
|
|||
groupByType = R.groupBy(R.prop("type")),
|
||||
stripTypes = R.map(R.map(R.omit("type"))),
|
||||
mergeLists = R.map(R.reduce(R.mergeWith(R.concat),{}))
|
||||
console.log("classify",classify(names))
|
||||
|
||||
return R.pipe(classify,groupByType,stripTypes,mergeLists)(names)
|
||||
}
|
||||
|
|
|
@ -80,9 +80,12 @@ let createVariableNode = (rules, rule, situationGate) => (parseResult) => {
|
|||
explanation = parsedRule,
|
||||
missingVariables = variableIsCalculable ? [] : (nodeValue == null ? [dottedName] : [])
|
||||
|
||||
let collectMissing = node => node.missingVariables
|
||||
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
collectMissing,
|
||||
missingVariables,
|
||||
explanation,
|
||||
jsx: {
|
||||
|
@ -171,16 +174,21 @@ let treat = (situationGate, rules, rule) => rawNode => {
|
|||
'+': 'add',
|
||||
'-': 'subtract'
|
||||
}[node.operator],
|
||||
value1 = evaluateNode(situation,parsedRules,node.explanation[0]).nodeValue,
|
||||
value2 = evaluateNode(situation,parsedRules,node.explanation[1]).nodeValue,
|
||||
explanation = R.map(R.curry(evaluateNode)(situation,parsedRules),node.explanation),
|
||||
value1 = explanation[0].nodeValue,
|
||||
value2 = explanation[1].nodeValue,
|
||||
operatorFunction = R[operatorFunctionName],
|
||||
nodeValue = value1 == null || value2 == null ?
|
||||
null
|
||||
: operatorFunction(value1, value2)
|
||||
|
||||
let collectMissing = node => R.chain(collectNodeMissing,node.explanation)
|
||||
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
collectMissing,
|
||||
explanation,
|
||||
jsx: {
|
||||
...node.jsx,
|
||||
value: nodeValue
|
||||
|
@ -237,15 +245,20 @@ let treat = (situationGate, rules, rule) => rawNode => {
|
|||
//TODO '='
|
||||
}[node.operator],
|
||||
comparatorFunction = R[comparatorFunctionName],
|
||||
value1 = evaluateNode(situation,parsedRules,node.explanation[0]).nodeValue,
|
||||
value2 = evaluateNode(situation,parsedRules,node.explanation[1]).nodeValue,
|
||||
explanation = R.map(R.curry(evaluateNode)(situation,parsedRules),node.explanation),
|
||||
value1 = explanation[0].nodeValue,
|
||||
value2 = explanation[1].nodeValue,
|
||||
nodeValue = value1 == null || value2 == null ?
|
||||
null
|
||||
: comparatorFunction(value1, value2)
|
||||
|
||||
let collectMissing = node => R.chain(collectNodeMissing,node.explanation)
|
||||
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
collectMissing,
|
||||
explanation,
|
||||
jsx: {
|
||||
...node.jsx,
|
||||
value: nodeValue
|
||||
|
@ -365,12 +378,23 @@ export let computeRuleValue = (formuleValue, condValue) =>
|
|||
export let treatRuleRoot = (situationGate, rules, rule) => {
|
||||
let evaluate = (situationGate, parsedRules, r) => {
|
||||
let
|
||||
formuleValue = r.formule && evaluateNode(situationGate, parsedRules, r.formule).nodeValue,
|
||||
condition = R.prop('non applicable si',r),
|
||||
condValue = condition && evaluateNode(situationGate, parsedRules, condition).nodeValue,
|
||||
evaluated = R.evolve({
|
||||
formule:R.curry(evaluateNode)(situationGate, parsedRules),
|
||||
"non applicable si":R.curry(evaluateNode)(situationGate, parsedRules)
|
||||
},r),
|
||||
formuleValue = evaluated.formule && evaluated.formule.nodeValue,
|
||||
condition = R.prop('non applicable si',evaluated),
|
||||
condValue = condition && condition.nodeValue,
|
||||
nodeValue = computeRuleValue(formuleValue, condValue)
|
||||
|
||||
return {...r, nodeValue}
|
||||
return {...evaluated, nodeValue}
|
||||
}
|
||||
let collectMissing = node => {
|
||||
let cond = R.prop('non applicable si',node),
|
||||
condMissing = cond ? collectNodeMissing(cond) : [],
|
||||
formule = node.formule,
|
||||
formMissing = formule ? collectNodeMissing(formule) : []
|
||||
return R.concat(condMissing,formMissing)
|
||||
}
|
||||
|
||||
let parsedRoot = R.evolve({ // -> Voilà les attributs que peut comporter, pour l'instant, une Variable.
|
||||
|
@ -380,10 +404,12 @@ export let treatRuleRoot = (situationGate, rules, rule) => {
|
|||
// 'cond' : Conditions d'applicabilité de la règle
|
||||
'non applicable si': value => {
|
||||
let evaluate = (situationGate, parsedRules, node) => {
|
||||
let nodeValue = evaluateNode(situationGate, parsedRules, node.explanation).nodeValue
|
||||
let explanation = evaluateNode(situationGate, parsedRules, node.explanation),
|
||||
nodeValue = explanation.nodeValue
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
explanation,
|
||||
jsx: {
|
||||
...node.jsx,
|
||||
value: nodeValue
|
||||
|
@ -393,8 +419,11 @@ export let treatRuleRoot = (situationGate, rules, rule) => {
|
|||
|
||||
let child = treat(situationGate, rules, rule)(value)
|
||||
|
||||
let collectMissing = node => collectNodeMissing(node.explanation)
|
||||
|
||||
return {
|
||||
evaluate,
|
||||
collectMissing,
|
||||
category: 'ruleProp',
|
||||
rulePropType: 'cond',
|
||||
name: 'non applicable si',
|
||||
|
@ -418,10 +447,12 @@ export let treatRuleRoot = (situationGate, rules, rule) => {
|
|||
// [n'importe quel mécanisme numérique] : multiplication || barème en taux marginaux || le maximum de || le minimum de || ...
|
||||
'formule': value => {
|
||||
let evaluate = (situationGate, parsedRules, node) => {
|
||||
let nodeValue = evaluateNode(situationGate, parsedRules, node.explanation).nodeValue
|
||||
let explanation = evaluateNode(situationGate, parsedRules, node.explanation),
|
||||
nodeValue = explanation.nodeValue
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
explanation,
|
||||
jsx: {
|
||||
...node.jsx,
|
||||
value: nodeValue
|
||||
|
@ -430,14 +461,16 @@ export let treatRuleRoot = (situationGate, rules, rule) => {
|
|||
}
|
||||
|
||||
let child = treat(situationGate, rules, rule)(value)
|
||||
let collectMissing = node => collectNodeMissing(node.explanation)
|
||||
|
||||
return {
|
||||
evaluate,
|
||||
collectMissing,
|
||||
category: 'ruleProp',
|
||||
rulePropType: 'formula',
|
||||
name: 'formule',
|
||||
type: 'numeric',
|
||||
explanation: child,
|
||||
shortCircuit: R.pathEq(['non applicable si', 'nodeValue'], true),
|
||||
jsx: <Node
|
||||
classes="ruleProp mecanism formula"
|
||||
name="formule"
|
||||
|
@ -457,11 +490,15 @@ export let treatRuleRoot = (situationGate, rules, rule) => {
|
|||
|
||||
return {
|
||||
...parsedRoot,
|
||||
evaluate
|
||||
evaluate,
|
||||
collectMissing
|
||||
}
|
||||
}
|
||||
|
||||
export let evaluateNode = (situationGate, parsedRules, node) => node.evaluate(situationGate, parsedRules, node)
|
||||
export let evaluateNode = (situationGate, parsedRules, node) =>
|
||||
node.evaluate(situationGate, parsedRules, node)
|
||||
export let collectNodeMissing = (node) =>
|
||||
node.collectMissing ? node.collectMissing(node) : []
|
||||
|
||||
/* Analyse the set of selected rules, and add derived information to them :
|
||||
- do they need variables that are not present in the user situation ?
|
||||
|
|
|
@ -36,14 +36,14 @@ export let reduceSteps = (state, action) => {
|
|||
return {
|
||||
...returnObject,
|
||||
foldedSteps: state.foldedSteps || [],
|
||||
unfoldedSteps: buildNextSteps(rules, returnObject.analysedSituation.root)
|
||||
unfoldedSteps: buildNextSteps(situationGate(state), rules, returnObject.analysedSituation)
|
||||
}
|
||||
}
|
||||
if (action.type == STEP_ACTION && action.name == 'fold') {
|
||||
return {
|
||||
...returnObject,
|
||||
foldedSteps: [...state.foldedSteps, R.head(state.unfoldedSteps)],
|
||||
unfoldedSteps: buildNextSteps(rules, returnObject.analysedSituation.root)
|
||||
unfoldedSteps: buildNextSteps(situationGate(state), rules, returnObject.analysedSituation)
|
||||
}
|
||||
}
|
||||
if (action.type == STEP_ACTION && action.name == 'unfold') {
|
||||
|
|
|
@ -4,7 +4,7 @@ import {rules, enrichRule} from '../source/engine/rules'
|
|||
import {analyseSituation, analyseTopDown} from '../source/engine/traverse'
|
||||
import {buildNextSteps, collectMissingVariables, getObjectives} from '../source/engine/generateQuestions'
|
||||
|
||||
let stateSelector = (state, name) => null
|
||||
let stateSelector = (name) => null
|
||||
|
||||
describe('getObjectives', function() {
|
||||
|
||||
|
@ -16,7 +16,7 @@ describe('getObjectives', function() {
|
|||
{nom: "ko", espace: "sum . evt"}],
|
||||
rules = rawRules.map(enrichRule),
|
||||
{root, parsedRules} = analyseTopDown(rules,"startHere")(stateSelector),
|
||||
result = getObjectives(root, parsedRules)
|
||||
result = getObjectives(stateSelector, root, parsedRules)
|
||||
|
||||
expect(result).to.have.lengthOf(1)
|
||||
expect(result[0]).to.have.property('name','deux')
|
||||
|
@ -34,7 +34,7 @@ describe('collectMissingVariables', function() {
|
|||
{nom: "ko", espace: "sum . evt"}],
|
||||
rules = rawRules.map(enrichRule),
|
||||
situation = analyseTopDown(rules,"startHere")(stateSelector),
|
||||
result = collectMissingVariables()(situation)
|
||||
result = collectMissingVariables()(stateSelector,situation)
|
||||
|
||||
expect(result).to.have.property('sum . evt . ko')
|
||||
});
|
||||
|
@ -47,7 +47,7 @@ describe('collectMissingVariables', function() {
|
|||
{nom: "nyet", espace: "sum . evt"}],
|
||||
rules = rawRules.map(enrichRule),
|
||||
situation = analyseTopDown(rules,"startHere")(stateSelector),
|
||||
result = collectMissingVariables()(situation)
|
||||
result = collectMissingVariables()(stateSelector,situation)
|
||||
|
||||
expect(result).to.have.property('sum . evt . nyet')
|
||||
expect(result).to.have.property('sum . evt . nope')
|
||||
|
@ -64,8 +64,8 @@ describe('buildNextSteps', function() {
|
|||
{nom: "evt", espace: "top . sum", formule: {"une possibilité":["ko"]}, titre: "Truc", question:"?"},
|
||||
{nom: "ko", espace: "top . sum . evt"}],
|
||||
rules = rawRules.map(enrichRule),
|
||||
situation = analyseSituation(rules,"sum")(stateSelector),
|
||||
result = buildNextSteps(rules, situation)
|
||||
situation = analyseTopDown(rules,"sum")(stateSelector),
|
||||
result = buildNextSteps(stateSelector, rules, situation)
|
||||
|
||||
expect(result).to.have.lengthOf(1)
|
||||
expect(R.path(["question","props","label"])(result[0])).to.equal("?")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import R from 'ramda'
|
||||
import {expect} from 'chai'
|
||||
import {rules, enrichRule, findVariantsAndRecords} from '../source/engine/rules'
|
||||
import {analyseSituation} from '../source/engine/traverse'
|
||||
import {analyseSituation, analyseTopDown} from '../source/engine/traverse'
|
||||
|
||||
let stateSelector = (state, name) => null
|
||||
|
||||
|
@ -32,7 +32,7 @@ describe('findVariantsAndRecords', function() {
|
|||
{nom: "dix", formule: "cinq", espace: "top"},
|
||||
{nom: "cinq", espace: "top", question:"?"}],
|
||||
rules = rawRules.map(enrichRule),
|
||||
situation = analyseSituation(rules,"startHere")(stateSelector),
|
||||
situation = analyseTopDown(rules,"startHere")(stateSelector),
|
||||
result = findVariantsAndRecords(rules, ['top . cinq'])
|
||||
|
||||
expect(result).to.have.deep.property('recordGroups', {top: ['top . cinq']})
|
||||
|
@ -45,7 +45,7 @@ describe('findVariantsAndRecords', function() {
|
|||
{nom: "evt", espace: "top . sum", formule: {"une possibilité":["ko"]}, titre: "Truc", question:"?"},
|
||||
{nom: "ko", espace: "top . sum . evt"}],
|
||||
rules = rawRules.map(enrichRule),
|
||||
situation = analyseSituation(rules,"sum")(stateSelector),
|
||||
situation = analyseTopDown(rules,"sum")(stateSelector),
|
||||
result = findVariantsAndRecords(rules, ['top . sum . evt . ko'])
|
||||
|
||||
expect(result).to.have.deep.property('variantGroups', {"top . sum . evt": ['top . sum . evt . ko']})
|
||||
|
|
Loading…
Reference in New Issue