🔨 ⚙️ Première version des tests (exemples) sur /règle

Les 4 éléments du CDD sont testées.
Il reste du boulot, voir le TODO des tests de majoration chômage
pull/2/head
mama 2017-05-10 16:09:36 +02:00
parent 819e9b04af
commit 75354efdc5
22 changed files with 457 additions and 181 deletions

View File

@ -19,3 +19,21 @@
références:
Code du travail - Article L6322-37 : https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000022234996&cidTexte=LEGITEXT000006072050
exemples:
- nom: SMIC
situation:
assiette cotisations sociales: 1480
valeur attendue: 14.8
- nom: salaire médian
situation:
motif . classique . usage: oui
assiette cotisations sociales: 2300
valeur attendue: 23
- nom: motif saisonnier -> non applicable
situation:
motif . classique . saisonnier: oui
assiette cotisations sociales: 2300
valeur attendue: null

View File

@ -16,7 +16,8 @@
# TODO aspect temporel
# L'indemnité est versée à la fin du contrat, sauf si le CDD se poursuit par un CDI.
#TODO avoir un vrai mécanisme de calcul temporel pour faire les conversions
#TODO cette formule pourrait être clarifiée,
# probablement grâce à un vrai mécanisme de calcul temporel pour faire les conversions
formule:
le maximum de:
- description: Méthode "du dixième"
@ -46,6 +47,30 @@
facteur: 1 / 21
exemples:
- nom: pas de congés non pris
situation:
salaire brut: 2300
prime fin de contrat: 0
congés non pris: 0
durée contrat: 12
valeur attendue: 0
- nom: 10 jours non pris
situation:
salaire brut: 2300
prime fin de contrat: 0
congés non pris: 10
durée contrat: 12
valeur attendue: 92
- nom: 3 jours non pris
situation:
salaire brut: 2300
prime fin de contrat: 0
congés non pris: 3
durée contrat: 6
valeur attendue: 54.76
notes: |
À noter, la loi El Khomri modifie l'article L3141-12:

View File

@ -10,7 +10,6 @@
- Dans les faits, les CDD Senior perçoivent une indemnité dun montant équivalent à lindemnité de précarité : [line](https://www.easycdd.com/LEGISLATION-CDD/Fin-ou-rupture-du-contrat-CDD/La-prime-de-precarite/La-prime-de-precarite-n-est-pas-due-si)
non applicable si:
une de ces conditions:
# Evènements particuliers
@ -37,6 +36,19 @@
assiette: salaire brut
taux: 10%
exemples:
- nom: salaire médian
situation:
salaire brut: 2300
valeur attendue: 230
- nom: CDD d'usage -> non applicable
situation:
motif . classique . usage: oui
événement . refus CDI avantageux: oui
salaire brut: 2300
valeur attendue: null
références:
Code du travail - Article L1243-8: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000006189459&cidTexte=LEGITEXT000006072050

View File

@ -25,6 +25,25 @@
durée contrat <= 3: 0.5%
exemples:
- nom: salaire médian, CDD d'usage, contrat de 2 mois
situation:
assiette cotisations sociales: 2300
motif . classique . accroissement activité: oui
durée contrat: 1
valeur attendue: 69
#TODO on ne peut aujourd'hui tester 'classique . usage' : l'évaluation n'aura pas la valeur de la première branche de la logique numérique, motif . classique . accroissement activité, et va donc s'arrêter en renvoyant un 'null'
# solution possible : un mode d'évaluation 'shallow' ou non renseigné = faux (null -> false)
- nom: durée de contrat de 4 mois -> non applicable
situation:
assiette cotisations sociales: 2300
durée contrat: 4
valeur attendue: null
références:
La mojoration de la contribution chômage: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/lassurance-chomage-et-lags/la-majoration-de-la-contribution.html

View File

@ -32,6 +32,7 @@
références:
Code du travail - Article L1242-2: https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=714D2E2B814371F4F1D5AA88472CD621.tpdila20v_1?idArticle=LEGIARTI000033024658&cidTexte=LEGITEXT000006072050&dateTexte=20170420
- espace: contrat salarié . CDD . motif . classique
nom: saisonnier
titre: Saisonnier

View File

@ -3,7 +3,7 @@ import {connect} from 'react-redux'
import {findRuleByDottedName} from '../engine/rules'
import './Aide.css'
import {EXPLAIN_VARIABLE} from '../actions'
import References from './References'
import References from './rule/References'
import marked from '../engine/marked'
@connect(

View File

@ -25,7 +25,7 @@ export let AttachDictionary = dictionary => Decorated =>
render(){
let {explanation, term} = this.state
return (
<div className="dictionaryWrapper">
<div style={{display: 'inline-block'}} className="dictionaryWrapper">
<Decorated ref={decorated => this.decorated = decorated} {...this.props} explain={this.explain}/>
{explanation &&
<div className="dictionaryPanelWrapper" onClick={() => this.setState({term: null, explanation: null})}>

View File

@ -2,7 +2,6 @@ import React, {Component} from 'react'
import './HomeSyso.css'
import {searchRules, encodeRuleName} from '../engine/rules.js'
import {Link} from 'react-router-dom'
import '../components/Rule.css'
import R from 'ramda'
export default class Home extends Component {

View File

@ -1,139 +0,0 @@
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import {decodeRuleName} from '../engine/rules.js'
import './Rule.css'
import JSONTree from 'react-json-tree'
import R from 'ramda'
import PageTypeIcon from './PageTypeIcon'
import {connect} from 'react-redux'
import mockSituation from '../engine/mockSituation.yaml'
import {START_CONVERSATION} from '../actions'
import classNames from 'classnames'
import possiblesDestinataires from '../../règles/ressources/destinataires/destinataires.yaml'
import {capitalise0} from '../utils'
import knownMecanisms from '../engine/known-mecanisms.yaml'
import marked from '../engine/marked'
import References from './References'
import {AttachDictionary} from './AttachDictionary'
import {analyseSituation} from '../engine/traverse'
import {formValueSelector} from 'redux-form'
// situationGate function useful for testing :
let testingSituationGate = v => // eslint-disable-line no-unused-vars
R.path(v.split('.'))(mockSituation)
@connect(
state => ({
situationGate: name => formValueSelector('conversation')(state, name),
form: state.form
}),
dispatch => ({
startConversation: rootVariable => dispatch({type: START_CONVERSATION, rootVariable}),
})
)
export default class Rule extends Component {
render() {
let {
match: {params: {name: encodedName}},
situationGate,
form
} = this.props,
name = decodeRuleName(encodedName)
let rule = analyseSituation(name)(situationGate)
console.log('rule', rule)
// if (!rule) {
// this.props.router.push('/404')
// return null
// }
let
situationExists = !R.isEmpty(form)
let destinataire = R.path([rule.type, 'destinataire'])(rule),
destinataireData = possiblesDestinataires[destinataire]
return (
<div id="rule">
<PageTypeIcon type="comprendre"/>
<h1>
<span className="rule-type">{rule.type}</span>
<span className="rule-name">{capitalise0(name)}</span>
</h1>
<section id="rule-meta">
<div id="meta-paragraph">
<p>
{rule.description}
</p>
</div>
<div id="destinataire">
<h2>Destinataire</h2>
{!destinataireData ?
<p>Non renseigné</p>
: <div>
<a href={destinataireData.lien} target="_blank">
{destinataireData.image &&
<img src={require('../../règles/ressources/destinataires/' + destinataireData.image)} /> }
{!destinataireData.image &&
<div id="calligraphy">{destinataire}</div>
}
</a>
{destinataireData.nom && <div id="destinataireName">{destinataireData.nom}</div>}
</div>
}
</div>
<div>
<h2>Références</h2>
{this.renderReferences(rule)}
</div>
</section>
<Algorithm {...{rule, situationExists}}/>
</div>
)
}
renderReferences({'références': refs}) {
if (!refs) return <p>Cette règle manque de références.</p>
return <References refs={refs}/>
}
}
@AttachDictionary(knownMecanisms)
class Algorithm extends React.Component {
state = {
showValues: false
}
render(){
let {rule, situationExists, explain} = this.props,
showValues = situationExists && this.state.showValues
return (
<div id="algorithm">
<section id="rule-rules" className={classNames({showValues})}>
{ do {
// TODO ce let est incompréhensible !
let [,cond] =
R.toPairs(rule).find(([,v]) => v && v.rulePropType == 'cond') || []
cond != null &&
<section id="declenchement">
<h2>Conditions de déclenchement</h2>
{cond.jsx}
</section>
}}
<section id="formule">
<h2>Calcul</h2>
{rule['formule'].jsx}
</section>
</section>
{situationExists && <div>
<button id="showValues" onClick={() => this.setState({showValues: !this.state.showValues})}>
<i className="fa fa-rocket" aria-hidden="true"></i> &nbsp;{!showValues ? 'Injecter votre situation' : 'Cacher votre situation'}
</button>
</div>}
</div>
)
}
}

View File

@ -59,8 +59,8 @@ export default class extends React.Component {
sim = path =>
R.path(R.unless(R.is(Array), R.of)(path))(this.rule.simulateur || {}),
reinitalise = () => {
this.props.resetForm(name);
this.props.startConversation(name);
this.props.resetForm(this.name);
this.props.startConversation(this.name);
}

View File

@ -0,0 +1,36 @@
import React from 'react'
import classNames from 'classnames'
import R from 'ramda'
import {AttachDictionary} from '../AttachDictionary'
import knownMecanisms from 'Engine/known-mecanisms.yaml'
import marked from 'Engine/marked'
@AttachDictionary(knownMecanisms)
export default class Algorithm extends React.Component {
state = {
showValues: false
}
render(){
let {traversedRule: rule, showValues} = this.props
return (
<div id="algorithm">
<section id="rule-rules" className={classNames({showValues})}>
{ do {
// TODO ce let est incompréhensible !
let [,cond] =
R.toPairs(rule).find(([,v]) => v && v.rulePropType == 'cond') || []
cond != null &&
<section id="declenchement">
<h2>Conditions de déclenchement</h2>
{cond.jsx}
</section>
}}
<section id="formule">
<h2>Calcul</h2>
{rule['formule'].jsx}
</section>
</section>
</div>
)
}
}

View File

@ -0,0 +1,67 @@
#examples {
min-width: 30%;
}
#examples h2.subtitled {
margin-bottom: .1em;
}
#examples .subtitle {
font-size: 85%;
font-style: italic;
margin-top: 0.3em;
margin-bottom: 1.6em
}
#injectSituation {
background: rgb(74, 137, 220);
color: white;
border: none;
font-size: 100%;
padding: .3em .6em;
margin-bottom: .6em;
}
#injectSituation.selected {
font-weight: bold;
}
#examples ul {
padding-left: 0;
list-style-type: none;
}
#examples .example {
font-weight: 500;
margin: .6em;
cursor: pointer;
}
#examples .example.selected .name {
font-weight: bold;
}
#examples i {
margin-right: .6em;
}
#examples .example i {
font-size: 200%;
vertical-align: sub;
}
#examples .example:not(.ok) {
color: #e74c3c;
}
#examples .example .ko {
font-size: 80%;
color: #666;
}
#examples .expected {
font-weight: 700;
color: #031bd6;
}
#examples .example.ok i {
color: #2ecc71
}

View File

@ -0,0 +1,89 @@
import React, { Component } from "react"
import R from "ramda"
import classNames from "classnames"
import {
decodeRuleName,
findRuleByName,
disambiguateRuleReference
} from "Engine/rules.js"
import { analyseSituation } from "Engine/traverse"
import "./Examples.css"
export default class Examples extends Component {
runExamples() {
let { rule } = this.props,
exemples = rule.exemples || []
return exemples.map(ex => {
// les variables dans les tests peuvent être exprimées relativement à l'espace de nom de la règle,
// comme dans sa formule
let exempleSituation = R.pipe(
R.toPairs,
R.map(([k, v]) => [disambiguateRuleReference(rule, k), v]),
R.fromPairs
)(ex.situation)
let runExemple = analyseSituation(rule.name)(v => exempleSituation[v]),
exempleCalculatedValue = runExemple["non applicable si"] &&
runExemple["non applicable si"].nodeValue
? null
: runExemple.formule.nodeValue
return {
...ex,
ok: Math.abs( ex['valeur attendue'] - exempleCalculatedValue ) < .1, //TODO on peut sûrement faire mieux...
rule: runExemple
}
})
}
render() {
let examples = this.runExamples(),
focusedExample = R.path(['focusedExample', 'nom'])(this.props),
{clearInjection, inject, situationExists , showValues} = this.props
return (
<div id="examples">
<h2 className="subtitled">Examples de calcul</h2>
<p className="subtitle">Cliquez sur un exemple pour le visualiser</p>
{situationExists && <div>
<button
className={classNames({selected: !focusedExample && showValues})}
id="injectSituation"
onClick={() => showValues ? clearInjection(): inject()}>
<i className="fa fa-rocket" aria-hidden="true"></i> &nbsp;{focusedExample || !showValues ? 'Injecter votre situation' : 'Cacher votre situation'}
</button>
</div>
}
{R.isEmpty(examples) ?
<p><i className="fa fa-exclamation-triangle" aria-hidden="true"></i><em>Cette règle manque d'exemples...</em></p>
: <ul>{
examples.map(({nom, ok, rule, 'valeur attendue': expected}) =>
<li key={nom} className={classNames("example", {ok, selected: focusedExample == nom})}
onClick={() => focusedExample == nom ? clearInjection() : inject({nom, ok, rule})}
>
<span> {
ok ?
<i className="fa fa-check-circle" aria-hidden="true"></i>
: <i className="fa fa-times" aria-hidden="true"></i>
}</span>
<span className="name">{nom}</span>
{!ok &&
<div className="ko">
Ce test ne passe pas
{showValues && <span>
: la valeur attendue était {' '}
<span className="expected">{expected}</span>
</span>}
</div>
}
</li>
)
}
</ul>
}
</div>
)
}
}

View File

@ -1,7 +1,7 @@
import React from 'react'
import references from '../../règles/ressources/références/références.yaml'
import './References.css'
import R from 'ramda'
import references from 'Règles/ressources/références/références.yaml'
import './References.css'
export default ({refs}) => (
<ul className="references">
@ -18,7 +18,7 @@ export default ({refs}) => (
<span className="url">
{domain}
{refData.image &&
<img src={require('../../règles/ressources/références/' + refData.image)}/> }
<img src={require('Règles/ressources/références/' + refData.image)}/> }
</span>
</span>
<a href={link} target="_blank">

View File

@ -30,7 +30,7 @@
}
#rule h2 {
font-size: 100%;
font-size: 140%;
font-weight: 400;
border-bottom: 1px solid #4B4B66;
display: inline-block;
@ -105,27 +105,17 @@
#rule-rules .situationValue {
display: none;
padding-left: 1em;
font-weight: 600;
color: #4A89DC;
font-weight: 700;
color: #031bd6;
}
#rule-rules.showValues .situationValue {
display: inline;
}
#showValues {
background: rgb(74, 137, 220);
color: white;
border: none;
font-size: 100%;
padding: .4em;
margin: auto;
margin-left: 3em;
}
#algorithm {
margin-top: 3em;
width: 100%;
margin-right: 10em;
}
.dictionaryPanelWrapper {
position: fixed;
@ -170,11 +160,10 @@
#rule-rules section {
margin: 1em 0 1em 3em;
font-weight: 500;
font-size: 90%;
color: #444
}
#rule-rules section h2 {
font-size: 130%;
#rule-rules section > div {
font-size: 90%;
}
.node {
padding-left: 1em;
@ -350,3 +339,12 @@
font-size: 160%;
color: #3498db;
}
#rule-calc {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: baseline;
}

View File

