mirror of
https://github.com/CatalaLang/catala.git
synced 2024-09-20 00:41:05 +03:00
Cross-test French housing benefits with official simulator (#338)
This commit is contained in:
commit
5e768ec747
@ -50,136 +50,102 @@ function run_computation_AF(log) {
|
||||
|
||||
function run_computation_AL(log) {
|
||||
var result = Law.computeAidesAuLogement({
|
||||
dateCouranteIn: "2022-01-01",
|
||||
menageIn: {
|
||||
prestationsRecues: [
|
||||
{ kind: "AllocationSoutienEnfantHandicape", payload: null },
|
||||
{ kind: "ComplementFamilial", payload: null },
|
||||
{ kind: "AllocationsFamiliales", payload: null },
|
||||
"menageIn": {
|
||||
"prestationsRecues": [],
|
||||
"logement": {
|
||||
"residencePrincipale": true,
|
||||
"estEhpadOuMaisonAutonomieL31312Asf": false,
|
||||
"modeOccupation": {
|
||||
"kind": "Locataire",
|
||||
"payload": {
|
||||
"bailleur": {
|
||||
"kind": "BailleurPrive"
|
||||
},
|
||||
"beneficiaireAideAdulteOuEnfantHandicapes": false,
|
||||
"logementEstChambre": false,
|
||||
"colocation": false,
|
||||
"ageesOuHandicapAdultesHebergeesOnereuxParticuliers": false,
|
||||
"reductionLoyerSolidarite": 0,
|
||||
"logementMeubleD8422": false,
|
||||
"changementLogementD8424": {
|
||||
"kind": "PasDeChangement",
|
||||
"payload": null
|
||||
},
|
||||
"loyerPrincipal": 450
|
||||
}
|
||||
},
|
||||
"proprietaire": {
|
||||
"kind": "Autre",
|
||||
"payload": null
|
||||
},
|
||||
"loueOuSousLoueADesTiers": {
|
||||
"kind": "Non"
|
||||
},
|
||||
"usufruit": {
|
||||
"kind": "Autre",
|
||||
"payload": null
|
||||
},
|
||||
"logementDecentL89462": true,
|
||||
"zone": {
|
||||
"kind": "Zone2"
|
||||
},
|
||||
"surfaceMCarres": 65
|
||||
},
|
||||
"personnesACharge": [
|
||||
{
|
||||
"kind": "EnfantACharge",
|
||||
"payload": {
|
||||
"beneficieTitrePersonnelAidePersonnelleLogement": false,
|
||||
"aDejaOuvertDroitAuxAllocationsFamiliales": true,
|
||||
"remunerationMensuelle": 0,
|
||||
"obligationScolaire": {
|
||||
"kind": "Pendant"
|
||||
},
|
||||
"situationGardeAlternee": {
|
||||
"kind": "PasDeGardeAlternee"
|
||||
},
|
||||
"dateDeNaissance": "2015-01-01",
|
||||
"identifiant": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "EnfantACharge",
|
||||
"payload": {
|
||||
"beneficieTitrePersonnelAidePersonnelleLogement": false,
|
||||
"aDejaOuvertDroitAuxAllocationsFamiliales": true,
|
||||
"remunerationMensuelle": 0,
|
||||
"obligationScolaire": {
|
||||
"kind": "Pendant"
|
||||
},
|
||||
"situationGardeAlternee": {
|
||||
"kind": "PasDeGardeAlternee"
|
||||
},
|
||||
"dateDeNaissance": "2016-01-01",
|
||||
"identifiant": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
situationFamiliale: {
|
||||
kind: "Maries",
|
||||
payload: "2010-11-26",
|
||||
},
|
||||
personnesACharge: [
|
||||
{
|
||||
kind: "EnfantACharge",
|
||||
payload: {
|
||||
beneficieTitrePersonnelAidePersonnelleLogement: false,
|
||||
priseEnCharge: { kind: "EffectiveEtPermanente", payload: null },
|
||||
age: 19,
|
||||
identifiant: 0,
|
||||
aDejaOuvertDroitAuxAllocationsFamiliales: true,
|
||||
dateDeNaissance: "2003-01-01",
|
||||
remunerationMensuelle: 0,
|
||||
obligationScolaire: { kind: "Apres", payload: null },
|
||||
situationGardeAlternee: {
|
||||
kind: "GardeAlterneeCoefficientPriseEnCharge",
|
||||
payload: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "EnfantACharge",
|
||||
payload: {
|
||||
beneficieTitrePersonnelAidePersonnelleLogement: false,
|
||||
priseEnCharge: { kind: "EffectiveEtPermanente", payload: null },
|
||||
age: 11,
|
||||
identifiant: 1,
|
||||
aDejaOuvertDroitAuxAllocationsFamiliales: true,
|
||||
dateDeNaissance: "2011-01-01",
|
||||
remunerationMensuelle: 0,
|
||||
obligationScolaire: { kind: "Pendant", payload: null },
|
||||
situationGardeAlternee: {
|
||||
kind: "PasDeGardeAlternee",
|
||||
payload: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "EnfantACharge",
|
||||
payload: {
|
||||
beneficieTitrePersonnelAidePersonnelleLogement: false,
|
||||
priseEnCharge: { kind: "EffectiveEtPermanente", payload: null },
|
||||
age: 8,
|
||||
identifiant: 2,
|
||||
aDejaOuvertDroitAuxAllocationsFamiliales: true,
|
||||
dateDeNaissance: "2014-01-01",
|
||||
remunerationMensuelle: 0,
|
||||
obligationScolaire: { kind: "Pendant", payload: null },
|
||||
situationGardeAlternee: {
|
||||
kind: "PasDeGardeAlternee",
|
||||
payload: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
logement: {
|
||||
zone: { kind: "Zone1", payload: null },
|
||||
residencePrincipale: true,
|
||||
estEhpadOuMaisonAutonomieL31312Asf: false,
|
||||
modeOccupation: {
|
||||
kind: "Locataire",
|
||||
payload: {
|
||||
bailleur: {
|
||||
typeBailleur: { kind: "BailleurPrive", payload: null },
|
||||
respecteConventionTitreV: true,
|
||||
respecteConventionTitreII: true,
|
||||
construitAmelioreConditionsL83114: false,
|
||||
acquisitionAidesEtatPretTitreIIOuLivreIII: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
proprietaire: { kind: "Autre", payload: null },
|
||||
loueOuSousLoueADesTiers: { kind: "Non", payload: null },
|
||||
usufruit: { kind: "Autre", payload: null },
|
||||
logementDecentL89462: true,
|
||||
loyersL8233: 700,
|
||||
surfaceMCarres: 80,
|
||||
estAncienL8312: false,
|
||||
situeCommuneDesequilibreL8312: false,
|
||||
},
|
||||
nombreAutresOccupantsLogement: 1,
|
||||
conditionRattacheFoyerFiscalParentIfi: false,
|
||||
nombreEnfantsANaitreApresTroisiemeMoisGrossesse: 0,
|
||||
enfantANaitreApresQuatriemeMoisGrossesse: false,
|
||||
dateNaissanceTroisiemeEnfantOuDernierSiPlus: {
|
||||
kind: "PlusDeTroisEnfants",
|
||||
payload: {
|
||||
kind: "DateDeNaissance",
|
||||
payload: "2014-09-15",
|
||||
},
|
||||
"nombreAutresOccupantsLogement": 0,
|
||||
"situationFamiliale": {
|
||||
"kind": "Concubins",
|
||||
"payload": null
|
||||
},
|
||||
"conditionRattacheFoyerFiscalParentIfi": false,
|
||||
"nombreEnfantsANaitreApresTroisiemeMoisGrossesse": 0,
|
||||
},
|
||||
demandeurIn: {
|
||||
personneHebergeeCentreSoinLL162223SecuriteSociale: false,
|
||||
satisfaitConditionsL5122CodeSecuriteSociale: true,
|
||||
ageDemandeur: 52,
|
||||
dateNaissance: "1970-05-02",
|
||||
contratDeTravail: { kind: "CDI", payload: null },
|
||||
nationalite: { kind: "Francaise", payload: null },
|
||||
patrimoine: {
|
||||
produisantRevenuPeriodeR82233R8224: 0,
|
||||
neProduisantPasRevenuPeriodeR82233R8224: 0,
|
||||
"demandeurIn": {
|
||||
"nationalite": {
|
||||
"kind": "Francaise"
|
||||
},
|
||||
},
|
||||
informationsCalculIn: {
|
||||
kind: "InfosLocatif",
|
||||
payload: {
|
||||
loyerPrincipal: 1700,
|
||||
beneficiaireAideAdulteOuEnfantHandicapes: false,
|
||||
logementEstChambre: false,
|
||||
colocation: false,
|
||||
ageesOuHandicapAdultesHebergeesOnereuxParticuliers: false,
|
||||
reductionLoyerSolidarite: 0,
|
||||
logementMeubleD8422: false,
|
||||
changementLogementD8424: {
|
||||
kind: "PasDeChangement",
|
||||
payload: null,
|
||||
},
|
||||
"patrimoine": {
|
||||
"produisantRevenuPeriodeR82233R8224": 0,
|
||||
"neProduisantPasRevenuPeriodeR82233R8224": 0
|
||||
},
|
||||
"personneHebergeeCentreSoinLL162223SecuriteSociale": false,
|
||||
"dateNaissance": "1992-01-01"
|
||||
},
|
||||
ressourcesMenagePrisesEnCompteIn: 20000,
|
||||
"dateCouranteIn": "2022-05-01",
|
||||
"ressourcesMenagePrisesEnCompteIn": 11500
|
||||
});
|
||||
if (log) {
|
||||
console.log(
|
||||
|
9072
french_law/js/french_law.js
generated
9072
french_law/js/french_law.js
generated
File diff suppressed because one or more lines are too long
2
french_law/ocaml/law_source/aides_logement.ml
generated
2
french_law/ocaml/law_source/aides_logement.ml
generated
@ -15253,7 +15253,7 @@ let ressources_aides_personnelle_logement (ressources_aides_personnelle_logement
|
||||
if
|
||||
(demandeur_exerce_activite_remuneree_ &&
|
||||
(conjoint_exerce_activite_remuneree_ &&
|
||||
((ressources_conjoint_ +$ ressources_conjoint_) >=$
|
||||
((ressources_demandeur_ +$ ressources_conjoint_) >=$
|
||||
(base_mensuelle_allocations_familiales_dot_montant_ *$
|
||||
(decimal_of_string "12."))))) then
|
||||
montant_forfaitaire_r_822_7_ else (money_of_cents_string "0")))
|
||||
|
@ -13,7 +13,8 @@ format:
|
||||
autopep8 --in-place $(SOURCES)
|
||||
|
||||
bench:
|
||||
python main.py bench
|
||||
python main.py bench_family
|
||||
python main.py bench_housing
|
||||
|
||||
show_log:
|
||||
python main.py show_log
|
||||
|
145
french_law/python/cnaf_cross_tester/cnaf_to_catala.py
Normal file
145
french_law/python/cnaf_cross_tester/cnaf_to_catala.py
Normal file
@ -0,0 +1,145 @@
|
||||
import datetime
|
||||
from typing import List
|
||||
from src.aides_logement import CategorieEquivalenceLoyerAllocationLogementFoyer_Code, LogementFoyer, ModeOccupation_Code, Nationalite_Code, SituationFamiliale_Code, SituationGardeAlternee_Code, SituationObligationScolaire_Code, TypeBailleur_Code, TypeLogementFoyer_Code, ZoneDHabitation_Code
|
||||
from input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, Enfant, LogementCrous, LogementCrousType, LogementMaisonRetraite, LogementResidenceSocialeFJT, SeulOuCouple, Zone
|
||||
from src.api import EnfantAPL, InfosLocation, InfosLogementFoyer, InfosSpecifiques, PersonneAChargeAPL, aides_logement
|
||||
|
||||
|
||||
def run_catala_by_converting_cnaf_input(sample_input: CnafSimulatorInput) -> float:
|
||||
enfants: List[PersonneAChargeAPL] = []
|
||||
i = 0
|
||||
for enfant in sample_input.enfants:
|
||||
enfants.append(EnfantAPL(
|
||||
identifiant=i,
|
||||
beneficie_titre_personnel_aide_personnelle_logement=False,
|
||||
a_deja_ouvert_droit_aux_allocations_familiales=False,
|
||||
date_de_naissance=datetime.date.today() - datetime.timedelta(days=366 * enfant.age),
|
||||
remuneration_mensuelle=int(
|
||||
enfant.remuneration_derniere_annee / 12),
|
||||
obligation_scolaire=SituationObligationScolaire_Code.Avant if enfant.age < 3 else (
|
||||
SituationObligationScolaire_Code.Apres if enfant.age > 16 else SituationObligationScolaire_Code.Pendant),
|
||||
situation_garde_alternee=SituationGardeAlternee_Code.PasDeGardeAlternee,
|
||||
coefficient_garde_alternee=None
|
||||
))
|
||||
i += 1
|
||||
|
||||
mode_occupation: ModeOccupation_Code
|
||||
infos_specifiques: InfosSpecifiques
|
||||
if isinstance(sample_input.logement, AppartementOuMaison):
|
||||
mode_occupation = ModeOccupation_Code.Locataire
|
||||
infos_specifiques = InfosLocation(
|
||||
loyer_principal=sample_input.loyer,
|
||||
beneficiaire_aide_adulte_ou_enfant_handicapes=False,
|
||||
logement_est_chambre=False,
|
||||
colocation=sample_input.logement.typ_v == AppartementOuMaisonType.Colocation,
|
||||
agees_ou_handicap_adultes_hebergees_onereux_particuliers=False,
|
||||
logement_meuble_d842_2=sample_input.logement.meuble_v,
|
||||
ancien_loyer_et_apl_relogement=None,
|
||||
type_bailleur=TypeBailleur_Code.BailleurPrive,
|
||||
bailleur_conventionne=None,
|
||||
reduction_loyer_solidarite=None
|
||||
)
|
||||
elif isinstance(sample_input.logement, LogementCrous):
|
||||
# Les correspondances avec les catégories réglementaires sont faites selon DGALN/DHUP/FE4 (mail du 26/07/2022)
|
||||
mode_occupation = ModeOccupation_Code.Locataire if sample_input.logement.typ_v == LogementCrousType.Studio else ModeOccupation_Code.ResidentLogementFoyer
|
||||
infos_specifiques = InfosLocation(
|
||||
loyer_principal=sample_input.loyer,
|
||||
beneficiaire_aide_adulte_ou_enfant_handicapes=False,
|
||||
logement_est_chambre=False,
|
||||
colocation=False,
|
||||
agees_ou_handicap_adultes_hebergees_onereux_particuliers=False,
|
||||
logement_meuble_d842_2=False,
|
||||
ancien_loyer_et_apl_relogement=None,
|
||||
type_bailleur=TypeBailleur_Code.BailleurPrive,
|
||||
bailleur_conventionne=None,
|
||||
reduction_loyer_solidarite=None
|
||||
) if sample_input.logement.typ_v == LogementCrousType.Studio else InfosLogementFoyer(
|
||||
type=TypeLogementFoyer_Code.Autre,
|
||||
remplit_conditions_r832_21=True,
|
||||
conventionne_livre_III_titre_V_chap_III=True,
|
||||
date_conventionnement=datetime.date(2000, 1, 1),
|
||||
construit_application_loi_1957_12_III=False,
|
||||
redevance=sample_input.loyer,
|
||||
categorie_equivalence_loyer_d842_16=CategorieEquivalenceLoyerAllocationLogementFoyer_Code.EtudiantLogeEnChambreCROUS if
|
||||
sample_input.logement.typ_v == LogementCrousType.Chambre else
|
||||
CategorieEquivalenceLoyerAllocationLogementFoyer_Code.EtudiantLogeEnChambreCROUSRehabilitee
|
||||
)
|
||||
elif isinstance(sample_input.logement, LogementFoyer):
|
||||
mode_occupation = ModeOccupation_Code.ResidentLogementFoyer
|
||||
infos_specifiques = InfosLogementFoyer(
|
||||
type=TypeLogementFoyer_Code.Autre,
|
||||
remplit_conditions_r832_21=True,
|
||||
conventionne_livre_III_titre_V_chap_III=True,
|
||||
date_conventionnement=datetime.date(2000, 1, 1),
|
||||
construit_application_loi_1957_12_III=False,
|
||||
redevance=sample_input.loyer,
|
||||
categorie_equivalence_loyer_d842_16=CategorieEquivalenceLoyerAllocationLogementFoyer_Code.AutresPersonnes
|
||||
)
|
||||
elif isinstance(sample_input.logement, LogementResidenceSocialeFJT):
|
||||
# Correspond au 2° du D832-25 selon DGALN/DHUP/FE4 (mail du 26/07/2022)
|
||||
mode_occupation = ModeOccupation_Code.ResidentLogementFoyer
|
||||
infos_specifiques = InfosLogementFoyer(
|
||||
type=TypeLogementFoyer_Code.ResidenceSociale,
|
||||
remplit_conditions_r832_21=True,
|
||||
conventionne_livre_III_titre_V_chap_III=True,
|
||||
date_conventionnement=datetime.date(2000, 1, 1),
|
||||
construit_application_loi_1957_12_III=False,
|
||||
redevance=sample_input.loyer,
|
||||
categorie_equivalence_loyer_d842_16=CategorieEquivalenceLoyerAllocationLogementFoyer_Code.AutresPersonnes
|
||||
)
|
||||
elif isinstance(sample_input.logement, LogementMaisonRetraite):
|
||||
# Correspond au 3° du D842-16 et au 1° du R832-20 selon DGALN/DHUP/FE4 (mail du 26/07/2022)
|
||||
mode_occupation = ModeOccupation_Code.ResidentLogementFoyer
|
||||
infos_specifiques = InfosLogementFoyer(
|
||||
type=TypeLogementFoyer_Code.LogementPersonnesAgeesOuHandicapees,
|
||||
remplit_conditions_r832_21=True,
|
||||
conventionne_livre_III_titre_V_chap_III=True,
|
||||
date_conventionnement=datetime.date(2000, 1, 1),
|
||||
construit_application_loi_1957_12_III=False,
|
||||
redevance=sample_input.loyer,
|
||||
categorie_equivalence_loyer_d842_16=CategorieEquivalenceLoyerAllocationLogementFoyer_Code.PersonnesAgeesSelon3DeD842_16
|
||||
)
|
||||
else: # isinstance(sample_input.logement, LogementChambre):
|
||||
mode_occupation = ModeOccupation_Code.Locataire
|
||||
infos_specifiques = InfosLocation(
|
||||
loyer_principal=sample_input.loyer,
|
||||
beneficiaire_aide_adulte_ou_enfant_handicapes=False,
|
||||
logement_est_chambre=True,
|
||||
colocation=False,
|
||||
agees_ou_handicap_adultes_hebergees_onereux_particuliers=False,
|
||||
logement_meuble_d842_2=False,
|
||||
ancien_loyer_et_apl_relogement=None,
|
||||
type_bailleur=TypeBailleur_Code.BailleurPrive,
|
||||
bailleur_conventionne=None,
|
||||
reduction_loyer_solidarite=None
|
||||
)
|
||||
|
||||
housing_benefits_gross = aides_logement(
|
||||
datetime.date.today(),
|
||||
ressources_menage_prises_en_compte=sample_input.revenu_pris_en_compte,
|
||||
date_naissance_demandeur=datetime.date(1992, 1, 1),
|
||||
nationalite_demandeur=Nationalite_Code.Francaise,
|
||||
patrimoine_produisant_revenu=0,
|
||||
patrimoine_ne_produisant_pas_revenu=0,
|
||||
personne_hebergee_centre_soins=False,
|
||||
personne_rattache_foyer_fiscal_parent_ifi=False,
|
||||
nombre_autres_occupants_logement_hors_menage=0,
|
||||
enfant_a_naitre_apres_quatrieme_mois_grossesse=False,
|
||||
situation_familiale=SituationFamiliale_Code.Celibataire if sample_input.seul_ou_couple == SeulOuCouple.Seul else SituationFamiliale_Code.Concubins,
|
||||
date_mariage=None,
|
||||
prestations_recues=[],
|
||||
residence_principale=True,
|
||||
logement_est_maison_de_retraite=True if isinstance(
|
||||
sample_input.logement, LogementMaisonRetraite) else False,
|
||||
logement_est_decent=True,
|
||||
surface_logement_m_carres=10000,
|
||||
zone=ZoneDHabitation_Code.Zone1 if sample_input.zone == Zone.Zone1 else (
|
||||
ZoneDHabitation_Code.Zone2 if sample_input.zone == Zone.Zone2 else ZoneDHabitation_Code.Zone3),
|
||||
parts_logement_propriete_famille=None,
|
||||
parts_logement_usufruits_famille=None,
|
||||
date_naissance_et_conformite_sous_locataire_tiers=None,
|
||||
personnes_a_charge=enfants,
|
||||
mode_occupation=mode_occupation,
|
||||
infos_specifiques=infos_specifiques)
|
||||
|
||||
return round(housing_benefits_gross*0.995) # We take the CRDS
|
69
french_law/python/cnaf_cross_tester/differing_outputs.txt
Normal file
69
french_law/python/cnaf_cross_tester/differing_outputs.txt
Normal file
@ -0,0 +1,69 @@
|
||||
🏡 Description du ménage
|
||||
-> Code postal : Zone2
|
||||
-> Logement : AppartementOuMaison(typ=Colocation,meuble=True)
|
||||
-> Loyer : 1240 €
|
||||
-> Seul of couple : Seul
|
||||
-> Enfants :
|
||||
Enfant(age=13,remuneration_derniere_annee=7385)
|
||||
-> Revenus pris en compte : 5800 €
|
||||
💰 Aides au logement (Catala): 394 €
|
||||
💰 Aides au logement (CNAF) : 137 €
|
||||
❌ Différence
|
||||
|
||||
🏡 Description du ménage
|
||||
-> Code postal : Zone3
|
||||
-> Logement : LogementResidenceSocialeFJT
|
||||
-> Loyer : 1557 €
|
||||
-> Seul of couple : Seul
|
||||
-> Enfants :
|
||||
Enfant(age=10,remuneration_derniere_annee=8827)
|
||||
Enfant(age=7,remuneration_derniere_annee=8643)
|
||||
-> Revenus pris en compte : 3800 €
|
||||
💰 Aides au logement (Catala): 435 €
|
||||
💰 Aides au logement (CNAF) : 81 €
|
||||
❌ Différence
|
||||
|
||||
🏡 Description du ménage
|
||||
-> Code postal : Zone1
|
||||
-> Logement : LogementChambre(meuble=True)
|
||||
-> Loyer : 1225 €
|
||||
-> Seul of couple : EnCouple
|
||||
-> Enfants :
|
||||
Enfant(age=16,remuneration_derniere_annee=0)
|
||||
Enfant(age=10,remuneration_derniere_annee=0)
|
||||
Enfant(age=19,remuneration_derniere_annee=0)
|
||||
Enfant(age=7,remuneration_derniere_annee=0)
|
||||
-> Revenus pris en compte : 11800 €
|
||||
💰 Aides au logement (Catala): 0 €
|
||||
💰 Aides au logement (CNAF) : 291 €
|
||||
❌ Différence
|
||||
|
||||
🏡 Description du ménage
|
||||
-> Code postal : Zone3
|
||||
-> Logement : LogementCrous(typ=Studio)
|
||||
-> Loyer : 1271 €
|
||||
-> Seul of couple : EnCouple
|
||||
-> Enfants :
|
||||
Enfant(age=12,remuneration_derniere_annee=3789)
|
||||
Enfant(age=10,remuneration_derniere_annee=4198)
|
||||
-> Revenus pris en compte : 9200 €
|
||||
💰 Aides au logement (Catala): 0 €
|
||||
💰 Aides au logement (CNAF) : 297 €
|
||||
❌ Différence
|
||||
|
||||
🏡 Description du ménage
|
||||
-> Code postal : Zone3
|
||||
-> Logement : LogementCrous(typ=Studio)
|
||||
-> Loyer : 513 €
|
||||
-> Seul of couple : Seul
|
||||
-> Enfants :
|
||||
Enfant(age=18,remuneration_derniere_annee=0)
|
||||
Enfant(age=7,remuneration_derniere_annee=0)
|
||||
Enfant(age=17,remuneration_derniere_annee=0)
|
||||
Enfant(age=21,remuneration_derniere_annee=0)
|
||||
Enfant(age=6,remuneration_derniere_annee=0)
|
||||
Enfant(age=5,remuneration_derniere_annee=0)
|
||||
-> Revenus pris en compte : 11800 €
|
||||
💰 Aides au logement (Catala): 524 €
|
||||
💰 Aides au logement (CNAF) : 372 €
|
||||
❌ Différence
|
182
french_law/python/cnaf_cross_tester/input.py
Normal file
182
french_law/python/cnaf_cross_tester/input.py
Normal file
@ -0,0 +1,182 @@
|
||||
from abc import ABC
|
||||
from enum import Enum
|
||||
from typing import Any, List, Optional
|
||||
|
||||
|
||||
class Logement(ABC):
|
||||
def residence(self) -> str:
|
||||
pass
|
||||
|
||||
def typ(self) -> Optional[str]:
|
||||
pass
|
||||
|
||||
def meublee(self) -> Optional[bool]:
|
||||
pass
|
||||
|
||||
def __str__(self) -> str:
|
||||
pass
|
||||
|
||||
|
||||
class AppartementOuMaisonType(Enum):
|
||||
Location = "LOCATION"
|
||||
Colocation = "COLOCATION"
|
||||
|
||||
|
||||
class AppartementOuMaison(Logement):
|
||||
def __init__(self, typ: AppartementOuMaisonType, meuble: bool):
|
||||
self.residence_v = "APPARTEMENT_OU_MAISON"
|
||||
self.typ_v = typ
|
||||
self.meuble_v = meuble
|
||||
|
||||
def residence(self) -> str:
|
||||
return self.residence_v
|
||||
|
||||
def typ(self) -> Optional[str]:
|
||||
return self.typ_v.value
|
||||
|
||||
def meublee(self) -> Optional[bool]:
|
||||
return self.meuble_v
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "AppartementOuMaison(typ={},meuble={})".format(self.typ_v.name, self.meuble_v)
|
||||
|
||||
|
||||
class LogementCrousType(Enum):
|
||||
Chambre = 'CHAMBRE'
|
||||
Chambre_rehabilitee = 'CHAMBRE_REHABILITEE'
|
||||
Studio = 'STUDIO'
|
||||
|
||||
|
||||
class LogementCrous(Logement):
|
||||
def __init__(self, typ: LogementCrousType):
|
||||
self.residence_v = 'LOGEMENT_CROUS'
|
||||
self.typ_v = typ
|
||||
|
||||
def residence(self) -> str:
|
||||
return self.residence_v
|
||||
|
||||
def typ(self) -> Optional[str]:
|
||||
return self.typ_v.value
|
||||
|
||||
def meublee(self) -> Optional[bool]:
|
||||
return None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "LogementCrous(typ={})".format(self.typ_v.name)
|
||||
|
||||
|
||||
class LogementFoyer(Logement):
|
||||
def __init__(self):
|
||||
self.residence_v = 'FOYER'
|
||||
|
||||
def residence(self) -> str:
|
||||
return self.residence_v
|
||||
|
||||
def typ(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
def meublee(self) -> Optional[bool]:
|
||||
return None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "LogementFoyer"
|
||||
|
||||
|
||||
class LogementResidenceSocialeFJT(Logement):
|
||||
def __init__(self):
|
||||
self.residence_v = 'RESIDENCE_SOCIALE_FJT'
|
||||
|
||||
def residence(self) -> str:
|
||||
return self.residence_v
|
||||
|
||||
def typ(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
def meublee(self) -> Optional[bool]:
|
||||
return None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "LogementResidenceSocialeFJT"
|
||||
|
||||
|
||||
class LogementMaisonRetraite(Logement):
|
||||
def __init__(self):
|
||||
self.residence_v = 'MAISON_RETRAITE_EHPAD'
|
||||
|
||||
def residence(self) -> str:
|
||||
return self.residence_v
|
||||
|
||||
def typ(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
def meublee(self) -> Optional[bool]:
|
||||
return None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "LogementMaisonRetraite"
|
||||
|
||||
|
||||
class LogementChambre(Logement):
|
||||
def __init__(self, meuble: bool):
|
||||
self.residence_v = 'CHAMBRE'
|
||||
self.meuble_v = meuble
|
||||
|
||||
def residence(self) -> str:
|
||||
return self.residence_v
|
||||
|
||||
def typ(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
def meublee(self) -> Optional[bool]:
|
||||
return self.meuble_v
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "LogementChambre(meuble={})".format(self.meuble_v)
|
||||
|
||||
|
||||
class SeulOuCouple(Enum):
|
||||
Seul = 'CEL'
|
||||
EnCouple = 'VIM'
|
||||
|
||||
|
||||
class Enfant():
|
||||
def __init__(self,
|
||||
age: int,
|
||||
remuneration_derniere_annee: int):
|
||||
self.age = age
|
||||
self.remuneration_derniere_annee = remuneration_derniere_annee
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "Enfant(age={},remuneration_derniere_annee={})".format(self.age, self.remuneration_derniere_annee)
|
||||
|
||||
|
||||
class Zone(Enum):
|
||||
Zone1 = "75001"
|
||||
Zone2 = "69001"
|
||||
Zone3 = "46800"
|
||||
|
||||
|
||||
class CnafSimulatorInput():
|
||||
def __init__(self,
|
||||
zone: Zone,
|
||||
logement: Logement,
|
||||
loyer: int,
|
||||
seul_ou_couple: SeulOuCouple,
|
||||
enfants: List[Enfant],
|
||||
revenu_pris_en_compte: int):
|
||||
self.zone = zone
|
||||
self.logement = logement
|
||||
self.loyer = loyer
|
||||
self.seul_ou_couple = seul_ou_couple
|
||||
self.enfants = enfants
|
||||
self.revenu_pris_en_compte = revenu_pris_en_compte
|
||||
|
||||
def __str__(self):
|
||||
return "-> Code postal : {}\n-> Logement : {}\n-> Loyer : {} €\n-> Seul of couple : {}\n-> Enfants :\n{}\n-> Revenus pris en compte : {} €".format(
|
||||
self.zone.name,
|
||||
self.logement,
|
||||
self.loyer,
|
||||
self.seul_ou_couple.name,
|
||||
"\n".join(["{}".format(enfant) for enfant in self.enfants]),
|
||||
self.revenu_pris_en_compte
|
||||
)
|
33
french_law/python/cnaf_cross_tester/main.py
Normal file
33
french_law/python/cnaf_cross_tester/main.py
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
|
||||
from cnaf_to_catala import run_catala_by_converting_cnaf_input
|
||||
from pupeteer import run_simulator
|
||||
from input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, Enfant, SeulOuCouple, Zone
|
||||
from random_input_generator import generate_random_input
|
||||
|
||||
# input identical to the JS test of the housing benefits
|
||||
sample_input = CnafSimulatorInput(
|
||||
zone=Zone.Zone2,
|
||||
logement=AppartementOuMaison(
|
||||
AppartementOuMaisonType.Location, meuble=False),
|
||||
loyer=450,
|
||||
seul_ou_couple=SeulOuCouple.EnCouple,
|
||||
enfants=[Enfant(age=7, remuneration_derniere_annee=0),
|
||||
Enfant(age=8, remuneration_derniere_annee=0)],
|
||||
revenu_pris_en_compte=11_500
|
||||
)
|
||||
# Or a random input
|
||||
sample_input = generate_random_input()
|
||||
print("🏡 Description du ménage")
|
||||
print(sample_input)
|
||||
housing_benefits_catala = run_catala_by_converting_cnaf_input(sample_input)
|
||||
print("💰 Aides au logement (Catala): {} €".format(housing_benefits_catala))
|
||||
housing_benefits_cnaf = run_simulator(sample_input)
|
||||
print("💰 Aides au logement (CNAF) : {} €".format(housing_benefits_cnaf))
|
||||
delta = abs(housing_benefits_catala - housing_benefits_cnaf)
|
||||
if delta == 0:
|
||||
print("✅ Pas de difference")
|
||||
exit(0)
|
||||
else:
|
||||
print("❌ Différence")
|
||||
exit(-1)
|
205
french_law/python/cnaf_cross_tester/pupeteer.py
Normal file
205
french_law/python/cnaf_cross_tester/pupeteer.py
Normal file
@ -0,0 +1,205 @@
|
||||
# This code is ported from https://github.com/jboillot/apl-fetcher
|
||||
|
||||
from pdb import runeval
|
||||
from playwright.sync_api import sync_playwright
|
||||
from input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, Enfant, LogementCrous, SeulOuCouple
|
||||
import re
|
||||
|
||||
HOME_PAGE = 'https://wwwd.caf.fr/wps/portal/caffr/aidesetservices/lesservicesenligne/estimervosdroits/lelogement'
|
||||
|
||||
|
||||
def run_simulator(input: CnafSimulatorInput) -> int:
|
||||
with sync_playwright() as p:
|
||||
browser = p.firefox.launch(headless=False, slow_mo=100)
|
||||
page = browser.new_page()
|
||||
|
||||
# Go to the CNAF simulator
|
||||
page.goto(HOME_PAGE)
|
||||
|
||||
# Click on cookie banner
|
||||
cookie_button = page.wait_for_selector(
|
||||
"div[id=\"popup-accept-cookies\"] >> button")
|
||||
if cookie_button is None:
|
||||
raise RuntimeError
|
||||
cookie_button.click(force=True)
|
||||
|
||||
# Click on 'Commencer'
|
||||
commencer_button = page.wait_for_selector('button[id="btn-suivant"]')
|
||||
if commencer_button is None:
|
||||
raise RuntimeError
|
||||
commencer_button.click(force=True)
|
||||
|
||||
# Code Postal
|
||||
code_postal_input = page.wait_for_selector('input[id="cpCommune"]')
|
||||
if code_postal_input is None:
|
||||
raise RuntimeError
|
||||
code_postal_input.fill(input.zone.value)
|
||||
|
||||
# Select first match
|
||||
page.wait_for_selector('a[class="ng-tns-c31-0"]')
|
||||
page.keyboard.press('Enter')
|
||||
|
||||
# Select residence type
|
||||
type_residence_button = page.wait_for_selector(
|
||||
"input[id=\"lieuResidence_{}\"]".format(input.logement.residence()))
|
||||
if type_residence_button is None:
|
||||
raise RuntimeError
|
||||
type_residence_button.click(force=True)
|
||||
|
||||
# Select housing type for some things
|
||||
if isinstance(input.logement, AppartementOuMaison):
|
||||
type_appartement_ou_maison_button = page.wait_for_selector(
|
||||
"input[id=\"typeOccupation_{}\"]".format(input.logement.typ()))
|
||||
if type_appartement_ou_maison_button is None:
|
||||
raise RuntimeError
|
||||
type_appartement_ou_maison_button.click(force=True)
|
||||
elif isinstance(input.logement, LogementCrous):
|
||||
type_crous_button = page.wait_for_selector(
|
||||
"input[id=\"typeCrous_{}\"]".format(input.logement.typ()))
|
||||
if type_crous_button is None:
|
||||
raise RuntimeError
|
||||
type_crous_button.click(force=True)
|
||||
|
||||
# Is the location meublee
|
||||
|
||||
if not (input.logement.meublee() is None):
|
||||
meuble_button = page.wait_for_selector(
|
||||
"input[id=\"estMeuble_{}\"]".format("true" if input.logement.meublee() else "false"))
|
||||
if meuble_button is None:
|
||||
raise RuntimeError
|
||||
meuble_button.click(force=True)
|
||||
|
||||
# Monthly rent
|
||||
loyer_mensuel_input = page.wait_for_selector(
|
||||
'input[id="mttDeclare"]')
|
||||
if loyer_mensuel_input is None:
|
||||
raise RuntimeError
|
||||
loyer_mensuel_input.fill("{}".format(input.loyer))
|
||||
|
||||
# Couple or not
|
||||
seul_button = page.wait_for_selector(
|
||||
"input[id=\"situationFamiliale_{}\"]".format(input.seul_ou_couple.value))
|
||||
if seul_button is None:
|
||||
raise RuntimeError
|
||||
seul_button.click(force=True)
|
||||
|
||||
# Number of children
|
||||
if len(input.enfants) < 0 or len(input.enfants) > 20:
|
||||
raise RuntimeError
|
||||
plus_button = page.wait_for_selector('button:has-text("+")')
|
||||
if plus_button is None:
|
||||
raise RuntimeError
|
||||
for i in range(len(input.enfants)):
|
||||
plus_button.click(force=True)
|
||||
|
||||
# Main applicant salary
|
||||
salaires_button = page.wait_for_selector(
|
||||
"button[id=\"SALAIRE_0_checkbox\"]")
|
||||
if salaires_button is None:
|
||||
raise RuntimeError
|
||||
salaires_button.click(force=True)
|
||||
salaires_input = page.wait_for_selector(
|
||||
"input[id=\"SALAIRE_0_montant\"]")
|
||||
if salaires_input is None:
|
||||
raise RuntimeError
|
||||
salaires_input.fill("{}".format(
|
||||
int(float(input.revenu_pris_en_compte) / 0.9)))
|
||||
salaires_input.wait_for_element_state(state="stable")
|
||||
# We divide by 0.9 because salaries have a 10% franchise
|
||||
# Now if the salary is too low you have to click another button
|
||||
status_selector = "input[id=\"sitro_0_SITPRO_ACT_INCONNUE\"]"
|
||||
if not (page.query_selector is None):
|
||||
status_button = page.wait_for_selector(status_selector)
|
||||
if status_button is None:
|
||||
raise RuntimeError
|
||||
status_button.click(force=True)
|
||||
|
||||
# If there is a partner we assume no income for simplicity
|
||||
if input.seul_ou_couple == SeulOuCouple.EnCouple:
|
||||
sans_button = page.wait_for_selector(
|
||||
"button[id=\"aucunRevenu_1\"]")
|
||||
if sans_button is None:
|
||||
raise RuntimeError
|
||||
sans_button.click(force=True)
|
||||
status_button = page.wait_for_selector(
|
||||
"input[id=\"sitro_1_SITPRO_ACT_INCONNUE\"]")
|
||||
if status_button is None:
|
||||
raise RuntimeError
|
||||
status_button.click(force=True)
|
||||
|
||||
# Continue
|
||||
continuer_button = page.wait_for_selector('button[id="btn-suivant"]')
|
||||
if continuer_button is None:
|
||||
raise RuntimeError
|
||||
continuer_button.click(force=True)
|
||||
|
||||
# Extra questions about children
|
||||
if len(input.enfants) > 0:
|
||||
for i in range(len(input.enfants)):
|
||||
if input.enfants[i].age < 21:
|
||||
yes_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_AGE_MAX_{}_true\"]".format(i))
|
||||
if yes_button is None:
|
||||
raise RuntimeError
|
||||
yes_button.click(force=True)
|
||||
else:
|
||||
no_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_AGE_MAX_{}_false\"]".format(i))
|
||||
if no_button is None:
|
||||
raise RuntimeError
|
||||
no_button.click(force=True)
|
||||
if int(float(input.enfants[i].remuneration_derniere_annee) / 0.9) > 4500:
|
||||
yes_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_REVENUS_DERNIERSMOIS_{}_true\"]".format(i))
|
||||
if yes_button is None:
|
||||
raise RuntimeError
|
||||
yes_button.click(force=True)
|
||||
# We have to provide the exact remuneration
|
||||
salaires_button = page.wait_for_selector(
|
||||
"button[id=\"salaire{}_checkbox\"]".format(i))
|
||||
if salaires_button is None:
|
||||
raise RuntimeError
|
||||
salaires_button.click(force=True)
|
||||
salaires_input = page.wait_for_selector(
|
||||
"input[id=\"MONTANT_salaire{}\"]".format(i))
|
||||
if salaires_input is None:
|
||||
raise RuntimeError
|
||||
salaires_input.fill("{}".format(
|
||||
int(float(input.enfants[i].remuneration_derniere_annee) / 0.9)))
|
||||
salaires_status = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_SITPRO_{}_SALARIE_CP\"]".format(i))
|
||||
if salaires_status is None:
|
||||
raise RuntimeError
|
||||
salaires_status.click(force=True)
|
||||
else:
|
||||
no_button = page.wait_for_selector(
|
||||
"input[id=\"QUESTION_REVENUS_DERNIERSMOIS_{}_false\"]".format(i))
|
||||
if no_button is None:
|
||||
raise RuntimeError
|
||||
no_button.click(force=True)
|
||||
|
||||
# Continue
|
||||
continuer_button = page.wait_for_selector(
|
||||
'button[id="btn-suivant"]')
|
||||
if continuer_button is None:
|
||||
raise RuntimeError
|
||||
continuer_button.click(force=True)
|
||||
|
||||
# Wait for result page
|
||||
page.wait_for_selector("section[id=\"resultat\"]")
|
||||
|
||||
# Retrieve the amount
|
||||
result = page.query_selector('text=/\\d+ € par mois/').text_content()
|
||||
if result is None:
|
||||
# Then no benefits!
|
||||
housing_benefits = 0
|
||||
else:
|
||||
match = re.search("(\\d+) € par mois", result)
|
||||
if match is None:
|
||||
raise RuntimeError
|
||||
housing_benefits = match.group(1)
|
||||
if housing_benefits is None:
|
||||
raise RuntimeError
|
||||
|
||||
browser.close()
|
||||
return int(housing_benefits)
|
@ -0,0 +1,67 @@
|
||||
import random
|
||||
from input import AppartementOuMaison, AppartementOuMaisonType, CnafSimulatorInput, Enfant, Logement, LogementChambre, LogementCrous, LogementCrousType, LogementFoyer, LogementMaisonRetraite, LogementResidenceSocialeFJT, SeulOuCouple, Zone
|
||||
|
||||
|
||||
def generate_random_child() -> Enfant:
|
||||
age = random.randint(0, 25)
|
||||
# For now we don't put income for children for simplicity
|
||||
remuneration_derniere_annee = random.randint(0, 0)
|
||||
return Enfant(age, remuneration_derniere_annee)
|
||||
|
||||
|
||||
def generate_random_input() -> CnafSimulatorInput:
|
||||
zone_i = random.randint(1, 3)
|
||||
if zone_i == 1:
|
||||
zone = Zone.Zone1
|
||||
elif zone_i == 2:
|
||||
zone = Zone.Zone2
|
||||
else: # zone_i == 3
|
||||
zone = Zone.Zone3
|
||||
loyer = random.randint(300, 1800)
|
||||
revenus_pris_en_compte = random.randint(0, 200) * 100
|
||||
seul_ou_couple_i = random.randint(1, 2)
|
||||
if seul_ou_couple_i == 1:
|
||||
seul_ou_couple = SeulOuCouple.Seul
|
||||
else: # seul_ou_couple_i == 2
|
||||
seul_ou_couple = SeulOuCouple.EnCouple
|
||||
nb_enfants = random.randint(0, 8)
|
||||
enfants = [generate_random_child() for i in range(nb_enfants)]
|
||||
typ_logement = random.randint(1, 6)
|
||||
meuble_i = random.randint(1, 2)
|
||||
logement: Logement
|
||||
if meuble_i == 1:
|
||||
meuble = True
|
||||
else: # meuble_i == 2
|
||||
meuble = False
|
||||
if typ_logement == 1:
|
||||
typ_location_i = random.randint(1, 2)
|
||||
if typ_location_i == 1:
|
||||
typ_location = AppartementOuMaisonType.Location
|
||||
else: # typ_location_i == 2
|
||||
typ_location = AppartementOuMaisonType.Colocation
|
||||
logement = AppartementOuMaison(typ_location, meuble)
|
||||
elif typ_logement == 2:
|
||||
typ_i = random.randint(1, 3)
|
||||
if typ_i == 1:
|
||||
typ = LogementCrousType.Chambre
|
||||
elif typ_i == 2:
|
||||
typ = LogementCrousType.Chambre_rehabilitee
|
||||
else: # typ_i == 3
|
||||
typ = LogementCrousType.Studio
|
||||
logement = LogementCrous(typ)
|
||||
elif typ_logement == 3:
|
||||
logement = LogementFoyer()
|
||||
elif typ_logement == 4:
|
||||
logement = LogementResidenceSocialeFJT()
|
||||
elif typ_logement == 5:
|
||||
logement = LogementMaisonRetraite()
|
||||
else: # typ_logement == 6:
|
||||
logement = LogementChambre(meuble)
|
||||
return CnafSimulatorInput(
|
||||
zone=zone,
|
||||
logement=logement,
|
||||
loyer=loyer,
|
||||
seul_ou_couple=seul_ou_couple,
|
||||
enfants=enfants,
|
||||
revenu_pris_en_compte=revenus_pris_en_compte
|
||||
)
|
@ -1,8 +1,9 @@
|
||||
#!python3
|
||||
|
||||
from datetime import date
|
||||
from src.allocations_familiales import PriseEnCharge_Code, Collectivite_Code
|
||||
from src.api import allocations_familiales, Enfant
|
||||
from src.aides_logement import ModeOccupation_Code, Nationalite_Code, PrestationRecue_Code, SituationFamiliale_Code, SituationGardeAlternee_Code, SituationObligationScolaire_Code, TypeBailleur_Code, ZoneDHabitation_Code
|
||||
from src.allocations_familiales import PriseEnCharge_Code, Collectivite_Code, SituationObligationScolaire
|
||||
from src.api import EnfantAPL, InfosLocation, aides_logement, allocations_familiales, Enfant
|
||||
from catala.runtime import LogEvent, LogEventCode, reset_log, retrieve_log
|
||||
import timeit
|
||||
import argparse
|
||||
@ -33,11 +34,77 @@ def call_allocations_familiales() -> float:
|
||||
)
|
||||
|
||||
|
||||
def benchmark_iteration():
|
||||
def call_aides_logement() -> float:
|
||||
return aides_logement(
|
||||
date_courante=date(2022, 5, 1),
|
||||
ressources_menage_prises_en_compte=11_500,
|
||||
date_naissance_demandeur=date(1992, 1, 1),
|
||||
nationalite_demandeur=Nationalite_Code.Francaise,
|
||||
patrimoine_produisant_revenu=0,
|
||||
patrimoine_ne_produisant_pas_revenu=0,
|
||||
personne_hebergee_centre_soins=False,
|
||||
personne_rattache_foyer_fiscal_parent_ifi=False,
|
||||
nombre_autres_occupants_logement_hors_menage=0,
|
||||
enfant_a_naitre_apres_quatrieme_mois_grossesse=False,
|
||||
situation_familiale=SituationFamiliale_Code.Concubins,
|
||||
date_mariage=None,
|
||||
prestations_recues=[],
|
||||
residence_principale=True,
|
||||
logement_est_maison_de_retraite=False,
|
||||
surface_logement_m_carres=65,
|
||||
zone=ZoneDHabitation_Code.Zone2,
|
||||
parts_logement_propriete_famille=None,
|
||||
parts_logement_usufruits_famille=None,
|
||||
date_naissance_et_conformite_sous_locataire_tiers=None,
|
||||
mode_occupation=ModeOccupation_Code.Locataire,
|
||||
personnes_a_charge=[
|
||||
EnfantAPL(
|
||||
identifiant=1,
|
||||
beneficie_titre_personnel_aide_personnelle_logement=False,
|
||||
a_deja_ouvert_droit_aux_allocations_familiales=True,
|
||||
date_de_naissance=date(2015, 1, 1),
|
||||
remuneration_mensuelle=0,
|
||||
obligation_scolaire=SituationObligationScolaire_Code.Pendant,
|
||||
situation_garde_alternee=SituationGardeAlternee_Code.PasDeGardeAlternee,
|
||||
coefficient_garde_alternee=None
|
||||
),
|
||||
EnfantAPL(
|
||||
identifiant=2,
|
||||
beneficie_titre_personnel_aide_personnelle_logement=False,
|
||||
a_deja_ouvert_droit_aux_allocations_familiales=True,
|
||||
date_de_naissance=date(2016, 1, 1),
|
||||
remuneration_mensuelle=0,
|
||||
obligation_scolaire=SituationObligationScolaire_Code.Pendant,
|
||||
situation_garde_alternee=SituationGardeAlternee_Code.PasDeGardeAlternee,
|
||||
coefficient_garde_alternee=None)
|
||||
],
|
||||
logement_est_decent=True,
|
||||
infos_specifiques=InfosLocation(
|
||||
loyer_principal=450,
|
||||
beneficiaire_aide_adulte_ou_enfant_handicapes=False,
|
||||
colocation=False,
|
||||
logement_est_chambre=False,
|
||||
agees_ou_handicap_adultes_hebergees_onereux_particuliers=False,
|
||||
logement_meuble_d842_2=False,
|
||||
ancien_loyer_et_apl_relogement=None,
|
||||
type_bailleur=TypeBailleur_Code.BailleurPrive,
|
||||
bailleur_conventionne=None,
|
||||
reduction_loyer_solidarite=None
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
|
||||
def benchmark_iteration_family():
|
||||
money_given = call_allocations_familiales()
|
||||
assert (money_given == 99.46)
|
||||
|
||||
|
||||
def benchmark_iteration_housing():
|
||||
money_given = call_aides_logement()
|
||||
assert (money_given == 352.77)
|
||||
|
||||
|
||||
def run_with_log() -> List[LogEvent]:
|
||||
money_given = call_allocations_familiales()
|
||||
assert (money_given == 99.46)
|
||||
@ -61,11 +128,16 @@ if __name__ == '__main__':
|
||||
|
||||
args = parser.parse_args()
|
||||
action = args.action[0]
|
||||
if action == "bench":
|
||||
if action == "bench_family":
|
||||
iterations = 1000
|
||||
print("Iterating {} iterations of the family benefits computation. Total time (s):".format(
|
||||
iterations))
|
||||
print(timeit.timeit(benchmark_iteration, number=iterations))
|
||||
print(timeit.timeit(benchmark_iteration_family, number=iterations))
|
||||
elif action == "bench_housing":
|
||||
iterations = 1000
|
||||
print("Iterating {} iterations of the family benefits computation. Total time (s):".format(
|
||||
iterations))
|
||||
print(timeit.timeit(benchmark_iteration_housing, number=iterations))
|
||||
elif action == "show_log":
|
||||
log = run_with_log()
|
||||
indentation = 0
|
||||
|
2749
french_law/python/src/aides_logement.py
generated
2749
french_law/python/src/aides_logement.py
generated
File diff suppressed because it is too large
Load Diff
138
french_law/python/src/allocations_familiales.py
generated
138
french_law/python/src/allocations_familiales.py
generated
@ -780,21 +780,23 @@ def prestations_familiales(prestations_familiales_in:PrestationsFamilialesIn):
|
||||
temp_smic_dot_date_courante = date_courante_2
|
||||
except EmptyError:
|
||||
temp_smic_dot_date_courante = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/../smic/smic.catala_fr",
|
||||
start_line=9, start_column=10,
|
||||
end_line=9, end_column=23,
|
||||
law_headings=["Prologue",
|
||||
"Montant du salaire minimum de croissance"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=69, start_column=14,
|
||||
end_line=69, end_column=32,
|
||||
law_headings=["Prestations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
smic_dot_date_courante = temp_smic_dot_date_courante
|
||||
try:
|
||||
temp_smic_dot_residence = residence_1
|
||||
except EmptyError:
|
||||
temp_smic_dot_residence = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/../smic/smic.catala_fr",
|
||||
start_line=10, start_column=10,
|
||||
end_line=10, end_column=19,
|
||||
law_headings=["Prologue",
|
||||
"Montant du salaire minimum de croissance"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=68, start_column=14,
|
||||
end_line=68, end_column=28,
|
||||
law_headings=["Prestations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
smic_dot_residence = temp_smic_dot_residence
|
||||
result = smic(SmicIn(date_courante_in = smic_dot_date_courante,
|
||||
residence_in = smic_dot_residence))
|
||||
@ -1316,10 +1318,12 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
|
||||
temp_bmaf_dot_date_courante = date_courante_3
|
||||
except EmptyError:
|
||||
temp_bmaf_dot_date_courante = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/../base_mensuelle_allocations_familiales/bmaf.catala_fr",
|
||||
start_line=5, start_column=10,
|
||||
end_line=5, end_column=23,
|
||||
law_headings=["Montant de la base mensuelle des allocations familiales"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=159, start_column=14,
|
||||
end_line=159, end_column=32,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
bmaf_dot_date_courante = temp_bmaf_dot_date_courante
|
||||
result_2 = base_mensuelle_allocations_familiales(BaseMensuelleAllocationsFamilialesIn(date_courante_in = bmaf_dot_date_courante))
|
||||
bmaf_dot_montant = result_2.montant_out
|
||||
@ -1328,9 +1332,9 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
|
||||
except EmptyError:
|
||||
temp_prestations_familiales_dot_date_courante = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=62, start_column=10,
|
||||
end_line=62, end_column=23,
|
||||
law_headings=["Prestations familiales",
|
||||
start_line=155, start_column=14,
|
||||
end_line=155, end_column=50,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
prestations_familiales_dot_date_courante = temp_prestations_familiales_dot_date_courante
|
||||
@ -1340,9 +1344,9 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
|
||||
except EmptyError:
|
||||
temp_prestations_familiales_dot_prestation_courante = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=63, start_column=10,
|
||||
end_line=63, end_column=29,
|
||||
law_headings=["Prestations familiales",
|
||||
start_line=153, start_column=14,
|
||||
end_line=153, end_column=56,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
prestations_familiales_dot_prestation_courante = temp_prestations_familiales_dot_prestation_courante
|
||||
@ -1351,9 +1355,9 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
|
||||
except EmptyError:
|
||||
temp_prestations_familiales_dot_residence = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=64, start_column=10,
|
||||
end_line=64, end_column=19,
|
||||
law_headings=["Prestations familiales",
|
||||
start_line=157, start_column=14,
|
||||
end_line=157, end_column=46,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
prestations_familiales_dot_residence = temp_prestations_familiales_dot_residence
|
||||
@ -1368,12 +1372,11 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
|
||||
temp_enfant_le_plus_age_dot_enfants = enfants_a_charge
|
||||
except EmptyError:
|
||||
temp_enfant_le_plus_age_dot_enfants = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=79, start_column=10,
|
||||
end_line=79, end_column=17,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=32, start_column=14,
|
||||
end_line=32, end_column=40,
|
||||
law_headings=["Règles diverses",
|
||||
"Épilogue"]))
|
||||
enfant_le_plus_age_dot_enfants = temp_enfant_le_plus_age_dot_enfants
|
||||
result_4 = enfant_le_plus_age(EnfantLePlusAgeIn(enfants_in = enfant_le_plus_age_dot_enfants))
|
||||
enfant_le_plus_age_dot_le_plus_age = result_4.le_plus_age_out
|
||||
@ -3009,12 +3012,11 @@ def interface_allocations_familiales(interface_allocations_familiales_in:Interfa
|
||||
temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1 = temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent
|
||||
except EmptyError:
|
||||
temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1 = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=85, start_column=10,
|
||||
end_line=85, end_column=57,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=91, start_column=5,
|
||||
end_line=91, end_column=75,
|
||||
law_headings=["Interface du programme",
|
||||
"Épilogue"]))
|
||||
allocations_familiales_dot_personne_charge_effective_permanente_est_parent = temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1
|
||||
try:
|
||||
try:
|
||||
@ -3028,56 +3030,51 @@ def interface_allocations_familiales(interface_allocations_familiales_in:Interfa
|
||||
temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i_1 = temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i
|
||||
except EmptyError:
|
||||
temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i_1 = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=86, start_column=10,
|
||||
end_line=86, end_column=62,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=95, start_column=5,
|
||||
end_line=95, end_column=80,
|
||||
law_headings=["Interface du programme",
|
||||
"Épilogue"]))
|
||||
allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i = temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i_1
|
||||
try:
|
||||
temp_allocations_familiales_dot_ressources_menage = i_ressources_menage
|
||||
except EmptyError:
|
||||
temp_allocations_familiales_dot_ressources_menage = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=87, start_column=10,
|
||||
end_line=87, end_column=27,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=87, start_column=14,
|
||||
end_line=87, end_column=54,
|
||||
law_headings=["Interface du programme",
|
||||
"Épilogue"]))
|
||||
allocations_familiales_dot_ressources_menage = temp_allocations_familiales_dot_ressources_menage
|
||||
try:
|
||||
temp_allocations_familiales_dot_residence = i_residence
|
||||
except EmptyError:
|
||||
temp_allocations_familiales_dot_residence = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=88, start_column=10,
|
||||
end_line=88, end_column=19,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=88, start_column=14,
|
||||
end_line=88, end_column=46,
|
||||
law_headings=["Interface du programme",
|
||||
"Épilogue"]))
|
||||
allocations_familiales_dot_residence = temp_allocations_familiales_dot_residence
|
||||
try:
|
||||
temp_allocations_familiales_dot_date_courante = i_date_courante
|
||||
except EmptyError:
|
||||
temp_allocations_familiales_dot_date_courante = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=91, start_column=10,
|
||||
end_line=91, end_column=23,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=85, start_column=14,
|
||||
end_line=85, end_column=50,
|
||||
law_headings=["Interface du programme",
|
||||
"Épilogue"]))
|
||||
allocations_familiales_dot_date_courante = temp_allocations_familiales_dot_date_courante
|
||||
try:
|
||||
temp_allocations_familiales_dot_enfants_a_charge = enfants_a_charge_1
|
||||
except EmptyError:
|
||||
temp_allocations_familiales_dot_enfants_a_charge = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=94, start_column=10,
|
||||
end_line=94, end_column=26,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=86, start_column=14,
|
||||
end_line=86, end_column=53,
|
||||
law_headings=["Interface du programme",
|
||||
"Épilogue"]))
|
||||
allocations_familiales_dot_enfants_a_charge = temp_allocations_familiales_dot_enfants_a_charge
|
||||
try:
|
||||
try:
|
||||
@ -3091,12 +3088,11 @@ def interface_allocations_familiales(interface_allocations_familiales_in:Interfa
|
||||
temp_allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012_1 = temp_allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012
|
||||
except EmptyError:
|
||||
temp_allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012_1 = dead_value
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
|
||||
start_line=115, start_column=10,
|
||||
end_line=115, end_column=54,
|
||||
law_headings=["Allocations familiales",
|
||||
"Champs d'applications",
|
||||
"Prologue"]))
|
||||
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
|
||||
start_line=99, start_column=5,
|
||||
end_line=99, end_column=72,
|
||||
law_headings=["Interface du programme",
|
||||
"Épilogue"]))
|
||||
allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012 = temp_allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012_1
|
||||
result_5 = allocations_familiales(AllocationsFamilialesIn(personne_charge_effective_permanente_est_parent_in = allocations_familiales_dot_personne_charge_effective_permanente_est_parent,
|
||||
personne_charge_effective_permanente_remplit_titre_I_in = allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i,
|
||||
|
@ -1,5 +1,9 @@
|
||||
from abc import ABC
|
||||
from catala.runtime import *
|
||||
from .allocations_familiales import Collectivite, Collectivite_Code, InterfaceAllocationsFamilialesIn, PriseEnCharge, interface_allocations_familiales, PriseEnCharge_Code, EnfantEntree, InterfaceAllocationsFamilialesIn
|
||||
from .aides_logement import AutrePersonneACharge, CategorieEquivalenceLoyerAllocationLogementFoyer, CategorieEquivalenceLoyerAllocationLogementFoyer_Code, ChangementLogementD8424, ChangementLogementD8424_Code, ConventionANHA, ConventionBailleurSocial, EnfantACharge, InfosChangementLogementD8424, Location, Logement, LogementFoyer, LoueOuSousLoueADesTiers, LoueOuSousLoueADesTiers_Code, Menage, ModeOccupation, ModeOccupation_Code, Nationalite, Nationalite_Code, NeufOuAncien, NeufOuAncien_Code, ParentOuAutre, ParentOuAutre_Code, Parente, Parente_Code, Patrimoine, PersonneACharge, PersonneSousLocation, PrestationRecue, PrestationRecue_Code, Pret, Proprietaire, SituationFamiliale, SituationFamiliale_Code, SituationGardeAlternee_Code, SituationObligationScolaire_Code, TitulairePret, TitulairePret_Code, TypeBailleur, TypeBailleur_Code, TypeLogementFoyer, TypeLogementFoyer_Code, TypePret, TypePret_Code, TypeTravauxLogementD83215, TypeTravauxLogementD83215_Code, TypeTravauxLogementR8425, TypeTravauxLogementR8425_Code, ZoneDHabitation, ZoneDHabitation_Code, calculette_aides_au_logement_garde_alternee, CalculetteAidesAuLogementGardeAlterneeIn, ressources_aides_personnelle_logement, Demandeur, PersonneACharge_Code, SituationObligationScolaire, SituationGardeAlternee
|
||||
|
||||
# Allocations familiales
|
||||
|
||||
|
||||
class Enfant:
|
||||
@ -51,3 +55,310 @@ def allocations_familiales(
|
||||
i_avait_enfant_a_charge_avant_1er_janvier_2012_in=avait_enfant_a_charge_avant_1er_janvier_2012
|
||||
))
|
||||
return money_to_float(out.i_montant_verse_out)
|
||||
|
||||
# Aides au logement
|
||||
|
||||
|
||||
class PersonneAChargeAPL(ABC):
|
||||
pass
|
||||
|
||||
|
||||
class EnfantAPL(PersonneAChargeAPL):
|
||||
def __init__(self, identifiant: int, beneficie_titre_personnel_aide_personnelle_logement: bool,
|
||||
a_deja_ouvert_droit_aux_allocations_familiales: bool,
|
||||
date_de_naissance: datetime.date,
|
||||
remuneration_mensuelle: int,
|
||||
obligation_scolaire: SituationObligationScolaire_Code,
|
||||
situation_garde_alternee: SituationGardeAlternee_Code,
|
||||
coefficient_garde_alternee: Optional[int]):
|
||||
self.identifiant = identifiant
|
||||
self.beneficie_titre_personnel_aide_personnelle_logement = beneficie_titre_personnel_aide_personnelle_logement
|
||||
self.a_deja_ouvert_droit_aux_allocations_familiales = a_deja_ouvert_droit_aux_allocations_familiales
|
||||
self.date_de_naissance = date_de_naissance,
|
||||
self.remuneration_mensuelle = remuneration_mensuelle
|
||||
self.obligation_scolaire = obligation_scolaire
|
||||
self.situation_garde_alternee = situation_garde_alternee
|
||||
self.coefficient_garde_alternee = coefficient_garde_alternee
|
||||
|
||||
|
||||
class ParentAPL(PersonneAChargeAPL):
|
||||
def __init__(self, date_naissance: datetime.date,
|
||||
ressources: int,
|
||||
ascendant_descendant_collateral_deuxieme_troisieme_degre: bool,
|
||||
parente: Parente_Code,
|
||||
incapacite_80_pourcent_ou_restriction_emploi: bool,
|
||||
beneficiaire_l161_19_l351_8_l643_3_secu: bool,
|
||||
titulaire_allocation_personne_agee: bool):
|
||||
self.date_naissance = date_naissance
|
||||
self.ressources = ressources
|
||||
self.ascendant_descendant_collateral_deuxieme_troisieme_degre = ascendant_descendant_collateral_deuxieme_troisieme_degre
|
||||
self.parente = parente
|
||||
self.incapacite_80_pourcent_ou_restriction_emploi = incapacite_80_pourcent_ou_restriction_emploi
|
||||
self.beneficiaire_l161_19_l351_8_l643_3_secu = beneficiaire_l161_19_l351_8_l643_3_secu
|
||||
self.titulaire_allocation_personne_agee = titulaire_allocation_personne_agee
|
||||
|
||||
|
||||
class InfosSpecifiques(ABC):
|
||||
pass
|
||||
|
||||
|
||||
class InfosLocation(InfosSpecifiques):
|
||||
def __init__(self,
|
||||
loyer_principal: int,
|
||||
beneficiaire_aide_adulte_ou_enfant_handicapes: bool,
|
||||
logement_est_chambre: bool,
|
||||
colocation: bool,
|
||||
agees_ou_handicap_adultes_hebergees_onereux_particuliers: bool,
|
||||
logement_meuble_d842_2: bool,
|
||||
ancien_loyer_et_apl_relogement: Optional[Tuple[int, int]],
|
||||
type_bailleur: TypeBailleur_Code,
|
||||
bailleur_conventionne: Optional[bool],
|
||||
reduction_loyer_solidarite: Optional[int]):
|
||||
self.loyer_principal = loyer_principal
|
||||
self.beneficiaire_aide_adulte_ou_enfant_handicapes = beneficiaire_aide_adulte_ou_enfant_handicapes
|
||||
self.logement_est_chambre = logement_est_chambre
|
||||
self.colocation = colocation
|
||||
self.agees_ou_handicap_adultes_hebergees_onereux_particuliers = agees_ou_handicap_adultes_hebergees_onereux_particuliers
|
||||
self.logement_meuble_d842_2 = logement_meuble_d842_2
|
||||
self.ancien_loyer_et_apl_relogement = ancien_loyer_et_apl_relogement
|
||||
self.type_bailleur = type_bailleur
|
||||
self.bailleur_conventionne = bailleur_conventionne
|
||||
self.reduction_loyer_solidarite = reduction_loyer_solidarite
|
||||
|
||||
|
||||
class InfosLogementFoyer(InfosSpecifiques):
|
||||
def __init__(self,
|
||||
type: TypeLogementFoyer_Code,
|
||||
remplit_conditions_r832_21: bool,
|
||||
conventionne_livre_III_titre_V_chap_III: bool,
|
||||
date_conventionnement: datetime.date,
|
||||
construit_application_loi_1957_12_III: bool,
|
||||
redevance: int,
|
||||
categorie_equivalence_loyer_d842_16: CategorieEquivalenceLoyerAllocationLogementFoyer_Code):
|
||||
self.type = type
|
||||
self.remplit_conditions_r832_21 = remplit_conditions_r832_21
|
||||
self.conventionne_livre_III_titre_V_chap_III = conventionne_livre_III_titre_V_chap_III
|
||||
self.date_conventionnement = date_conventionnement
|
||||
self.construit_application_loi_1957_12_III = construit_application_loi_1957_12_III
|
||||
self.redevance = redevance
|
||||
self.categorie_equivalence_loyer_d842_16 = categorie_equivalence_loyer_d842_16
|
||||
|
||||
|
||||
class InfosAccessionPropriete(InfosSpecifiques):
|
||||
def __init__(self,
|
||||
logement_situe_commune_desequilibre_l831_2: bool,
|
||||
mensualite_principale: int,
|
||||
charges_mensuelles_pret: int,
|
||||
date_entree_logement: datetime.date,
|
||||
local_habite_premiere_fois_beneficiaire: bool,
|
||||
copropriete: bool,
|
||||
situation_r822_11_13_17: bool,
|
||||
type_travaux_logement_d832_15: TypeTravauxLogementD83215_Code,
|
||||
type_travaux_logement_r842_5: TypeTravauxLogementR8425_Code,
|
||||
anciennete_logement: NeufOuAncien_Code,
|
||||
ameliore_par_occupant: Optional[bool],
|
||||
type_pret: TypePret_Code,
|
||||
date_signature_pret: datetime.date,
|
||||
titulaire_pret: TitulairePret_Code):
|
||||
self.logement_situe_commune_desequilibre_l831_2 = logement_situe_commune_desequilibre_l831_2
|
||||
self.mensualite_principale = mensualite_principale
|
||||
self.charges_mensuelles_pret = charges_mensuelles_pret
|
||||
self.date_entree_logement = date_entree_logement
|
||||
self.local_habite_premiere_fois_beneficiaire = local_habite_premiere_fois_beneficiaire
|
||||
self.copropriete = copropriete
|
||||
self.situation_r822_11_13_17 = situation_r822_11_13_17
|
||||
self.type_travaux_logement_d832_15 = type_travaux_logement_d832_15
|
||||
self.type_travaux_logement_r842_5 = type_travaux_logement_r842_5
|
||||
self.anciennete_logement = anciennete_logement
|
||||
self.ameliore_par_occupant = ameliore_par_occupant
|
||||
self.type_pret = type_pret
|
||||
self.date_signature_pret = date_signature_pret
|
||||
self.titulaire_pret = titulaire_pret
|
||||
|
||||
|
||||
def aides_logement(
|
||||
date_courante: datetime.date,
|
||||
ressources_menage_prises_en_compte: int,
|
||||
date_naissance_demandeur: datetime.date,
|
||||
nationalite_demandeur: Nationalite_Code,
|
||||
patrimoine_produisant_revenu: int,
|
||||
patrimoine_ne_produisant_pas_revenu: int,
|
||||
personne_hebergee_centre_soins: bool,
|
||||
personne_rattache_foyer_fiscal_parent_ifi: bool,
|
||||
nombre_autres_occupants_logement_hors_menage: int,
|
||||
enfant_a_naitre_apres_quatrieme_mois_grossesse: bool,
|
||||
situation_familiale: SituationFamiliale_Code,
|
||||
date_mariage: Optional[datetime.date],
|
||||
prestations_recues: List[PrestationRecue_Code],
|
||||
residence_principale: bool,
|
||||
logement_est_maison_de_retraite: bool,
|
||||
logement_est_decent: bool,
|
||||
surface_logement_m_carres: int,
|
||||
zone: ZoneDHabitation_Code,
|
||||
parts_logement_propriete_famille: Optional[int],
|
||||
parts_logement_usufruits_famille: Optional[int],
|
||||
date_naissance_et_conformite_sous_locataire_tiers: Optional[Tuple[datetime.date, bool]],
|
||||
mode_occupation: ModeOccupation_Code,
|
||||
infos_specifiques: InfosSpecifiques,
|
||||
personnes_a_charge: List[PersonneAChargeAPL],
|
||||
):
|
||||
out = calculette_aides_au_logement_garde_alternee(CalculetteAidesAuLogementGardeAlterneeIn(
|
||||
menage_in=Menage(
|
||||
prestations_recues=[PrestationRecue(
|
||||
code=presta, value=Unit()) for presta in prestations_recues],
|
||||
logement=Logement(
|
||||
residence_principale=residence_principale,
|
||||
est_ehpad_ou_maison_autonomie_l313_12_asf=logement_est_maison_de_retraite,
|
||||
mode_occupation=ModeOccupation(
|
||||
code=mode_occupation,
|
||||
value=(Location(
|
||||
loyer_principal=money_of_units_int(
|
||||
infos_specifiques.loyer_principal),
|
||||
beneficiaire_aide_adulte_ou_enfant_handicapes=infos_specifiques.beneficiaire_aide_adulte_ou_enfant_handicapes,
|
||||
logement_est_chambre=infos_specifiques.logement_est_chambre,
|
||||
colocation=infos_specifiques.colocation,
|
||||
agees_ou_handicap_adultes_hebergees_onereux_particuliers=infos_specifiques.agees_ou_handicap_adultes_hebergees_onereux_particuliers,
|
||||
logement_meuble_d842_2=infos_specifiques.logement_meuble_d842_2,
|
||||
changement_logement_d842_4=ChangementLogementD8424(
|
||||
code=ChangementLogementD8424_Code.PasDeChangement if infos_specifiques.ancien_loyer_et_apl_relogement is None else
|
||||
ChangementLogementD8424_Code.Changement,
|
||||
value=Unit() if infos_specifiques.ancien_loyer_et_apl_relogement is None else
|
||||
InfosChangementLogementD8424(ancien_loyer_principal=money_of_units_int(infos_specifiques.ancien_loyer_et_apl_relogement[0]),
|
||||
ancienne_allocation_logement=money_of_units_int(infos_specifiques.ancien_loyer_et_apl_relogement[1]))
|
||||
),
|
||||
bailleur=TypeBailleur(
|
||||
code=infos_specifiques.type_bailleur,
|
||||
value=Unit() if infos_specifiques.type_bailleur == TypeBailleur_Code.BailleurPrive else (
|
||||
ConventionBailleurSocial(
|
||||
conventionne_livre_III_titre_V_chap_III=False if infos_specifiques.bailleur_conventionne is None else infos_specifiques.bailleur_conventionne,
|
||||
reduction_loyer_solidarite_percue=money_of_units_int(0 if infos_specifiques.reduction_loyer_solidarite is None else infos_specifiques.reduction_loyer_solidarite))
|
||||
) if infos_specifiques.type_bailleur == TypeBailleur_Code.BailleurSocial else (
|
||||
ConventionANHA(
|
||||
conventionne_livre_III_titre_II_chap_I_sec_3=False if infos_specifiques.bailleur_conventionne is None else infos_specifiques.bailleur_conventionne)
|
||||
if infos_specifiques.type_bailleur == TypeBailleur_Code.BailleurPriveAvecConventionnementSocial else
|
||||
None # type: ignore
|
||||
))
|
||||
) if isinstance(infos_specifiques, InfosLocation) else
|
||||
(LogementFoyer(
|
||||
type=TypeLogementFoyer(
|
||||
code=infos_specifiques.type, value=Unit()),
|
||||
remplit_conditions_r832_21=infos_specifiques.remplit_conditions_r832_21,
|
||||
conventionne_livre_III_titre_V_chap_III=infos_specifiques.conventionne_livre_III_titre_V_chap_III,
|
||||
date_conventionnement=date_of_datetime(
|
||||
infos_specifiques.date_conventionnement),
|
||||
construit_application_loi_1957_12_III=infos_specifiques.construit_application_loi_1957_12_III,
|
||||
redevance=money_of_units_int(
|
||||
infos_specifiques.redevance),
|
||||
categorie_equivalence_loyer_d842_16=CategorieEquivalenceLoyerAllocationLogementFoyer(
|
||||
code=infos_specifiques.categorie_equivalence_loyer_d842_16,
|
||||
value=Unit()
|
||||
)
|
||||
) if isinstance(infos_specifiques, InfosLogementFoyer) else
|
||||
(Proprietaire(
|
||||
logement_situe_commune_desequilibre_l831_2=infos_specifiques.logement_situe_commune_desequilibre_l831_2,
|
||||
mensualite_principale=money_of_units_int(
|
||||
infos_specifiques.mensualite_principale),
|
||||
charges_mensuelles_pret=money_of_units_int(
|
||||
infos_specifiques.charges_mensuelles_pret),
|
||||
date_entree_logement=date_of_datetime(
|
||||
infos_specifiques.date_entree_logement),
|
||||
local_habite_premiere_fois_beneficiaire=infos_specifiques.local_habite_premiere_fois_beneficiaire,
|
||||
copropriete=infos_specifiques.copropriete,
|
||||
situation_r822_11_13_17=infos_specifiques.situation_r822_11_13_17,
|
||||
type_travaux_logement_d832_15=TypeTravauxLogementD83215(
|
||||
code=infos_specifiques.type_travaux_logement_d832_15, value=Unit()),
|
||||
type_travaux_logement_r842_5=TypeTravauxLogementR8425(
|
||||
code=infos_specifiques.type_travaux_logement_r842_5,
|
||||
value=Unit()
|
||||
),
|
||||
anciennete_logement=NeufOuAncien(code=infos_specifiques.anciennete_logement,
|
||||
value=Unit() if infos_specifiques.ameliore_par_occupant is None else infos_specifiques.ameliore_par_occupant),
|
||||
pret=Pret(
|
||||
type_pret=TypePret(
|
||||
code=infos_specifiques.type_pret, value=Unit()),
|
||||
date_signature=date_of_datetime(
|
||||
infos_specifiques.date_signature_pret),
|
||||
titulaire_pret=TitulairePret(
|
||||
code=infos_specifiques.titulaire_pret, value=Unit())
|
||||
)
|
||||
) if isinstance(infos_specifiques, InfosAccessionPropriete)
|
||||
else None # type: ignore
|
||||
)))
|
||||
),
|
||||
proprietaire=ParentOuAutre(
|
||||
code=ParentOuAutre_Code.Autre if parts_logement_propriete_famille is None else ParentOuAutre_Code.DemandeurOuConjointOuParentOuViaPartsSocietes,
|
||||
value=Unit() if parts_logement_propriete_famille is None else parts_logement_propriete_famille),
|
||||
usufruit=ParentOuAutre(
|
||||
code=ParentOuAutre_Code.Autre if parts_logement_usufruits_famille is None else ParentOuAutre_Code.DemandeurOuConjointOuParentOuViaPartsSocietes,
|
||||
value=Unit() if parts_logement_usufruits_famille is None else parts_logement_usufruits_famille),
|
||||
loue_ou_sous_loue_a_des_tiers=LoueOuSousLoueADesTiers(
|
||||
code=LoueOuSousLoueADesTiers_Code.Non if date_naissance_et_conformite_sous_locataire_tiers is None else LoueOuSousLoueADesTiers_Code.Oui,
|
||||
value=Unit() if date_naissance_et_conformite_sous_locataire_tiers is None else PersonneSousLocation(
|
||||
date_naissance_personne_sous_location=date_of_datetime(date_naissance_et_conformite_sous_locataire_tiers[
|
||||
0]),
|
||||
conforme_article_l442_1=date_naissance_et_conformite_sous_locataire_tiers[
|
||||
1]
|
||||
)
|
||||
),
|
||||
logement_decent_l89_462=logement_est_decent,
|
||||
surface_m_carres=integer_of_int(surface_logement_m_carres),
|
||||
zone=ZoneDHabitation(code=zone, value=Unit())
|
||||
),
|
||||
personnes_a_charge=[
|
||||
(PersonneACharge(code=PersonneACharge_Code.EnfantACharge,
|
||||
value=EnfantACharge(
|
||||
identifiant=integer_of_int(
|
||||
personne_a_charge.identifiant),
|
||||
beneficie_titre_personnel_aide_personnelle_logement=personne_a_charge.beneficie_titre_personnel_aide_personnelle_logement,
|
||||
a_deja_ouvert_droit_aux_allocations_familiales=personne_a_charge.a_deja_ouvert_droit_aux_allocations_familiales,
|
||||
date_de_naissance=date_of_datetime(
|
||||
personne_a_charge.date_de_naissance[0]),
|
||||
remuneration_mensuelle=money_of_units_int(
|
||||
personne_a_charge.remuneration_mensuelle),
|
||||
obligation_scolaire=SituationObligationScolaire(
|
||||
code=personne_a_charge.obligation_scolaire, value=Unit()),
|
||||
situation_garde_alternee=SituationGardeAlternee(code=personne_a_charge.situation_garde_alternee,
|
||||
value=Unit() if personne_a_charge.coefficient_garde_alternee is None else personne_a_charge.coefficient_garde_alternee)
|
||||
))
|
||||
if isinstance(personne_a_charge, EnfantAPL)
|
||||
else (PersonneACharge(
|
||||
code=PersonneACharge_Code.AutrePersonneACharge,
|
||||
value=AutrePersonneACharge(
|
||||
date_naissance=date_of_datetime(
|
||||
personne_a_charge.date_naissance),
|
||||
ressources=money_of_units_int(
|
||||
personne_a_charge.ressources),
|
||||
ascendant_descendant_collateral_deuxieme_troisieme_degre=personne_a_charge.ascendant_descendant_collateral_deuxieme_troisieme_degre,
|
||||
incapacite_80_pourcent_ou_restriction_emploi=personne_a_charge.incapacite_80_pourcent_ou_restriction_emploi,
|
||||
beneficiaire_l161_19_l351_8_l643_3_secu=personne_a_charge.beneficiaire_l161_19_l351_8_l643_3_secu,
|
||||
titulaire_allocation_personne_agee=personne_a_charge.titulaire_allocation_personne_agee,
|
||||
parente=Parente(
|
||||
code=personne_a_charge.parente, value=Unit())
|
||||
)) if isinstance(personne_a_charge, ParentAPL)
|
||||
else None # type: ignore
|
||||
)) for personne_a_charge in personnes_a_charge],
|
||||
nombre_autres_occupants_logement=integer_of_int(
|
||||
nombre_autres_occupants_logement_hors_menage),
|
||||
situation_familiale=SituationFamiliale(
|
||||
code=situation_familiale,
|
||||
value=Unit() if date_mariage is None else date_of_datetime(date_mariage)
|
||||
),
|
||||
condition_rattache_foyer_fiscal_parent_ifi=personne_rattache_foyer_fiscal_parent_ifi,
|
||||
enfant_a_naitre_apres_quatrieme_mois_grossesse=enfant_a_naitre_apres_quatrieme_mois_grossesse
|
||||
),
|
||||
demandeur_in=Demandeur(
|
||||
date_naissance=date_of_datetime(date_naissance_demandeur),
|
||||
nationalite=Nationalite(code=nationalite_demandeur, value=Unit()),
|
||||
patrimoine=Patrimoine(
|
||||
produisant_revenu_periode_r822_3_3_r822_4=money_of_units_int(
|
||||
patrimoine_produisant_revenu),
|
||||
ne_produisant_pas_revenu_periode_r822_3_3_r822_4=money_of_units_int(
|
||||
patrimoine_ne_produisant_pas_revenu)
|
||||
),
|
||||
personne_hebergee_centre_soin_l_L162_22_3_securite_sociale=personne_hebergee_centre_soins,
|
||||
),
|
||||
date_courante_in=date_of_datetime(date_courante),
|
||||
ressources_menage_prises_en_compte_in=money_of_units_int(
|
||||
ressources_menage_prises_en_compte),
|
||||
))
|
||||
return money_to_float(out.aide_finale_out)
|
||||
|
@ -1,681 +0,0 @@
|
||||
"""
|
||||
.. module:: catala_runtime
|
||||
:platform: Unix, Windows
|
||||
:synopsis: The Python bindings for the functions used in the generated Catala code
|
||||
:noindex:
|
||||
|
||||
.. moduleauthor:: Denis Merigoux <denis.merigoux@inria.fr>
|
||||
"""
|
||||
|
||||
# This file should be in sync with compiler/runtime.{ml, mli} !
|
||||
|
||||
from this import d
|
||||
from gmpy2 import log2, mpz, mpq, mpfr, t_divmod, f_div, sign # type: ignore
|
||||
import datetime
|
||||
import calendar
|
||||
import dateutil.relativedelta
|
||||
from typing import NewType, List, Callable, Tuple, Optional, TypeVar, Iterable, Union, Any
|
||||
from functools import reduce
|
||||
from enum import Enum
|
||||
import copy
|
||||
|
||||
Alpha = TypeVar('Alpha')
|
||||
Beta = TypeVar('Beta')
|
||||
|
||||
# ============
|
||||
# Type classes
|
||||
# ============
|
||||
|
||||
|
||||
class Integer:
|
||||
def __init__(self, value: Union[str, int]) -> None:
|
||||
self.value = mpz(value)
|
||||
|
||||
def __add__(self, other: 'Integer') -> 'Integer':
|
||||
return Integer(self.value + other.value)
|
||||
|
||||
def __sub__(self, other: 'Integer') -> 'Integer':
|
||||
return Integer(self.value - other.value)
|
||||
|
||||
def __mul__(self, other: 'Integer') -> 'Integer':
|
||||
return Integer(self.value * other.value)
|
||||
|
||||
def __truediv__(self, other: 'Integer') -> 'Integer':
|
||||
return Integer(self.value // other.value)
|
||||
|
||||
def __neg__(self: 'Integer') -> 'Integer':
|
||||
return Integer(- self.value)
|
||||
|
||||
def __lt__(self, other: 'Integer') -> bool:
|
||||
return self.value < other.value
|
||||
|
||||
def __le__(self, other: 'Integer') -> bool:
|
||||
return self.value <= other.value
|
||||
|
||||
def __gt__(self, other: 'Integer') -> bool:
|
||||
return self.value > other.value
|
||||
|
||||
def __ge__(self, other: 'Integer') -> bool:
|
||||
return self.value >= other.value
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
if isinstance(other, Integer):
|
||||
return self.value != other.value
|
||||
else:
|
||||
return True
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Integer):
|
||||
return self.value == other.value
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value.__str__()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Integer({self.value.__repr__()})"
|
||||
|
||||
|
||||
class Decimal:
|
||||
def __init__(self, value: Union[str, int, float]) -> None:
|
||||
self.value = mpq(value)
|
||||
|
||||
def __add__(self, other: 'Decimal') -> 'Decimal':
|
||||
return Decimal(self.value + other.value)
|
||||
|
||||
def __sub__(self, other: 'Decimal') -> 'Decimal':
|
||||
return Decimal(self.value - other.value)
|
||||
|
||||
def __mul__(self, other: 'Decimal') -> 'Decimal':
|
||||
return Decimal(self.value * other.value)
|
||||
|
||||
def __truediv__(self, other: 'Decimal') -> 'Decimal':
|
||||
return Decimal(self.value / other.value)
|
||||
|
||||
def __neg__(self: 'Decimal') -> 'Decimal':
|
||||
return Decimal(- self.value)
|
||||
|
||||
def __lt__(self, other: 'Decimal') -> bool:
|
||||
return self.value < other.value
|
||||
|
||||
def __le__(self, other: 'Decimal') -> bool:
|
||||
return self.value <= other.value
|
||||
|
||||
def __gt__(self, other: 'Decimal') -> bool:
|
||||
return self.value > other.value
|
||||
|
||||
def __ge__(self, other: 'Decimal') -> bool:
|
||||
return self.value >= other.value
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
if isinstance(other, Decimal):
|
||||
return self.value != other.value
|
||||
else:
|
||||
return True
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Decimal):
|
||||
return self.value == other.value
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "{}".format(mpfr(self.value))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Decimal({self.value.__repr__()})"
|
||||
|
||||
|
||||
class Money:
|
||||
def __init__(self, value: Integer) -> None:
|
||||
self.value = value
|
||||
|
||||
def __add__(self, other: 'Money') -> 'Money':
|
||||
return Money(self.value + other.value)
|
||||
|
||||
def __sub__(self, other: 'Money') -> 'Money':
|
||||
return Money(self.value - other.value)
|
||||
|
||||
def __mul__(self, other: Decimal) -> 'Money':
|
||||
cents = self.value.value
|
||||
coeff = other.value
|
||||
# TODO: change, does not work with negative values. Must divide the
|
||||
# absolute values and then multiply by the resulting sign.
|
||||
rat_result = self.value.value * other.value
|
||||
out = Money(Integer(rat_result))
|
||||
res, remainder = t_divmod(rat_result.numerator, rat_result.denominator)
|
||||
if 2 * remainder >= rat_result.denominator:
|
||||
return Money(Integer(res + 1))
|
||||
else:
|
||||
return Money(Integer(res))
|
||||
|
||||
def __truediv__(self, other: 'Money') -> Decimal:
|
||||
return Decimal(mpq(self.value.value / other.value.value))
|
||||
|
||||
def __neg__(self: 'Money') -> 'Money':
|
||||
return Money(- self.value)
|
||||
|
||||
def __lt__(self, other: 'Money') -> bool:
|
||||
return self.value < other.value
|
||||
|
||||
def __le__(self, other: 'Money') -> bool:
|
||||
return self.value <= other.value
|
||||
|
||||
def __gt__(self, other: 'Money') -> bool:
|
||||
return self.value > other.value
|
||||
|
||||
def __ge__(self, other: 'Money') -> bool:
|
||||
return self.value >= other.value
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
if isinstance(other, Money):
|
||||
return self.value != other.value
|
||||
else:
|
||||
return True
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Money):
|
||||
return self.value == other.value
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "${:.2}".format(self.value.value / 100)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Money({self.value.__repr__()})"
|
||||
|
||||
|
||||
class Date:
|
||||
def __init__(self, value: datetime.date) -> None:
|
||||
self.value = value
|
||||
|
||||
def __add__(self, other: 'Duration') -> 'Date':
|
||||
return Date(self.value + other.value)
|
||||
|
||||
def __sub__(self, other: 'Date') -> 'Duration':
|
||||
return Duration(dateutil.relativedelta.relativedelta(self.value, other.value))
|
||||
|
||||
def __lt__(self, other: 'Date') -> bool:
|
||||
return self.value < other.value
|
||||
|
||||
def __le__(self, other: 'Date') -> bool:
|
||||
return self.value <= other.value
|
||||
|
||||
def __gt__(self, other: 'Date') -> bool:
|
||||
return self.value > other.value
|
||||
|
||||
def __ge__(self, other: 'Date') -> bool:
|
||||
return self.value >= other.value
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
if isinstance(other, Date):
|
||||
return self.value != other.value
|
||||
else:
|
||||
return True
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Date):
|
||||
return self.value == other.value
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value.__str__()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Date({self.value.__repr__()})"
|
||||
|
||||
|
||||
class Duration:
|
||||
def __init__(self, value: dateutil.relativedelta.relativedelta) -> None:
|
||||
self.value = value
|
||||
|
||||
def __add__(self, other: 'Duration') -> 'Duration':
|
||||
return Duration(self.value + other.value)
|
||||
|
||||
def __sub__(self, other: 'Duration') -> 'Duration':
|
||||
return Duration(self.value - other.value)
|
||||
|
||||
def __neg__(self: 'Duration') -> 'Duration':
|
||||
return Duration(- self.value)
|
||||
|
||||
def __truediv__(self, other: 'Duration') -> Decimal:
|
||||
x = self.value.normalized()
|
||||
y = other.value.normalized()
|
||||
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||
raise Exception("Can only divide durations expressed in days")
|
||||
else:
|
||||
return Decimal(x.days / y.days)
|
||||
|
||||
def __mul__(self: 'Duration', rhs: Integer) -> 'Duration':
|
||||
return Duration(
|
||||
dateutil.relativedelta.relativedelta(years=self.value.years * rhs.value,
|
||||
months=self.value.months * rhs.value,
|
||||
days=self.value.days * rhs.value))
|
||||
|
||||
def __lt__(self, other: 'Duration') -> bool:
|
||||
x = self.value.normalized()
|
||||
y = other.value.normalized()
|
||||
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||
raise Exception("Can only compare durations expressed in days")
|
||||
else:
|
||||
return x.days < y.days
|
||||
|
||||
def __le__(self, other: 'Duration') -> bool:
|
||||
x = self.value.normalized()
|
||||
y = other.value.normalized()
|
||||
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||
raise Exception("Can only compare durations expressed in days")
|
||||
else:
|
||||
return x.days <= y.days
|
||||
|
||||
def __gt__(self, other: 'Duration') -> bool:
|
||||
x = self.value.normalized()
|
||||
y = other.value.normalized()
|
||||
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||
raise Exception("Can only compare durations expressed in days")
|
||||
else:
|
||||
return x.days > y.days
|
||||
|
||||
def __ge__(self, other: 'Duration') -> bool:
|
||||
x = self.value.normalized()
|
||||
y = other.value.normalized()
|
||||
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||
raise Exception("Can only compare durations expressed in days")
|
||||
else:
|
||||
return x.days >= y.days
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
if isinstance(other, Duration):
|
||||
return self.value != other.value
|
||||
else:
|
||||
return True
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Duration):
|
||||
return self.value == other.value
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value.__str__()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Duration({self.value.__repr__()})"
|
||||
|
||||
|
||||
class Unit:
|
||||
def __init__(self) -> None:
|
||||
...
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Unit):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
if isinstance(other, Unit):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "()"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "Unit()"
|
||||
|
||||
|
||||
class SourcePosition:
|
||||
def __init__(self,
|
||||
filename: str,
|
||||
start_line: int,
|
||||
start_column: int,
|
||||
end_line: int,
|
||||
end_column: int,
|
||||
law_headings: List[str]) -> None:
|
||||
self.filename = filename
|
||||
self.start_line = start_line
|
||||
self.start_column = start_column
|
||||
self.end_line = end_line
|
||||
self.end_column = end_column
|
||||
self.law_headings = law_headings
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "in file {}, from {}:{} to {}:{}".format(
|
||||
self.filename, self.start_line, self.start_column, self.end_line, self.end_column)
|
||||
|
||||
# ==========
|
||||
# Exceptions
|
||||
# ==========
|
||||
|
||||
|
||||
class EmptyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AssertionFailed(Exception):
|
||||
def __init__(self, source_position: SourcePosition) -> None:
|
||||
self.source_position = SourcePosition
|
||||
|
||||
|
||||
class ConflictError(Exception):
|
||||
def __init__(self, source_position: SourcePosition) -> None:
|
||||
self.source_position = SourcePosition
|
||||
|
||||
|
||||
class NoValueProvided(Exception):
|
||||
def __init__(self, source_position: SourcePosition) -> None:
|
||||
self.source_position = SourcePosition
|
||||
|
||||
|
||||
class AssertionFailure(Exception):
|
||||
def __init__(self, source_position: SourcePosition) -> None:
|
||||
self.source_position = SourcePosition
|
||||
|
||||
|
||||
# ============================
|
||||
# Constructors and conversions
|
||||
# ============================
|
||||
|
||||
# -----
|
||||
# Money
|
||||
# -----
|
||||
|
||||
|
||||
def money_of_cents_string(v: str) -> Money:
|
||||
return Money(Integer(v))
|
||||
|
||||
|
||||
def money_of_units_int(v: int) -> Money:
|
||||
return Money(Integer(v) * Integer(100))
|
||||
|
||||
|
||||
def money_of_cents_integer(v: Integer) -> Money:
|
||||
return Money(v)
|
||||
|
||||
|
||||
def money_to_float(m: Money) -> float:
|
||||
return float(mpfr(mpq(m.value.value, 100)))
|
||||
|
||||
|
||||
def money_to_string(m: Money) -> str:
|
||||
return str(money_to_float(m))
|
||||
|
||||
|
||||
def money_to_cents(m: Money) -> Integer:
|
||||
return m.value
|
||||
|
||||
|
||||
def money_round(m: Money) -> Money:
|
||||
res, remainder = t_divmod(m, 100)
|
||||
if remainder < 50:
|
||||
return res * 100
|
||||
else:
|
||||
return (res + sign(res)) * 100
|
||||
|
||||
|
||||
def money_of_decimal(m: Decimal) -> Money:
|
||||
"""
|
||||
Warning: rounds to nearest cent.
|
||||
"""
|
||||
return Money(mpz(m.value))
|
||||
|
||||
# --------
|
||||
# Decimals
|
||||
# --------
|
||||
|
||||
|
||||
def decimal_of_string(d: str) -> Decimal:
|
||||
return Decimal(d)
|
||||
|
||||
|
||||
def decimal_to_float(d: Decimal) -> float:
|
||||
return float(mpfr(d.value))
|
||||
|
||||
|
||||
def decimal_of_float(d: float) -> Decimal:
|
||||
return Decimal(d)
|
||||
|
||||
|
||||
def decimal_of_integer(d: Integer) -> Decimal:
|
||||
return Decimal(d.value)
|
||||
|
||||
|
||||
def decimal_to_string(precision: int, i: Decimal) -> str:
|
||||
return "{1:.{0}}".format(precision, mpfr(i.value, precision * 10 // 2))
|
||||
|
||||
|
||||
def decimal_round(q: Decimal) -> Decimal:
|
||||
# Implements the workaround by
|
||||
# https://gmplib.org/list-archives/gmp-discuss/2009-May/003767.html *)
|
||||
return f_div(2*q.numerator + q.denominator, 2*q.denominator) # type:ignore
|
||||
|
||||
|
||||
def decimal_of_money(m: Money) -> Decimal:
|
||||
return Decimal(f_div(mpq(m.value), mpq(100)))
|
||||
|
||||
# --------
|
||||
# Integers
|
||||
# --------
|
||||
|
||||
|
||||
def integer_of_string(s: str) -> Integer:
|
||||
return Integer(s)
|
||||
|
||||
|
||||
def integer_to_string(d: Integer) -> str:
|
||||
return str(d.value)
|
||||
|
||||
|
||||
def integer_of_int(d: int) -> Integer:
|
||||
return Integer(d)
|
||||
|
||||
|
||||
def integer_to_int(d: Integer) -> int:
|
||||
return int(d.value)
|
||||
|
||||
|
||||
def integer_exponentiation(i: Integer, e: int) -> Integer:
|
||||
return i ** e # type: ignore
|
||||
|
||||
|
||||
def integer_log2(i: Integer) -> int:
|
||||
return int(log2(i.value))
|
||||
|
||||
# -----
|
||||
# Dates
|
||||
# -----
|
||||
|
||||
|
||||
def day_of_month_of_date(d: Date) -> Integer:
|
||||
return integer_of_int(d.value.day)
|
||||
|
||||
|
||||
def month_number_of_date(d: Date) -> Integer:
|
||||
return integer_of_int(d.value.month)
|
||||
|
||||
|
||||
def year_of_date(d: Date) -> Integer:
|
||||
return integer_of_int(d.value.year)
|
||||
|
||||
|
||||
def date_to_string(d: Date) -> str:
|
||||
return "{}".format(d.value)
|
||||
|
||||
|
||||
def date_of_numbers(year: int, month: int, day: int) -> Date:
|
||||
# The datetime.date does not take year=0 as an entry, we trick it into
|
||||
# 1 in that case because year=0 cases don't care about the actual year
|
||||
return Date(datetime.date(year if year != 0 else 1, month, day))
|
||||
|
||||
|
||||
def date_of_datetime(d: datetime.date) -> Date:
|
||||
return Date(d)
|
||||
|
||||
|
||||
def first_day_of_month(d: Date) -> Date:
|
||||
return Date(datetime.date(d.value.year, d.value.month, 1))
|
||||
|
||||
|
||||
def last_day_of_month(d: Date) -> Date:
|
||||
return Date(datetime.date(d.value.year, d.value.month, calendar.monthrange(d.value.year, d.value.month)[1]))
|
||||
|
||||
# ---------
|
||||
# Durations
|
||||
# ---------
|
||||
|
||||
|
||||
def duration_of_numbers(years: int, months: int, days: int) -> Duration:
|
||||
return Duration(dateutil.relativedelta.relativedelta(years=years, months=months, days=days))
|
||||
|
||||
|
||||
def duration_to_years_months_days(d: Duration) -> Tuple[int, int, int]:
|
||||
return (d.value.years, d.value.months, d.value.days) # type: ignore
|
||||
|
||||
|
||||
def duration_to_string(s: Duration) -> str:
|
||||
return "{}".format(s.value)
|
||||
|
||||
# -----
|
||||
# Lists
|
||||
# -----
|
||||
|
||||
|
||||
def list_fold_left(f: Callable[[Alpha, Beta], Alpha], init: Alpha, l: List[Beta]) -> Alpha:
|
||||
return reduce(f, l, init)
|
||||
|
||||
|
||||
def list_filter(f: Callable[[Alpha], bool], l: List[Alpha]) -> List[Alpha]:
|
||||
return [i for i in l if f(i)]
|
||||
|
||||
|
||||
def list_map(f: Callable[[Alpha], Beta], l: List[Alpha]) -> List[Beta]:
|
||||
return [f(i) for i in l]
|
||||
|
||||
|
||||
def list_length(l: List[Alpha]) -> Integer:
|
||||
return Integer(len(l))
|
||||
|
||||
# ========
|
||||
# Defaults
|
||||
# ========
|
||||
|
||||
|
||||
def handle_default(
|
||||
pos: SourcePosition,
|
||||
exceptions: List[Callable[[Unit], Alpha]],
|
||||
just: Callable[[Unit], Alpha],
|
||||
cons: Callable[[Unit], Alpha]
|
||||
) -> Alpha:
|
||||
acc: Optional[Alpha] = None
|
||||
for exception in exceptions:
|
||||
new_val: Optional[Alpha]
|
||||
try:
|
||||
new_val = exception(Unit())
|
||||
except EmptyError:
|
||||
new_val = None
|
||||
if acc is None:
|
||||
acc = new_val
|
||||
elif not (acc is None) and new_val is None:
|
||||
pass # acc stays the same
|
||||
elif not (acc is None) and not (new_val is None):
|
||||
raise ConflictError(pos)
|
||||
if acc is None:
|
||||
if just(Unit()):
|
||||
return cons(Unit())
|
||||
else:
|
||||
raise EmptyError
|
||||
else:
|
||||
return acc
|
||||
|
||||
|
||||
def handle_default_opt(
|
||||
pos: SourcePosition,
|
||||
exceptions: List[Optional[Any]],
|
||||
just: Optional[bool],
|
||||
cons: Optional[Alpha]
|
||||
) -> Optional[Alpha]:
|
||||
acc: Optional[Alpha] = None
|
||||
for exception in exceptions:
|
||||
if acc is None:
|
||||
acc = exception
|
||||
elif not (acc is None) and exception is None:
|
||||
pass # acc stays the same
|
||||
elif not (acc is None) and not (exception is None):
|
||||
raise ConflictError(pos)
|
||||
if acc is None:
|
||||
if just is None:
|
||||
return None
|
||||
else:
|
||||
if just:
|
||||
return cons
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return acc
|
||||
|
||||
|
||||
def no_input() -> Callable[[Unit], Alpha]:
|
||||
def closure(_: Unit):
|
||||
raise EmptyError
|
||||
return closure
|
||||
|
||||
|
||||
# This value is used for the Python code generation to trump mypy and forcing
|
||||
# it to accept dead code. Indeed, when raising an exception during a variable
|
||||
# definition, mypy complains that the later dead code will not know what
|
||||
# this variable was. So we give this variable a dead value.
|
||||
dead_value: Any = 0
|
||||
|
||||
# =======
|
||||
# Logging
|
||||
# =======
|
||||
|
||||
|
||||
class LogEventCode(Enum):
|
||||
VariableDefinition = 0
|
||||
BeginCall = 1
|
||||
EndCall = 2
|
||||
DecisionTaken = 3
|
||||
|
||||
|
||||
class LogEvent:
|
||||
def __init__(self, code: LogEventCode, payload: Union[List[str], SourcePosition, Tuple[List[str], Alpha]]) -> None:
|
||||
self.code = code
|
||||
self.payload = payload
|
||||
|
||||
|
||||
log: List[LogEvent] = []
|
||||
|
||||
|
||||
def reset_log():
|
||||
log = []
|
||||
|
||||
|
||||
def retrieve_log() -> List[LogEvent]:
|
||||
return log
|
||||
|
||||
|
||||
def log_variable_definition(headings: List[str], value: Alpha) -> Alpha:
|
||||
log.append(LogEvent(LogEventCode.VariableDefinition,
|
||||
(headings, copy.deepcopy(value))))
|
||||
return value
|
||||
|
||||
|
||||
def log_begin_call(headings: List[str], f: Callable[[Alpha], Beta], value: Alpha) -> Beta:
|
||||
log.append(LogEvent(LogEventCode.BeginCall, headings))
|
||||
return f(value)
|
||||
|
||||
|
||||
def log_end_call(headings: List[str], value: Alpha) -> Alpha:
|
||||
log.append(LogEvent(LogEventCode.EndCall, headings))
|
||||
return value
|
||||
|
||||
|
||||
def log_decision_taken(pos: SourcePosition, value: bool) -> bool:
|
||||
log.append(LogEvent(LogEventCode.DecisionTaken, pos))
|
||||
return value
|
@ -1,3 +1,5 @@
|
||||
from setuptools import setup
|
||||
from setuptools import setup # type: ignore
|
||||
|
||||
setup()
|
||||
setup(package_data={
|
||||
'catala-runtime': ['py.typed'],
|
||||
},)
|
||||
|
@ -9,8 +9,7 @@
|
||||
|
||||
# This file should be in sync with compiler/runtime.{ml, mli} !
|
||||
|
||||
from this import d
|
||||
from gmpy2 import log2, mpz, mpq, mpfr, t_divmod, f_div, sign # type: ignore
|
||||
from gmpy2 import log2, mpz, mpq, mpfr, t_divmod, qdiv, f_div, sign # type: ignore
|
||||
import datetime
|
||||
import calendar
|
||||
import dateutil.relativedelta
|
||||
@ -411,18 +410,19 @@ def money_to_cents(m: Money) -> Integer:
|
||||
|
||||
|
||||
def money_round(m: Money) -> Money:
|
||||
res, remainder = t_divmod(m, 100)
|
||||
res, remainder = t_divmod(m.value.value, 100)
|
||||
if remainder < 50:
|
||||
return res * 100
|
||||
return Money(Integer(res * 100))
|
||||
else:
|
||||
return (res + sign(res)) * 100
|
||||
return Money(Integer((res + sign(res)) * 100))
|
||||
|
||||
|
||||
def money_of_decimal(m: Decimal) -> Money:
|
||||
def money_of_decimal(d: Decimal) -> Money:
|
||||
"""
|
||||
Warning: rounds to nearest cent.
|
||||
Warning: rounds to the nearest cent
|
||||
"""
|
||||
return Money(mpz(m.value))
|
||||
return Money(Integer(mpz(d.value)))
|
||||
|
||||
|
||||
# --------
|
||||
# Decimals
|
||||
@ -450,13 +450,18 @@ def decimal_to_string(precision: int, i: Decimal) -> str:
|
||||
|
||||
|
||||
def decimal_round(q: Decimal) -> Decimal:
|
||||
# Implements the workaround by
|
||||
# https://gmplib.org/list-archives/gmp-discuss/2009-May/003767.html *)
|
||||
return f_div(2*q.numerator + q.denominator, 2*q.denominator) # type:ignore
|
||||
"""
|
||||
Implements the workaround by
|
||||
https://gmplib.org/list-archives/gmp-discuss/2009-May/003767.html
|
||||
"""
|
||||
return Decimal(
|
||||
mpq(f_div(2*q.value.numerator + q.value.denominator,
|
||||
2*q.value.denominator), 1) # type:ignore
|
||||
)
|
||||
|
||||
|
||||
def decimal_of_money(m: Money) -> Decimal:
|
||||
return Decimal(f_div(mpq(m.value), mpq(100)))
|
||||
return Decimal(mpq(qdiv(m.value.value, 100)))
|
||||
|
||||
# --------
|
||||
# Integers
|
||||
|
Loading…
Reference in New Issue
Block a user