[moteur] les questions sont court-circuitées à chaque réponse
parent
041e99d8aa
commit
9f34ddfbcf
|
@ -95,7 +95,7 @@
|
|||
|
||||
- Variable: événements
|
||||
attache: Salariat . CDD
|
||||
description: Certains événements influent le prix d'un CDD
|
||||
description: Certains événements impactent le prix d'un CDD
|
||||
# au lieu de lister tous les cas, l'alternative est de simplement indiquer qu'ils sont exclusifs,
|
||||
# et les identifier dynamiquement par leur attribut "attache" :
|
||||
# choix: exlusif # par rapport à 'choix: multiples'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
- Variable: Contrat aidé
|
||||
- Variable: contrat aidé
|
||||
attache: Salariat
|
||||
choix exclusifs:
|
||||
- contrat unique insertion
|
||||
|
@ -14,7 +14,7 @@
|
|||
attache: Salariat . contrat aidé
|
||||
description: Contrat de travail aidé créé pour faciliter l'insertion professionnelle et l’accès à une qualification pour les jeunes en difficulté
|
||||
|
||||
- Variable: Contrat unique insertion
|
||||
- Variable: contrat unique insertion
|
||||
attache: Salariat . contrat aidé
|
||||
choix exclusifs:
|
||||
- CUI-CAE
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
# Cet ensemble de variables sont définies implicitement sur l'entité Salariat
|
||||
|
||||
- Variable: Salaire de base
|
||||
- Variable: salaire de base
|
||||
attache: Salariat
|
||||
contrainte: nombre positif
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- Cotisation: CIF CDD
|
||||
attache: CDD
|
||||
description: Conrtibution au financement du congé individuel de formation spécifique aux CDD
|
||||
description: Contribution au financement du congé individuel de formation spécifique aux CDD
|
||||
attributs:
|
||||
collecteur: OPCA
|
||||
référence: Code du travail - Article L6322-37
|
||||
|
|
|
@ -7,10 +7,10 @@ import HoverDecorator from '../HoverDecorator'
|
|||
class RadioLabel extends Component {
|
||||
|
||||
render() {
|
||||
let {choice, input, submit, hover, themeColours} = this.props,
|
||||
let {choice: {value, label}, input, submit, hover, themeColours} = this.props,
|
||||
labelStyle =
|
||||
Object.assign(
|
||||
(choice === input.value || hover) ? answered(themeColours) : answer(themeColours),
|
||||
(value === input.value || hover) ? answered(themeColours) : answer(themeColours),
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -19,8 +19,8 @@ class RadioLabel extends Component {
|
|||
className="radio" >
|
||||
<input
|
||||
type="radio" {...input} onClick={submit}
|
||||
value={choice} checked={choice === input.value ? 'checked' : ''} />
|
||||
{choice}
|
||||
value={value} checked={value === input.value ? 'checked' : ''} />
|
||||
{label}
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export default class Question extends Component {
|
|||
return (
|
||||
<span>
|
||||
{ choices.map((choice) =>
|
||||
<RadioLabel key={choice} {...{choice, input, submit, themeColours}}/>
|
||||
<RadioLabel key={choice.value} {...{choice, input, submit, themeColours}}/>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export let constructStepMeta = ({dottedName, name, description}) => ({
|
||||
// name: dottedName.split(' . ').join('.'),
|
||||
name: dottedName,
|
||||
question: description || name,
|
||||
title: name,
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
import removeDiacritics from './remove-diacritics'
|
||||
import R from 'ramda'
|
||||
import {parentName, nameLeaf} from './rules'
|
||||
|
||||
// TODO: handle dotted variable syntax
|
||||
// ([\w\s]+(\s\.?\s\w+)+)\s([<?>?]?=?)\s([\w\s]+)
|
||||
var replace = "regex";
|
||||
var re = new RegExp(replace,"g");
|
||||
"mystring".replace(re, "newstring");
|
||||
// Ces regexp sont trop complexe. TODO Ce n'est que temporaire !
|
||||
|
||||
// Ces regexp sont trop complexe. Ce n'est que temporaire !
|
||||
// composants des regexps
|
||||
let
|
||||
vn = '[A-Za-z\\u00C0-\\u017F\\s]+', //variableName
|
||||
sep = '\\s\\.\\s'
|
||||
|
||||
let expressionTests = {
|
||||
// 'negatedVariable': v => /!((?:[a-z0-9]|\s|_)+)/g.exec(v),
|
||||
// 'variableIsIncludedIn': v => /((?:[a-z0-9]|\s|_)+)⊂*/g.exec(v),
|
||||
'variableComparedToNumber': v => /([\w\s]+(?:\s\.\s[\w\s]+)*)\s([<>]=?)\s([0-9]+)/g.exec(v),
|
||||
'variableEqualsString': v => /([\w\s]+(?:\s\.\s[\w\s]+)*)\s=\s([\w\s]+)/g.exec(v),
|
||||
'variable': v => /^([\w\s]+(?:\s\.\s[\w\s]+)*)$/g.exec(v)
|
||||
'variable': v => new RegExp(`^(${vn}(?:${sep}${vn})*)$`, 'g').exec(v)
|
||||
}
|
||||
|
||||
export let recognizeExpression = rawValue => {
|
||||
let
|
||||
value = removeDiacritics(rawValue).toLowerCase(),
|
||||
match
|
||||
export let recognizeExpression = value => {
|
||||
let match
|
||||
|
||||
// match = expressionTests['negatedVariable'](value)
|
||||
// if (match) {
|
||||
|
@ -43,6 +42,15 @@ export let recognizeExpression = rawValue => {
|
|||
match = expressionTests['variable'](value)
|
||||
if (match) {
|
||||
let [variableName] = match
|
||||
return [variableName, situation => situation(variableName) == 'oui']
|
||||
return [
|
||||
variableName,
|
||||
situation => {
|
||||
// let yo = parentName(variableName),
|
||||
// ya = nameLeaf(variableName),
|
||||
// yi = situation(parentName(variableName))
|
||||
// debugger;
|
||||
return removeDiacritics(situation(variableName)) == 'oui' ||
|
||||
removeDiacritics(situation(parentName(variableName))) == nameLeaf(variableName)
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,13 +109,8 @@ for (var i=0; i < defaultDiacriticsRemovalMap .length; i++){
|
|||
}
|
||||
|
||||
// "what?" version ... http://jsperf.com/diacritics/12
|
||||
export default function removeDiacritics (str) {
|
||||
return str.replace(/[^\u0000-\u007E]/g, function(a){
|
||||
return diacriticsMap[a] || a
|
||||
})
|
||||
}
|
||||
|
||||
export let borrify = string => removeDiacritics(string).toLowerCase()
|
||||
export default str => str != null &&
|
||||
str.replace(/[^\u0000-\u007E]/g, a => diacriticsMap[a] || a)
|
||||
|
||||
var paragraph = 'L\'avantage d\'utiliser le lorem ipsum est bien évidemment de pouvoir créer des maquettes ou de remplir un site internet de contenus qui présentent un rendu s\'approchant un maximum du rendu final. \n Par défaut lorem ipsum ne contient pas d\'accent ni de caractères spéciaux contrairement à la langue française qui en contient beaucoup. C\'est sur ce critère que nous proposons une solution avec cet outil qui générant du faux-texte lorem ipsum mais avec en plus, des caractères spéciaux tel que les accents ou certains symboles utiles pour la langue française. \n L\'utilisation du lorem standard est facile d’utilisation mais lorsque le futur client utilisera votre logiciel il se peut que certains caractères spéciaux ou qu\'un accent ne soient pas codés correctement. \n Cette page a pour but donc de pouvoir perdre le moins de temps possible et donc de tester directement si tous les encodages de base de donnée ou des sites sont les bons de plus il permet de récuperer un code css avec le texte formaté !'
|
||||
// alert(removeDiacritics(paragraph))
|
||||
|
|
|
@ -3,7 +3,6 @@ import rawRules from './load-rules'
|
|||
import rawEntityRules from './load-entity-rules'
|
||||
import R from 'ramda'
|
||||
import possibleVariableTypes from './possibleVariableTypes.yaml'
|
||||
import {borrify} from './remove-diacritics'
|
||||
|
||||
|
||||
/***********************************
|
||||
|
@ -13,17 +12,27 @@ export let enrichRule = rule => {
|
|||
let
|
||||
type = possibleVariableTypes.find(t => rule[t]),
|
||||
name = rule[type],
|
||||
dottedName = rule.attache && borrify(
|
||||
[ rule.attache, rule.alias || name].join(' . ')
|
||||
)
|
||||
console.log('enrich : dottedName', dottedName)
|
||||
dottedName = rule.attache && [
|
||||
rule.attache,
|
||||
rule.alias || name
|
||||
].join(' . ')
|
||||
|
||||
return {...rule, type, name, dottedName}
|
||||
}
|
||||
|
||||
export let hasKnownRuleType = rule => rule && enrichRule(rule).type
|
||||
|
||||
let splitName = R.split(' . ')
|
||||
|
||||
export let parentName = R.pipe(
|
||||
splitName,
|
||||
R.dropLast(1),
|
||||
R.join(' . ')
|
||||
)
|
||||
export let nameLeaf = R.pipe(
|
||||
splitName,
|
||||
R.last
|
||||
)
|
||||
|
||||
// On enrichit la base de règles avec des propriétés dérivées de celles du YAML
|
||||
let [rules, entityRules] = //R.map(R.map(enrichRule))([rawRules, rawEntityRules])
|
||||
|
@ -49,8 +58,10 @@ export let searchRules = searchInput =>
|
|||
|
||||
|
||||
|
||||
export let findRuleByDottedName = dottedName =>
|
||||
entityRules.find(rule => rule.dottedName == borrify(dottedName))
|
||||
export let findRuleByDottedName = dottedName => do {
|
||||
let found = entityRules.find(rule => rule.dottedName == dottedName)
|
||||
found || console.log('dottedName = ', dottedName, ' a déserté')
|
||||
}
|
||||
|
||||
export let findGroup = R.pipe(
|
||||
findRuleByDottedName,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import R from 'ramda'
|
||||
import rules from './load-rules'
|
||||
import removeDiacritics from './remove-diacritics'
|
||||
import {findRuleByName, enrichRule} from './rules'
|
||||
import {findRuleByName, enrichRule, parentName} from './rules'
|
||||
import {recognizeExpression} from './expressions'
|
||||
|
||||
|
||||
|
@ -9,23 +8,28 @@ import {recognizeExpression} from './expressions'
|
|||
let selectedRules = rules.filter(rule =>
|
||||
R.contains(
|
||||
enrichRule(rule).name,
|
||||
['CIF CDD', 'Indemnité de fin de contrat']
|
||||
// ['CIF CDD', 'Indemnité de fin de contrat']
|
||||
['CIF CDD']
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
let knownVariable = (situation, variableName) => (typeof situation(variableName) !== 'undefined')
|
||||
let knownVariable = (situation, variableName) => typeof R.or(
|
||||
situation(variableName),
|
||||
situation(parentName(variableName))
|
||||
) !== 'undefined'
|
||||
|
||||
let deriveRule = situation => R.pipe(
|
||||
|
||||
let deriveRule = situationGate => R.pipe(
|
||||
R.toPairs,
|
||||
// Reduce to [variables needed to compute that variable, computed variable value]
|
||||
R.reduce(([variableNames, result], [key, value]) => {
|
||||
if (key === 'concerne') {
|
||||
let [variableName, evaluation] = recognizeExpression(value)
|
||||
// Si cette variable a été renseignée
|
||||
if (knownVariable(situation, variableName)) {
|
||||
if (knownVariable(situationGate, variableName)) {
|
||||
// Si l'expression n'est pas vraie...
|
||||
if (!evaluation(situation)) {
|
||||
if (!evaluation(situationGate)) {
|
||||
// On court-circuite toute la variable, et on n'a besoin d'aucune information !
|
||||
return R.reduced([[]])
|
||||
} else {
|
||||
|
@ -39,10 +43,11 @@ let deriveRule = situation => R.pipe(
|
|||
if (key === 'non applicable si') {
|
||||
let conditions = value['l\'une de ces conditions']
|
||||
let [subVariableNames, reduced] = R.reduce(([variableNames], expression) => {
|
||||
|
||||
let [variableName, evaluation] = recognizeExpression(expression)
|
||||
|
||||
if (knownVariable(situation, variableName)) {
|
||||
if (evaluation(situation)) {
|
||||
if (knownVariable(situationGate, variableName)) {
|
||||
if (evaluation(situationGate)) {
|
||||
return R.reduced([[], true])
|
||||
} else {
|
||||
return [variableNames]
|
||||
|
@ -59,8 +64,8 @@ let deriveRule = situation => R.pipe(
|
|||
let {assiette, taux} = value['linéaire']
|
||||
|
||||
// A propos de l'assiette
|
||||
let assietteVariableName = removeDiacritics(assiette),
|
||||
assietteValue = situation(assietteVariableName),
|
||||
let assietteVariableName = assiette,
|
||||
assietteValue = situationGate(assietteVariableName),
|
||||
unknownAssiette = assietteValue == undefined
|
||||
|
||||
if (unknownAssiette) {
|
||||
|
@ -89,17 +94,22 @@ let deriveRule = situation => R.pipe(
|
|||
}, [[], null])
|
||||
)
|
||||
|
||||
let analyseRule = situation =>
|
||||
let analyseRule = situationGate =>
|
||||
R.pipe(
|
||||
enrichRule, // -> {type, name, rule}
|
||||
data => R.assoc(
|
||||
'derived',
|
||||
deriveRule(situation)(data)
|
||||
deriveRule(situationGate)(data)
|
||||
)(data)
|
||||
)
|
||||
|
||||
export let analyseSituation = situation =>
|
||||
selectedRules.map(analyseRule(situation))
|
||||
export let analyseSituation = situationGate =>
|
||||
selectedRules.map(analyseRule(situationGate))
|
||||
|
||||
// export let analyseSituation = R.pipe(
|
||||
// analyseRule,
|
||||
// R.flip(R.map)
|
||||
// )
|
||||
|
||||
export let variableType = name => {
|
||||
if (name == null) return null
|
||||
|
|
|
@ -11,9 +11,8 @@ import RhetoricalQuestion from './components/conversation/RhetoricalQuestion'
|
|||
|
||||
import { STEP_ACTION, UNSUBMIT_ALL, START_CONVERSATION} from './actions'
|
||||
import R from 'ramda'
|
||||
import {borrify} from './engine/remove-diacritics'
|
||||
|
||||
import {findGroup, findRuleByDottedName, dottedName} from './engine/rules'
|
||||
import {findGroup, findRuleByDottedName, dottedName, parentName} from './engine/rules'
|
||||
import {constructStepMeta} from './engine/conversation'
|
||||
|
||||
import computeThemeColours from './components/themeColours'
|
||||
|
@ -33,6 +32,9 @@ function themeColours(state = computeThemeColours(), {type, colour}) {
|
|||
else return state
|
||||
}
|
||||
|
||||
let situationGate = state =>
|
||||
name => formValueSelector('conversation')(state, name)
|
||||
|
||||
export default reduceReducers(
|
||||
combineReducers({
|
||||
// this is handled by redux-form, pas touche !
|
||||
|
@ -50,15 +52,21 @@ export default reduceReducers(
|
|||
(state, action) => {
|
||||
if (action.type == STEP_ACTION || action.type == START_CONVERSATION) {
|
||||
let {newState, name} = action
|
||||
|
||||
console.log('action', action)
|
||||
// une étape vient d'être validée : on va changer son état
|
||||
let newSteps = R.pipe(
|
||||
R.map(step => step.name == name ? {...step, state: newState} : step),
|
||||
R.reject(R.whereEq({theEnd: true}))
|
||||
)(state.steps)
|
||||
|
||||
window.situationGate = situationGate(state)
|
||||
|
||||
// on calcule la prochaine étape, à ajouter sur la pile
|
||||
let analysedSituation = analyseSituation(name => formValueSelector('conversation')(state, name)),
|
||||
let
|
||||
analysedSituation = analyseSituation(
|
||||
situationGate(state)
|
||||
),
|
||||
|
||||
missingVariables = R.pipe(
|
||||
R.map( ({name, derived: [missingVariables]}) =>
|
||||
(missingVariables || []).map(mv => [mv, name])
|
||||
|
@ -69,35 +77,10 @@ export default reduceReducers(
|
|||
)(analysedSituation),
|
||||
missingVariablesList = R.keys(missingVariables),
|
||||
|
||||
yà = console.log('missingVariablesList', missingVariablesList),
|
||||
|
||||
// identification des groupes de variables manquantes
|
||||
// groups = [...missingVariablesList.reduce(
|
||||
// (set, variable) => {
|
||||
// let subs = R.pipe(
|
||||
// borrify,
|
||||
// R.split(' . '),
|
||||
// R.dropLast(1),
|
||||
// R.join(' . ')
|
||||
// )(variable)
|
||||
//
|
||||
// if (subs.length)
|
||||
// set.add(subs)
|
||||
//
|
||||
// return set
|
||||
// }
|
||||
// , new Set())],
|
||||
groups = R.groupBy(
|
||||
R.pipe(
|
||||
borrify,
|
||||
R.split(' . '),
|
||||
R.dropLast(1),
|
||||
R.join(' . ')
|
||||
)
|
||||
parentName
|
||||
)(missingVariablesList),
|
||||
|
||||
yo = console.log('groups', groups),
|
||||
|
||||
// on va maintenant construire la liste des composants React correspondant aux questions pour obtenir les variables manquantes
|
||||
yyoo = R.pipe(
|
||||
R.mapObjIndexed((variables, group) =>
|
||||
|
@ -106,9 +89,7 @@ export default reduceReducers(
|
|||
R.cond([
|
||||
// Pas de groupe trouvé : ce sont des variables individuelles
|
||||
[R.isNil, () => variables.map(dottedName => {
|
||||
console.log('dottedName', dottedName)
|
||||
let rule = findRuleByDottedName(dottedName)
|
||||
console.log('rule', rule)
|
||||
return Object.assign(constructStepMeta(rule),
|
||||
rule.contrainte == 'nombre positif' ?
|
||||
{
|
||||
|
@ -121,7 +102,10 @@ export default reduceReducers(
|
|||
}
|
||||
} : {
|
||||
component: Question,
|
||||
choices: ['Non', 'Oui'],
|
||||
choices: [
|
||||
{value: 'non', label: 'Non'},
|
||||
{value: 'oui', label: 'Oui'}
|
||||
],
|
||||
defaultValue: 'Non',
|
||||
}
|
||||
)})],
|
||||
|
@ -134,7 +118,10 @@ export default reduceReducers(
|
|||
let rule = findRuleByDottedName(
|
||||
group.dottedName + ' . ' + name
|
||||
)
|
||||
return rule && rule.titre || name
|
||||
return {
|
||||
value: rule.name,
|
||||
label: rule && rule.titre || name
|
||||
}
|
||||
}),
|
||||
defaultValue: 'Non',
|
||||
helpText: 'Choisissez une réponse'
|
||||
|
@ -145,9 +132,7 @@ export default reduceReducers(
|
|||
),
|
||||
R.values,
|
||||
R.unnest
|
||||
)(groups),
|
||||
|
||||
l = console.log('yyoo', yyoo)
|
||||
)(groups)
|
||||
|
||||
// la question doit pouvoir stocker tout ça dans la situation (redux-form) correctement
|
||||
|
||||
|
@ -156,49 +141,8 @@ export default reduceReducers(
|
|||
|
||||
|
||||
|
||||
let [firstMissingVariable, dependencyOfVariables] = R.isEmpty(missingVariables) ? [] : R.toPairs(missingVariables)[0],
|
||||
|
||||
type = variableType(firstMissingVariable),
|
||||
|
||||
stepData = Object.assign({
|
||||
name: firstMissingVariable,
|
||||
state: null,
|
||||
dependencyOfVariables: dependencyOfVariables,
|
||||
title: firstMissingVariable,
|
||||
question: firstMissingVariable,
|
||||
visible: true,
|
||||
helpText: <p>
|
||||
Le contrat à durée indéterminée est une exception au CDI.
|
||||
<br/>
|
||||
<a href="https://www.service-public.fr/professionnels-entreprises/vosdroits/F33777" target="_blank">
|
||||
En savoir plus (service-public.fr)
|
||||
</a>
|
||||
</p>
|
||||
}, type == 'boolean' ? {
|
||||
component: Question,
|
||||
choices: ['non', 'oui'],
|
||||
defaultValue: 'Non'
|
||||
}: type == 'numeric' ? {
|
||||
component: Input,
|
||||
defaultValue: 0,
|
||||
valueType: euro,
|
||||
attributes: {
|
||||
/* We use 'text' inputs : browser behaviour with input=number
|
||||
doesn't quite work with our "update simulation on input change"... */
|
||||
inputMode: 'numeric',
|
||||
placeholder: 'votre réponse'
|
||||
}
|
||||
} : firstMissingVariable == undefined ? {
|
||||
theEnd: true,
|
||||
component: RhetoricalQuestion,
|
||||
question: <span>
|
||||
{'Merci. N\'hésitez pas à partager le simulateur !'}
|
||||
</span>,
|
||||
helpText: null
|
||||
}: {})
|
||||
|
||||
|
||||
return {...state, steps: [...newSteps, stepData], analysedSituation}
|
||||
// return {...state, steps: [...newSteps, stepData], analysedSituation}
|
||||
// ... do stuff
|
||||
} else {
|
||||
return state
|
||||
|
|
Loading…
Reference in New Issue