2017-07-02 17:12:02 +00:00
|
|
|
import R from 'ramda'
|
2017-01-10 18:22:44 +00:00
|
|
|
import React from 'react'
|
2016-11-15 18:46:17 +00:00
|
|
|
import { combineReducers } from 'redux'
|
2017-01-10 18:22:44 +00:00
|
|
|
import reduceReducers from 'reduce-reducers'
|
|
|
|
import {reducer as formReducer, formValueSelector} from 'redux-form'
|
|
|
|
|
2017-09-21 13:46:59 +00:00
|
|
|
import {rules, findRuleByName } from 'Engine/rules'
|
|
|
|
import {buildNextSteps} from 'Engine/generateQuestions'
|
2017-07-02 17:12:02 +00:00
|
|
|
import computeThemeColours from 'Components/themeColours'
|
2017-07-08 11:28:50 +00:00
|
|
|
import { STEP_ACTION, START_CONVERSATION, EXPLAIN_VARIABLE, POINT_OUT_OBJECTIVES, CHANGE_THEME_COLOUR} from './actions'
|
|
|
|
|
2017-07-09 22:14:22 +00:00
|
|
|
import {analyseTopDown} from 'Engine/traverse'
|
2017-07-08 11:28:50 +00:00
|
|
|
|
2017-09-21 13:46:59 +00:00
|
|
|
// Our situationGate retrieves data from the "conversation" form
|
|
|
|
let fromConversation = state => name => formValueSelector('conversation')(state, name)
|
2017-07-08 11:28:50 +00:00
|
|
|
|
2017-09-21 13:46:59 +00:00
|
|
|
// assume "wraps" a given situation function with one that overrides its values with
|
|
|
|
// the given assumptions
|
2017-09-26 15:20:46 +00:00
|
|
|
let assume = (evaluator, assumptions) => state => name => {
|
|
|
|
let userInput = evaluator(state)(name)
|
|
|
|
return userInput != null ? userInput : assumptions[name]
|
|
|
|
}
|
2017-07-07 08:41:06 +00:00
|
|
|
|
|
|
|
export let reduceSteps = (state, action) => {
|
|
|
|
|
2017-09-21 13:46:59 +00:00
|
|
|
let flatRules = rules
|
|
|
|
|
2017-07-07 08:41:06 +00:00
|
|
|
if (![START_CONVERSATION, STEP_ACTION].includes(action.type))
|
|
|
|
return state
|
|
|
|
|
2017-07-09 22:14:22 +00:00
|
|
|
let rootVariable = action.type == START_CONVERSATION ? action.rootVariable : state.analysedSituation.root.name
|
2017-07-07 08:41:06 +00:00
|
|
|
|
2017-09-21 13:46:59 +00:00
|
|
|
let sim = findRuleByName(flatRules, rootVariable),
|
|
|
|
// Hard assumptions cannot be changed, they are used to specialise a simulator
|
|
|
|
hardAssumptions = R.pathOr({},['simulateur','hypothèses'],sim),
|
|
|
|
// Soft assumptions are revealed after the simulation starts, and can be changed
|
|
|
|
softAssumptions = R.pathOr({},['simulateur','par défaut'],sim),
|
|
|
|
intermediateSituation = assume(fromConversation, hardAssumptions),
|
|
|
|
completeSituation = assume(intermediateSituation,softAssumptions)
|
|
|
|
|
|
|
|
let situationGate = completeSituation(state),
|
|
|
|
analysedSituation = analyseTopDown(flatRules,rootVariable)(situationGate)
|
|
|
|
|
2017-09-26 15:20:46 +00:00
|
|
|
let newState = {
|
2017-07-07 08:41:06 +00:00
|
|
|
...state,
|
2017-09-21 13:46:59 +00:00
|
|
|
analysedSituation,
|
|
|
|
situationGate: situationGate
|
2017-07-07 08:41:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (action.type == START_CONVERSATION) {
|
|
|
|
return {
|
2017-09-26 15:20:46 +00:00
|
|
|
...newState,
|
2017-07-31 16:16:25 +00:00
|
|
|
foldedSteps: [],
|
2017-09-26 15:20:46 +00:00
|
|
|
unfoldedSteps: buildNextSteps(situationGate, flatRules, newState.analysedSituation)
|
2017-07-07 08:41:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (action.type == STEP_ACTION && action.name == 'fold') {
|
2017-09-21 13:46:59 +00:00
|
|
|
let foldedSteps = [...state.foldedSteps, R.head(state.unfoldedSteps)],
|
2017-09-26 15:20:46 +00:00
|
|
|
unfoldedSteps = buildNextSteps(situationGate, flatRules, newState.analysedSituation)
|
2017-09-21 13:46:59 +00:00
|
|
|
|
|
|
|
// The simulation is "over" - except we can now fill in extra questions
|
|
|
|
// where the answers were previously given reasonable assumptions
|
|
|
|
if (unfoldedSteps.length == 0 && !R.isEmpty(softAssumptions)) {
|
|
|
|
let newSituation = intermediateSituation(state),
|
|
|
|
reanalyse = analyseTopDown(flatRules,rootVariable)(newSituation),
|
|
|
|
extraSteps = buildNextSteps(newSituation, flatRules, reanalyse)
|
|
|
|
|
|
|
|
return {
|
2017-09-26 15:20:46 +00:00
|
|
|
...newState,
|
2017-09-21 13:46:59 +00:00
|
|
|
foldedSteps,
|
|
|
|
extraSteps,
|
2017-09-26 15:20:46 +00:00
|
|
|
unfoldedSteps: []
|
2017-09-21 13:46:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-07 08:41:06 +00:00
|
|
|
return {
|
2017-09-26 15:20:46 +00:00
|
|
|
...newState,
|
2017-09-21 13:46:59 +00:00
|
|
|
foldedSteps,
|
|
|
|
unfoldedSteps
|
2017-07-07 08:41:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (action.type == STEP_ACTION && action.name == 'unfold') {
|
|
|
|
let stepFinder = R.propEq('name', action.step),
|
2017-09-21 13:46:59 +00:00
|
|
|
foldedSteps = R.reject(stepFinder)(state.foldedSteps),
|
|
|
|
extraSteps = R.reject(stepFinder)(state.extraSteps)
|
2017-07-07 08:41:06 +00:00
|
|
|
|
|
|
|
return {
|
2017-09-26 15:20:46 +00:00
|
|
|
...newState,
|
2017-07-07 08:41:06 +00:00
|
|
|
foldedSteps,
|
2017-09-21 13:46:59 +00:00
|
|
|
extraSteps,
|
|
|
|
unfoldedSteps: [R.find(stepFinder)(R.concat(state.foldedSteps,state.extraSteps))]
|
2017-07-07 08:41:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-10 18:22:44 +00:00
|
|
|
|
|
|
|
function themeColours(state = computeThemeColours(), {type, colour}) {
|
2017-07-08 11:28:50 +00:00
|
|
|
if (type == CHANGE_THEME_COLOUR)
|
2017-01-10 18:22:44 +00:00
|
|
|
return computeThemeColours(colour)
|
|
|
|
else return state
|
|
|
|
}
|
|
|
|
|
2017-02-09 17:38:51 +00:00
|
|
|
function explainedVariable(state = null, {type, variableName=null}) {
|
|
|
|
switch (type) {
|
|
|
|
case EXPLAIN_VARIABLE:
|
2017-02-09 17:15:25 +00:00
|
|
|
return variableName
|
2017-02-09 17:38:51 +00:00
|
|
|
default:
|
|
|
|
return state
|
|
|
|
}
|
2017-02-08 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
2017-03-15 15:26:00 +00:00
|
|
|
function pointedOutObjectives(state=[], {type, objectives}) {
|
|
|
|
switch (type) {
|
|
|
|
case POINT_OUT_OBJECTIVES:
|
|
|
|
return objectives
|
|
|
|
default:
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-10 18:22:44 +00:00
|
|
|
export default reduceReducers(
|
|
|
|
combineReducers({
|
2017-05-18 14:04:23 +00:00
|
|
|
sessionId: (id = Math.floor(Math.random() * 1000000000000) + '') => id,
|
2017-01-10 18:22:44 +00:00
|
|
|
// this is handled by redux-form, pas touche !
|
|
|
|
form: formReducer,
|
|
|
|
|
|
|
|
/* Have forms been filled or ignored ?
|
|
|
|
false means the user is reconsidering its previous input */
|
2017-04-24 18:03:38 +00:00
|
|
|
foldedSteps: (steps = []) => steps,
|
2017-09-21 13:46:59 +00:00
|
|
|
extraSteps: (steps = []) => steps,
|
2017-04-24 18:03:38 +00:00
|
|
|
unfoldedSteps: (steps = []) => steps,
|
2017-01-10 18:22:44 +00:00
|
|
|
|
|
|
|
analysedSituation: (state = []) => state,
|
|
|
|
|
2017-09-21 13:46:59 +00:00
|
|
|
situationGate: (state = state => name => null) => state,
|
|
|
|
refine: (state = false) => state,
|
|
|
|
|
2017-02-08 16:50:22 +00:00
|
|
|
themeColours,
|
|
|
|
|
2017-03-15 15:26:00 +00:00
|
|
|
explainedVariable,
|
|
|
|
|
2017-04-24 18:03:38 +00:00
|
|
|
pointedOutObjectives,
|
2017-01-10 18:22:44 +00:00
|
|
|
}),
|
|
|
|
// cross-cutting concerns because here `state` is the whole state tree
|
2017-04-28 15:03:34 +00:00
|
|
|
reduceSteps
|
2017-01-10 18:22:44 +00:00
|
|
|
)
|