Skip to content
Snippets Groups Projects
Commit 932c079e authored by sandcha's avatar sandcha
Browse files

Corrige l'identification des paramètres issus de barèmes seuil/taux/montant

Et extrait l'identification de paramètre openfisca amendé dans mapping/reform_parameters.py
Permet à get_openfisca_parameter de ne chercher les Parameter du modèle que s'ils ne sont pas déjà pré-identifiés
Optimise ainsi légèrement le calcul de réforme
parent fd20b5fa
No related branches found
No related tags found
1 merge request!20Corrige l'identification des seuils et valeurs de barèmes amendés
......@@ -5,9 +5,9 @@ from openfisca_core import periods
from openfisca_core.parameters import Parameter
from openfisca_core.reforms import Reform
from leximpact_dotations_back.mapping.reform_parameters import get_openfisca_parameter
from leximpact_dotations_back.computing.dotations_simulation import DotationsSimulation
# configure _root_ logger
logger = logging.getLogger()
REFORM_YEAR = 2024
......@@ -28,32 +28,13 @@ class reform_from_amendement(Reform):
self.amendement_parameters: Dict[str, float] = amendement_parameters
super().__init__(baseline)
def get_openfisca_parameter(self, parameters, parameter_dot_name) -> Parameter | None:
try:
parameter_name = parameter_dot_name
cles = parameter_name.split('.')
for cle in cles:
parameters = parameters.children[cle]
logger.debug(f"La valeur correspondante à '{parameter_name}' est : {parameters}")
return parameters # un Parameter si parameter_dot_name indique bien une feuille de l'arbre des parameters
except KeyError:
# La clé '{cle}' n'a pas été trouvée dans modèle
logger.error(f"Paramètre '{parameter_name}' introuvable.")
return None
except AttributeError:
logger.error("La structure du modèle n'est pas un dictionnaire à un niveau donné.")
return None
def reform_parameters_from_amendement(self, parameters, amendement_parameters):
reform_period = periods.period(REFORM_YEAR)
for parameter_name, value in amendement_parameters.items():
try:
one_parameter: Parameter = self.get_openfisca_parameter(parameters, parameter_name)
one_parameter: Parameter = get_openfisca_parameter(parameters, parameter_name)
if one_parameter is not None:
one_parameter.update(period=reform_period, value=value)
except ValueError as e:
......@@ -85,24 +66,7 @@ class reform_from_plf(Reform):
self.plf_parameters: Dict[str, float] = plf_parameters
super().__init__(baseline)
def get_openfisca_parameter(parameters, parameter_dot_name) -> Parameter | None:
try:
parameter_name = parameter_dot_name
cles = parameter_name.split('.')
for cle in cles:
parameters = parameters.children[cle]
logger.debug(f"La valeur correspondante à '{parameter_name}' est : {parameters}")
return parameters # un Parameter si parameter_dot_name indique bien une feuille de l'arbre des parameters
except KeyError:
# La clé '{cle}' n'a pas été trouvée dans modèle
logger.error(f"Paramètre '{parameter_name}' introuvable.")
return None
except AttributeError:
logger.error("La structure du modèle n'est pas un dictionnaire à un niveau donné.")
return None
def reform_parameters_from_plf(self, parameters, plf_parameters):
reform_period = periods.period(REFORM_YEAR)
......@@ -110,7 +74,7 @@ class reform_from_plf(Reform):
for parameter_name, value in plf_parameters.items():
try:
one_parameter: Parameter = self.get_openfisca_parameter(parameters, parameter_name)
one_parameter: Parameter = get_openfisca_parameter(parameters, parameter_name)
if one_parameter is not None:
one_parameter.update(period=reform_period, value=value)
except ValueError as e:
......
import logging
from openfisca_core.parameters import Parameter, ParameterNode
# configure _root_ logger
logger = logging.getLogger()
def extract_openfisca_parameter(parameters: ParameterNode, parameter_dot_name: str) -> Parameter | None:
'''
Extrait le Parameter openfisca de l'arbre des paramètres fourni
à partir de son nom en notation pointée.
'''
parameter_name: str = parameter_dot_name
is_scale_bracket: bool = False
bracket_openfisca_index: int | None = None
remaining_path: str | None = None
if 'brackets' in parameter_dot_name:
is_scale_bracket = True
start_bracket_index = parameter_dot_name.index('.brackets[') + 10 # 10 pour longueur '.brackets['
parameter_name = parameter_dot_name[:start_bracket_index - 10] # tout ce qui précède '.brackets['
end_bracket_index = parameter_dot_name.index(']')
bracket_openfisca_index = int(parameter_dot_name[start_bracket_index:end_bracket_index])
# le remaining_path attendu est 'threshold', 'rate' ou 'amount'
remaining_path = parameter_dot_name[end_bracket_index + 2:] # 2 pour longueur "]."
try:
# pour un paramètre simple, parameter_name est le nom fourni en argument
# pour un barème, parameter_name est le nom fourni en argument tronqué à partir de '.brackets'
cles = parameter_name.split('.')
for cle in cles:
parameters = parameters.children[cle]
if is_scale_bracket:
# parameters.brackets est list[ParameterScaleBracket]
# mais structure étrange : parameters.brackets[bracket_openfisca_index] est un ParameterNode
parameters = parameters.brackets[bracket_openfisca_index].children[remaining_path]
logger.debug(f"La valeur correspondante à '{parameter_name}' est : {parameters}")
return parameters # un Parameter si parameter_dot_name indique bien une feuille de l'arbre des parameters
except KeyError as ke:
# La clé '{cle}' n'a pas été trouvée dans modèle
logger.error(f"Paramètre '{parameter_name}' introuvable.")
return None
except AttributeError as ae:
logger.error(f"La structure de l'arbre de paramètres n'est pas un dictionnaire à partir de : {parameters}")
return None
def get_openfisca_parameter(openfisca_parameters: ParameterNode, openfisca_parameter_suffix: str) -> Parameter:
identified_reform_parameters_2024 = {
"dotation_forfaitaire.ecretement.plafond_pourcentage_recettes_max" : openfisca_parameters.dotation_forfaitaire.ecretement.plafond_pourcentage_recettes_max,
"dotation_forfaitaire.ecretement.seuil_rapport_potentiel_fiscal" : openfisca_parameters.dotation_forfaitaire.ecretement.seuil_rapport_potentiel_fiscal,
"dotation_solidarite_rurale.attribution.plafond_effort_fiscal" : openfisca_parameters.dotation_solidarite_rurale.attribution.plafond_effort_fiscal,
"dotation_solidarite_rurale.attribution.poids_enfants" : openfisca_parameters.dotation_solidarite_rurale.attribution.poids_enfants,
"dotation_solidarite_rurale.attribution.poids_longueur_voirie" : openfisca_parameters.dotation_solidarite_rurale.attribution.poids_longueur_voirie,
"dotation_solidarite_rurale.attribution.poids_potentiel_financier_par_habitant" : openfisca_parameters.dotation_solidarite_rurale.attribution.poids_potentiel_financier_par_habitant,
"dotation_solidarite_rurale.attribution.poids_potentiel_financier_par_hectare" : openfisca_parameters.dotation_solidarite_rurale.attribution.poids_potentiel_financier_par_hectare,
"dotation_solidarite_rurale.augmentation_montant" : openfisca_parameters.dotation_solidarite_rurale.augmentation_montant,
"dotation_solidarite_rurale.bourg_centre.attribution.coefficient_zrr" : openfisca_parameters.dotation_solidarite_rurale.bourg_centre.attribution.coefficient_zrr,
"dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_part_population_dgf_agglomeration_departement" : openfisca_parameters.dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_part_population_dgf_agglomeration_departement,
"dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_population_dgf_agglomeration" : openfisca_parameters.dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_population_dgf_agglomeration,
"dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_population_dgf_chef_lieu_de_canton" : openfisca_parameters.dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_population_dgf_chef_lieu_de_canton,
"dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_population_dgf_maximum_commune_agglomeration" : openfisca_parameters.dotation_solidarite_rurale.bourg_centre.eligibilite.exclusion.seuil_population_dgf_maximum_commune_agglomeration,
"dotation_solidarite_rurale.bourg_centre.eligibilite.seuil_nombre_habitants_chef_lieu" : openfisca_parameters.dotation_solidarite_rurale.bourg_centre.eligibilite.seuil_nombre_habitants_chef_lieu,
"dotation_solidarite_rurale.bourg_centre.eligibilite.seuil_part_population_canton" : openfisca_parameters.dotation_solidarite_rurale.bourg_centre.eligibilite.seuil_part_population_canton,
"dotation_solidarite_rurale.cible.eligibilite.indice_synthetique.poids_potentiel_financier" : openfisca_parameters.dotation_solidarite_rurale.cible.eligibilite.indice_synthetique.poids_potentiel_financier,
"dotation_solidarite_rurale.cible.eligibilite.indice_synthetique.poids_revenu" : openfisca_parameters.dotation_solidarite_rurale.cible.eligibilite.indice_synthetique.poids_revenu,
"dotation_solidarite_rurale.cible.eligibilite.seuil_classement" : openfisca_parameters.dotation_solidarite_rurale.cible.eligibilite.seuil_classement,
"dotation_solidarite_rurale.perequation.seuil_rapport_potentiel_financier" : openfisca_parameters.dotation_solidarite_rurale.perequation.seuil_rapport_potentiel_financier,
"dotation_solidarite_rurale.seuil_nombre_habitants" : openfisca_parameters.dotation_solidarite_rurale.seuil_nombre_habitants,
"dotation_solidarite_urbaine.attribution.facteur_classement_max" : openfisca_parameters.dotation_solidarite_urbaine.attribution.facteur_classement_max,
"dotation_solidarite_urbaine.attribution.facteur_classement_min" : openfisca_parameters.dotation_solidarite_urbaine.attribution.facteur_classement_min,
"dotation_solidarite_urbaine.attribution.plafond_effort_fiscal" : openfisca_parameters.dotation_solidarite_urbaine.attribution.plafond_effort_fiscal,
"dotation_solidarite_urbaine.attribution.poids_quartiers_prioritaires_ville" : openfisca_parameters.dotation_solidarite_urbaine.attribution.poids_quartiers_prioritaires_ville,
"dotation_solidarite_urbaine.attribution.poids_zone_franche_urbaine" : openfisca_parameters.dotation_solidarite_urbaine.attribution.poids_zone_franche_urbaine,
"dotation_solidarite_urbaine.augmentation_montant" : openfisca_parameters.dotation_solidarite_urbaine.augmentation_montant,
"dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_aides_au_logement" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_aides_au_logement,
"dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_logements_sociaux" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_logements_sociaux,
"dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_potentiel_financier" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_potentiel_financier,
"dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_revenu" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.indice_synthetique.poids_revenu,
"dotation_solidarite_urbaine.eligibilite.part_eligible_seuil_bas" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.part_eligible_seuil_bas,
"dotation_solidarite_urbaine.eligibilite.part_eligible_seuil_haut" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.part_eligible_seuil_haut,
"dotation_solidarite_urbaine.eligibilite.seuil_bas_nombre_habitants" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.seuil_bas_nombre_habitants,
"dotation_solidarite_urbaine.eligibilite.seuil_haut_nombre_habitants" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.seuil_haut_nombre_habitants,
"dotation_solidarite_urbaine.eligibilite.seuil_rapport_potentiel_financier" : openfisca_parameters.dotation_solidarite_urbaine.eligibilite.seuil_rapport_potentiel_financier,
"population.plafond_dgf.brackets[0].amount" : openfisca_parameters.population.plafond_dgf.brackets[0].amount,
"population.plafond_dgf.brackets[1].amount" : openfisca_parameters.population.plafond_dgf.brackets[1].amount,
"population.plafond_dgf.brackets[2].amount" : openfisca_parameters.population.plafond_dgf.brackets[2].amount,
"population.plafond_dgf.brackets[1].threshold" : openfisca_parameters.population.plafond_dgf.brackets[1].threshold,
"population.plafond_dgf.brackets[3].threshold" : openfisca_parameters.population.plafond_dgf.brackets[3].threshold
}
parameter: Parameter | None = identified_reform_parameters_2024.get(openfisca_parameter_suffix)
if parameter is None:
parameter = extract_openfisca_parameter(
openfisca_parameters,
openfisca_parameter_suffix
)
return parameter
import logging
import pytest
from openfisca_core.reforms import Reform
from openfisca_france_dotations_locales import CountryTaxBenefitSystem as OpenFiscaFranceDotationsLocales
from leximpact_dotations_back.computing.reform import reform_from_amendement
@pytest.fixture
def model():
return OpenFiscaFranceDotationsLocales()
@pytest.fixture
def caplog(caplog):
caplog.set_level(logging.WARNING)
return caplog
def test_reform_from_amendement_parameter_update(model):
empty_amendement = {}
empty_amendement_model = reform_from_amendement(model, empty_amendement)
assert empty_amendement_model.__class__.__bases__[0] is Reform
assert empty_amendement_model.parameters.dotation_solidarite_rurale.seuil_nombre_habitants(2024) == 10_000
dsr_seuil_amendement = {
"dotation_solidarite_rurale.seuil_nombre_habitants": 5_000
}
dsr_seuil_amendement_model = reform_from_amendement(model, dsr_seuil_amendement)
assert dsr_seuil_amendement_model.parameters.dotation_solidarite_rurale.seuil_nombre_habitants(2024) == 5_000
def test_reform_from_amendement_parameter_name_error(model, caplog):
surprise_amendement = {
"dotation_truc.parametre.mal.nomme": 42
}
surprise_model = reform_from_amendement(model, surprise_amendement)
assert "Paramètre 'dotation_truc.parametre.mal.nomme' introuvable." in caplog.text # via extract_openfisca_parameter(...)
assert surprise_model is not None # devrait avoir le même contenu que 'model'
import pytest
from openfisca_core.parameters import Parameter
from openfisca_france_dotations_locales import CountryTaxBenefitSystem as OpenFiscaFranceDotationsLocales
from leximpact_dotations_back.mapping.reform_parameters import extract_openfisca_parameter, get_openfisca_parameter
@pytest.fixture
def model():
return OpenFiscaFranceDotationsLocales()
def test_extract_openfisca_parameter(model):
simple_parameter_suffix = "dotation_solidarite_rurale.attribution.poids_longueur_voirie"
simple_parameter: Parameter | None = extract_openfisca_parameter(model.parameters, simple_parameter_suffix)
assert simple_parameter is not None
assert simple_parameter(2024) == 0.30
scale_parameter_suffix = "population.plafond_dgf.brackets[0].threshold"
scale_item_parameter : Parameter | None = extract_openfisca_parameter(model.parameters, scale_parameter_suffix)
print(scale_item_parameter)
assert scale_item_parameter is not None
assert scale_item_parameter(2024) == 0
def test_get_openfisca_parameter(model):
known_parameter_suffix = "dotation_solidarite_rurale.augmentation_montant" # in the pre-identified list of parameters
known_parameter: Parameter | None = get_openfisca_parameter(model.parameters, known_parameter_suffix)
assert known_parameter is not None
assert known_parameter(2024) == 150_000_000
unknown_parameter_suffix = "population.plafond_dgf.brackets[0].threshold" # not in the 'identified_reform_parameters_2024' list of parameters
extracted_parameter: Parameter | None = get_openfisca_parameter(model.parameters, unknown_parameter_suffix)
assert extracted_parameter is not None
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment