MAJ publicodes (#1645)
* Squashed 'publicodes/' changes from 10a30d32..71b68707 71b68707 📦 Publicodes v1.0.0-beta.16 bdc92216 Merge the tests and publish workflows 1c032ebc ✅ Add test for a value with a percentage in its unit d2865e8c Disable sum optimization inside comparisons f4faa35d Ajout d'un test qui casse l'implé actuelle des missing parentes f6105283 🖋 Document packages publication on NPM a79eeb86 Better Github workflows d0db4d09 Import publish action c268cff5 Type checking in CI a35403d7 Correction formattage 3022fd78 Add a separate cache for applicability 35095da9 Optimize the evaluation of applicability 7525446e Add a github action to run tests on push 39a12a13 Ajout d'un prettierrc / reformattage de quelques fichiers récents c296a25e Ajout d'un deuxièmes test non fonctionnel sur le sujet #33 9f5afb4e Désactivation d'un nouveau test pas encore résolu 76d00085 Récupération de la complexité initiale du test missing variations 93210235 🐛 Meilleures missingVariables des variations 369abeae Simplification du test missingVariables qui ne marche pas 64217d3d Nouveau test missing variables éval paresseuse variations d9c3e1f6 Conversion d'un gros test JS object en YAML 615ae5e5 Ajout d'un test râté pour #33 d290b46d Passage à mochapack pour webpack 5 5d7a5b31 Paquets NPM et conf babel manquants 1df9a8d4 Ajout d'un .gitignore et yarn.lock 6c2d0203 Uniformise l'unité des arrondis 2cbffe8a ⬆ MAJ Typescript vers 4.3 678403e4 Corrige le calcul des cotisations forfaitaires de début d'activité 8cdaac05 Simpler condition component (#1578) b7459617 🔥 Supprime les variables temporelles db62b57d 🔥 Supprime l'utilisation des temporals dans les mécanismes git-subtree-dir: publicodes git-subtree-split: 71b687077ec30ea3959209657c2ac7fd7a0277e1 * 🔥 Supprime l'action de déploiement de publicodes Migrée sur betagouv/publicodes * 🖋 MAJ documentation de contribution * ✅ Désactive prettier pour publicodes Je préférerais ne pas avoir à désactiver Prettier pour le répertoire publicodes mais pour une raison qui m'échappe (peut-être liée aux versions des differentes dépendances prettier/babel/typescript ?) entre betagouv/publicodes et betagouv/mon-entreprise, l'une des lignes est formatée différemment ce qui provoque une erreur dans la CI. On va considérer que la “bonne configuration” est celle de betagouv/publicodes et ignorer celle de betagouv/mon-entreprise. Mais ça serait quand même mieux d'avoir la même des deux côtés. * 🔨 Sort une question d'un espace parfois non applicablepull/1662/head
parent
21801ea73a
commit
3e4463d5d1
|
@ -1,45 +0,0 @@
|
|||
name: Publication du paquet publicodes
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- publicodes/**
|
||||
- .github/workflows/publish-publicodes.yaml
|
||||
|
||||
jobs:
|
||||
test:
|
||||
if: contains(join(github.event.commits.*.message, ' | '), '📦 Publicodes v1.0.0-beta.')
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2
|
||||
- run: yarn install --frozen-lockfile
|
||||
- working-directory: ./publicodes/example/publicodes-react
|
||||
run: |
|
||||
yarn install
|
||||
yarn test
|
||||
|
||||
publish:
|
||||
needs: test
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2
|
||||
- run: yarn install --frozen-lockfile
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_PUBLISH_SECRET }}
|
||||
dry-run: ${{ github.ref != 'refs/heads/master' }}
|
||||
package: ./publicodes/core/package.json
|
||||
tag: next
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_PUBLISH_SECRET }}
|
||||
dry-run: ${{ github.ref != 'refs/heads/master' }}
|
||||
package: ./publicodes/ui-react/package.json
|
||||
tag: next
|
|
@ -1,2 +1,3 @@
|
|||
.eslintrc.js
|
||||
dist
|
||||
publicodes
|
||||
|
|
131
CONTRIBUTING.md
131
CONTRIBUTING.md
|
@ -66,28 +66,22 @@ REDUX_TRACE=true yarn start
|
|||
|
||||
A mettre sans retenue dans les messages de commit :
|
||||
|
||||
https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages
|
||||
|
||||
- 🎨 `:art:` when working on the app's visual style
|
||||
- 🐎 `:racehorse:` when improving performance
|
||||
- 📝 `:memo:` when writing docs
|
||||
- 🐛 `:bug:` when fixing a bug
|
||||
- 🔥 `:fire:` when removing code or files
|
||||
- 💚 `:green_heart:` when fixing the CI build
|
||||
- ✅ `:white_check_mark:` when adding tests
|
||||
- ⬆️ `:arrow_up:` when upgrading dependencies
|
||||
- :sparkles: `:sparkles:` when formatting, renaming, reorganizing files
|
||||
|
||||
Et ceux spécifiques au projet :
|
||||
|
||||
- :gear: `:gear:` pour une contribution au moteur qui traite les YAML
|
||||
- :hammer: `:hammer:` pour une contribution à la base de règles
|
||||
- :calendar: `:calendar:` pour un changement de règle du à une évolution temporelle (en attendant mieux)
|
||||
- :chart_with_upwards_trend: `:chart_with_upwards_trend:` pour une amélioration du tracking
|
||||
- :alien: `:alien:` pour ajouter des traductions
|
||||
- :wheelchair: `:wheelchair:` pour corriger les problèmes liés à l'accessibilité
|
||||
- :fountain_pen: `:fountain_pen:` pour séparer les commits liés à la modification du contenu
|
||||
- :mag: `:mag:` pour les modifications liées au référencement naturel
|
||||
- 🎨 `:art:` pour une modification de l'UI
|
||||
- 🐎 `:racehorse:` pour une amélioration de performance
|
||||
- 🐛 `:bug:` pour une correction de bug
|
||||
- 🔥 `:fire:` pour une suppression de code ou de fichier
|
||||
- 💚 `:green_heart:` pour une correction de CI
|
||||
- ✅ `:white_check_mark:` pour un ajout de test
|
||||
- ⬆️ `:arrow_up:` pour une mise à jour de dépendances
|
||||
- ✨ `:sparkles:` pour une ré-organisation du code
|
||||
- ⚙ `:gear:` pour une contribution sur le moteur publicodes
|
||||
- 🔨 `:hammer:` pour une contribution à la base de règles
|
||||
- 📆 `:calendar:` pour un changement de règle du à une évolution temporelle (en attendant mieux)
|
||||
- 📈 `:chart_with_upwards_trend:` pour une amélioration du tracking
|
||||
- 👽 `:alien:` pour ajouter des traductions
|
||||
- ♿ `:wheelchair:` pour corriger les problèmes liés à l'accessibilité
|
||||
- 🖋 `:fountain_pen:` pour séparer les commits liés à la modification du contenu
|
||||
- 🔍 `:mag:` pour les modifications liées au référencement naturel
|
||||
|
||||
### Tests
|
||||
|
||||
|
@ -167,6 +161,44 @@ La commande `yarn run build:analyse-bundle` gènere une visualisation interactiv
|
|||
contenu packagé, cf.
|
||||
[webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer)
|
||||
|
||||
### Tests
|
||||
|
||||
Pour tester les règles, il est recommandé de:
|
||||
|
||||
- faire tourner un simulateur et vérifier à la main l'adéquation des règles avec les normes
|
||||
traduites ;
|
||||
- créer des cas de tests de non-régression sous la forme de nouveaux snapshots (cf.
|
||||
`mon-entreprise/test/regressions`).
|
||||
|
||||
## Publicodes
|
||||
|
||||
### Documentation
|
||||
|
||||
Un tutoriel sur publicodes est disponible sur https://publi.codes.
|
||||
|
||||
Un wiki contenant des informations intéressantes sur publicodes et le
|
||||
raisonnement ayant abouti à ce langage sont dispos sur le repository
|
||||
[betagouv/publicodes](https://github.com/betagouv/publicodes/wiki), qui est par
|
||||
ailleurs inutilisé.
|
||||
|
||||
Pour se familiariser avec les règles, vous pouvez jeter un œil aux fichiers
|
||||
contenant les règles elles-mêmes (dans le dossier `modele-social`) mais cela
|
||||
peut s'avérer assez abrupt.
|
||||
|
||||
Essayez plutôt de jeter un oeil [aux tests](./publicodes/test/mécanismes/expressions.yaml)
|
||||
dans un premier temps, puis au [mécanismes en
|
||||
place](./publicodes/source/mecanisms).
|
||||
|
||||
### Traduction des normes (lois) en règles Publicodes
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] Lire les articles de vulgarisation (sur le site de l'URSSAF, des impôts, etc.).
|
||||
- [ ] Utiliser un moteur de recherche spécialisé, comme [RFPaye](https://rfpaye.grouperf.com/).
|
||||
- [ ] [Lire les normes][wiki normes] et noter leurs référence dans les règles Publicodes.
|
||||
|
||||
[wiki normes]: https://github.com/betagouv/mon-entreprise/wiki/Comment-lire-les-normes-(la-loi)-efficacement-pour-r%C3%A9diger-des-r%C3%A8gles-Publicodes%3F
|
||||
|
||||
### Modifier publicodes
|
||||
|
||||
Publicodes dispose désormais de son propre dépôt GitHub https://github.com/betagouv/publicodes
|
||||
|
@ -198,57 +230,4 @@ Dans l'autre sens il est possible de rapatrier les changements avec la commande
|
|||
$ git subtree pull --prefix=publicodes publicodes master --squash
|
||||
```
|
||||
|
||||
## Développement de modèles Publicodes
|
||||
|
||||
### Traduction des normes (lois) en règles Publicodes
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] Lire les articles de vulgarisation (sur le site de l'URSSAF, des impôts, etc.).
|
||||
- [ ] Utiliser un moteur de recherche spécialisé, comme [RFPaye](https://rfpaye.grouperf.com/).
|
||||
- [ ] [Lire les normes][wiki normes] et noter leurs référence dans les règles Publicodes.
|
||||
|
||||
[wiki normes]: https://github.com/betagouv/mon-entreprise/wiki/Comment-lire-les-normes-(la-loi)-efficacement-pour-r%C3%A9diger-des-r%C3%A8gles-Publicodes%3F
|
||||
|
||||
### Tests
|
||||
|
||||
Pour tester les règles, il est recommandé de:
|
||||
|
||||
- faire tourner un simulateur et vérifier à la main l'adéquation des règles avec les normes
|
||||
traduites ;
|
||||
- créer des cas de tests de non-régression sous la forme de nouveaux snapshots (cf.
|
||||
`mon-entreprise/test/regressions`).
|
||||
|
||||
## Documentation
|
||||
|
||||
### Publicodes
|
||||
|
||||
Un tutoriel sur publicodes est disponible sur https://publi.codes.
|
||||
|
||||
Un wiki contenant des informations intéressantes sur publicodes et le
|
||||
raisonnement ayant abouti à ce langage sont dispos sur le repository
|
||||
[betagouv/publicodes](https://github.com/betagouv/publicodes/wiki), qui est par
|
||||
ailleurs inutilisé.
|
||||
|
||||
Pour se familiariser avec les règles, vous pouvez jeter un œil aux fichiers
|
||||
contenant les règles elles-mêmes (dans le dossier `rules`) mais cela peut
|
||||
s'avérer assez abrupt.
|
||||
|
||||
Essayez plutôt de jeter un oeil [aux tests](./publicodes/test/mécanismes/expressions.yaml)
|
||||
dans un premier temps, puis au [mécanismes en
|
||||
place](./publicodes/source/mecanisms).
|
||||
|
||||
## Publier une nouvelle version des paquets publicodes
|
||||
|
||||
<!-- TODO: action à déplacer dans le dépot betagouv/publicodes -->
|
||||
|
||||
Voici la marche à suivre pour publier une nouvelle version :
|
||||
|
||||
1. Renseigner les modifications dans publicodes/CHANGELOG.md
|
||||
2. Remplacer les références à la précédente version par la nouvelle version dans les packages.json
|
||||
3. Ajouter tous les changement dans un commit avec le message suivant :
|
||||
```
|
||||
📦 Publicodes v1.0.0-beta.<n>
|
||||
```
|
||||
> **Important** Le message doit être exactement celui-ci (emoji compris), car le script de déploiement automatique sur le CI se base sur ce dernier.
|
||||
4. Laisser faire le CI, une fois le commit mergé sur master, le paquet sera déployé effectivement
|
||||
Les dépendances peuvent avoir changé côté publicodes, mieux vaut donc enchaîner avec un `yarn install` pour être à jour.
|
||||
|
|
|
@ -2106,6 +2106,10 @@ contrat salarié . aides employeur . aide exceptionnelle à l'embauche d'apprent
|
|||
références:
|
||||
Plan \#1jeune1solution: https://travail-emploi.gouv.fr/formation-professionnelle/entreprise-et-alternance/aide-exceptionnelle-apprentissage
|
||||
|
||||
contrat salarié . jeune de moins de 26 ans:
|
||||
question: Le salarié a-t-il moins de 26 ans ?
|
||||
par défaut: non
|
||||
|
||||
contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes:
|
||||
non applicable si: aides employeur . emploi franc
|
||||
description: >-
|
||||
|
@ -2126,9 +2130,7 @@ contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeune
|
|||
- toutes ces conditions:
|
||||
- CDD
|
||||
- CDD . durée contrat >= 3 mois
|
||||
- nom: jeune de moins de 26 ans
|
||||
question: Le salarié a-t-il moins de 26 ans ?
|
||||
par défaut: non
|
||||
- jeune de moins de 26 ans
|
||||
rend non applicable:
|
||||
# Dispositifs moins généreux et non cumulables
|
||||
- aide à l'embauche des travailleurs handicapés
|
||||
|
@ -2173,7 +2175,7 @@ contrat salarié . aides employeur . emploi franc:
|
|||
toutes ces conditions:
|
||||
- ancienneté . date d'embauche >= 15/10/2020
|
||||
- ancienneté . date d'embauche <= 31/05/2021
|
||||
- contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes . jeune de moins de 26 ans
|
||||
- jeune de moins de 26 ans
|
||||
alors:
|
||||
nom: emploi franc plus
|
||||
variations:
|
||||
|
|
|
@ -3703,6 +3703,11 @@ contrat salarié . intermittents du spectacle . technicien:
|
|||
contrat salarié . intermittents du spectacle . technicien . non cadre:
|
||||
titre.en: not "cadre"
|
||||
titre.fr: non cadre
|
||||
contrat salarié . jeune de moins de 26 ans:
|
||||
question.en: '[automatic] Is the employee under 26 years of age?'
|
||||
question.fr: Le salarié a-t-il moins de 26 ans ?
|
||||
titre.en: '[automatic] young person under 26 years old'
|
||||
titre.fr: jeune de moins de 26 ans
|
||||
contrat salarié . lodeom:
|
||||
description.en: >
|
||||
A rather complex set of contribution reductions is available for overseas
|
||||
|
|
|
@ -113,7 +113,7 @@ aides:
|
|||
- contrat salarié . rémunération . brut de base: 2000 €/mois
|
||||
contrat salarié . aides employeur . emploi franc . éligible: oui
|
||||
contrat salarié . ancienneté . date d'embauche: 01/11/2020
|
||||
contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes . jeune de moins de 26 ans: oui
|
||||
contrat salarié . jeune de moins de 26 ans: oui
|
||||
# travailleur handicapé
|
||||
- contrat salarié . rémunération . brut de base: 2000 €/mois
|
||||
contrat salarié . ancienneté . date d'embauche: 01/03/2021
|
||||
|
@ -122,15 +122,15 @@ aides:
|
|||
|
||||
aides embauche covid:
|
||||
- contrat salarié . ancienneté . date d'embauche: 01/08/2020
|
||||
contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes . jeune de moins de 26 ans: oui
|
||||
contrat salarié . jeune de moins de 26 ans: oui
|
||||
contrat salarié . rémunération . brut de base: 1500 €/mois
|
||||
- contrat salarié . ancienneté . date d'embauche: 01/08/2020
|
||||
contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes . jeune de moins de 26 ans: oui
|
||||
contrat salarié . jeune de moins de 26 ans: oui
|
||||
contrat salarié . rémunération . brut de base: 5000 €/mois
|
||||
- contrat salarié . ancienneté . date d'embauche: 01/08/2020
|
||||
contrat salarié: "'CDD'"
|
||||
contrat salarié . CDD . durée contrat: 3 mois
|
||||
contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes . jeune de moins de 26 ans: oui
|
||||
contrat salarié . jeune de moins de 26 ans: oui
|
||||
contrat salarié . rémunération . brut de base: 1500 €/mois
|
||||
- contrat salarié . ancienneté . date d'embauche: 01/08/2020
|
||||
contrat salarié: "'apprentissage'"
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
name: Test and Publish
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
- run: yarn install
|
||||
- run: yarn test
|
||||
|
||||
test-type:
|
||||
name: Type checking
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
- run: yarn install
|
||||
- run: yarn test:type
|
||||
|
||||
test-example-app:
|
||||
name: Test example app
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- working-directory: ./example/publicodes-react
|
||||
run: |
|
||||
yarn install
|
||||
yarn test
|
||||
|
||||
# This job could be in a separate workflow triggered when all the tests passes
|
||||
# using the `workflow_run` event, but it makes it difficult to retrieve the
|
||||
# commit message.
|
||||
publish:
|
||||
if: contains(join(github.event.commits.*.message, ' | '), '📦 Publicodes v1.0.0-beta.')
|
||||
needs: [test, test-type, test-example-app]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2
|
||||
- run: yarn install --frozen-lockfile
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_PUBLISH_SECRET }}
|
||||
dry-run: ${{ github.ref != 'refs/heads/master' }}
|
||||
package: ./core/package.json
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_PUBLISH_SECRET }}
|
||||
dry-run: ${{ github.ref != 'refs/heads/master' }}
|
||||
package: ./ui-react/package.json
|
|
@ -0,0 +1,12 @@
|
|||
.tags*
|
||||
.tmp
|
||||
/tmp
|
||||
.DS_Store
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
node_modules/
|
||||
.env
|
||||
dist/
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
|
@ -0,0 +1,4 @@
|
|||
bracketSpacing: true
|
||||
semi: false
|
||||
singleQuote: true
|
||||
useTabs: true
|
|
@ -26,8 +26,11 @@
|
|||
|
||||
- Fix bug sur le mécanisme minimum, une valeur non applicable n'est plus considérée comme valant "0" (#1493)
|
||||
|
||||
## 1.0.0-beta.16 (release candidate)
|
||||
## 1.0.0-beta.16
|
||||
|
||||
**core**
|
||||
|
||||
- Répare un bug dans le mécanisme résoudre le cycle
|
||||
- Suppression des variables temporelles
|
||||
- Optimisation de la désactivation de branches
|
||||
- Meilleures performances
|
||||
|
|
|
@ -7,3 +7,16 @@ Voici quelques informations pour démarrer :
|
|||
## Rapport de bug, nouvelles fonctionnalités
|
||||
|
||||
Nous utilisons GitHub pour suivre tous les bugs et discussions sur les nouvelles fonctionnalités. Pour rapporter un bug ou proposer une évolution vous pouvez [ouvrir une nouvelle discussion](https://github.com/betagouv/publicodes/discussions). N'hésitez pas à utiliser la recherche pour vérifier si le sujet n'est pas déjà traité dans une discussion ouverte.
|
||||
|
||||
## Publier une nouvelle version sur NPM
|
||||
|
||||
Voici la marche à suivre pour publier une nouvelle version :
|
||||
|
||||
1. Renseigner les modifications dans `CHANGELOG.md`
|
||||
2. Remplacer les références à la précédente version par la nouvelle version dans les packages.json
|
||||
3. Ajouter tous les changements dans un commit avec le message suivant :
|
||||
```
|
||||
📦 Publicodes v1.0.0-beta.<n>
|
||||
```
|
||||
> **Important** Le message doit être exactement celui-ci (emoji compris), car le script de déploiement automatique sur le CI se base sur ce dernier.
|
||||
4. Laisser faire le CI, une fois le commit mergé sur master, le paquet sera déployé effectivement
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@babel/preset-typescript"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/plugin-syntax-dynamic-import"
|
||||
]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "publicodes",
|
||||
"version": "1.0.0-beta.15",
|
||||
"version": "1.0.0-beta.16",
|
||||
"description": "A declarative language for encoding public algorithm",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
|
@ -25,14 +25,27 @@
|
|||
],
|
||||
"private": false,
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.14.5",
|
||||
"babel-loader": "^8.2.2",
|
||||
"chai": "^4.2.0",
|
||||
"dedent-js": "1.0.1",
|
||||
"intl": "^1.2.5",
|
||||
"json-loader": "^0.5.7",
|
||||
"mocha": "^9.0.1",
|
||||
"mochapack": "^2.1.2",
|
||||
"nearley-loader": "^2.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.3.2",
|
||||
"dedent-js": "1.0.1"
|
||||
"webpack-cli": "^4.7.2",
|
||||
"yaml-loader": "^0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.14.6",
|
||||
"@babel/preset-env": "^7.14.5",
|
||||
"@types/webpack-env": "^1.16.0",
|
||||
"moo": "^0.5.1",
|
||||
"nearley": "^2.19.2",
|
||||
"webpack": "^5.39.1",
|
||||
"yaml": "^1.9.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -41,7 +54,7 @@
|
|||
"prepare": "yarn run rimraf dist && yarn run build",
|
||||
"build": "yarn run webpack --config webpack.config.js && yarn run tsc",
|
||||
"build:watch": "concurrently \"yarn run webpack --watch --config webpack.config.js\" \"yarn run tsc -w\"",
|
||||
"test:file": "yarn mocha-webpack --include test/setupIntl.js --webpack-config ./webpack.test.js ",
|
||||
"test:file": "yarn mochapack --include test/setupIntl.js --webpack-config ./webpack.test.js ",
|
||||
"test": "yarn test:file \"./{,!(node_modules)/**/}!(webpack).test.js\""
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
@ -16,14 +16,17 @@ const emptyCache = (): Cache => ({
|
|||
_meta: {
|
||||
parentRuleStack: [],
|
||||
evaluationRuleStack: [],
|
||||
disableApplicabilityContextCounter: 0,
|
||||
},
|
||||
nodes: new Map(),
|
||||
nodesApplicability: new Map(),
|
||||
})
|
||||
|
||||
type Cache = {
|
||||
_meta: {
|
||||
parentRuleStack: Array<string>
|
||||
evaluationRuleStack: Array<string>
|
||||
disableApplicabilityContextCounter: number
|
||||
inversionFail?:
|
||||
| {
|
||||
given: string
|
||||
|
@ -34,6 +37,7 @@ type Cache = {
|
|||
filter?: string
|
||||
}
|
||||
nodes: Map<PublicodesExpression | ASTNode, EvaluatedNode>
|
||||
nodesApplicability: Map<PublicodesExpression | ASTNode, EvaluatedNode>
|
||||
}
|
||||
|
||||
export type EvaluationOptions = Partial<{
|
||||
|
@ -157,8 +161,17 @@ export default class Engine<Name extends string = string> {
|
|||
evaluate(value: PublicodesExpression): EvaluatedNode
|
||||
evaluate(value: PublicodesExpression | ASTNode): EvaluatedNode {
|
||||
const cachedNode = this.cache.nodes.get(value)
|
||||
// The evaluation of parent applicabilty is slightly different from
|
||||
// regular rules since we cut some of the paths (sums) for optimization.
|
||||
// That's why we need to have a separate cache for this evaluation.
|
||||
|
||||
if (cachedNode !== undefined) {
|
||||
return cachedNode
|
||||
} else if (this.inApplicabilityEvaluationContext) {
|
||||
const cachedNodeApplicability = this.cache.nodesApplicability.get(value)
|
||||
if (cachedNodeApplicability) {
|
||||
return cachedNodeApplicability
|
||||
}
|
||||
}
|
||||
|
||||
let parsedNode: ASTNode
|
||||
|
@ -180,7 +193,16 @@ export default class Engine<Name extends string = string> {
|
|||
this,
|
||||
parsedNode
|
||||
)
|
||||
this.cache.nodes.set(value, evaluatedNode)
|
||||
|
||||
// TODO: In most cases the two evaluation provide the same result, this
|
||||
// could be optimized. The idea would be to use the “nodesApplicability”
|
||||
// cache iff the rule uses a sum mechanism (ie, some paths are cut from
|
||||
// the full evaluaiton).
|
||||
if (!this.inApplicabilityEvaluationContext) {
|
||||
this.cache.nodes.set(value, evaluatedNode)
|
||||
} else {
|
||||
this.cache.nodesApplicability.set(value, evaluatedNode)
|
||||
}
|
||||
return evaluatedNode
|
||||
}
|
||||
|
||||
|
@ -196,6 +218,13 @@ export default class Engine<Name extends string = string> {
|
|||
newEngine.cache = this.cache
|
||||
return newEngine
|
||||
}
|
||||
|
||||
get inApplicabilityEvaluationContext(): boolean {
|
||||
return (
|
||||
this.cache._meta.parentRuleStack.length > 0 &&
|
||||
this.cache._meta.disableApplicabilityContextCounter === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,10 +41,24 @@ const parseOperation = (k, symbol) => (v, context) => {
|
|||
}
|
||||
|
||||
const evaluate: EvaluationFunction<'operation'> = function (node) {
|
||||
// When we only need to evaluate the applicability of a rule, we don't enter
|
||||
// inside “sum terms” since we know that the sum will always be applicable.
|
||||
// However, if somewhere in the evaluation stack we do a comparison, we need
|
||||
// to disable this optimization since in this case we'll need the exact value
|
||||
// of sums in the evaluation subtree.
|
||||
const disableApplicabilityContext = ['≠', '=', '<', '>', '≤', '≥'].includes(
|
||||
node.operator
|
||||
)
|
||||
if (disableApplicabilityContext && this.inApplicabilityEvaluationContext) {
|
||||
this.cache._meta.disableApplicabilityContextCounter += 1
|
||||
}
|
||||
const explanation = node.explanation.map((node) => this.evaluate(node)) as [
|
||||
EvaluatedNode,
|
||||
EvaluatedNode
|
||||
]
|
||||
if (disableApplicabilityContext && this.inApplicabilityEvaluationContext) {
|
||||
this.cache._meta.disableApplicabilityContextCounter -= 1
|
||||
}
|
||||
let [node1, node2] = explanation
|
||||
const missingVariables = mergeAllMissing([node1, node2])
|
||||
|
||||
|
|
|
@ -18,4 +18,17 @@ export const mecanismSum = (v, context) => {
|
|||
} as SommeNode
|
||||
}
|
||||
|
||||
registerEvaluationFunction('somme', evaluate)
|
||||
registerEvaluationFunction('somme', function (node) {
|
||||
if (this.inApplicabilityEvaluationContext) {
|
||||
return {
|
||||
// With a clearer distinction between `getApplicability` and
|
||||
// `getValue` we could avoid faking a `nodeValue: true` and instead
|
||||
// simply return `isApplicable: true, nodeValue: undefined`
|
||||
nodeValue: true,
|
||||
nodeKind: 'somme',
|
||||
missingVariables: {},
|
||||
explanation: [],
|
||||
}
|
||||
}
|
||||
return evaluate.call(this, node)
|
||||
})
|
||||
|
|
|
@ -131,10 +131,10 @@ const evaluate: EvaluationFunction<'variations'> = function (node) {
|
|||
|
||||
const missingVariables = mergeAllMissing(
|
||||
explanation.reduce<ASTNode[]>(
|
||||
(values, { condition, consequence }) => [
|
||||
(values, { condition, satisfied, consequence }) => [
|
||||
...values,
|
||||
condition,
|
||||
consequence,
|
||||
...(satisfied ? [consequence] : []),
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
|
|
@ -21,6 +21,12 @@ describe('format engine values', () => {
|
|||
expect(formatValue(10, { displayedUnit: '%' })).to.equal('10 %')
|
||||
expect(formatValue(100, { displayedUnit: '%' })).to.equal('100 %')
|
||||
expect(formatValue(10.2, { displayedUnit: '%' })).to.equal('10,2 %')
|
||||
expect(
|
||||
formatValue({
|
||||
nodeValue: 441,
|
||||
unit: parseUnit('%.kgCO2e'),
|
||||
})
|
||||
).to.equal('4,41 kgCO2e')
|
||||
})
|
||||
|
||||
it('format values', () => {
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
import { expect } from 'chai'
|
||||
import Engine from '../source/index'
|
||||
import { parse } from 'yaml'
|
||||
|
||||
describe('Missing variables', function () {
|
||||
it('should identify missing variables', function () {
|
||||
const rawRules = {
|
||||
ko: 'oui',
|
||||
sum: 'oui',
|
||||
'sum . startHere': {
|
||||
formule: 2,
|
||||
'non applicable si': 'sum . evt . ko',
|
||||
},
|
||||
'sum . evt': {
|
||||
formule: { 'une possibilité': ['ko'] },
|
||||
titre: 'Truc',
|
||||
question: '?',
|
||||
},
|
||||
'sum . evt . ko': {},
|
||||
}
|
||||
// Rules in tests can be expressed in YAML like to for more clarity than JS objects
|
||||
const rawRules = parse(`
|
||||
ko: oui
|
||||
sum: oui
|
||||
sum . startHere:
|
||||
formule: 2
|
||||
non applicable si: sum . evt . ko
|
||||
sum . evt:
|
||||
formule:
|
||||
une possibilité:
|
||||
- ko
|
||||
titre: Truc
|
||||
question: '?'
|
||||
sum . evt . ko:
|
||||
`)
|
||||
|
||||
const result = Object.keys(
|
||||
new Engine(rawRules).evaluate('sum . startHere').missingVariables
|
||||
)
|
||||
|
@ -129,60 +132,74 @@ describe('Missing variables', function () {
|
|||
expect(result).to.be.empty
|
||||
})
|
||||
|
||||
// TODO : réparer ce test
|
||||
it.skip('should report missing variables in variations', function () {
|
||||
const rawRules = {
|
||||
top: 'oui',
|
||||
'top . startHere': {
|
||||
formule: { somme: ['variations'] },
|
||||
},
|
||||
'top . variations': {
|
||||
formule: {
|
||||
variations: [
|
||||
{
|
||||
si: 'dix',
|
||||
alors: {
|
||||
barème: {
|
||||
assiette: 2008,
|
||||
multiplicateur: 'deux',
|
||||
tranches: [
|
||||
{ plafond: 1, taux: 0.1 },
|
||||
{ plafond: 2, taux: 'trois' },
|
||||
{ taux: 10 },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
si: '3 > 4',
|
||||
alors: {
|
||||
barème: {
|
||||
assiette: 2008,
|
||||
multiplicateur: 'quatre',
|
||||
tranches: [
|
||||
{ plafond: 1, taux: 0.1 },
|
||||
{ plafond: 2, taux: 1.8 },
|
||||
{ 'au-dessus de': 2, taux: 10 },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'top . dix': {},
|
||||
'top . deux': {},
|
||||
'top . trois': {},
|
||||
'top . quatre': {},
|
||||
}
|
||||
it('should report missing variables in simple variations', function () {
|
||||
const rawRules = parse(`
|
||||
|
||||
somme: a + b
|
||||
a: 10
|
||||
b:
|
||||
formule:
|
||||
variations:
|
||||
- si: a > 100
|
||||
alors: c
|
||||
- sinon: 0
|
||||
c:
|
||||
question: Alors ?`)
|
||||
const result = Object.keys(
|
||||
new Engine(rawRules).evaluate('top . startHere').missingVariables
|
||||
new Engine(rawRules).evaluate('somme').missingVariables
|
||||
)
|
||||
|
||||
expect(result).to.include('top . dix')
|
||||
expect(result).to.include('top . deux')
|
||||
expect(result).to.include('top . trois')
|
||||
expect(result).not.to.include('top . quatre')
|
||||
expect(result).to.have.lengthOf(0)
|
||||
})
|
||||
|
||||
// TODO : réparer ce test
|
||||
it('should report missing variables in variations', function () {
|
||||
const rawRules = parse(`
|
||||
startHere:
|
||||
formule:
|
||||
somme:
|
||||
- variations
|
||||
variations:
|
||||
formule:
|
||||
variations:
|
||||
- si: dix
|
||||
alors:
|
||||
barème:
|
||||
assiette: 2008
|
||||
multiplicateur: deux
|
||||
tranches:
|
||||
- plafond: 1
|
||||
taux: 0.1
|
||||
- plafond: 2
|
||||
taux: trois
|
||||
- taux: 10
|
||||
- si: 3 > 4
|
||||
alors:
|
||||
barème:
|
||||
assiette: 2008
|
||||
multiplicateur: quatre
|
||||
tranches:
|
||||
- plafond: 1
|
||||
taux: 0.1
|
||||
- plafond: 2
|
||||
taux: 1.8
|
||||
- au-dessus de: 2
|
||||
taux: 10
|
||||
|
||||
dix: {}
|
||||
deux: {}
|
||||
trois: {}
|
||||
quatre: {}
|
||||
|
||||
`)
|
||||
const result = Object.keys(
|
||||
new Engine(rawRules).evaluate('startHere').missingVariables
|
||||
)
|
||||
|
||||
expect(result).to.include('dix')
|
||||
expect(result).to.include('deux')
|
||||
expect(result).to.include('trois')
|
||||
expect(result).not.to.include('quatre')
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -249,4 +266,98 @@ describe('nextSteps', function () {
|
|||
|
||||
expect(result).to.eql(['top . sum . evt'])
|
||||
})
|
||||
|
||||
it("Parent's other descendands in sums should not be included as missing variables", function () {
|
||||
// See https://github.com/betagouv/publicodes/issues/33
|
||||
const rawRules = parse(`
|
||||
transport:
|
||||
somme:
|
||||
- voiture
|
||||
- avion
|
||||
|
||||
transport . voiture:
|
||||
formule: empreinte * km
|
||||
|
||||
transport . voiture . empreinte: 0.12
|
||||
transport . voiture . km:
|
||||
question: COMBIENKM
|
||||
par défaut: 1000
|
||||
|
||||
transport . avion:
|
||||
applicable si: usager
|
||||
formule: empreinte * km
|
||||
|
||||
transport . avion . km:
|
||||
question: COMBIENKM
|
||||
par défaut: 10000
|
||||
|
||||
transport . avion . empreinte: 0.300
|
||||
|
||||
transport . avion . usager:
|
||||
question: Prenez-vous l'avion ?
|
||||
par défaut: oui
|
||||
`)
|
||||
const result = Object.keys(
|
||||
new Engine(rawRules).evaluate('transport . avion').missingVariables
|
||||
)
|
||||
|
||||
expect(result).deep.to.equal([
|
||||
'transport . avion . km',
|
||||
'transport . avion . usager',
|
||||
])
|
||||
expect(result).to.have.lengthOf(2)
|
||||
})
|
||||
it("Parent's other descendands in sums should not be included as missing variables - 2", function () {
|
||||
// See https://github.com/betagouv/publicodes/issues/33
|
||||
const rawRules = parse(`
|
||||
avion:
|
||||
question: prenez-vous l'avion ?
|
||||
par défaut: oui
|
||||
|
||||
avion . impact:
|
||||
formule:
|
||||
somme:
|
||||
- au sol
|
||||
- en vol
|
||||
|
||||
avion . impact . en vol:
|
||||
question: Combien de temps passé en vol ?
|
||||
par défaut: 10
|
||||
|
||||
avion . impact . au sol: 5
|
||||
`)
|
||||
const result = Object.keys(
|
||||
new Engine(rawRules).evaluate('avion . impact . au sol').missingVariables
|
||||
)
|
||||
|
||||
expect(result).deep.to.equal(['avion'])
|
||||
expect(result).to.have.lengthOf(1)
|
||||
})
|
||||
|
||||
it("Parent's other descendands in sums in applicability should be included as missing variables", function () {
|
||||
// See https://github.com/betagouv/publicodes/issues/33
|
||||
const rawRules = parse(`
|
||||
a:
|
||||
applicable si: d > 3
|
||||
valeur: oui
|
||||
|
||||
d:
|
||||
formule:
|
||||
somme:
|
||||
- e
|
||||
- 8
|
||||
|
||||
e:
|
||||
question: Vous venez à combien à la soirée ?
|
||||
par défaut: 3
|
||||
|
||||
a . b: 20 + 9
|
||||
`)
|
||||
const result = Object.keys(
|
||||
new Engine(rawRules).evaluate('a . b').missingVariables
|
||||
)
|
||||
|
||||
expect(result).deep.to.equal(['e'])
|
||||
expect(result).to.have.lengthOf(1)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"publicodes": "../../core/publicodes-1.0.0-beta.15.tgz",
|
||||
"publicodes-react": "../../ui-react/publicodes-react-1.0.0-beta.15.tgz",
|
||||
"publicodes": "../../core/publicodes-1.0.0-beta.16.tgz",
|
||||
"publicodes-react": "../../ui-react/publicodes-react-1.0.0-beta.16.tgz",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router": "^5.2.0",
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "publicodes-monorepo",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"core",
|
||||
"ui-react"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "yarn workspaces run test",
|
||||
"test:type": "yarn workspaces run tsc"
|
||||
}
|
||||
}
|
|
@ -11,7 +11,8 @@
|
|||
"clean": "rimraf node_modules"
|
||||
},
|
||||
"devDependencies": {
|
||||
"core-js": "^3.8.1"
|
||||
"core-js": "^3.8.1",
|
||||
"typescript": "^4.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "publicodes-react",
|
||||
"version": "1.0.0-beta.15",
|
||||
"version": "1.0.0-beta.16",
|
||||
"description": "UI to explore publicodes computations",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
@ -22,17 +22,22 @@
|
|||
"react-easy-emoji": "^1.4.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-router-hash-link": "^2.4.3",
|
||||
"styled-components": "^5.1.0",
|
||||
"yaml": "^1.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"publicodes": "1.0.0-beta.15",
|
||||
"publicodes": "1.0.0-beta.16",
|
||||
"react": "^17.0.2",
|
||||
"react-router-dom": "^5.1.1",
|
||||
"react-router-hash-link": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"js-yaml": "^4.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/react-router-hash-link": "^2.4.0",
|
||||
"@types/styled-components": "^5.1.10",
|
||||
"js-yaml": "^4.1.0",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue