Skip to content

Commit

Permalink
Refacto de réarangement de la réforme aides_jeunes_reform_dynamic (#160)
Browse files Browse the repository at this point in the history
Améliore la compréhension et la lisibilité du code

- Change l’ordre de certaines méthodes ou fonctions.
- Change le nom l'ordre de certaines méthodes ou fonctions.
- Change le nom de certaines variables.
  • Loading branch information
Allan-CodeWorks authored Jun 6, 2023
1 parent 4edc9bb commit fae47fc
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 106 deletions.
230 changes: 131 additions & 99 deletions openfisca_france_local/aides_jeunes_reform.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@


from openfisca_core.populations.population import Population
from openfisca_france.model.prestations.education import (
TypesScolarite, TypesClasse)
from openfisca_france.model.caracteristiques_socio_demographiques.\
logement import TypesCodeInseeRegion
from openfisca_france.model.prestations.education import TypesScolarite, TypesClasse
from openfisca_france.model.caracteristiques_socio_demographiques.logement import TypesCodeInseeRegion
from openfisca_france.model.caracteristiques_socio_demographiques.demographie \
import (RegimeSecuriteSociale, GroupeSpecialitesFormation)

Expand All @@ -30,7 +28,7 @@
'<=': operator.le,
'>': operator.gt,
'>=': operator.ge,
}
}


def is_age_eligible(individu, period, condition):
Expand All @@ -45,28 +43,37 @@ def is_age_eligible(individu, period, condition):

def is_department_eligible(individu: Population, period: Period, condition):
depcom = individu.menage('depcom', period)
eligibilities = [startswith(depcom, code.encode('UTF-8'))
for code in condition['values']]
eligibilities = [
startswith(depcom, code.encode('UTF-8'))
for code in condition['values']
]

return sum(eligibilities) > 0


def is_region_eligible(individu: Population, period: Period, condition):
region = individu.menage('region', period)
eligibilities = [region == TypesCodeInseeRegion(code_region)
for code_region in condition['values']]
eligibilities = [
region == TypesCodeInseeRegion(code_region)
for code_region in condition['values']
]

return sum(eligibilities) > 0


def is_regime_securite_sociale_eligible(individu: Population, period: Period, condition):
regime_securite_sociale = individu('regime_securite_sociale', period)
eligibilities = [regime_securite_sociale == RegimeSecuriteSociale[regime]
for regime in condition['includes']]
eligibilities = [
regime_securite_sociale == RegimeSecuriteSociale[regime]
for regime in condition['includes']
]

return sum(eligibilities) > 0


def is_quotient_familial_eligible(individu: Population, period: Period, condition) -> np.array:

period_divider = 12 if condition["period"] == 'month' else 1
period_divider = 12 if condition['period'] == 'month' else 1
rfr = individu.foyer_fiscal('rfr', period.n_2)
nbptr = individu.foyer_fiscal('nbptr', period.n_2)

Expand All @@ -81,27 +88,35 @@ def is_formation_sanitaire_social_eligible(individu: Population, period: Period,
id_formation_sanitaire_social = GroupeSpecialitesFormation.groupe_330
id_formation_groupe = individu(
'groupe_specialites_formation', period)

return id_formation_groupe == id_formation_sanitaire_social


def is_beneficiaire_rsa_eligible(individu: Population, period: Period, condition: dict) -> np.array:
rsa = individu.famille('rsa', period)

return rsa > 0


def is_annee_etude_eligible(individu: Population, period: Period, condition) -> np.array:
current_year = individu(
'annee_etude', period)
eligibilities = [current_year == TypesClasse[value]
for value in condition['values']]
eligibilities = [
current_year == TypesClasse[value]
for value in condition['values']
]

return sum(eligibilities) > 0


def has_mention_baccalaureat(individu: Population, period: Period, condition) -> np.array:
has_mention = individu(
'mention_baccalaureat', period)
eligibilities = [has_mention == TypesMention[value]
for value in condition['values']]
eligibilities = [
has_mention == TypesMention[value]
for value in condition['values']
]

return sum(eligibilities) > 0


Expand All @@ -112,15 +127,20 @@ def is_boursier(individu: Population, period: Period, condition: dict) -> np.arr
def is_commune_eligible(individu: Population, period: Period, condition: dict) -> np.array:
depcom = individu.menage('depcom', period)
eligible_depcoms = condition['values']
return sum([depcom == eligible_depcom.encode('UTF-8')
for eligible_depcom
in eligible_depcoms])

return sum([
depcom == eligible_depcom.encode('UTF-8')
for eligible_depcom in eligible_depcoms
])


def is_epci_eligible(individu: Population, period: Period, condition: dict) -> np.array:
eligible_epcis: list[str] = condition['values']
eligibilities = [individu.menage(f'menage_dans_epci_siren_{epci}', period)
for epci in eligible_epcis]
eligibilities = [
individu.menage(f'menage_dans_epci_siren_{epci}', period)
for epci in eligible_epcis
]

return sum(eligibilities)


Expand All @@ -129,13 +149,16 @@ def is_taux_incapacite_eligible(individu: Population, period: Period, condition:
'inferieur_50': lambda taux: taux < 0.5,
'entre_50_et_80': lambda taux: np.logical_and(taux >= 0.5, taux < 0.8),
'superieur_ou_egal_80': lambda taux: taux >= 0.8
}
}

