Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pote #259

Merged
merged 26 commits into from
Aug 9, 2024
Merged

Pote #259

Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,6 @@ openfisca_erfs_fpr.json
.pytest_cache/

figures_directory

# test python
clallemand marked this conversation as resolved.
Show resolved Hide resolved
*.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 emplate du fichier config.ini de openfisca-survey-manager
clallemand marked this conversation as resolved.
Show resolved Hide resolved
# 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 code pour mettre POTE au format de données openfisca-survey-manager et initialisation d'un survey-scenario spécifique à POTE.
clallemand marked this conversation as resolved.
Show resolved Hide resolved
- `/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
Loading