2017-01-26 10:27:24 +00:00
// Séparation artificielle, temporaire, entre ces deux types de règles
2018-12-10 16:21:20 +00:00
import rawRules from 'Règles/base.yaml'
2018-09-04 14:23:05 +00:00
import translations from 'Règles/externalized.yaml'
2018-01-08 15:07:26 +00:00
import {
2018-02-26 14:25:47 +00:00
assoc ,
2018-09-04 14:23:05 +00:00
mapObjIndexed ,
2018-06-06 07:07:11 +00:00
chain ,
2018-08-31 12:08:26 +00:00
has ,
pipe ,
2018-09-04 14:23:05 +00:00
toPairs ,
map ,
fromPairs ,
split ,
join ,
dropLast ,
take ,
2018-01-24 18:07:55 +00:00
propEq ,
2018-08-31 12:08:26 +00:00
reduce ,
2018-09-04 14:23:05 +00:00
when ,
is ,
props ,
identity ,
path ,
2018-08-31 12:08:26 +00:00
reject ,
2018-09-04 14:23:05 +00:00
reduced ,
range ,
last ,
2018-11-27 16:35:42 +00:00
trim ,
isNil ,
find
2018-01-08 15:07:26 +00:00
} from 'ramda'
2018-08-31 12:08:26 +00:00
import possibleVariableTypes from './possibleVariableTypes.yaml'
2018-09-04 14:23:05 +00:00
import marked from './marked'
import { capitalise0 } from '../utils'
import formValueTypes from 'Components/conversation/formValueTypes'
// TODO - should be in UI, not engine
import taux _versement _transport from 'Règles/taux-versement-transport.json'
2017-09-18 07:29:26 +00:00
2018-01-03 15:54:19 +00:00
// console.log('rawRules', rawRules.map(({espace, nom}) => espace + nom))
2017-01-26 10:27:24 +00:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Méthodes agissant sur une règle * /
2017-02-10 14:12:00 +00:00
// Enrichissement de la règle avec des informations évidentes pour un lecteur humain
2017-09-18 07:29:26 +00:00
export let enrichRule = ( rule , sharedData = { } ) => {
2018-09-26 09:35:29 +00:00
try {
let type = possibleVariableTypes . find ( t => has ( t , rule ) || rule . type === t ) ,
name = rule [ 'nom' ] ,
title = capitalise0 ( rule [ 'titre' ] || name ) ,
ns = rule [ 'espace' ] ,
data = rule [ 'données' ] ? sharedData [ rule [ 'données' ] ] : null ,
dottedName = buildDottedName ( rule ) ,
subquestionMarkdown = rule [ 'sous-question' ] ,
subquestion = subquestionMarkdown && marked ( subquestionMarkdown ) ,
defaultValue = rule [ 'par défaut' ] ,
examples = rule [ 'exemples' ] ,
2018-12-12 11:00:49 +00:00
icon = rule [ 'icônes' ]
2017-01-26 10:27:24 +00:00
2018-09-26 09:35:29 +00:00
return {
... rule ,
type ,
name ,
title ,
ns ,
data ,
dottedName ,
subquestion ,
defaultValue ,
raw : rule ,
examples ,
2018-11-26 17:09:14 +00:00
icon
2018-09-26 09:35:29 +00:00
}
} catch ( e ) {
2018-12-05 17:58:38 +00:00
console . log ( e )
2018-09-26 09:35:29 +00:00
throw new Error ( 'Problem enriching ' + JSON . stringify ( rule ) )
2018-01-03 15:54:19 +00:00
}
2017-01-26 10:27:24 +00:00
}
2018-10-22 15:33:00 +00:00
export let buildDottedName = rule =>
2018-03-05 16:21:29 +00:00
rule [ 'espace' ] ? [ rule [ 'espace' ] , rule [ 'nom' ] ] . join ( ' . ' ) : rule [ 'nom' ]
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 ) =>
2018-01-08 15:07:26 +00:00
pipe (
toPairs ,
map ( ( [ k , v ] ) => [ disambiguateRuleReference ( rules , rule , k ) , v ] ) ,
fromPairs
2018-01-03 15:54:19 +00:00
)
2017-12-08 14:12:41 +00:00
2017-01-26 10:27:24 +00:00
export let hasKnownRuleType = rule => rule && enrichRule ( rule ) . type
2018-01-08 15:07:26 +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 (
splitName ,
dropLast ( 1 ) ,
joinName
)
export let nameLeaf = pipe (
splitName ,
last
)
2017-01-26 10:27:24 +00:00
2018-02-12 14:53:07 +00:00
export let encodeRuleName = name =>
name . replace ( /\s\.\s/g , '--' ) . replace ( /\s/g , '-' )
export let decodeRuleName = name =>
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 => {
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 = (
allRules ,
2018-09-19 08:14:18 +00:00
{ dottedName , name } ,
2018-01-03 15:54:19 +00:00
partialName
) => {
2018-09-19 08:14:18 +00:00
let pathPossibilities = [
[ ] , // the top level namespace
... ruleParents ( dottedName ) , // the parents namespace
splitName ( dottedName ) // the rule's own namespace
] ,
2018-01-08 15:07:26 +00:00
found = reduce (
2018-01-03 15:54:19 +00:00
( res , path ) =>
2018-01-08 15:07:26 +00:00
when ( is ( Object ) , reduced ) (
2018-12-11 12:50:53 +00:00
do {
let dottedNameToCheck = [ ... path , partialName ] . join ( ' . ' )
findRuleByDottedName ( allRules , dottedNameToCheck )
}
2018-01-03 15:54:19 +00:00
) ,
null ,
pathPossibilities
)
return (
( found && found . dottedName ) ||
do {
2018-06-06 07:07:11 +00:00
throw new Error (
` OUUUUPS la référence ' ${ partialName } ' dans la règle ' ${ name } ' est introuvable dans la base `
)
2018-01-03 15:54:19 +00:00
}
)
2017-03-16 18:30:30 +00:00
}
2018-01-08 15:07:26 +00:00
export let collectDefaults = pipe (
map ( props ( [ 'dottedName' , 'defaultValue' ] ) ) ,
reject ( ( [ , v ] ) => v === undefined ) ,
fromPairs
2017-11-22 09:57:07 +00:00
)
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 ) =>
allRules . find ( ( { name } ) => name === query )
export let findRulesByName = ( allRules , query ) =>
allRules . filter ( ( { name } ) => name === query )
2016-12-07 18:08:10 +00:00
2018-09-04 14:23:05 +00:00
export let searchRules = searchInput =>
rules
. filter (
rule =>
rule &&
hasKnownRuleType ( rule ) &&
JSON . stringify ( rule )
. toLowerCase ( )
. indexOf ( searchInput ) > - 1
)
. map ( enrichRule )
2017-11-23 10:25:24 +00:00
export let findRuleByDottedName = ( allRules , dottedName ) => {
2017-11-27 12:57:11 +00:00
return allRules . find ( rule => rule . dottedName == dottedName )
2017-11-23 10:25:24 +00:00
}
2017-03-01 19:27:35 +00:00
2018-02-21 17:40:45 +00:00
export let findRule = ( rules , nameOrDottedName ) =>
nameOrDottedName . includes ( ' . ' )
? findRuleByDottedName ( rules , nameOrDottedName )
: findRuleByName ( rules , nameOrDottedName )
2018-01-24 18:07:55 +00:00
export let findRuleByNamespace = ( allRules , ns ) =>
allRules . filter ( propEq ( 'ns' , ns ) )
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
2018-02-06 16:02:13 +00:00
export let queryRule = rule => query => path ( query . split ( ' . ' ) ) ( rule )
2018-06-06 07:07:11 +00:00
var findObjectByLabel = function ( obj , label ) {
if ( obj . label === label ) {
return obj
}
for ( var i in obj ) {
if ( obj . hasOwnProperty ( i ) ) {
var foundLabel = findObjectByLabel ( obj [ i ] , label )
if ( foundLabel ) {
return foundLabel
}
}
}
return null
}
2017-11-24 17:55:15 +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 => {
let rec = ( o , currentPath ) =>
typeof o === 'object'
2018-06-18 09:28:47 +00:00
? chain ( ( [ k , v ] ) => rec ( v , [ ... currentPath , trim ( k ) ] ) , toPairs ( o ) )
2018-06-18 10:12:08 +00:00
: [ [ currentPath . join ( ' . ' ) , o + '' ] ]
2018-06-06 07:07:11 +00:00
return fromPairs ( rec ( situation , [ ] ) )
2017-11-24 17:55:15 +00:00
}
2018-02-26 14:25:47 +00:00
2018-06-06 07:07:11 +00:00
export let formatInputs = ( flatRules , pathValueMap ) =>
2018-06-06 16:17:13 +00:00
mapObjIndexed ( ( value , path ) => {
2018-06-06 07:07:11 +00:00
// Our situationGate retrieves data from the "conversation" form
// The search below is to apply input conversions such as replacing "," with "."
if ( name . startsWith ( 'sys.' ) ) return null
let rule = findRuleByDottedName ( flatRules , path ) ,
format = rule ? formValueTypes [ rule . format ] : null ,
pre = format && format . validator . pre ? format . validator . pre : identity
return pre ( value )
2018-06-06 16:17:13 +00:00
} , pathValueMap )
2018-06-06 07:07:11 +00:00
2018-02-26 14:25:47 +00:00
/* Traduction */
export let translateAll = ( translations , flatRules ) => {
2018-03-05 16:21:29 +00:00
let translationsOf = rule => translations [ buildDottedName ( rule ) ] ,
2018-02-26 14:25:47 +00:00
translateProp = ( lang , translation ) => ( rule , prop ) => {
2018-03-05 14:11:35 +00:00
let propTrans = translation [ prop + '.' + lang ]
2018-06-06 07:07:11 +00:00
if ( prop === 'suggestions' && propTrans )
return assoc (
'suggestions' ,
pipe (
toPairs ,
map ( ( [ key , translatedKey ] ) => [
translatedKey ,
rule . suggestions [ key ]
] ) ,
fromPairs
) ( propTrans ) ,
rule
)
2018-03-05 14:11:35 +00:00
return propTrans ? assoc ( prop , propTrans , rule ) : rule
2018-02-26 14:25:47 +00:00
} ,
translateRule = ( lang , translations , props ) => rule => {
let ruleTrans = translationsOf ( rule )
2018-03-05 14:11:35 +00:00
return ruleTrans
? reduce ( translateProp ( lang , ruleTrans ) , rule , props )
: rule
2018-02-26 14:25:47 +00:00
}
2018-06-06 07:07:11 +00:00
let targets = [
'titre' ,
'description' ,
'question' ,
'sous-question' ,
'résumé' ,
'suggestions'
]
2018-02-26 14:25:47 +00:00
2018-03-05 14:11:35 +00:00
return map ( translateRule ( 'en' , translations , targets ) , flatRules )
2018-02-26 14:25:47 +00:00
}
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 =>
enrichRule ( rule , { taux _versement _transport } )
)
export let rulesFr = rawRules . map ( rule =>
enrichRule ( rule , { taux _versement _transport } )
)
2018-11-26 17:09:14 +00:00
export let findParentDependency = ( rules , rule ) => {
// 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 )
2018-11-27 16:35:42 +00:00
return pipe (
map ( parent => findRuleByDottedName ( rules , parent ) ) ,
reject ( isNil ) ,
find (
2018-11-26 17:09:14 +00:00
//Find the first "calculable" parent
2018-11-27 16:35:42 +00:00
( { question , format , formule } ) => question && ! format && ! formule //implicitly, the format is boolean
2018-11-26 17:09:14 +00:00
)
2018-11-27 16:35:42 +00:00
) ( parentDependencies )
2018-11-26 17:09:14 +00:00
}