[moteur] conciliation des mécanismes existants avec le nouveau parsing d'expressions
parent
1a9d112eca
commit
376e72fc47
|
@ -1,4 +1,4 @@
|
|||
- Cotisation: Indemnité compensatrice congés payés simplifiée
|
||||
- Cotisation: simplifiée
|
||||
attache: Salariat . CDD
|
||||
non applicable si:
|
||||
l'une de ces conditions:
|
||||
|
@ -14,7 +14,7 @@
|
|||
assiette: salaire de base
|
||||
taux: 10%
|
||||
# prorata: congés non pris / 25
|
||||
prorata: 0.12 # 3/25
|
||||
# prorata: 0.12 # 3/25
|
||||
#
|
||||
# - description: Méthode "maintien du salaire"
|
||||
# note: Cette méthode sera le plus souvent favorable au salarié lorsque celui-ci a bénéficié d’une augmentation de salaire.
|
||||
|
@ -27,7 +27,7 @@
|
|||
# # Comment ?
|
||||
# # mensuel / nombre moyen de jours ouvrés par an
|
||||
|
||||
- 789
|
||||
- 80
|
||||
|
||||
notes: |
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ rules:
|
|||
no-console: 1
|
||||
no-global-assign: 0
|
||||
no-unsafe-negation: 0
|
||||
no-undef: 0
|
||||
no-undef: 1
|
||||
|
||||
parser: babel-eslint
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import './CDD.css'
|
||||
import IntroCDD from './IntroCDD'
|
||||
import Results from './Results'
|
||||
|
@ -8,43 +8,42 @@ import './conversation/conversation.css'
|
|||
import {START_CONVERSATION} from '../actions'
|
||||
import Aide from './Aide'
|
||||
|
||||
|
||||
let situationSelector = formValueSelector('conversation')
|
||||
|
||||
@reduxForm(
|
||||
{form: 'conversation', destroyOnUnmount: false}
|
||||
)
|
||||
@connect(state => ({
|
||||
@reduxForm({form: 'conversation', destroyOnUnmount: false})
|
||||
@connect(
|
||||
state => ({
|
||||
situation: variableName => situationSelector(state, variableName),
|
||||
steps: state.steps,
|
||||
themeColours: state.themeColours,
|
||||
analysedSituation: state.analysedSituation
|
||||
}), dispatch => ({
|
||||
startConversation: () => dispatch({type: START_CONVERSATION})
|
||||
}))
|
||||
analysedSituation: state.analysedSituation,
|
||||
}),
|
||||
dispatch => ({
|
||||
startConversation: () => dispatch({type: START_CONVERSATION}),
|
||||
}),
|
||||
)
|
||||
export default class CDD extends Component {
|
||||
componentDidMount() {
|
||||
this.props.startConversation()
|
||||
}
|
||||
render() {
|
||||
|
||||
let {steps} = this.props
|
||||
|
||||
let conversation = steps.map(step =>
|
||||
<step.component key={step.name} {...step}/>
|
||||
)
|
||||
let conversation = steps.map(step => (
|
||||
<step.component key={step.name} {...step} />
|
||||
))
|
||||
|
||||
return (
|
||||
<div id="sim">
|
||||
<IntroCDD />
|
||||
<div id="conversation">
|
||||
<section id="questions-answers">
|
||||
{conversation}
|
||||
</section>
|
||||
<Aide />
|
||||
</div>
|
||||
<Results {...this.props}/>
|
||||
</div>
|
||||
<div id="sim">
|
||||
<IntroCDD />
|
||||
<div id="conversation">
|
||||
<section id="questions-answers">
|
||||
{conversation}
|
||||
</section>
|
||||
<Aide />
|
||||
</div>
|
||||
<Results {...this.props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,14 +85,12 @@
|
|||
border: 1px solid black;
|
||||
background: #d5911a
|
||||
}
|
||||
.expression {
|
||||
|
||||
.mecanism li {
|
||||
margin-bottom: .6em;
|
||||
}
|
||||
.expression > div > .name {
|
||||
padding: 0 1em;
|
||||
border: 1px solid black;
|
||||
background: #6666ea;
|
||||
}
|
||||
|
||||
|
||||
#rule-rules .value {
|
||||
padding-left: 1em;
|
||||
font-weight: bold;
|
||||
|
@ -111,6 +109,12 @@
|
|||
background: #6666ea;
|
||||
}
|
||||
|
||||
.comparison .name {
|
||||
padding: 0 1em;
|
||||
border: 1px solid black;
|
||||
background: #407ee7;
|
||||
}
|
||||
|
||||
.rate .name {
|
||||
padding: 0 1em;
|
||||
border: 1px solid black;
|
||||
|
@ -123,3 +127,7 @@
|
|||
vertical-align: sub;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.json {
|
||||
font-size: 60%;
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ let testingSituationGate = v => R.path(v.split('.'))(
|
|||
"Salariat ":{
|
||||
" CDD ":{
|
||||
" événements": "_",
|
||||
" motif":"usage",
|
||||
" motif":"saisonnier",
|
||||
" engagement employeur complément formation":"non",
|
||||
" durée contrat":"2"
|
||||
" durée contrat": 2
|
||||
},
|
||||
" contrat aidé":"non",
|
||||
" salaire de base": 1481,
|
||||
|
@ -118,9 +118,6 @@ let RuleProp = ({nodeValue, explanation, name}) =>
|
|||
{
|
||||
explanation.category == 'mecanism' && <Mecanism {...explanation}/>
|
||||
}
|
||||
{
|
||||
explanation.category == 'expression' && <Expression {...explanation}/>
|
||||
}
|
||||
</div>
|
||||
|
||||
let Mecanism = ({nodeValue, name, explanation}) =>
|
||||
|
@ -131,9 +128,12 @@ let Mecanism = ({nodeValue, name, explanation}) =>
|
|||
</div>
|
||||
{R.contains(name)(["l'une de ces conditions", 'toutes ces conditions']) &&
|
||||
<ul>
|
||||
{explanation.map(item => <li key={item.expression + item.name}>
|
||||
{item.category == 'expression' ?
|
||||
<Expression {...item} /> : <Mecanism {...item} />
|
||||
{explanation.map(item => <li key={item.variableName + item.name}>
|
||||
{item.category == 'variable' ?
|
||||
<Variable {...item} />
|
||||
: item.category == 'comparison' ?
|
||||
<Comparison {...item} />
|
||||
: <Mecanism {...item} />
|
||||
}
|
||||
</li>)}
|
||||
</ul>
|
||||
|
@ -166,22 +166,25 @@ let Variable = ({nodeValue, variableName}) =>
|
|||
</span>
|
||||
|
||||
|
||||
let Comparison = ({nodeValue, text}) =>
|
||||
<span className="comparison" >
|
||||
<span className="name">{text}</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let Percentage = ({percentage}) =>
|
||||
<span className="rate" >
|
||||
<span className="name">{percentage}</span>
|
||||
</span>
|
||||
|
||||
|
||||
let Expression = ({nodeValue, expression}) =>
|
||||
<div className="expression node" >
|
||||
<div>
|
||||
<span className="name">{expression}</span>
|
||||
<NodeValue data={nodeValue}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
let NodeValue = ({data}) => do {
|
||||
console.log('NodeValue', data)
|
||||
let valeur = data == null ?
|
||||
'?'
|
||||
: ( R.is(Number)(data) ?
|
||||
|
@ -203,13 +206,17 @@ let Formula = ({explanation, nodeValue}) => do {
|
|||
</div>
|
||||
}
|
||||
|
||||
let JSONView = ({o, rootKey}) =>
|
||||
<JSONTree
|
||||
getItemString={() => ''}
|
||||
theme={theme}
|
||||
hideRoot={true}
|
||||
shouldExpandNode={() => true}
|
||||
data={rootKey ? {[rootKey]: o} : o} />
|
||||
let JSONView = ({o, rootKey}) => (
|
||||
<div className="json">
|
||||
<JSONTree
|
||||
getItemString={() => ''}
|
||||
theme={theme}
|
||||
hideRoot={true}
|
||||
shouldExpandNode={() => true}
|
||||
data={rootKey ? {[rootKey]: o} : o}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -120,7 +120,11 @@ export let knownVariable = (situationGate, variableName) =>
|
|||
|| situationGate(parentName(variableName)) != null
|
||||
// pour 'usage', 'motif' ( le parent de 'usage') = 'usage'
|
||||
|
||||
export let evaluateVariable = (situationGate, variableName) => {
|
||||
let value = situationGate(variableName)
|
||||
|
||||
export let evaluateVariable = (situationGate, variableName) => //console.log('variableName', variableName, situationGate(parentName(variableName))) ||
|
||||
situationGate(variableName) == 'oui'
|
||||
|| situationGate(parentName(variableName)) == nameLeaf(variableName)
|
||||
return R.is(Number)(value)
|
||||
? value
|
||||
: value == 'oui' ||
|
||||
situationGate(parentName(variableName)) == nameLeaf(variableName)
|
||||
}
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
@{% function buildNode(type, d){return ({nodeType: type, explanation: d})} %}
|
||||
|
||||
main ->
|
||||
CalcExpression {% id %}
|
||||
| Variable {% id %}
|
||||
| ModifiedVariable {% id %}
|
||||
| Comparison {% id %}
|
||||
|
||||
Comparison -> Comparable _ ComparisonOperator _ Comparable {% d => ({nodeType: 'Comparison', operator: d[2][0], explanation: [d[0], d[4]]}) %}
|
||||
Comparison -> Comparable _ ComparisonOperator _ Comparable {% d => ({
|
||||
category: 'comparison',
|
||||
type: 'boolean',
|
||||
operator: d[2][0],
|
||||
explanation: [d[0], d[4]]
|
||||
}) %}
|
||||
|
||||
Comparable -> (int | CalcExpression | Variable) {% d => d[0][0] %}
|
||||
|
||||
ComparisonOperator -> ">" | "<" | ">=" | "<=" | "="
|
||||
|
||||
ModifiedVariable -> Variable _ Modifier {% d => ({nodeType: 'ModifiedVariable', modifier: d[2], variable: d[0] }) %}
|
||||
ModifiedVariable -> Variable _ Modifier {% d => ({category: 'modifiedVariable', modifier: d[2], variable: d[0] }) %}
|
||||
|
||||
Modifier -> "[" TemporalModifier "]" {% d =>d[1][0] %}
|
||||
|
||||
TemporalModifier -> "annuel" | "mensuel" | "jour ouvré" {% id %}
|
||||
|
||||
CalcExpression -> Term _ ArithmeticOperator _ Term {% d => ({
|
||||
nodeType: 'CalcExpression',
|
||||
category: 'calcExpression',
|
||||
operator: d[2],
|
||||
explanation: [d[0], d[4]],
|
||||
type: 'numeric'
|
||||
|
@ -38,7 +41,7 @@ ArithmeticOperator -> "+" {% id %}
|
|||
|
||||
|
||||
Variable -> VariableFragment (_ Dot _ VariableFragment {% d => d[3] %}):* {% d => ({
|
||||
nodeType: 'Variable',
|
||||
category: 'variable',
|
||||
fragments: [d[0], ...d[1]],
|
||||
type: 'numeric | boolean'
|
||||
}) %}
|
||||
|
@ -54,4 +57,4 @@ Dot -> [\.] {% d => null %}
|
|||
_ -> [\s] {% d => null %}
|
||||
|
||||
|
||||
int -> [0-9]:+ {% d => ({nodeType: 'value', value: d[0].join("")}) %}
|
||||
int -> [0-9]:+ {% d => ({category: 'value', nodeValue: +d[0].join("")}) %}
|
||||
|
|
|
@ -7,7 +7,6 @@ import Grammar from './grammar.ne'
|
|||
|
||||
let nearley = () => new Parser(Grammar.ParserRules, Grammar.ParserStart)
|
||||
|
||||
console.log('a', nearley().feed('allez on essaie plusieurs combinaisons accentuées'))
|
||||
/*
|
||||
Dans ce fichier, les règles YAML sont parsées.
|
||||
Elles expriment un langage orienté expression, les expressions étant
|
||||
|
@ -23,8 +22,8 @@ let selectedRules = rules.filter(rule =>
|
|||
[
|
||||
'CIF CDD',
|
||||
'fin de contrat',
|
||||
// 'majoration chômage CDD',
|
||||
// 'Indemnité compensatrice congés payés simplifiée'
|
||||
'majoration chômage CDD',
|
||||
'simplifiée'
|
||||
]
|
||||
)
|
||||
)
|
||||
|
@ -63,6 +62,25 @@ par exemple ainsi : https://github.com/Engelberg/instaparse#transforming-the-tre
|
|||
|
||||
*/
|
||||
|
||||
let fillVariableNode = (rule, situationGate) => (parseResult) => {
|
||||
let
|
||||
{fragments} = parseResult,
|
||||
variablePartialName = fragments.join(' . '),
|
||||
variableName = completeVariableName(rule, variablePartialName),
|
||||
known = knownVariable(situationGate, variableName),
|
||||
nodeValue = !known ? null : evaluateVariable(situationGate, variableName)
|
||||
|
||||
return {
|
||||
nodeValue,
|
||||
category: 'variable',
|
||||
fragments: fragments,
|
||||
variableName,
|
||||
type: 'boolean | numeric',
|
||||
explanation: null,
|
||||
missingVariables: known ? [] : [variableName]
|
||||
}
|
||||
}
|
||||
|
||||
let treat = (situationGate, rule) => rawNode => {
|
||||
|
||||
if (R.is(String)(rawNode)) {
|
||||
|
@ -72,45 +90,47 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
Cet objet est alors rebalancé à 'treat'.
|
||||
*/
|
||||
|
||||
let [parseResults, ...additionnalResults] = nearley().feed(rawNode).results
|
||||
let [parseResult, ...additionnalResults] = nearley().feed(rawNode).results
|
||||
|
||||
if (additionnalResults && additionnalResults.length > 0) throw "Attention ! L'expression <" + rawNode + '> ne peut être traitée de façon univoque'
|
||||
|
||||
if (!R.contains(parseResults.nodeType)(['Variable', 'CalcExpression', 'ModifiedVariable', 'Comparison']))
|
||||
if (!R.contains(parseResult.category)(['variable', 'calcExpression', 'modifiedVariable', 'comparison']))
|
||||
throw "Attention ! Erreur de traitement de l'expression : " + rawNode
|
||||
|
||||
if (parseResults.nodeType == 'Variable') {
|
||||
if (parseResult.category == 'variable')
|
||||
return fillVariableNode(rule, situationGate)(parseResult, rawNode)
|
||||
|
||||
if (parseResult.category == 'comparison') {
|
||||
|
||||
let
|
||||
variablePartialName = parseResults.fragments.join(' . '),
|
||||
variableName = completeVariableName(rule, variablePartialName),
|
||||
known = knownVariable(situationGate, variableName)
|
||||
debugger
|
||||
// variablePartialName = parseResult.fragments.join(' . '),
|
||||
// variableName = completeVariableName(rule, variablePartialName),
|
||||
// known = knownVariable(situationGate, variableName)
|
||||
filledExplanation = parseResult.explanation.map(
|
||||
R.when(R.propEq('category', 'variable'), fillVariableNode(rule, situationGate))
|
||||
),
|
||||
[{nodeValue: value1}, {nodeValue: value2}] = filledExplanation,
|
||||
comparatorFunctionName = {
|
||||
'<': 'lt',
|
||||
'<=': 'lte',
|
||||
'>': 'gt',
|
||||
'>=': 'gte'
|
||||
//TODO '='
|
||||
}[parseResult.operator],
|
||||
comparatorFunction = R[comparatorFunctionName],
|
||||
nodeValue = value1 == null || value2 == null ?
|
||||
null
|
||||
: comparatorFunction(value1, value2)
|
||||
|
||||
|
||||
return {
|
||||
expression: rawNode,
|
||||
nodeValue: !known ? null : evaluateVariable(situationGate, variableName),
|
||||
category: 'expression',
|
||||
type: 'boolean | numeric',
|
||||
explanation: null,
|
||||
missingVariables: known ? [] : [variableName]
|
||||
text: rawNode,
|
||||
nodeValue: nodeValue,
|
||||
category: 'comparison',
|
||||
type: 'boolean',
|
||||
explanation: filledExplanation
|
||||
}
|
||||
}
|
||||
// if (parseResults.nodeType == 'CalcExpression') {
|
||||
//
|
||||
// let
|
||||
// variablePartialName = parseResults.fragments.join(' . '),
|
||||
// variableName = completeVariableName(rule, variablePartialName),
|
||||
// known = knownVariable(situationGate, variableName)
|
||||
//
|
||||
// return {
|
||||
// expression: rawNode,
|
||||
// nodeValue: situationGate(baseVariableName),
|
||||
// category: 'expression',
|
||||
// type: 'boolean | numeric',
|
||||
// explanation: null,
|
||||
// missingVariables: known ? [] : [variableName]
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
//TODO C'est pas bien ça. Devrait être traité par le parser plus haut !
|
||||
|
@ -123,7 +143,6 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
}
|
||||
|
||||
if (!R.is(Object)(rawNode)) {
|
||||
console.log('This node : ', rawNode)
|
||||
throw ' should be string or object'
|
||||
}
|
||||
|
||||
|
@ -195,15 +214,15 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
),
|
||||
R.toPairs,
|
||||
R.reduce( (memo, [condition, consequence]) => {
|
||||
let {nodeValue, explanation} = memo,
|
||||
[variableName, evaluation] = recognizeExpression(rule, condition),
|
||||
let
|
||||
{nodeValue, explanation} = memo,
|
||||
conditionNode = treat(situationGate, rule)(condition), // can be a 'comparison', a 'variable', TODO a 'negation'
|
||||
childNumericalLogic = treatNumericalLogicRec(consequence),
|
||||
known = knownVariable(situationGate, variableName),
|
||||
nextNodeValue = !known ?
|
||||
nextNodeValue = conditionNode.nodeValue == null ?
|
||||
// Si la proposition n'est pas encore résolvable
|
||||
null
|
||||
// Si la proposition est résolvable
|
||||
: evaluation(situationGate) ?
|
||||
: conditionNode.nodeValue == true ?
|
||||
// Si elle est vraie
|
||||
childNumericalLogic.nodeValue
|
||||
// Si elle est fausse
|
||||
|
@ -215,16 +234,15 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
: nodeValue !== false ?
|
||||
nodeValue // l'une des propositions renvoie déjà une valeur numérique donc différente de false
|
||||
: nextNodeValue,
|
||||
// condition: condition,
|
||||
explanation: [...explanation, {
|
||||
nodeValue: nextNodeValue,
|
||||
category: 'condition',
|
||||
condition,
|
||||
conditionValue: evaluation(situationGate),
|
||||
text: condition,
|
||||
condition: conditionNode,
|
||||
conditionValue: conditionNode.nodeValue,
|
||||
type: 'boolean',
|
||||
explanation: childNumericalLogic
|
||||
}],
|
||||
missingVariables: known ? [] : [variableName]
|
||||
}
|
||||
}, {
|
||||
nodeValue: false,
|
||||
|
@ -262,17 +280,25 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
}
|
||||
|
||||
if (k === 'multiplication') {
|
||||
|
||||
let base = v['assiette'],
|
||||
parsed = nearley().feed(base),
|
||||
baseVariableFound = parsed.results[0].nodeType == 'Variable',
|
||||
variablePartialName = baseVariableFound && parsed.results[0].fragments.join(' . '),
|
||||
baseVariableFound = parsed.results[0].category == 'variable'
|
||||
if (!baseVariableFound) throw "L'assiette d'une multiplication doit pour le moment être une variable"
|
||||
|
||||
let
|
||||
variablePartialName = parsed.results[0].fragments.join(' . '),
|
||||
baseVariableName = completeVariableName(rule, variablePartialName),
|
||||
baseValue = situationGate(baseVariableName),
|
||||
rateNode = treat(situationGate, rule)({taux: v['taux']}),
|
||||
rate = rateNode.nodeValue
|
||||
|
||||
return {
|
||||
nodeValue: ((baseValue && rate) || null) && +baseValue * rate, // null * 6 = 0 :-o
|
||||
nodeValue: (rate === 0 || rate === false || baseValue === 0) ?
|
||||
0
|
||||
: (rate == null || baseValue == null) ?
|
||||
null
|
||||
: +baseValue * rate,
|
||||
category: 'mecanism',
|
||||
name: 'multiplication',
|
||||
type: 'numeric',
|
||||
|
@ -309,7 +335,6 @@ let treat = (situationGate, rule) => rawNode => {
|
|||
}
|
||||
}
|
||||
|
||||
console.log('rawNode', rawNode)
|
||||
throw "Le mécanisme qui vient d'être loggué est inconnu !"
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue