mon-entreprise/selectors/usedVariables.js

175 lines
4.6 KiB
JavaScript

import R from 'ramda'
import removeDiacritics from '../utils/remove-diacritics'
import { createSelector } from 'reselect'
import {variablesSelector} from './selectors'
import traversalGuide from './traversalGuide'
import dump from 'json!../adhoc-variable-dump/variables.json'
/* AIM : Parse all the variables and extract their references to other variables.
Rank the results by count. */
// Use http://regexr.com/ to understand and write regexps !!
let fetchVariables = false
let resolveVariable = (variable, name, callback) => {
if (variable == null) return callback('TODO ' + name)
let {formula} = variable
if (formula && formula['input_variables']) {
return callback({calls: formula['input_variables']})
} else {
return callback(name)
}
}
let GETAdHocVariable = name => {
let obscureName = name.trim().replace(/\s/g, '_')
if (!fetchVariables){
return new Promise(
resolve => resolveVariable(
dump.variables.find(v => v.name === obscureName),
name,
resolve)
)
}
return new Promise(resolve =>
window.fetch('https://api.openfisca.fr/api/1/variables/?name=' + obscureName)
.then(res => res.json())
.then(json => {
let {error, variables} = json
if (error && JSON.stringify(error).indexOf('Variable does not exist') + 1){
resolveVariable(null, name, resolve)
}
if (variables) {
resolveVariable(variables[0], name, resolve)
}
})
)
}
async function getAdHocVariables(id) {
let variable = await GETAdHocVariable(id)
if (R.is(String, variable))
return variable
else
return Promise.all(
variable.calls.map(getAdHocVariables)
)
}
// Returns a list of list of used variables
// recursive function
let findUsedVariables = (variables, schema, toAnalyse) =>
/*TODO
If instruction of GET call -> return Promise
1) input var OK return string
2) call to other variables -> list of promises
*/
R.cond([
[R.isNil, () => []],
[R.has('adHoc'), ({id}) => getAdHocVariables(id)],
// The traversal of variables has found a parsable variable attribute.
// Parse it to extract variables
[R.is(Function), extractor =>
R.pipe(
extractor,
R.unless(R.isArrayLike, R.of),
R.map(R.cond([
[R.is(String), name => findVariables(variables, name)],
[R.is(Object), tags => findVariables(variables, null, tags)]
])),
R.unnest,
R.map(
/*TODO
if instruction of GET call findUsedVariables(GET CALL)
*/
R.cond([
[R.has('adHoc'), adHocSpec => findUsedVariables(variables, adHocSpec)],
[R.is(String), R.identity],
[R.T, found => findUsedVariables(variables, traversalGuide, found)]
])
)
)(toAnalyse)
],
// Walk the graph using the guiding object until you find a parsable attribute
[R.is(Object), traversalObject =>
R.toPairs(traversalObject).reduce(
(res, [key, value]) => {
return toAnalyse[key] != null ?
[...res, ...findUsedVariables(variables, value, toAnalyse[key])] :
res
}, []
)],
[R.T, () => []]
])(schema)
let calculableVariables = createSelector(
[variablesSelector],
R.pipe(R.pluck('calculable'), R.unnest)
)
/************************************
Functions to find variables */
let findVariablesByName = name =>
R.filter(variable => removeDiacritics(variable.variable) == name)
let findVariablesByTags = tags =>
R.filter(R.pipe(
R.prop('tags'),
R.whereEq(tags)
))
let findVariables = (variables, name, tags) =>
R.pipe(
variables => R.is(String, name)
? findVariablesByName(name)(variables)
: variables,
variables => R.is(Object, tags)
? findVariablesByTags(tags)(variables)
: variables,
R.cond([
[ R.isEmpty, () => variableNotFound(name)],
[ (variables) => R.is(String, name) && variables.length > 1,
() => variableNameCollision(name, tags)],
[ R.T,
R.identity]
]),
)(variables)
let variableNotFound = name =>
// Should do a query to openfisca web api
({adHoc: true, id: name})
let variableNameCollision = (name, tags) =>
`More than one variable corresponds to this name, tags tuple : ${name}, ${tags}`
/*****************************************/
export let usedVariables = createSelector(
[state => state.rootVariables, calculableVariables],
(roots, variables) => {
// get all variables from these roots, rec !
return R.compose(
promises =>
Promise.all(promises).then(
R.pipe(
R.flatten,
R.countBy(R.identity),
R.toPairs,
R.sortBy(R.last),
R.reverse
)
),
R.flatten,
R.map(rootObject => findUsedVariables(variables, traversalGuide, rootObject)),
R.map(root => findVariables(variables, removeDiacritics(root))[0])
)(roots)
}
)