Skip to content

Commit

Permalink
Merge pull request #259 from openfisca/pote
Browse files Browse the repository at this point in the history
Pote
  • Loading branch information
clallemand authored Aug 9, 2024
2 parents 35ea9cb + eee061f commit 57636b7
Show file tree
Hide file tree
Showing 19 changed files with 1,248 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,5 @@ openfisca_erfs_fpr.json
.pytest_cache/

figures_directory

*.parquet
10 changes: 10 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ stages:
- build_input_data
- diagnostics
- aggregates
- pote
- run_on_all_years
- build_input_data_all
- aggregates_all
Expand Down Expand Up @@ -133,6 +134,15 @@ diagnostics:
tags:
- openfisca

build_and_test_pote:
image: $CI_REGISTRY_IMAGE:latest
script:
- cp ./.gitlab-ci/pote_openfisca_survey_manager_config.ini ~/.config/openfisca-survey-manager/config.ini
- python tests/pote/create_fake_data.py -y 2022 -p /tests/pote/fake_data/raw/
- build-pote -y 2022 -c ~/.config/openfisca-survey-manager/
- pytest tests/pote/test_pote_survey_scenario.py
stage: pote

check-version-and-changelog:
stage: diagnostics
before_script:
Expand Down
17 changes: 17 additions & 0 deletions .gitlab-ci/pote_openfisca_survey_manager_config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Version pote du template du fichier config.ini de openfisca-survey-manager
# pour qu'il fonctionne avec openfisca-france-data


[collections]
collections_directory = /tests/pote/fake_data/output/data_collections
pote = /tests/pote/fake_data/output/data_collections/pote.json

[data]
sas_pote = /
chunks_pote = /
raw_pote = /tests/pote/fake_data/raw/
output_directory = /tests/pote/fake_data/output/
tmp_directory = /tests/pote/fake_data/tmp/

[openfisca_france_data_pote]
errors_path = /tests/pote/fake_data/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

### 3.5.0 [#259](https://github.com/openfisca/openfisca-france-data/pull/259)

