diff --git a/publicodes/core/source/AST/index.ts b/publicodes/core/source/AST/index.ts index bbbdcf15f3..b07baa71dd 100644 --- a/publicodes/core/source/AST/index.ts +++ b/publicodes/core/source/AST/index.ts @@ -174,8 +174,6 @@ export const traverseASTNode: TraverseFunction = (fn, node) => { return traverseUnitéNode(fn, node) case 'variations': return traverseVariationNode(fn, node) - case 'variable temporelle': - return traverseVariableTemporelle(fn, node) case 'replacementRule': return traverseReplacementNode(fn, node) default: @@ -382,17 +380,3 @@ const traverseVariationNode: TraverseFunction<'variations'> = (fn, node) => ({ consequence: fn(consequence), })), }) - -const traverseVariableTemporelle: TraverseFunction<'variable temporelle'> = ( - fn, - node -) => ({ - ...node, - explanation: { - period: { - end: node.explanation.period.end && fn(node.explanation.period.end), - start: node.explanation.period.start && fn(node.explanation.period.start), - }, - value: fn(node.explanation.value), - }, -}) diff --git a/publicodes/core/source/AST/types.ts b/publicodes/core/source/AST/types.ts index 66886e52ae..516a2fae79 100644 --- a/publicodes/core/source/AST/types.ts +++ b/publicodes/core/source/AST/types.ts @@ -23,12 +23,10 @@ import { SommeNode } from '../mecanisms/sum' import { SynchronisationNode } from '../mecanisms/synchronisation' import { TauxProgressifNode } from '../mecanisms/tauxProgressif' import { UnitéNode } from '../mecanisms/unité' -import { VariableTemporelleNode } from '../mecanisms/variableTemporelle' import { VariationNode } from '../mecanisms/variations' import { ReferenceNode } from '../reference' import { ReplacementRule } from '../replacement' import { RuleNode } from '../rule' -import { Temporal } from '../temporal' export type ConstantNode = { type: 'boolean' | 'objet' | 'number' | 'string' @@ -64,7 +62,6 @@ export type ASTNode = ( | SynchronisationNode | TauxProgressifNode | UnitéNode - | VariableTemporelleNode | VariationNode | ConstantNode | ReplacementRule @@ -109,12 +106,10 @@ export type Unit = { } // Idée : une évaluation est un n-uple : (value, unit, missingVariable, isApplicable) -// Une temporalEvaluation est une liste d'evaluation sur chaque période. : [(Evaluation, Period)] type EvaluationDecoration = { nodeValue: Evaluation missingVariables: Record unit?: Unit - temporalValue?: Temporal } export type Types = number | boolean | string | Record export type Evaluation = T | false | null diff --git a/publicodes/core/source/grammar.ne b/publicodes/core/source/grammar.ne index 93c12ff1f0..2a2207d592 100644 --- a/publicodes/core/source/grammar.ne +++ b/publicodes/core/source/grammar.ne @@ -7,8 +7,7 @@ @{% const { - string, date, variable, temporalNumericValue, binaryOperation, - unaryOperation, boolean, number, numberWithUnit, JSONObject + string, date, variable, binaryOperation, unaryOperation, boolean, number, numberWithUnit, JSONObject } = require('./grammarFunctions') const moo = require("moo"); @@ -61,11 +60,6 @@ main -> NumericValue -> AdditionSubstraction {% id %} | Negation {% id %} - | TemporalNumericValue {% id %} - -TemporalNumericValue -> - NumericValue %space %periodWord %space %date {% ([value,,word,,dateString]) => temporalNumericValue(value, word, date([dateString])) %} - | NumericValue %space %periodWord %colon Date {% ([value,,word,,date]) => temporalNumericValue(value, word, date) %} NumericTerminal -> Variable {% id %} diff --git a/publicodes/core/source/grammarFunctions.js b/publicodes/core/source/grammarFunctions.js index 70d7c87228..0433059093 100644 --- a/publicodes/core/source/grammarFunctions.js +++ b/publicodes/core/source/grammarFunctions.js @@ -1,7 +1,6 @@ /* Those are postprocessor functions for the Nearley grammar.ne. The advantage of putting them here is to get prettier's JS formatting, since Nealrey doesn't support it https://github.com/kach/nearley/issues/310 */ import { normalizeDateString } from './date' -import { parsePeriod } from './temporal' export let binaryOperation = (operationType) => ([A, , operator, , B]) => ({ [operator]: { @@ -17,13 +16,6 @@ export let unaryOperation = (operationType) => ([operator, , A]) => ({ }, }) -export let temporalNumericValue = (variable, word, date) => ({ - temporalValue: { - explanation: variable, - period: parsePeriod(word.value.slice(2), date), - }, -}) - export let variable = ([firstFragment, nextFragment], _, reject) => { const fragments = [firstFragment, ...nextFragment].map(({ value }) => value) if (!nextFragment.length && ['oui', 'non'].includes(firstFragment)) { diff --git a/publicodes/core/source/mecanisms/product.ts b/publicodes/core/source/mecanisms/product.ts index f56195126d..2d636700ed 100644 --- a/publicodes/core/source/mecanisms/product.ts +++ b/publicodes/core/source/mecanisms/product.ts @@ -1,7 +1,7 @@ import { EvaluationFunction } from '..' import { ASTNode } from '../AST/types' import { warning } from '../error' -import { defaultNode, parseObject } from '../evaluation' +import { defaultNode, mergeAllMissing, parseObject } from '../evaluation' import { registerEvaluationFunction } from '../evaluationFunctions' import { convertNodeToUnit, simplifyNodeUnit } from '../nodeUnits' import { areUnitConvertible, convertUnit, inferUnit } from '../units' @@ -72,11 +72,17 @@ const evaluateProduit: EvaluationFunction<'produit'> = function (node) { nodeValue = convertUnit(unit, assiette.unit, nodeValue) unit = assiette.unit } + return simplifyNodeUnit({ + ...node, + missingVariables: mergeAllMissing([assiette, taux, facteur, plafond]), nodeValue, unit, - explanation: { + assiette, + taux, + facteur, + plafond, plafondActif: (assiette.nodeValue as any) > (plafond as any).nodeValue, }, }) diff --git a/publicodes/core/source/mecanisms/variableTemporelle.ts b/publicodes/core/source/mecanisms/variableTemporelle.ts deleted file mode 100644 index f16edd8769..0000000000 --- a/publicodes/core/source/mecanisms/variableTemporelle.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { EvaluationFunction } from '..' -import { ASTNode } from '../AST/types' -import { registerEvaluationFunction } from '../evaluationFunctions' -import parse from '../parse' -import { - createTemporalEvaluation, - narrowTemporalValue, - Temporal, - temporalAverage, -} from '../temporal' - -export type VariableTemporelleNode = { - explanation: { - period: { - start: ASTNode | undefined - end: ASTNode | undefined - } - value: ASTNode - } - nodeKind: 'variable temporelle' -} - -const evaluate: EvaluationFunction<'variable temporelle'> = function ( - node: any -) { - const start = - node.explanation.period.start && - this.evaluate(node.explanation.period.start) - const end = - node.explanation.period.end && this.evaluate(node.explanation.period.end) - const value = this.evaluate(node.explanation.value) - const period = { - start: start?.nodeValue || null, - end: end?.nodeValue || null, - } - const temporalValue = value.temporalValue - ? narrowTemporalValue(period, value.temporalValue) - : createTemporalEvaluation(value.nodeValue, period) - // TODO explanation missingVariables / period missing variables - return { - ...node, - nodeValue: temporalAverage(temporalValue as Temporal, value.unit), - temporalValue, - explanation: { - period: { start, end }, - value, - }, - ...('unit' in value && { unit: value.unit }), - } -} - -export default function parseVariableTemporelle( - v, - context -): VariableTemporelleNode { - const explanation = parse(v.explanation, context) - return { - nodeKind: 'variable temporelle', - explanation: { - period: { - start: v.period.start && parse(v.period.start, context), - end: v.period.end && parse(v.period.end, context), - }, - value: explanation, - }, - } -} - -registerEvaluationFunction('variable temporelle', evaluate) diff --git a/publicodes/core/source/parse.ts b/publicodes/core/source/parse.ts index 46bbb96ba5..68a8c871bb 100644 --- a/publicodes/core/source/parse.ts +++ b/publicodes/core/source/parse.ts @@ -28,7 +28,6 @@ import { mecanismSum } from './mecanisms/sum' import { mecanismSynchronisation } from './mecanisms/synchronisation' import tauxProgressif from './mecanisms/tauxProgressif' import unité from './mecanisms/unité' -import variableTemporelle from './mecanisms/variableTemporelle' import variations, { devariate } from './mecanisms/variations' import { Context } from './parsePublicodes' import parseReference from './reference' @@ -190,7 +189,6 @@ const parseFunctions = { somme: mecanismSum, multiplication: mecanismProduct, produit: mecanismProduct, - temporalValue: variableTemporelle, barème, grille, 'taux progressif': tauxProgressif, diff --git a/publicodes/core/source/temporal.ts b/publicodes/core/source/temporal.ts deleted file mode 100644 index 14cbd2e796..0000000000 --- a/publicodes/core/source/temporal.ts +++ /dev/null @@ -1,430 +0,0 @@ -import { - convertToDate, - getDifferenceInDays, - getDifferenceInMonths, - getDifferenceInYears, - getRelativeDate, - getYear, -} from './date' -import { Unit, Evaluation, Types, ASTNode, EvaluatedNode } from './AST/types' - -export type Period = { - start: T | null - end: T | null -} - -export function parsePeriod(word: string, date: Date): Period { - const startWords = [ - 'depuis', - 'depuis le', - 'depuis la', - 'à partir de', - 'à partir du', - 'du', - ] - const endWords = [ - "jusqu'à", - "jusqu'au", - "jusqu'à la", - 'avant', - 'avant le', - 'avant la', - 'au', - ] - const intervalWords = ['le', 'en'] - if (!startWords.concat(endWords, intervalWords).includes(word)) { - throw new SyntaxError( - `Le mot clé '${word}' n'est pas valide. Les mots clés possible sont les suivants :\n\t ${startWords.join( - ', ' - )}` - ) - } - if (word === 'le') { - return { - start: date, - end: date, - } - } - if (word === 'en') { - return { start: null, end: null } - } - if (startWords.includes(word)) { - return { - start: date, - end: null, - } - } - if (endWords.includes(word)) { - return { - start: null, - end: date, - } - } - throw new Error('Non implémenté') -} - -export type TemporalNode = Temporal -export type Temporal = Array & { value: T }> - -export function narrowTemporalValue( - period: Period, - temporalValue: Temporal> -): Temporal> { - return liftTemporal2( - (value, filter) => filter && value, - temporalValue, - createTemporalEvaluation(true, period) - ) -} - -// Returns a temporal value that's true for the given period and false otherwise. -export function createTemporalEvaluation( - value: Evaluation, - period: Period = { start: null, end: null } -): Temporal> { - const temporalValue = [{ ...period, value }] - if (period.start != null) { - temporalValue.unshift({ - start: null, - end: getRelativeDate(period.start, -1), - value: false, - }) - } - if (period.end != null) { - temporalValue.push({ - start: getRelativeDate(period.end, 1), - end: null, - value: false, - }) - } - return temporalValue -} - -export function pureTemporal(value: T): Temporal { - return [{ start: null, end: null, value }] -} - -export function mapTemporal( - fn: (value: T1) => T2, - temporalValue: Temporal -): Temporal { - return temporalValue.map(({ start, end, value }) => ({ - start, - end, - value: fn(value), - })) -} -export function sometime( - fn: (value: T1) => boolean, - temporalValue: Temporal -): boolean { - return temporalValue.some(({ start, end, value }) => fn(value)) -} - -export function liftTemporal2( - fn: (value1: T1, value2: T2) => T3, - temporalValue1: Temporal, - temporalValue2: Temporal -): Temporal { - return mapTemporal( - ([a, b]) => fn(a, b), - zipTemporals(temporalValue1, temporalValue2) - ) -} - -export function concatTemporals( - temporalValues: Array> -): Temporal> { - return temporalValues.reduce( - (values, value) => liftTemporal2((a, b) => [...a, b], values, value), - pureTemporal([]) as Temporal> - ) -} - -export function liftTemporalNode( - node: N -): Temporal>> { - if (!('temporalValue' in node)) { - return pureTemporal(node) - } - const { temporalValue, ...baseNode } = node as N & { - temporalValue: Temporal> - } - return mapTemporal( - (nodeValue) => ({ - ...baseNode, - nodeValue, - }), - temporalValue - ) -} - -export function zipTemporals( - temporalValue1: Temporal, - temporalValue2: Temporal, - acc: Temporal<[T1, T2]> = [] -): Temporal<[T1, T2]> { - if (!temporalValue1.length && !temporalValue2.length) { - return acc - } - const [value1, ...rest1] = temporalValue1 - const [value2, ...rest2] = temporalValue2 - console.assert(value1.start === value2.start) - const endDateComparison = compareEndDate(value1.end, value2.end) - - // End dates are equals - if (endDateComparison === 0) { - return zipTemporals(rest1, rest2, [ - ...acc, - { ...value1, value: [value1.value, value2.value] }, - ]) - } - // Value1 lasts longuer than value1 - if (endDateComparison > 0) { - console.assert(value2.end !== null) - return zipTemporals( - [ - { ...value1, start: getRelativeDate(value2.end as string, 1) }, - ...rest1, - ], - rest2, - [ - ...acc, - { - ...value2, - value: [value1.value, value2.value], - }, - ] - ) - } - - // Value2 lasts longuer than value1 - if (endDateComparison < 0) { - console.assert(value1.end !== null) - return zipTemporals( - rest1, - [ - { ...value2, start: getRelativeDate(value1.end as string, 1) }, - ...rest2, - ], - [ - ...acc, - { - ...value1, - value: [value1.value, value2.value], - }, - ] - ) - } - throw new EvalError('All case should have been covered') -} - -function beginningOfNextYear(date: string): string { - return `01/01/${getYear(date) + 1}` -} - -function endsOfPreviousYear(date: string): string { - return `31/12/${getYear(date) - 1}` -} - -function splitStartsAt( - fn: (date: string) => string, - temporal: Temporal -): Temporal { - return temporal.reduce((acc, period) => { - const { start, end } = period - const newStart = start === null ? start : fn(start) - if (compareEndDate(newStart, end) !== -1) { - return [...acc, period] - } - console.assert(newStart !== null) - return [ - ...acc, - { ...period, end: getRelativeDate(newStart as string, -1) }, - { ...period, start: newStart }, - ] - }, [] as Temporal) -} - -function splitEndsAt( - fn: (date: string) => string, - temporal: Temporal -): Temporal { - return temporal.reduce((acc, period) => { - const { start, end } = period - const newEnd = end === null ? end : fn(end) - if (compareStartDate(start, newEnd) !== -1) { - return [...acc, period] - } - console.assert(newEnd !== null) - return [ - ...acc, - { ...period, end: newEnd }, - { ...period, start: getRelativeDate(newEnd as string, 1) }, - ] - }, [] as Temporal) -} - -export function groupByYear(temporalValue: Temporal): Array> { - return ( - // First step: split period by year if needed - splitEndsAt( - endsOfPreviousYear, - splitStartsAt(beginningOfNextYear, temporalValue) - ) - // Second step: group period by year - .reduce((acc, period) => { - const [currentTemporal, ...otherTemporal] = acc - if (currentTemporal === undefined) { - return [[period]] - } - const firstPeriod = currentTemporal[0] - console.assert( - firstPeriod !== undefined && - firstPeriod.end !== null && - period.start !== null, - 'invariant non verifié' - ) - if ( - (firstPeriod.end as string).slice(-4) !== - (period.start as string).slice(-4) - ) { - return [[period], ...acc] - } - return [[...currentTemporal, period], ...otherTemporal] - }, [] as Array>) - .reverse() - ) -} - -function simplify(temporalValue: Temporal): Temporal { - return temporalValue -} - -function compareStartDate( - dateA: string | null, - dateB: string | null -): -1 | 0 | 1 { - if (dateA == dateB) { - return 0 - } - if (dateA == null) { - return -1 - } - if (dateB == null) { - return 1 - } - return convertToDate(dateA) < convertToDate(dateB) ? -1 : 1 -} - -function compareEndDate( - dateA: string | null, - dateB: string | null -): -1 | 0 | 1 { - if (dateA == dateB) { - return 0 - } - if (dateA == null) { - return 1 - } - if (dateB == null) { - return -1 - } - return convertToDate(dateA) < convertToDate(dateB) ? -1 : 1 -} - -export function temporalAverage( - temporalValue: Temporal>, - unit?: Unit -): Evaluation { - temporalValue = temporalValue.filter(({ value }) => value !== false) - if (!temporalValue.length) { - return false - } - if (temporalValue.length === 1) { - return temporalValue[0].value - } - - if (temporalValue.some(({ value }) => value == null)) { - return null - } - - const temporalNumber = temporalValue as Temporal - const first = temporalNumber[0] - const last = temporalNumber[temporalNumber.length - 1] - - // La variable est définie sur un interval infini - if (first.start == null || last.end == null) { - if (first.start != null) { - return last.value - } - if (last.end != null) { - return first.value - } - return (first.value + last.value) / 2 - } - - let totalWeight = 0 - const weights = temporalNumber.map(({ start, end, value }) => { - ;[start, end] = [start, end] as [string, string] - let weight = 0 - if (unit?.denominators.includes('mois')) { - weight = getDifferenceInMonths(start, end) - } else if (unit?.denominators.includes('année')) { - weight = getDifferenceInYears(start, end) - } else { - weight = getDifferenceInDays(start, end) - } - totalWeight += weight - return value * weight - }) - return weights.reduce( - (average, weightedValue) => average + weightedValue / totalWeight, - 0 - ) -} - -export function temporalCumul( - temporalValue: Temporal>, - unit: Unit -): Evaluation { - temporalValue = temporalValue.filter(({ value }) => value !== false) - if (!temporalValue.length) { - return false - } - - if (temporalValue.some(({ value }) => value == null)) { - return null - } - - const temporalNumber = temporalValue as Temporal - const first = temporalNumber[0] - const last = temporalNumber[temporalNumber.length - 1] - - // La variable est définie sur un interval infini - if (first.start == null || last.end == null) { - if (first.start != null) { - return !last.value ? last.value : last.value > 0 ? Infinity : -Infinity - } - if (last.end != null) { - return !last.value ? last.value : last.value > 0 ? Infinity : -Infinity - } - return null - } - if (temporalNumber.some(({ value }) => value == null)) { - return null - } - - return temporalNumber.reduce((acc, { start, end, value }) => { - ;[start, end] = [start, end] as [string, string] - let weight = 1 - if (unit?.denominators.includes('mois')) { - weight = getDifferenceInMonths(start, end) - } else if (unit?.denominators.includes('année')) { - weight = getDifferenceInYears(start, end) - } else if (unit?.denominators.includes('jour')) { - weight = getDifferenceInDays(start, end) - } - return value * weight + acc - }, 0) -} diff --git "a/publicodes/core/test/m\303\251canismes/grille.yaml" "b/publicodes/core/test/m\303\251canismes/grille.yaml" index 4796dde593..33fac7ea4b 100644 --- "a/publicodes/core/test/m\303\251canismes/grille.yaml" +++ "b/publicodes/core/test/m\303\251canismes/grille.yaml" @@ -59,7 +59,7 @@ Grille avec valeur manquante: situation: assiette: 3000 valeur attendue: 300 - - nom: 'assiette au delà du plagond' + - nom: 'assiette au delà du plafond' situation: assiette: 5000 valeur attendue: false diff --git a/publicodes/core/test/period.test.js b/publicodes/core/test/period.test.js deleted file mode 100644 index 51c0f2ec57..0000000000 --- a/publicodes/core/test/period.test.js +++ /dev/null @@ -1,132 +0,0 @@ -import { expect } from 'chai' -import { - concatTemporals, - createTemporalEvaluation, - groupByYear, - zipTemporals, -} from '../source/temporal' - -const neverEnding = (value) => [{ start: null, end: null, value: value }] -describe('Periods : zip', () => { - it('should zip two empty temporalValue', () => { - const result = zipTemporals([], []) - expect(result).to.deep.equal([]) - }) - - it('should zip constant temporalValue', () => { - const result = zipTemporals(neverEnding(1), neverEnding(2)) - expect(result).to.deep.equal(neverEnding([1, 2])) - }) - - it('should zip changing temporalValue', () => { - const value1 = createTemporalEvaluation(true, { - start: null, - end: '01/08/2020', - }) - const value2 = neverEnding(1) - expect(zipTemporals(value1, value2)).to.deep.equal([ - { start: null, end: '01/08/2020', value: [true, 1] }, - { start: '02/08/2020', end: null, value: [false, 1] }, - ]) - expect(zipTemporals(value2, value1)).to.deep.equal([ - { start: null, end: '01/08/2020', value: [1, true] }, - { start: '02/08/2020', end: null, value: [1, false] }, - ]) - }) - - it('should zip two overlapping temporalValue', () => { - const value1 = createTemporalEvaluation(1, { - start: '01/07/2019', - end: '30/06/2020', - }) - const value2 = createTemporalEvaluation(2, { - start: '01/01/2019', - end: '31/12/2019', - }) - - expect(zipTemporals(value1, value2)).to.deep.equal([ - { start: null, end: '31/12/2018', value: [false, false] }, - { start: '01/01/2019', end: '30/06/2019', value: [false, 2] }, - { start: '01/07/2019', end: '31/12/2019', value: [1, 2] }, - { start: '01/01/2020', end: '30/06/2020', value: [1, false] }, - { start: '01/07/2020', end: null, value: [false, false] }, - ]) - }) -}) - -describe('Periods : concat', () => { - it('should merge concat overlapping temporalValue', () => { - const value1 = createTemporalEvaluation(10) - const value2 = [ - { start: null, end: '14/04/2019', value: 100 }, - { start: '15/04/2019', end: '08/08/2019', value: 2000 }, - { start: '09/08/2019', end: null, value: 200 }, - ] - - expect(concatTemporals([value1, value2])).to.deep.equal([ - { start: null, end: '14/04/2019', value: [10, 100] }, - { start: '15/04/2019', end: '08/08/2019', value: [10, 2000] }, - { start: '09/08/2019', end: null, value: [10, 200] }, - ]) - }) -}) - -describe('Periods : groupByYear', () => { - const invariants = (temporalYear) => { - const startDate = temporalYear[0].start - const endDate = temporalYear.slice(-1)[0].end - expect( - startDate === null || startDate.startsWith('01/01'), - 'starts at the beginning of a year' - ) - expect( - endDate === null || endDate.startsWith('31/12'), - 'stops at the end of a year' - ) - } - it('should handle constant value', () => { - const value = createTemporalEvaluation(10) - expect(groupByYear(value)).to.deep.equal([value]) - }) - it('should handle changing value', () => { - const value = createTemporalEvaluation(10, { - start: '06/06/2020', - end: '20/12/2020', - }) - const result = groupByYear(value) - expect(result).to.have.length(3) - result.forEach(invariants) - }) - it('should handle changing value over several years', () => { - const value = createTemporalEvaluation(10, { - start: '06/06/2020', - end: '20/12/2022', - }) - const result = groupByYear(value) - expect(result).to.have.length(5) - result.forEach(invariants) - }) - it('should handle complex case', () => { - const result = groupByYear( - concatTemporals([ - createTemporalEvaluation(1, { - start: '06/06/2020', - end: '20/12/2022', - }), - createTemporalEvaluation(2, { - start: '01/01/1991', - end: '20/12/1992', - }), - createTemporalEvaluation(3, { - start: '31/01/1990', - end: '20/12/2021', - }), - createTemporalEvaluation(4, { - start: '31/12/2020', - end: '01/01/2021', - }), - ]) - ) - result.forEach(invariants) - }) -}) diff --git a/publicodes/core/test/temporal.test.js b/publicodes/core/test/temporal.test.js deleted file mode 100644 index 51c0f2ec57..0000000000 --- a/publicodes/core/test/temporal.test.js +++ /dev/null @@ -1,132 +0,0 @@ -import { expect } from 'chai' -import { - concatTemporals, - createTemporalEvaluation, - groupByYear, - zipTemporals, -} from '../source/temporal' - -const neverEnding = (value) => [{ start: null, end: null, value: value }] -describe('Periods : zip', () => { - it('should zip two empty temporalValue', () => { - const result = zipTemporals([], []) - expect(result).to.deep.equal([]) - }) - - it('should zip constant temporalValue', () => { - const result = zipTemporals(neverEnding(1), neverEnding(2)) - expect(result).to.deep.equal(neverEnding([1, 2])) - }) - - it('should zip changing temporalValue', () => { - const value1 = createTemporalEvaluation(true, { - start: null, - end: '01/08/2020', - }) - const value2 = neverEnding(1) - expect(zipTemporals(value1, value2)).to.deep.equal([ - { start: null, end: '01/08/2020', value: [true, 1] }, - { start: '02/08/2020', end: null, value: [false, 1] }, - ]) - expect(zipTemporals(value2, value1)).to.deep.equal([ - { start: null, end: '01/08/2020', value: [1, true] }, - { start: '02/08/2020', end: null, value: [1, false] }, - ]) - }) - - it('should zip two overlapping temporalValue', () => { - const value1 = createTemporalEvaluation(1, { - start: '01/07/2019', - end: '30/06/2020', - }) - const value2 = createTemporalEvaluation(2, { - start: '01/01/2019', - end: '31/12/2019', - }) - - expect(zipTemporals(value1, value2)).to.deep.equal([ - { start: null, end: '31/12/2018', value: [false, false] }, - { start: '01/01/2019', end: '30/06/2019', value: [false, 2] }, - { start: '01/07/2019', end: '31/12/2019', value: [1, 2] }, - { start: '01/01/2020', end: '30/06/2020', value: [1, false] }, - { start: '01/07/2020', end: null, value: [false, false] }, - ]) - }) -}) - -describe('Periods : concat', () => { - it('should merge concat overlapping temporalValue', () => { - const value1 = createTemporalEvaluation(10) - const value2 = [ - { start: null, end: '14/04/2019', value: 100 }, - { start: '15/04/2019', end: '08/08/2019', value: 2000 }, - { start: '09/08/2019', end: null, value: 200 }, - ] - - expect(concatTemporals([value1, value2])).to.deep.equal([ - { start: null, end: '14/04/2019', value: [10, 100] }, - { start: '15/04/2019', end: '08/08/2019', value: [10, 2000] }, - { start: '09/08/2019', end: null, value: [10, 200] }, - ]) - }) -}) - -describe('Periods : groupByYear', () => { - const invariants = (temporalYear) => { - const startDate = temporalYear[0].start - const endDate = temporalYear.slice(-1)[0].end - expect( - startDate === null || startDate.startsWith('01/01'), - 'starts at the beginning of a year' - ) - expect( - endDate === null || endDate.startsWith('31/12'), - 'stops at the end of a year' - ) - } - it('should handle constant value', () => { - const value = createTemporalEvaluation(10) - expect(groupByYear(value)).to.deep.equal([value]) - }) - it('should handle changing value', () => { - const value = createTemporalEvaluation(10, { - start: '06/06/2020', - end: '20/12/2020', - }) - const result = groupByYear(value) - expect(result).to.have.length(3) - result.forEach(invariants) - }) - it('should handle changing value over several years', () => { - const value = createTemporalEvaluation(10, { - start: '06/06/2020', - end: '20/12/2022', - }) - const result = groupByYear(value) - expect(result).to.have.length(5) - result.forEach(invariants) - }) - it('should handle complex case', () => { - const result = groupByYear( - concatTemporals([ - createTemporalEvaluation(1, { - start: '06/06/2020', - end: '20/12/2022', - }), - createTemporalEvaluation(2, { - start: '01/01/1991', - end: '20/12/1992', - }), - createTemporalEvaluation(3, { - start: '31/01/1990', - end: '20/12/2021', - }), - createTemporalEvaluation(4, { - start: '31/12/2020', - end: '01/01/2021', - }), - ]) - ) - result.forEach(invariants) - }) -}) diff --git a/publicodes/ui-react/source/Explanation.tsx b/publicodes/ui-react/source/Explanation.tsx index bafe2d6895..ff32415442 100644 --- a/publicodes/ui-react/source/Explanation.tsx +++ b/publicodes/ui-react/source/Explanation.tsx @@ -64,7 +64,6 @@ const UIComponents = { 'une possibilité': UnePossibilité, 'résoudre référence circulaire': RésoudreRéférenceCirculaire, unité: Unité, - 'variable temporelle': () => '[variable temporelle]', variations: Variations, } as const diff --git a/publicodes/ui-react/source/index.tsx b/publicodes/ui-react/source/index.tsx index fe4dddea51..4a79e5a5a2 100644 --- a/publicodes/ui-react/source/index.tsx +++ b/publicodes/ui-react/source/index.tsx @@ -67,7 +67,6 @@ export function Documentation({ defaultEngine, state?.situation ) - console.log(engine) return (