taux_incapacite = individu('taux_incapacite', period)
elibible_taux = condition['values']

eligibilities = [evaluates[taux](taux_incapacite)
for taux in elibible_taux]
eligibilities = [
evaluates[taux](taux_incapacite)
for taux in elibible_taux
]

return sum(eligibilities) > 0


Expand Down Expand Up @@ -181,48 +204,47 @@ def is_situation_handicap(individu: Population, period: Period) -> np.array:


def not_implemented_condition(_: Population, __: Period, condition: dict) -> np.array:
raise NotImplementedError(
f'Condition `{condition["type"]}` is not implemented')
raise NotImplementedError(f'Condition `{condition["type"]}` is not implemented')


condition_table = {
"age": is_age_eligible,
"regions": is_region_eligible,
"departements": is_department_eligible,
"quotient_familial": is_quotient_familial_eligible,
"formation_sanitaire_social": is_formation_sanitaire_social_eligible,
"regime_securite_sociale": is_regime_securite_sociale_eligible,
"beneficiaire_rsa": is_beneficiaire_rsa_eligible,
"annee_etude": is_annee_etude_eligible,
"boursier": is_boursier,
"mention_baccalaureat": has_mention_baccalaureat,
"communes": is_commune_eligible,
"epcis": is_epci_eligible,
"taux_incapacite": is_taux_incapacite_eligible,
"attached_to_institution": not_implemented_condition,
}
'age': is_age_eligible,
'regions': is_region_eligible,
'departements': is_department_eligible,
'quotient_familial': is_quotient_familial_eligible,
'formation_sanitaire_social': is_formation_sanitaire_social_eligible,
'regime_securite_sociale': is_regime_securite_sociale_eligible,
'beneficiaire_rsa': is_beneficiaire_rsa_eligible,
'annee_etude': is_annee_etude_eligible,
'boursier': is_boursier,
'mention_baccalaureat': has_mention_baccalaureat,
'communes': is_commune_eligible,
'epcis': is_epci_eligible,
'taux_incapacite': is_taux_incapacite_eligible,
'attached_to_institution': not_implemented_condition,
}


profil_table = {
"enseignement_superieur": is_enseignement_superieur,
"chomeur": is_chomeur,
"apprenti": is_apprenti,
"lyceen": is_lyceen,
"etudiant": is_etudiant,
"stagiaire": is_stagiaire,
"professionnalisation": is_professionnalisation,
"independant": is_actif,
"salarie": is_actif,
"service_civique": is_actif,
"inactif": is_inactif,
"situation_handicap": is_situation_handicap,
}
'enseignement_superieur': is_enseignement_superieur,
'chomeur': is_chomeur,
'apprenti': is_apprenti,
'lyceen': is_lyceen,
'etudiant': is_etudiant,
'stagiaire': is_stagiaire,
'professionnalisation': is_professionnalisation,
'independant': is_actif,
'salarie': is_actif,
'service_civique': is_actif,
'inactif': is_inactif,
'situation_handicap': is_situation_handicap,
}


type_table = {
'float': float,
'bool': bool,
}
}


ConditionEvaluator = collections.namedtuple(
Expand All @@ -231,11 +253,12 @@ def not_implemented_condition(_: Population, __: Period, condition: dict) -> np.
'ProfileEvaluator', ['predicate', 'conditions'])


def build_condition_evaluator_list(
conditions: 'list[dict]') -> 'list[ConditionEvaluator]':
def build_condition_evaluator_list(conditions: 'list[dict]') -> 'list[ConditionEvaluator]':
try:
evaluators = [ConditionEvaluator(condition, condition_table[condition['type']])
for condition in conditions]
evaluators = [
ConditionEvaluator(condition, condition_table[condition['type']])
for condition in conditions
]
except KeyError as e:
raise KeyError(f"Condition: `{(e.args[0])}` is unknown")

Expand All @@ -249,14 +272,16 @@ def build_profil_evaluator(profil: dict) -> ProfileEvaluator:
raise KeyError(f"Profil: `{profil['type']}` is unknown")

conditions = profil.get('conditions', [])
return ProfileEvaluator(predicate,
build_condition_evaluator_list(conditions))

return ProfileEvaluator(predicate, build_condition_evaluator_list(conditions))


def eval_conditions(test_conditions: 'list[ConditionEvaluator]', individu: Population, period: Period) -> np.array:
conditions_results = [
test.evaluator(individu, period, test.condition)
for test in test_conditions
]

def eval_conditions(test_conditions: "list[ConditionEvaluator]",
individu: Population, period: Period) -> np.array:
conditions_results = [test.evaluator(individu, period, test.condition)
for test in test_conditions]
return sum(conditions_results) == len(test_conditions)


Expand All @@ -269,37 +294,39 @@ def eval_profil(profil_evaluator: ProfileEvaluator, individu: Population, period


def generate_variable(benefit: dict):
value_type = type_table[benefit['type']]
variable_type = type_table[benefit['type']]
amount = benefit.get('montant')
conditions_generales_tests = build_condition_evaluator_list(
benefit['conditions_generales'])
eligible_profiles_tests = [build_profil_evaluator(profil)
for profil in benefit["profils"]]
conditions_generales_tests = build_condition_evaluator_list(benefit['conditions_generales'])
eligible_profiles_tests = [
build_profil_evaluator(profil)
for profil in benefit['profils']
]

def compute_amount(eligibilities: np.array):
return amount * eligibilities

def compute_bool(eligibilities: np.array):
return eligibilities

compute_value = compute_amount if value_type == float else compute_bool
compute_value = compute_amount if variable_type == float else compute_bool

def formula(individu: Population, period: Period):
eligibilities = [eval_profil(profil, individu, period)
for profil in eligible_profiles_tests]
eligibilities = [
eval_profil(profil, individu, period)
for profil in eligible_profiles_tests
]
is_profile_eligible = len(eligibilities) == 0 or sum(eligibilities) >= 1

general_eligibilities = eval_conditions(
conditions_generales_tests, individu, period)
general_eligibilities = eval_conditions(conditions_generales_tests, individu, period)

return compute_value(general_eligibilities * is_profile_eligible)

return type(benefit['slug'], (Variable,), {
"value_type": value_type,
"entity": Individu,
"definition_period": MONTH,
"formula": formula,
})
'value_type': variable_type,
'entity': Individu,
'definition_period': MONTH,
'formula': formula,
})


root = '.'
Expand All @@ -312,29 +339,34 @@ def __init__(self, baseline, benefits_folder_path=current_path):
self.benefits_folder_path = benefits_folder_path
super().__init__(baseline)

def extract_benefit_file_content(self, benefit_path):
benefit: dict = yaml.safe_load(open(benefit_path))
benefit['slug'] = benefit_path.split(
'/')[-1].replace('-', '_').split('.')[0]
return benefit

def extract_benefits_paths(self, benefits_folder: str) -> "list[str]":
def isYAMLfile(path: str): return str(path).endswith(
'.yml') or str(path).endswith('.yaml')
liste_fichiers = [
str(benefit) for benefit in Path(benefits_folder).iterdir()
if isYAMLfile(benefit)
]
return liste_fichiers

def apply(self):
try:
benefit_files_paths = self.extract_benefits_paths(
benefit_files_paths: 'list[str]' = self._extract_benefits_paths(
self.benefits_folder_path)
for path in benefit_files_paths:
self.add_variable(generate_variable(
self.extract_benefit_file_content(path)))
self.add_variable(generate_variable(self._extract_benefit_file_content(path)))
except KeyError as e:
raise KeyError(f"{e.args[0]} - Input file: {path}")
raise KeyError(f'{e.args[0]} - Input file: {path}')
except Exception as e:
raise Exception(f'{e.args[0]} in file {path}')

def _extract_benefit_file_content(self, benefit_path: str):
def _slug_from_path(path: str):
return path.split('/')[-1].replace('-', '_').split('.')[0]

benefit: dict = yaml.safe_load(open(benefit_path))
benefit['slug'] = _slug_from_path(benefit_path)

return benefit

def _extract_benefits_paths(self, benefits_folder: str) -> 'list[str]':
def _isYAMLfile(path: str):
return str(path).endswith('.yml') or str(path).endswith('.yaml')

files: 'list[str]' = [
str(benefit)
for benefit in Path(benefits_folder).iterdir()
if _isYAMLfile(benefit)
]

return files
12 changes: 12 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
; E128/133: We prefer hang-closing visual indents
; E251: We prefer `function(x = 1)` over `function(x=1)`
; F403/405: We ignore * imports
; E501: We do not enforce a maximum line length
; W503: We break lines before binary operators (Knuth's style)

[flake8]
hang-closing = true
ignore = E128,E251,F403,F405,E501,W503
docstring-quotes = single
inline-quotes = single
multiline-quotes = single
Loading

0 comments on commit fae47fc

Please sign in to comment.