* New features
- Ajout du support de la base POTE (données exhaustive de l'IR) et initialisation d'un survey-scenario spécifique à POTE.
- `/openfisca_france_data/pote/`

### 3.4.2 [#258](https://github.com/openfisca/openfisca-france-data/pull/258)

* Technical changes
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ format-style:
autopep8 `git ls-files | grep "\.py$$"`

test: clean check-syntax-errors
pytest --ignore=tests/erfs_fpr/integration
pytest --ignore=tests/erfs_fpr/integration --ignore=tests/pote/

test-local: clean check-syntax-errors
pytest
Expand Down
Empty file.
192 changes: 192 additions & 0 deletions openfisca_france_data/pote/annualisation_variables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
from openfisca_france.model.base import (
FoyerFiscal,
Individu,
YEAR,
Variable,
Reform
)

from numpy import maximum as max_

class AnnualisationVariablesIR(Reform):
name = "Annualisation des variables dans le calcul de l'impôt sur le revenu"
tax_benefit_system_name = "openfisca_france"

def apply(self):

class salaire_imposable(Variable):
value_type = float
unit = 'currency'
cerfa_field = { # (f1aj, f1bj, f1cj, f1dj, f1ej)
0: '1AJ',
1: '1BJ',
2: '1CJ',
3: '1DJ',
4: '1EJ',
}
entity = Individu
label = 'Salaires imposables'
reference = 'https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000042683657'
definition_period = YEAR

class retraite_imposable(Variable):
unit = 'currency'
value_type = float
cerfa_field = {
0: '1AS',
1: '1BS',
2: '1CS',
3: '1DS',
4: '1ES',
}
entity = Individu
label = 'Retraites au sens strict imposables (rentes à titre onéreux exclues)'
reference = 'http://vosdroits.service-public.fr/particuliers/F415.xhtml'
definition_period = YEAR

class chomage_imposable(Variable):
value_type = float
unit = 'currency'
cerfa_field = {
0: '1AP',
1: '1BP',
2: '1CP',
3: '1DP',
4: '1EP',
}
entity = Individu
label = 'Allocations chômage imposables'
reference = 'http://www.insee.fr/fr/methodes/default.asp?page=definitions/chomage.htm'
definition_period = YEAR


baseline_revenus_capitaux_pfu = self.baseline.get_variable(
"revenus_capitaux_prelevement_forfaitaire_unique_ir"
)
formula_revenus_capitaux_pfu = (
baseline_revenus_capitaux_pfu.get_formula("2021-01-01")
)

class revenus_capitaux_prelevement_forfaitaire_unique_ir(Variable):
value_type = float
entity = FoyerFiscal
label = 'Revenus des valeurs et capitaux mobiliers soumis au prélèvement forfaitaire unique (partie impôt sur le revenu)'
definition_period = YEAR

def formula_2021_01_01(foyer_fiscal, period, parameters):
return formula_revenus_capitaux_pfu(foyer_fiscal, period.first_month, parameters) * 12


baseline_revenus_capitaux_prelevement_bareme = self.baseline.get_variable(
"revenus_capitaux_prelevement_bareme"
)
formula_revenus_capitaux_prelevement_bareme = (
baseline_revenus_capitaux_prelevement_bareme.get_formula("2021-01-01")
)

class revenus_capitaux_prelevement_bareme(Variable):
value_type = float
entity = FoyerFiscal
label = 'Revenus du capital imposés au barème (montants bruts)'
reference = 'http://bofip.impots.gouv.fr/bofip/3775-PGP'
definition_period = YEAR

def formula_2021_01_01(foyer_fiscal, period, parameters):
formula_revenus_capitaux_prelevement_bareme(foyer_fiscal, period.first_month, parameters) * 12

baseline_revenus_capitaux_prelevement_liberatoire = self.baseline.get_variable(
"revenus_capitaux_prelevement_liberatoire"
)
formula_revenus_capitaux_prelevement_liberatoire = (
baseline_revenus_capitaux_prelevement_liberatoire.get_formula("2021-01-01")
)

class revenus_capitaux_prelevement_liberatoire(Variable):
value_type = float
entity = FoyerFiscal
label = 'Revenu du capital imposé au prélèvement libératoire (montants bruts)'
reference = 'http://bofip.impots.gouv.fr/bofip/3817-PGP'
definition_period = YEAR

def formula_2021_01_01(foyer_fiscal, period, parameters):
return formula_revenus_capitaux_prelevement_liberatoire(foyer_fiscal, period, parameters) * 12

class revenus_individuels(Variable):
value_type = float
entity = FoyerFiscal
label = "Somme des revenus_individuels utilisés pour l'imputation des revenus du capital"
definition_period = YEAR

def formula(foyer_fiscal, period):
revenu_assimile_salaire_i = foyer_fiscal.members("revenu_assimile_salaire", period)
revenu_assimile_salaire = foyer_fiscal.sum(revenu_assimile_salaire_i)
revenu_assimile_pension_i = foyer_fiscal.members("revenu_assimile_pension", period)
revenu_assimile_pension = foyer_fiscal.sum(revenu_assimile_pension_i)
rpns_imposables_i = foyer_fiscal.members("rpns_imposables", period)
rpns_imposables = foyer_fiscal.sum(rpns_imposables_i)

return max_(revenu_assimile_salaire + revenu_assimile_pension + rpns_imposables, 0)

baseline_rfr = self.baseline.get_variable(
"rfr"
)
formula_rfr = (
baseline_rfr.get_formula()
)

class rfr(Variable):
value_type = float
entity = FoyerFiscal
label = "Revenu fiscal de référence"
definition_period = YEAR

def formula(foyer_fiscal, period, parameters):
rfr = formula_rfr(foyer_fiscal, period, parameters)
return max_(rfr, 0)

class salaire_imposable_large(Variable):
value_type = float
entity = Individu
label = "Salaires imposables au sens large, mais sans le chomage imposable"
definition_period = YEAR

def formula(individu, period):
revenu_assimile_salaire = individu("revenu_assimile_salaire", period)
chomage_imposable = individu("chomage_imposable", period)

return revenu_assimile_salaire - chomage_imposable

class rfr_par_part(Variable):
value_type = float
entity = FoyerFiscal
label = "Revenu fiscal de référence par part"
definition_period = YEAR

def formula(foyer_fiscal,period):
rfr = foyer_fiscal("rfr", period)
nbptr = foyer_fiscal("nbptr", period)

return rfr / nbptr


variables_annualisees = [
salaire_imposable,
retraite_imposable,
chomage_imposable,
revenus_capitaux_prelevement_forfaitaire_unique_ir,
revenus_capitaux_prelevement_bareme,
revenus_capitaux_prelevement_liberatoire,
rfr
]

variables_ajout = [
revenus_individuels,
salaire_imposable_large,
rfr_par_part
]

for variable in variables_annualisees:
self.update_variable(variable)

for variable in variables_ajout:
self.add_variable(variable)
130 changes: 130 additions & 0 deletions openfisca_france_data/pote/input_data_builder/analyse_variables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from openfisca_france_data.utils import build_cerfa_fields_by_variable
from openfisca_core.simulation_builder import SimulationBuilder
from openfisca_france_data.pote.annualisation_variables import AnnualisationVariablesIR
import openfisca_france



def liens_variables(year):
'''
Pour une année year de simulation de l'impôt sur le revenu renvoi :
- les variables composées d'input variables qui ne sont utilisée que dans cette variables
- la liste des input variables qui composent chaque variable du point précédent
Ces listes vont être utilisées pour faire des pré calcul dans l'impôt sur le revenu afin de limiter le nombre de colonnes.
'''

var_foyer_fiscal = list()
cerfa_var_dict = build_cerfa_fields_by_variable(year = year)

for openfisca_var, cerfa in cerfa_var_dict.items():
if len(cerfa) == 1:
var_foyer_fiscal.append(openfisca_var)

cas_type = {
"individus": {
"ind0": {
"date_naissance": {'ETERNITY':'1970-01-01'},
"salaire_imposable": {f"{year}":0},
"retraite_imposable": {f"{year}":0},
"chomage_imposable": {f"{year}":0}
}
}
}
tax_benefit_system = AnnualisationVariablesIR(openfisca_france.FranceTaxBenefitSystem())
simulation = SimulationBuilder()
simulation = simulation.build_from_entities(tax_benefit_system, cas_type)
simulation.trace = True
simulation.calculate('irpp_economique', year)
lines = simulation.tracer.computation_log.lines()
text = list()
for line in lines:
line = line.split(">>")
if len(line)==2:
text.append(line[0])

indented_variables = list()
for line in text:
split_line = line.split("<")
assert len(split_line) == 2
assert split_line[1].startswith(str(year)), f"{split_line} doesn't start with {year}"
indented_variables += [split_line[0]]

indent_max = 0
for line in indented_variables:
level = (len(line) - len(line.lstrip()))/2
if level > indent_max:
indent_max = level

tot = dict()
for max in reversed(range(int(indent_max))):
arborescence = dict()
arborescence_i = dict()
for line in indented_variables:
indent = (len(line) - len(line.lstrip()))/2
if indent < max:
arborescence_i[indent] = line.lstrip()
elif indent == max:
arborescence[line.lstrip()] = arborescence_i
tot[max] = arborescence

i = 0
dictionnaire_parent_enfants = dict()

for max in reversed(range(2, int(indent_max + 1))):
for line in indented_variables:
level = (len(line) - len(line.lstrip()))/2
if level == max - 1:
variable = line.lstrip()
rang_ident_1 = i
if level == max:
if i == rang_ident_1 + 1:
dictionnaire_parent_enfants[variable] = [line.lstrip()]
else:
dictionnaire_parent_enfants[variable] += [line.lstrip()]
i += 1
variables = list(set([line.strip() for line in indented_variables]))

dictionnaire_enfant_parents = dict()
for variable in variables:
dictionnaire_enfant_parents[variable] = []

for parent, enfants in dictionnaire_parent_enfants.items():
for enfant in enfants:
dictionnaire_enfant_parents[enfant] += [parent]

unique_appel = list()
for enfant, parents in dictionnaire_enfant_parents.items():
if len(parents) == 1:
unique_appel += [enfant]
assert len(unique_appel) == len(list(set(unique_appel))), "Il y a des doublons dans les appels uniques"

variables_to_compute = list()
for case_fiscal in var_foyer_fiscal:
if case_fiscal in unique_appel:
parent = dictionnaire_enfant_parents[case_fiscal]
assert len(parent) == 1
if parent[0] not in variables_to_compute: # si déjà dedans c'est qu'on a déjà checké que tous les enfants étaient bien appelés qu'une seule fois
enfants_parent = dictionnaire_parent_enfants[parent[0]]
unique_appel_enfants = [e for e in enfants_parent if e in unique_appel]
only_case_fiscal = [c for c in enfants_parent if c in var_foyer_fiscal]
if len(enfants_parent) == len(unique_appel_enfants):
if len(enfants_parent) == len(only_case_fiscal):
variables_to_compute += [parent[0]]

variables_to_compute = [v for v in variables_to_compute if len(dictionnaire_parent_enfants[v])>1] # cela ne sert à rien de calculer si qu'une variable, aucun gain de colonnes
enfants_tot = list()
for var in variables_to_compute:
enfants_tot += dictionnaire_parent_enfants[var]

for enfant in dictionnaire_parent_enfants['duflot_pinel_denormandie_metropole'] + dictionnaire_parent_enfants['duflot_pinel_denormandie_om']:
assert enfant.startswith("f7")
ref = {'duflot_pinel_denormandie_metropole', 'duflot_pinel_denormandie_om'}
assert set(dictionnaire_enfant_parents[enfant]).issubset(ref), f"{enfant}"
enfants_tot = enfants_tot + dictionnaire_parent_enfants['duflot_pinel_denormandie_metropole']
enfants_tot = enfants_tot + dictionnaire_parent_enfants['duflot_pinel_denormandie_om']
variables_to_compute += ['duflot_pinel_denormandie_metropole', 'duflot_pinel_denormandie_om']

return variables_to_compute, enfants_tot, dictionnaire_enfant_parents, dictionnaire_parent_enfants
Loading

0 comments on commit 57636b7

Please sign in to comment.