Nettoyage d'automne; première brique, la recherche de cotisations
parent
2fa97f1516
commit
3075b83963
11
IDEES.ms
11
IDEES.ms
|
@ -1,11 +0,0 @@
|
|||
Prochaine étape :
|
||||
- mettre les données dans le state : ce qu'on veut c'est la liste de variables fusionnées.
|
||||
- en déduire, à partir de la liste des sélections user elle aussi dans le state, la liste des variables à afficher
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- Afficher du YAML simplifié
|
||||
En fonction des valeurs des variables de la simulation, ou des choix de l'utilisateur dans les étiquettes, le parser YAML doit être capable de nous donner une vue simplifiée de la variable.
|
||||
Ex. en choisissant 'salarié cadre', je dois pouvoir calculer l'AGFF mais surtout obtenir une description du calcul, qui peut être dans un premier temps un bout de YAML "résolu", cad où tout ce qui ne concerne pas les cadre a été enlevé.
|
19
actions.js
19
actions.js
|
@ -1,19 +0,0 @@
|
|||
/* TAGS */
|
||||
export let SELECT_TAG = 'SELECT_TAG'
|
||||
|
||||
export function selectTag(tagName, tagValue) {
|
||||
return {type: SELECT_TAG, tagName, tagValue}
|
||||
}
|
||||
|
||||
export let RESET_TAGS = 'RESET_TAGS'
|
||||
|
||||
export function resetTags() {
|
||||
return {type: RESET_TAGS}
|
||||
}
|
||||
|
||||
/* VARIBALES */
|
||||
export let SELECT_VARIABLE = 'SELECT_VARIABLE'
|
||||
|
||||
export function selectVariable(name) {
|
||||
return {type: SELECT_VARIABLE, name}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
#top-input-variables {
|
||||
text-align: center;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {usedVariables} from '../selectors/usedVariables'
|
||||
import './Analyse.css'
|
||||
|
||||
let mapStateToProps = state => (
|
||||
{
|
||||
usedVariables: usedVariables(state)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@connect(mapStateToProps)
|
||||
export default class Analyse extends Component {
|
||||
state = {variableStats: null}
|
||||
render() {
|
||||
let {variableStats} = this.state
|
||||
return (
|
||||
<div>
|
||||
{variableStats ?
|
||||
<ul id="top-input-variables">
|
||||
{variableStats.map(([name, count]) =>
|
||||
<li key={name}>{name} {count}</li>
|
||||
)}
|
||||
</ul>
|
||||
: <div>En attente...</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
componentDidMount(){
|
||||
this.props.usedVariables.then(
|
||||
variables => this.setState({variableStats: variables})
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
body {
|
||||
font-family: Open Sans;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#header {
|
||||
text-align: center;
|
||||
background: blue;
|
||||
color: red;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-weight: 200;
|
||||
font-size: 215%;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#tag-navigation {
|
||||
font-size: 130%;
|
||||
font-weight: 300;
|
||||
width: 20%;
|
||||
padding: 1em;
|
||||
display: inline-block;
|
||||
}
|
||||
#tag-navigation h2 {
|
||||
font-size: 120%;
|
||||
font-weight: 300;
|
||||
margin-top: 0;
|
||||
opacity: .65;
|
||||
padding-bottom: .3em;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin-bottom: 0
|
||||
}
|
||||
#tag-navigation .content {
|
||||
padding-top: 1.5em;
|
||||
border-right: 1px solid #ccc;
|
||||
|
||||
}
|
||||
|
||||
#to-select > li {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.choices {
|
||||
margin-top: .6em;
|
||||
}
|
||||
.tag-value {
|
||||
font-size: 75%;
|
||||
font-weight: 400;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
margin-right: 1em;
|
||||
color: #2980b9;
|
||||
border: 1px solid #2980b9;
|
||||
padding: .05em .6em;
|
||||
cursor: pointer;
|
||||
border-radius: .1em
|
||||
}
|
||||
#to-select span.nb {
|
||||
font-size: 60%;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#selected {
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 1em;
|
||||
margin-bottom: 1em;
|
||||
font-weight: 500;
|
||||
color: #16a085;
|
||||
}
|
||||
|
||||
.tag-map .tag-value {
|
||||
margin-left: 1em;
|
||||
background: #16a085;
|
||||
border: none;
|
||||
color: white;
|
||||
margin-bottom: .5em;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#selected button {
|
||||
background: none;
|
||||
border: none;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
color: #16a085;
|
||||
}
|
||||
|
||||
#variables {
|
||||
width: 70%;
|
||||
display: inline-block;
|
||||
padding: 3%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.variable {
|
||||
width: 8em;
|
||||
min-height: 7em;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 1em;
|
||||
margin-right: 1em;
|
||||
padding: 1em 2em;
|
||||
text-align: center;
|
||||
}
|
||||
.variable h3 {
|
||||
font-weight: 400;
|
||||
font-size: 120%;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.variable ul {
|
||||
padding-left: .6em
|
||||
}
|
||||
|
||||
#selected-variable {
|
||||
width: 70%;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#selected-variable h1 {
|
||||
text-transform: capitalize;
|
||||
font-size: 180%;
|
||||
}
|
||||
|
||||
#selected-variable p {
|
||||
width: 50%;
|
||||
margin: 0 auto;
|
||||
border: 1px solid #ddd;
|
||||
border-left: 2px solid #ddd;
|
||||
padding: 1em 2em;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
import React from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import TagNavigation from '../components/TagNavigation'
|
||||
import Variables from '../components/Variables'
|
||||
import * as actions from '../actions'
|
||||
import {bindActionCreators} from 'redux'
|
||||
import {tagsToSelectSelector, variablesSelector} from '../selectors/selectors'
|
||||
|
||||
|
||||
class Explorer extends React.Component {
|
||||
render() {
|
||||
let {variables, selectedTags, selectedVariable, tagsToSelect, actions: {selectTag, resetTags, selectVariable}} = this.props
|
||||
return (
|
||||
<div>
|
||||
<h1>Les prélèvements sociaux sur les salaires</h1>
|
||||
<TagNavigation selectedTags={selectedTags} tagsToSelect={tagsToSelect} selectTag={selectTag} resetTags={resetTags}/>
|
||||
<Variables variables={variables}
|
||||
selectedTags={selectedTags} selectedVariable={selectedVariable}
|
||||
selectVariable={selectVariable}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const mapStateToProps = state => (
|
||||
{
|
||||
selectedTags: state.selectedTags,
|
||||
tagsToSelect: tagsToSelectSelector(state),
|
||||
variables: variablesSelector(state),
|
||||
selectedVariable: state.selectedVariable
|
||||
}
|
||||
)
|
||||
|
||||
const actionsToProps = dispatch => ({
|
||||
actions: bindActionCreators(actions, dispatch)
|
||||
})
|
||||
|
||||
const VariableExplorer = connect(mapStateToProps, actionsToProps)(Explorer)
|
||||
|
||||
export default VariableExplorer
|
|
@ -1,10 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
|
||||
export default class Layout extends Component {
|
||||
render() {
|
||||
return (<div>
|
||||
<div id="header">En-tête</div>
|
||||
{this.props.children}
|
||||
</div>)
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>prel2</title>
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700,300' rel='stylesheet' type='text/css'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:200,300,400,700' rel='stylesheet' type='text/css'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
83
model.js
83
model.js
|
@ -1,83 +0,0 @@
|
|||
import parameters from './load-parameters'
|
||||
import deepAssign from 'deep-assign'
|
||||
import R from 'ramda'
|
||||
|
||||
/* Fonctions utiles */
|
||||
let
|
||||
hasHistoryProp = R.pipe(JSON.stringify, R.contains('"historique":')),
|
||||
itemHasHistoricProp = (item, prop) => R.has(prop)(item) && hasHistoryProp(item[prop]),
|
||||
itemIsCalculable = item => false
|
||||
|| itemHasHistoricProp(item, 'linear')
|
||||
|| itemHasHistoricProp(item, 'marginalRateTaxScale')
|
||||
|| item['+'] || item['-']
|
||||
|| item['logique'] || item['logique numérique']
|
||||
,
|
||||
|
||||
/*
|
||||
L'attribut tags est une hash map,
|
||||
ou une liste
|
||||
- de clés, qui sont un moyen courant d'exprimer [clef]: oui
|
||||
- de hash maps.
|
||||
Cette fonction fusionne tout ceci dans un objet
|
||||
|
||||
*/
|
||||
handleHybridTags = R.when(R.isArrayLike,
|
||||
R.reduce((final, tag) =>
|
||||
R.merge(final, R.is(Object, tag) ? tag : {[tag]: 'oui'})
|
||||
, {})
|
||||
),
|
||||
|
||||
tagsConflict = (tags1, tags2) =>
|
||||
R.compose(
|
||||
R.any(R.identity),
|
||||
R.values,
|
||||
R.mapObjIndexed((tagValue, tag) => tags2[tag] != undefined && tags2[tag] !== tagValue)
|
||||
)(tags1)
|
||||
|
||||
let
|
||||
groupedItemsByVariable = R.pipe(
|
||||
// Desugar tags
|
||||
R.map(p => R.merge(p, {tags: handleHybridTags(p.tags) || {}})),
|
||||
R.groupBy(R.prop('variable'))
|
||||
)(parameters),
|
||||
mergedItemsByVariable =
|
||||
R.mapObjIndexed((variableItems, name) => {
|
||||
/* Les items sont des fragments de variables.
|
||||
Les premiers fragments vont être fusionnés dans les suivants,
|
||||
sauf s'ils provoquent l'écrasement d'un tag */
|
||||
let mergedVariableItems = R.tail(variableItems) // Le premier ne peut être étendu
|
||||
.reduce((mergedItems, item) =>
|
||||
[ ...mergedItems,
|
||||
mergedItems.reduce((final, higherLevelItem) => {
|
||||
let oups = tagsConflict(higherLevelItem.tags, item.tags)
|
||||
return oups ? item : deepAssign({}, item, higherLevelItem)
|
||||
}, item)
|
||||
], R.of(R.head(variableItems)))
|
||||
|
||||
return {
|
||||
name,
|
||||
// La variable de haut niveau, contenant la plupart du temps une description, etc.
|
||||
first: R.head(variableItems),
|
||||
// Tous les tags qui peuvent être trouvés dans les items de cette variable
|
||||
tags: R.pipe(
|
||||
R.pluck('tags'),
|
||||
R.map(R.map(R.of)),
|
||||
R.reduce(R.mergeWith(R.union), {})
|
||||
)(variableItems),
|
||||
// Gardons seulement les variables ayant une implémentation : capable de faire un calcul
|
||||
calculable: R.filter(itemIsCalculable)(mergedVariableItems)
|
||||
}}
|
||||
)(groupedItemsByVariable),
|
||||
mergedItems = R.values(mergedItemsByVariable),
|
||||
calculableItems =
|
||||
R.pipe(
|
||||
R.values,
|
||||
R.pluck('calculable'),
|
||||
R.unnest
|
||||
)(mergedItemsByVariable)
|
||||
|
||||
export {
|
||||
groupedItemsByVariable,
|
||||
calculableItems,
|
||||
mergedItems
|
||||
}
|
|
@ -55,6 +55,6 @@
|
|||
"yaml-loader": "^0.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
"start": "node source/server.js"
|
||||
}
|
||||
}
|
||||
|
|
35
reducers.js
35
reducers.js
|
@ -1,35 +0,0 @@
|
|||
|
||||
import { combineReducers } from 'redux'
|
||||
import { SELECT_TAG, SELECT_VARIABLE, RESET_TAGS} from './actions'
|
||||
|
||||
function selectedTags(state = [], {type, tagName, tagValue}) {
|
||||
switch (type) {
|
||||
case SELECT_TAG:
|
||||
return [...state, [tagName, tagValue]]
|
||||
case RESET_TAGS:
|
||||
return []
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function selectedVariable(state = null, {type, name}) {
|
||||
switch (type) {
|
||||
case SELECT_VARIABLE:
|
||||
return name
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function rootVariables(state = ['cout du travail']) {
|
||||
return state
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default combineReducers({
|
||||
selectedTags,
|
||||
selectedVariable,
|
||||
rootVariables
|
||||
})
|
|
@ -1,57 +0,0 @@
|
|||
import { createSelector } from 'reselect'
|
||||
import {calculableItems, higherOrderVariables, mergedItems} from '../model'
|
||||
import R from 'ramda'
|
||||
|
||||
let
|
||||
// variableHasTagValue = variable => ([tag, value]) => console.log('tv', variable.tags, tag, value),
|
||||
variableHasTagValue = variable => ([osef, [tag, value]]) => R.pathEq(['tags', tag], value)(variable),
|
||||
variableHasSelectedTags = variable => R.compose(
|
||||
R.all(variableHasTagValue(variable)),
|
||||
R.toPairs
|
||||
),
|
||||
filterVariables = variables => tags => R.filter(item => variableHasSelectedTags(item)(tags))(variables)
|
||||
|
||||
export const finalVariablesSelector = createSelector(
|
||||
[state => state.selectedTags],
|
||||
filterVariables(calculableItems)
|
||||
)
|
||||
|
||||
/* Tag names, values, and number of variables per tag */
|
||||
const unorderedTagStats = finalVariables =>
|
||||
finalVariables
|
||||
.reduce((stats, variable) => {
|
||||
Object.keys(variable.tags).map(
|
||||
k => {
|
||||
stats[k] = stats[k] || {number: 0, choices: new Set()}
|
||||
stats[k].number = stats[k].number + 1
|
||||
stats[k].choices.add(variable.tags[k])
|
||||
}
|
||||
)
|
||||
return stats
|
||||
}, {}),
|
||||
|
||||
tagStats = stats =>
|
||||
Object.keys(stats)
|
||||
.reduce((acc, n) => ([...acc, {name: n, ...stats[n]}]), [])
|
||||
.sort((a, b) => b.number - a.number)
|
||||
|
||||
let tagStatsSelector = createSelector(
|
||||
[finalVariablesSelector],
|
||||
variables => tagStats(unorderedTagStats(variables))
|
||||
)
|
||||
|
||||
export let tagsToSelectSelector = createSelector(
|
||||
[state => state.selectedTags, tagStatsSelector],
|
||||
(selectedTags, availableTags) =>
|
||||
availableTags.filter(t => !selectedTags.find(([name]) => t.name === name))
|
||||
)
|
||||
|
||||
export let variablesSelector = createSelector(
|
||||
[state => state.selectedTags],
|
||||
selectedTags => R.filter(
|
||||
({tags}) =>
|
||||
R.all(
|
||||
([tag, value]) => tags[tag] && R.contains(value, tags[tag]),
|
||||
)(selectedTags)
|
||||
)(mergedItems)
|
||||
)
|
|
@ -1,111 +0,0 @@
|
|||
import R from 'ramda'
|
||||
// used to aseptise all yaml text, to avoid doing regexps on accents...
|
||||
import removeDiacritics from '../utils/remove-diacritics'
|
||||
|
||||
/* When you encounter a variable, it may have a calculable key.
|
||||
This file provides an object with these mappings :
|
||||
calculable-key -> extract variables called by the calculation value */
|
||||
|
||||
let scalarMult = value => {
|
||||
value = removeDiacritics(value)
|
||||
// 4 * assiette cotisations sociales
|
||||
let match = /(?:([0-9])*\s\*\s)?((?:[a-z0-9]|\s|_)+)/g.exec(value)
|
||||
if (match) return match[2]
|
||||
}
|
||||
|
||||
let logicalCondition = value => {
|
||||
value = removeDiacritics(value)
|
||||
let match, variable
|
||||
// ! ma variable
|
||||
if (R.contains('!')(value)) {
|
||||
match = /!((?:[a-z0-9]|\s|_)+)/g.exec(value)
|
||||
if (match) return match[1]
|
||||
}
|
||||
|
||||
// département établissement ⊂ [57 67 68]
|
||||
if (R.contains('⊂')(value)) {
|
||||
match = /((?:[a-z0-9]|\s|_)+)⊂*/g.exec(value)
|
||||
if (match) return match[1]
|
||||
}
|
||||
|
||||
// ma cotisation > 20
|
||||
match = /((?:[a-z0-9]|\s|_)+)([<|>]=?)\s+[0-9]+/g.exec(value)
|
||||
if (match) return match[1]
|
||||
|
||||
// 20 <= ma cotisation
|
||||
match = /[0-9]+\s+([<|>]=?)((?:[a-z0-9]|\s|_)+)/g.exec(value)
|
||||
if (match) return match[2]
|
||||
|
||||
// ma variable = xxxx z
|
||||
match = /((?:[a-z0-9]|\s|_)+)=((?:[a-z0-9]|\s|_)+)/g.exec(value)
|
||||
if (match) return match[1]
|
||||
|
||||
// ma variable
|
||||
match = /((?:[a-z0-9]|\s|_)+)/g.exec(value)
|
||||
if (match) return match[1]
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Ce YAML :
|
||||
- cond1
|
||||
- cond2:
|
||||
- cond2.1:
|
||||
- cond2.1.1
|
||||
- cond2.1.2
|
||||
- cond2.2
|
||||
cond2.2.1
|
||||
- cond3
|
||||
|
||||
se traduit en :
|
||||
cond1 || (
|
||||
cond2 && (
|
||||
(
|
||||
cond2.1 && (
|
||||
cond2.1.1 ||
|
||||
cond2.1.2
|
||||
)
|
||||
) || cond2.2
|
||||
)
|
||||
) || cond3
|
||||
|
||||
(':-D)
|
||||
|
||||
*/
|
||||
let logic = list => { // a list is a ||
|
||||
return list.reduce((variables, next) =>
|
||||
typeof next == 'string' ?
|
||||
[...variables, logicalCondition(next)] :
|
||||
// it's a single key object -> we're facing a && condition
|
||||
[...variables, logicalCondition(R.keys(next)[0]), ...logic(R.values(next))]
|
||||
, [])
|
||||
}
|
||||
R.uniq(logic)
|
||||
|
||||
|
||||
let plusOrMinus = R.cond([
|
||||
[R.is(String), removeDiacritics],
|
||||
[R.isArrayLike, R.map(plusOrMinus)],
|
||||
[R.is(Object), R.identity]
|
||||
])
|
||||
|
||||
let traversalGuide = {
|
||||
linear: {
|
||||
base: scalarMult,
|
||||
limit: scalarMult,
|
||||
historique: null
|
||||
// VAR/case: logic predicate
|
||||
},
|
||||
marginalRateTaxScale: {
|
||||
base: scalarMult
|
||||
// VAR/case: logic predicate
|
||||
},
|
||||
concerne: logic,
|
||||
'ne concerne pas': logic,
|
||||
logique: logic, // predicates leading to a boolean,
|
||||
'logique numérique': () => null, // predicates leading to a number
|
||||
'+': plusOrMinus, // It's a string or list of string representing variable calls, that's all we want for now :-)
|
||||
'-': plusOrMinus // Same :-)
|
||||
}
|
||||
|
||||
export default traversalGuide
|
|
@ -1,174 +0,0 @@
|
|||
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)
|
||||
|
||||
}
|
||||
)
|
|
@ -5,8 +5,6 @@ import DevTools from '../DevTools'
|
|||
import routes from '../routes'
|
||||
import {Router, browserHistory} from 'react-router'
|
||||
|
||||
import './App.css'
|
||||
|
||||
export default class App extends Component {
|
||||
render() {
|
||||
const { store } = this.props
|
|
@ -4,9 +4,6 @@ import { Provider } from 'react-redux'
|
|||
import routes from '../routes'
|
||||
import Router from 'react-router'
|
||||
|
||||
|
||||
import './App.css'
|
||||
|
||||
export default class App extends Component {
|
||||
render() {
|
||||
const { store } = this.props
|
|
@ -0,0 +1,58 @@
|
|||
#home {
|
||||
height: 400px;
|
||||
/*display:flex;
|
||||
justify-content: center;
|
||||
align-items: center;*/
|
||||
}
|
||||
|
||||
#brand {
|
||||
position: relative;
|
||||
color: #333350;
|
||||
width: 160px;
|
||||
margin: 6em auto;
|
||||
}
|
||||
|
||||
#brand img {
|
||||
display: block;
|
||||
width: 160px;
|
||||
margin-bottom: .6em;
|
||||
}
|
||||
|
||||
#brand #name {
|
||||
font-size: 250%;
|
||||
line-height: .9em;
|
||||
}
|
||||
#brand #version {
|
||||
font-style: italic;
|
||||
font-size: 110%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
#warning {
|
||||
padding: 1em 0;
|
||||
background: #8a1c1c;
|
||||
color: white;
|
||||
font-style: italic;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#search {
|
||||
width: 400px;
|
||||
margin: 4em auto;
|
||||
}
|
||||
|
||||
#search input {
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-bottom: 1px solid #333350
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import React, { Component } from 'react'
|
||||
import './Home.css'
|
||||
|
||||
export default class Home extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div id="home">
|
||||
<section id="brand">
|
||||
<img src={require('../images/logo.png')} />
|
||||
<span id="name">
|
||||
Système <br/>
|
||||
Social
|
||||
</span>
|
||||
<span id="version">alpha</span>
|
||||
</section>
|
||||
<section id="search">
|
||||
<input placeholder="ex. retraite"/>
|
||||
</section>
|
||||
|
||||
</div>)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
body {
|
||||
font-family: 'Open Sans';
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import React, { Component } from 'react'
|
||||
import './Layout.css'
|
||||
|
||||
export default class Layout extends Component {
|
||||
render() {
|
||||
return (<div>
|
||||
<div id="header"></div>
|
||||
{this.props.children}
|
||||
<div id="warning">
|
||||
Attention ! Tout le contenu de ce site est hautement expérimental.
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ render(
|
|||
anchor
|
||||
)
|
||||
|
||||
// Hot react component reloading. Unstable but helpful.
|
||||
if (module.hot) {
|
||||
module.hot.accept('./containers/App', () => {
|
||||
// If you use Webpack 2 in ES modules mode, you can
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,8 @@
|
|||
|
||||
import { combineReducers } from 'redux'
|
||||
import { } from './actions'
|
||||
|
||||
|
||||
|
||||
export default combineReducers({
|
||||
})
|
|
@ -1,14 +1,11 @@
|
|||
import React from 'react'
|
||||
import { Route, IndexRoute } from 'react-router'
|
||||
import Layout from './containers/Layout'
|
||||
import Explorer from './containers/Explorer'
|
||||
import Analyse from './containers/Analyse'
|
||||
import Home from './containers/Home'
|
||||
|
||||
export default (
|
||||
<Route path="/" component={Layout}>
|
||||
<Route path="analyse" component={Analyse} />
|
||||
<Route path="variables" component={Explorer} />
|
||||
<IndexRoute component={Explorer} />
|
||||
<IndexRoute component={Home} />
|
||||
<Route path="*" component={() => <h2>On vous a perdu !</h2>} />
|
||||
</Route>
|
||||
)
|
|
@ -2,6 +2,7 @@ import { takeEvery} from 'redux-saga'
|
|||
import { call, put} from 'redux-saga/effects'
|
||||
import Promise from 'core-js/fn/promise'
|
||||
|
||||
// Nothing happening here !
|
||||
|
||||
function* handleSubmitStep() {
|
||||
console.log('salut')
|
|
@ -8,7 +8,7 @@ module.exports = {
|
|||
'webpack/hot/only-dev-server',
|
||||
'react-hot-loader/patch',
|
||||
'babel-polyfill',
|
||||
'./entry.js'
|
||||
'./source/entry.js'
|
||||
],
|
||||
output: {
|
||||
path: require('path').resolve('./dist/'),
|
Loading…
Reference in New Issue