2019-07-09 13:35:09 +00:00
import { parseUnit } from "Engine/units" ;
2019-04-11 13:23:46 +00:00
import {
2019-07-09 13:35:09 +00:00
assoc ,
chain ,
dropLast ,
find ,
fromPairs ,
has ,
is ,
isNil ,
join ,
last ,
map ,
path ,
pipe ,
propEq ,
props ,
range ,
reduce ,
reduced ,
reject ,
split ,
take ,
toPairs ,
trim ,
when
} from "ramda" ;
import rawRules from "Règles/base.yaml" ;
import translations from "Règles/externalized.yaml" ;
2018-09-04 14:23:05 +00:00
// TODO - should be in UI, not engine
2019-07-09 13:35:09 +00:00
import { capitalise0 , coerceArray } from "../utils" ;
import possibleVariableTypes from "./possibleVariableTypes.yaml" ;
2017-09-18 07:29:26 +00:00
2017-01-26 10:27:24 +00:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2019-04-10 09:42:55 +00:00
Functions working on one rule * /
2017-01-26 10:27:24 +00:00
2019-04-03 15:40:51 +00:00
export let enrichRule = rule => {
2019-07-09 13:35:09 +00:00
try {
let unit = rule . unité && parseUnit ( rule . unité ) ;
return {
... rule ,
type : possibleVariableTypes . find ( t => has ( t , rule ) || rule . type === t ) ,
name : rule [ "nom" ] ,
title : capitalise0 ( rule [ "titre" ] || rule [ "nom" ] ) ,
ns : rule [ "espace" ] ,
dottedName : buildDottedName ( rule ) ,
subquestionMarkdown : rule [ "sous-question" ] ,
defaultValue : rule [ "par défaut" ] ,
examples : rule [ "exemples" ] ,
icons : rule [ "icônes" ] ,
summary : rule [ "résumé" ] ,
unit
} ;
} catch ( e ) {
console . log ( e ) ;
throw new Error ( "Problem enriching " + JSON . stringify ( rule ) ) ;
}
} ;
2017-01-26 10:27:24 +00:00
2018-10-22 15:33:00 +00:00
export let buildDottedName = rule =>
2019-07-09 13:35:09 +00:00
rule [ "espace" ] ? [ rule [ "espace" ] , rule [ "nom" ] ] . join ( " . " ) : rule [ "nom" ] ;
2018-03-05 16:21:29 +00:00
2018-06-13 08:26:27 +00:00
// les variables dans les tests peuvent être exprimées relativement à l'espace de nom de la règle,
// comme dans sa formule
2018-01-03 15:54:19 +00:00
export let disambiguateExampleSituation = ( rules , rule ) =>
2019-07-09 13:35:09 +00:00
pipe (
toPairs ,
map ( ( [ k , v ] ) => [ disambiguateRuleReference ( rules , rule , k ) , v ] ) ,
fromPairs
) ;
2017-12-08 14:12:41 +00:00
2019-07-09 13:35:09 +00:00
export let hasKnownRuleType = rule => rule && enrichRule ( rule ) . type ;
2017-01-26 10:27:24 +00:00
2019-07-09 13:35:09 +00:00
export let splitName = split ( " . " ) ,
joinName = join ( " . " ) ;
2017-01-26 10:27:24 +00:00
2018-09-06 16:33:12 +00:00
export let parentName = pipe (
2019-07-09 13:35:09 +00:00
splitName ,
dropLast ( 1 ) ,
joinName
) ;
2018-09-06 16:33:12 +00:00
export let nameLeaf = pipe (
2019-07-09 13:35:09 +00:00
splitName ,
last
) ;
2017-01-26 10:27:24 +00:00
2018-02-12 14:53:07 +00:00
export let encodeRuleName = name =>
2019-07-09 13:35:09 +00:00
encodeURI ( name . replace ( /\s\.\s/g , "/" ) . replace ( /\s/g , "-" ) ) ;
2018-02-12 14:53:07 +00:00
export let decodeRuleName = name =>
2019-07-09 13:35:09 +00:00
decodeURI ( name . replace ( /\//g , " . " ) . replace ( /-/g , " " ) ) ;
2017-05-09 14:33:35 +00:00
2018-09-19 08:14:18 +00:00
export let ruleParents = dottedName => {
2019-07-09 13:35:09 +00:00
let fragments = splitName ( dottedName ) ; // dottedName ex. [CDD . événements . rupture]
return range ( 1 , fragments . length )
. map ( nbEl => take ( nbEl ) ( fragments ) )
. reverse ( ) ; // -> [ [CDD . événements . rupture], [CDD . événements], [CDD] ]
} ;
2017-04-24 18:03:38 +00:00
/ * L e s v a r i a b l e s p e u v e n t ê t r e e x p r i m é e s d a n s l a f o r m u l e d ' u n e r è g l e r e l a t i v e m e n t à s o n p r o p r e e s p a c e d e n o m , p o u r u n e p l u s g r a n d e l i s i b i l i t é . C e t t e f o n c t i o n r é s o u d c e t t e a m b i g u i t é .
2018-11-19 16:55:36 +00:00
* /
2018-01-03 15:54:19 +00:00
export let disambiguateRuleReference = (
2019-07-09 13:35:09 +00:00
allRules ,
{ dottedName , name } ,
partialName
2018-01-03 15:54:19 +00:00
) => {
2019-07-09 13:35:09 +00:00
let pathPossibilities = [
[ ] , // the top level namespace
... ruleParents ( dottedName ) , // the parents namespace
splitName ( dottedName ) // the rule's own namespace
] ,
found = reduce (
( res , path ) =>
when ( is ( Object ) , reduced ) (
do {
let dottedNameToCheck = [ ... path , partialName ] . join ( " . " ) ;
findRuleByDottedName ( allRules , dottedNameToCheck ) ;
}
) ,
null ,
pathPossibilities
) ;
2018-01-03 15:54:19 +00:00
2019-07-09 13:35:09 +00:00
return (
( found && found . dottedName ) ||
do {
throw new Error (
` OUUUUPS la référence ' ${ partialName } ' dans la règle ' ${ name } ' est introuvable dans la base `
) ;
}
) ;
} ;
2017-03-16 18:30:30 +00:00
2018-01-08 15:07:26 +00:00
export let collectDefaults = pipe (
2019-07-09 13:35:09 +00:00
map ( props ( [ "dottedName" , "defaultValue" ] ) ) ,
reject ( ( [ , v ] ) => v === undefined ) ,
fromPairs
) ;
2017-11-22 09:17:22 +00:00
2017-01-26 10:27:24 +00:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Méthodes de recherche d ' une règle * /
2016-12-07 18:08:10 +00:00
2018-02-12 14:53:07 +00:00
export let findRuleByName = ( allRules , query ) =>
2019-07-09 13:35:09 +00:00
( Array . isArray ( allRules ) ? allRules : Object . values ( allRules ) ) . find (
( { name } ) => name === query
) ;
2018-02-12 14:53:07 +00:00
export let findRulesByName = ( allRules , query ) =>
2019-07-09 13:35:09 +00:00
( Array . isArray ( allRules ) ? allRules : Object . values ( allRules ) ) . filter (
( { name } ) => name === query
) ;
2018-09-04 14:23:05 +00:00
2019-06-13 16:17:22 +00:00
export let findRuleByDottedName = ( allRules , dottedName ) =>
2019-07-09 13:35:09 +00:00
Array . isArray ( allRules )
? allRules . find ( rule => rule . dottedName == dottedName )
: allRules [ dottedName ] ;
2017-03-01 19:27:35 +00:00
2018-02-21 17:40:45 +00:00
export let findRule = ( rules , nameOrDottedName ) =>
2019-07-09 13:35:09 +00:00
nameOrDottedName . includes ( " . " )
? findRuleByDottedName ( rules , nameOrDottedName )
: findRuleByName ( rules , nameOrDottedName ) ;
2018-02-21 17:40:45 +00:00
2018-01-24 18:07:55 +00:00
export let findRuleByNamespace = ( allRules , ns ) =>
2019-07-09 13:35:09 +00:00
allRules . filter ( propEq ( "ns" , ns ) ) ;
2018-01-24 18:07:55 +00:00
2017-03-01 19:27:35 +00:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2018-01-03 15:54:19 +00:00
Autres * /
2017-03-01 19:27:35 +00:00
2019-07-09 13:35:09 +00:00
export let queryRule = rule => query => path ( query . split ( " . " ) ) ( rule ) ;
2018-02-06 16:02:13 +00:00
2018-06-06 07:07:11 +00:00
// Redux-form stores the form values as a nested object
// This helper makes a dottedName => value Map
export let nestedSituationToPathMap = situation => {
2019-07-09 13:35:09 +00:00
if ( situation == undefined ) return { } ;
let rec = ( o , currentPath ) =>
typeof o === "object"
? chain ( ( [ k , v ] ) => rec ( v , [ ... currentPath , trim ( k ) ] ) , toPairs ( o ) )
: [ [ currentPath . join ( " . " ) , o + "" ] ] ;
2018-06-06 07:07:11 +00:00
2019-07-09 13:35:09 +00:00
return fromPairs ( rec ( situation , [ ] ) ) ;
} ;
2018-02-26 14:25:47 +00:00
/* Traduction */
export let translateAll = ( translations , flatRules ) => {
2019-07-09 13:35:09 +00:00
let translationsOf = rule => translations [ buildDottedName ( rule ) ] ,
translateProp = ( lang , translation ) => ( rule , prop ) => {
let propTrans = translation [ prop + "." + lang ] ;
if ( prop === "suggestions" && propTrans )
return assoc (
"suggestions" ,
pipe (
toPairs ,
map ( ( [ key , translatedKey ] ) => [
translatedKey ,
rule . suggestions [ key ]
] ) ,
fromPairs
) ( propTrans ) ,
rule
) ;
return propTrans ? assoc ( prop , propTrans , rule ) : rule ;
} ,
translateRule = ( lang , translations , props ) => rule => {
let ruleTrans = translationsOf ( rule ) ;
return ruleTrans
? reduce ( translateProp ( lang , ruleTrans ) , rule , props )
: rule ;
} ;
2018-02-26 14:25:47 +00:00
2019-07-09 13:35:09 +00:00
let targets = [
"titre" ,
"description" ,
"question" ,
"sous-question" ,
"résumé" ,
"suggestions" ,
"contrôles"
] ;
2018-02-26 14:25:47 +00:00
2019-07-09 13:35:09 +00:00
return map ( translateRule ( "en" , translations , targets ) , flatRules ) ;
} ;
2018-09-04 14:23:05 +00:00
// On enrichit la base de règles avec des propriétés dérivées de celles du YAML
export let rules = translateAll ( translations , rawRules ) . map ( rule =>
2019-07-09 13:35:09 +00:00
enrichRule ( rule )
) ;
2019-06-11 10:03:45 +00:00
2019-07-09 13:35:09 +00:00
export let rulesFr = rawRules . map ( rule => enrichRule ( rule ) ) ;
2018-11-26 17:09:14 +00:00
export let findParentDependency = ( rules , rule ) => {
2019-07-09 13:35:09 +00:00
// A parent dependency means that one of a rule's parents is not just a namespace holder, it is a boolean question. E.g. is it a fixed-term contract, yes / no
// When it is resolved to false, then the whole branch under it is disactivated (non applicable)
// It lets those children omit obvious and repetitive parent applicability tests
let parentDependencies = ruleParents ( rule . dottedName ) . map ( joinName ) ;
return pipe (
map ( parent => findRuleByDottedName ( rules , parent ) ) ,
reject ( isNil ) ,
find (
//Find the first "calculable" parent
( { question , unit , formule } ) => question && ! unit && ! formule //implicitly, the format is boolean
)
) ( parentDependencies ) ;
} ;
2019-07-03 15:33:46 +00:00
export let getRuleFromAnalysis = analysis => dottedName => {
2019-07-09 13:35:09 +00:00
if ( ! analysis ) {
throw new Error ( "[getRuleFromAnalysis] The analysis can't be nil !" ) ;
}
let rule = coerceArray ( analysis ) // In some simulations, there are multiple "branches" : the analysis is run with e.g. 3 different input situations
. map (
analysis =>
analysis . cache [ dottedName ] ? . explanation || // the cache stores a reference to a variable, the variable is contained in the 'explanation' attribute
analysis . targets . find ( propEq ( "dottedName" , dottedName ) )
)
. filter ( Boolean ) [ 0 ] ;
2019-07-03 15:33:46 +00:00
2019-07-09 13:35:09 +00:00
if ( ! rule ) {
throw new Error (
` [getRuleFromAnalysis] Unable to find the rule ${ dottedName } `
) ;
}
2019-07-03 15:33:46 +00:00
2019-07-09 13:35:09 +00:00
return rule ;
} ;