refactor: groupe la manipulation d'objets de Publicodes dans un fichier utils dédié

pull/3159/head
Alice Dahan 2024-09-06 15:38:40 +02:00 committed by liliced
parent 3c2d9a3999
commit 4c4ff2cbdf
13 changed files with 104 additions and 99 deletions

View File

@ -8,7 +8,7 @@ import { styled } from 'styled-components'
import { Switch } from '@/design-system/switch'
import { batchUpdateSituation } from '@/store/actions/actions'
import { situationSelector } from '@/store/selectors/simulationSelectors'
import { catchDivideByZeroError } from '@/utils'
import { catchDivideByZeroError } from '@/utils/publicodes'
import { ExplicableRule } from './conversation/Explicable'
import { Condition } from './EngineValue/Condition'

View File

@ -1,10 +1,11 @@
import { DottedName } from 'modele-social'
import { ASTNode, formatValue, reduceAST } from 'publicodes'
import { formatValue } from 'publicodes'
import { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import RuleLink from '@/components/RuleLink'
import { EngineContext } from '@/components/utils/EngineContext'
import { findReferenceInNode } from '@/utils/publicodes'
export default function CotisationLine({
dottedName,
@ -53,24 +54,3 @@ export default function CotisationLine({
function isExoneration(dottedName: DottedName): boolean {
return dottedName === 'salarié . cotisations . exonérations'
}
function findReferenceInNode(
dottedName: DottedName,
node: ASTNode
): string | undefined {
return reduceAST<string | undefined>(
(acc, node) => {
if (
node.nodeKind === 'reference' &&
node.dottedName?.startsWith(dottedName) &&
!node.dottedName.endsWith('$SITUATION')
) {
return node.dottedName
} else if (node.nodeKind === 'reference') {
return acc
}
},
undefined,
node
)
}

View File

@ -25,7 +25,7 @@ import {
companySituationSelector,
situationSelector,
} from '@/store/selectors/simulationSelectors'
import { evaluateQuestion } from '@/utils'
import { evaluateQuestion } from '@/utils/publicodes'
import Value from '../EngineValue/Value'
import { JeDonneMonAvis } from '../JeDonneMonAvis'

View File

@ -23,7 +23,7 @@ import {
} from '@/store/actions/actions'
import { estSurLaPremièreQuestionRépondueSelector } from '@/store/selectors/estSurLaPremièreQuestionRépondue.selector'
import { situationSelector } from '@/store/selectors/simulationSelectors'
import { evaluateQuestion } from '@/utils'
import { evaluateQuestion } from '@/utils/publicodes'
interface Props {
previousAnswers: DottedName[]

View File

@ -14,7 +14,7 @@ import SelectCommune from '@/components/conversation/select/SelectCommune'
import { EngineContext } from '@/components/utils/EngineContext'
import { DateFieldProps } from '@/design-system/field/DateField'
import { Spacing } from '@/design-system/layout'
import { getMeta } from '@/utils'
import { getMeta } from '@/utils/publicodes'
import { Choice, MultipleAnswerInput, OuiNonInput } from './ChoicesInput'
import DateInput from './DateInput'

View File

@ -19,7 +19,7 @@ import {
situationSelector,
targetUnitSelector,
} from '@/store/selectors/simulationSelectors'
import { evaluateQuestion, getMeta } from '@/utils'
import { evaluateQuestion, getMeta } from '@/utils/publicodes'
type SubSectionProp = {
dottedName: DottedName

View File

@ -19,13 +19,12 @@ import PopoverConfirm from '@/design-system/popover/PopoverConfirm'
import { headings } from '@/design-system/typography'
import { Intro, SmallBody } from '@/design-system/typography/paragraphs'
import useSimulationConfig from '@/hooks/useSimulationConfig'
import { hash, omit } from '@/utils'
import {
buildSituationFromObject,
evaluateQuestion,
getMeta,
hash,
omit,
} from '@/utils'
} from '@/utils/publicodes'
import formulaire from './demande-mobilité.yaml'

View File

@ -42,7 +42,7 @@ import { useSitePaths } from '@/sitePaths'
import { resetCompany } from '@/store/actions/companyActions'
import { SimulationConfig } from '@/store/reducers/rootReducer'
import { companySituationSelector } from '@/store/selectors/simulationSelectors'
import { evaluateQuestion } from '@/utils'
import { evaluateQuestion } from '@/utils/publicodes'
import forms from './forms.svg'
import growth from './growth.svg'

View File

@ -20,7 +20,7 @@ import { H2 } from '@/design-system/typography/heading'
import { Link } from '@/design-system/typography/link'
import { Li, Ul } from '@/design-system/typography/list'
import { Body } from '@/design-system/typography/paragraphs'
import { catchDivideByZeroError } from '@/utils'
import { catchDivideByZeroError } from '@/utils/publicodes'
declare global {
interface Window {

View File

@ -4,7 +4,7 @@ import Engine, { PublicodesExpression } from 'publicodes'
import { SimpleRuleEvaluation } from '@/domaine/engine/SimpleRuleEvaluation'
import { SimulationConfig } from '@/store/reducers/rootReducer'
import { QuestionRépondue } from '@/store/reducers/simulation.reducer'
import { buildSituationFromObject } from '@/utils'
import { buildSituationFromObject } from '@/utils/publicodes'
import { CompanyActions } from './companyActions'
import { HiringChecklistAction } from './hiringChecklistAction'

View File

@ -4,7 +4,8 @@ import { CodeCatégorieJuridique } from '@/domaine/CodeCatégorieJuridique'
import { toPublicodeDate } from '@/domaine/Date'
import { Entreprise } from '@/domaine/Entreprise'
import { Action } from '@/store/actions/actions'
import { buildSituationFromObject, omit } from '@/utils'
import { omit } from '@/utils'
import { buildSituationFromObject } from '@/utils/publicodes'
import { Situation } from './rootReducer'

View File

@ -1,13 +1,4 @@
import { DottedName } from 'modele-social'
import Engine, {
formatValue,
isPublicodesError,
PublicodesExpression,
Rule,
RuleNode,
} from 'publicodes'
import { Situation } from '@/store/reducers/rootReducer'
import { formatValue } from 'publicodes'
/** The `capitalise0` function is a utility function that capitalizes the first letter of a string. The
function takes an optional `name` parameter, which is a string that needs to be capitalized. */
@ -180,17 +171,6 @@ export const getValueFrom = <
): Extract<T, { [k in K]?: unknown }>[K] | undefined =>
key in obj ? obj[key] : undefined
const isMeta = <T>(rule: Rule): rule is Rule & { meta?: T } => 'meta' in rule
/**
* Return typed meta property from a rule
* @param rule
* @param defaultValue
* @returns
*/
export const getMeta = <T>(rule: Rule, defaultValue: T) =>
(isMeta<T>(rule) ? getValueFrom(rule, 'meta') : null) ?? defaultValue
/**
* Wraps each event function specified in eventsToWrap (default onPress) with an
* asynchronous function that waits x ms before executing the original function
@ -245,50 +225,6 @@ export async function getIframeOffset(): Promise<number> {
})
}
export function evaluateQuestion(
engine: Engine,
rule: RuleNode
): string | undefined {
const question = rule.rawNode.question as Exclude<
number,
PublicodesExpression
>
if (question && typeof question === 'object') {
return engine.evaluate(question as PublicodesExpression).nodeValue as string
}
return question
}
export function buildSituationFromObject<Names extends string = DottedName>(
contextDottedName: Names,
situationObject: Record<string, PublicodesExpression>
): Situation {
return Object.fromEntries(
Object.entries(situationObject).map(
([key, value]: [string, PublicodesExpression]) => [
`${contextDottedName} . ${key}` as Names,
typeof value === 'string' ? `'${value}'` : value,
]
)
)
}
export const catchDivideByZeroError = <T>(func: () => T) => {
try {
return func()
} catch (err) {
if (
isPublicodesError(err, 'EvaluationError') &&
err.message === 'Division by zero'
) {
// eslint-disable-next-line no-console
console.error(err)
}
throw err
}
}
export const generateUuid = () => {
return Math.floor(Math.random() * Date.now()).toString(16)
}

View File

@ -0,0 +1,89 @@
import { DottedName } from 'modele-social'
import Engine, {
ASTNode,
isPublicodesError,
PublicodesExpression,
reduceAST,
Rule,
RuleNode,
} from 'publicodes'
import { Situation } from '@/store/reducers/rootReducer'
import { getValueFrom } from '.'
const isMeta = <T>(rule: Rule): rule is Rule & { meta?: T } => 'meta' in rule
/**
* Return typed meta property from a rule
* @param rule
* @param defaultValue
* @returns
*/
export const getMeta = <T>(rule: Rule, defaultValue: T) =>
(isMeta<T>(rule) ? getValueFrom(rule, 'meta') : null) ?? defaultValue
export function evaluateQuestion(
engine: Engine,
rule: RuleNode
): string | undefined {
const question = rule.rawNode.question as Exclude<
number,
PublicodesExpression
>
if (question && typeof question === 'object') {
return engine.evaluate(question as PublicodesExpression).nodeValue as string
}
return question
}
export function buildSituationFromObject<Names extends string = DottedName>(
contextDottedName: Names,
situationObject: Record<string, PublicodesExpression>
): Situation {
return Object.fromEntries(
Object.entries(situationObject).map(
([key, value]: [string, PublicodesExpression]) => [
`${contextDottedName} . ${key}` as Names,
typeof value === 'string' ? `'${value}'` : value,
]
)
)
}
export const catchDivideByZeroError = <T>(func: () => T) => {
try {
return func()
} catch (err) {
if (
isPublicodesError(err, 'EvaluationError') &&
err.message === 'Division by zero'
) {
// eslint-disable-next-line no-console
console.error(err)
}
throw err
}
}
export function findReferenceInNode(
dottedName: DottedName,
node: ASTNode
): string | undefined {
return reduceAST<string | undefined>(
(acc, node) => {
if (
node.nodeKind === 'reference' &&
node.dottedName?.startsWith(dottedName) &&
!node.dottedName.endsWith('$SITUATION')
) {
return node.dottedName
} else if (node.nodeKind === 'reference') {
return acc
}
},
undefined,
node
)
}