@ -0,0 +1,120 @@
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import {connect} from 'react-redux'
import {formValueSelector} from 'redux-form'
import R from 'ramda'
import './Rule.css'
import PageTypeIcon from '../PageTypeIcon'
import {decodeRuleName, findRuleByName, disambiguateRuleReference} from 'Engine/rules.js'
import mockSituation from 'Engine/mockSituation.yaml'
import {analyseSituation} from 'Engine/traverse'
import {START_CONVERSATION} from '../../actions'
import possiblesDestinataires from 'Règles/ressources/destinataires/destinataires.yaml'
import {capitalise0} from '../../utils'
import References from './References'
import Algorithm from './Algorithm'
import Examples from './Examples'
// situationGate function useful for testing :
let testingSituationGate = v => // eslint-disable-line no-unused-vars
R.path(v.split('.'))(mockSituation)
@connect(
state => ({
situationGate: name => formValueSelector('conversation')(state, name),
form: state.form
}),
dispatch => ({
startConversation: rootVariable => dispatch({type: START_CONVERSATION, rootVariable}),
})
)
export default class Rule extends Component {
state = {
example: null, showValues: false
}
componentWillReceiveProps(nextProps){
let get = R.path(['match', 'params', 'name'])
if (get(nextProps) !== get(this.props))
this.setRule(get(nextProps))
}
setRule(name){
this.rule = analyseSituation(decodeRuleName(name))(this.props.situationGate)
}
componentWillMount(){
let {
match: {params: {name}},
situationGate
} = this.props
this.setRule(name)
}
render() {
// if (!rule) {
// this.props.router.push('/404')
// return null
// }
let
situationExists = !R.isEmpty(this.props.form)
let
{type, name, description} = this.rule,
destinataire = R.path([type, 'destinataire'])(this.rule),
destinataireData = possiblesDestinataires[destinataire]
return (
<div id="rule">
<PageTypeIcon type="comprendre"/>
<h1>
<span className="rule-type">{type}</span>
<span className="rule-name">{capitalise0(name)}</span>
</h1>
<section id="rule-meta">
<div id="meta-paragraph">
<p>
{description}
</p>
</div>
<div id="destinataire">
<h2>Destinataire</h2>
{!destinataireData ?
<p>Non renseigné</p>
: <div>
<a href={destinataireData.lien} target="_blank">
{destinataireData.image &&
<img src={require('Règles/ressources/destinataires/' + destinataireData.image)} /> }
{!destinataireData.image &&
<div id="calligraphy">{destinataire}</div>
}
</a>
{destinataireData.nom && <div id="destinataireName">{destinataireData.nom}</div>}
</div>
}
</div>
<div>
<h2>Références</h2>
{this.renderReferences(this.rule)}
</div>
</section>
<section id="rule-calc">
<Algorithm {...{traversedRule: R.path(['example', 'rule'])(this.state) || this.rule, showValues: this.state.showValues}}/>
<Examples
situationExists={situationExists}
rule={this.rule}
focusedExample={this.state.example}
showValues={this.state.showValues}
clearInjection={() => this.setState({example: null, showValues: false})}
inject={example => this.setState({example, showValues: true})}/>
</section>
</div>
)
}
renderReferences({'références': refs}) {
if (!refs) return <p>Cette règle manque de références.</p>
return <References refs={refs}/>
}
}

View File

@ -4,7 +4,7 @@ import './reset.css'
import {Link, Route, BrowserRouter as Router, Switch} from 'react-router-dom'
import HomeEmbauche from '../components/HomeEmbauche'
import HomeSyso from '../components/HomeSyso'
import Rule from '../components/Rule'
import Rule from '../components/rule/Rule'
import Route404 from '../components/Route404'
import Contact from '../components/Contact'
import Simulateur from '../components/Simulateur'

View File

@ -2,6 +2,7 @@
# La description peut être rédigée en markdown :-)
une de ces conditions:
type: boolean
description: |
C'est un `ou` logique.
@ -9,6 +10,7 @@ une de ces conditions:
Renvoie vrai si l'une des conditions est vraie.
toutes ces conditions:
type: boolean
description: |
C'est un `et` logique.
@ -17,6 +19,7 @@ toutes ces conditions:
Renvoie vrai si toutes les conditions vraies.
logique numérique:
type: numeric
description: |
Contient une liste de couples condition-conséquence.
@ -27,10 +30,12 @@ logique numérique:
Si aucune condition n'est vraie, alors ce mécanisme renvoie implicitement `non applicable` (ce qui peut se traduire par la valeur `0` si nous sommes dans un contexte numérique).
taux:
type: numeric
description: |
C'est tout simplement une valeur numérique exprimée en pourcentage.
multiplication:
type: numeric
description: |
C'est une multiplication un peu améliorée, très utile pour exprimer les cotisations.
@ -39,6 +44,7 @@ multiplication:
La multiplication peut être plafonnée : ce plafond sépare l'assiette en deux, et la partie au-dessus du plafond est tout simplement ignorée. Dans ce cas, elle se comporte comme une barème en taux marginaux à deux tranches, la deuxième au taux nul et allant de `plafond` à l'infini.
le maximum de:
type: numeric
description: |
Renvoie l'élément de la liste de propositions fournie qui la la plus grande valeur.
@ -47,6 +53,7 @@ le maximum de:
Il est conseillé de renseigner une description de chaque proposition par exemple quand elles représentent des méthodes de calcul alternatives parmi lesquelles il faut en choisir une.
somme:
type: numeric
description: |
C'est tout simplement la somme de chaque terme de la liste.
@ -71,12 +78,14 @@ non applicable si:
La formule de calcul peut donc être ignorée, quel que soit son montant.
barème:
type: numeric
description: |
C'est un barème en taux marginaux, mécanisme de calcul connu son utilisation dans le calcul de l'impôt sur le revenu.
L'assiette est décomposée en plusieurs tranches, qui sont multipliées par un taux spécifique.
Les tranches sont très souvent exprimées sous forme de facteurs (par exemple [1, 2, 4]) d'une variable que l'on appelle multiplicateur, par exemple le plafond de la sécurité sociale.
composantes:
type: numeric
description: |
Beaucoup de cotisations sont composées de deux parties qui partage la méthode de calcul mais diffèrent par des paramètres différents.

View File

@ -60,17 +60,23 @@ let fillVariableNode = (rule, situationGate) => (parseResult) => {
variablePartialName = fragments.join(' . '),
dottedName = disambiguateRuleReference(rule, variablePartialName),
variable = findRuleByDottedName(dottedName),
variableIsRule = variable.formule != null,
variableIsCalculable = variable.formule != null,
//TODO perf : mettre un cache sur les variables !
// On le fait pas pour l'instant car ça peut compliquer les fonctionnalités futures
// et qu'il n'y a aucun problème de perf aujourd'hui
parsedRule = variableIsRule && treatRuleRoot(
parsedRule = variableIsCalculable && treatRuleRoot(
situationGate,
variable
),
nodeValue = variableIsRule ? parsedRule.nodeValue : evaluateVariable(situationGate, dottedName, variable.format),
missingVariables = variableIsRule ? [] : (nodeValue == null ? [dottedName] : [])
situationValue = evaluateVariable(situationGate, dottedName, variable),
nodeValue = situationValue
!= null ? situationValue
: !variableIsCalculable
? null
: parsedRule.nodeValue,
explanation = parsedRule,
missingVariables = variableIsCalculable ? [] : (nodeValue == null ? [dottedName] : [])
return {
nodeValue,
@ -394,13 +400,14 @@ let treat = (situationGate, rule) => rawNode => {
}
if (k === 'taux') {
//TODO gérer les taux historisés
if (R.is(String)(v))
let reg = /^(\d+(\.\d+)?)\%$/
console.log('taux, v', v)
if (R.test(reg)(v))
return {
category: 'percentage',
type: 'numeric',
percentage: v,
nodeValue: transformPercentage(v),
nodeValue: R.match(reg)(v)[1]/100,
explanation: null,
jsx:
<span className="percentage" >
@ -409,7 +416,7 @@ let treat = (situationGate, rule) => rawNode => {
}
// Si c'est une liste historisée de pourcentages
// TODO revoir le test avant le bug de l'an 2100
else if ( R.all(R.test(/(19|20)\d\d(-\d\d)?(-\d\d)?/))(R.keys(v)) ) {
else if ( R.is(Array)(v) && R.all(R.test(/(19|20)\d\d(-\d\d)?(-\d\d)?/))(R.keys(v)) ) {
//TODO sélectionner la date de la simulation en cours
let lazySelection = R.first(R.values(v))
return {
@ -497,7 +504,7 @@ let treat = (situationGate, rule) => rawNode => {
name="multiplication"
value={nodeValue}
child={
<ul>
<ul className="properties">
<li key="assiette">
<span className="key">assiette: </span>
<span className="value">{assiette.jsx}</span>

View File

@ -20,10 +20,18 @@ let evaluateBottomUp = situationGate => startingFragments => {
}
export let evaluateVariable = (situationGate, variableName, format) => {
/* Evalue la valeur d'une variable
en utilisant la fonction situationGate qui donne accès à la situation courante*/
export let evaluateVariable = (situationGate, variableName, rule) => {
// test rec
let value = situationGate(variableName)
return format != null ?
(value == undefined ? null : value)
: evaluateBottomUp(situationGate)(splitName(variableName))
return rule.format != null ?
value
: !rule.formule ?
// c'est une variante, eg. motifs . classique . accroissement d'activité
evaluateBottomUp(situationGate)(splitName(variableName))
: rule.formule['une possibilité'] ?
evaluateBottomUp(situationGate)(splitName(variableName))
: value
}

View File

@ -1,5 +1,6 @@
var webpack = require('webpack'),
autoprefixer = require('autoprefixer'),
path = require('path'),
prodEnv = process.env.NODE_ENV == 'production' // eslint-disable-line no-undef
module.exports = {
@ -15,10 +16,16 @@ module.exports = {
'./source/entry.js'
],
output: {
path: require('path').resolve('./dist/'),
path: path.resolve('./dist/'),
filename: 'bundle.js',
publicPath: '/dist/'
},
resolve: {
alias: {
Engine: path.resolve('source/engine/'),
Règles: path.resolve('règles/')
}
},
module: {
loaders: [ {
test: /\.css$/,