Déplacement de la génération du JSX dans traverse.js
parent
b8b327a9bf
commit
124bfdd1cc
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 38 KiB |
|
@ -22,7 +22,6 @@
|
|||
"ramda": "^0.23.0",
|
||||
"react": "^15.0.1",
|
||||
"react-dom": "^15.0.1",
|
||||
"react-hot-loader": "3.0.0-beta.2",
|
||||
"react-json-tree": "^0.10.0",
|
||||
"react-redux": "^5.0.2",
|
||||
"react-router": "^3.0.2",
|
||||
|
@ -55,6 +54,7 @@
|
|||
"json-loader": "^0.5.4",
|
||||
"nearley-loader": "0.0.2",
|
||||
"postcss-loader": "^1.2.2",
|
||||
"react-hot-loader": "^3.0.0-beta.6",
|
||||
"redux-devtools": "^3.2.0",
|
||||
"redux-devtools-dock-monitor": "^1.1.1",
|
||||
"redux-devtools-log-monitor": "^1.0.9",
|
||||
|
|
|
@ -13,17 +13,6 @@
|
|||
- motif . jeune vacances
|
||||
- contrat aidé
|
||||
|
||||
# Données de test
|
||||
# non applicable si:
|
||||
# l'une de ces conditions:
|
||||
# - A
|
||||
# - toutes ces conditions:
|
||||
# - B
|
||||
# - C
|
||||
# - l'une de ces conditions:
|
||||
# - X
|
||||
# - Z
|
||||
|
||||
formule:
|
||||
multiplication:
|
||||
assiette: salaire de base
|
||||
|
|
|
@ -13,9 +13,13 @@ rules:
|
|||
no-global-assign: 0
|
||||
no-unsafe-negation: 0
|
||||
no-undef: 1
|
||||
react/jsx-uses-vars: 2
|
||||
react/jsx-uses-react: 2
|
||||
|
||||
parser: babel-eslint
|
||||
|
||||
plugins:
|
||||
- react
|
||||
env:
|
||||
browser: true
|
||||
commonjs: true
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
background: #407ee7;
|
||||
}
|
||||
|
||||
.multiplicationSign {
|
||||
.operator {
|
||||
margin: .1em .6em;
|
||||
font-size: 150%;
|
||||
vertical-align: sub;
|
||||
|
|
|
@ -7,22 +7,11 @@ import R from 'ramda'
|
|||
import PageTypeIcon from './PageTypeIcon'
|
||||
import {connect} from 'react-redux'
|
||||
import {formValueSelector} from 'redux-form'
|
||||
import mockSituation from '../engine/mockSituation.yaml'
|
||||
|
||||
// situationGate function useful for testing :
|
||||
let testingSituationGate = v =>
|
||||
R.path(v.split('.'))({
|
||||
'Salariat ': {
|
||||
' CDD ': {
|
||||
' événements': '_',
|
||||
' motif': 'saisonnier',
|
||||
' engagement employeur complément formation': 'non',
|
||||
' durée contrat': '2',
|
||||
},
|
||||
' contrat aidé': 'non',
|
||||
' salaire de base': '1481',
|
||||
' congés non pris': '3',
|
||||
},
|
||||
})
|
||||
let testingSituationGate = v => // eslint-disable-line no-unused-vars
|
||||
R.path(v.split('.'))(mockSituation)
|
||||
|
||||
@connect(state => ({
|
||||
situationGate: name => formValueSelector('conversation')(state, name)
|
||||
|
@ -68,16 +57,17 @@ export default class Rule extends Component {
|
|||
</section>
|
||||
<section id="rule-rules">
|
||||
{ do {
|
||||
let cond =
|
||||
R.toPairs(rule).find(([,v]) => v.rulePropType == 'cond')
|
||||
cond != null && <section id="declenchement">
|
||||
<h2>Conditions de déclenchement</h2>
|
||||
<RuleProp {...cond[1]} />
|
||||
</section>
|
||||
let [,cond] =
|
||||
R.toPairs(rule).find(([,v]) => v.rulePropType == 'cond') || []
|
||||
cond != null &&
|
||||
<section id="declenchement">
|
||||
<h2>Conditions de déclenchement</h2>
|
||||
{cond.jsx}
|
||||
</section>
|
||||
}}
|
||||
<section id="formule">
|
||||
<h2>Calcul</h2>
|
||||
<RuleProp {...rule['formule']}/>
|
||||
{rule['formule'].jsx}
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
@ -159,9 +149,9 @@ let Multiplication = ({base, rate}) =>
|
|||
</div>
|
||||
|
||||
|
||||
let Variable = (yo) => do {let {nodeValue, variableName} = yo;
|
||||
console.log('yo', yo);
|
||||
<span className="variable" >
|
||||
let Variable = (yo) => do {
|
||||
let {nodeValue, variableName} = yo
|
||||
;<span className="variable" >
|
||||
<span className="name">{variableName}</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</span>
|
||||
|
@ -186,38 +176,25 @@ let Percentage = ({percentage}) =>
|
|||
|
||||
|
||||
|
||||
let NodeValue = ({data}) => do {
|
||||
let valeur = data == null ?
|
||||
'?'
|
||||
: ( R.is(Number)(data) ?
|
||||
Math.round(data)
|
||||
: ( data ? 'oui' : 'non')
|
||||
);
|
||||
|
||||
<span className={"value " + valeur}>←
|
||||
{valeur}
|
||||
</span>
|
||||
}
|
||||
|
||||
let Formula = ({explanation, nodeValue}) => do {
|
||||
<div className="form node" >
|
||||
<div>
|
||||
<span className="name">{expression}</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
// let Formula = ({explanation, nodeValue}) => do {
|
||||
// <div className="form node" >
|
||||
// <div>
|
||||
// <span className="name">{expression}</span>
|
||||
// <NodeValue data={nodeValue}/>
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
|
||||
let JSONView = ({o, rootKey}) => (
|
||||
<div className="json">
|
||||
<JSONTree
|
||||
getItemString={() => ''}
|
||||
theme={theme}
|
||||
hideRoot={true}
|
||||
shouldExpandNode={() => true}
|
||||
data={rootKey ? {[rootKey]: o} : o}
|
||||
/>
|
||||
</div>
|
||||
<div className="json">
|
||||
<JSONTree
|
||||
getItemString={() => ''}
|
||||
theme={theme}
|
||||
hideRoot={true}
|
||||
shouldExpandNode={() => true}
|
||||
data={rootKey ? {[rootKey]: o} : o}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
"Salariat ":
|
||||
" CDD ":
|
||||
" événements": "_"
|
||||
" motif": "saisonnier"
|
||||
" engagement employeur complément formation": "non"
|
||||
" durée contrat": "2"
|
||||
|
||||
" contrat aidé": "non"
|
||||
" salaire de base": "1481"
|
||||
" congés non pris": "3"
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react'
|
||||
import R from 'ramda'
|
||||
|
||||
export let NodeValue = ({data}) => do {
|
||||
let valeur = data == null ?
|
||||
'?'
|
||||
: ( R.is(Number)(data) ?
|
||||
Math.round(data)
|
||||
: ( data ? 'oui' : 'non')
|
||||
)
|
||||
|
||||
;<span className={"value " + valeur}>←
|
||||
{valeur}
|
||||
</span>
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
import {rules, findRuleByName, parentName} from './rules'
|
||||
import React from 'react'
|
||||
import {rules, findRuleByName} from './rules'
|
||||
import {completeVariableName, evaluateVariable, knownVariable} from './expressions'
|
||||
import R from 'ramda'
|
||||
import knownMecanisms from './known-mecanisms.yaml'
|
||||
import { Parser } from 'nearley'
|
||||
import Grammar from './grammar.ne'
|
||||
import variablesInDevelopment from './variablesInDevelopment.yaml'
|
||||
import {NodeValue} from './traverse-common-jsx'
|
||||
|
||||
|
||||
|
||||
let nearley = () => new Parser(Grammar.ParserRules, Grammar.ParserStart)
|
||||
|
||||
|
@ -71,7 +75,12 @@ let fillVariableNode = (rule, situationGate) => (parseResult) => {
|
|||
variableName,
|
||||
type: 'boolean | numeric',
|
||||
explanation: null,
|
||||
missingVariables: known ? [] : [variableName]
|
||||
missingVariables: known ? [] : [variableName],
|
||||
jsx:
|
||||
<span className="variable" >
|
||||
<span className="name">{variableName}</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +108,14 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
if (parseResult.category == 'calcExpression') {
|
||||
let
|
||||
filledExplanation = parseResult.explanation.map(
|
||||
R.when(R.propEq('category', 'variable'), fillVariableNode(rule, situationGate))
|
||||
R.cond([
|
||||
[R.propEq('category', 'variable'), fillVariableNode(rule, situationGate)],
|
||||
[R.propEq('category', 'value'), node =>
|
||||
R.assoc('jsx', <span className="value">
|
||||
{node.nodeValue}
|
||||
</span>)(node)
|
||||
]
|
||||
])
|
||||
),
|
||||
[{nodeValue: value1}, {nodeValue: value2}] = filledExplanation,
|
||||
operatorFunctionName = {
|
||||
|
@ -115,17 +131,34 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
|
||||
return {
|
||||
text: rawNode,
|
||||
nodeValue: nodeValue,
|
||||
nodeValue,
|
||||
category: 'calcExpression',
|
||||
type: 'numeric',
|
||||
explanation: filledExplanation
|
||||
explanation: filledExplanation,
|
||||
jsx:
|
||||
<div className="mecanism node" >
|
||||
<div>
|
||||
<span className="name">Éxpression de calcul</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</div>
|
||||
{filledExplanation[0].jsx}
|
||||
<span className="operator">{parseResult.operator}</span>
|
||||
{filledExplanation[1].jsx}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
if (parseResult.category == 'comparison') {
|
||||
//TODO mutualise code for 'comparison' & 'calclExpression'. Harmonise their names
|
||||
let
|
||||
filledExplanation = parseResult.explanation.map(
|
||||
R.when(R.propEq('category', 'variable'), fillVariableNode(rule, situationGate))
|
||||
R.cond([
|
||||
[R.propEq('category', 'variable'), fillVariableNode(rule, situationGate)],
|
||||
[R.propEq('category', 'value'), node =>
|
||||
R.assoc('jsx', <span className="value">
|
||||
{node.nodeValue}
|
||||
</span>)(node)
|
||||
]
|
||||
])
|
||||
),
|
||||
[{nodeValue: value1}, {nodeValue: value2}] = filledExplanation,
|
||||
comparatorFunctionName = {
|
||||
|
@ -140,13 +173,18 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
null
|
||||
: comparatorFunction(value1, value2)
|
||||
|
||||
|
||||
return {
|
||||
text: rawNode,
|
||||
nodeValue: nodeValue,
|
||||
category: 'comparison',
|
||||
type: 'boolean',
|
||||
explanation: filledExplanation
|
||||
explanation: filledExplanation,
|
||||
jsx:
|
||||
<div className="comparison node" >
|
||||
{filledExplanation[0].jsx}
|
||||
<span className="operator">{parseResult.operator}</span>
|
||||
{filledExplanation[1].jsx}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,13 +194,17 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
return {
|
||||
category: 'number',
|
||||
nodeValue: rawNode,
|
||||
type: 'numeric'
|
||||
type: 'numeric',
|
||||
jsx:
|
||||
<span className="number">
|
||||
{rawNode}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!R.is(Object)(rawNode)) {
|
||||
console.log('Cette donnée : ', rawNode)
|
||||
console.log('Cette donnée : ', rawNode) // eslint-disable-line no-console
|
||||
throw ' doit être un Number, String ou Object'
|
||||
}
|
||||
|
||||
|
@ -172,7 +214,7 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
v = rawNode[k]
|
||||
|
||||
if (k === "l'une de ces conditions") {
|
||||
return R.pipe(
|
||||
let result = R.pipe(
|
||||
R.unless(R.is(Array), () => {throw 'should be array'}),
|
||||
R.reduce( (memo, next) => {
|
||||
let {nodeValue, explanation} = memo,
|
||||
|
@ -193,6 +235,18 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
explanation: []
|
||||
}) // Reduce but don't use R.reduced to set the nodeValue : we need to treat all the nodes
|
||||
)(v)
|
||||
return {...result,
|
||||
jsx:
|
||||
<div className="mecanism node" >
|
||||
<div>
|
||||
<span className="name">{result.name}</span>
|
||||
<NodeValue data={result.nodeValue}/>
|
||||
</div>
|
||||
<ul>
|
||||
{result.explanation.map(item => <li>{item.jsx}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
if (k === 'toutes ces conditions') {
|
||||
return R.pipe(
|
||||
|
@ -220,12 +274,16 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
let treatNumericalLogicRec =
|
||||
R.ifElse(
|
||||
R.is(String),
|
||||
rate => ({
|
||||
rate => ({ //TODO unifier ce code
|
||||
nodeValue: transformPercentage(rate),
|
||||
type: 'numeric',
|
||||
category: 'percentage',
|
||||
percentage: rate,
|
||||
explanation: null
|
||||
explanation: null,
|
||||
jsx:
|
||||
<span className="rate" >
|
||||
<span className="name">{rate}</span>
|
||||
</span>
|
||||
}),
|
||||
R.pipe(
|
||||
R.unless(
|
||||
|
@ -261,7 +319,13 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
condition: conditionNode,
|
||||
conditionValue: conditionNode.nodeValue,
|
||||
type: 'boolean',
|
||||
explanation: childNumericalLogic
|
||||
explanation: childNumericalLogic,
|
||||
jsx: <span className="condition">
|
||||
{conditionNode.jsx}
|
||||
<span>
|
||||
---> {childNumericalLogic.jsx}
|
||||
</span>
|
||||
</span>
|
||||
}],
|
||||
}
|
||||
}, {
|
||||
|
@ -270,6 +334,18 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
name: "logique numérique",
|
||||
type: 'boolean || numeric', // lol !
|
||||
explanation: []
|
||||
}),
|
||||
node => ({...node,
|
||||
jsx:
|
||||
<div className="mecanism node" >
|
||||
<div>
|
||||
<span className="name">logique numérique</span>
|
||||
<NodeValue data={node.nodeValue}/>
|
||||
</div>
|
||||
<ul>
|
||||
{node.explanation.map(item => <li>{item.jsx}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
})
|
||||
))
|
||||
|
||||
|
@ -281,11 +357,15 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
//TODO gérer les taux historisés
|
||||
if (R.is(String)(v))
|
||||
return {
|
||||
type: 'numeric',
|
||||
category: 'percentage',
|
||||
type: 'numeric',
|
||||
percentage: v,
|
||||
nodeValue: transformPercentage(v),
|
||||
explanation: null
|
||||
explanation: null,
|
||||
jsx:
|
||||
<span className="rate" >
|
||||
<span className="name">{v}</span>
|
||||
</span>
|
||||
}
|
||||
else {
|
||||
let node = reTreat(v)
|
||||
|
@ -294,7 +374,8 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
category: 'percentage',
|
||||
percentage: node.nodeValue,
|
||||
nodeValue: node.nodeValue,
|
||||
explanation: node
|
||||
explanation: node,
|
||||
jsx: node.jsx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -322,7 +403,14 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
facteur
|
||||
//TODO limit: 'plafond'
|
||||
//TODO introduire 'prorata' ou 'multiplicateur', pour sémantiser les opérandes ?
|
||||
}
|
||||
},
|
||||
jsx:
|
||||
<div className="multiplication node" >
|
||||
{base.jsx}
|
||||
<span className="multiplicationSign">×</span>
|
||||
{rate && rate.jsx}
|
||||
{facteur && facteur.jsx}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,14 +418,25 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
let contenders = v.map(treat(situationGate, rule)),
|
||||
contenderValues = R.pluck('nodeValue')(contenders),
|
||||
stopEverything = R.contains(null, contenderValues),
|
||||
maxValue = R.max(...contenderValues)
|
||||
maxValue = R.max(...contenderValues),
|
||||
nodeValue = stopEverything ? null : maxValue
|
||||
|
||||
return {
|
||||
type: 'numeric',
|
||||
category: 'mecanism',
|
||||
name: 'le maximum de',
|
||||
nodeValue: stopEverything ? null : maxValue,
|
||||
explanation: contenders
|
||||
nodeValue,
|
||||
explanation: contenders,
|
||||
jsx:
|
||||
<div className="mecanism node" >
|
||||
<div>
|
||||
<span className="name">le maximum de</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</div>
|
||||
<ul>
|
||||
{contenders.map(item => <li>{item.jsx}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,14 +450,24 @@ let treatRuleRoot = (situationGate, rule) => R.evolve({ // -> Voilà les attribu
|
|||
|
||||
// 'cond' : Conditions d'applicabilité de la règle
|
||||
'non applicable si': value => {
|
||||
let child = treat(situationGate, rule)(value)
|
||||
let
|
||||
child = treat(situationGate, rule)(value),
|
||||
nodeValue = child.nodeValue
|
||||
return {
|
||||
category: 'ruleProp',
|
||||
rulePropType: 'cond',
|
||||
name: 'non applicable si',
|
||||
type: 'boolean',
|
||||
nodeValue: child.nodeValue,
|
||||
explanation: child
|
||||
explanation: child,
|
||||
jsx:
|
||||
<div className="ruleProp node" >
|
||||
<div>
|
||||
<span className="name">non applicable si</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</div>
|
||||
{ child.jsx }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
,
|
||||
|
@ -368,15 +477,25 @@ let treatRuleRoot = (situationGate, rule) => R.evolve({ // -> Voilà les attribu
|
|||
// note: pour certaines variables booléennes, ex. appartenance à régime Alsace-Moselle, la formule et le non applicable si se rejoignent
|
||||
// [n'importe quel mécanisme numérique] : multiplication || barème en taux marginaux || le maximum de || le minimum de || ...
|
||||
'formule': value => {
|
||||
let child = treat(situationGate, rule)(value)
|
||||
let
|
||||
child = treat(situationGate, rule)(value),
|
||||
nodeValue = child.nodeValue
|
||||
return {
|
||||
category: 'ruleProp',
|
||||
rulePropType: 'formula',
|
||||
name: 'formule',
|
||||
type: 'numeric',
|
||||
nodeValue: child.nodeValue,
|
||||
nodeValue: nodeValue,
|
||||
explanation: child,
|
||||
shortCircuit: R.pathEq(['non applicable si', 'nodeValue'], true)
|
||||
shortCircuit: R.pathEq(['non applicable si', 'nodeValue'], true),
|
||||
jsx:
|
||||
<div className="ruleProp node" >
|
||||
<div>
|
||||
<span className="name">formula</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</div>
|
||||
{ child.jsx }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
,
|
||||
|
|
Loading…
Reference in New Issue