Cross-test French housing benefits with official simulator (#338)

This commit is contained in:
Denis Merigoux 2022-10-17 17:04:27 +02:00 committed by GitHub
commit 5e768ec747
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 7379 additions and 6609 deletions

View File

@ -50,136 +50,102 @@ function run_computation_AF(log) {
function run_computation_AL(log) { function run_computation_AL(log) {
var result = Law.computeAidesAuLogement({ var result = Law.computeAidesAuLogement({
dateCouranteIn: "2022-01-01", "menageIn": {
menageIn: { "prestationsRecues": [],
prestationsRecues: [ "logement": {
{ kind: "AllocationSoutienEnfantHandicape", payload: null }, "residencePrincipale": true,
{ kind: "ComplementFamilial", payload: null }, "estEhpadOuMaisonAutonomieL31312Asf": false,
{ kind: "AllocationsFamiliales", payload: null }, "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: { "nombreAutresOccupantsLogement": 0,
kind: "Maries", "situationFamiliale": {
payload: "2010-11-26", "kind": "Concubins",
}, "payload": null
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",
},
}, },
"conditionRattacheFoyerFiscalParentIfi": false,
"nombreEnfantsANaitreApresTroisiemeMoisGrossesse": 0,
}, },
demandeurIn: { "demandeurIn": {
personneHebergeeCentreSoinLL162223SecuriteSociale: false, "nationalite": {
satisfaitConditionsL5122CodeSecuriteSociale: true, "kind": "Francaise"
ageDemandeur: 52,
dateNaissance: "1970-05-02",
contratDeTravail: { kind: "CDI", payload: null },
nationalite: { kind: "Francaise", payload: null },
patrimoine: {
produisantRevenuPeriodeR82233R8224: 0,
neProduisantPasRevenuPeriodeR82233R8224: 0,
}, },
}, "patrimoine": {
informationsCalculIn: { "produisantRevenuPeriodeR82233R8224": 0,
kind: "InfosLocatif", "neProduisantPasRevenuPeriodeR82233R8224": 0
payload: {
loyerPrincipal: 1700,
beneficiaireAideAdulteOuEnfantHandicapes: false,
logementEstChambre: false,
colocation: false,
ageesOuHandicapAdultesHebergeesOnereuxParticuliers: false,
reductionLoyerSolidarite: 0,
logementMeubleD8422: false,
changementLogementD8424: {
kind: "PasDeChangement",
payload: null,
},
}, },
"personneHebergeeCentreSoinLL162223SecuriteSociale": false,
"dateNaissance": "1992-01-01"
}, },
ressourcesMenagePrisesEnCompteIn: 20000, "dateCouranteIn": "2022-05-01",
"ressourcesMenagePrisesEnCompteIn": 11500
}); });
if (log) { if (log) {
console.log( console.log(

File diff suppressed because one or more lines are too long

View File

@ -15253,7 +15253,7 @@ let ressources_aides_personnelle_logement (ressources_aides_personnelle_logement
if if
(demandeur_exerce_activite_remuneree_ && (demandeur_exerce_activite_remuneree_ &&
(conjoint_exerce_activite_remuneree_ && (conjoint_exerce_activite_remuneree_ &&
((ressources_conjoint_ +$ ressources_conjoint_) >=$ ((ressources_demandeur_ +$ ressources_conjoint_) >=$
(base_mensuelle_allocations_familiales_dot_montant_ *$ (base_mensuelle_allocations_familiales_dot_montant_ *$
(decimal_of_string "12."))))) then (decimal_of_string "12."))))) then
montant_forfaitaire_r_822_7_ else (money_of_cents_string "0"))) montant_forfaitaire_r_822_7_ else (money_of_cents_string "0")))

View File

@ -13,7 +13,8 @@ format:
autopep8 --in-place $(SOURCES) autopep8 --in-place $(SOURCES)
bench: bench:
python main.py bench python main.py bench_family
python main.py bench_housing
show_log: show_log:
python main.py show_log python main.py show_log

View 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

View 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

View 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
)

View 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)

View 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)

View File

@ -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
)

View File

@ -1,8 +1,9 @@
#!python3 #!python3
from datetime import date from datetime import date
from src.allocations_familiales import PriseEnCharge_Code, Collectivite_Code from src.aides_logement import ModeOccupation_Code, Nationalite_Code, PrestationRecue_Code, SituationFamiliale_Code, SituationGardeAlternee_Code, SituationObligationScolaire_Code, TypeBailleur_Code, ZoneDHabitation_Code
from src.api import allocations_familiales, Enfant 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 from catala.runtime import LogEvent, LogEventCode, reset_log, retrieve_log
import timeit import timeit
import argparse 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() money_given = call_allocations_familiales()
assert (money_given == 99.46) 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]: def run_with_log() -> List[LogEvent]:
money_given = call_allocations_familiales() money_given = call_allocations_familiales()
assert (money_given == 99.46) assert (money_given == 99.46)
@ -61,11 +128,16 @@ if __name__ == '__main__':
args = parser.parse_args() args = parser.parse_args()
action = args.action[0] action = args.action[0]
if action == "bench": if action == "bench_family":
iterations = 1000 iterations = 1000
print("Iterating {} iterations of the family benefits computation. Total time (s):".format( print("Iterating {} iterations of the family benefits computation. Total time (s):".format(
iterations)) 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": elif action == "show_log":
log = run_with_log() log = run_with_log()
indentation = 0 indentation = 0

File diff suppressed because it is too large Load Diff

View File

@ -780,21 +780,23 @@ def prestations_familiales(prestations_familiales_in:PrestationsFamilialesIn):
temp_smic_dot_date_courante = date_courante_2 temp_smic_dot_date_courante = date_courante_2
except EmptyError: except EmptyError:
temp_smic_dot_date_courante = dead_value temp_smic_dot_date_courante = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/../smic/smic.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
start_line=9, start_column=10, start_line=69, start_column=14,
end_line=9, end_column=23, end_line=69, end_column=32,
law_headings=["Prologue", law_headings=["Prestations familiales",
"Montant du salaire minimum de croissance"])) "Champs d'applications",
"Prologue"]))
smic_dot_date_courante = temp_smic_dot_date_courante smic_dot_date_courante = temp_smic_dot_date_courante
try: try:
temp_smic_dot_residence = residence_1 temp_smic_dot_residence = residence_1
except EmptyError: except EmptyError:
temp_smic_dot_residence = dead_value temp_smic_dot_residence = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/../smic/smic.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
start_line=10, start_column=10, start_line=68, start_column=14,
end_line=10, end_column=19, end_line=68, end_column=28,
law_headings=["Prologue", law_headings=["Prestations familiales",
"Montant du salaire minimum de croissance"])) "Champs d'applications",
"Prologue"]))
smic_dot_residence = temp_smic_dot_residence smic_dot_residence = temp_smic_dot_residence
result = smic(SmicIn(date_courante_in = smic_dot_date_courante, result = smic(SmicIn(date_courante_in = smic_dot_date_courante,
residence_in = smic_dot_residence)) residence_in = smic_dot_residence))
@ -1316,10 +1318,12 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
temp_bmaf_dot_date_courante = date_courante_3 temp_bmaf_dot_date_courante = date_courante_3
except EmptyError: except EmptyError:
temp_bmaf_dot_date_courante = dead_value temp_bmaf_dot_date_courante = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/../base_mensuelle_allocations_familiales/bmaf.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
start_line=5, start_column=10, start_line=159, start_column=14,
end_line=5, end_column=23, end_line=159, end_column=32,
law_headings=["Montant de la base mensuelle des allocations familiales"])) law_headings=["Allocations familiales",
"Champs d'applications",
"Prologue"]))
bmaf_dot_date_courante = temp_bmaf_dot_date_courante bmaf_dot_date_courante = temp_bmaf_dot_date_courante
result_2 = base_mensuelle_allocations_familiales(BaseMensuelleAllocationsFamilialesIn(date_courante_in = 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 bmaf_dot_montant = result_2.montant_out
@ -1328,9 +1332,9 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
except EmptyError: except EmptyError:
temp_prestations_familiales_dot_date_courante = dead_value temp_prestations_familiales_dot_date_courante = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
start_line=62, start_column=10, start_line=155, start_column=14,
end_line=62, end_column=23, end_line=155, end_column=50,
law_headings=["Prestations familiales", law_headings=["Allocations familiales",
"Champs d'applications", "Champs d'applications",
"Prologue"])) "Prologue"]))
prestations_familiales_dot_date_courante = temp_prestations_familiales_dot_date_courante prestations_familiales_dot_date_courante = temp_prestations_familiales_dot_date_courante
@ -1340,9 +1344,9 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
except EmptyError: except EmptyError:
temp_prestations_familiales_dot_prestation_courante = dead_value temp_prestations_familiales_dot_prestation_courante = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
start_line=63, start_column=10, start_line=153, start_column=14,
end_line=63, end_column=29, end_line=153, end_column=56,
law_headings=["Prestations familiales", law_headings=["Allocations familiales",
"Champs d'applications", "Champs d'applications",
"Prologue"])) "Prologue"]))
prestations_familiales_dot_prestation_courante = temp_prestations_familiales_dot_prestation_courante prestations_familiales_dot_prestation_courante = temp_prestations_familiales_dot_prestation_courante
@ -1351,9 +1355,9 @@ def allocations_familiales(allocations_familiales_in:AllocationsFamilialesIn):
except EmptyError: except EmptyError:
temp_prestations_familiales_dot_residence = dead_value temp_prestations_familiales_dot_residence = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr",
start_line=64, start_column=10, start_line=157, start_column=14,
end_line=64, end_column=19, end_line=157, end_column=46,
law_headings=["Prestations familiales", law_headings=["Allocations familiales",
"Champs d'applications", "Champs d'applications",
"Prologue"])) "Prologue"]))
prestations_familiales_dot_residence = temp_prestations_familiales_dot_residence 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 temp_enfant_le_plus_age_dot_enfants = enfants_a_charge
except EmptyError: except EmptyError:
temp_enfant_le_plus_age_dot_enfants = dead_value temp_enfant_le_plus_age_dot_enfants = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=79, start_column=10, start_line=32, start_column=14,
end_line=79, end_column=17, end_line=32, end_column=40,
law_headings=["Allocations familiales", law_headings=["Règles diverses",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
enfant_le_plus_age_dot_enfants = temp_enfant_le_plus_age_dot_enfants 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)) 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 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 temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1 = temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent
except EmptyError: except EmptyError:
temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1 = dead_value temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1 = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=85, start_column=10, start_line=91, start_column=5,
end_line=85, end_column=57, end_line=91, end_column=75,
law_headings=["Allocations familiales", law_headings=["Interface du programme",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
allocations_familiales_dot_personne_charge_effective_permanente_est_parent = temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1 allocations_familiales_dot_personne_charge_effective_permanente_est_parent = temp_allocations_familiales_dot_personne_charge_effective_permanente_est_parent_1
try: try:
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 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: except EmptyError:
temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i_1 = dead_value temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i_1 = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=86, start_column=10, start_line=95, start_column=5,
end_line=86, end_column=62, end_line=95, end_column=80,
law_headings=["Allocations familiales", law_headings=["Interface du programme",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i = temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i_1 allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i = temp_allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i_1
try: try:
temp_allocations_familiales_dot_ressources_menage = i_ressources_menage temp_allocations_familiales_dot_ressources_menage = i_ressources_menage
except EmptyError: except EmptyError:
temp_allocations_familiales_dot_ressources_menage = dead_value temp_allocations_familiales_dot_ressources_menage = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=87, start_column=10, start_line=87, start_column=14,
end_line=87, end_column=27, end_line=87, end_column=54,
law_headings=["Allocations familiales", law_headings=["Interface du programme",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
allocations_familiales_dot_ressources_menage = temp_allocations_familiales_dot_ressources_menage allocations_familiales_dot_ressources_menage = temp_allocations_familiales_dot_ressources_menage
try: try:
temp_allocations_familiales_dot_residence = i_residence temp_allocations_familiales_dot_residence = i_residence
except EmptyError: except EmptyError:
temp_allocations_familiales_dot_residence = dead_value temp_allocations_familiales_dot_residence = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=88, start_column=10, start_line=88, start_column=14,
end_line=88, end_column=19, end_line=88, end_column=46,
law_headings=["Allocations familiales", law_headings=["Interface du programme",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
allocations_familiales_dot_residence = temp_allocations_familiales_dot_residence allocations_familiales_dot_residence = temp_allocations_familiales_dot_residence
try: try:
temp_allocations_familiales_dot_date_courante = i_date_courante temp_allocations_familiales_dot_date_courante = i_date_courante
except EmptyError: except EmptyError:
temp_allocations_familiales_dot_date_courante = dead_value temp_allocations_familiales_dot_date_courante = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=91, start_column=10, start_line=85, start_column=14,
end_line=91, end_column=23, end_line=85, end_column=50,
law_headings=["Allocations familiales", law_headings=["Interface du programme",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
allocations_familiales_dot_date_courante = temp_allocations_familiales_dot_date_courante allocations_familiales_dot_date_courante = temp_allocations_familiales_dot_date_courante
try: try:
temp_allocations_familiales_dot_enfants_a_charge = enfants_a_charge_1 temp_allocations_familiales_dot_enfants_a_charge = enfants_a_charge_1
except EmptyError: except EmptyError:
temp_allocations_familiales_dot_enfants_a_charge = dead_value temp_allocations_familiales_dot_enfants_a_charge = dead_value
raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/prologue.catala_fr", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=94, start_column=10, start_line=86, start_column=14,
end_line=94, end_column=26, end_line=86, end_column=53,
law_headings=["Allocations familiales", law_headings=["Interface du programme",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
allocations_familiales_dot_enfants_a_charge = temp_allocations_familiales_dot_enfants_a_charge allocations_familiales_dot_enfants_a_charge = temp_allocations_familiales_dot_enfants_a_charge
try: try:
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 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: except EmptyError:
temp_allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012_1 = dead_value 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", raise NoValueProvided(SourcePosition(filename="examples/allocations_familiales/epilogue.catala_fr",
start_line=115, start_column=10, start_line=99, start_column=5,
end_line=115, end_column=54, end_line=99, end_column=72,
law_headings=["Allocations familiales", law_headings=["Interface du programme",
"Champs d'applications", "Épilogue"]))
"Prologue"]))
allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012 = temp_allocations_familiales_dot_avait_enfant_a_charge_avant_1er_janvier_2012_1 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, 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, personne_charge_effective_permanente_remplit_titre_I_in = allocations_familiales_dot_personne_charge_effective_permanente_remplit_titre__i,

View File

@ -1,5 +1,9 @@
from abc import ABC
from catala.runtime import * from catala.runtime import *
from .allocations_familiales import Collectivite, Collectivite_Code, InterfaceAllocationsFamilialesIn, PriseEnCharge, interface_allocations_familiales, PriseEnCharge_Code, EnfantEntree, InterfaceAllocationsFamilialesIn 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: 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 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) 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)

View File

@ -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

View File

@ -1,3 +1,5 @@
from setuptools import setup from setuptools import setup # type: ignore
setup() setup(package_data={
'catala-runtime': ['py.typed'],
},)

View File

@ -9,8 +9,7 @@
# This file should be in sync with compiler/runtime.{ml, mli} ! # This file should be in sync with compiler/runtime.{ml, mli} !
from this import d from gmpy2 import log2, mpz, mpq, mpfr, t_divmod, qdiv, f_div, sign # type: ignore
from gmpy2 import log2, mpz, mpq, mpfr, t_divmod, f_div, sign # type: ignore
import datetime import datetime
import calendar import calendar
import dateutil.relativedelta import dateutil.relativedelta
@ -411,18 +410,19 @@ def money_to_cents(m: Money) -> Integer:
def money_round(m: Money) -> Money: def money_round(m: Money) -> Money:
res, remainder = t_divmod(m, 100) res, remainder = t_divmod(m.value.value, 100)
if remainder < 50: if remainder < 50:
return res * 100 return Money(Integer(res * 100))
else: 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 # Decimals
@ -450,13 +450,18 @@ def decimal_to_string(precision: int, i: Decimal) -> str:
def decimal_round(q: Decimal) -> Decimal: def decimal_round(q: Decimal) -> Decimal:
# Implements the workaround by """
# https://gmplib.org/list-archives/gmp-discuss/2009-May/003767.html *) Implements the workaround by
return f_div(2*q.numerator + q.denominator, 2*q.denominator) # type:ignore 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: 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 # Integers