diff --git a/docs/geps/gep-04.md b/docs/geps/gep-04.md index 0942b5b557..4d985f0667 100644 --- a/docs/geps/gep-04.md +++ b/docs/geps/gep-04.md @@ -233,7 +233,7 @@ functions of the taxes and transfer system should define a dictionary `aggregation_specs` at the module level. This dictionary must specify the aggregated columns as keys and the AggregateByGroupSpec data class as values. The data class specifies the `source` (i.e. the column which is being aggregated) and the aggregation -method `aggr`. +method `agg`. For example, in `household_characteristics.py`, we could have: @@ -241,8 +241,8 @@ For example, in `household_characteristics.py`, we could have: from ttsim.aggregation import AggregateByGroupSpec aggregation_specs = { - "anzahl_kinder_hh": AggregateByGroupSpec(source="familie__kind", aggr="sum"), - "anzahl_personen_hh": AggregateByGroupSpec(aggr="count"), + "anzahl_kinder_hh": AggregateByGroupSpec(source="familie__kind", agg="sum"), + "anzahl_personen_hh": AggregateByGroupSpec(agg="count"), } ``` @@ -293,7 +293,7 @@ from ttsim.aggregation import AggregateByGroupSpec aggregation_specs = { "kindergeld__betrag_m_bg": AggregateByGroupSpec( source="kindergeld__betrag_m", - aggr="sum" + agg="sum" ) } ``` @@ -312,12 +312,12 @@ The implementation is similar to aggregations to the level of groupings: In orde specify new aggregation functions, scripts with functions of the taxes and transfer system should define a dictionary `aggregation_specs` at the module level. This dictionary must specify the aggregated columns as keys and the `AggregateByPIDSpec` data -class as values. The class specifies the `source`, `p_id_to_aggregate_by`, and `aggr`. -If `aggr` is `count`, `source` is not needed. +class as values. The class specifies the `source`, `p_id_to_aggregate_by`, and `agg`. If +`agg` is `count`, `source` is not needed. The key `source` specifies which column is the source of the aggregation operation. The key `p_id_to_aggregate_by` specifies the column that indicates to which `p_id` the -values in `source` should be ascribed to. The key `aggr` gives the aggregation method. +values in `source` should be ascribed to. The key `agg` gives the aggregation method. For example, in `kindergeld.py`, we could have: @@ -326,7 +326,7 @@ aggregation_specs = { "kindergeld__anzahl_ansprüche": AggregateByPIDSpec( p_id_to_aggregate_by="kindergeld__p_id_empfänger", source="kindergeld__grundsätzlich_anspruchsberechtigt", - aggr="sum", + agg="sum", ), } ``` diff --git a/docs/gettsim_objects/input_variables.md b/docs/gettsim_objects/input_variables.md deleted file mode 100644 index 9b06f13729..0000000000 --- a/docs/gettsim_objects/input_variables.md +++ /dev/null @@ -1,660 +0,0 @@ -(input_variables)= - -# Basic input variables - -The table below gives an overview of all variables needed to run GETTSIM completely. -Note that the variables with \_hh at the end, have to be constant over the whole -household. - -(hh_id)= - -## `hh_id` - -Household identifier following §5 WoGG - -Type: int - -(familie__kind)= - -## `familie__kind` - -Dependent child living with parents - -Type: bool - -(einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m)= - -## `einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m` - -Monthly wage - -Type: float - -(alter)= - -## `alter` - -Individual's age. - -Type: int - -(weiblich)= - -## `weiblich` - -Female - -Type: bool - -(sozialversicherung__rente__bezieht_rente)= - -## `sozialversicherung__rente__bezieht_rente` - -Draws public pension benefits. - -Type: bool - -(familie__alleinerziehend)= - -## `familie__alleinerziehend` - -Single parent - -Type: bool - -(einkommensteuer__gemeinsam_veranlagt)= - -## `einkommensteuer__gemeinsam_veranlagt` - -Taxes are filed jointly - -Type: bool - -(familie__p_id_elternteil_1)= - -## `familie__p_id_elternteil_1` - -Identifier of the first parent - -Type: int - -(familie__p_id_elternteil_2)= - -## `familie__p_id_elternteil_2` - -Identifier of the second parent - -Type: int - -(familie__p_id_ehepartner)= - -## `familie__p_id_ehepartner` - -Identifier of married partner - -Type: int - -(arbeitslosengeld_2__p_id_einstandspartner)= - -## `arbeitslosengeld_2__p_id_einstandspartner` - -Identifier of Einstandspartner - -Type: int - -(arbeitslosengeld_2__p_id_einstandspartner)= - -## `kindergeld__p_id_empfänger` - -Identifier of person that claims Kindergeld for the particular child - -Type: int - -(wohnort_ost)= - -## `wohnort_ost` - -Living in former East Germany - -Type: bool - -(sozialversicherung__kranken__beitrag__privat_versichert)= - -## `sozialversicherung__kranken__beitrag__privat_versichert` - -In private health insurance - -Type: bool - -(einkommensteuer__abzüge__beitrag_private_rentenversicherung_m)= - -## `einkommensteuer__abzüge__beitrag_private_rentenversicherung_m` - -Monthly private pension contribution - -Type: float - -(kindergeld__in_ausbildung)= - -## `kindergeld__in_ausbildung` - -Employment status “in education” - -Type: bool - -(einkommensteuer__einkünfte__ist_selbstständig)= - -## `einkommensteuer__einkünfte__ist_selbstständig` - -Self-employed (main profession) - -Type: bool - -(sozialversicherung__pflege__beitrag__hat_kinder)= - -## `sozialversicherung__pflege__beitrag__hat_kinder` - -Has kids (incl. not in hh) - -Type: bool - -(einkommensteuer__abzüge__betreuungskosten_m)= - -## `einkommensteuer__abzüge__betreuungskosten_m` - -Monthly childcare expenses for a particular child under the age of 14 - -Type: float - -(einkommensteuer__abzüge__p_id_betreuungskosten_träger)= - -## `einkommensteuer__abzüge__p_id_betreuungskosten_träger` - -Identifier of the person who paid childcare expenses. - -Type: float - -(einkommensteuer__einkünfte__sonstige__betrag_m)= - -## `einkommensteuer__einkünfte__sonstige__betrag_m` - -Additional income: includes private and public transfers that are not yet implemented in -GETTSIM (e.g., BAföG, Kriegsopferfürsorge) - -Type: float - -(einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m)= - -## `einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m` - -Monthly income from self-employment - -Type: float - -(einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m)= - -## `einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m` - -Monthly rental income net of deductions - -Type: float - -(einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m)= - -## `einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m` - -Monthly capital income - -Type: float - -(wohnen__bruttokaltmiete_m_hh)= - -## `wohnen__bruttokaltmiete_m_hh` - -Monthly rent expenses for household - -Type: float - -(wohnen__heizkosten_m_hh)= - -## `wohnen__heizkosten_m_hh` - -Monthly heating expenses for household - -Type: float - -(wohnen__wohnfläche_hh)= - -## `wohnen__wohnfläche_hh` - -Size of household dwelling in square meters - -Type: float - -(wohnen__bewohnt_eigentum_hh)= - -## `wohnen__bewohnt_eigentum_hh` - -Owner-occupied housing - -Type: bool - -(arbeitsstunden_w)= - -## `arbeitsstunden_w` - -Weekly working hours of individual - -Type: float - -(elterngeld__claimed)= - -## `elterngeld__claimed` - -Individual claims Elterngeld - -Type: bool - -(elterngeld__nettoeinkommen_vorjahr_m)= - -## `elterngeld__nettoeinkommen_vorjahr_m` - -Approximation of the net wage in the 12 months before birth of youngest child (according -to simplified calculation rules). You may let GETTSIM compute this variable via the -`elterngeld__nettoeinkommen_approximation_m` target in a separate run, which would -typically be for the previous calendar year. - -Type: float - -(elterngeld__zu_versteuerndes_einkommen_vorjahr_y_sn)= - -## `elterngeld__zu_versteuerndes_einkommen_vorjahr_y_sn` - -Taxable income in the calendar year prior to the youngest child's birth year. You may -let GETTSIM compute this variable via the -`einkommensteuer__zu_versteuerndes_einkommen_y_sn` target in a separate run, which would -typically be for the previous calendar year. - -Type: float - -(einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_vorjahr_m)= - -## `einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_vorjahr_m` - -Gross wage in the previous year - -Type: float - -(geburtstag)= - -## `geburtstag` - -Day of birth (within month) - -Type: int - -(geburtsmonat)= - -## `geburtsmonat` - -Month of birth - -Type: int - -(geburtsjahr)= - -## `geburtsjahr` - -Year of birth - -Type: int - -(sozialversicherung__rente__jahr_renteneintritt)= - -## `sozialversicherung__rente__jahr_renteneintritt` - -Year of retirement - -Type: int - -(sozialversicherung__rente__monat_renteneintritt)= - -## `sozialversicherung__rente__monat_renteneintritt` - -Month of retirement - -Type: int - -(elterngeld__bisherige_bezugsmonate)= - -## `elterngeld__bisherige_bezugsmonate` - -Number of months the individual received Elterngeld for the current youngest child. - -Type: int - -(behinderungsgrad)= - -## `behinderungsgrad` - -Handicap degree (between 0 and 100) - -Type: int - -(schwerbehindert_grad_g)= - -## `schwerbehindert_grad_g` - -Severerly handicapped, with flag "G" - -Type: bool - -(wohngeld__mietstufe)= - -## `wohngeld__mietstufe` - -Level of rents in city (1: low, 3: average) - -Type: int - -(wohnen__baujahr_immobilie_hh)= - -## `wohnen__baujahr_immobilie_hh` - -Construction year of dwelling - -Type: int - -(vermögen)= - -## `vermögen` - -Assets for means testing on individual -level.{ref}`See this page for more details. ` - -Type: float - -(sozialversicherung__rente__entgeltpunkte_west)= - -## `sozialversicherung__rente__entgeltpunkte_west` - -Earnings points for pension claim accumulated in western states - -Type: float - -(sozialversicherung__rente__entgeltpunkte_ost)= - -## `sozialversicherung__rente__entgeltpunkte_ost` - -Earnings points for pension claim accumulated in eastern states - -Type: float - -(sozialversicherung__rente__grundrente__grundrentenzeiten_monate)= - -## `sozialversicherung__rente__grundrente__grundrentenzeiten_monate` - -Number of months determining eligibility for Grundrente. - -Type: int - -(sozialversicherung__rente__grundrente__bewertungszeiten_monate)= - -## `sozialversicherung__rente__grundrente__bewertungszeiten_monate` - -Number of months determining Grundrentepayments - -Type: int - -(sozialversicherung__rente__grundrente__mean_entgeltpunkte)= - -## `sozialversicherung__rente__grundrente__mean_entgeltpunkte` - -Average `entgeltpunkte` during -`sozialversicherung__rente__grundrente__bewertungszeiten_monate` - -Type: float - -(sozialversicherung__rente__private_rente_betrag_m)= - -## `sozialversicherung__rente__private_rente_betrag_m` - -Amount of monthly private pension - -Type: float - -(sozialversicherung__rente__pflichtbeitragsmonate)= - -## `sozialversicherung__rente__pflichtbeitragsmonate` - -Total months of mandatory pension insurance contributions - -Type: float - -(sozialversicherung__rente__freiwillige_beitragsmonate)= - -## `sozialversicherung__rente__freiwillige_beitragsmonate` - -Total months of voluntary pensioninsurance contributions - -Type: float - -(sozialversicherung__rente__monate_in_mutterschutz)= - -## `sozialversicherung__rente__monate_in_mutterschutz` - -Total months of maternal protections - -Type: float - -(sozialversicherung__rente__monate_in_arbeitsunfähigkeit)= - -## `sozialversicherung__rente__monate_in_arbeitsunfähigkeit` - -Total months of sickness, rehabilitation,measures for worklife participation(Teilhabe) - -Type: float - -(sozialversicherung__rente__krankheitszeiten_ab_16_bis_24_monate)= - -## `sozialversicherung__rente__krankheitszeiten_ab_16_bis_24_monate` - -Months of sickness between age 16 and 24 - -Type: float - -(sozialversicherung__rente__monate_in_arbeitslosigkeit)= - -## `sozialversicherung__rente__monate_in_arbeitslosigkeit` - -Total months of unemployment (registered) - -Type: float - -(sozialversicherung__rente__monate_in_ausbildungssuche)= - -## `sozialversicherung__rente__monate_in_ausbildungssuche` - -Total months of apprenticeship search - -Type: float - -(sozialversicherung__rente__monate_in_schulausbildung)= - -## `sozialversicherung__rente__monate_in_schulausbildung` - -Months of schooling (incl college, unifrom age 17, max. 8 years) - -Type: float - -(sozialversicherung__rente__monate_mit_bezug_entgeltersatzleistungen_wegen_arbeitslosigkeit)= - -## `sozialversicherung__rente__monate_mit_bezug_entgeltersatzleistungen_wegen_arbeitslosigkeit` - -Total months of unemployment (only timeof Entgeltersatzleistungen, not ALGII),i.e. -Arbeitslosengeld, Unterhaltsgeld, Übergangsgeld - -Type: float - -(sozialversicherung__rente__monate_geringfügiger_beschäftigung)= - -## `sozialversicherung__rente__monate_geringfügiger_beschäftigung` - -Total months of marginal employment (w/o mandatory contributions) - -Type: float - -(sozialversicherung__rente__ersatzzeiten_monate)= - -## `sozialversicherung__rente__ersatzzeiten_monate` - -Total months during military, persecution/escape, internment, and consecutive sickness - -Type: float - -(sozialversicherung__rente__kinderberücksichtigungszeiten_monate)= - -## `sozialversicherung__rente__kinderberücksichtigungszeiten_monate` - -Total months of childcare till age 10 - -Type: float - -(sozialversicherung__rente__pflegeberücksichtigungszeiten_monate)= - -## `sozialversicherung__rente__pflegeberücksichtigungszeiten_monate` - -Total months of home care provision (01.01.1992-31.03.1995) - -Type: float - -(sozialversicherung__rente__altersrente__für_frauen__pflichtsbeitragsjahre_ab_alter_40)= - -## `sozialversicherung__rente__altersrente__für_frauen__pflichtsbeitragsjahre_ab_alter_40` - -Total years of mandatory contributions after age 40 - -Type: float - -(sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__pflichtbeitragsjahre_8_von_10)= - -## `sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__pflichtbeitragsjahre_8_von_10` - -Has at least 8 contribution years in past 10 years - -Type: bool - -(sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__arbeitslos_für_1_jahr_nach_alter_58_ein_halb)= - -## `sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__arbeitslos_für_1_jahr_nach_alter_58_ein_halb` - -Has been unemployed at least 1 year after age 58.5 - -Type: bool - -(sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__vertrauensschutz_1997)= - -## `sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__vertrauensschutz_1997` - -Is covered by Vertrauensschutz rules for the Altersrente wegen Arbeitslosigkeit -implemented in 1997 (§ 237 SGB VI Abs. 4). - -Type: bool - -(sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__vertrauensschutz_2004)= - -## `sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__vertrauensschutz_2004` - -Is covered by Vertrauensschutz rules for the Altersrente wegen Arbeitslosigkeit enacted -in July 2004 (§ 237 SGB VI Abs. 5). - -Type: bool - -(sozialversicherung__rente__altersrente__höchster_bruttolohn_letzte_15_jahre_vor_rente_y)= - -## `sozialversicherung__rente__altersrente__höchster_bruttolohn_letzte_15_jahre_vor_rente_y` - -Highest gross income from regular employment in the last 15 years before pension benefit -claiming. Relevant to determine pension benefit deductions for retirees in early -retirement. - -(arbeitslosengeld_2__arbeitslosengeld_2_bezug_im_vorjahr)= - -## `arbeitslosengeld_2__arbeitslosengeld_2_bezug_im_vorjahr` - -Received Bürgergeld in previous year - -Type: bool - -(sozialversicherung__arbeitslosen__anwartschaftszeit)= - -## `sozialversicherung__arbeitslosen__anwartschaftszeit` - -At least 12 months of unemployment contributions in the 30 months before claiming -unemployment insurance - -Type: bool - -(sozialversicherung__arbeitslosen__arbeitssuchend)= - -## `sozialversicherung__arbeitslosen__arbeitssuchend` - -Looking for employment - -Type: bool - -(sozialversicherung__arbeitslosen__monate_durchgängigen_bezugs_von_arbeitslosengeld)= - -## `sozialversicherung__arbeitslosen__monate_durchgängigen_bezugs_von_arbeitslosengeld` - -Number of months the individual already receives Arbeitslosengeld without interruption. - -Type: float - -(sozialversicherung__arbeitslosen__monate_sozialversicherungspflichtiger_beschäftigung_in_letzten_5_jahren)= - -## `sozialversicherung__arbeitslosen__monate_sozialversicherungspflichtiger_beschäftigung_in_letzten_5_jahren` - -Months of subjection to compulsory insurance in the 5 years before claiming unemployment -insurance - -Type: float - -## `unterhalt__anspruch_m` - -Monthly gross child alimony payments to be received by the child as determined by the -court. - -Type: float - -## `unterhalt__tatsächlich_erhaltener_betrag_m` - -Child alimony payments the child actually receives. - -Type: float - -(lohnsteuer__steuerklasse)= - -## `lohnsteuer__steuerklasse` - -Tax Bracket (1 to 5) for withholding tax - -Type: int - -## `erziehungsgeld__budgetsatz` - -Applied for "Budgetsatz" of parental leave benefit - -Type: bool - -## `sozialversicherung__rente__erwerbsminderung__voll_erwerbsgemindert` - -Unable to provide more than 3 hours of market labor per day. - -Type: bool - -## `sozialversicherung__rente__erwerbsminderung__teilweise_erwerbsgemindert` - -Able to provide at least 3 but no more than 6 hours of market labor per day. - -Type: bool diff --git a/docs/tutorials/policy_functions.ipynb b/docs/tutorials/policy_functions.ipynb index d00d41ea06..e515e5ea27 100644 --- a/docs/tutorials/policy_functions.ipynb +++ b/docs/tutorials/policy_functions.ipynb @@ -355,8 +355,8 @@ "\n", "```python\n", "aggregate_by_group_specs_tree = {\n", - " \"anzahl_erwachsene_fg\": {\"source\": \"familie__erwachsen\", \"aggr\": \"sum\"},\n", - " \"anz_personen_hh\": {\"aggr\": \"count\"},\n", + " \"anzahl_erwachsene_fg\": {\"source\": \"familie__erwachsen\", \"agg\": \"sum\"},\n", + " \"anz_personen_hh\": {\"agg\": \"count\"},\n", "}\n", "```\n", "\n", @@ -371,7 +371,7 @@ " \"kindergeld__anzahl_ansprüche\": {\n", " \"p_id_to_aggregate_by\": \"kindergeld__p_id_empfänger\",\n", " \"source\": \"kindergeld__grundsätzlich_anspruchsberechtigt\",\n", - " \"aggr\": \"sum\",\n", + " \"agg\": \"sum\",\n", " },\n", "}\n", "```\n", diff --git a/pixi.lock b/pixi.lock index fdb3d75917..c0c9d7cf18 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5610,8 +5610,8 @@ packages: timestamp: 1694400856979 - pypi: . name: gettsim - version: 0.7.1.dev106+g8c224365.d20250403 - sha256: c500019b58e8256c35e138a3cfbd01672a92164aafce28adb21f09b687c51bd0 + version: 0.7.1.dev144+gfa847097.d20250410 + sha256: aaa3c4836fc1c1ca8d25c593772e0435ef08e8a46757d9335242f26995636a30 requires_dist: - astor - dags>=0.3.0 diff --git a/pyproject.toml b/pyproject.toml index 2d8421b155..c0f15261fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -219,6 +219,7 @@ extend-ignore = [ "PLC2401", # Allow non-ASCII characters in variable names. "PLC2403", # Allow non-ASCII function names for imports. "N999", # Allow non-ASCII characters in file names. + "PLR0913", # Too many arguments in function definition. # Things we are not sure we want # ============================== diff --git a/src/_gettsim/arbeitslosengeld_2/einkommen.py b/src/_gettsim/arbeitslosengeld_2/einkommen.py index 31a034d213..49106b3506 100644 --- a/src/_gettsim/arbeitslosengeld_2/einkommen.py +++ b/src/_gettsim/arbeitslosengeld_2/einkommen.py @@ -112,9 +112,9 @@ def nettoeinkommen_vor_abzug_freibetrag_m( @policy_function() -def bruttoeinkommen_m( # noqa: PLR0913 +def bruttoeinkommen_m( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, - einkommensteuer__einkünfte__sonstige__betrag_m: float, + einkommensteuer__einkünfte__sonstige__ohne_renten_m: float, einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float, einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float, einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m: float, @@ -131,8 +131,8 @@ def bruttoeinkommen_m( # noqa: PLR0913 ---------- einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m See basic input variable :ref:`hh_id `. - einkommensteuer__einkünfte__sonstige__betrag_m - See basic input variable :ref:`einkommensteuer__einkünfte__sonstige__betrag_m `. + einkommensteuer__einkünfte__sonstige__ohne_renten_m + See basic input variable :ref:`einkommensteuer__einkünfte__sonstige__ohne_renten_m `. einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m See basic input variable :ref:`einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m `. einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m @@ -155,7 +155,7 @@ def bruttoeinkommen_m( # noqa: PLR0913 """ out = ( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m - + einkommensteuer__einkünfte__sonstige__betrag_m + + einkommensteuer__einkünfte__sonstige__ohne_renten_m + einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m + einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m + einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m @@ -169,7 +169,7 @@ def bruttoeinkommen_m( # noqa: PLR0913 @policy_function(end_date="2005-09-30") -def nettoquote_m( # noqa: PLR0913 +def nettoquote_m( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, einkommensteuer__betrag_m_sn: float, solidaritätszuschlag__betrag_m_sn: float, diff --git a/src/_gettsim/arbeitslosengeld_2/group_by_ids.py b/src/_gettsim/arbeitslosengeld_2/group_by_ids.py index 2e83a686bf..cb2a47f6ea 100644 --- a/src/_gettsim/arbeitslosengeld_2/group_by_ids.py +++ b/src/_gettsim/arbeitslosengeld_2/group_by_ids.py @@ -6,50 +6,64 @@ # TODO(@MImmesberger): Many of these keys can go once we have `_eg` for SGB XII. # https://github.com/iza-institute-of-labor-economics/gettsim/issues/738 -aggregation_specs = { - "anzahl_erwachsene_fg": AggregateByGroupSpec( +aggregation_specs = ( + AggregateByGroupSpec( + target="anzahl_erwachsene_fg", source="familie__erwachsen", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_kinder_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_kinder_fg", source="familie__kind", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_kinder_bis_6_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_kinder_bis_6_fg", source="familie__kind_bis_6", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_kinder_bis_15_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_kinder_bis_15_fg", source="familie__kind_bis_15", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_erwachsene_bg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_erwachsene_bg", source="familie__erwachsen", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_kinder_bg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_kinder_bg", source="familie__kind", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_personen_bg": AggregateByGroupSpec(aggr=AggregationType.COUNT), - "anzahl_kinder_bis_17_bg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_personen_bg", source=None, agg=AggregationType.COUNT + ), + AggregateByGroupSpec( + target="anzahl_kinder_bis_17_bg", source="familie__kind_bis_17", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "alleinerziehend_bg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="alleinerziehend_bg", source="familie__alleinerziehend", - aggr=AggregationType.ANY, + agg=AggregationType.ANY, ), - "anzahl_erwachsene_eg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_erwachsene_eg", source="familie__erwachsen", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_kinder_eg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_kinder_eg", source="familie__kind", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, + ), + AggregateByGroupSpec( + target="anzahl_personen_eg", source=None, agg=AggregationType.COUNT ), - "anzahl_personen_eg": AggregateByGroupSpec(aggr=AggregationType.COUNT), -} +) @group_by_function() @@ -82,7 +96,7 @@ def bg_id( @group_by_function() -def fg_id( # noqa: PLR0913 PLR0912 +def fg_id( # noqa: PLR0912 p_id_einstandspartner: numpy.ndarray[int], p_id: numpy.ndarray[int], hh_id: numpy.ndarray[int], diff --git a/src/_gettsim/arbeitslosengeld_2/inputs.py b/src/_gettsim/arbeitslosengeld_2/inputs.py new file mode 100644 index 0000000000..ff7dffae00 --- /dev/null +++ b/src/_gettsim/arbeitslosengeld_2/inputs.py @@ -0,0 +1,21 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input(start_date="2023-01-01") +def bezug_im_vorjahr() -> bool: + """Received Arbeitslosengeld II / Bürgergeld in previous year.""" + + +# TODO(@MImmesberger): Remove input variable eigenbedarf_gedeckt once +# Bedarfsgemeinschaften are fully endogenous +# https://github.com/iza-institute-of-labor-economics/gettsim/issues/763 +@policy_input(start_date="2023-01-01") +def eigenbedarf_gedeckt() -> bool: + """Received Arbeitslosengeld II / Bürgergeld in previous year.""" + + +@policy_input(start_date="2005-01-01") +def p_id_einstandspartner() -> int: + """Identifier of Einstandspartner.""" diff --git "a/src/_gettsim/arbeitslosengeld_2/kindergeld\303\274bertrag.py" "b/src/_gettsim/arbeitslosengeld_2/kindergeld\303\274bertrag.py" index 0cc5e51b35..c1467ea265 100644 --- "a/src/_gettsim/arbeitslosengeld_2/kindergeld\303\274bertrag.py" +++ "b/src/_gettsim/arbeitslosengeld_2/kindergeld\303\274bertrag.py" @@ -4,13 +4,14 @@ from ttsim import AggregateByPIDSpec, AggregationType, join_numpy, policy_function -aggregation_specs = { - "kindergeldübertrag_m": AggregateByPIDSpec( - p_id_to_aggregate_by="kindergeld__p_id_empfänger", +aggregation_specs = ( + AggregateByPIDSpec( + target="kindergeldübertrag_m", source="differenz_kindergeld_kindbedarf_m", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="kindergeld__p_id_empfänger", + agg=AggregationType.SUM, ), -} +) @policy_function(end_date="2022-12-31", leaf_name="kindergeld_pro_kind_m") @@ -102,7 +103,7 @@ def kindergeld_zur_bedarfsdeckung_m( @policy_function() -def differenz_kindergeld_kindbedarf_m( # noqa: PLR0913 +def differenz_kindergeld_kindbedarf_m( regelbedarf_m_bg: float, nettoeinkommen_nach_abzug_freibetrag_m: float, wohngeld__anspruchshöhe_m_bg: float, diff --git a/src/_gettsim/config.py b/src/_gettsim/config.py index 204e8a7fc5..87bca8bb0e 100644 --- a/src/_gettsim/config.py +++ b/src/_gettsim/config.py @@ -77,7 +77,7 @@ } -DEFAULT_TARGETS = { +_TO_DELETE_DEFAULT_TARGETS = { "einkommensteuer": { "betrag_y_sn": None, "abgeltungssteuer": {"betrag_y_sn": None}, @@ -107,154 +107,6 @@ } -TYPES_INPUT_VARIABLES = { - "arbeitslosengeld_2": { - "arbeitslosengeld_2_bezug_im_vorjahr": bool, - # TODO(@MImmesberger): Remove input variable eigenbedarf_gedeckt once - # Bedarfsgemeinschaften are fully endogenous - # https://github.com/iza-institute-of-labor-economics/gettsim/issues/763 - "eigenbedarf_gedeckt": bool, - "p_id_einstandspartner": int, - }, - "familie": { - "alleinerziehend": bool, - "kind": bool, - "p_id_ehepartner": int, - "p_id_elternteil_1": int, - "p_id_elternteil_2": int, - }, - "alter": int, - "arbeitsstunden_w": float, - "behinderungsgrad": int, - "geburtsjahr": int, - "geburtsmonat": int, - "geburtstag": int, - "schwerbehindert_grad_g": bool, - "vermögen": float, - "weiblich": bool, - "wohnort_ost": bool, - "einkommensteuer": { - "abzüge": { - "beitrag_private_rentenversicherung_m": float, - "betreuungskosten_m": float, - "p_id_betreuungskosten_träger": int, - }, - "einkünfte": { - "aus_kapitalvermögen": { - "kapitalerträge_m": float, - }, - "aus_nichtselbstständiger_arbeit": { - "bruttolohn_m": float, - "bruttolohn_vorjahr_m": float, - }, - "aus_selbstständiger_arbeit": { - "betrag_m": float, - }, - "aus_vermietung_und_verpachtung": { - "betrag_m": float, - }, - "ist_selbstständig": bool, - "sonstige": { - "betrag_m": float, - }, - }, - "gemeinsam_veranlagt": bool, - }, - "elterngeld": { - "bisherige_bezugsmonate": int, - "claimed": bool, - "nettoeinkommen_vorjahr_m": float, - "zu_versteuerndes_einkommen_vorjahr_y_sn": float, - }, - "erziehungsgeld": { - "budgetsatz": bool, - "p_id_empfänger": int, - }, - "hh_id": int, - "kindergeld": { - "in_ausbildung": bool, - "p_id_empfänger": int, - }, - "lohnsteuer": { - "steuerklasse": int, - }, - "p_id": int, - "sozialversicherung": { - "arbeitslosen": { - "anwartschaftszeit": bool, - "arbeitssuchend": bool, - "monate_durchgängigen_bezugs_von_arbeitslosengeld": float, - "monate_sozialversicherungspflichtiger_beschäftigung_in_letzten_5_jahren": float, - }, - "kranken": { - "beitrag": { - "privat_versichert": bool, - } - }, - "pflege": { - "beitrag": { - "hat_kinder": bool, - } - }, - "rente": { - "altersrente": { - "für_frauen": { - "pflichtsbeitragsjahre_ab_alter_40": float, - }, - "höchster_bruttolohn_letzte_15_jahre_vor_rente_y": float, - "wegen_arbeitslosigkeit": { - "arbeitslos_für_1_jahr_nach_alter_58_ein_halb": bool, - "pflichtbeitragsjahre_8_von_10": bool, - "vertrauensschutz_1997": bool, - "vertrauensschutz_2004": bool, - }, - }, - "bezieht_rente": bool, - "entgeltpunkte_ost": float, - "entgeltpunkte_west": float, - "erwerbsminderung": { - "teilweise_erwerbsgemindert": bool, - "voll_erwerbsgemindert": bool, - }, - "ersatzzeiten_monate": float, - "freiwillige_beitragsmonate": float, - "grundrente": { - "bewertungszeiten_monate": int, - "grundrentenzeiten_monate": int, - "mean_entgeltpunkte": float, - }, - "jahr_renteneintritt": int, - "kinderberücksichtigungszeiten_monate": float, - "krankheitszeiten_ab_16_bis_24_monate": float, - "monat_renteneintritt": int, - "monate_geringfügiger_beschäftigung": float, - "monate_in_arbeitslosigkeit": float, - "monate_in_arbeitsunfähigkeit": float, - "monate_in_ausbildungssuche": float, - "monate_in_mutterschutz": float, - "monate_in_schulausbildung": float, - "monate_mit_bezug_entgeltersatzleistungen_wegen_arbeitslosigkeit": float, - "pflichtbeitragsmonate": float, - "private_rente_betrag_m": float, - "pflegeberücksichtigungszeiten_monate": float, - }, - }, - "unterhalt": { - "anspruch_m": float, - "tatsächlich_erhaltener_betrag_m": float, - }, - "wohngeld": { - "mietstufe": int, - }, - "wohnen": { - "baujahr_immobilie_hh": int, - "bewohnt_eigentum_hh": bool, - "bruttokaltmiete_m_hh": float, - "heizkosten_m_hh": float, - "wohnfläche_hh": float, - }, -} - FOREIGN_KEYS = [ ("arbeitslosengeld_2", "p_id_einstandspartner"), ("familie", "p_id_ehepartner"), diff --git "a/src/_gettsim/einkommensteuer/abz\303\274ge/altersfreibetrag.py" "b/src/_gettsim/einkommensteuer/abz\303\274ge/altersfreibetrag.py" index 23fbf44cad..d287a3b4bb 100644 --- "a/src/_gettsim/einkommensteuer/abz\303\274ge/altersfreibetrag.py" +++ "b/src/_gettsim/einkommensteuer/abz\303\274ge/altersfreibetrag.py" @@ -5,7 +5,7 @@ @policy_function(end_date="2004-12-31", leaf_name="altersfreibetrag_y") -def altersfreibetrag_y_bis_2004( # noqa: PLR0913 +def altersfreibetrag_y_bis_2004( alter: int, einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_y: float, einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_y: float, @@ -57,7 +57,7 @@ def altersfreibetrag_y_bis_2004( # noqa: PLR0913 @policy_function(start_date="2005-01-01", leaf_name="altersfreibetrag_y") -def altersfreibetrag_y_ab_2005( # noqa: PLR0913 +def altersfreibetrag_y_ab_2005( alter: int, geburtsjahr: int, sozialversicherung__geringfügig_beschäftigt: bool, diff --git "a/src/_gettsim/einkommensteuer/abz\303\274ge/inputs.py" "b/src/_gettsim/einkommensteuer/abz\303\274ge/inputs.py" new file mode 100644 index 0000000000..ecc82480e5 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/abz\303\274ge/inputs.py" @@ -0,0 +1,18 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def beitrag_private_rentenversicherung_m() -> float: + pass + + +@policy_input() +def betreuungskosten_m() -> float: + """Monthly childcare expenses for a particular child under the age of 14.""" + + +@policy_input() +def p_id_betreuungskosten_träger() -> int: + """Identifier of the person who paid childcare expenses.""" diff --git "a/src/_gettsim/einkommensteuer/abz\303\274ge/sonderausgaben.py" "b/src/_gettsim/einkommensteuer/abz\303\274ge/sonderausgaben.py" index dd675a92a6..a5b1634b80 100644 --- "a/src/_gettsim/einkommensteuer/abz\303\274ge/sonderausgaben.py" +++ "b/src/_gettsim/einkommensteuer/abz\303\274ge/sonderausgaben.py" @@ -3,18 +3,18 @@ from ttsim import ( AggregateByPIDSpec, AggregationType, - RoundingDirection, RoundingSpec, policy_function, ) -aggregation_specs = { - "betreuungskosten_elternteil_m": AggregateByPIDSpec( - p_id_to_aggregate_by="p_id_betreuungskosten_träger", +aggregation_specs = ( + AggregateByPIDSpec( + target="betreuungskosten_elternteil_m", source="betreuungskosten_m", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="p_id_betreuungskosten_träger", + agg=AggregationType.SUM, ), -} +) @policy_function(end_date="2011-12-31", leaf_name="sonderausgaben_y_sn") @@ -107,7 +107,7 @@ def ausgaben_für_betreuung_y( return out -@policy_function(rounding_spec=RoundingSpec(base=1, direction=RoundingDirection.UP)) +@policy_function(rounding_spec=RoundingSpec(base=1, direction="up")) def absetzbare_betreuungskosten_y_sn( ausgaben_für_betreuung_y_sn: float, eink_st_abzuege_params: dict, diff --git "a/src/_gettsim/einkommensteuer/abz\303\274ge/vorsorgeaufwendungen.py" "b/src/_gettsim/einkommensteuer/abz\303\274ge/vorsorgeaufwendungen.py" index 82b03ce63a..59b024ff05 100644 --- "a/src/_gettsim/einkommensteuer/abz\303\274ge/vorsorgeaufwendungen.py" +++ "b/src/_gettsim/einkommensteuer/abz\303\274ge/vorsorgeaufwendungen.py" @@ -1,12 +1,10 @@ -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function( end_date="2004-12-31", leaf_name="vorsorgeaufwendungen_y_sn", - rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 10 Abs. 3 EStG" - ), + rounding_spec=RoundingSpec(base=1, direction="up", reference="§ 10 Abs. 3 EStG"), ) def vorsorgeaufwendungen_y_sn_bis_2004( vorsorgeaufwendungen_regime_bis_2004_y_sn: float, @@ -29,9 +27,7 @@ def vorsorgeaufwendungen_y_sn_bis_2004( start_date="2005-01-01", end_date="2009-12-31", leaf_name="vorsorgeaufwendungen_y_sn", - rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 10 Abs. 3 EStG" - ), + rounding_spec=RoundingSpec(base=1, direction="up", reference="§ 10 Abs. 3 EStG"), ) def vorsorgeaufwendungen_y_sn_ab_2005_bis_2009( vorsorgeaufwendungen_regime_bis_2004_y_sn: float, @@ -63,9 +59,7 @@ def vorsorgeaufwendungen_y_sn_ab_2005_bis_2009( start_date="2010-01-01", end_date="2019-12-31", leaf_name="vorsorgeaufwendungen_y_sn", - rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 10 Abs. 3 EStG" - ), + rounding_spec=RoundingSpec(base=1, direction="up", reference="§ 10 Abs. 3 EStG"), ) def vorsorgeaufwendungen_y_sn_ab_2010_bis_2019( vorsorgeaufwendungen_regime_bis_2004_y_sn: float, @@ -96,9 +90,7 @@ def vorsorgeaufwendungen_y_sn_ab_2010_bis_2019( @policy_function( start_date="2020-01-01", leaf_name="vorsorgeaufwendungen_y_sn", - rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 10 Abs. 3 EStG" - ), + rounding_spec=RoundingSpec(base=1, direction="up", reference="§ 10 Abs. 3 EStG"), ) def vorsorgeaufwendungen_y_sn_ab_2020( vorsorgeaufwendungen_keine_kappung_krankenversicherung_y_sn: float, @@ -186,7 +178,7 @@ def vorsorgeaufwendungen_regime_bis_2004_y_sn( start_date="2005-01-01", end_date="2009-12-31", ) -def vorsorgeaufwendungen_globale_kappung_y_sn( # noqa: PLR0913 +def vorsorgeaufwendungen_globale_kappung_y_sn( altersvorsorge_y_sn: float, sozialversicherung__kranken__beitrag__betrag_versicherter_y_sn: float, sozialversicherung__arbeitslosen__beitrag__betrag_versicherter_y_sn: float, @@ -236,7 +228,7 @@ def vorsorgeaufwendungen_globale_kappung_y_sn( # noqa: PLR0913 @policy_function( start_date="2010-01-01", ) -def vorsorgeaufwendungen_keine_kappung_krankenversicherung_y_sn( # noqa: PLR0913 +def vorsorgeaufwendungen_keine_kappung_krankenversicherung_y_sn( altersvorsorge_y_sn: float, sozialversicherung__pflege__beitrag__betrag_versicherter_y_sn: float, sozialversicherung__kranken__beitrag__betrag_versicherter_y_sn: float, diff --git a/src/_gettsim/einkommensteuer/einkommen.py b/src/_gettsim/einkommensteuer/einkommen.py index 9a389154cc..1871bcc846 100644 --- a/src/_gettsim/einkommensteuer/einkommen.py +++ b/src/_gettsim/einkommensteuer/einkommen.py @@ -3,162 +3,15 @@ Einkommen are Einkünfte minus Sonderausgaben, Vorsorgeaufwendungen, außergewöhnliche Belastungen and sonstige Abzüge.""" -from ttsim import piecewise_polynomial, policy_function +from ttsim import policy_function @policy_function() def gesamteinkommen_y( - gesamteinkommen_ohne_abzüge_y_sn: float, - einkommensteuer__abzüge__betrag_y_sn: float, + einkünfte__gesamtbetrag_der_einkünfte_y_sn: float, + abzüge__betrag_y_sn: float, ) -> float: - """Calculate taxable income without child allowance on Steuernummer level. - - Parameters - ---------- - gesamteinkommen_ohne_abzüge_y_sn - See :func:`gesamteinkommen_ohne_abzüge_y_sn`. - einkommensteuer__abzüge__betrag_y_sn - See :func:`einkommensteuer__abzüge__betrag_y_sn`. - - - Returns - ------- - - """ - out = gesamteinkommen_ohne_abzüge_y_sn - einkommensteuer__abzüge__betrag_y_sn + """Gesamteinkommen without Kinderfreibetrag on tax unit level.""" + out = einkünfte__gesamtbetrag_der_einkünfte_y_sn - abzüge__betrag_y_sn return max(out, 0.0) - - -@policy_function(end_date="2008-12-31", leaf_name="gesamteinkommen_ohne_abzüge_y") -def gesamteinkommen_ohne_abzüge_mit_kapitaleinkünften_y( - einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y: float, - einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y: float, - einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y: float, - renteneinkommen_y: float, - einkommensteuer__einkünfte__aus_kapitalvermögen__betrag_y: float, -) -> float: - """Sum of gross incomes with capital income. - - # TODO(@MImmesberger): Use this function to compute sum of Einkünfte instead. In - particular, don't add Renteneinkommen here, but Renteneinkünfte. - # https://github.com/iza-institute-of-labor-economics/gettsim/issues/842 - - Parameters - ---------- - einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y - See :func:`einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y`. - einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y - See :func:`einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y`. - einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y - See :func:`einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y`. - renteneinkommen_y - See :func:`renteneinkommen_y`. - einkommensteuer__einkünfte__aus_kapitalvermögen__betrag_y - See :func:`einkommensteuer__einkünfte__aus_kapitalvermögen__betrag_y`. - - Returns - ------- - - """ - out = ( - einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y - + einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y - + einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y - + renteneinkommen_y - + einkommensteuer__einkünfte__aus_kapitalvermögen__betrag_y - ) - return out - - -@policy_function(start_date="2009-01-01", leaf_name="gesamteinkommen_ohne_abzüge_y") -def gesamteinkommen_ohne_abzüge_ohne_kapitaleinkünfte_y( - einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y: float, - einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y: float, - einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y: float, - renteneinkommen_y: float, -) -> float: - """Sum of gross incomes without capital income. - - Since 2009 capital income is not subject to normal taxation. - Parameters - ---------- - einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y - See :func:`einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y`. - einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y - See :func:`einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y`. - einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y - See :func:`einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y`. - renteneinkommen_y - See :func:`renteneinkommen_y`. - - Returns - ------- - - """ - out = ( - einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_y - + einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_y - + einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_y - + renteneinkommen_y - ) - return out - - -@policy_function() -def renteneinkommen_m( - sozialversicherung__rente__altersrente__betrag_m: float, - sozialversicherung__rente__private_rente_betrag_m: float, - rente_ertragsanteil: float, -) -> float: - """Calculate monthly pension payment subject to taxation. - - # TODO(@MImmesberger): Add Renteneinkünfte to Einkünfte/Sonstige namespace. - # Calculate deduction from Renteneinkünfte in Abzüge dir instead of Renteneinkommen - # here directly. - # https://github.com/iza-institute-of-labor-economics/gettsim/issues/842 - - Parameters - ---------- - sozialversicherung__rente__altersrente__betrag_m - See :func:`sozialversicherung__rente__altersrente__betrag_m`. - sozialversicherung__rente__private_rente_betrag_m - See :func:`sozialversicherung__rente__private_rente_betrag_m`. - rente_ertragsanteil - See :func:`rente_ertragsanteil`. - - Returns - ------- - - """ - return rente_ertragsanteil * ( - sozialversicherung__rente__altersrente__betrag_m - + sozialversicherung__rente__private_rente_betrag_m - ) - - -@policy_function() -def rente_ertragsanteil( - sozialversicherung__rente__jahr_renteneintritt: int, eink_st_params: dict -) -> float: - """Share of pensions subject to income taxation. - - Parameters - ---------- - sozialversicherung__rente__jahr_renteneintritt - See basic input variable :ref:`sozialversicherung__rente__jahr_renteneintritt `. - eink_st_params - See params documentation :ref:`eink_st_params `. - Returns - ------- - - """ - out = piecewise_polynomial( - x=sozialversicherung__rente__jahr_renteneintritt, - thresholds=eink_st_params["rente_ertragsanteil"]["thresholds"], - rates=eink_st_params["rente_ertragsanteil"]["rates"], - intercepts_at_lower_thresholds=eink_st_params["rente_ertragsanteil"][ - "intercepts_at_lower_thresholds" - ], - ) - return out diff --git a/src/_gettsim/einkommensteuer/einkommensteuer.py b/src/_gettsim/einkommensteuer/einkommensteuer.py index 3e3a94c0f1..95154ef0da 100644 --- a/src/_gettsim/einkommensteuer/einkommensteuer.py +++ b/src/_gettsim/einkommensteuer/einkommensteuer.py @@ -3,31 +3,32 @@ from ttsim import ( AggregateByPIDSpec, AggregationType, - RoundingDirection, RoundingSpec, piecewise_polynomial, policy_function, ) -aggregation_specs = { - "anzahl_kindergeld_ansprüche_1": AggregateByPIDSpec( - p_id_to_aggregate_by="familie__p_id_elternteil_1", +aggregation_specs = ( + AggregateByPIDSpec( + target="anzahl_kindergeld_ansprüche_1", source="kindergeld__grundsätzlich_anspruchsberechtigt", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="familie__p_id_elternteil_1", + agg=AggregationType.SUM, ), - "anzahl_kindergeld_ansprüche_2": AggregateByPIDSpec( - p_id_to_aggregate_by="familie__p_id_elternteil_2", + AggregateByPIDSpec( + target="anzahl_kindergeld_ansprüche_2", source="kindergeld__grundsätzlich_anspruchsberechtigt", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="familie__p_id_elternteil_2", + agg=AggregationType.SUM, ), -} +) @policy_function( end_date="1996-12-31", leaf_name="betrag_y_sn", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 32a Abs. 1 S. 6 EStG" + base=1, direction="down", reference="§ 32a Abs. 1 S. 6 EStG" ), ) def betrag_y_sn_kindergeld_kinderfreibetrag_parallel( @@ -52,7 +53,7 @@ def betrag_y_sn_kindergeld_kinderfreibetrag_parallel( start_date="1997-01-01", leaf_name="betrag_y_sn", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 32a Abs. 1 S.6 EStG" + base=1, direction="down", reference="§ 32a Abs. 1 S.6 EStG" ), ) def betrag_y_sn_kindergeld_oder_kinderfreibetrag( @@ -118,7 +119,7 @@ def kinderfreibetrag_günstiger_sn( end_date="2001-12-31", leaf_name="betrag_mit_kinderfreibetrag_y_sn", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 32a Abs. 1 S.6 EStG" + base=1, direction="down", reference="§ 32a Abs. 1 S.6 EStG" ), ) def betrag_mit_kinderfreibetrag_y_sn_bis_2001() -> float: @@ -129,7 +130,7 @@ def betrag_mit_kinderfreibetrag_y_sn_bis_2001() -> float: start_date="2002-01-01", leaf_name="betrag_mit_kinderfreibetrag_y_sn", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 32a Abs. 1 S.6 EStG" + base=1, direction="down", reference="§ 32a Abs. 1 S.6 EStG" ), ) def betrag_mit_kinderfreibetrag_y_sn_ab_2002( @@ -165,7 +166,7 @@ def betrag_mit_kinderfreibetrag_y_sn_ab_2002( @policy_function( rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 32a Abs. 1 S.6 EStG" + base=1, direction="down", reference="§ 32a Abs. 1 S.6 EStG" ) ) def betrag_ohne_kinderfreibetrag_y_sn( diff --git a/tests/ttsim/mettsim/functions/__init__.py "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_forst_und_landwirtschaft/__init__.py" similarity index 100% rename from tests/ttsim/mettsim/functions/__init__.py rename to "src/_gettsim/einkommensteuer/eink\303\274nfte/aus_forst_und_landwirtschaft/__init__.py" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_forst_und_landwirtschaft/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_forst_und_landwirtschaft/inputs.py" new file mode 100644 index 0000000000..258f856491 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_forst_und_landwirtschaft/inputs.py" @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def betrag_m() -> float: + """Monthly income from forestry and agriculture.""" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_gewerbebetrieb/__init__.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_gewerbebetrieb/__init__.py" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_gewerbebetrieb/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_gewerbebetrieb/inputs.py" new file mode 100644 index 0000000000..5ff853b24d --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_gewerbebetrieb/inputs.py" @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def betrag_m() -> float: + """Monthly business income.""" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_kapitalverm\303\266gen/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_kapitalverm\303\266gen/inputs.py" new file mode 100644 index 0000000000..c4ea8403ef --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_kapitalverm\303\266gen/inputs.py" @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def kapitalerträge_m() -> float: + """Monthly capital income.""" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_nichtselbstst\303\244ndiger_arbeit/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_nichtselbstst\303\244ndiger_arbeit/inputs.py" new file mode 100644 index 0000000000..bc235e6814 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_nichtselbstst\303\244ndiger_arbeit/inputs.py" @@ -0,0 +1,13 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def bruttolohn_m() -> float: + """Monthly wage.""" + + +@policy_input() +def bruttolohn_vorjahr_m() -> float: + """Monthly wage of previous year.""" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_selbstst\303\244ndiger_arbeit/__init__.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_selbstst\303\244ndiger_arbeit/__init__.py" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_selbstst\303\244ndiger_arbeit/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_selbstst\303\244ndiger_arbeit/inputs.py" new file mode 100644 index 0000000000..819d2c0aa7 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_selbstst\303\244ndiger_arbeit/inputs.py" @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def betrag_m() -> float: + """Monthly income from self-employment.""" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_vermietung_und_verpachtung/__init__.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_vermietung_und_verpachtung/__init__.py" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_vermietung_und_verpachtung/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_vermietung_und_verpachtung/inputs.py" new file mode 100644 index 0000000000..273a26e693 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/aus_vermietung_und_verpachtung/inputs.py" @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def betrag_m() -> float: + """Monthly rental income net of deductions.""" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/eink\303\274nfte.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/eink\303\274nfte.py" new file mode 100644 index 0000000000..8ea38e9034 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/eink\303\274nfte.py" @@ -0,0 +1,50 @@ +"""Einkünfte according to §§ 13-24 EStG.""" + +from ttsim import policy_function + + +@policy_function(end_date="2008-12-31", leaf_name="gesamtbetrag_der_einkünfte_y") +def gesamtbetrag_der_einkünfte_y_mit_kapiteleinkünften( + aus_forst_und_landwirtschaft__betrag_y: float, + aus_gewerbebetrieb__betrag_y: float, + aus_selbstständiger_arbeit__betrag_y: float, + aus_nichtselbstständiger_arbeit__betrag_y: float, + aus_kapitalvermögen__betrag_y: float, + aus_vermietung_und_verpachtung__betrag_y: float, + sonstige__betrag_y: float, +) -> float: + """Gesamtbetrag der Einkünfte (GdE) with capital income.""" + out = ( + aus_forst_und_landwirtschaft__betrag_y + + aus_gewerbebetrieb__betrag_y + + aus_selbstständiger_arbeit__betrag_y + + aus_nichtselbstständiger_arbeit__betrag_y + + aus_kapitalvermögen__betrag_y + + aus_vermietung_und_verpachtung__betrag_y + + sonstige__betrag_y + ) + return out + + +@policy_function(start_date="2009-01-01", leaf_name="gesamtbetrag_der_einkünfte_y") +def gesamtbetrag_der_einkünfte_y_ohne_kapitaleinkünfte( + aus_forst_und_landwirtschaft__betrag_y: float, + aus_gewerbebetrieb__betrag_y: float, + aus_selbstständiger_arbeit__betrag_y: float, + aus_nichtselbstständiger_arbeit__betrag_y: float, + aus_vermietung_und_verpachtung__betrag_y: float, + sonstige__betrag_y: float, +) -> float: + """Gesamtbetrag der Einkünfte (GdE) without capital income. + + Since 2009 capital income is not subject to normal taxation. + """ + out = ( + aus_forst_und_landwirtschaft__betrag_y + + aus_gewerbebetrieb__betrag_y + + aus_selbstständiger_arbeit__betrag_y + + aus_nichtselbstständiger_arbeit__betrag_y + + aus_vermietung_und_verpachtung__betrag_y + + sonstige__betrag_y + ) + return out diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/inputs.py" new file mode 100644 index 0000000000..1d67eb0028 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/inputs.py" @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def ist_selbstständig() -> bool: + """Self-employed (main profession).""" diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/__init__.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/__init__.py" new file mode 100644 index 0000000000..e69de29bb2 diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/inputs.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/inputs.py" new file mode 100644 index 0000000000..2a03c9ca84 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/inputs.py" @@ -0,0 +1,12 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def ohne_renten_m() -> float: + """Additional income: includes private and public transfers that are not yet + implemented in GETTSIM (e.g., BAföG, Kriegsopferfürsorge). + + Excludes income from public and private pensions. + """ diff --git "a/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/sonstige.py" "b/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/sonstige.py" new file mode 100644 index 0000000000..343669f932 --- /dev/null +++ "b/src/_gettsim/einkommensteuer/eink\303\274nfte/sonstige/sonstige.py" @@ -0,0 +1,40 @@ +"""Sonstige Einkünfte according to § 22 EStG.""" + +from ttsim import piecewise_polynomial, policy_function + + +@policy_function() +def betrag_m( + ohne_renten_m: float, + renteneinkünfte_m: float, +) -> float: + """Total sonstige Einkünfte.""" + return ohne_renten_m + renteneinkünfte_m + + +@policy_function() +def renteneinkünfte_m( + rente_ertragsanteil: float, + sozialversicherung__rente__altersrente__betrag_m: float, + sozialversicherung__rente__private_rente_betrag_m: float, +) -> float: + """Pension income counting towards taxable income.""" + return rente_ertragsanteil * ( + sozialversicherung__rente__altersrente__betrag_m + + sozialversicherung__rente__private_rente_betrag_m + ) + + +@policy_function() +def rente_ertragsanteil( + sozialversicherung__rente__jahr_renteneintritt: int, eink_st_params: dict +) -> float: + """Share of pensions subject to income taxation.""" + return piecewise_polynomial( + x=sozialversicherung__rente__jahr_renteneintritt, + thresholds=eink_st_params["rente_ertragsanteil"]["thresholds"], + rates=eink_st_params["rente_ertragsanteil"]["rates"], + intercepts_at_lower_thresholds=eink_st_params["rente_ertragsanteil"][ + "intercepts_at_lower_thresholds" + ], + ) diff --git a/src/_gettsim/einkommensteuer/group_by_ids.py b/src/_gettsim/einkommensteuer/group_by_ids.py index be1cf5b9ac..babef7f139 100644 --- a/src/_gettsim/einkommensteuer/group_by_ids.py +++ b/src/_gettsim/einkommensteuer/group_by_ids.py @@ -4,13 +4,16 @@ from ttsim import AggregateByGroupSpec, AggregationType, group_by_function -aggregation_specs = { - "anzahl_personen_sn": AggregateByGroupSpec(aggr=AggregationType.COUNT), - "alleinerziehend_sn": AggregateByGroupSpec( +aggregation_specs = ( + AggregateByGroupSpec( + target="anzahl_personen_sn", source=None, agg=AggregationType.COUNT + ), + AggregateByGroupSpec( + target="alleinerziehend_sn", source="familie__alleinerziehend", - aggr=AggregationType.ANY, + agg=AggregationType.ANY, ), -} +) @group_by_function() diff --git a/src/_gettsim/einkommensteuer/inputs.py b/src/_gettsim/einkommensteuer/inputs.py new file mode 100644 index 0000000000..97cbdff507 --- /dev/null +++ b/src/_gettsim/einkommensteuer/inputs.py @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def gemeinsam_veranlagt() -> bool: + """Taxes are filed jointly.""" diff --git a/src/_gettsim/einkommensteuer/kinderfreibetrag.py b/src/_gettsim/einkommensteuer/kinderfreibetrag.py index ac3152b8b2..b1eeec1130 100644 --- a/src/_gettsim/einkommensteuer/kinderfreibetrag.py +++ b/src/_gettsim/einkommensteuer/kinderfreibetrag.py @@ -2,18 +2,20 @@ from ttsim import AggregateByPIDSpec, AggregationType, policy_function -aggregation_specs = { - "anzahl_kinderfreibeträge_1": AggregateByPIDSpec( - p_id_to_aggregate_by="p_id_kinderfreibetragsempfänger_1", +aggregation_specs = ( + AggregateByPIDSpec( + target="anzahl_kinderfreibeträge_1", source="kindergeld__grundsätzlich_anspruchsberechtigt", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="p_id_kinderfreibetragsempfänger_1", + agg=AggregationType.SUM, ), - "anzahl_kinderfreibeträge_2": AggregateByPIDSpec( - p_id_to_aggregate_by="p_id_kinderfreibetragsempfänger_2", + AggregateByPIDSpec( + target="anzahl_kinderfreibeträge_2", source="kindergeld__grundsätzlich_anspruchsberechtigt", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="p_id_kinderfreibetragsempfänger_2", + agg=AggregationType.SUM, ), -} +) @policy_function() diff --git a/src/_gettsim/einkommensteuer/zu_versteuerndes_einkommen.py b/src/_gettsim/einkommensteuer/zu_versteuerndes_einkommen.py index 7497806335..6fe08b0178 100644 --- a/src/_gettsim/einkommensteuer/zu_versteuerndes_einkommen.py +++ b/src/_gettsim/einkommensteuer/zu_versteuerndes_einkommen.py @@ -1,11 +1,11 @@ """Taxable income.""" -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function( rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 32a Abs. 1 S.1 EStG" + base=1, direction="down", reference="§ 32a Abs. 1 S.1 EStG" ), start_date="2004-01-01", leaf_name="zu_versteuerndes_einkommen_y_sn", @@ -41,7 +41,7 @@ def zu_versteuerndes_einkommen_y_sn_mit_abrundungsregel( @policy_function( rounding_spec=RoundingSpec( base=36, - direction=RoundingDirection.DOWN, + direction="down", to_add_after_rounding=18, reference="§ 32a Abs. 2 EStG", ), @@ -80,7 +80,7 @@ def zu_versteuerndes_einkommen_y_sn_mit_grober_54er_rundungsregel( @policy_function( rounding_spec=RoundingSpec( base=27.609762, - direction=RoundingDirection.DOWN, + direction="down", to_add_after_rounding=13.804881, reference="§ 32a Abs. 2 EStG", ), diff --git a/src/_gettsim/elterngeld/einkommen.py b/src/_gettsim/elterngeld/einkommen.py index 5397cdcc6a..32e05d6229 100644 --- a/src/_gettsim/elterngeld/einkommen.py +++ b/src/_gettsim/elterngeld/einkommen.py @@ -1,6 +1,6 @@ """Relevant income for parental leave benefits.""" -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function(start_date="2007-01-01") @@ -36,9 +36,7 @@ def anzurechnendes_nettoeinkommen_m( @policy_function( start_date="2007-01-01", - rounding_spec=RoundingSpec( - base=2, direction=RoundingDirection.DOWN, reference="§ 2 (2) BEEG" - ), + rounding_spec=RoundingSpec(base=2, direction="down", reference="§ 2 (2) BEEG"), ) def lohnersatzanteil_einkommen_untere_grenze( nettoeinkommen_vorjahr_m: float, @@ -65,9 +63,7 @@ def lohnersatzanteil_einkommen_untere_grenze( @policy_function( start_date="2007-01-01", - rounding_spec=RoundingSpec( - base=2, direction=RoundingDirection.DOWN, reference="§ 2 (2) BEEG" - ), + rounding_spec=RoundingSpec(base=2, direction="down", reference="§ 2 (2) BEEG"), ) def lohnersatzanteil_einkommen_obere_grenze( nettoeinkommen_vorjahr_m: float, @@ -156,7 +152,7 @@ def einkommen_vorjahr_unter_bezugsgrenze_ohne_unterscheidung_single_paar( @policy_function( start_date="2006-01-01", - rounding_spec=RoundingSpec(base=0.01, direction=RoundingDirection.DOWN), + rounding_spec=RoundingSpec(base=0.01, direction="down"), ) def nettoeinkommen_approximation_m( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, diff --git a/src/_gettsim/elterngeld/elterngeld.py b/src/_gettsim/elterngeld/elterngeld.py index 14097b9b60..870a4379b3 100644 --- a/src/_gettsim/elterngeld/elterngeld.py +++ b/src/_gettsim/elterngeld/elterngeld.py @@ -4,47 +4,53 @@ AggregateByGroupSpec, AggregateByPIDSpec, AggregationType, - RoundingDirection, RoundingSpec, policy_function, ) -aggregation_specs = { - "kind_grundsätzlich_anspruchsberechtigt_fg": AggregateByGroupSpec( +aggregation_specs = ( + AggregateByGroupSpec( + target="kind_grundsätzlich_anspruchsberechtigt_fg", source="kind_grundsätzlich_anspruchsberechtigt", - aggr=AggregationType.ANY, + agg=AggregationType.ANY, ), - "anzahl_anträge_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_anträge_fg", source="claimed", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "bezugsmonate_partner": AggregateByPIDSpec( + AggregateByPIDSpec( + target="bezugsmonate_partner", p_id_to_aggregate_by="arbeitslosengeld_2__p_id_einstandspartner", source="bisherige_bezugsmonate", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "alter_monate_jüngstes_mitglied_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="alter_monate_jüngstes_mitglied_fg", source="alter_monate", - aggr=AggregationType.MIN, + agg=AggregationType.MIN, ), - "anzahl_kinder_bis_2_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_kinder_bis_2_fg", source="familie__kind_bis_2", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_kinder_bis_5_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_kinder_bis_5_fg", source="familie__kind_bis_5", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_mehrlinge_jüngstes_kind_fg": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_mehrlinge_jüngstes_kind_fg", source="jüngstes_kind_oder_mehrling", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), -} +) @policy_function( start_date="2011-01-01", - rounding_spec=RoundingSpec(base=0.01, direction=RoundingDirection.DOWN), + rounding_spec=RoundingSpec(base=0.01, direction="down"), ) def betrag_m( grundsätzlich_anspruchsberechtigt: bool, @@ -110,7 +116,7 @@ def basisbetrag_m( start_date="2007-01-01", end_date="2010-12-31", leaf_name="betrag_m", - rounding_spec=RoundingSpec(base=0.01, direction=RoundingDirection.DOWN), + rounding_spec=RoundingSpec(base=0.01, direction="down"), ) def elterngeld_not_implemented() -> float: raise NotImplementedError("Elterngeld is not implemented prior to 2011.") @@ -156,7 +162,7 @@ def anspruchshöhe_m( @policy_function(start_date="2007-01-01") -def grundsätzlich_anspruchsberechtigt( # noqa: PLR0913 +def grundsätzlich_anspruchsberechtigt( claimed: bool, arbeitsstunden_w: float, kind_grundsätzlich_anspruchsberechtigt_fg: bool, diff --git a/src/_gettsim/elterngeld/inputs.py b/src/_gettsim/elterngeld/inputs.py new file mode 100644 index 0000000000..092d270611 --- /dev/null +++ b/src/_gettsim/elterngeld/inputs.py @@ -0,0 +1,23 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def bisherige_bezugsmonate() -> int: + """Number of months the individual received Elterngeld for the current youngest child.""" + + +@policy_input() +def claimed() -> bool: + """Individual claims Elterngeld.""" + + +@policy_input() +def nettoeinkommen_vorjahr_m() -> float: + """Net wage in the 12 months before birth of youngest child.""" + + +@policy_input() +def zu_versteuerndes_einkommen_vorjahr_y_sn() -> float: + """Taxable income in the calendar year prior to the youngest child's birth year.""" diff --git a/src/_gettsim/erziehungsgeld/erziehungsgeld.py b/src/_gettsim/erziehungsgeld/erziehungsgeld.py index cbd5edaccf..5ddf76cf63 100644 --- a/src/_gettsim/erziehungsgeld/erziehungsgeld.py +++ b/src/_gettsim/erziehungsgeld/erziehungsgeld.py @@ -3,18 +3,18 @@ from ttsim import ( AggregateByPIDSpec, AggregationType, - RoundingDirection, RoundingSpec, policy_function, ) -aggregation_specs = { - "anspruchshöhe_m": AggregateByPIDSpec( - p_id_to_aggregate_by="p_id_empfänger", +aggregation_specs = ( + AggregateByPIDSpec( + target="anspruchshöhe_m", source="anspruchshöhe_kind_m", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="p_id_empfänger", + agg=AggregationType.SUM, ), -} +) @policy_function(start_date="2004-01-01", end_date="2008-12-31") @@ -49,7 +49,7 @@ def betrag_m( @policy_function( end_date="2003-12-31", leaf_name="anspruchshöhe_kind_m", - rounding_spec=RoundingSpec(base=0.01, direction=RoundingDirection.NEAREST), + rounding_spec=RoundingSpec(base=0.01, direction="nearest"), ) def erziehungsgeld_kind_ohne_budgetsatz_m() -> float: raise NotImplementedError( @@ -64,7 +64,7 @@ def erziehungsgeld_kind_ohne_budgetsatz_m() -> float: start_date="2004-01-01", end_date="2008-12-31", leaf_name="anspruchshöhe_kind_m", - rounding_spec=RoundingSpec(base=0.01, direction=RoundingDirection.NEAREST), + rounding_spec=RoundingSpec(base=0.01, direction="nearest"), ) def anspruchshöhe_kind_mit_budgetsatz_m( kind_grundsätzlich_anspruchsberechtigt: bool, diff --git a/src/_gettsim/erziehungsgeld/inputs.py b/src/_gettsim/erziehungsgeld/inputs.py new file mode 100644 index 0000000000..33acc86630 --- /dev/null +++ b/src/_gettsim/erziehungsgeld/inputs.py @@ -0,0 +1,13 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def budgetsatz() -> bool: + """Applied for "Budgetsatz" of parental leave benefit.""" + + +@policy_input() +def p_id_empfänger() -> int: + pass diff --git a/src/_gettsim/familie/familie.py b/src/_gettsim/familie/familie.py index c117ce16d3..2e0bf40c23 100644 --- a/src/_gettsim/familie/familie.py +++ b/src/_gettsim/familie/familie.py @@ -13,9 +13,11 @@ policy_function, ) -aggregation_specs = { - "anzahl_personen_ehe": AggregateByGroupSpec(aggr=AggregationType.COUNT), -} +aggregation_specs = ( + AggregateByGroupSpec( + target="anzahl_personen_ehe", source=None, agg=AggregationType.COUNT + ), +) @policy_function() diff --git a/src/_gettsim/familie/inputs.py b/src/_gettsim/familie/inputs.py new file mode 100644 index 0000000000..806b95928f --- /dev/null +++ b/src/_gettsim/familie/inputs.py @@ -0,0 +1,28 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def alleinerziehend() -> bool: + """Single parent.""" + + +@policy_input() +def kind() -> bool: + """Dependent child living with parents.""" + + +@policy_input() +def p_id_ehepartner() -> int: + """Identifier of married partner.""" + + +@policy_input() +def p_id_elternteil_1() -> int: + """Identifier of the first parent.""" + + +@policy_input() +def p_id_elternteil_2() -> int: + """Identifier of the second parent.""" diff --git a/src/_gettsim/grundsicherung/im_alter/einkommen.py b/src/_gettsim/grundsicherung/im_alter/einkommen.py index eafbd0319c..4051da17bb 100644 --- a/src/_gettsim/grundsicherung/im_alter/einkommen.py +++ b/src/_gettsim/grundsicherung/im_alter/einkommen.py @@ -4,11 +4,11 @@ @policy_function() -def einkommen_m( # noqa: PLR0913 +def einkommen_m( erwerbseinkommen_m: float, private_rente_betrag_m: float, gesetzliche_rente_m: float, - einkommensteuer__einkünfte__sonstige__betrag_m: float, + einkommensteuer__einkünfte__sonstige__ohne_renten_m: float, einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float, kapitaleinkommen_brutto_m: float, einkommensteuer__betrag_m_sn: float, @@ -28,8 +28,8 @@ def einkommen_m( # noqa: PLR0913 See :func:`private_rente_betrag_m`. gesetzliche_rente_m See :func:`gesetzliche_rente_m`. - einkommensteuer__einkünfte__sonstige__betrag_m - See :func:`einkommensteuer__einkünfte__sonstige__betrag_m`. + einkommensteuer__einkünfte__sonstige__ohne_renten_m + See :func:`einkommensteuer__einkünfte__sonstige__ohne_renten_m`. einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m See :func:`einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m`. kapitaleinkommen_brutto_m @@ -55,7 +55,7 @@ def einkommen_m( # noqa: PLR0913 erwerbseinkommen_m + gesetzliche_rente_m + private_rente_betrag_m - + einkommensteuer__einkünfte__sonstige__betrag_m + + einkommensteuer__einkünfte__sonstige__ohne_renten_m + einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m + kapitaleinkommen_brutto_m + elterngeld__anrechenbarer_betrag_m diff --git a/src/_gettsim/grundsicherung/im_alter/im_alter.py b/src/_gettsim/grundsicherung/im_alter/im_alter.py index 374463e06c..a782395a0c 100644 --- a/src/_gettsim/grundsicherung/im_alter/im_alter.py +++ b/src/_gettsim/grundsicherung/im_alter/im_alter.py @@ -4,7 +4,7 @@ @policy_function() -def betrag_m_eg( # noqa: PLR0913 +def betrag_m_eg( arbeitslosengeld_2__regelbedarf_m_bg: float, mehrbedarf_schwerbehinderung_g_m_eg: float, kindergeld__betrag_m_eg: float, diff --git a/src/_gettsim/household_characteristics.py b/src/_gettsim/household_characteristics.py index 50efc93e92..e89ad04b71 100644 --- a/src/_gettsim/household_characteristics.py +++ b/src/_gettsim/household_characteristics.py @@ -1,16 +1,20 @@ from ttsim import AggregateByGroupSpec, AggregationType, policy_function -aggregation_specs = { - "anzahl_erwachsene_hh": AggregateByGroupSpec( +aggregation_specs = ( + AggregateByGroupSpec( + target="anzahl_erwachsene_hh", source="familie__erwachsen", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_rentenbezieher_hh": AggregateByGroupSpec( + AggregateByGroupSpec( + target="anzahl_rentenbezieher_hh", source="sozialversicherung__rente__bezieht_rente", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), - "anzahl_personen_hh": AggregateByGroupSpec(aggr=AggregationType.COUNT), -} + AggregateByGroupSpec( + target="anzahl_personen_hh", source=None, agg=AggregationType.COUNT + ), +) @policy_function() diff --git a/src/_gettsim/inputs.py b/src/_gettsim/inputs.py new file mode 100644 index 0000000000..bedec8153b --- /dev/null +++ b/src/_gettsim/inputs.py @@ -0,0 +1,63 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def alter() -> int: + """Age in years.""" + + +@policy_input() +def arbeitsstunden_w() -> float: + """Working hours.""" + + +@policy_input() +def behinderungsgrad() -> int: + pass + + +@policy_input() +def geburtsjahr() -> int: + """Birth year.""" + + +@policy_input() +def geburtsmonat() -> int: + """Month of birth (within year).""" + + +@policy_input() +def geburtstag() -> int: + """Day of birth (within month).""" + + +@policy_input() +def hh_id() -> int: + pass + + +@policy_input() +def p_id() -> int: + pass + + +@policy_input() +def schwerbehindert_grad_g() -> bool: + pass + + +@policy_input() +def vermögen() -> float: + """Assets for means testing on individual level. {ref}`See this page for more details. `""" + + +@policy_input() +def weiblich() -> bool: + """Female.""" + + +@policy_input() +def wohnort_ost() -> bool: + """Whether the person lives in the Eastern part of Germany.""" diff --git a/src/_gettsim/kindergeld/inputs.py b/src/_gettsim/kindergeld/inputs.py new file mode 100644 index 0000000000..ab41600d38 --- /dev/null +++ b/src/_gettsim/kindergeld/inputs.py @@ -0,0 +1,13 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def in_ausbildung() -> bool: + """In education according to Kindergeld definition.""" + + +@policy_input() +def p_id_empfänger() -> int: + """Identifier of person who receives Kindergeld for the particular child.""" diff --git a/src/_gettsim/kindergeld/kindergeld.py b/src/_gettsim/kindergeld/kindergeld.py index 4e3499152c..3e0935f0fd 100644 --- a/src/_gettsim/kindergeld/kindergeld.py +++ b/src/_gettsim/kindergeld/kindergeld.py @@ -4,13 +4,14 @@ from ttsim import AggregateByPIDSpec, AggregationType, join_numpy, policy_function -aggregation_specs = { - "anzahl_ansprüche": AggregateByPIDSpec( +aggregation_specs = ( + AggregateByPIDSpec( + target="anzahl_ansprüche", p_id_to_aggregate_by="p_id_empfänger", source="grundsätzlich_anspruchsberechtigt", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), -} +) @policy_function(start_date="2023-01-01", leaf_name="betrag_m") diff --git a/src/_gettsim/kinderzuschlag/einkommen.py b/src/_gettsim/kinderzuschlag/einkommen.py index 5434282fe5..ed0f4c279d 100644 --- a/src/_gettsim/kinderzuschlag/einkommen.py +++ b/src/_gettsim/kinderzuschlag/einkommen.py @@ -3,17 +3,17 @@ from ttsim import ( AggregateByGroupSpec, AggregationType, - RoundingDirection, RoundingSpec, policy_function, ) -aggregation_specs = { - "arbeitslosengeld_2__anzahl_kinder_bg": AggregateByGroupSpec( +aggregation_specs = ( + AggregateByGroupSpec( + target="arbeitslosengeld_2__anzahl_kinder_bg", source="kindergeld__anzahl_ansprüche", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), -} +) @policy_function() @@ -53,9 +53,7 @@ def bruttoeinkommen_eltern_m( @policy_function( - rounding_spec=RoundingSpec( - base=10, direction=RoundingDirection.DOWN, reference="§ 6a Abs. 4 BKGG" - ), + rounding_spec=RoundingSpec(base=10, direction="down", reference="§ 6a Abs. 4 BKGG"), leaf_name="nettoeinkommen_eltern_m", end_date="2019-06-30", ) @@ -91,9 +89,7 @@ def nettoeinkommen_eltern_m_mit_grober_rundung( @policy_function( - rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 11 Abs. 2 BKGG" - ), + rounding_spec=RoundingSpec(base=1, direction="down", reference="§ 11 Abs. 2 BKGG"), leaf_name="nettoeinkommen_eltern_m", start_date="2019-07-01", ) diff --git a/src/_gettsim/kinderzuschlag/kinderzuschlag.py b/src/_gettsim/kinderzuschlag/kinderzuschlag.py index 9f8f2f8ba2..21035993de 100644 --- a/src/_gettsim/kinderzuschlag/kinderzuschlag.py +++ b/src/_gettsim/kinderzuschlag/kinderzuschlag.py @@ -137,7 +137,7 @@ def vermögensfreibetrag_bg_ab_2023( end_date="2019-06-30", leaf_name="basisbetrag_m_bg", ) -def basisbetrag_m_bg_check_eink_max( # noqa: PLR0913 +def basisbetrag_m_bg_check_eink_max( bruttoeinkommen_eltern_m_bg: float, nettoeinkommen_eltern_m_bg: float, mindestbruttoeinkommen_m_bg: float, @@ -239,7 +239,7 @@ def basisbetrag_m_bg( @policy_function() -def basisbetrag_kind_m( # noqa: PLR0913 +def basisbetrag_kind_m( kindergeld__grundsätzlich_anspruchsberechtigt: bool, einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, unterhalt__tatsächlich_erhaltener_betrag_m: float, diff --git a/src/_gettsim/lohnsteuer/einkommen.py b/src/_gettsim/lohnsteuer/einkommen.py index 99f0cdb3b3..9bb5292a48 100644 --- a/src/_gettsim/lohnsteuer/einkommen.py +++ b/src/_gettsim/lohnsteuer/einkommen.py @@ -1,9 +1,9 @@ """Income relevant for withholding tax on earnings (Lohnsteuer).""" -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function -@policy_function(rounding_spec=RoundingSpec(base=1, direction=RoundingDirection.DOWN)) +@policy_function(rounding_spec=RoundingSpec(base=1, direction="down")) def einkommen_y( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_y: float, steuerklasse: int, @@ -186,9 +186,9 @@ def vorsorge_krankenv_option_a( @policy_function( start_date="2010-01-01", leaf_name="vorsorgepauschale_y", - rounding_spec=RoundingSpec(base=1, direction=RoundingDirection.UP), + rounding_spec=RoundingSpec(base=1, direction="up"), ) -def vorsorgepauschale_y_ab_2010( # noqa: PLR0913 +def vorsorgepauschale_y_ab_2010( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_y: float, wohnort_ost: bool, sozialv_beitr_params: dict, diff --git a/src/_gettsim/lohnsteuer/inputs.py b/src/_gettsim/lohnsteuer/inputs.py new file mode 100644 index 0000000000..93e866030b --- /dev/null +++ b/src/_gettsim/lohnsteuer/inputs.py @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def steuerklasse() -> int: + """Tax Bracket (1 to 5) for withholding tax.""" diff --git a/src/_gettsim/sozialversicherung/arbeitslosen/arbeitslosengeld.py b/src/_gettsim/sozialversicherung/arbeitslosen/arbeitslosengeld.py index a56c1996b3..da611b9a76 100644 --- a/src/_gettsim/sozialversicherung/arbeitslosen/arbeitslosengeld.py +++ b/src/_gettsim/sozialversicherung/arbeitslosen/arbeitslosengeld.py @@ -126,7 +126,7 @@ def monate_verbleibender_anspruchsdauer( @policy_function() -def grundsätzlich_anspruchsberechtigt( # noqa: PLR0913 +def grundsätzlich_anspruchsberechtigt( alter: int, arbeitssuchend: bool, monate_verbleibender_anspruchsdauer: int, @@ -170,7 +170,7 @@ def grundsätzlich_anspruchsberechtigt( # noqa: PLR0913 @policy_function() -def einkommen_vorjahr_proxy_m( # noqa: PLR0913 +def einkommen_vorjahr_proxy_m( sozialversicherung__rente__beitrag__beitragsbemessungsgrenze_m: float, einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_vorjahr_m: float, arbeitsl_geld_params: dict, diff --git a/src/_gettsim/sozialversicherung/arbeitslosen/inputs.py b/src/_gettsim/sozialversicherung/arbeitslosen/inputs.py new file mode 100644 index 0000000000..b4bbb333eb --- /dev/null +++ b/src/_gettsim/sozialversicherung/arbeitslosen/inputs.py @@ -0,0 +1,23 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def anwartschaftszeit() -> bool: + """At least 12 months of unemployment contributions in the 30 months before claiming unemployment insurance.""" + + +@policy_input() +def arbeitssuchend() -> bool: + """Looking for employment.""" + + +@policy_input() +def monate_durchgängigen_bezugs_von_arbeitslosengeld() -> float: + """Number of months the individual already receives Arbeitslosengeld without interruption.""" + + +@policy_input() +def monate_sozialversicherungspflichtiger_beschäftigung_in_letzten_5_jahren() -> float: + """Months of subjection to compulsory insurance in the 5 years before claiming unemployment.""" diff --git "a/src/_gettsim/sozialversicherung/geringf\303\274gig_besch\303\244ftigt.py" "b/src/_gettsim/sozialversicherung/geringf\303\274gig_besch\303\244ftigt.py" index 0e75bbdf9d..084b221e00 100644 --- "a/src/_gettsim/sozialversicherung/geringf\303\274gig_besch\303\244ftigt.py" +++ "b/src/_gettsim/sozialversicherung/geringf\303\274gig_besch\303\244ftigt.py" @@ -1,6 +1,6 @@ """Marginally employed.""" -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function() @@ -37,7 +37,7 @@ def geringfügig_beschäftigt( end_date="1999-12-31", leaf_name="minijob_grenze", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 8 Abs. 1a Satz 2 SGB IV" + base=1, direction="up", reference="§ 8 Abs. 1a Satz 2 SGB IV" ), ) def minijob_grenze_unterscheidung_ost_west( @@ -68,7 +68,7 @@ def minijob_grenze_unterscheidung_ost_west( end_date="2022-09-30", leaf_name="minijob_grenze", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 8 Abs. 1a Satz 2 SGB IV" + base=1, direction="up", reference="§ 8 Abs. 1a Satz 2 SGB IV" ), ) def minijob_grenze_fixer_betrag(sozialv_beitr_params: dict) -> float: @@ -92,7 +92,7 @@ def minijob_grenze_fixer_betrag(sozialv_beitr_params: dict) -> float: start_date="2022-10-01", leaf_name="minijob_grenze", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 8 Abs. 1a Satz 2 SGB IV" + base=1, direction="up", reference="§ 8 Abs. 1a Satz 2 SGB IV" ), ) def minijob_grenze_abgeleitet_von_mindestlohn(sozialv_beitr_params: dict) -> float: diff --git a/src/_gettsim/sozialversicherung/kranken/beitrag/beitrag.py b/src/_gettsim/sozialversicherung/kranken/beitrag/beitrag.py index 29b229568f..114da50b4a 100644 --- a/src/_gettsim/sozialversicherung/kranken/beitrag/beitrag.py +++ b/src/_gettsim/sozialversicherung/kranken/beitrag/beitrag.py @@ -45,7 +45,7 @@ def betrag_versicherter_m_ohne_midijob( @policy_function(start_date="2003-04-01", leaf_name="betrag_versicherter_m") -def betrag_versicherter_m_mit_midijob( # noqa: PLR0913 +def betrag_versicherter_m_mit_midijob( sozialversicherung__geringfügig_beschäftigt: bool, betrag_rentner_m: float, betrag_selbstständig_m: float, @@ -94,7 +94,7 @@ def betrag_versicherter_m_mit_midijob( # noqa: PLR0913 @policy_function(end_date="2003-03-31", leaf_name="betrag_arbeitgeber_m") -def betrag_arbeitgeber_m_ohne_midijob( # noqa: PLR0913 +def betrag_arbeitgeber_m_ohne_midijob( sozialversicherung__geringfügig_beschäftigt: bool, einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, einkommen_m: float, @@ -140,7 +140,7 @@ def betrag_arbeitgeber_m_ohne_midijob( # noqa: PLR0913 @policy_function(start_date="2003-04-01", leaf_name="betrag_arbeitgeber_m") -def betrag_arbeitgeber_m_mit_midijob( # noqa: PLR0913 +def betrag_arbeitgeber_m_mit_midijob( sozialversicherung__geringfügig_beschäftigt: bool, sozialversicherung__in_gleitzone: bool, einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, diff --git a/src/_gettsim/sozialversicherung/kranken/beitrag/einkommen.py b/src/_gettsim/sozialversicherung/kranken/beitrag/einkommen.py index 9f67871290..817d73c429 100644 --- a/src/_gettsim/sozialversicherung/kranken/beitrag/einkommen.py +++ b/src/_gettsim/sozialversicherung/kranken/beitrag/einkommen.py @@ -63,7 +63,7 @@ def einkommen_regulär_beschäftigt_m( @policy_function() -def bemessungsgrundlage_selbstständig_m( # noqa: PLR0913 +def bemessungsgrundlage_selbstständig_m( einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float, bezugsgröße_selbstständig_m: float, einkommensteuer__einkünfte__ist_selbstständig: bool, diff --git a/src/_gettsim/sozialversicherung/kranken/beitrag/inputs.py b/src/_gettsim/sozialversicherung/kranken/beitrag/inputs.py new file mode 100644 index 0000000000..8142a6713f --- /dev/null +++ b/src/_gettsim/sozialversicherung/kranken/beitrag/inputs.py @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def privat_versichert() -> bool: + """Has (only) a private health insurance contract.""" diff --git a/src/_gettsim/sozialversicherung/midijob.py b/src/_gettsim/sozialversicherung/midijob.py index a893cfb766..d75214f65b 100644 --- a/src/_gettsim/sozialversicherung/midijob.py +++ b/src/_gettsim/sozialversicherung/midijob.py @@ -1,6 +1,6 @@ """Midijob.""" -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function() @@ -50,7 +50,7 @@ def beitragspflichtige_einnahmen_aus_midijob_arbeitnehmer_m( start_date="2003-04-01", end_date="2004-12-31", leaf_name="midijob_faktor_f", - rounding_spec=RoundingSpec(base=0.0001, direction=RoundingDirection.NEAREST), + rounding_spec=RoundingSpec(base=0.0001, direction="nearest"), ) def midijob_faktor_f_mit_minijob_steuerpauschale_bis_2004( sozialversicherung__kranken__beitrag__beitragssatz_arbeitnehmer_jahresanfang: float, @@ -111,7 +111,7 @@ def midijob_faktor_f_mit_minijob_steuerpauschale_bis_2004( start_date="2005-01-01", end_date="2022-09-30", leaf_name="midijob_faktor_f", - rounding_spec=RoundingSpec(base=0.0001, direction=RoundingDirection.NEAREST), + rounding_spec=RoundingSpec(base=0.0001, direction="nearest"), ) def midijob_faktor_f_mit_minijob_steuerpauschale_ab_2005( sozialversicherung__kranken__beitrag__beitragssatz_arbeitnehmer_jahresanfang: float, @@ -174,7 +174,7 @@ def midijob_faktor_f_mit_minijob_steuerpauschale_ab_2005( @policy_function( start_date="2022-10-01", leaf_name="midijob_faktor_f", - rounding_spec=RoundingSpec(base=0.0001, direction=RoundingDirection.NEAREST), + rounding_spec=RoundingSpec(base=0.0001, direction="nearest"), ) def midijob_faktor_f_ohne_minijob_steuerpauschale( sozialversicherung__kranken__beitrag__beitragssatz_arbeitnehmer_jahresanfang: float, diff --git a/src/_gettsim/sozialversicherung/pflege/beitrag/beitrag.py b/src/_gettsim/sozialversicherung/pflege/beitrag/beitrag.py index 6a9c5a6b72..eecec5c1c1 100644 --- a/src/_gettsim/sozialversicherung/pflege/beitrag/beitrag.py +++ b/src/_gettsim/sozialversicherung/pflege/beitrag/beitrag.py @@ -44,7 +44,7 @@ def betrag_versicherter_m_ohne_midijob( @policy_function(start_date="2003-04-01", leaf_name="betrag_versicherter_m") -def betrag_versicherter_m_mit_midijob( # noqa: PLR0913 +def betrag_versicherter_m_mit_midijob( betrag_versicherter_regulär_beschäftigt_m: float, sozialversicherung__geringfügig_beschäftigt: bool, betrag_rentner_m: float, @@ -159,7 +159,7 @@ def betrag_arbeitgeber_m_ohne_midijob( @policy_function(start_date="2003-04-01", leaf_name="betrag_arbeitgeber_m") -def betrag_arbeitgeber_m_mit_midijob( # noqa: PLR0913 +def betrag_arbeitgeber_m_mit_midijob( sozialversicherung__geringfügig_beschäftigt: bool, betrag_arbeitgeber_midijob_m: float, sozialversicherung__kranken__beitrag__einkommen_m: float, diff --git a/src/_gettsim/sozialversicherung/pflege/beitrag/beitragssatz.py b/src/_gettsim/sozialversicherung/pflege/beitrag/beitragssatz.py index baae9556e2..db130cc8ac 100644 --- a/src/_gettsim/sozialversicherung/pflege/beitrag/beitragssatz.py +++ b/src/_gettsim/sozialversicherung/pflege/beitrag/beitragssatz.py @@ -2,18 +2,20 @@ from ttsim import AggregateByPIDSpec, AggregationType, policy_function -aggregation_specs = { - "anzahl_kinder_bis_24_elternteil_1": AggregateByPIDSpec( - p_id_to_aggregate_by=("einkommensteuer__p_id_kinderfreibetragsempfänger_1"), +aggregation_specs = ( + AggregateByPIDSpec( + target="anzahl_kinder_bis_24_elternteil_1", source="alter_bis_24", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="einkommensteuer__p_id_kinderfreibetragsempfänger_1", + agg=AggregationType.SUM, ), - "anzahl_kinder_bis_24_elternteil_2": AggregateByPIDSpec( - p_id_to_aggregate_by=("einkommensteuer__p_id_kinderfreibetragsempfänger_2"), + AggregateByPIDSpec( + target="anzahl_kinder_bis_24_elternteil_2", source="alter_bis_24", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="einkommensteuer__p_id_kinderfreibetragsempfänger_2", + agg=AggregationType.SUM, ), -} +) @policy_function( diff --git a/src/_gettsim/sozialversicherung/pflege/beitrag/inputs.py b/src/_gettsim/sozialversicherung/pflege/beitrag/inputs.py new file mode 100644 index 0000000000..3437f3f310 --- /dev/null +++ b/src/_gettsim/sozialversicherung/pflege/beitrag/inputs.py @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def hat_kinder() -> bool: + """Has kids (incl. not in hh).""" diff --git a/src/_gettsim/sozialversicherung/rente/altersrente/altersgrenzen.py b/src/_gettsim/sozialversicherung/rente/altersrente/altersgrenzen.py index 08f1ae4747..aa8f9782d3 100644 --- a/src/_gettsim/sozialversicherung/rente/altersrente/altersgrenzen.py +++ b/src/_gettsim/sozialversicherung/rente/altersrente/altersgrenzen.py @@ -4,7 +4,7 @@ @policy_function(end_date="2011-12-31", leaf_name="altersgrenze_abschlagsfrei") -def altersgrenze_abschlagsfrei_mit_arbeitsl_frauen_ohne_besond_langj( # noqa: PLR0913 +def altersgrenze_abschlagsfrei_mit_arbeitsl_frauen_ohne_besond_langj( sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__grundsätzlich_anspruchsberechtigt: bool, sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__altersgrenze: float, sozialversicherung__rente__altersrente__für_frauen__grundsätzlich_anspruchsberechtigt: bool, @@ -68,7 +68,7 @@ def altersgrenze_abschlagsfrei_mit_arbeitsl_frauen_ohne_besond_langj( # noqa: P end_date="2017-12-31", leaf_name="altersgrenze_abschlagsfrei", ) -def altersgrenze_abschlagsfrei_mit_frauen_arbeitsl_besond_langj( # noqa: PLR0913 +def altersgrenze_abschlagsfrei_mit_frauen_arbeitsl_besond_langj( sozialversicherung__rente__altersrente__für_frauen__grundsätzlich_anspruchsberechtigt: bool, sozialversicherung__rente__altersrente__für_frauen__altersgrenze: float, sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__grundsätzlich_anspruchsberechtigt: bool, @@ -196,7 +196,7 @@ def altersgrenze_abschlagsfrei_mit_besond_langj_ohne_arbeitsl_frauen( @policy_function(end_date="2017-12-31", leaf_name="altersgrenze_vorzeitig") -def altersgrenze_vorzeitig_mit_rente_arbeitsl_frauen( # noqa: PLR0913 +def altersgrenze_vorzeitig_mit_rente_arbeitsl_frauen( sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__grundsätzlich_anspruchsberechtigt: bool, sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__altersgrenze_vorzeitig: float, sozialversicherung__rente__altersrente__für_frauen__grundsätzlich_anspruchsberechtigt: bool, @@ -357,7 +357,7 @@ def vorzeitig_grundsätzlich_anspruchsberechtigt_vorzeitig_ohne_rente_arbeitsl_f @policy_function(end_date="2017-12-31", leaf_name="referenzalter_abschlag") -def referenzalter_abschlag_mit_rente_arbeitsl_frauen( # noqa: PLR0913 +def referenzalter_abschlag_mit_rente_arbeitsl_frauen( sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__grundsätzlich_anspruchsberechtigt: bool, sozialversicherung__rente__altersrente__wegen_arbeitslosigkeit__altersgrenze: float, sozialversicherung__rente__altersrente__für_frauen__grundsätzlich_anspruchsberechtigt: bool, diff --git a/src/_gettsim/sozialversicherung/rente/altersrente/altersrente.py b/src/_gettsim/sozialversicherung/rente/altersrente/altersrente.py index 0d85123013..beca547239 100644 --- a/src/_gettsim/sozialversicherung/rente/altersrente/altersrente.py +++ b/src/_gettsim/sozialversicherung/rente/altersrente/altersrente.py @@ -1,12 +1,12 @@ """Public pension benefits for retirement due to age.""" -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function( end_date="2020-12-31", rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), leaf_name="betrag_m", ) @@ -19,7 +19,7 @@ def betrag_m( @policy_function( start_date="2021-01-01", rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), leaf_name="betrag_m", ) @@ -54,7 +54,7 @@ def betrag_m_mit_grundrente( @policy_function( end_date="2016-12-31", rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), leaf_name="bruttorente_m", ) @@ -106,7 +106,7 @@ def bruttorente_m_mit_harter_hinzuverdienstgrenze( end_date="2022-12-31", leaf_name="bruttorente_m", rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), ) def bruttorente_m_mit_hinzuverdienstdeckel( @@ -161,7 +161,7 @@ def bruttorente_m_mit_hinzuverdienstdeckel( start_date="2017-01-01", end_date="2022-12-31", ) -def zahlbetrag_ohne_deckel_m( # noqa: PLR0913 +def zahlbetrag_ohne_deckel_m( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_y: float, alter: int, sozialversicherung__rente__altersrente__regelaltersrente__altersgrenze: float, @@ -278,7 +278,7 @@ def differenz_bruttolohn_hinzuverdienstdeckel_y( start_date="2023-01-01", leaf_name="bruttorente_m", rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), ) def bruttorente_m_ohne_einkommensanrechnung( @@ -371,7 +371,7 @@ def rentenwert(wohnort_ost: bool, ges_rente_params: dict) -> float: @policy_function() -def zugangsfaktor( # noqa: PLR0913 +def zugangsfaktor( sozialversicherung__rente__alter_bei_renteneintritt: float, sozialversicherung__rente__altersrente__regelaltersrente__altersgrenze: float, referenzalter_abschlag: float, diff --git "a/src/_gettsim/sozialversicherung/rente/altersrente/f\303\274r_frauen/inputs.py" "b/src/_gettsim/sozialversicherung/rente/altersrente/f\303\274r_frauen/inputs.py" new file mode 100644 index 0000000000..89ec4e5dad --- /dev/null +++ "b/src/_gettsim/sozialversicherung/rente/altersrente/f\303\274r_frauen/inputs.py" @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def pflichtsbeitragsjahre_ab_alter_40() -> float: + """Total years of mandatory contributions after age 40.""" diff --git a/src/_gettsim/sozialversicherung/rente/altersrente/inputs.py b/src/_gettsim/sozialversicherung/rente/altersrente/inputs.py new file mode 100644 index 0000000000..738cb389ab --- /dev/null +++ b/src/_gettsim/sozialversicherung/rente/altersrente/inputs.py @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def höchster_bruttolohn_letzte_15_jahre_vor_rente_y() -> float: + """Highest gross income from regular employment in the last 15 years before pension benefit claiming. Relevant to determine pension benefit deductions for retirees in early retirement.""" diff --git a/src/_gettsim/sozialversicherung/rente/altersrente/wegen_arbeitslosigkeit/inputs.py b/src/_gettsim/sozialversicherung/rente/altersrente/wegen_arbeitslosigkeit/inputs.py new file mode 100644 index 0000000000..33811e3e9f --- /dev/null +++ b/src/_gettsim/sozialversicherung/rente/altersrente/wegen_arbeitslosigkeit/inputs.py @@ -0,0 +1,23 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def arbeitslos_für_1_jahr_nach_alter_58_ein_halb() -> bool: + """Has been unemployed at least 1 year after age 58.5.""" + + +@policy_input() +def pflichtbeitragsjahre_8_von_10() -> bool: + """Has at least 8 contribution years in past 10 years.""" + + +@policy_input() +def vertrauensschutz_1997() -> bool: + """Is covered by Vertrauensschutz rules for the Altersrente wegen Arbeitslosigkeit implemented in 1997 (§ 237 SGB VI Abs. 4).""" + + +@policy_input() +def vertrauensschutz_2004() -> bool: + """Is covered by Vertrauensschutz rules for the Altersrente wegen Arbeitslosigkeit enacted in July 2004 (§ 237 SGB VI Abs. 5).""" diff --git a/src/_gettsim/sozialversicherung/rente/beitrag/beitrag.py b/src/_gettsim/sozialversicherung/rente/beitrag/beitrag.py index 68d5569bfc..2f53b874da 100644 --- a/src/_gettsim/sozialversicherung/rente/beitrag/beitrag.py +++ b/src/_gettsim/sozialversicherung/rente/beitrag/beitrag.py @@ -123,7 +123,7 @@ def betrag_arbeitgeber_m_ohne_midijob( @policy_function(start_date="2003-04-01", leaf_name="betrag_arbeitgeber_m") -def betrag_arbeitgeber_m_mit_midijob( # noqa: PLR0913 +def betrag_arbeitgeber_m_mit_midijob( sozialversicherung__geringfügig_beschäftigt: bool, betrag_midijob_arbeitgeber_m: float, einkommen_m: float, diff --git a/src/_gettsim/sozialversicherung/rente/erwerbsminderung/erwerbsminderung.py b/src/_gettsim/sozialversicherung/rente/erwerbsminderung/erwerbsminderung.py index b885f616c8..3eb4bc602f 100644 --- a/src/_gettsim/sozialversicherung/rente/erwerbsminderung/erwerbsminderung.py +++ b/src/_gettsim/sozialversicherung/rente/erwerbsminderung/erwerbsminderung.py @@ -4,7 +4,7 @@ @policy_function(start_date="2001-01-01") -def betrag_m( # noqa: PLR0913 +def betrag_m( zugangsfaktor: float, entgeltpunkte_west: float, entgeltpunkte_ost: float, @@ -292,7 +292,7 @@ def zugangsfaktor( # TODO(@MImmesberger): Reuse Altersrente Wartezeiten for Erwerbsminderungsrente # https://github.com/iza-institute-of-labor-economics/gettsim/issues/838 @policy_function(start_date="2001-01-01") -def wartezeit_langjährig_versichert_erfüllt( # noqa: PLR0913 +def wartezeit_langjährig_versichert_erfüllt( sozialversicherung__rente__pflichtbeitragsmonate: float, sozialversicherung__rente__freiwillige_beitragsmonate: float, sozialversicherung__rente__anrechnungsmonate_45_jahre_wartezeit: float, diff --git a/src/_gettsim/sozialversicherung/rente/erwerbsminderung/inputs.py b/src/_gettsim/sozialversicherung/rente/erwerbsminderung/inputs.py new file mode 100644 index 0000000000..8b258df540 --- /dev/null +++ b/src/_gettsim/sozialversicherung/rente/erwerbsminderung/inputs.py @@ -0,0 +1,13 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def teilweise_erwerbsgemindert() -> bool: + """Able to provide at least 3 but no more than 6 hours of market labor per day..""" + + +@policy_input() +def voll_erwerbsgemindert() -> bool: + """Unable to provide more than 3 hours of market labor per day..""" diff --git a/src/_gettsim/sozialversicherung/rente/grundrente/grundrente.py b/src/_gettsim/sozialversicherung/rente/grundrente/grundrente.py index ad2eead8eb..731a24ca33 100644 --- a/src/_gettsim/sozialversicherung/rente/grundrente/grundrente.py +++ b/src/_gettsim/sozialversicherung/rente/grundrente/grundrente.py @@ -1,9 +1,9 @@ -from ttsim import RoundingDirection, RoundingSpec, piecewise_polynomial, policy_function +from ttsim import RoundingSpec, piecewise_polynomial, policy_function @policy_function( rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), start_date="2021-01-01", ) @@ -87,7 +87,7 @@ def einkommen_m( @policy_function( rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), start_date="2021-01-01", ) @@ -147,7 +147,7 @@ def anzurechnendes_einkommen_m( @policy_function( rounding_spec=RoundingSpec( - base=0.01, direction=RoundingDirection.NEAREST, reference="§ 123 SGB VI Abs. 1" + base=0.01, direction="nearest", reference="§ 123 SGB VI Abs. 1" ), start_date="2021-01-01", ) @@ -235,7 +235,7 @@ def durchschnittliche_entgeltpunkte( @policy_function( rounding_spec=RoundingSpec( base=0.0001, - direction=RoundingDirection.NEAREST, + direction="nearest", reference="§76g SGB VI Abs. 4 Nr. 4", ), start_date="2021-01-01", @@ -279,7 +279,7 @@ def höchstbetrag_m( @policy_function( rounding_spec=RoundingSpec( base=0.0001, - direction=RoundingDirection.NEAREST, + direction="nearest", reference="§ 123 SGB VI Abs. 1", ), start_date="2021-01-01", @@ -339,12 +339,12 @@ def mean_entgeltpunkte_zuschlag( @policy_function( rounding_spec=RoundingSpec( base=0.0001, - direction=RoundingDirection.NEAREST, + direction="nearest", reference="§ 123 SGB VI Abs. 1", ), start_date="2021-01-01", ) -def proxy_rente_vorjahr_m( # noqa: PLR0913 +def proxy_rente_vorjahr_m( sozialversicherung__rente__bezieht_rente: bool, sozialversicherung__rente__private_rente_betrag_m: float, sozialversicherung__rente__jahr_renteneintritt: int, diff --git a/src/_gettsim/sozialversicherung/rente/grundrente/inputs.py b/src/_gettsim/sozialversicherung/rente/grundrente/inputs.py new file mode 100644 index 0000000000..6796f05265 --- /dev/null +++ b/src/_gettsim/sozialversicherung/rente/grundrente/inputs.py @@ -0,0 +1,18 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def bewertungszeiten_monate() -> int: + """Number of months determining amount of Grundrente.""" + + +@policy_input() +def grundrentenzeiten_monate() -> int: + """Number of months determining eligibility for Grundrente.""" + + +@policy_input() +def mean_entgeltpunkte() -> float: + """Mean Entgeltpunkte during Bewertungszeiten.""" diff --git a/src/_gettsim/sozialversicherung/rente/inputs.py b/src/_gettsim/sozialversicherung/rente/inputs.py new file mode 100644 index 0000000000..8d96e516de --- /dev/null +++ b/src/_gettsim/sozialversicherung/rente/inputs.py @@ -0,0 +1,98 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def bezieht_rente() -> bool: + """Draws public pension benefits.""" + + +@policy_input() +def entgeltpunkte_ost() -> float: + """Earnings points for pension claim accumulated in Eastern states.""" + + +@policy_input() +def entgeltpunkte_west() -> float: + """Earnings points for pension claim accumulated in Western states.""" + + +@policy_input() +def ersatzzeiten_monate() -> float: + """Total months during military, persecution/escape, internment, and consecutive sickness.""" + + +@policy_input() +def freiwillige_beitragsmonate() -> float: + """Total months of voluntary pensioninsurance contributions.""" + + +@policy_input() +def jahr_renteneintritt() -> int: + """Year of pension claiming.""" + + +@policy_input() +def monat_renteneintritt() -> int: + """Month of retirement.""" + + +@policy_input() +def kinderberücksichtigungszeiten_monate() -> float: + """Total months of childcare till age 10.""" + + +@policy_input() +def krankheitszeiten_ab_16_bis_24_monate() -> float: + """Total months of sickness between age 16 and 24.""" + + +@policy_input() +def monate_geringfügiger_beschäftigung() -> float: + """Total months of marginal employment (w/o mandatory contributions).""" + + +@policy_input() +def monate_in_arbeitslosigkeit() -> float: + """Total months of unemployment (registered).""" + + +@policy_input() +def monate_in_arbeitsunfähigkeit() -> float: + """Total months of sickness, rehabilitation, measures for worklife participation(Teilhabe).""" + + +@policy_input() +def monate_in_ausbildungssuche() -> float: + """Total months of apprenticeship search.""" + + +@policy_input() +def monate_in_mutterschutz() -> float: + """Total months of maternal protections.""" + + +@policy_input() +def monate_in_schulausbildung() -> float: + """Months of schooling (incl college, unifrom age 17, max. 8 years).""" + + +@policy_input() +def monate_mit_bezug_entgeltersatzleistungen_wegen_arbeitslosigkeit() -> float: + """Total months of unemployment (only time of Entgeltersatzleistungen, not ALGII),i.e. Arbeitslosengeld, Unterhaltsgeld, Übergangsgeld.""" + + +@policy_input() +def pflichtbeitragsmonate() -> float: + """Total months of mandatory pension insurance contributions.""" + + +@policy_input() +def private_rente_betrag_m() -> float: + """Amount of monthly private pension.""" + + +@policy_input() +def pflegeberücksichtigungszeiten_monate() -> float: + """Total months of home care provision (01.01.1992-31.03.1995).""" diff --git a/src/_gettsim/sozialversicherung/rente/wartezeit.py b/src/_gettsim/sozialversicherung/rente/wartezeit.py index 3f373253dc..1c2d683b14 100644 --- a/src/_gettsim/sozialversicherung/rente/wartezeit.py +++ b/src/_gettsim/sozialversicherung/rente/wartezeit.py @@ -68,7 +68,7 @@ def wartezeit_15_jahre_erfüllt( @policy_function() -def wartezeit_35_jahre_erfüllt( # noqa: PLR0913 +def wartezeit_35_jahre_erfüllt( pflichtbeitragsmonate: float, freiwillige_beitragsmonate: float, anrechnungsmonate_35_jahre_wartezeit: float, @@ -115,7 +115,7 @@ def wartezeit_35_jahre_erfüllt( # noqa: PLR0913 @policy_function(start_date="2012-01-01") -def wartezeit_45_jahre_erfüllt( # noqa: PLR0913 +def wartezeit_45_jahre_erfüllt( pflichtbeitragsmonate: float, freiwillige_beitragsmonate: float, anrechnungsmonate_45_jahre_wartezeit: float, @@ -175,7 +175,7 @@ def wartezeit_45_jahre_erfüllt( # noqa: PLR0913 @policy_function() -def anrechnungsmonate_35_jahre_wartezeit( # noqa: PLR0913 +def anrechnungsmonate_35_jahre_wartezeit( monate_in_arbeitsunfähigkeit: float, krankheitszeiten_ab_16_bis_24_monate: float, monate_in_mutterschutz: float, diff --git a/src/_gettsim/synthetic.py b/src/_gettsim/synthetic.py index 76849aead2..08cd7c915d 100644 --- a/src/_gettsim/synthetic.py +++ b/src/_gettsim/synthetic.py @@ -9,14 +9,13 @@ from _gettsim.config import ( RESOURCE_DIR, SUPPORTED_GROUPINGS, - TYPES_INPUT_VARIABLES, ) from ttsim.policy_environment import _load_parameter_group_from_yaml current_year = datetime.datetime.today().year -def create_synthetic_data( # noqa: PLR0913 +def create_synthetic_data( n_adults=1, n_children=0, adults_married=True, diff --git a/src/_gettsim/unterhalt/inputs.py b/src/_gettsim/unterhalt/inputs.py new file mode 100644 index 0000000000..143c84345c --- /dev/null +++ b/src/_gettsim/unterhalt/inputs.py @@ -0,0 +1,13 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def anspruch_m() -> float: + """Monthly gross child alimony payments to be received by the child as determined by the court.""" + + +@policy_input() +def tatsächlich_erhaltener_betrag_m() -> float: + """Child alimony payments the child actually receives.""" diff --git a/src/_gettsim/unterhaltsvorschuss/unterhaltsvorschuss.py b/src/_gettsim/unterhaltsvorschuss/unterhaltsvorschuss.py index 7799010702..ef59071e6d 100644 --- a/src/_gettsim/unterhaltsvorschuss/unterhaltsvorschuss.py +++ b/src/_gettsim/unterhaltsvorschuss/unterhaltsvorschuss.py @@ -5,25 +5,25 @@ from ttsim import ( AggregateByPIDSpec, AggregationType, - RoundingDirection, RoundingSpec, join_numpy, policy_function, ) -aggregation_specs = { - "an_elternteil_auszuzahlender_betrag_m": AggregateByPIDSpec( +aggregation_specs = ( + AggregateByPIDSpec( + target="an_elternteil_auszuzahlender_betrag_m", p_id_to_aggregate_by="kindergeld__p_id_empfänger", source="betrag_m", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ), -} +) @policy_function( start_date="2009-01-01", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.UP, reference="§ 9 Abs. 3 UhVorschG" + base=1, direction="up", reference="§ 9 Abs. 3 UhVorschG" ), ) def betrag_m( @@ -104,7 +104,7 @@ def elternteil_alleinerziehend( end_date="2008-12-31", leaf_name="betrag_m", rounding_spec=RoundingSpec( - base=1, direction=RoundingDirection.DOWN, reference="§ 9 Abs. 3 UhVorschG" + base=1, direction="down", reference="§ 9 Abs. 3 UhVorschG" ), ) def not_implemented_m() -> float: @@ -395,9 +395,9 @@ def mindesteinkommen_erreicht( @policy_function(start_date="2017-01-01") -def einkommen_m( # noqa: PLR0913 +def einkommen_m( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, - einkommensteuer__einkünfte__sonstige__betrag_m: float, + einkommensteuer__einkünfte__sonstige__ohne_renten_m: float, einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float, einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float, einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m: float, @@ -411,8 +411,8 @@ def einkommen_m( # noqa: PLR0913 ---------- einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m See :func:`einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m`. - einkommensteuer__einkünfte__sonstige__betrag_m - See :func:`einkommensteuer__einkünfte__sonstige__betrag_m`. + einkommensteuer__einkünfte__sonstige__ohne_renten_m + See :func:`einkommensteuer__einkünfte__sonstige__ohne_renten_m`. einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m See :func:`einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m`. einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m @@ -432,7 +432,7 @@ def einkommen_m( # noqa: PLR0913 """ out = ( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m - + einkommensteuer__einkünfte__sonstige__betrag_m + + einkommensteuer__einkünfte__sonstige__ohne_renten_m + einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m + einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m + einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m diff --git "a/src/_gettsim/vorrangpr\303\274fungen/vorrangpr\303\274fungen.py" "b/src/_gettsim/vorrangpr\303\274fungen/vorrangpr\303\274fungen.py" index 252f20f6c1..5ed68556be 100644 --- "a/src/_gettsim/vorrangpr\303\274fungen/vorrangpr\303\274fungen.py" +++ "b/src/_gettsim/vorrangpr\303\274fungen/vorrangpr\303\274fungen.py" @@ -2,16 +2,18 @@ from ttsim import AggregateByGroupSpec, AggregationType, policy_function -aggregation_specs = { - "wohngeld_vorrang_wthh": AggregateByGroupSpec( +aggregation_specs = ( + AggregateByGroupSpec( + target="wohngeld_vorrang_wthh", source="wohngeld_vorrang_vor_arbeitslosengeld_2_bg", - aggr=AggregationType.ANY, + agg=AggregationType.ANY, ), - "wohngeld_kinderzuschlag_vorrang_wthh": AggregateByGroupSpec( + AggregateByGroupSpec( + target="wohngeld_kinderzuschlag_vorrang_wthh", source="wohngeld_und_kinderzuschlag_vorrang_vor_arbeitslosengeld_2_bg", - aggr=AggregationType.ANY, + agg=AggregationType.ANY, ), -} +) @policy_function() diff --git a/src/_gettsim/wohnen/inputs.py b/src/_gettsim/wohnen/inputs.py new file mode 100644 index 0000000000..e21fc2491a --- /dev/null +++ b/src/_gettsim/wohnen/inputs.py @@ -0,0 +1,28 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def baujahr_immobilie_hh() -> int: + """Year of construction of the household dwelling.""" + + +@policy_input() +def bewohnt_eigentum_hh() -> bool: + """Owner-occupied housing.""" + + +@policy_input() +def bruttokaltmiete_m_hh() -> float: + """Rent expenses excluding utilities.""" + + +@policy_input() +def heizkosten_m_hh() -> float: + """Heating expenses.""" + + +@policy_input() +def wohnfläche_hh() -> float: + """Size of household dwelling in square meters.""" diff --git a/src/_gettsim/wohngeld/einkommen.py b/src/_gettsim/wohngeld/einkommen.py index d5ff2a97cf..df2c84b451 100644 --- a/src/_gettsim/wohngeld/einkommen.py +++ b/src/_gettsim/wohngeld/einkommen.py @@ -8,13 +8,14 @@ ) from ttsim.config import numpy_or_jax as np -aggregation_specs = { - "alleinerziehendenbonus": AggregateByPIDSpec( - p_id_to_aggregate_by="kindergeld__p_id_empfänger", +aggregation_specs = ( + AggregateByPIDSpec( + target="alleinerziehendenbonus", source="kindergeld__kind_bis_10_mit_kindergeld", - aggr=AggregationType.SUM, + p_id_to_aggregate_by="kindergeld__p_id_empfänger", + agg=AggregationType.SUM, ), -} +) @policy_function() @@ -137,14 +138,14 @@ def abzugsanteil_vom_einkommen_für_steuern_sozialversicherung( @policy_function(end_date="2006-12-31", leaf_name="einkommen_vor_freibetrag_m") -def einkommen_vor_freibetrag_m_ohne_elterngeld( # noqa: PLR0913 +def einkommen_vor_freibetrag_m_ohne_elterngeld( einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float, einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_ohne_minijob_m: float, einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m: float, einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float, sozialversicherung__arbeitslosen__betrag_m: float, - einkommensteuer__einkünfte__sonstige__betrag_m: float, - einkommensteuer__renteneinkommen_m: float, + einkommensteuer__einkünfte__sonstige__ohne_renten_m: float, + einkommensteuer__renteneinkünfte_m: float, unterhalt__tatsächlich_erhaltener_betrag_m: float, unterhaltsvorschuss__betrag_m: float, abzugsanteil_vom_einkommen_für_steuern_sozialversicherung: float, @@ -165,10 +166,10 @@ def einkommen_vor_freibetrag_m_ohne_elterngeld( # noqa: PLR0913 See :func:`einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m`. sozialversicherung__arbeitslosen__betrag_m See :func:`sozialversicherung__arbeitslosen__betrag_m`. - einkommensteuer__einkünfte__sonstige__betrag_m - See :func:`einkommensteuer__einkünfte__sonstige__betrag_m`. - einkommensteuer__renteneinkommen_m - See :func:`einkommensteuer__renteneinkommen_m`. + einkommensteuer__einkünfte__sonstige__ohne_renten_m + See :func:`einkommensteuer__einkünfte__sonstige__ohne_renten_m`. + einkommensteuer__renteneinkünfte_m + See :func:`einkommensteuer__renteneinkünfte_m`. unterhalt__tatsächlich_erhaltener_betrag_m See basic input variable :ref:`unterhalt__tatsächlich_erhaltener_betrag_m `. unterhaltsvorschuss__betrag_m @@ -189,25 +190,27 @@ def einkommen_vor_freibetrag_m_ohne_elterngeld( # noqa: PLR0913 transfers = ( sozialversicherung__arbeitslosen__betrag_m - + einkommensteuer__renteneinkommen_m + + einkommensteuer__renteneinkünfte_m + unterhalt__tatsächlich_erhaltener_betrag_m + unterhaltsvorschuss__betrag_m ) - eink_ind = einkommen + transfers + einkommensteuer__einkünfte__sonstige__betrag_m + eink_ind = ( + einkommen + transfers + einkommensteuer__einkünfte__sonstige__ohne_renten_m + ) out = (1 - abzugsanteil_vom_einkommen_für_steuern_sozialversicherung) * eink_ind return out @policy_function(start_date="2007-01-01", leaf_name="einkommen_vor_freibetrag_m") -def einkommen_vor_freibetrag_m_mit_elterngeld( # noqa: PLR0913 +def einkommen_vor_freibetrag_m_mit_elterngeld( einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float, einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__betrag_ohne_minijob_m: float, einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m: float, einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float, sozialversicherung__arbeitslosen__betrag_m: float, - einkommensteuer__einkünfte__sonstige__betrag_m: float, - einkommensteuer__renteneinkommen_m: float, + einkommensteuer__einkünfte__sonstige__ohne_renten_m: float, + einkommensteuer__renteneinkünfte_m: float, unterhalt__tatsächlich_erhaltener_betrag_m: float, unterhaltsvorschuss__betrag_m: float, elterngeld__anrechenbarer_betrag_m: float, @@ -229,10 +232,10 @@ def einkommen_vor_freibetrag_m_mit_elterngeld( # noqa: PLR0913 See :func:`einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m`. sozialversicherung__arbeitslosen__betrag_m See :func:`sozialversicherung__arbeitslosen__betrag_m`. - einkommensteuer__einkünfte__sonstige__betrag_m - See :func:`einkommensteuer__einkünfte__sonstige__betrag_m`. - einkommensteuer__renteneinkommen_m - See :func:`einkommensteuer__renteneinkommen_m`. + einkommensteuer__einkünfte__sonstige__ohne_renten_m + See :func:`einkommensteuer__einkünfte__sonstige__ohne_renten_m`. + einkommensteuer__renteneinkünfte_m + See :func:`einkommensteuer__renteneinkünfte_m`. unterhalt__tatsächlich_erhaltener_betrag_m See basic input variable :ref:`unterhalt__tatsächlich_erhaltener_betrag_m `. unterhaltsvorschuss__betrag_m @@ -258,19 +261,21 @@ def einkommen_vor_freibetrag_m_mit_elterngeld( # noqa: PLR0913 transfers = ( sozialversicherung__arbeitslosen__betrag_m - + einkommensteuer__renteneinkommen_m + + einkommensteuer__renteneinkünfte_m + unterhalt__tatsächlich_erhaltener_betrag_m + unterhaltsvorschuss__betrag_m + elterngeld__anrechenbarer_betrag_m ) - eink_ind = einkommen + transfers + einkommensteuer__einkünfte__sonstige__betrag_m + eink_ind = ( + einkommen + transfers + einkommensteuer__einkünfte__sonstige__ohne_renten_m + ) out = (1 - abzugsanteil_vom_einkommen_für_steuern_sozialversicherung) * eink_ind return out @policy_function(end_date="2015-12-31", leaf_name="freibetrag_m") -def freibetrag_m_bis_2015( # noqa: PLR0913 +def freibetrag_m_bis_2015( einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float, ist_kind_mit_erwerbseinkommen: bool, behinderungsgrad: int, diff --git a/src/_gettsim/wohngeld/group_by_ids.py b/src/_gettsim/wohngeld/group_by_ids.py index 5885a34049..9d1ac3ec72 100644 --- a/src/_gettsim/wohngeld/group_by_ids.py +++ b/src/_gettsim/wohngeld/group_by_ids.py @@ -4,9 +4,11 @@ from ttsim import AggregateByGroupSpec, AggregationType, group_by_function -aggregation_specs = { - "anzahl_personen_wthh": AggregateByGroupSpec(aggr=AggregationType.COUNT), -} +aggregation_specs = ( + AggregateByGroupSpec( + target="anzahl_personen_wthh", source=None, agg=AggregationType.COUNT + ), +) @group_by_function() diff --git a/src/_gettsim/wohngeld/inputs.py b/src/_gettsim/wohngeld/inputs.py new file mode 100644 index 0000000000..66349c539c --- /dev/null +++ b/src/_gettsim/wohngeld/inputs.py @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def mietstufe() -> int: + """Municipality's rent classification.""" diff --git a/src/_gettsim/wohngeld/miete.py b/src/_gettsim/wohngeld/miete.py index 50ae40805d..6c8255a9c0 100644 --- a/src/_gettsim/wohngeld/miete.py +++ b/src/_gettsim/wohngeld/miete.py @@ -80,7 +80,7 @@ def min_miete_m_hh(anzahl_personen_hh: int, wohngeld_params: dict) -> float: @policy_function(end_date="2008-12-31", leaf_name="miete_m_hh") -def miete_m_hh_bis_2008( # noqa: PLR0913 +def miete_m_hh_bis_2008( mietstufe: int, wohnen__baujahr_immobilie_hh: int, anzahl_personen_hh: int, diff --git a/src/_gettsim/wohngeld/wohngeld.py b/src/_gettsim/wohngeld/wohngeld.py index f6b4f43218..7cd03da64d 100644 --- a/src/_gettsim/wohngeld/wohngeld.py +++ b/src/_gettsim/wohngeld/wohngeld.py @@ -16,7 +16,7 @@ 3. In this sense, this implementation is an approximation of the actual Wohngeld. """ -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function() @@ -68,7 +68,7 @@ def betrag_m_wthh( @policy_function( rounding_spec=RoundingSpec( base=1, - direction=RoundingDirection.NEAREST, + direction="nearest", reference="§ 19 WoGG Abs.2 Anlage 3", ) ) @@ -118,7 +118,7 @@ def anspruchshöhe_m_wthh( @policy_function( rounding_spec=RoundingSpec( base=1, - direction=RoundingDirection.NEAREST, + direction="nearest", reference="§ 19 WoGG Abs.2 Anlage 3", ) ) diff --git a/src/_gettsim_tests/_helpers.py b/src/_gettsim_tests/_helpers.py deleted file mode 100644 index 42835be8c1..0000000000 --- a/src/_gettsim_tests/_helpers.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import annotations - -from functools import lru_cache -from typing import TYPE_CHECKING - -from ttsim import ( - PolicyEnvironment, - set_up_policy_environment, -) -from ttsim.policy_environment import _parse_date - -if TYPE_CHECKING: - import datetime - - -def cached_set_up_policy_environment( - date: int | str | datetime.date, -) -> PolicyEnvironment: - normalized_date = _parse_date(date) - return _cached_set_up_policy_environment(normalized_date) - - -@lru_cache(maxsize=100) -def _cached_set_up_policy_environment(date: datetime.date) -> PolicyEnvironment: - return set_up_policy_environment(date) diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2005/hh_id_8.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2005/hh_id_8.yaml index 2451fd3565..a47d20bb85 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2005/hh_id_8.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2005/hh_id_8.yaml @@ -26,7 +26,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2006/hh_id_4.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2006/hh_id_4.yaml index e5dafe8d9e..076fe1306f 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2006/hh_id_4.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2006/hh_id_4.yaml @@ -66,7 +66,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/hh_id_3.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/hh_id_3.yaml index 02bf1cbfc3..051b0961d5 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/hh_id_3.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/hh_id_3.yaml @@ -36,7 +36,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 250.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/kind_20_jahre_in_bg.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/kind_20_jahre_in_bg.yaml index 8444fb5c55..ee5f140853 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/kind_20_jahre_in_bg.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2009/kind_20_jahre_in_bg.yaml @@ -36,7 +36,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 250.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_12.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_12.yaml index 3bec2d4fd8..c257d0f0b9 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_12.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_12.yaml @@ -36,7 +36,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 100.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_13.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_13.yaml index 7afbd8b0a6..35fe57ff3b 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_13.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_13.yaml @@ -36,7 +36,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 100.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_14.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_14.yaml index c755e0c74a..9cbaa14055 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_14.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_14.yaml @@ -36,7 +36,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 100.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_15.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_15.yaml index b355791f45..ad8fe65b27 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_15.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_15.yaml @@ -36,7 +36,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 100.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_2.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_2.yaml index fc1eb40633..a9663b7c1c 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_2.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2013/hh_id_2.yaml @@ -36,7 +36,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 100.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2018/hh_id_9.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2018/hh_id_9.yaml index 6b87c65abe..05a4c5954d 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2018/hh_id_9.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2018/hh_id_9.yaml @@ -66,7 +66,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/hh_id_6.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/hh_id_6.yaml index 78bd56c024..e5dfc9e46d 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/hh_id_6.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/hh_id_6.yaml @@ -46,7 +46,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_13_14.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_13_14.yaml index baf063b81c..217cd55c7a 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_13_14.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_13_14.yaml @@ -46,7 +46,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_5_6.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_5_6.yaml index 4faeab81a6..feaf7ba588 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_5_6.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2019/test_age_limits_5_6.yaml @@ -46,7 +46,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_10.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_10.yaml index 023ab047bc..7477d1b9a7 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_10.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_10.yaml @@ -46,7 +46,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_11.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_11.yaml index 210fd47f3f..6681d70231 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_11.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2022/hh_id_11.yaml @@ -46,7 +46,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_16.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_16.yaml index 3f23dae45c..9ba11fa0ad 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_16.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_16.yaml @@ -26,7 +26,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_19.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_19.yaml index d297ed3b21..bc005f3003 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_19.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_19.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_25.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_25.yaml index 7a55615c51..4fa57f9503 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_25.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/hh_id_25.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_age_limits_17_18.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_age_limits_17_18.yaml index d417ef1253..17bb7b17cf 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_age_limits_17_18.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_age_limits_17_18.yaml @@ -57,7 +57,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_high_wealth.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_high_wealth.yaml index 27dd091063..5c29c99f1a 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_high_wealth.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_high_wealth.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_wealth_below_threshold.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_wealth_below_threshold.yaml index d0da663b32..433553a9bb 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_wealth_below_threshold.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-01/test_family_wealth_below_threshold.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_20.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_20.yaml index eb0283bd21..9bccd82150 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_20.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_20.yaml @@ -26,7 +26,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_21.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_21.yaml index 0bba1be93f..0924cf06c5 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_21.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_21.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_22.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_22.yaml index 565cc283d6..83e30a51d7 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_22.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_22.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_23.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_23.yaml index 3be9ae0fb7..f23aa95b8f 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_23.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_23.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_24.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_24.yaml index 7b71261ebd..7f4e5ff59f 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_24.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2023-07/hh_id_24.yaml @@ -26,7 +26,7 @@ inputs: - 1200.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_kein_kindergelduebertrag.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_kein_kindergelduebertrag.yaml index 0087bf9ccf..a754508a0d 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_kein_kindergelduebertrag.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_kein_kindergelduebertrag.yaml @@ -47,7 +47,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_teilw_kindergelduebertrag.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_teilw_kindergelduebertrag.yaml index 8db03976fc..768ec0653c 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_teilw_kindergelduebertrag.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_teilw_kindergelduebertrag.yaml @@ -48,7 +48,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_voller_kindergelduebertrag.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_voller_kindergelduebertrag.yaml index 752337129a..c3495e57b8 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_voller_kindergelduebertrag.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/alleinerz_voller_kindergelduebertrag.yaml @@ -48,7 +48,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/single_no_income.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/single_no_income.yaml index f16ac9db21..d4da154518 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/single_no_income.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/single_no_income.yaml @@ -29,7 +29,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__ist_selbstständig: - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/skip_single_no_children.yaml b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/skip_single_no_children.yaml index ecc7262437..a6aa22bbc3 100644 --- a/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/skip_single_no_children.yaml +++ b/src/_gettsim_tests/test_data/arbeitslosengeld_2/2024/skip_single_no_children.yaml @@ -29,7 +29,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__ist_selbstständig: - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_1.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_1.yaml index 20c6a53cc5..d05d9883db 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_1.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_1.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 0 einkommensteuer__gesamteinkommen_y: - 0.0 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_2.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_2.yaml index ec2ee32034..7af7362283 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_2.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_2.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 6280 einkommensteuer__gesamteinkommen_y: - 4328.0 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_3.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_3.yaml index 840ffffd30..c9985d20e6 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_3.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_3.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 9880 einkommensteuer__gesamteinkommen_y: - 7916.2 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_4.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_4.yaml index 0c6fe6796e..51434257d4 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_4.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_4.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 13480 einkommensteuer__gesamteinkommen_y: - 11109.8 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_5.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_5.yaml index 19c2bb1bd4..2ca9098670 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_5.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2010/hh_id_5.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 17080 einkommensteuer__gesamteinkommen_y: - 14489.98 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_16.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_16.yaml index 1287fe54fd..e4f39ebc78 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_16.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_16.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 35000 einkommensteuer__gesamteinkommen_y: - 29964.0 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_17.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_17.yaml index 2cf9395f7d..d15d5e654b 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_17.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2015/hh_id_17.yaml @@ -106,7 +106,7 @@ outputs: einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 35000 - 23000 einkommensteuer__gesamteinkommen_y: diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2017/hh_id_18.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2017/hh_id_18.yaml index 4a8a650847..b82d3b6054 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2017/hh_id_18.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2017/hh_id_18.yaml @@ -106,7 +106,7 @@ outputs: einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 35000 - 29000 einkommensteuer__gesamteinkommen_y: diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_10.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_10.yaml index f64827bb3c..55cc05a8e0 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_10.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_10.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 13400 einkommensteuer__gesamteinkommen_y: - 10816.78 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_11.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_11.yaml index 3a1774b845..fa446bdc77 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_11.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_11.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 17000 einkommensteuer__gesamteinkommen_y: - 13858.72 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_12.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_12.yaml index 5fd537f34e..f201d5eb3d 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_12.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_12.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 59000 einkommensteuer__gesamteinkommen_y: - 53046.4 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_7.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_7.yaml index c965403895..bd5726b3f6 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_7.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_7.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 0 einkommensteuer__gesamteinkommen_y: - 0.0 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_8.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_8.yaml index 727a69325b..e039b630ed 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_8.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_8.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 6200 einkommensteuer__gesamteinkommen_y: - 5173.26 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_9.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_9.yaml index eca499883e..bde317a5fe 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_9.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2018/hh_id_9.yaml @@ -73,7 +73,7 @@ outputs: - 0 einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 9800 einkommensteuer__gesamteinkommen_y: - 7853.59 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2019/hh_id_13.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2019/hh_id_13.yaml index f1a299c167..2492a1a6af 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2019/hh_id_13.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2019/hh_id_13.yaml @@ -106,7 +106,7 @@ outputs: einkommensteuer__abzüge__pauschbetrag_behinderung_y: - 0 - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 17000 - 0 einkommensteuer__gesamteinkommen_y: diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_14.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_14.yaml index 32a2da8c97..3c1516744f 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_14.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_14.yaml @@ -172,7 +172,7 @@ outputs: - 0 - 0 - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 41000 - 23000 - 0 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_19.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_19.yaml index 299fa4690e..e110012e07 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_19.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_19.yaml @@ -172,7 +172,7 @@ outputs: - 0 - 0 - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 41000 - 23000 - 0 diff --git a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_20.yaml b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_20.yaml index c1711a71d0..939c5b3c14 100644 --- a/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_20.yaml +++ b/src/_gettsim_tests/test_data/einkommensteuer/zu_versteuerndes_einkommen/2020/hh_id_20.yaml @@ -172,7 +172,7 @@ outputs: - 0 - 0 - 0 - einkommensteuer__gesamteinkommen_ohne_abzüge_y: + einkommensteuer__einkommensteuer__einkünfte__gesamtbetrag_der_einkünfte_y: - 41000 - 23000 - 0 diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_1.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_1.yaml index c9a612810e..da22dd16d5 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_1.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_1.yaml @@ -81,16 +81,25 @@ inputs: - false - false - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 - 50.0 - 50.0 - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + - 0.0 + - 0.0 + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 + - 0.0 + - 0.0 + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false - false - false - - false elterngeld__bisherige_bezugsmonate: - 0 - 0 diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_2.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_2.yaml index 173a0f44e4..7b1c7a65c2 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_2.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_2.yaml @@ -81,11 +81,21 @@ inputs: - false - false - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 - 50.0 - 50.0 - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + - 0.0 + - 0.0 + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 + - 0.0 + - 0.0 + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false - false diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_3.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_3.yaml index ed60016c2c..06921d87ac 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_3.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_3.yaml @@ -36,8 +36,12 @@ inputs: - 8.0 einkommensteuer__einkünfte__ist_selbstständig: - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false elterngeld__bisherige_bezugsmonate: diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_4.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_4.yaml index d0f22d1716..45ae07cc3d 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_4.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_4.yaml @@ -36,8 +36,12 @@ inputs: - 9.0 einkommensteuer__einkünfte__ist_selbstständig: - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false elterngeld__bisherige_bezugsmonate: diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_5.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_5.yaml index 3ab65adad3..2528614d1b 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_5.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_5.yaml @@ -36,8 +36,12 @@ inputs: - 10.0 einkommensteuer__einkünfte__ist_selbstständig: - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false elterngeld__bisherige_bezugsmonate: diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_6.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_6.yaml index 2939ac7f2e..9a9bb3ab49 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_6.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_6.yaml @@ -36,8 +36,12 @@ inputs: - 11.0 einkommensteuer__einkünfte__ist_selbstständig: - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false elterngeld__bisherige_bezugsmonate: diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_7.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_7.yaml index 4b9b4c384d..0df467869d 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_7.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_7.yaml @@ -36,8 +36,12 @@ inputs: - 12.0 einkommensteuer__einkünfte__ist_selbstständig: - false - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false elterngeld__bisherige_bezugsmonate: diff --git a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_8.yaml b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_8.yaml index 375814ac75..7793676160 100644 --- a/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_8.yaml +++ b/src/_gettsim_tests/test_data/full_taxes_and_transfers/2019/hh_id_8.yaml @@ -36,8 +36,12 @@ inputs: - 13.0 einkommensteuer__einkünfte__ist_selbstständig: - true - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 50.0 + einkommensteuer__einkünfte__aus_forst_und_landwirtschaft__betrag_m: + - 0.0 + einkommensteuer__einkünfte__aus_gewerbebetrieb__betrag_m: + - 0.0 einkommensteuer__gemeinsam_veranlagt: - false elterngeld__bisherige_bezugsmonate: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_13.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_13.yaml index 403b56808c..95be84ce8b 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_13.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_13.yaml @@ -31,7 +31,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_16.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_16.yaml index 0a26896b6e..86d267d400 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_16.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_16.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_17.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_17.yaml index 35ac5c294c..ca9ada9b0b 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_17.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_17.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_6.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_6.yaml index 0cdf7d605c..1bbaaed949 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_6.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2017-07-01/hh_id_6.yaml @@ -30,7 +30,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_12.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_12.yaml index 7d0d960c3b..9717a914fd 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_12.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_12.yaml @@ -23,7 +23,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_20.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_20.yaml index ad16474a28..8aa63c2ad7 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_20.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_20.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_22.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_22.yaml index 0c873152ed..ab697f1752 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_22.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_22.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_3.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_3.yaml index 0eb9537bf5..747f24b131 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_3.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2018-07-01/hh_id_3.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_18.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_18.yaml index 8df7067d1f..6d6eb446a5 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_18.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_18.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_19.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_19.yaml index cf2ed9d736..67b4ff35d7 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_19.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2020-07-01/hh_id_19.yaml @@ -30,7 +30,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_1.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_1.yaml index 4beecbb4c4..7d7bff8adf 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_1.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_1.yaml @@ -30,7 +30,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_14.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_14.yaml index 9841eebf2e..e4ee90a71d 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_14.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_14.yaml @@ -23,7 +23,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_15.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_15.yaml index 5e36d0e795..4af7cd07e0 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_15.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_15.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_2.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_2.yaml index 7fdbf2e658..96c145ed9f 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_2.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_2.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_21.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_21.yaml index 11664ae592..1fb55ac33d 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_21.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_21.yaml @@ -31,7 +31,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_23.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_23.yaml index ee93d97606..ee2e2393c0 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_23.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_23.yaml @@ -31,7 +31,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_5.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_5.yaml index 60fe439f33..85ed9dbe07 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_5.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_5.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_7.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_7.yaml index 8ae137b68f..74f8acb65a 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_7.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_7.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_9.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_9.yaml index 3b442041db..4bc3f02e94 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_9.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2021-07-01/hh_id_9.yaml @@ -31,7 +31,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_10.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_10.yaml index 0542a2bd40..1ad2d025b3 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_10.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_10.yaml @@ -23,7 +23,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_11.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_11.yaml index fafa5be9eb..f281ac2283 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_11.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_11.yaml @@ -23,7 +23,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_24.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_24.yaml index ff54e47082..cb40eafa4d 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_24.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_24.yaml @@ -30,7 +30,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_25.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_25.yaml index 45f1ab9d77..349bd5d25d 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_25.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_25.yaml @@ -31,7 +31,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_4.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_4.yaml index 6fe73b8d52..6e5734e187 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_4.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_4.yaml @@ -30,7 +30,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: diff --git a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_8.yaml b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_8.yaml index 2e9f389a85..602f5c7e62 100644 --- a/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_8.yaml +++ b/src/_gettsim_tests/test_data/grundsicherung/im_alter/2022-07-01/hh_id_8.yaml @@ -22,7 +22,7 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_getrennt_1_kind.yaml b/src/_gettsim_tests/test_data/household_links/2007/eltern_getrennt_1_kind.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_getrennt_1_kind.yaml rename to src/_gettsim_tests/test_data/household_links/2007/eltern_getrennt_1_kind.yaml diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_verheiratet_1_kind.yaml b/src/_gettsim_tests/test_data/household_links/2007/eltern_verheiratet_1_kind.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_verheiratet_1_kind.yaml rename to src/_gettsim_tests/test_data/household_links/2007/eltern_verheiratet_1_kind.yaml diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_verheiratet_2_kinder.yaml b/src/_gettsim_tests/test_data/household_links/2007/eltern_verheiratet_2_kinder.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_verheiratet_2_kinder.yaml rename to src/_gettsim_tests/test_data/household_links/2007/eltern_verheiratet_2_kinder.yaml diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml b/src/_gettsim_tests/test_data/household_links/2007/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2007/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml rename to src/_gettsim_tests/test_data/household_links/2007/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml diff --git "a/src/_gettsim_tests/test_data/aggregate_by_p_id/2007/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" "b/src/_gettsim_tests/test_data/household_links/2007/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" similarity index 100% rename from "src/_gettsim_tests/test_data/aggregate_by_p_id/2007/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" rename to "src/_gettsim_tests/test_data/household_links/2007/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_getrennt_1_kind.yaml b/src/_gettsim_tests/test_data/household_links/2024/eltern_getrennt_1_kind.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_getrennt_1_kind.yaml rename to src/_gettsim_tests/test_data/household_links/2024/eltern_getrennt_1_kind.yaml diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_verheiratet_1_kind.yaml b/src/_gettsim_tests/test_data/household_links/2024/eltern_verheiratet_1_kind.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_verheiratet_1_kind.yaml rename to src/_gettsim_tests/test_data/household_links/2024/eltern_verheiratet_1_kind.yaml diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_verheiratet_2_kinder.yaml b/src/_gettsim_tests/test_data/household_links/2024/eltern_verheiratet_2_kinder.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_verheiratet_2_kinder.yaml rename to src/_gettsim_tests/test_data/household_links/2024/eltern_verheiratet_2_kinder.yaml diff --git a/src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml b/src/_gettsim_tests/test_data/household_links/2024/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml similarity index 100% rename from src/_gettsim_tests/test_data/aggregate_by_p_id/2024/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml rename to src/_gettsim_tests/test_data/household_links/2024/eltern_verheiratet_2_kinder_1_kind_mit_anderem_partner.yaml diff --git "a/src/_gettsim_tests/test_data/aggregate_by_p_id/2024/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" "b/src/_gettsim_tests/test_data/household_links/2024/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" similarity index 100% rename from "src/_gettsim_tests/test_data/aggregate_by_p_id/2024/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" rename to "src/_gettsim_tests/test_data/household_links/2024/gro\303\237eltern_1_kind_eltern_unbekannt.yaml" diff --git a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2018-1/hh_id_5.yaml b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2018-1/hh_id_5.yaml index d5d55c678b..1fa18bd78e 100644 --- a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2018-1/hh_id_5.yaml +++ b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2018-1/hh_id_5.yaml @@ -24,7 +24,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 familie__alleinerziehend: diff --git a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_with_income.yaml b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_with_income.yaml index 66fca147bb..4330c34533 100644 --- a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_with_income.yaml +++ b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_with_income.yaml @@ -30,7 +30,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_without_income.yaml b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_without_income.yaml index 3879793392..b5d1c56586 100644 --- a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_without_income.yaml +++ b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/older_child_parent_without_income.yaml @@ -30,7 +30,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/young_child_parent_without_income.yaml b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/young_child_parent_without_income.yaml index 0c5f9d3e4d..4e2dbd50ce 100644 --- a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/young_child_parent_without_income.yaml +++ b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-1/young_child_parent_without_income.yaml @@ -30,7 +30,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-8/hh_id_1.yaml b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-8/hh_id_1.yaml index 0035c196ef..13da24da86 100644 --- a/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-8/hh_id_1.yaml +++ b/src/_gettsim_tests/test_data/unterhaltsvorschuss/2019-8/hh_id_1.yaml @@ -24,7 +24,7 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 familie__alleinerziehend: diff --git a/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_15.yaml b/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_15.yaml index bbaf707c49..0459c304ed 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_15.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_15.yaml @@ -97,7 +97,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -115,7 +115,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_4.yaml b/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_4.yaml index f753816a02..57b557302a 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_4.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2006/hh_id_4.yaml @@ -66,7 +66,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -78,7 +78,7 @@ inputs: - true - true - true - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2009/vermoegen_zu_hoch.yaml b/src/_gettsim_tests/test_data/wohngeld/2009/vermoegen_zu_hoch.yaml index cf3335cbfd..51e294d119 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2009/vermoegen_zu_hoch.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2009/vermoegen_zu_hoch.yaml @@ -39,13 +39,13 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 250.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 elterngeld__betrag_m: diff --git a/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_2.yaml b/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_2.yaml index c383bac6d4..686c5070d0 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_2.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_2.yaml @@ -36,13 +36,13 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 100.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: - true - true - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 elterngeld__betrag_m: diff --git a/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_9.yaml b/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_9.yaml index 19b81423a0..32afef27d9 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_9.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2013/hh_id_9.yaml @@ -36,13 +36,13 @@ inputs: einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 100.0 - 0.0 einkommensteuer__gemeinsam_veranlagt: - true - true - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 elterngeld__betrag_m: diff --git a/src/_gettsim_tests/test_data/wohngeld/2016/hh_id_1.yaml b/src/_gettsim_tests/test_data/wohngeld/2016/hh_id_1.yaml index 6c769cb371..dfb3eca805 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2016/hh_id_1.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2016/hh_id_1.yaml @@ -50,7 +50,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -58,7 +58,7 @@ inputs: - true - true - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_7.yaml b/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_7.yaml index e1773902d3..fcf2d23a92 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_7.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_7.yaml @@ -146,7 +146,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -174,7 +174,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_8.yaml b/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_8.yaml index acbc6684ee..282ace279d 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_8.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2018/hh_id_8.yaml @@ -76,7 +76,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -90,7 +90,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2018/vermoegen_zu_hoch.yaml b/src/_gettsim_tests/test_data/wohngeld/2018/vermoegen_zu_hoch.yaml index 5acad76b1d..9f58ce4c42 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2018/vermoegen_zu_hoch.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2018/vermoegen_zu_hoch.yaml @@ -76,7 +76,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 800.0 - 0.0 - 0.0 @@ -90,7 +90,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2019/hh_id_5.yaml b/src/_gettsim_tests/test_data/wohngeld/2019/hh_id_5.yaml index e1ca6d89c6..35b8fe455f 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2019/hh_id_5.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2019/hh_id_5.yaml @@ -26,11 +26,11 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 elterngeld__betrag_m: - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2021/hh_id_10.yaml b/src/_gettsim_tests/test_data/wohngeld/2021/hh_id_10.yaml index 6b2ac8f2ba..69b9c0126b 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2021/hh_id_10.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2021/hh_id_10.yaml @@ -28,11 +28,11 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 elterngeld__betrag_m: - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_11.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_11.yaml index 152ce3fdee..d42812889a 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_11.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_11.yaml @@ -30,11 +30,11 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 elterngeld__betrag_m: - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_12.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_12.yaml index 2dbc8d5a93..adb5802a0e 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_12.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_12.yaml @@ -30,11 +30,11 @@ inputs: - 0.0 einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 einkommensteuer__gemeinsam_veranlagt: - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 elterngeld__betrag_m: - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_13.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_13.yaml index 097a040023..219a0ef52c 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_13.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_13.yaml @@ -54,7 +54,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -62,7 +62,7 @@ inputs: - true - true - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_14.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_14.yaml index 3a15e03bfc..3660c774d0 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_14.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_14.yaml @@ -90,7 +90,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -104,7 +104,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_16.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_16.yaml index 92c861d1aa..106686b68e 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_16.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_16.yaml @@ -66,7 +66,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -76,7 +76,7 @@ inputs: - true - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_17.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_17.yaml index 538687c4e1..ccffebac6b 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_17.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/hh_id_17.yaml @@ -78,7 +78,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -90,7 +90,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/mindesteinkommen.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/mindesteinkommen.yaml index 14c2ffa004..aebe1d9b7a 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/mindesteinkommen.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/mindesteinkommen.yaml @@ -76,7 +76,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -90,7 +90,7 @@ inputs: - true - true - true - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_einer_sgb_ii_bg.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_einer_sgb_ii_bg.yaml index 61b54fba52..eee2be192b 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_einer_sgb_ii_bg.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_einer_sgb_ii_bg.yaml @@ -72,7 +72,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -84,7 +84,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_mehreren_sgb_ii_bg.yaml b/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_mehreren_sgb_ii_bg.yaml index 2f2a5e3a42..778bbbaab1 100644 --- a/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_mehreren_sgb_ii_bg.yaml +++ b/src/_gettsim_tests/test_data/wohngeld/2023/mischhaushalt_mit_mehreren_sgb_ii_bg.yaml @@ -94,7 +94,7 @@ inputs: - 0.0 - 0.0 - 0.0 - einkommensteuer__einkünfte__sonstige__betrag_m: + einkommensteuer__einkünfte__sonstige__ohne_renten_m: - 0.0 - 0.0 - 0.0 @@ -110,7 +110,7 @@ inputs: - false - false - false - einkommensteuer__rente_ertragsanteil: + einkommensteuer__einkünfte__sonstige__rente_ertragsanteil: - 0.0 - 0.0 - 0.0 diff --git a/src/_gettsim_tests/test_full_taxes_and_transfers.py b/src/_gettsim_tests/test_full_taxes_and_transfers.py index 09de9fbf19..0ef23d490d 100644 --- a/src/_gettsim_tests/test_full_taxes_and_transfers.py +++ b/src/_gettsim_tests/test_full_taxes_and_transfers.py @@ -1,10 +1,14 @@ import dags.tree as dt import pytest -from _gettsim.config import TYPES_INPUT_VARIABLES -from _gettsim_tests._helpers import cached_set_up_policy_environment -from _gettsim_tests._policy_test_utils import PolicyTest, load_policy_test_data +from _gettsim.config import FOREIGN_KEYS, SUPPORTED_GROUPINGS +from _gettsim_tests.utils import ( + PolicyTest, + cached_set_up_policy_environment, + load_policy_test_data, +) from ttsim import compute_taxes_and_transfers +from ttsim.function_types import PolicyInput from ttsim.typing import check_series_has_expected_type test_data = load_policy_test_data("full_taxes_and_transfers") @@ -18,6 +22,8 @@ def test_full_taxes_transfers(test: PolicyTest): data_tree=test.input_tree, environment=environment, targets_tree=test.target_structure, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) @@ -29,10 +35,16 @@ def test_data_types(test: PolicyTest): data_tree=test.input_tree, environment=environment, targets_tree=test.target_structure, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) - flat_types_input_variables = dt.flatten_to_qual_names(TYPES_INPUT_VARIABLES) - flat_functions = dt.flatten_to_qual_names(environment.functions_tree) + flat_types_input_variables = { + n: pi.data_type + for n, pi in dt.flatten_to_qual_names(environment.raw_objects_tree).items() + if isinstance(pi, PolicyInput) + } + flat_functions = dt.flatten_to_qual_names(environment.raw_objects_tree) for column_name, result_array in dt.flatten_to_qual_names(result).items(): if column_name in flat_types_input_variables: @@ -51,6 +63,9 @@ def test_data_types(test: PolicyTest): assert check_series_has_expected_type(result_array, internal_type) +@pytest.mark.skip( + reason="Got rid of DEFAULT_TARGETS, there might not be a replacement." +) @pytest.mark.parametrize("test", test_data, ids=lambda x: x.test_name) def test_allow_none_as_target_tree(test: PolicyTest): environment = cached_set_up_policy_environment(date=test.date) @@ -59,4 +74,6 @@ def test_allow_none_as_target_tree(test: PolicyTest): data_tree=test.input_tree, environment=environment, targets_tree=None, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) diff --git a/src/_gettsim_tests/test_groupings.py b/src/_gettsim_tests/test_groupings.py index 7927a7cc6d..c5572d4867 100644 --- a/src/_gettsim_tests/test_groupings.py +++ b/src/_gettsim_tests/test_groupings.py @@ -3,8 +3,12 @@ import pytest from numpy.testing import assert_array_almost_equal -from _gettsim_tests._helpers import cached_set_up_policy_environment -from _gettsim_tests._policy_test_utils import PolicyTest, load_policy_test_data +from _gettsim.config import FOREIGN_KEYS, SUPPORTED_GROUPINGS +from _gettsim_tests.utils import ( + PolicyTest, + cached_set_up_policy_environment, + load_policy_test_data, +) from ttsim import compute_taxes_and_transfers test_data = load_policy_test_data("groupings") @@ -18,6 +22,8 @@ def test_groupings(test: PolicyTest): data_tree=test.input_tree, environment=environment, targets_tree=test.target_structure, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) flat_result = dt.flatten_to_qual_names(result) @@ -50,4 +56,6 @@ def test_fail_to_compute_sn_id_if_married_but_gemeinsam_veranlagt_differs(): data_tree=data, environment=environment, targets_tree={"einkommensteuer": {"sn_id": None}}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) diff --git a/src/_gettsim_tests/test_grundrente.py b/src/_gettsim_tests/test_grundrente_proxy_rente.py similarity index 74% rename from src/_gettsim_tests/test_grundrente.py rename to src/_gettsim_tests/test_grundrente_proxy_rente.py index eefcf2fe12..e1a2606fca 100644 --- a/src/_gettsim_tests/test_grundrente.py +++ b/src/_gettsim_tests/test_grundrente_proxy_rente.py @@ -4,17 +4,20 @@ import pytest from numpy.testing import assert_array_almost_equal -from _gettsim_tests._helpers import cached_set_up_policy_environment -from _gettsim_tests._policy_test_utils import PolicyTest, load_policy_test_data +from _gettsim.config import FOREIGN_KEYS, SUPPORTED_GROUPINGS +from _gettsim_tests.utils import ( + PolicyTest, + cached_set_up_policy_environment, + load_policy_test_data, +) from ttsim import compute_taxes_and_transfers -proxy_rente_test_data = load_policy_test_data("grundrente_proxy_rente") +proxy_rente_test_data = load_policy_test_data( + "sozialversicherung/rente/grundrente_proxy_rente" +) -@pytest.mark.parametrize( - "test", - proxy_rente_test_data, -) +@pytest.mark.parametrize("test", proxy_rente_test_data) def test_grundrente_proxy_rente_vorjahr_comparison(test: PolicyTest): environment = cached_set_up_policy_environment(date=test.date) @@ -26,6 +29,8 @@ def test_grundrente_proxy_rente_vorjahr_comparison(test: PolicyTest): "rente": {"grundrente": {"proxy_rente_vorjahr_m": None}} } }, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) # Calculate pension of last year @@ -37,6 +42,8 @@ def test_grundrente_proxy_rente_vorjahr_comparison(test: PolicyTest): targets_tree={ "sozialversicherung": {"rente": {"altersrente": {"bruttorente_m": None}}} }, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) flat_result = dt.flatten_to_qual_names(result) diff --git a/tests/ttsim/test_aggregate_by_p_id.py b/src/_gettsim_tests/test_household_links.py similarity index 70% rename from tests/ttsim/test_aggregate_by_p_id.py rename to src/_gettsim_tests/test_household_links.py index 388328360d..a46a71a0fc 100644 --- a/tests/ttsim/test_aggregate_by_p_id.py +++ b/src/_gettsim_tests/test_household_links.py @@ -2,11 +2,15 @@ import pytest from numpy.testing import assert_array_almost_equal -from _gettsim_tests._helpers import cached_set_up_policy_environment -from _gettsim_tests._policy_test_utils import PolicyTest, load_policy_test_data +from _gettsim.config import FOREIGN_KEYS, SUPPORTED_GROUPINGS +from _gettsim_tests.utils import ( + PolicyTest, + cached_set_up_policy_environment, + load_policy_test_data, +) from ttsim import compute_taxes_and_transfers -test_data = load_policy_test_data("aggregate_by_p_id") +test_data = load_policy_test_data("household_links") @pytest.mark.parametrize("test", test_data) @@ -17,6 +21,8 @@ def test_aggregate_by_p_id(test: PolicyTest): data_tree=test.input_tree, environment=environment, targets_tree=test.target_structure, + foreign_keys=FOREIGN_KEYS, + supported_groupings=SUPPORTED_GROUPINGS, ) flat_result = dt.flatten_to_qual_names(result) diff --git a/src/_gettsim_tests/test_lohnsteuer.py b/src/_gettsim_tests/test_lohnsteuer.py index 7e803d1d42..ab43424c36 100644 --- a/src/_gettsim_tests/test_lohnsteuer.py +++ b/src/_gettsim_tests/test_lohnsteuer.py @@ -1,15 +1,16 @@ +from _gettsim.config import RESOURCE_DIR from ttsim import set_up_policy_environment def test_lohnsteuer_rv_anteil(): - environment = set_up_policy_environment(2018) + environment = set_up_policy_environment(resource_dir=RESOURCE_DIR, date=2018) assert ( environment.params["eink_st_abzuege"]["vorsorgepauschale_rentenv_anteil"] == 0.72 ) - environment = set_up_policy_environment(2023) + environment = set_up_policy_environment(resource_dir=RESOURCE_DIR, date=2023) assert ( environment.params["eink_st_abzuege"]["vorsorgepauschale_rentenv_anteil"] == 1 diff --git a/src/_gettsim_tests/test_steuern_und_transfers.py b/src/_gettsim_tests/test_policy.py similarity index 87% rename from src/_gettsim_tests/test_steuern_und_transfers.py rename to src/_gettsim_tests/test_policy.py index 815448f2ce..d3fbb798f9 100644 --- a/src/_gettsim_tests/test_steuern_und_transfers.py +++ b/src/_gettsim_tests/test_policy.py @@ -1,6 +1,6 @@ import pytest -from _gettsim_tests._policy_test_utils import ( +from _gettsim_tests.utils import ( PolicyTest, execute_test, get_policy_test_ids_and_cases, diff --git a/src/_gettsim_tests/test_synthetic.py b/src/_gettsim_tests/test_synthetic.py index 985c002806..3b94cf5d03 100644 --- a/src/_gettsim_tests/test_synthetic.py +++ b/src/_gettsim_tests/test_synthetic.py @@ -4,7 +4,6 @@ import pandas as pd import pytest -from _gettsim.config import DEFAULT_TARGETS from _gettsim.synthetic import create_synthetic_data from ttsim import compute_taxes_and_transfers, set_up_policy_environment @@ -349,6 +348,6 @@ def test_default_targets(fixture, policy_date, request): environment = set_up_policy_environment(policy_date) compute_taxes_and_transfers( data_tree=request.getfixturevalue(fixture), - targets_tree=DEFAULT_TARGETS, + targets_tree=fixme, environment=environment, ) diff --git a/src/_gettsim_tests/utils.py b/src/_gettsim_tests/utils.py new file mode 100644 index 0000000000..76ef3d7532 --- /dev/null +++ b/src/_gettsim_tests/utils.py @@ -0,0 +1,230 @@ +from __future__ import annotations + +import datetime +from functools import lru_cache +from typing import TYPE_CHECKING + +import dags.tree as dt +import pandas as pd +import yaml + +from _gettsim.config import FOREIGN_KEYS, RESOURCE_DIR, SUPPORTED_GROUPINGS +from _gettsim_tests import TEST_DIR +from ttsim import ( + PolicyEnvironment, + merge_trees, + set_up_policy_environment, +) +from ttsim.policy_environment import _parse_date + +if TYPE_CHECKING: + from pathlib import Path + + from ttsim import NestedDataDict, NestedInputStructureDict + + +def cached_set_up_policy_environment( + date: int | str | datetime.date, +) -> PolicyEnvironment: + normalized_date = _parse_date(date) + return _cached_set_up_policy_environment(normalized_date) + + +@lru_cache(maxsize=100) +def _cached_set_up_policy_environment(date: datetime.date) -> PolicyEnvironment: + return set_up_policy_environment(date=date, resource_dir=RESOURCE_DIR) + + +class PolicyTest: + """A class for a single policy test.""" + + def __init__( + self, + info: NestedDataDict, + input_tree: NestedDataDict, + expected_output_tree: NestedDataDict, + path: Path, + date: datetime.date, + ) -> None: + self.info = info + self.input_tree = input_tree + self.expected_output_tree = expected_output_tree + self.path = path + self.date = date + + @property + def target_structure(self) -> NestedInputStructureDict: + flat_target_structure = dict.fromkeys( + dt.flatten_to_tree_paths(self.expected_output_tree) + ) + return dt.unflatten_from_tree_paths(flat_target_structure) + + @property + def test_name(self) -> str: + return self.path.relative_to(TEST_DIR / "test_data").as_posix() + + +def execute_test(test: PolicyTest): + from pandas.testing import assert_frame_equal + + from _gettsim_tests.utils import cached_set_up_policy_environment + from ttsim import compute_taxes_and_transfers + + environment = cached_set_up_policy_environment(date=test.date) + + result = compute_taxes_and_transfers( + data_tree=test.input_tree, + environment=environment, + targets_tree=test.target_structure, + supported_groupings=SUPPORTED_GROUPINGS, + foreign_keys=FOREIGN_KEYS, + ) + + flat_result = dt.flatten_to_qual_names(result) + flat_expected_output_tree = dt.flatten_to_qual_names(test.expected_output_tree) + + if flat_expected_output_tree: + result_dataframe = pd.DataFrame(flat_result) + expected_dataframe = pd.DataFrame(flat_expected_output_tree) + assert_frame_equal( + result_dataframe, + expected_dataframe, + atol=test.info["precision"], + check_dtype=False, + ) + + +def get_policy_test_ids_and_cases() -> dict[str, PolicyTest]: + all_policy_tests = load_policy_test_data("") + return {policy_test.test_name: policy_test for policy_test in all_policy_tests} + + +def load_policy_test_data(policy_name: str) -> list[PolicyTest]: + out = [] + + for path_to_yaml in (TEST_DIR / "test_data" / policy_name).glob("**/*.yaml"): + if _is_skipped(path_to_yaml): + continue + + with path_to_yaml.open("r", encoding="utf-8") as file: + raw_test_data: NestedDataDict = yaml.safe_load(file) + + # TODO(@MImmesberger): Remove this before merging this PR. + raw_test_data["inputs"], raw_test_data["outputs"] = get_test_data_as_tree( + raw_test_data + ) + + out.extend( + _get_policy_tests_from_raw_test_data( + raw_test_data=raw_test_data, + path_to_yaml=path_to_yaml, + ) + ) + + return out + + +def get_test_data_as_tree(test_data: NestedDataDict) -> NestedDataDict: + provided_inputs = test_data["inputs"].get("provided", {}) + assumed_inputs = test_data["inputs"].get("assumed", {}) + + unflattened_dict = {} + unflattened_dict["inputs"] = {} + unflattened_dict["outputs"] = {} + + if provided_inputs: + unflattened_dict["inputs"]["provided"] = dt.unflatten_from_qual_names( + provided_inputs + ) + else: + unflattened_dict["inputs"]["provided"] = {} + if assumed_inputs: + unflattened_dict["inputs"]["assumed"] = dt.unflatten_from_qual_names( + assumed_inputs + ) + else: + unflattened_dict["inputs"]["assumed"] = {} + + unflattened_dict["outputs"] = dt.unflatten_from_qual_names(test_data["outputs"]) + + return unflattened_dict["inputs"], unflattened_dict["outputs"] + + +def _is_skipped(test_file: Path) -> bool: + return "skip" in test_file.stem or "skip" in test_file.parent.name + + +def _get_policy_tests_from_raw_test_data( + raw_test_data: NestedDataDict, path_to_yaml: Path +) -> list[PolicyTest]: + """Get a list of PolicyTest objects from raw test data. + + Args: + raw_test_data: The raw test data. + path_to_yaml: The path to the YAML file. + + Returns: + A list of PolicyTest objects. + """ + test_info: NestedDataDict = raw_test_data.get("info", {}) + inputs: NestedDataDict = raw_test_data.get("inputs", {}) + input_tree: NestedDataDict = dt.unflatten_from_tree_paths( + { + k: pd.Series(v) + for k, v in dt.flatten_to_tree_paths( + merge_trees(inputs.get("provided", {}), inputs.get("assumed", {})) + ).items() + } + ) + + expected_output_tree: NestedDataDict = dt.unflatten_from_tree_paths( + { + k: pd.Series(v) + for k, v in dt.flatten_to_tree_paths( + raw_test_data.get("outputs", {}) + ).items() + } + ) + + date: datetime.date = _parse_date_from_dir_name(path_to_yaml.parent.name) + + out = [] + if expected_output_tree == {}: + out.append( + PolicyTest( + info=test_info, + input_tree=input_tree, + expected_output_tree={}, + path=path_to_yaml, + date=date, + ) + ) + else: + for target_name, output_data in dt.flatten_to_tree_paths( + expected_output_tree + ).items(): + one_expected_output: NestedDataDict = dt.unflatten_from_tree_paths( + {target_name: output_data} + ) + out.append( + PolicyTest( + info=test_info, + input_tree=input_tree, + expected_output_tree=one_expected_output, + path=path_to_yaml, + date=date, + ) + ) + + return out + + +def _parse_date_from_dir_name(date: str) -> datetime.date: + parts = date.split("-") + + if len(parts) == 1: + return datetime.date(int(parts[0]), 1, 1) + if len(parts) == 2: + return datetime.date(int(parts[0]), int(parts[1]), 1) + if len(parts) == 3: + return datetime.date(int(parts[0]), int(parts[1]), int(parts[2])) diff --git a/src/ttsim/__init__.py b/src/ttsim/__init__.py index 55ad2a3d7c..c640619981 100644 --- a/src/ttsim/__init__.py +++ b/src/ttsim/__init__.py @@ -9,18 +9,20 @@ DerivedTimeConversionFunction, GroupByFunction, PolicyFunction, + PolicyInput, group_by_function, policy_function, + policy_input, ) from ttsim.loader import ( - ConflictingTimeDependentFunctionsError, - get_active_functions_tree_from_module, + ConflictingTimeDependentObjectsError, + get_active_ttsim_objects_tree_from_module, load_aggregation_specs_tree, - load_functions_tree_for_date, + load_objects_tree_for_date, ) from ttsim.piecewise_polynomial import get_piecewise_parameters, piecewise_polynomial from ttsim.policy_environment import PolicyEnvironment, set_up_policy_environment -from ttsim.rounding import RoundingDirection, RoundingSpec +from ttsim.rounding import RoundingSpec from ttsim.shared import ( insert_path_and_value, join_numpy, @@ -35,29 +37,30 @@ "AggregateByGroupSpec", "AggregateByPIDSpec", "AggregationType", - "ConflictingTimeDependentFunctionsError", + "ConflictingTimeDependentObjectsError", "DerivedAggregationFunction", "DerivedTimeConversionFunction", "FunctionsAndColumnsOverlapWarning", "GroupByFunction", "PolicyEnvironment", "PolicyFunction", - "RoundingDirection", + "PolicyInput", "RoundingSpec", "combine_policy_functions_and_derived_functions", "compute_taxes_and_transfers", "create_time_conversion_functions", - "get_active_functions_tree_from_module", + "get_active_ttsim_objects_tree_from_module", "get_piecewise_parameters", "group_by_function", "insert_path_and_value", "join_numpy", "load_aggregation_specs_tree", - "load_functions_tree_for_date", + "load_objects_tree_for_date", "merge_trees", "piecewise_polynomial", "plot_dag", "policy_function", + "policy_input", "set_up_policy_environment", "upsert_path_and_value", "upsert_tree", diff --git a/src/ttsim/aggregation.py b/src/ttsim/aggregation.py index 8a705fea19..bedff13ab5 100644 --- a/src/ttsim/aggregation.py +++ b/src/ttsim/aggregation.py @@ -48,23 +48,34 @@ class AggregationType(StrEnum): @dataclass -class AggregateByGroupSpec: +class AggregationSpec: """ - A container for aggregate by group specifications. + Base class for aggregation specifications. Only use for type checking. """ - aggr: AggregationType - source: str | None = None + target: str + source: str | None + agg: AggregationType _agg_func: Callable = field(init=False) def __post_init__(self): - if not isinstance(self.aggr, AggregationType): - raise ValueError( - f"aggr must be of type AggregationType, not {type(self.aggr)}" + if not isinstance(self.agg, AggregationType): + raise TypeError( + f"agg must be of type AggregationType, not {type(self.agg)}" ) - if self.aggr == AggregationType.COUNT and self.source is not None: - raise ValueError("COUNT aggregation cannot use a source.") + if self.agg == AggregationType.COUNT and self.source is not None: + raise ValueError("COUNT aggregation must not provide a source.") + + +@dataclass +class AggregateByGroupSpec(AggregationSpec): + """ + A container for aggregate by group specifications. + """ + + def __post_init__(self): + super().__post_init__() aggregation_registry = { AggregationType.SUM: grouped_sum, @@ -76,30 +87,64 @@ def __post_init__(self): AggregationType.COUNT: grouped_count, } - func = aggregation_registry.get(self.aggr) + func = aggregation_registry.get(self.agg) if func is None: - raise ValueError(f"Aggregation type {self.aggr} not implemented") + raise ValueError(f"Aggregation type {self.agg} not implemented") self._agg_func = func def agg_func(self, source, group_by_id): + # Need to leave this interface in order for renaming of arguments to work. return self._agg_func(source, group_by_id) def mapper(self, group_by_id): - if self.aggr == AggregationType.COUNT: + if self.agg == AggregationType.COUNT: return {"group_by_id": group_by_id} return {"source": self.source, "group_by_id": group_by_id} @dataclass -class AggregateByPIDSpec: +class AggregateByPIDSpec(AggregationSpec): """ A container for aggregate by p_id specifications. """ p_id_to_aggregate_by: str - source: str - aggr: AggregationType + + def __post_init__(self): + super().__post_init__() + + aggregation_registry = { + AggregationType.SUM: sum_by_p_id, + AggregationType.MEAN: mean_by_p_id, + AggregationType.MAX: max_by_p_id, + AggregationType.MIN: min_by_p_id, + AggregationType.ANY: any_by_p_id, + AggregationType.ALL: all_by_p_id, + AggregationType.COUNT: count_by_p_id, + } + + func = aggregation_registry.get(self.agg) + if func is None: + raise ValueError(f"Aggregation type {self.agg} not implemented") + + self._agg_func = func + + def agg_func(self, source, p_id_to_aggregate_by, p_id_to_store_by): + # Need to leave this interface in order for renaming of arguments to work. + return self._agg_func(source, p_id_to_aggregate_by, p_id_to_store_by) + + def mapper(self): + if self.agg == AggregationType.COUNT: + return { + "p_id_to_aggregate_by": self.p_id_to_aggregate_by, + "p_id_to_store_by": "p_id", + } + return { + "source": self.source, + "p_id_to_aggregate_by": self.p_id_to_aggregate_by, + "p_id_to_store_by": "p_id", + } def grouped_count(group_id): diff --git a/src/ttsim/combine_functions.py b/src/ttsim/combine_functions.py index 498040d841..aa98c4c17f 100644 --- a/src/ttsim/combine_functions.py +++ b/src/ttsim/combine_functions.py @@ -6,23 +6,17 @@ import dags import dags.tree as dt -from _gettsim.config import ( - SUPPORTED_GROUPINGS, - TYPES_INPUT_VARIABLES, -) from ttsim.aggregation import ( AggregateByGroupSpec, AggregateByPIDSpec, AggregationType, - all_by_p_id, - any_by_p_id, - count_by_p_id, - max_by_p_id, - mean_by_p_id, - min_by_p_id, - sum_by_p_id, ) -from ttsim.function_types import DerivedAggregationFunction, GroupByFunction +from ttsim.function_types import ( + DEFAULT_END_DATE, + DEFAULT_START_DATE, + DerivedAggregationFunction, + GroupByFunction, +) from ttsim.shared import ( format_errors_and_warnings, format_list_linewise, @@ -38,18 +32,21 @@ from ttsim.typing import ( QualNameAggregationSpecsDict, QualNameDataDict, - QualNameFunctionsDict, + QualNamePolicyInputDict, QualNameTargetList, + QualNameTTSIMFunctionDict, ) def combine_policy_functions_and_derived_functions( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, aggregation_specs_from_environment: QualNameAggregationSpecsDict, targets: QualNameTargetList, data: QualNameDataDict, + inputs: QualNamePolicyInputDict, top_level_namespace: set[str], -) -> QualNameFunctionsDict: + supported_groupings: tuple[str, ...], +) -> QualNameTTSIMFunctionDict: """Add derived functions to the qualified functions dict. Derived functions are time converted functions and aggregation functions (aggregate @@ -78,12 +75,14 @@ def combine_policy_functions_and_derived_functions( The qualified functions dict with derived functions. """ - # Create parent-child relationships + # Create parent-child relationships and similar. aggregate_by_p_id_functions = _create_aggregation_functions( functions=functions, + inputs=inputs, aggregation_functions_to_create=aggregation_specs_from_environment, top_level_namespace=top_level_namespace, aggregation_type="p_id", + supported_groupings=supported_groupings, ) current_functions = {**aggregate_by_p_id_functions, **functions} @@ -94,13 +93,15 @@ def combine_policy_functions_and_derived_functions( ) current_functions = {**time_conversion_functions, **current_functions} - # Create aggregation functions + # Create aggregation functions by group. aggregate_by_group_functions = _create_aggregate_by_group_functions( functions=current_functions, targets=targets, data=data, + inputs=inputs, aggregations_from_environment=aggregation_specs_from_environment, top_level_namespace=top_level_namespace, + supported_groupings=supported_groupings, ) current_functions = {**aggregate_by_group_functions, **current_functions} @@ -110,19 +111,24 @@ def combine_policy_functions_and_derived_functions( def _create_aggregate_by_group_functions( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, + inputs: QualNamePolicyInputDict, targets: QualNameTargetList, data: QualNameDataDict, aggregations_from_environment: QualNameAggregationSpecsDict, top_level_namespace: set[str], -) -> QualNameFunctionsDict: + supported_groupings: tuple[str, ...], +) -> QualNameTTSIMFunctionDict: """Create aggregation functions.""" # Create the aggregation functions that were explicitly specified. + aggregation_functions_from_environment = _create_aggregation_functions( functions=functions, + inputs=inputs, aggregation_functions_to_create=aggregations_from_environment, aggregation_type="group", top_level_namespace=top_level_namespace, + supported_groupings=supported_groupings, ) functions_with_aggregation_functions_from_environment = { @@ -136,12 +142,15 @@ def _create_aggregate_by_group_functions( targets=targets, data=data, top_level_namespace=top_level_namespace, + supported_groupings=supported_groupings, ) aggregation_functions_derived_from_names = _create_aggregation_functions( functions=functions_with_aggregation_functions_from_environment, + inputs=inputs, aggregation_functions_to_create=derived_aggregation_specs, aggregation_type="group", top_level_namespace=top_level_namespace, + supported_groupings=supported_groupings, ) return { **aggregation_functions_derived_from_names, @@ -150,11 +159,13 @@ def _create_aggregate_by_group_functions( def _create_aggregation_functions( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, + inputs: QualNamePolicyInputDict, aggregation_functions_to_create: QualNameAggregationSpecsDict, aggregation_type: Literal["group", "p_id"], top_level_namespace: set[str], -) -> QualNameFunctionsDict: + supported_groupings: tuple[str, ...], +) -> QualNameTTSIMFunctionDict: """Create aggregation functions for one aggregation type. Parameters @@ -186,54 +197,135 @@ def _create_aggregation_functions( ) aggregation_functions = {} - for target_name, aggregation_spec in aggregation_functions_to_create.items(): + for qual_name_target, aggregation_spec in aggregation_functions_to_create.items(): # Skip if aggregation spec is not the current aggregation type if not isinstance(aggregation_spec, expected_aggregation_spec_type): continue if aggregation_type == "group": group_by_id_name = get_name_of_group_by_id( - target_name=target_name, + target_name=qual_name_target, group_by_functions=group_by_functions, + supported_groupings=supported_groupings, ) - if not group_by_id_name: msg = format_errors_and_warnings( "Name of aggregated column needs to have a suffix " "indicating the group over which it is aggregated. " - f"{dt.tree_path_from_qual_name(target_name)} does not do so." + f"{dt.tree_path_from_qual_name(qual_name_target)} does not do so." ) raise ValueError(msg) - derived_func = _create_one_aggregate_by_group_func( - aggregation_target=target_name, - aggregation_spec=aggregation_spec, - group_by_id=group_by_id_name, - functions=functions, - top_level_namespace=top_level_namespace, - ) else: - derived_func = _create_one_aggregate_by_p_id_func( - aggregation_target=target_name, - aggregation_spec=aggregation_spec, - functions=functions, - top_level_namespace=top_level_namespace, - ) + group_by_id_name = None - aggregation_functions[target_name] = derived_func + derived_func = _create_one_aggregation_function( + aggregation_target=qual_name_target, + aggregation_spec=aggregation_spec, + aggregation_type=aggregation_type, + group_by_id=group_by_id_name, + functions=functions, + inputs=inputs, + top_level_namespace=top_level_namespace, + ) + if derived_func is not None: + aggregation_functions[qual_name_target] = derived_func return _annotate_aggregation_functions( functions=functions, + inputs=inputs, aggregation_functions=aggregation_functions, - types_input_variables=dt.flatten_to_qual_names(TYPES_INPUT_VARIABLES), + ) + + +def _create_one_aggregation_function( + aggregation_target: str, + aggregation_spec: AggregateByGroupSpec | AggregateByPIDSpec, + aggregation_type: Literal["group", "p_id"], + group_by_id: str | None, + functions: QualNameTTSIMFunctionDict, + inputs: QualNamePolicyInputDict, + top_level_namespace: set[str], +) -> DerivedAggregationFunction | None: + """Create a single aggregation function. + + Parameters + ---------- + aggregation_target + The qualified name of the target column. + aggregation_spec + The aggregation specification. + aggregation_type + The type of aggregation ("group" or "p_id"). + group_by_id + The name of the group by id column. Only required for group aggregations. + functions + Dict with qualified function names as keys and functions as values. + inputs + Dict with qualified input names as keys and policy inputs as values. + top_level_namespace + Set of top-level namespaces. + + Returns + ------- + The derived aggregation function. + """ + if aggregation_type == "group": + mapper = aggregation_spec.mapper(group_by_id) + else: + mapper = aggregation_spec.mapper() + + wrapped_func = dt.one_function_without_tree_logic( + function=dags.rename_arguments( + func=aggregation_spec.agg_func, + mapper=mapper, + ), + tree_path=dt.tree_path_from_qual_name(aggregation_target), + top_level_namespace=top_level_namespace, + ) + + qual_name_source = ( + _get_qual_name_of_source_col( + source=aggregation_spec.source, + wrapped_func=wrapped_func, + ) + if aggregation_spec.source + else None + ) + if qual_name_source in inputs: + start_date = inputs[qual_name_source].start_date + end_date = inputs[qual_name_source].end_date + elif qual_name_source in functions: + start_date = functions[qual_name_source].start_date + end_date = functions[qual_name_source].end_date + elif qual_name_source is None: + # Aggregation method is count + start_date = DEFAULT_START_DATE + end_date = DEFAULT_END_DATE + else: + start_date = DEFAULT_START_DATE + end_date = DEFAULT_END_DATE + # TODO(@MImmesberger): Once we filter the aggregations by start and end date, + # we should throw an error here. The qualified source name should be in either + # functions or inputs (or None if it is a count). + # https://github.com/iza-institute-of-labor-economics/gettsim/issues/870 + + return DerivedAggregationFunction( + leaf_name=dt.tree_path_from_qual_name(aggregation_target)[-1], + function=wrapped_func, + source=qual_name_source, + aggregation_method=aggregation_spec.agg, + start_date=start_date, + end_date=end_date, ) def _create_derived_aggregations_specs( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, targets: QualNameTargetList, data: QualNameDataDict, top_level_namespace: set[str], + supported_groupings: tuple[str, ...], ) -> QualNameAggregationSpecsDict: """Create automatic aggregation specs derived from functions and data. @@ -282,13 +374,14 @@ def _create_derived_aggregations_specs( # Don't create aggregation functions for unsupported groupings or functions that # already exist in the source tree. aggregation_specs_needed = ( - any(target_name.endswith(f"_{g}") for g in SUPPORTED_GROUPINGS) + any(target_name.endswith(f"_{g}") for g in supported_groupings) and target_name not in aggregation_sources ) if aggregation_specs_needed: derived_aggregations_specs[target_name] = AggregateByGroupSpec( - aggr=AggregationType.SUM, + target=target_name, + agg=AggregationType.SUM, source=_get_name_of_aggregation_source( target_name=target_name, top_level_namespace=top_level_namespace, @@ -301,7 +394,7 @@ def _create_derived_aggregations_specs( def _get_potential_aggregation_function_names_from_function_arguments( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, ) -> set[str]: """Get potential aggregation function names from function arguments. @@ -333,177 +426,11 @@ def _select_return_type(aggregation_method: str, source_col_type: type) -> type: return return_type -def _create_one_aggregate_by_group_func( - aggregation_target: str, - aggregation_spec: AggregateByGroupSpec, - group_by_id: str, - functions: QualNameFunctionsDict, - top_level_namespace: set[str], -) -> DerivedAggregationFunction: - """Create an aggregation function based on aggregation specification. - - Parameters - ---------- - aggregation_target - Leaf name of the aggregation target. - aggregation_spec - The aggregation specification. - annotations - The annotations for the derived function. - group_by_id - The group-by-identifier. - functions - The functions dict with qualified function names as keys and functions as - values. - top_level_namespace - Set of top-level namespaces. - - Returns - ------- - The derived function. - - """ - source = aggregation_spec.source - - wrapped_func = dt.one_function_without_tree_logic( - function=dags.rename_arguments( - func=aggregation_spec.agg_func, - mapper=aggregation_spec.mapper(group_by_id), - ), - tree_path=dt.tree_path_from_qual_name(aggregation_target), - top_level_namespace=top_level_namespace, - ) - - if isinstance(aggregation_spec, AggregateByGroupSpec): - qual_name_source = _get_qual_name_of_source_col( - source=source, - wrapped_func=wrapped_func, - ) - else: - qual_name_source = None - - return DerivedAggregationFunction( - function=wrapped_func, - source=qual_name_source, - source_function=functions.get(qual_name_source, None), - aggregation_target=aggregation_target, - aggregation_method=aggregation_spec.aggr, - ) - - -def _create_one_aggregate_by_p_id_func( - aggregation_target: str, - aggregation_spec: AggregateByPIDSpec, - functions: QualNameFunctionsDict, - top_level_namespace: set[str], -) -> DerivedAggregationFunction: - """Create one function that links variables across persons. - - Parameters - ---------- - aggregation_target - Name of the aggregation target. - aggregation_spec - The aggregation specification. - functions - The functions dict with qualified function names as keys and functions as - values. - top_level_namespace - Set of top-level namespaces. - - Returns - ------- - The derived function. - - """ - aggregation_method = aggregation_spec.aggr - p_id_to_aggregate_by = aggregation_spec.p_id_to_aggregate_by - source = aggregation_spec.source if aggregation_method != "count" else None - - if aggregation_method == "count": - mapper = { - "p_id_to_aggregate_by": p_id_to_aggregate_by, - "p_id_to_store_by": "p_id", - } - - def agg_func(p_id_to_aggregate_by, p_id_to_store_by): - return count_by_p_id(p_id_to_aggregate_by, p_id_to_store_by) - - else: - mapper = { - "p_id_to_aggregate_by": p_id_to_aggregate_by, - "p_id_to_store_by": "p_id", - "column": source, - } - - if aggregation_method == "sum": - - def agg_func(column, p_id_to_aggregate_by, p_id_to_store_by): - return sum_by_p_id(column, p_id_to_aggregate_by, p_id_to_store_by) - - elif aggregation_method == "mean": - - def agg_func(column, p_id_to_aggregate_by, p_id_to_store_by): - return mean_by_p_id(column, p_id_to_aggregate_by, p_id_to_store_by) - - elif aggregation_method == "max": - - def agg_func(column, p_id_to_aggregate_by, p_id_to_store_by): - return max_by_p_id(column, p_id_to_aggregate_by, p_id_to_store_by) - - elif aggregation_method == "min": - - def agg_func(column, p_id_to_aggregate_by, p_id_to_store_by): - return min_by_p_id(column, p_id_to_aggregate_by, p_id_to_store_by) - - elif aggregation_method == "any": - - def agg_func(column, p_id_to_aggregate_by, p_id_to_store_by): - return any_by_p_id(column, p_id_to_aggregate_by, p_id_to_store_by) - - elif aggregation_method == "all": - - def agg_func(column, p_id_to_aggregate_by, p_id_to_store_by): - return all_by_p_id(column, p_id_to_aggregate_by, p_id_to_store_by) - - else: - msg = format_errors_and_warnings( - f"Aggregation method {aggregation_method} is not implemented." - ) - raise ValueError(msg) - - wrapped_func = dt.one_function_without_tree_logic( - function=dags.rename_arguments( - func=agg_func, - mapper=mapper, - ), - tree_path=dt.tree_path_from_qual_name(aggregation_target), - top_level_namespace=top_level_namespace, - ) - - qual_name_source = ( - _get_qual_name_of_source_col( - source=source, - wrapped_func=wrapped_func, - ) - if source - else None - ) - - return DerivedAggregationFunction( - function=wrapped_func, - source=qual_name_source, - source_function=functions.get(qual_name_source, None), - aggregation_target=aggregation_target, - aggregation_method=aggregation_method, - ) - - def _annotate_aggregation_functions( - functions: QualNameFunctionsDict, - types_input_variables: dict[str, type], - aggregation_functions: QualNameFunctionsDict, -) -> QualNameFunctionsDict: + functions: QualNameTTSIMFunctionDict, + inputs: QualNamePolicyInputDict, + aggregation_functions: QualNameTTSIMFunctionDict, +) -> QualNameTTSIMFunctionDict: """Annotate aggregation functions. Add type annotations to the aggregation functions based on the type annotations of @@ -512,13 +439,12 @@ def _annotate_aggregation_functions( Parameters ---------- functions - Dict with qualified function names as keys and functions with qualified - arguments as values. + Map qualified function names to functions. + inputs + Map qualified input names to policy inputs. aggregation_functions Dict with qualified aggregation function names as keys and aggregation functions as values. - types_input_variables - Dict with qualified data names as keys and types as values. Returns ------- @@ -533,6 +459,11 @@ def _annotate_aggregation_functions( annotations = {} if aggregation_method == "count": annotations["return"] = int + elif source in inputs: + annotations[source] = inputs[source].data_type + annotations["return"] = _select_return_type( + aggregation_method, annotations[source] + ) elif source in functions: source_function = functions[source] if "return" in source_function.__annotations__: @@ -540,20 +471,11 @@ def _annotate_aggregation_functions( annotations["return"] = _select_return_type( aggregation_method, annotations[source] ) - else: - # TODO(@hmgaudecker): Think about how type annotations of aggregations - # of user-provided input variables are handled - # https://github.com/iza-institute-of-labor-economics/gettsim/issues/604 - pass - elif source in types_input_variables: - annotations[source] = types_input_variables[source] - annotations["return"] = _select_return_type( - aggregation_method, annotations[source] - ) else: - # TODO(@hmgaudecker): Think about how type annotations of aggregations of - # user-provided input variables are handled - # https://github.com/iza-institute-of-labor-economics/gettsim/issues/604 + # TODO(@MImmesberger): Once we filter the aggregations by start and end + # date, we should throw an error here. The qualified source name should be + # in either functions or inputs (or None if it is a count). + # https://github.com/iza-institute-of-labor-economics/gettsim/issues/870 pass aggregation_function.__annotations__ = annotations @@ -563,7 +485,7 @@ def _annotate_aggregation_functions( def _fail_if_targets_not_in_functions( - functions: QualNameFunctionsDict, targets: QualNameTargetList + functions: QualNameTTSIMFunctionDict, targets: QualNameTargetList ) -> None: """Fail if some target is not among functions. diff --git a/src/ttsim/compute_taxes_and_transfers.py b/src/ttsim/compute_taxes_and_transfers.py index dde6ed624b..e9eec9e6c5 100644 --- a/src/ttsim/compute_taxes_and_transfers.py +++ b/src/ttsim/compute_taxes_and_transfers.py @@ -10,31 +10,30 @@ import networkx as nx import pandas as pd -from _gettsim.config import ( - DEFAULT_TARGETS, - FOREIGN_KEYS, - TYPES_INPUT_VARIABLES, -) from ttsim.combine_functions import ( combine_policy_functions_and_derived_functions, ) from ttsim.config import numpy_or_jax as np from ttsim.function_types import ( DerivedAggregationFunction, - DerivedTimeConversionFunction, GroupByFunction, PolicyFunction, + PolicyInput, + TTSIMFunction, ) from ttsim.policy_environment import PolicyEnvironment from ttsim.shared import ( + all_variations_of_base_name, assert_valid_ttsim_pytree, format_errors_and_warnings, format_list_linewise, get_name_of_group_by_id, get_names_of_arguments_without_defaults, + get_re_pattern_for_all_time_units_and_groupings, merge_trees, partition_by_reference_dict, ) +from ttsim.time_conversion import TIME_UNITS from ttsim.typing import ( check_series_has_expected_type, convert_series_to_internal_type, @@ -45,15 +44,18 @@ NestedDataDict, NestedTargetDict, QualNameDataDict, - QualNameFunctionsDict, + QualNamePolicyInputDict, QualNameTargetList, + QualNameTTSIMFunctionDict, ) def compute_taxes_and_transfers( data_tree: NestedDataDict, environment: PolicyEnvironment, - targets_tree: NestedTargetDict | None = None, + targets_tree: NestedTargetDict, + foreign_keys: tuple[tuple[str, ...], ...], + supported_groupings: tuple[str, ...], rounding: bool = True, debug: bool = False, ) -> NestedDataDict: @@ -66,8 +68,7 @@ def compute_taxes_and_transfers( environment: PolicyEnvironment The policy environment which contains all necessary functions and parameters. targets_tree : NestedTargetDict | None - The targets tree. By default, ``targets_tree`` is ``None`` and all key outputs - as defined by `gettsim.config.DEFAULT_TARGETS` are returned. + The targets tree. rounding : bool, default True Indicator for whether rounding should be applied as specified in the law. debug : bool @@ -80,8 +81,6 @@ def compute_taxes_and_transfers( The computed variables as a tree. """ - # Use default targets if no targets are provided. - targets_tree = targets_tree if targets_tree else DEFAULT_TARGETS # Check user inputs _fail_if_targets_tree_not_valid(targets_tree) @@ -89,19 +88,28 @@ def compute_taxes_and_transfers( _fail_if_environment_not_valid(environment) # Transform functions tree to qualified names dict with qualified arguments - top_level_namespace = ( - set(environment.functions_tree.keys()) - | set(data_tree.keys()) - | set(TYPES_INPUT_VARIABLES.keys()) - | set(environment.aggregation_specs_tree.keys()) - ) - functions = dt.functions_without_tree_logic( - functions=environment.functions_tree, top_level_namespace=top_level_namespace + top_level_namespace = _get_top_level_namespace( + environment=environment, + supported_time_conversions=tuple(TIME_UNITS.keys()), + supported_groupings=supported_groupings, ) - + # Flatten nested objects to qualified names targets = dt.qual_names(targets_tree) data = dt.flatten_to_qual_names(data_tree) aggregation_specs = dt.flatten_to_qual_names(environment.aggregation_specs_tree) + functions: QualNameTTSIMFunctionDict = {} + inputs: QualNamePolicyInputDict = {} + for name, f_or_i in dt.flatten_to_qual_names(environment.raw_objects_tree).items(): + if isinstance(f_or_i, TTSIMFunction): + functions[name] = dt.one_function_without_tree_logic( + function=f_or_i, + tree_path=dt.tree_path_from_qual_name(name), + top_level_namespace=top_level_namespace, + ) + elif isinstance(f_or_i, PolicyInput): + inputs[name] = f_or_i + else: + raise ValueError(f"Unknown type: {type(f_or_i)}") # Add derived functions to the qualified functions tree. functions = combine_policy_functions_and_derived_functions( @@ -109,24 +117,26 @@ def compute_taxes_and_transfers( aggregation_specs_from_environment=aggregation_specs, targets=targets, data=data, + inputs=inputs, top_level_namespace=top_level_namespace, + supported_groupings=supported_groupings, ) - functions_overridden, functions_not_overridden = partition_by_reference_dict( + functions_overridden, functions_to_be_used = partition_by_reference_dict( to_partition=functions, reference_dict=data, ) _warn_if_functions_overridden_by_data(functions_overridden) - data_with_correct_types = _convert_data_to_correct_types( - data=data, - functions_overridden=functions_overridden, - ) + # data_with_correct_types = _convert_data_to_correct_types( + # data=data, + # functions_overridden=functions_overridden, + # ) functions_with_rounding_specs = ( - _add_rounding_to_functions(functions=functions_not_overridden) + _add_rounding_to_functions(functions=functions_to_be_used) if rounding - else functions_not_overridden + else functions_to_be_used ) functions_with_partialled_parameters = _partial_parameters_to_functions( functions=functions_with_rounding_specs, @@ -135,7 +145,7 @@ def compute_taxes_and_transfers( # Remove unnecessary elements from user-provided data. input_data = _create_input_data_for_concatenated_function( - data=data_with_correct_types, + data=data, functions=functions_with_partialled_parameters, targets=targets, ) @@ -143,10 +153,12 @@ def compute_taxes_and_transfers( _fail_if_group_variables_not_constant_within_groups( data=input_data, functions=functions, + supported_groupings=supported_groupings, ) _fail_if_foreign_keys_are_invalid( data=input_data, p_id=data.get("p_id", None), + foreign_keys=foreign_keys, ) tax_transfer_function = dags.concatenate_functions( @@ -170,8 +182,50 @@ def compute_taxes_and_transfers( return result_tree +def _get_top_level_namespace( + environment: PolicyEnvironment, + supported_time_conversions: tuple[str, ...], + supported_groupings: tuple[str, ...], +) -> set[str]: + """Get the top level namespace. + + Parameters + ---------- + environment: + The policy environment. + + Returns + ------- + top_level_namespace: + The top level namespace. + """ + direct_top_level_names = set(environment.raw_objects_tree.keys()) | set( + environment.aggregation_specs_tree.keys() + ) + re_pattern = get_re_pattern_for_all_time_units_and_groupings( + supported_groupings=supported_groupings, + supported_time_units=supported_time_conversions, + ) + + all_top_level_names = set() + for name in direct_top_level_names: + match = re_pattern.fullmatch(name) + base_name = match.group("base_name") + create_conversions_for_time_units = bool(match.group("time_unit")) + + all_top_level_names_for_name = all_variations_of_base_name( + base_name=base_name, + supported_time_conversions=supported_time_conversions, + supported_groupings=supported_groupings, + create_conversions_for_time_units=create_conversions_for_time_units, + ) + all_top_level_names.update(all_top_level_names_for_name) + + return all_top_level_names + + def _convert_data_to_correct_types( - data: QualNameDataDict, functions_overridden: QualNameFunctionsDict + data: QualNameDataDict, functions_overridden: QualNameTTSIMFunctionDict ) -> QualNameDataDict: """Convert all data columns to the type that is expected by GETTSIM. @@ -279,7 +333,7 @@ def _convert_data_to_correct_types( def _create_input_data_for_concatenated_function( data: QualNameDataDict, - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, targets: QualNameTargetList, ) -> QualNameDataDict: """Create input data for the concatenated function. @@ -322,9 +376,9 @@ def _create_input_data_for_concatenated_function( def _partial_parameters_to_functions( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, params: dict[str, Any], -) -> QualNameFunctionsDict: +) -> QualNameTTSIMFunctionDict: """Round and partial parameters into functions. Parameters @@ -361,8 +415,8 @@ def _partial_parameters_to_functions( def _add_rounding_to_functions( - functions: QualNameFunctionsDict, -) -> QualNameFunctionsDict: + functions: QualNameTTSIMFunctionDict, +) -> QualNameTTSIMFunctionDict: """Add appropriate rounding of outputs to function. Parameters @@ -414,12 +468,13 @@ def _fail_if_data_tree_not_valid(data_tree: NestedDataDict) -> None: leaf_checker=lambda leaf: isinstance(leaf, pd.Series | np.ndarray), tree_name="data_tree", ) - _fail_if_pid_is_non_unique(data_tree) + _fail_if_p_id_is_non_unique(data_tree) def _fail_if_group_variables_not_constant_within_groups( data: QualNameDataDict, - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, + supported_groupings: tuple[str, ...], ) -> None: """ Check that group variables are constant within each group. @@ -446,6 +501,7 @@ def _fail_if_group_variables_not_constant_within_groups( group_by_id = get_name_of_group_by_id( target_name=name, group_by_functions=group_by_functions, + supported_groupings=supported_groupings, ) if group_by_id in data: group_by_id_series = pd.Series(data[group_by_id]) @@ -470,7 +526,7 @@ def _fail_if_group_variables_not_constant_within_groups( raise ValueError(msg) -def _fail_if_pid_is_non_unique(data_tree: NestedDataDict) -> None: +def _fail_if_p_id_is_non_unique(data_tree: NestedDataDict) -> None: """Check that pid is unique.""" p_id = data_tree.get("p_id", None) if p_id is None: @@ -496,6 +552,7 @@ def _fail_if_pid_is_non_unique(data_tree: NestedDataDict) -> None: def _fail_if_foreign_keys_are_invalid( data: QualNameDataDict, p_id: pd.Series, + foreign_keys: tuple[tuple[str, ...], ...], ) -> None: """ Check that all foreign keys are valid. @@ -506,7 +563,7 @@ def _fail_if_foreign_keys_are_invalid( valid_ids = set(p_id) | {-1} for name, data_column in data.items(): - foreign_key_col = dt.tree_path_from_qual_name(name) in FOREIGN_KEYS + foreign_key_col = dt.tree_path_from_qual_name(name) in foreign_keys path = dt.tree_path_from_qual_name(name) if not foreign_key_col: continue @@ -533,7 +590,7 @@ def _fail_if_foreign_keys_are_invalid( def _warn_if_functions_overridden_by_data( - functions_overridden: QualNameFunctionsDict, + functions_overridden: QualNameTTSIMFunctionDict, ) -> None: """Warn if functions are overridden by data.""" if len(functions_overridden) > 0: @@ -598,7 +655,7 @@ def __init__(self, columns_overriding_functions: set[str]) -> None: def _fail_if_root_nodes_are_missing( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, data: QualNameDataDict, root_nodes: list[str], ) -> None: @@ -637,15 +694,14 @@ def _fail_if_root_nodes_are_missing( missing_nodes.append(str(node)) if missing_nodes: - formatted = format_list_linewise(missing_nodes) + formatted = format_list_linewise( + [str(dt.tree_path_from_qual_name(mn)) for mn in missing_nodes] + ) raise ValueError(f"The following data columns are missing.\n{formatted}") def _func_depends_on_parameters_only( - func: PolicyFunction - | DerivedAggregationFunction - | DerivedTimeConversionFunction - | GroupByFunction, + func: TTSIMFunction, ) -> bool: """Check if a function depends on parameters only.""" return ( diff --git a/src/ttsim/function_types.py b/src/ttsim/function_types.py index b7600dd09b..28f4222b7b 100644 --- a/src/ttsim/function_types.py +++ b/src/ttsim/function_types.py @@ -3,30 +3,133 @@ import datetime import functools import inspect -import re -from collections.abc import Callable -from typing import Literal, TypeVar +from dataclasses import dataclass +from typing import TYPE_CHECKING, Literal, TypeVar -import dags.tree as dt import numpy from ttsim.rounding import RoundingSpec +from ttsim.shared import validate_dashed_iso_date, validate_date_range + +if TYPE_CHECKING: + from collections.abc import Callable T = TypeVar("T") +DEFAULT_START_DATE = datetime.date(1900, 1, 1) +DEFAULT_END_DATE = datetime.date(2100, 12, 31) + + +@dataclass +class TTSIMObject: + """ + Abstract base class for all TTSIM Functions and Inputs. + """ + + leaf_name: str + start_date: datetime.date + end_date: datetime.date + + def is_active(self, date: datetime.date) -> bool: + """Check if the function is active at a given date.""" + return self.start_date <= date <= self.end_date + + +@dataclass +class PolicyInput(TTSIMObject): + """ + A dummy function representing an input variable. + + Parameters + ---------- + data_type: + The data type of the input variable. + start_date: + The date from which the input is relevant / active (inclusive). + end_date: + The date until which the input is relevant / active (inclusive). + """ + + data_type: type[float | int | bool] + + +def policy_input( + *, + start_date: str | datetime.date = DEFAULT_START_DATE, + end_date: str | datetime.date = DEFAULT_END_DATE, +) -> PolicyInput: + """ + Decorator that makes a (dummy) function a `PolicyInput`. + + **Dates active (start_date, end_date):** + + Specifies that a PolicyInput is only active between two dates, `start` and `end`. + + **Rounding spec (params_key_for_rounding):** + + Adds the location of the rounding specification to a PolicyInput. + + Parameters + ---------- + start_date + The start date (inclusive) in the format YYYY-MM-DD (part of ISO 8601). + end_date + The end date (inclusive) in the format YYYY-MM-DD (part of ISO 8601). + + Returns + ------- + A PolicyInput object. + """ + start_date, end_date = _convert_and_validate_dates(start_date, end_date) + + def inner(func: Callable) -> PolicyInput: + data_type = func.__annotations__["return"] + return PolicyInput( + leaf_name=func.__name__, + data_type=data_type, + start_date=start_date, + end_date=end_date, + ) + + return inner + + +@dataclass +class TTSIMFunction(TTSIMObject): + """ + Base class for all TTSIM functions. + """ + + function: Callable + skip_vectorization: bool = False + + def __call__(self, *args, **kwargs): + return self.function(*args, **kwargs) + + @property + def dependencies(self) -> set[str]: + """The names of input variables that the function depends on.""" + return set(inspect.signature(self).parameters) + + @property + def original_function_name(self) -> str: + """The name of the wrapped function.""" + return self.function.__name__ -class PolicyFunction(Callable): + +@dataclass +class PolicyFunction(TTSIMFunction): """ A function that computes an output vector based on some input vectors and/or parameters. Parameters ---------- + leaf_name: + The leaf name of the function in the functions tree. function: The function to wrap. Argument values of the `@policy_function` are reused unless explicitly overwritten. - leaf_name: - The leaf name of the function in the functions tree. start_date: The date from which the function is active (inclusive). end_date: @@ -37,30 +140,18 @@ class PolicyFunction(Callable): Whether the function should be vectorized. """ - def __init__( # noqa: PLR0913 - self, - *, - function: Callable, - leaf_name: str, - start_date: datetime.date, - end_date: datetime.date, - rounding_spec: RoundingSpec | None, - skip_vectorization: bool | None, - ): - self.skip_vectorization: bool = skip_vectorization + rounding_spec: RoundingSpec | None = None + + def __post_init__(self): + self._fail_if_rounding_has_wrong_type(self.rounding_spec) self.function = ( - function if self.skip_vectorization else _vectorize_func(function) + self.function if self.skip_vectorization else _vectorize_func(self.function) ) - self.leaf_name: str = leaf_name if leaf_name else function.__name__ - self.start_date: datetime.date = start_date - self.end_date: datetime.date = end_date - self._fail_if_rounding_has_wrong_type(rounding_spec) - self.rounding_spec: RoundingSpec | None = rounding_spec # Expose the signature of the wrapped function for dependency resolution - self.__annotations__ = function.__annotations__ - self.__module__ = function.__module__ - self.__name__ = function.__name__ + self.__annotations__ = self.function.__annotations__ + self.__module__ = self.function.__module__ + self.__name__ = self.function.__name__ self.__signature__ = inspect.signature(self.function) def _fail_if_rounding_has_wrong_type( @@ -102,9 +193,9 @@ def is_active(self, date: datetime.date) -> bool: def policy_function( *, - start_date: str | datetime.date = "1900-01-01", - end_date: str | datetime.date = "2100-12-31", leaf_name: str | None = None, + start_date: str | datetime.date = DEFAULT_START_DATE, + end_date: str | datetime.date = DEFAULT_END_DATE, rounding_spec: RoundingSpec | None = None, skip_vectorization: bool = False, ) -> PolicyFunction: @@ -127,13 +218,13 @@ def policy_function( Parameters ---------- + leaf_name + The name that should be used as the PolicyFunction's leaf name in the DAG. If + omitted, we use the name of the function as defined. start_date The start date (inclusive) in the format YYYY-MM-DD (part of ISO 8601). end_date The end date (inclusive) in the format YYYY-MM-DD (part of ISO 8601). - leaf_name - The name that should be used as the PolicyFunction's leaf name in the DAG. If - omitted, we use the name of the function as defined. rounding_spec The specification to be used for rounding. skip_vectorization @@ -145,18 +236,12 @@ def policy_function( A PolicyFunction object. """ - _validate_dashed_iso_date(start_date) - _validate_dashed_iso_date(end_date) - - start_date = datetime.date.fromisoformat(start_date) - end_date = datetime.date.fromisoformat(end_date) - - _validate_date_range(start_date, end_date) + start_date, end_date = _convert_and_validate_dates(start_date, end_date) def inner(func: Callable) -> PolicyFunction: return PolicyFunction( - function=func, leaf_name=leaf_name if leaf_name else func.__name__, + function=func, start_date=start_date, end_date=end_date, rounding_spec=rounding_spec, @@ -166,19 +251,6 @@ def inner(func: Callable) -> PolicyFunction: return inner -_DASHED_ISO_DATE = re.compile(r"\d{4}-\d{2}-\d{2}") - - -def _validate_dashed_iso_date(date: str | datetime.date): - if not _DASHED_ISO_DATE.match(date): - raise ValueError(f"Date {date} does not match the format YYYY-MM-DD.") - - -def _validate_date_range(start: datetime.date, end: datetime.date): - if start > end: - raise ValueError(f"The start date {start} must be before the end date {end}.") - - def _vectorize_func(func: Callable) -> Callable: # What should work once that Jax backend is fully supported signature = inspect.signature(func) @@ -193,130 +265,163 @@ def wrapper_vectorize_func(*args, **kwargs): return wrapper_vectorize_func -class GroupByFunction(Callable): +@dataclass +class GroupByFunction(TTSIMFunction): """ A function that computes endogenous group_by IDs. Parameters ---------- + leaf_name: + The leaf name of the function in the functions tree. function: - The group_by function. + The function calculating the group_by IDs. + start_date: + The date from which the function is active (inclusive). + end_date: + The date until which the function is active (inclusive). """ - def __init__( - self, - *, - function: Callable, - leaf_name: str | None = None, - ): - self.function = function - self.leaf_name = leaf_name if leaf_name else function.__name__ - + def __post_init__(self): # Expose the signature of the wrapped function for dependency resolution - self.__annotations__ = function.__annotations__ - self.__module__ = function.__module__ - self.__name__ = function.__name__ + self.__annotations__ = self.function.__annotations__ + self.__module__ = self.function.__module__ + self.__name__ = self.function.__name__ self.__signature__ = inspect.signature(self.function) - def __call__(self, *args, **kwargs): - return self.function(*args, **kwargs) - @property def dependencies(self) -> set[str]: """The names of input variables that the function depends on.""" return set(inspect.signature(self).parameters) -def group_by_function() -> GroupByFunction: +def group_by_function( + *, + leaf_name: str | None = None, + start_date: str | datetime.date = DEFAULT_START_DATE, + end_date: str | datetime.date = DEFAULT_END_DATE, +) -> GroupByFunction: """ Decorator that creates a group_by function from a function. """ + start_date, end_date = _convert_and_validate_dates(start_date, end_date) def decorator(func: Callable) -> GroupByFunction: - return GroupByFunction(function=func) + _leaf_name = func.__name__ if leaf_name is None else leaf_name + return GroupByFunction( + leaf_name=_leaf_name, + function=func, + start_date=start_date, + end_date=end_date, + ) return decorator -class DerivedAggregationFunction(PolicyFunction): +@dataclass +class DerivedAggregationFunction(TTSIMFunction): """ A function that is an aggregation of another function. Parameters ---------- + leaf_name: + The leaf name of the function in the functions tree. function: - The function to wrap. Argument values of the `@policy_function` are reused - unless explicitly overwritten. - aggregation_target: - The qualified name of the aggregation target. - source_function: - The function from which the new function is derived. + The function performing the aggregation. source: The name of the source function or data column. aggregation_method: The method of aggregation used. + start_date: + The date from which the function is active (inclusive). + end_date: + The date until which the function is active (inclusive). + params_key_for_rounding: + The key in the params dictionary that should be used for rounding. + skip_vectorization: + Whether the function should be vectorized. """ - def __init__( - self, - *, - function: Callable, - source_function: PolicyFunction - | DerivedTimeConversionFunction - | DerivedAggregationFunction - | None = None, - source: str, - aggregation_target: str, - aggregation_method: Literal["count", "sum", "mean", "min", "max", "any", "all"], - ): - super().__init__( - function=function, - leaf_name=dt.tree_path_from_qual_name(aggregation_target)[-1], - start_date=source_function.start_date if source_function else None, - end_date=source_function.end_date if source_function else None, - rounding_spec=None, - skip_vectorization=True, - ) + source: str | None = None + aggregation_method: ( + Literal["count", "sum", "mean", "min", "max", "any", "all"] | None + ) = None - self.source = source - self.aggregation_method = aggregation_method + def __post_init__(self): + if self.aggregation_method is None: + raise ValueError("The aggregation method must be specified.") + if self.source is None and self.aggregation_method != "count": + raise ValueError("The source must be specified.") + + # Expose the signature of the wrapped function for dependency resolution + self.__annotations__ = self.function.__annotations__ + self.__module__ = self.function.__module__ + self.__name__ = self.function.__name__ + self.__signature__ = inspect.signature(self.function) -class DerivedTimeConversionFunction(PolicyFunction): +@dataclass +class DerivedTimeConversionFunction(TTSIMFunction): """ A function that is a time conversion of another function. Parameters ---------- + leaf_name: + The leaf name of the function in the functions tree. function: - The function to wrap. Argument values of the `@policy_function` are reused - unless explicitly overwritten. - source_function: - The function from which the new function is derived. + The function performing the time conversion. source: The name of the source function or data column. - conversion_target: - The qualified name of the conversion target. + start_date: + The date from which the function is active (inclusive). + end_date: + The date until which the function is active (inclusive). + params_key_for_rounding: + The key in the params dictionary that should be used for rounding. + skip_vectorization: + Whether the function should be vectorized. """ - def __init__( - self, - *, - function: Callable, - source_function: PolicyFunction - | DerivedTimeConversionFunction - | DerivedAggregationFunction - | None = None, - source: str, - conversion_target: str, - ): - super().__init__( - function=function, - leaf_name=dt.tree_path_from_qual_name(conversion_target)[-1], - start_date=source_function.start_date if source_function else None, - end_date=source_function.end_date if source_function else None, - rounding_spec=None, - skip_vectorization=True, - ) + source: str | None = None + + def __post_init__(self): + if self.source is None: + raise ValueError("The source must be specified.") + + # Expose the signature of the wrapped function for dependency resolution + self.__annotations__ = self.function.__annotations__ + self.__module__ = self.function.__module__ + self.__name__ = self.function.__name__ + self.__signature__ = inspect.signature(self.function) + + +def _convert_and_validate_dates( + start_date: str | datetime.date, + end_date: str | datetime.date, +) -> tuple[datetime.date, datetime.date]: + """Convert and validate date strings to datetime.date objects. + + Parameters + ---------- + start_date + The start date (inclusive) in the format YYYY-MM-DD (part of ISO 8601). + end_date + The end date (inclusive) in the format YYYY-MM-DD (part of ISO 8601). + + Returns + ------- + tuple[datetime.date, datetime.date] + The converted and validated start and end dates. + """ + if isinstance(start_date, str): + validate_dashed_iso_date(start_date) + start_date = datetime.date.fromisoformat(start_date) + if isinstance(end_date, str): + validate_dashed_iso_date(end_date) + end_date = datetime.date.fromisoformat(end_date) + + validate_date_range(start_date, end_date) - self.source = source + return start_date, end_date diff --git a/src/ttsim/loader.py b/src/ttsim/loader.py index 2bf71da4f4..0751f6e682 100644 --- a/src/ttsim/loader.py +++ b/src/ttsim/loader.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from _gettsim.config import RESOURCE_DIR -from ttsim.function_types import GroupByFunction, PolicyFunction +from ttsim.function_types import TTSIMFunction, TTSIMObject from ttsim.shared import ( create_tree_from_path_and_value, insert_path_and_value, @@ -20,50 +20,51 @@ from types import ModuleType from ttsim.aggregation import AggregateByGroupSpec, AggregateByPIDSpec - from ttsim.typing import NestedAggregationSpecDict, NestedFunctionDict + from ttsim.typing import ( + NestedAggregationSpecDict, + NestedTTSIMObjectDict, + ) -def load_functions_tree_for_date(date: datetime.date) -> NestedFunctionDict: +def load_objects_tree_for_date( + resource_dir: Path, date: datetime.date +) -> NestedTTSIMObjectDict: """ - Load the functions tree for a given date. - - This function takes the list of root paths and searches for all modules containing - PolicyFunctions. Then it loads all PolicyFunctions that are active at the given date - and constructs the functions tree. - - Namespaces are at the directory level. + Traverse `resource_dir` and return all TTSIMObjects for a given date. Parameters ---------- + resource_dir: + The directory to traverse. date: - The date for which policy functions should be loaded. + The date for which policy objects should be loaded. Returns ------- - A tree of active PolicyFunctions. + A tree of active TTSIMObjects. """ - paths_to_functions = _find_python_files_recursively(RESOURCE_DIR) + paths_to_objects = _find_python_files_recursively(resource_dir) - functions_tree = {} + objects_tree = {} - for path in paths_to_functions: - new_functions_tree = get_active_functions_tree_from_module( - path=path, date=date, root_path=RESOURCE_DIR + for path in paths_to_objects: + new_objects_tree = get_active_ttsim_objects_tree_from_module( + path=path, date=date, root_path=resource_dir ) - functions_tree = merge_trees( - left=functions_tree, - right=new_functions_tree, + objects_tree = merge_trees( + left=objects_tree, + right=new_objects_tree, ) - return functions_tree + return objects_tree -def get_active_functions_tree_from_module( +def get_active_ttsim_objects_tree_from_module( path: Path, root_path: Path, date: datetime.date, -) -> dict[str, PolicyFunction | GroupByFunction]: +) -> dict[str, TTSIMFunction]: """Extract all active PolicyFunctions and GroupByFunctions from a module. Parameters @@ -80,68 +81,60 @@ def get_active_functions_tree_from_module( The tree of active PolicyFunctions and GroupByFunctions. """ module = _load_module(path, root_path) - module_name = _convert_path_to_importable_module_name(path, root_path) - - all_functions_in_module = inspect.getmembers(module) - policy_functions = [ - func for _, func in all_functions_in_module if isinstance(func, PolicyFunction) + ttsim_objects = [ + func for _, func in inspect.getmembers(module) if isinstance(func, TTSIMObject) ] - _fail_if_multiple_policy_functions_are_active_at_the_same_time( - policy_functions, module_name + _fail_if_multiple_ttsim_objects_are_active_at_the_same_time( + ttsim_objects, + module_name=root_path / path, ) - active_policy_functions = { - func.leaf_name: func for func in policy_functions if func.is_active(date) - } - - group_by_functions = { - func.leaf_name: func - for _, func in all_functions_in_module - if isinstance(func, GroupByFunction) + active_ttsim_functions = { + func.leaf_name: func for func in ttsim_objects if func.is_active(date) } return create_tree_from_path_and_value( path=_convert_path_to_tree_path(path=path, root_path=root_path), - value={**active_policy_functions, **group_by_functions}, + value=active_ttsim_functions, ) -def _fail_if_multiple_policy_functions_are_active_at_the_same_time( - policy_functions: list[PolicyFunction], +def _fail_if_multiple_ttsim_objects_are_active_at_the_same_time( + ttsim_objects: list[TTSIMObject], module_name: str, ) -> None: - """Raises an ConflictingTimeDependentFunctionsError if multiple functions with the + """Raises an ConflictingTimeDependentObjectsError if multiple objects with the same leaf name are active at the same time. Parameters ---------- - policy_functions - List of PolicyFunctions to check for conflicts. + ttsim_objects + List of TTSIMObjects to check for conflicts. module_name - The name of the module from which the PolicyFunctions are extracted. + The name of the module from which the TTSIMObjects are extracted. Raises ------ - ConflictingTimeDependentFunctionsError - If multiple functions with the same name are active at the same time. + ConflictingTimeDependentObjectsError + If multiple objects with the same leaf name are active at the same time. """ - # Create mapping from leaf names to functions. - leaf_names_to_funcs = {} - for func in policy_functions: - if func.leaf_name in leaf_names_to_funcs: - leaf_names_to_funcs[func.leaf_name].append(func) + # Create mapping from leaf names to objects. + leaf_names_to_objects = {} + for obj in ttsim_objects: + if obj.leaf_name in leaf_names_to_objects: + leaf_names_to_objects[obj.leaf_name].append(obj) else: - leaf_names_to_funcs[func.leaf_name] = [func] + leaf_names_to_objects[obj.leaf_name] = [obj] # Check for overlapping start and end dates for time-dependent functions. - for leaf_name, funcs in leaf_names_to_funcs.items(): - dates_active = [(f.start_date, f.end_date) for f in funcs] + for leaf_name, objects in leaf_names_to_objects.items(): + dates_active = [(f.start_date, f.end_date) for f in objects] for (start1, end1), (start2, end2) in itertools.combinations(dates_active, 2): if start1 <= end2 and start2 <= end1: - raise ConflictingTimeDependentFunctionsError( - affected_policy_functions=funcs, + raise ConflictingTimeDependentObjectsError( + affected_ttsim_objects=objects, leaf_name=leaf_name, module_name=module_name, overlap_start=max(start1, start2), @@ -149,29 +142,31 @@ def _fail_if_multiple_policy_functions_are_active_at_the_same_time( ) -class ConflictingTimeDependentFunctionsError(Exception): +class ConflictingTimeDependentObjectsError(Exception): def __init__( self, - affected_policy_functions: list[PolicyFunction], + affected_ttsim_objects: list[TTSIMObject], leaf_name: str, module_name: str, overlap_start: datetime.date, overlap_end: datetime.date, ): - self.affected_policy_functions = affected_policy_functions + self.affected_ttsim_objects = affected_ttsim_objects self.leaf_name = leaf_name self.module_name = module_name self.overlap_start = overlap_start self.overlap_end = overlap_end def __str__(self): - overlapping_functions = [ - func.original_function_name for func in self.affected_policy_functions + overlapping_objects = [ + obj.__getattribute__("original_function_name", obj.leaf_name) + for obj in self.affected_ttsim_objects + if obj ] return f""" Functions with leaf name {self.leaf_name} in module {self.module_name} have overlapping start and end dates. The following functions are affected: \n\n - {", ".join(overlapping_functions)} \n Overlapping + {", ".join(overlapping_objects)} \n Overlapping from {self.overlap_start} to {self.overlap_end}.""" @@ -192,30 +187,18 @@ def _find_python_files_recursively(root_path: Path) -> list[Path]: def _load_module(path: Path, root_path: Path) -> ModuleType: - module_name = _convert_path_to_importable_module_name(path, root_path) - spec = importlib.util.spec_from_file_location(module_name, path) + name = path.relative_to(root_path).with_suffix("").as_posix().replace("/", ".") + spec = importlib.util.spec_from_file_location( + name=name, + location=path, + ) module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module + sys.modules[name] = module spec.loader.exec_module(module) return module -def _convert_path_to_importable_module_name(path: Path, root_path: Path) -> str: - """ - Convert an absolute path to a Python module name. - - Examples - -------- - >>> _convert_path_to_importable_module_name( - path=Path("/usr/gettsim/src/_gettsim/familie/familie.py"), - root_path=Path("/usr/gettsim/src/_gettsim"), - ) - "familie.familie" - """ - return path.relative_to(root_path).with_suffix("").as_posix().replace("/", ".") - - def _convert_path_to_tree_path(path: Path, root_path: Path) -> tuple[str, ...]: """ Convert the path from the package root to a tree path. @@ -299,4 +282,5 @@ def _load_aggregation_specs_from_module( Loaded dictionaries. """ module = _load_module(path, root_path) - return getattr(module, "aggregation_specs", {}) + aggregation_specs = getattr(module, "aggregation_specs", ()) + return {a_s.target: a_s for a_s in aggregation_specs} diff --git a/src/ttsim/piecewise_polynomial.py b/src/ttsim/piecewise_polynomial.py index 8f7675f02b..a700aacc08 100644 --- a/src/ttsim/piecewise_polynomial.py +++ b/src/ttsim/piecewise_polynomial.py @@ -251,7 +251,7 @@ def _check_rates(parameter_dict, parameter, keys, func_type): return rates -def _check_intercepts( # noqa: PLR0913 +def _check_intercepts( parameter_dict, parameter, lower_thresholds, upper_thresholds, rates, keys ): """Check and transfer raw intercepte data. If necessary create intercepts. diff --git a/src/ttsim/policy_environment.py b/src/ttsim/policy_environment.py index c800588094..864ece8def 100644 --- a/src/ttsim/policy_environment.py +++ b/src/ttsim/policy_environment.py @@ -15,13 +15,12 @@ RESOURCE_DIR, ) from ttsim.function_types import ( - GroupByFunction, - PolicyFunction, + TTSIMObject, policy_function, ) from ttsim.loader import ( load_aggregation_specs_tree, - load_functions_tree_for_date, + load_objects_tree_for_date, ) from ttsim.piecewise_polynomial import ( _check_thresholds, @@ -35,9 +34,13 @@ ) if TYPE_CHECKING: + from collections.abc import Callable from pathlib import Path - from ttsim.typing import NestedAggregationSpecDict, NestedFunctionDict + from ttsim.typing import ( + NestedAggregationSpecDict, + NestedTTSIMObjectDict, + ) class PolicyEnvironment: @@ -61,21 +64,19 @@ class PolicyEnvironment: def __init__( self, - functions_tree: NestedFunctionDict, + raw_objects_tree: NestedTTSIMObjectDict, params: dict[str, Any] | None = None, aggregation_specs_tree: NestedAggregationSpecDict | None = None, ): # Check functions tree and convert functions to PolicyFunction if necessary assert_valid_ttsim_pytree( - functions_tree, - lambda leaf: isinstance(leaf, PolicyFunction | GroupByFunction), - "functions_tree", + tree=raw_objects_tree, + leaf_checker=lambda leaf: isinstance(leaf, TTSIMObject), + tree_name="raw_objects_tree", ) - self._functions_tree = optree.tree_map( - lambda leaf: leaf - if isinstance(leaf, GroupByFunction) - else _convert_function_to_policy_function(leaf), - functions_tree, + self._raw_objects_tree = optree.tree_map( + lambda leaf: _convert_to_policy_function_if_not_ttsim_object(leaf), + raw_objects_tree, ) # Read in parameters and aggregation specs @@ -85,9 +86,12 @@ def __init__( ) @property - def functions_tree(self) -> NestedFunctionDict: - """The policy functions. Does not include aggregations or time conversions.""" - return self._functions_tree + def raw_objects_tree(self) -> NestedTTSIMObjectDict: + """The raw TTSIM objects including policy_inputs. + + Does not include aggregations or time conversions. + """ + return self._raw_objects_tree @property def params(self) -> dict[str, Any]: @@ -102,46 +106,40 @@ def aggregation_specs_tree(self) -> NestedAggregationSpecDict: """ return self._aggregation_specs_tree - def upsert_policy_functions( - self, functions_tree_to_upsert: NestedFunctionDict + def upsert_objects( + self, tree_to_upsert: NestedTTSIMObjectDict ) -> PolicyEnvironment: - """Upsert GETTSIM's function tree with (parts of) a new function tree. + """Upsert GETTSIM's function tree with (parts of) a new TTSIM objects tree. - Adds to or overwrites functions of the policy environment. Note that this + Adds to or overwrites TTSIM objects of the policy environment. Note that this method does not modify the current policy environment but returns a new one. Parameters ---------- - functions_tree + tree_to_upsert The functions to add or overwrite. Returns ------- - The policy environment with the new functions. + The policy environment with the upserted functions. """ - new_functions_tree = {} - # Add old functions tree to new functions tree - new_functions_tree = {**self._functions_tree} - - functions_tree_to_upsert = optree.tree_map( - lambda leaf: leaf - if isinstance(leaf, GroupByFunction) - else _convert_function_to_policy_function(leaf), - functions_tree_to_upsert, + tree_to_upsert_with_correct_types = optree.tree_map( + lambda leaf: _convert_to_policy_function_if_not_ttsim_object(leaf), + tree_to_upsert, ) _fail_if_name_of_last_branch_element_not_leaf_name_of_function( - functions_tree_to_upsert + tree_to_upsert_with_correct_types ) # Add functions tree to upsert to new functions tree - new_functions_tree = upsert_tree( - base=new_functions_tree, - to_upsert=functions_tree_to_upsert, + new_tree = upsert_tree( + base={**self._raw_objects_tree}, + to_upsert=tree_to_upsert_with_correct_types, ) result = object.__new__(PolicyEnvironment) - result._functions_tree = new_functions_tree # noqa: SLF001 + result._raw_objects_tree = new_tree # noqa: SLF001 result._params = self._params # noqa: SLF001 result._aggregation_specs_tree = self._aggregation_specs_tree # noqa: SLF001 @@ -162,19 +160,23 @@ def replace_all_parameters(self, params: dict[str, Any]): The policy environment with the new parameters. """ result = object.__new__(PolicyEnvironment) - result._functions_tree = self._functions_tree # noqa: SLF001 + result._raw_objects_tree = self._raw_objects_tree # noqa: SLF001 result._params = params # noqa: SLF001 result._aggregation_specs_tree = self._aggregation_specs_tree # noqa: SLF001 return result -def set_up_policy_environment(date: datetime.date | str | int) -> PolicyEnvironment: +def set_up_policy_environment( + resource_dir: Path, date: datetime.date | str | int +) -> PolicyEnvironment: """ Set up the policy environment for a particular date. Parameters ---------- + resource_dir + The directory to load the policy environment from. date The date for which the policy system is set up. An integer is interpreted as the year. @@ -186,7 +188,7 @@ def set_up_policy_environment(date: datetime.date | str | int) -> PolicyEnvironm # Check policy date for correct format and convert to datetime.date date = _parse_date(date) - functions_tree = load_functions_tree_for_date(date) + functions_tree = load_objects_tree_for_date(resource_dir=resource_dir, date=date) params = {} for group in INTERNAL_PARAMS_GROUPS: @@ -228,28 +230,30 @@ def _parse_date(date: datetime.date | str | int) -> datetime.date: return date -def _convert_function_to_policy_function( - function: callable, -) -> PolicyFunction | GroupByFunction: - """Convert a function to a PolicyFunction. +def _convert_to_policy_function_if_not_ttsim_object( + input_object: Callable | TTSIMObject, +) -> TTSIMObject: + """Convert an object to a PolicyFunction if it is not already a TTSIMObject. Parameters ---------- - function - The function to convert. + input_object + The object to convert. Returns ------- - function - The converted function. + converted_object + The converted object. """ - if isinstance(function, PolicyFunction | GroupByFunction): - converted_function = function + if isinstance(input_object, TTSIMObject): + converted_object = input_object else: - converted_function = policy_function(leaf_name=function.__name__)(function) + converted_object = policy_function(leaf_name=input_object.__name__)( + input_object + ) - return converted_function + return converted_object def _parse_piecewise_parameters(tax_data): @@ -624,7 +628,7 @@ def transfer_dictionary(remaining_dict, new_dict, key_list): def _fail_if_name_of_last_branch_element_not_leaf_name_of_function( - functions_tree: NestedFunctionDict, + functions_tree: NestedTTSIMObjectDict, ) -> None: """Raise error if a PolicyFunction does not have the same leaf name as the last branch element of the tree path. diff --git a/src/ttsim/rounding.py b/src/ttsim/rounding.py index a32bddb286..3d39621cc6 100644 --- a/src/ttsim/rounding.py +++ b/src/ttsim/rounding.py @@ -2,25 +2,17 @@ import functools from dataclasses import dataclass -from enum import StrEnum +from typing import Literal, get_args import numpy as np - -class RoundingDirection(StrEnum): - """ - Enum for the rounding direction. - """ - - UP = "up" - DOWN = "down" - NEAREST = "nearest" +ROUNDING_DIRECTION = Literal["up", "down", "nearest"] @dataclass class RoundingSpec: base: int | float - direction: RoundingDirection + direction: ROUNDING_DIRECTION to_add_after_rounding: int | float = 0 reference: str | None = None @@ -28,9 +20,10 @@ def __post_init__(self): """Validate the types of base and to_add_after_rounding.""" if type(self.base) not in [int, float]: raise ValueError(f"base needs to be a number, got {self.base!r}") - if type(self.direction) not in [RoundingDirection]: + valid_directions = get_args(ROUNDING_DIRECTION) + if self.direction not in valid_directions: raise ValueError( - f"direction needs to be a RoundingDirection, got {self.direction!r}" + f"`direction` must be one of {valid_directions}, got {self.direction!r}" ) if type(self.to_add_after_rounding) not in [int, float]: raise ValueError( @@ -57,11 +50,11 @@ def apply_rounding(self, func: callable) -> callable: def wrapper(*args, **kwargs): out = func(*args, **kwargs) - if self.direction == RoundingDirection.UP: + if self.direction == "up": rounded_out = self.base * np.ceil(out / self.base) - elif self.direction == RoundingDirection.DOWN: + elif self.direction == "down": rounded_out = self.base * np.floor(out / self.base) - elif self.direction == RoundingDirection.NEAREST: + elif self.direction == "nearest": rounded_out = self.base * (out / self.base).round() rounded_out += self.to_add_after_rounding diff --git a/src/ttsim/shared.py b/src/ttsim/shared.py index d161319940..08ab7f4a6e 100644 --- a/src/ttsim/shared.py +++ b/src/ttsim/shared.py @@ -1,6 +1,8 @@ from __future__ import annotations import inspect +import itertools +import re import textwrap from typing import TYPE_CHECKING, Any, TypeVar @@ -11,13 +13,154 @@ from _gettsim.config import SUPPORTED_GROUPINGS if TYPE_CHECKING: + import datetime + from ttsim.function_types import PolicyFunction from ttsim.typing import ( GenericCallable, NestedDataDict, - NestedFunctionDict, - QualNameFunctionsDict, + NestedTTSIMObjectDict, + QualNameTTSIMFunctionDict, + ) + + +_DASHED_ISO_DATE = re.compile(r"\d{4}-\d{2}-\d{2}") + + +def validate_dashed_iso_date(date: str | datetime.date): + if not _DASHED_ISO_DATE.match(date): + raise ValueError(f"Date {date} does not match the format YYYY-MM-DD.") + + +def validate_date_range(start: datetime.date, end: datetime.date): + if start > end: + raise ValueError(f"The start date {start} must be before the end date {end}.") + + +def get_re_pattern_for_all_time_units_and_groupings( + supported_groupings: tuple[str, ...], supported_time_units: tuple[str, ...] +) -> re.Pattern: + """Get a regex pattern for time units and groupings. + + The pattern matches strings in any of these formats: + - (may contain underscores) + - _ + - _ + - __ + + Parameters + ---------- + supported_groupings + The supported groupings. + supported_time_units + The supported time units. + + Returns + ------- + pattern + The regex pattern. + """ + units = "".join(supported_time_units) + groupings = "|".join(supported_groupings) + return re.compile( + f"(?P.*?)" + f"(?:_(?P[{units}]))?" + f"(?:_(?P{groupings}))?" + f"$" + ) + + +def get_re_pattern_for_specific_time_units_and_groupings( + base_name: str, + supported_time_units: tuple[str, ...], + supported_groupings: tuple[str, ...], +) -> re.Pattern: + """Get a regex for a specific base name with optional time unit and aggregation. + + The pattern matches strings in any of these formats: + - + - _ + - _ + - __ + + Parameters + ---------- + base_name + The specific base name to match. + supported_time_units + The supported time units. + supported_groupings + The supported groupings. + + Returns + ------- + pattern + The regex pattern. + """ + units = "".join(supported_time_units) + groupings = "|".join(supported_groupings) + return re.compile( + f"(?P{re.escape(base_name)})" + f"(?:_(?P[{units}]))?" + f"(?:_(?P{groupings}))?" + f"$" + ) + + +def all_variations_of_base_name( + base_name: str, + supported_time_conversions: list[str], + supported_groupings: list[str], + create_conversions_for_time_units: bool, +) -> set[str]: + """Get possible derived function names given a base function name. + + Examples + -------- + >>> all_variations_of_base_name( + base_name="income", + supported_time_conversions=["y", "m"], + supported_groupings=["hh"], + create_conversions_for_time_units=True, + ) + {'income_m', 'income_y', 'income_hh_y', 'income_hh_m'} + + >>> all_variations_of_base_name( + base_name="claims_benefits", + supported_time_conversions=["y", "m"], + supported_groupings=["hh"], + create_conversions_for_time_units=False, ) + {'claims_benefits_hh'} + + Parameters + ---------- + base_name + The base function name. + supported_time_conversions + The supported time conversions. + supported_groupings + The supported groupings. + create_conversions_for_time_units + Whether to create conversions for time units. + + Returns + ------- + The names of all potential targets based on the base name. + """ + result = set() + if create_conversions_for_time_units: + for time_unit in supported_time_conversions: + result.add(f"{base_name}_{time_unit}") + for time_unit, aggregation in itertools.product( + supported_time_conversions, supported_groupings + ): + result.add(f"{base_name}_{time_unit}_{aggregation}") + else: + result.add(base_name) + for aggregation in supported_groupings: + result.add(f"{base_name}_{aggregation}") + return result class KeyErrorMessage(str): @@ -161,11 +304,11 @@ def insert_path_and_value( def partition_tree_by_reference_tree( - tree_to_partition: NestedFunctionDict | NestedDataDict, - reference_tree: NestedFunctionDict | NestedDataDict, + tree_to_partition: NestedTTSIMObjectDict | NestedDataDict, + reference_tree: NestedTTSIMObjectDict | NestedDataDict, ) -> tuple[ - NestedFunctionDict | NestedDataDict, - NestedFunctionDict | NestedDataDict, + NestedTTSIMObjectDict | NestedDataDict, + NestedTTSIMObjectDict | NestedDataDict, ]: """ Partition a tree into two based on the presence of its paths in a reference tree. @@ -211,8 +354,9 @@ def partition_by_reference_dict( Returns ------- - A tuple containing: - The first dictionary with keys present in both dictionaries. - - The second dictionary with keys absent in the reference dictionary. + A tuple containing: + - The first dictionary with keys present in both dictionaries. + - The second dictionary with keys absent in the reference dictionary. """ intersection = {k: v for k, v in to_partition.items() if k in reference_dict} difference = {k: v for k, v in to_partition.items() if k not in reference_dict} @@ -404,7 +548,8 @@ def format_key_path(key_tuple: tuple[str, ...]) -> str: def get_name_of_group_by_id( target_name: str, - group_by_functions: QualNameFunctionsDict, + group_by_functions: QualNameTTSIMFunctionDict, + supported_groupings: tuple[str, ...], ) -> str: """Get the group-by-identifier name for some target. @@ -428,7 +573,7 @@ def get_name_of_group_by_id( ------- The group-by-identifier, or an empty tuple if it is an individual-level variable. """ - for g in SUPPORTED_GROUPINGS: + for g in supported_groupings: if target_name.endswith(f"_{g}") and g == "hh": # Hardcode because hh_id is not part of the functions tree return "hh_id" diff --git a/src/ttsim/time_conversion.py b/src/ttsim/time_conversion.py index c962c2c3e3..fd21d1d1c2 100644 --- a/src/ttsim/time_conversion.py +++ b/src/ttsim/time_conversion.py @@ -1,20 +1,28 @@ from __future__ import annotations import inspect -import re from typing import TYPE_CHECKING +import dags.tree as dt from dags import rename_arguments from _gettsim.config import SUPPORTED_GROUPINGS -from ttsim.function_types import DerivedTimeConversionFunction, PolicyFunction +from ttsim.function_types import ( + DerivedTimeConversionFunction, + TTSIMFunction, +) +from ttsim.shared import ( + get_re_pattern_for_all_time_units_and_groupings, + get_re_pattern_for_specific_time_units_and_groupings, +) if TYPE_CHECKING: + import re from collections.abc import Callable - from ttsim.typing import QualNameDataDict, QualNameFunctionsDict + from ttsim.typing import QualNameDataDict, QualNameTTSIMFunctionDict -_TIME_UNITS = { +TIME_UNITS = { "y": "year", "q": "quarter", "m": "month", @@ -373,9 +381,9 @@ def d_to_w(value: float) -> float: def create_time_conversion_functions( - functions: QualNameFunctionsDict, + functions: QualNameTTSIMFunctionDict, data: QualNameDataDict, -) -> QualNameFunctionsDict: +) -> QualNameTTSIMFunctionDict: """ Create functions that convert variables to different time units. @@ -413,58 +421,63 @@ def create_time_conversion_functions( The functions dict with the new time conversion functions. """ - converted_functions = {} + converted_ttsim_objects = {} - # Create time-conversions for existing functions - for name, function in functions.items(): - all_time_conversions_for_this_function = _create_time_conversion_functions( - name=name, func=function + for source_name, ttsim_object in functions.items(): + all_time_units = tuple(TIME_UNITS) + pattern_all = get_re_pattern_for_all_time_units_and_groupings( + supported_groupings=SUPPORTED_GROUPINGS, + supported_time_units=all_time_units, ) - for der_name, der_func in all_time_conversions_for_this_function.items(): - # Skip if the function already exists or the data column exists - if der_name in converted_functions or der_name in data: - continue - else: - converted_functions[der_name] = der_func + pattern_specific = pattern_all.fullmatch(source_name) + base_name = pattern_specific.group("base_name") + + for data_name in data: + # If base_name is in provided data, base time conversions on that. + if pattern_specific := get_re_pattern_for_specific_time_units_and_groupings( + base_name=base_name, + supported_time_units=all_time_units, + supported_groupings=SUPPORTED_GROUPINGS, + ).fullmatch(data_name): + source_name = data_name # noqa: PLW2901 + break - # Create time-conversions for data columns - for name in data: - all_time_conversions_for_this_data_column = _create_time_conversion_functions( - name=name + all_time_conversions_for_this_function = _create_time_conversion_functions( + source_name=source_name, + function=ttsim_object, + time_unit_pattern=pattern_all, + all_time_units=all_time_units, ) - for der_name, der_func in all_time_conversions_for_this_data_column.items(): - # Skip if the function already exists or the data column exists - if der_name in converted_functions or der_name in data: + for der_name, der_func in all_time_conversions_for_this_function.items(): + if der_name in converted_ttsim_objects or der_name in data: continue else: - converted_functions[der_name] = der_func + converted_ttsim_objects[der_name] = der_func - return converted_functions + return converted_ttsim_objects def _create_time_conversion_functions( - name: str, func: PolicyFunction | None = None + source_name: str, + function: TTSIMFunction, + time_unit_pattern: re.Pattern, + all_time_units: tuple[str, ...], ) -> dict[str, DerivedTimeConversionFunction]: result: dict[str, DerivedTimeConversionFunction] = {} + match = time_unit_pattern.fullmatch(source_name) + base_name = match.group("base_name") + time_unit = match.group("time_unit") or "" + aggregation = match.group("aggregation") or "" + dependencies = set(inspect.signature(function).parameters) if function else set() - all_time_units = list(_TIME_UNITS) - - units = "".join(all_time_units) - groupings = "|".join([f"_{grouping}" for grouping in SUPPORTED_GROUPINGS]) - function_with_time_unit = re.compile( - f"(?P.*_)(?P[{units}])(?P{groupings})?" - ) - match = function_with_time_unit.fullmatch(name) - dependencies = set(inspect.signature(func).parameters) if func else set() - - if match: - base_name = match.group("base_name") - time_unit = match.group("time_unit") - aggregation = match.group("aggregation") or "" - + if match and time_unit: missing_time_units = [unit for unit in all_time_units if unit != time_unit] for missing_time_unit in missing_time_units: - new_name = f"{base_name}{missing_time_unit}{aggregation}" + new_name = ( + f"{base_name}_{missing_time_unit}_{aggregation}" + if aggregation + else f"{base_name}_{missing_time_unit}" + ) # Without this check, we could create cycles in the DAG: Consider a # hard-coded function `var_y` that takes `var_m` as an input, assuming it @@ -479,22 +492,25 @@ def _create_time_conversion_functions( continue result[new_name] = DerivedTimeConversionFunction( + leaf_name=dt.tree_path_from_qual_name(new_name)[-1], function=_create_function_for_time_unit( - name, - _time_conversion_functions[f"{time_unit}_to_{missing_time_unit}"], + source=source_name, + converter=_time_conversion_functions[ + f"{time_unit}_to_{missing_time_unit}" + ], ), - source=name, - source_function=func, - conversion_target=new_name, + source=source_name, + start_date=function.start_date, + end_date=function.end_date, ) return result def _create_function_for_time_unit( - function_name: str, converter: Callable[[float], float] + source: str, converter: Callable[[float], float] ) -> Callable[[float], float]: - @rename_arguments(mapper={"x": function_name}) + @rename_arguments(mapper={"x": source}) def func(x: float) -> float: return converter(x) diff --git a/src/ttsim/typing.py b/src/ttsim/typing.py index 7632251ebd..d07ea38914 100644 --- a/src/ttsim/typing.py +++ b/src/ttsim/typing.py @@ -24,29 +24,15 @@ ) from ttsim.aggregation import AggregateByGroupSpec, AggregateByPIDSpec - from ttsim.function_types import ( - DerivedAggregationFunction, - DerivedTimeConversionFunction, - GroupByFunction, - PolicyFunction, - ) + from ttsim.function_types import PolicyInput, TTSIMFunction, TTSIMObject + + NestedTTSIMObjectDict = Mapping[str, TTSIMObject | "NestedTTSIMObjectDict"] + QualNameTTSIMObjectDict = Mapping[str, TTSIMObject] # Specialise from dags' GenericCallable types to GETTSIM's functions. - NestedFunctionDict = Mapping[ - str, - PolicyFunction - | DerivedAggregationFunction - | DerivedTimeConversionFunction - | GroupByFunction - | "NestedFunctionDict", - ] - QualNameFunctionsDict = Mapping[ - str, - PolicyFunction - | DerivedAggregationFunction - | DerivedTimeConversionFunction - | GroupByFunction, - ] + NestedTTSIMFunctionDict = Mapping[str, TTSIMFunction | "NestedTTSIMFunctionDict"] + QualNameTTSIMFunctionDict = Mapping[str, TTSIMFunction] + QualNamePolicyInputDict = Mapping[str, PolicyInput] # Specialise from dags' NestedInputDict to GETTSIM's types. NestedDataDict = Mapping[str, pd.Series | "NestedDataDict"] diff --git a/src/ttsim/visualization.py b/src/ttsim/visualization.py index 0033a666b7..f14c759e6c 100644 --- a/src/ttsim/visualization.py +++ b/src/ttsim/visualization.py @@ -11,7 +11,6 @@ from pygments import highlight, lexers from pygments.formatters import HtmlFormatter -from _gettsim.config import DEFAULT_TARGETS from ttsim.combine_functions import ( combine_policy_functions_and_derived_functions, ) @@ -66,7 +65,7 @@ def plot_dag( a hover information. Sometimes, the tooltip is not properly displayed. """ - targets = build_targets_tree(DEFAULT_TARGETS if targets is None else targets) # noqa: F821 + targets = build_targets_tree(fixme if targets is None else targets) # noqa: F821 if isinstance(columns_overriding_functions, dict): names_of_columns_overriding_functions = dt.flatten_to_qual_names( diff --git a/tests/ttsim/_helpers.py b/tests/ttsim/_helpers.py deleted file mode 100644 index 42835be8c1..0000000000 --- a/tests/ttsim/_helpers.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import annotations - -from functools import lru_cache -from typing import TYPE_CHECKING - -from ttsim import ( - PolicyEnvironment, - set_up_policy_environment, -) -from ttsim.policy_environment import _parse_date - -if TYPE_CHECKING: - import datetime - - -def cached_set_up_policy_environment( - date: int | str | datetime.date, -) -> PolicyEnvironment: - normalized_date = _parse_date(date) - return _cached_set_up_policy_environment(normalized_date) - - -@lru_cache(maxsize=100) -def _cached_set_up_policy_environment(date: datetime.date) -> PolicyEnvironment: - return set_up_policy_environment(date) diff --git a/tests/ttsim/mettsim/config.py b/tests/ttsim/mettsim/config.py index 113729f6f6..66115b85da 100644 --- a/tests/ttsim/mettsim/config.py +++ b/tests/ttsim/mettsim/config.py @@ -6,7 +6,7 @@ from pathlib import Path -METTSIM_RESSOURCE_DIR = Path(__file__).parent / "functions" +RESOURCE_DIR = Path(__file__).parent FOREIGN_KEYS = ( @@ -14,3 +14,5 @@ ("p_id_parent_1",), ("p_id_parent_2",), ) + +SUPPORTED_GROUPINGS = ("fam", "sp", "hh") diff --git a/tests/ttsim/mettsim/functions/housing_benefits/amount.py b/tests/ttsim/mettsim/housing_benefits/amount.py similarity index 100% rename from tests/ttsim/mettsim/functions/housing_benefits/amount.py rename to tests/ttsim/mettsim/housing_benefits/amount.py diff --git a/tests/ttsim/mettsim/functions/housing_benefits/eligibility/eligibility.py b/tests/ttsim/mettsim/housing_benefits/eligibility/eligibility.py similarity index 89% rename from tests/ttsim/mettsim/functions/housing_benefits/eligibility/eligibility.py rename to tests/ttsim/mettsim/housing_benefits/eligibility/eligibility.py index aeeb66668c..af796cf8c0 100644 --- a/tests/ttsim/mettsim/functions/housing_benefits/eligibility/eligibility.py +++ b/tests/ttsim/mettsim/housing_benefits/eligibility/eligibility.py @@ -9,14 +9,15 @@ - Subsistence income is calculated per spouse and child """ -from ttsim import AggregateByGroupSpec, policy_function +from ttsim import AggregateByGroupSpec, AggregationType, policy_function -aggregation_specs = { - "number_of_children_fam": AggregateByGroupSpec( +aggregation_specs = ( + AggregateByGroupSpec( + target="number_of_children_fam", source="child", - aggr="sum", + agg=AggregationType.SUM, ), -} +) @policy_function(end_date="2019-12-31", leaf_name="requirement_fulfilled_fam") diff --git a/tests/ttsim/mettsim/functions/housing_benefits/income/income.py b/tests/ttsim/mettsim/housing_benefits/income/income.py similarity index 70% rename from tests/ttsim/mettsim/functions/housing_benefits/income/income.py rename to tests/ttsim/mettsim/housing_benefits/income/income.py index 493c146a4e..9ff2384dfa 100644 --- a/tests/ttsim/mettsim/functions/housing_benefits/income/income.py +++ b/tests/ttsim/mettsim/housing_benefits/income/income.py @@ -1,10 +1,10 @@ -from ttsim import RoundingDirection, RoundingSpec, policy_function +from ttsim import RoundingSpec, policy_function @policy_function( rounding_spec=RoundingSpec( base=1, - direction=RoundingDirection.DOWN, + direction="down", reference="§ 4 Gondorian Housing Benefit Law", ) ) diff --git a/tests/ttsim/mettsim/inputs.py b/tests/ttsim/mettsim/inputs.py new file mode 100644 index 0000000000..bab97a76fd --- /dev/null +++ b/tests/ttsim/mettsim/inputs.py @@ -0,0 +1,11 @@ +from ttsim import policy_input + + +@policy_input() +def p_id() -> int: + pass + + +@policy_input() +def hh_id() -> int: + pass diff --git a/tests/ttsim/mettsim/functions/payroll_tax/amount.py b/tests/ttsim/mettsim/payroll_tax/amount.py similarity index 100% rename from tests/ttsim/mettsim/functions/payroll_tax/amount.py rename to tests/ttsim/mettsim/payroll_tax/amount.py diff --git a/tests/ttsim/mettsim/functions/payroll_tax/child_tax_credit/child_tax_credit.py b/tests/ttsim/mettsim/payroll_tax/child_tax_credit/child_tax_credit.py similarity index 82% rename from tests/ttsim/mettsim/functions/payroll_tax/child_tax_credit/child_tax_credit.py rename to tests/ttsim/mettsim/payroll_tax/child_tax_credit/child_tax_credit.py index f06a245f21..33d46d4ad2 100644 --- a/tests/ttsim/mettsim/functions/payroll_tax/child_tax_credit/child_tax_credit.py +++ b/tests/ttsim/mettsim/payroll_tax/child_tax_credit/child_tax_credit.py @@ -1,12 +1,13 @@ -from ttsim import AggregateByPIDSpec, join_numpy, policy_function +from ttsim import AggregateByPIDSpec, AggregationType, join_numpy, policy_function -aggregation_specs = { - "amount_y": AggregateByPIDSpec( - p_id_to_aggregate_by="recipient_id", +aggregation_specs = ( + AggregateByPIDSpec( + target="amount_y", source="claim_of_child_y", - aggr="sum", + p_id_to_aggregate_by="recipient_id", + agg=AggregationType.SUM, ), -} +) @policy_function() diff --git a/tests/ttsim/mettsim/functions/payroll_tax/group_by_ids.py b/tests/ttsim/mettsim/payroll_tax/group_by_ids.py similarity index 100% rename from tests/ttsim/mettsim/functions/payroll_tax/group_by_ids.py rename to tests/ttsim/mettsim/payroll_tax/group_by_ids.py diff --git a/tests/ttsim/mettsim/functions/payroll_tax/income/amount.py b/tests/ttsim/mettsim/payroll_tax/income/amount.py similarity index 100% rename from tests/ttsim/mettsim/functions/payroll_tax/income/amount.py rename to tests/ttsim/mettsim/payroll_tax/income/amount.py diff --git a/tests/ttsim/mettsim/functions/payroll_tax/income/deductions.py b/tests/ttsim/mettsim/payroll_tax/income/deductions.py similarity index 100% rename from tests/ttsim/mettsim/functions/payroll_tax/income/deductions.py rename to tests/ttsim/mettsim/payroll_tax/income/deductions.py diff --git a/tests/ttsim/mettsim/payroll_tax/income/inputs.py b/tests/ttsim/mettsim/payroll_tax/income/inputs.py new file mode 100644 index 0000000000..2ffc246279 --- /dev/null +++ b/tests/ttsim/mettsim/payroll_tax/income/inputs.py @@ -0,0 +1,8 @@ +"""Input columns.""" + +from ttsim import policy_input + + +@policy_input() +def gross_wage_y() -> float: + """Annual gross wage.""" diff --git a/tests/ttsim/test_combine_functions.py b/tests/ttsim/test_combine_functions.py index 03d3f37b17..c21227b1c1 100644 --- a/tests/ttsim/test_combine_functions.py +++ b/tests/ttsim/test_combine_functions.py @@ -1,3 +1,4 @@ +import datetime import inspect import pandas as pd @@ -8,16 +9,17 @@ _annotate_aggregation_functions, _create_aggregate_by_group_functions, _create_aggregation_functions, - _create_one_aggregate_by_group_func, - _create_one_aggregate_by_p_id_func, + _create_one_aggregation_function, _fail_if_targets_not_in_functions, _get_name_of_aggregation_source, ) from ttsim.compute_taxes_and_transfers import compute_taxes_and_transfers from ttsim.function_types import ( + DEFAULT_END_DATE, + DEFAULT_START_DATE, DerivedAggregationFunction, - group_by_function, policy_function, + policy_input, ) from ttsim.policy_environment import PolicyEnvironment @@ -28,6 +30,31 @@ def function_with_bool_return(x: bool) -> bool: return x +@policy_input() +def x() -> int: + pass + + +@policy_input() +def x_f() -> float: + pass + + +@policy_input() +def x_b() -> bool: + pass + + +@policy_input() +def p_id() -> int: + pass + + +@policy_input() +def hh_id() -> int: + pass + + @pytest.fixture @policy_function(leaf_name="bar") def function_with_int_return(x: int) -> int: @@ -42,7 +69,7 @@ def function_with_float_return(x: int) -> float: @pytest.mark.parametrize( ( - "functions_tree", + "objects_tree", "targets_tree", "data_tree", "aggregations_specs_from_env", @@ -50,7 +77,14 @@ def function_with_float_return(x: int) -> float: [ ( # Aggregations derived from simple function arguments - {"namespace1": {"f": policy_function(leaf_name="f")(lambda x_hh: x_hh)}}, + { + "hh_id": hh_id, + "p_id": p_id, + "namespace1": { + "f": policy_function(leaf_name="f")(lambda x_hh: x_hh), + "x": x, + }, + }, {"namespace1": {"f": None}}, { "namespace1": {"x": pd.Series([1, 1, 1])}, @@ -62,11 +96,14 @@ def function_with_float_return(x: int) -> float: ( # Aggregations derived from namespaced function arguments { + "hh_id": hh_id, + "p_id": p_id, "namespace1": { "f": policy_function(leaf_name="f")( lambda inputs__x_hh: inputs__x_hh ) - } + }, + "inputs": {"x": x}, }, {"namespace1": {"f": None}}, { @@ -78,7 +115,14 @@ def function_with_float_return(x: int) -> float: ), ( # Aggregations derived from target - {"namespace1": {"f": policy_function(leaf_name="f")(lambda x: x)}}, + { + "hh_id": hh_id, + "p_id": p_id, + "namespace1": { + "f": policy_function(leaf_name="f")(lambda x: x), + "x": x, + }, + }, {"namespace1": {"f_hh": None}}, { "namespace1": {"x": pd.Series([1, 1, 1])}, @@ -89,7 +133,14 @@ def function_with_float_return(x: int) -> float: ), ( # Aggregations derived from simple environment specification - {"namespace1": {"f": policy_function(leaf_name="f")(lambda y_hh: y_hh)}}, + { + "hh_id": hh_id, + "p_id": p_id, + "namespace1": { + "f": policy_function(leaf_name="f")(lambda y_hh: y_hh), + "x": x, + }, + }, {"namespace1": {"f": None}}, { "namespace1": {"x": pd.Series([1, 1, 1])}, @@ -99,15 +150,21 @@ def function_with_float_return(x: int) -> float: { "namespace1": { "y_hh": AggregateByGroupSpec( + target="y_hh", source="x", - aggr=AggregationType.SUM, - ), + agg=AggregationType.SUM, + ) }, }, ), ( # Aggregations derived from namespaced environment specification - {"namespace1": {"f": policy_function(leaf_name="f")(lambda y_hh: y_hh)}}, + { + "hh_id": hh_id, + "p_id": p_id, + "namespace1": {"f": policy_function(leaf_name="f")(lambda y_hh: y_hh)}, + "inputs": {"x": x}, + }, {"namespace1": {"f": None}}, { "inputs": {"x": pd.Series([1, 1, 1])}, @@ -117,143 +174,164 @@ def function_with_float_return(x: int) -> float: { "namespace1": { "y_hh": AggregateByGroupSpec( + target="y_hh", source="inputs__x", - aggr=AggregationType.SUM, - ), + agg=AggregationType.SUM, + ) }, }, ), ], ) def test_create_aggregate_by_group_functions( - functions_tree, + objects_tree, targets_tree, data_tree, aggregations_specs_from_env, ): environment = PolicyEnvironment( - functions_tree=functions_tree, + raw_objects_tree=objects_tree, aggregation_specs_tree=aggregations_specs_from_env, ) compute_taxes_and_transfers( environment=environment, data_tree=data_tree, targets_tree=targets_tree, + supported_groupings=("hh",), + foreign_keys=(), ) +START_DATE = datetime.date.fromisoformat("1900-01-01") +END_DATE = datetime.date.fromisoformat("2100-12-31") + + @pytest.mark.parametrize( ( "functions", + "inputs", "aggregation_functions", - "types_input_variables", "expected_return_type", ), [ ( + {}, {}, { "foo": DerivedAggregationFunction( + leaf_name="foo", function=lambda x: x, source="x", - aggregation_target="foo", aggregation_method="count", + start_date=START_DATE, + end_date=END_DATE, ) }, - {}, int, ), ( {}, + {"x": x}, { "foo": DerivedAggregationFunction( + leaf_name="foo", function=lambda x: x, source="x", - aggregation_target="foo", aggregation_method="sum", + start_date=START_DATE, + end_date=END_DATE, ) }, - {"x": int}, int, ), ( {}, + {"x": x_f}, { "foo": DerivedAggregationFunction( + leaf_name="foo", function=lambda x: x, source="x", - aggregation_target="foo", aggregation_method="sum", + start_date=START_DATE, + end_date=END_DATE, ) }, - {"x": float}, float, ), ( {}, + {"x": x_b}, { "foo": DerivedAggregationFunction( + leaf_name="foo", function=lambda x: x, source="x", - aggregation_target="foo", aggregation_method="sum", + start_date=START_DATE, + end_date=END_DATE, ) }, - {"x": bool}, int, ), ( {"n1__foo": function_with_bool_return}, + {}, { "n1__foo_hh": DerivedAggregationFunction( + leaf_name="foo_hh", function=function_with_bool_return, source="n1__foo", - aggregation_target="foo_hh", aggregation_method="sum", + start_date=START_DATE, + end_date=END_DATE, ) }, - {}, int, ), ( {"n1__foo": function_with_float_return}, + {}, { "n1__foo_hh": DerivedAggregationFunction( + leaf_name="foo_hh", function=function_with_float_return, source="n1__foo", - aggregation_target="foo_hh", aggregation_method="sum", + start_date=START_DATE, + end_date=END_DATE, ) }, - {}, float, ), ( {"n1__foo": function_with_int_return}, + {}, { "n1__foo_hh": DerivedAggregationFunction( + leaf_name="foo_hh", function=function_with_int_return, source="n1__foo", - aggregation_target="foo_hh", aggregation_method="sum", + start_date=START_DATE, + end_date=END_DATE, ) }, - {}, int, ), ], ) def test_annotations_for_aggregation( functions, + inputs, aggregation_functions, - types_input_variables, expected_return_type, ): name_of_aggregation_function = next(iter(aggregation_functions.keys())) annotation_of_aggregation_function = _annotate_aggregation_functions( functions=functions, + inputs=inputs, aggregation_functions=aggregation_functions, - types_input_variables=types_input_variables, )[name_of_aggregation_function].__annotations__["return"] assert annotation_of_aggregation_function == expected_return_type @@ -276,6 +354,7 @@ def test_fail_if_targets_are_not_among_functions( @pytest.mark.parametrize( ( "functions", + "inputs", "aggregations", "aggregation_type", "top_level_namespace", @@ -284,25 +363,37 @@ def test_fail_if_targets_are_not_among_functions( [ ( {"foo": function_with_bool_return}, - {"foo_hh": AggregateByGroupSpec(source="foo", aggr=AggregationType.SUM)}, + {}, + { + "foo_hh": AggregateByGroupSpec( + target="foo_hh", source="foo", agg=AggregationType.SUM + ), + }, "group", ["foo"], {"foo": bool, "return": int}, ), ( {"foo": function_with_float_return}, - {"foo_hh": AggregateByGroupSpec(source="foo", aggr=AggregationType.SUM)}, + {}, + { + "foo_hh": AggregateByGroupSpec( + target="foo_hh", source="foo", agg=AggregationType.SUM + ), + }, "group", ["foo"], {"foo": float, "return": float}, ), ( {"foo": function_with_int_return}, + {}, { "foo_hh": AggregateByPIDSpec( + target="foo_hh", p_id_to_aggregate_by="foreign_id_col", source="foo", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ) }, "p_id", @@ -312,16 +403,23 @@ def test_fail_if_targets_are_not_among_functions( ], ) def test_annotations_are_applied_to_derived_functions( - functions, aggregations, aggregation_type, top_level_namespace, expected_annotations + functions, + inputs, + aggregations, + aggregation_type, + top_level_namespace, + expected_annotations, ): """Test that the annotations are applied to the derived functions.""" result_func = next( iter( _create_aggregation_functions( functions=functions, + inputs=inputs, aggregation_functions_to_create=aggregations, aggregation_type=aggregation_type, top_level_namespace=top_level_namespace, + supported_groupings=("hh",), ).values() ) ) @@ -331,6 +429,7 @@ def test_annotations_are_applied_to_derived_functions( @pytest.mark.parametrize( ( "functions", + "inputs", "targets", "data", "aggregations_from_environment", @@ -340,6 +439,7 @@ def test_annotations_are_applied_to_derived_functions( [ ( {"foo": policy_function(leaf_name="foo")(lambda x_hh: x_hh)}, + {"x": x}, {}, {"x": pd.Series([1])}, {}, @@ -348,6 +448,7 @@ def test_annotations_are_applied_to_derived_functions( ), ( {"n1__foo": policy_function(leaf_name="foo")(lambda n2__x_hh: n2__x_hh)}, + {"hh_id": hh_id, "n2__x": x}, {}, {"n2": {"x": pd.Series([1])}}, {}, @@ -356,6 +457,7 @@ def test_annotations_are_applied_to_derived_functions( ), ( {}, + {"x": x}, {"x_hh": None}, {"x": pd.Series([1])}, {}, @@ -364,11 +466,14 @@ def test_annotations_are_applied_to_derived_functions( ), ( {"foo": policy_function(leaf_name="foo")(lambda x: x)}, + {"hh_id": hh_id, "x": x}, {}, {"x": pd.Series([1])}, { "n1__foo_hh": AggregateByGroupSpec( - source="foo", aggr=AggregationType.SUM + target="foo_hh", + source="foo", + agg=AggregationType.SUM, ) }, ["x", "foo", "n1"], @@ -376,8 +481,9 @@ def test_annotations_are_applied_to_derived_functions( ), ], ) -def test_derived_aggregation_functions_are_in_correct_namespace( # noqa: PLR0913 +def test_derived_aggregation_functions_are_in_correct_namespace( functions, + inputs, targets, data, aggregations_from_environment, @@ -391,36 +497,174 @@ def test_derived_aggregation_functions_are_in_correct_namespace( # noqa: PLR091 """ result = _create_aggregate_by_group_functions( functions=functions, + inputs=inputs, targets=targets, data=data, aggregations_from_environment=aggregations_from_environment, top_level_namespace=top_level_namespace, + supported_groupings=("hh",), ) assert expected in result -def test_create_aggregation_with_derived_soure_column(): - aggregation_spec_dict = { - "foo_hh": AggregateByGroupSpec( - source="bar_bg", - aggr=AggregationType.SUM, - ) - } - result = _create_aggregate_by_group_functions( - functions={"bg_id": group_by_function()(lambda x: x)}, - targets={}, - data={"bar": pd.Series([1])}, - aggregations_from_environment=aggregation_spec_dict, - top_level_namespace=["foo", "bar", "bg_id"], +@pytest.mark.parametrize( + ( + "aggregation_target", + "aggregation_spec", + "group_by_id", + "functions", + "inputs", + "top_level_namespace", + "expected_start_date", + "expected_end_date", + ), + [ + ( + "x_hh", + AggregateByGroupSpec(target="x_hh", source="x", agg=AggregationType.SUM), + "hh_id", + {}, + {"x": x}, + ["x", "x_hh", "hh_id"], + DEFAULT_START_DATE, + DEFAULT_END_DATE, + ), + ( + "x_hh", + AggregateByGroupSpec(target="x_hh", source="x", agg=AggregationType.SUM), + "hh_id", + {"x": policy_function(leaf_name="x")(lambda x: x)}, + {}, + ["x", "x_hh", "hh_id"], + DEFAULT_START_DATE, + DEFAULT_END_DATE, + ), + ( + "x_hh", + AggregateByGroupSpec(target="x_hh", source="x", agg=AggregationType.SUM), + "hh_id", + { + "x": policy_function( + leaf_name="x", start_date="2025-01-01", end_date="2025-12-31" + )(lambda x: x) + }, + {}, + ["x", "x_hh", "hh_id"], + datetime.date.fromisoformat("2025-01-01"), + datetime.date.fromisoformat("2025-12-31"), + ), + ], +) +def test_aggregate_by_group_function_start_and_end_date( + aggregation_target, + aggregation_spec, + group_by_id, + functions, + inputs, + top_level_namespace, + expected_start_date, + expected_end_date, +): + result = _create_one_aggregation_function( + aggregation_target=aggregation_target, + aggregation_spec=aggregation_spec, + aggregation_type="group", + group_by_id=group_by_id, + functions=functions, + inputs=inputs, + top_level_namespace=top_level_namespace, ) - assert "foo_hh" in result - assert "bar_bg" in inspect.signature(result["foo_hh"]).parameters + assert result.start_date == expected_start_date + assert result.end_date == expected_end_date @pytest.mark.parametrize( ( "aggregation_target", "aggregation_spec", + "functions", + "inputs", + "top_level_namespace", + "expected_start_date", + "expected_end_date", + ), + [ + ( + "bar", + AggregateByPIDSpec( + target="bar_hh", + source="x", + agg=AggregationType.SUM, + p_id_to_aggregate_by="foreign_id_col", + ), + {"x": policy_function(leaf_name="x")(lambda x: x)}, + {}, + ["x", "bar", "foreign_id_col"], + DEFAULT_START_DATE, + DEFAULT_END_DATE, + ), + ( + "bar", + AggregateByPIDSpec( + target="bar_hh", + source="x", + agg=AggregationType.SUM, + p_id_to_aggregate_by="foreign_id_col", + ), + {}, + {"x": x}, + ["x", "bar", "foreign_id_col"], + DEFAULT_START_DATE, + DEFAULT_END_DATE, + ), + ( + "bar", + AggregateByPIDSpec( + target="bar_hh", + source="x", + agg=AggregationType.SUM, + p_id_to_aggregate_by="foreign_id_col", + ), + { + "x": policy_function( + leaf_name="x", start_date="2025-01-01", end_date="2025-12-31" + )(lambda x: x) + }, + {}, + ["x", "bar", "foreign_id_col"], + datetime.date.fromisoformat("2025-01-01"), + datetime.date.fromisoformat("2025-12-31"), + ), + ], +) +def test_aggregate_by_p_id_function_start_and_end_date( + aggregation_target, + aggregation_spec, + functions, + inputs, + top_level_namespace, + expected_start_date, + expected_end_date, +): + result = _create_one_aggregation_function( + aggregation_target=aggregation_target, + aggregation_spec=aggregation_spec, + aggregation_type="p_id", + group_by_id=None, + functions=functions, + inputs=inputs, + top_level_namespace=top_level_namespace, + ) + assert result.start_date == expected_start_date + assert result.end_date == expected_end_date + + +@pytest.mark.parametrize( + ( + "aggregation_target", + "aggregation_spec", + "functions", + "inputs", "group_by_id", "top_level_namespace", "expected_arg_names", @@ -428,21 +672,33 @@ def test_create_aggregation_with_derived_soure_column(): [ ( "foo_hh", - AggregateByGroupSpec(aggr=AggregationType.COUNT), + AggregateByGroupSpec( + target="foo_hh", source=None, agg=AggregationType.COUNT + ), + {"foo": policy_function(leaf_name="foo")(lambda x: x)}, + {}, "hh_id", ["foo", "hh_id"], ["hh_id"], ), ( "foo_hh", - AggregateByGroupSpec(aggr=AggregationType.SUM, source="foo"), + AggregateByGroupSpec( + target="foo_hh", source="foo", agg=AggregationType.SUM + ), + {"foo": policy_function(leaf_name="foo")(lambda x: x)}, + {}, "hh_id", ["foo", "hh_id"], ["hh_id", "foo"], ), ( "foo__bar_hh", - AggregateByGroupSpec(aggr=AggregationType.SUM, source="bar"), + AggregateByGroupSpec( + target="foo__bar_hh", source="bar", agg=AggregationType.SUM + ), + {"foo__bar": policy_function(leaf_name="bar")(lambda x: x)}, + {}, "hh_id", ["foo", "hh_id"], ["hh_id", "foo__bar"], @@ -452,15 +708,19 @@ def test_create_aggregation_with_derived_soure_column(): def test_function_arguments_are_namespaced_for_derived_group_funcs( aggregation_target, aggregation_spec, + functions, + inputs, group_by_id, top_level_namespace, expected_arg_names, ): - result = _create_one_aggregate_by_group_func( + result = _create_one_aggregation_function( aggregation_target=aggregation_target, aggregation_spec=aggregation_spec, + aggregation_type="group", group_by_id=group_by_id, - functions={}, + functions=functions, + inputs=inputs, top_level_namespace=top_level_namespace, ) assert all( @@ -473,6 +733,9 @@ def test_function_arguments_are_namespaced_for_derived_group_funcs( ( "aggregation_target", "aggregation_spec", + "functions", + "inputs", + "group_by_id", "top_level_namespace", "expected_arg_names", ), @@ -480,35 +743,63 @@ def test_function_arguments_are_namespaced_for_derived_group_funcs( ( "foo", AggregateByPIDSpec( - aggr=AggregationType.SUM, + target="foo_hh", + agg=AggregationType.SUM, source="bar", p_id_to_aggregate_by="foreign_id_col", ), + {"bar": policy_function(leaf_name="bar")(lambda x: x)}, + {}, + "foreign_id_col", ["foo", "foreign_id_col", "bar"], ["foreign_id_col", "bar"], ), ( "foo__fünc", AggregateByPIDSpec( - aggr=AggregationType.SUM, + target="foo_hh", + agg=AggregationType.SUM, source="bär", p_id_to_aggregate_by="foreign_id_col", ), + {"foo__bär": policy_function(leaf_name="bär")(lambda x: x)}, + {}, + "foreign_id_col", ["foo", "foreign_id_col"], ["foreign_id_col", "foo__bär"], ), + ( + "foo", + AggregateByPIDSpec( + target="foo_hh", + agg=AggregationType.SUM, + source="x", + p_id_to_aggregate_by="foreign_id_col", + ), + {}, + {"x": x}, + "foreign_id_col", + ["foo", "foreign_id_col", "x"], + ["foreign_id_col", "x"], + ), ], ) def test_function_arguments_are_namespaced_for_derived_p_id_funcs( aggregation_target, aggregation_spec, + functions, + inputs, + group_by_id, top_level_namespace, expected_arg_names, ): - result = _create_one_aggregate_by_p_id_func( + result = _create_one_aggregation_function( aggregation_target=aggregation_target, aggregation_spec=aggregation_spec, - functions={}, + aggregation_type="p_id", + group_by_id=group_by_id, + functions=functions, + inputs=inputs, top_level_namespace=top_level_namespace, ) assert all( @@ -521,6 +812,8 @@ def test_function_arguments_are_namespaced_for_derived_p_id_funcs( ( "aggregation_target", "aggregation_spec", + "functions", + "inputs", "group_by_id", "top_level_namespace", "source_col_name", @@ -528,14 +821,22 @@ def test_function_arguments_are_namespaced_for_derived_p_id_funcs( [ ( "foo_hh", - AggregateByGroupSpec(aggr=AggregationType.SUM, source="foo"), + AggregateByGroupSpec( + target="foo_hh", agg=AggregationType.SUM, source="foo" + ), + {}, + {"foo": policy_function(leaf_name="foo")(lambda x: x)}, "hh_id", ["foo", "hh_id"], "foo", ), ( "foo__bar_hh", - AggregateByGroupSpec(aggr=AggregationType.SUM, source="bar"), + AggregateByGroupSpec( + target="bar_hh", agg=AggregationType.SUM, source="bar" + ), + {}, + {"foo__bar": policy_function(leaf_name="bar")(lambda x: x)}, "hh_id", ["foo", "hh_id"], "foo__bar", @@ -545,15 +846,19 @@ def test_function_arguments_are_namespaced_for_derived_p_id_funcs( def test_source_column_name_of_aggregate_by_group_func_is_qualified( aggregation_target, aggregation_spec, + functions, + inputs, group_by_id, top_level_namespace, source_col_name, ): - result = _create_one_aggregate_by_group_func( + result = _create_one_aggregation_function( aggregation_target=aggregation_target, aggregation_spec=aggregation_spec, + aggregation_type="group", group_by_id=group_by_id, - functions={}, + functions=functions, + inputs=inputs, top_level_namespace=top_level_namespace, ) assert result.source == source_col_name @@ -563,6 +868,8 @@ def test_source_column_name_of_aggregate_by_group_func_is_qualified( ( "aggregation_target", "aggregation_spec", + "functions", + "inputs", "top_level_namespace", "source_col_name", ), @@ -570,20 +877,26 @@ def test_source_column_name_of_aggregate_by_group_func_is_qualified( ( "foo", AggregateByPIDSpec( - aggr=AggregationType.SUM, + target="foo_hh", + agg=AggregationType.SUM, source="bar", p_id_to_aggregate_by="foreign_id_col", ), + {}, + {"bar": policy_function(leaf_name="bar")(lambda x: x)}, ["foo", "foreign_id_col", "bar"], "bar", ), ( "foo__fünc", AggregateByPIDSpec( - aggr=AggregationType.SUM, + target="foo_hh", + agg=AggregationType.SUM, source="bär", p_id_to_aggregate_by="foreign_id_col", ), + {}, + {"foo__bär": policy_function(leaf_name="bär")(lambda x: x)}, ["foo", "foreign_id_col"], "foo__bär", ), @@ -592,13 +905,18 @@ def test_source_column_name_of_aggregate_by_group_func_is_qualified( def test_source_column_name_of_aggregate_by_p_id_func_is_qualified( aggregation_target, aggregation_spec, + functions, + inputs, top_level_namespace, source_col_name, ): - result = _create_one_aggregate_by_p_id_func( + result = _create_one_aggregation_function( aggregation_target=aggregation_target, aggregation_spec=aggregation_spec, - functions={}, + aggregation_type="p_id", + group_by_id=None, + functions=functions, + inputs=inputs, top_level_namespace=top_level_namespace, ) assert result.source == source_col_name diff --git a/tests/ttsim/test_compute_taxes_and_transfers.py b/tests/ttsim/test_compute_taxes_and_transfers.py index 7e1de59d2e..f07c94aebf 100644 --- a/tests/ttsim/test_compute_taxes_and_transfers.py +++ b/tests/ttsim/test_compute_taxes_and_transfers.py @@ -6,26 +6,42 @@ import numpy import pandas as pd import pytest -from mettsim.config import FOREIGN_KEYS -from mettsim.functions.payroll_tax.group_by_ids import fam_id, sp_id +from mettsim.config import FOREIGN_KEYS, SUPPORTED_GROUPINGS +from mettsim.payroll_tax.group_by_ids import fam_id, sp_id -from gettsim import FunctionsAndColumnsOverlapWarning from ttsim.aggregation import AggregateByGroupSpec, AggregateByPIDSpec, AggregationType from ttsim.compute_taxes_and_transfers import ( + FunctionsAndColumnsOverlapWarning, _convert_data_to_correct_types, _fail_if_foreign_keys_are_invalid, _fail_if_group_variables_not_constant_within_groups, - _fail_if_pid_is_non_unique, + _fail_if_p_id_is_non_unique, + _get_top_level_namespace, _partial_parameters_to_functions, compute_taxes_and_transfers, ) from ttsim.config import numpy_or_jax as np -from ttsim.function_types import group_by_function, policy_function +from ttsim.function_types import group_by_function, policy_function, policy_input from ttsim.policy_environment import PolicyEnvironment from ttsim.shared import assert_valid_ttsim_pytree from ttsim.typing import convert_series_to_internal_type +@policy_input() +def p_id() -> int: + pass + + +@policy_input() +def hh_id() -> int: + pass + + +@policy_input() +def betrag_m() -> float: + pass + + @pytest.fixture(scope="module") def minimal_input_data(): n_individuals = 5 @@ -61,9 +77,10 @@ def func_before_partial(arg_1, payroll_tax_params): def test_output_as_tree(minimal_input_data): environment = PolicyEnvironment( { + "p_id": p_id, "module": { "test_func": policy_function(leaf_name="test_func")(lambda p_id: p_id) - } + }, } ) @@ -71,6 +88,8 @@ def test_output_as_tree(minimal_input_data): data_tree=minimal_input_data, environment=environment, targets_tree={"module": {"test_func": None}}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) assert isinstance(out, dict) @@ -93,6 +112,8 @@ def test_warn_if_functions_and_columns_overlap(): }, environment=environment, targets_tree={"some_target": None}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) @@ -109,6 +130,8 @@ def test_dont_warn_if_functions_and_columns_dont_overlap(): }, environment=environment, targets_tree={"some_func": None}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) @@ -131,35 +154,39 @@ def test_recipe_to_ignore_warning_if_functions_and_columns_overlap(): }, environment=environment, targets_tree={"unique": None}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) assert len(warning_list) == 0 -def test_fail_if_pid_does_not_exist(): +def test_fail_if_p_id_does_not_exist(): data = {"hh_id": pd.Series(data=numpy.arange(8), name="hh_id")} with pytest.raises(ValueError): - _fail_if_pid_is_non_unique(data) + _fail_if_p_id_is_non_unique(data) -def test_fail_if_pid_is_non_unique(): +def test_fail_if_p_id_is_non_unique(): data = {"p_id": pd.Series(data=numpy.arange(4).repeat(2), name="p_id")} with pytest.raises(ValueError): - _fail_if_pid_is_non_unique(data) + _fail_if_p_id_is_non_unique(data) @pytest.mark.parametrize("foreign_key_path", FOREIGN_KEYS) -def test_fail_if_foreign_key_points_to_non_existing_pid(foreign_key_path): +def test_fail_if_foreign_key_points_to_non_existing_p_id(foreign_key_path): foreign_key_name = dt.qual_name_from_tree_path(foreign_key_path) data = { foreign_key_name: pd.Series([0, 1, 4]), "p_id": pd.Series([1, 2, 3]), } - with pytest.raises(ValueError, match="not a valid p_id in the\ninput data"): - _fail_if_foreign_keys_are_invalid(data, p_id=data["p_id"]) + with pytest.raises(ValueError, match=r"not a valid p_id in the\sinput data"): + _fail_if_foreign_keys_are_invalid( + data=data, p_id=data["p_id"], foreign_keys=FOREIGN_KEYS + ) @pytest.mark.parametrize("foreign_key_path", FOREIGN_KEYS) @@ -170,11 +197,13 @@ def test_allow_minus_one_as_foreign_key(foreign_key_path): "p_id": pd.Series([1, 2, 3]), } - _fail_if_foreign_keys_are_invalid(data, p_id=data["p_id"]) + _fail_if_foreign_keys_are_invalid( + data=data, p_id=data["p_id"], foreign_keys=FOREIGN_KEYS + ) @pytest.mark.parametrize("foreign_key_path", FOREIGN_KEYS) -def test_fail_if_foreign_key_points_to_pid_of_same_row(foreign_key_path): +def test_fail_if_foreign_key_points_to_p_id_of_same_row(foreign_key_path): foreign_key_name = dt.qual_name_from_tree_path(foreign_key_path) data = { foreign_key_name: pd.Series([1, 3, 3]), @@ -182,7 +211,9 @@ def test_fail_if_foreign_key_points_to_pid_of_same_row(foreign_key_path): } with pytest.raises(ValueError, match="are equal to the p_id"): - _fail_if_foreign_keys_are_invalid(data, p_id=data["p_id"]) + _fail_if_foreign_keys_are_invalid( + data=data, p_id=data["p_id"], foreign_keys=FOREIGN_KEYS + ) @pytest.mark.parametrize( @@ -211,6 +242,7 @@ def test_fail_if_group_variables_not_constant_within_groups(data, functions): _fail_if_group_variables_not_constant_within_groups( data=data, functions=functions, + supported_groupings=SUPPORTED_GROUPINGS, ) @@ -233,7 +265,11 @@ def c(b): match="The following data columns are missing", ): compute_taxes_and_transfers( - minimal_input_data, environment, targets_tree={"c": None} + data_tree=minimal_input_data, + environment=environment, + targets_tree={"c": None}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) @@ -248,7 +284,11 @@ def b(a): environment = PolicyEnvironment({"a": a, "b": b}) compute_taxes_and_transfers( - minimal_input_data, environment, targets_tree={"b": None} + data_tree=minimal_input_data, + environment=environment, + targets_tree={"b": None}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) @@ -262,20 +302,30 @@ def test_fail_if_targets_are_not_in_functions_or_in_columns_overriding_functions match="The following targets have no corresponding function", ): compute_taxes_and_transfers( - minimal_input_data, environment, targets_tree={"unknown_target": None} + data_tree=minimal_input_data, + environment=environment, + targets_tree={"unknown_target": None}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) -def test_fail_if_missing_pid(): +def test_fail_if_missing_p_id(): data = {"hh_id": pd.Series([1, 2, 3], name="hh_id")} with pytest.raises( ValueError, match="The input data must contain the p_id", ): - compute_taxes_and_transfers(data, PolicyEnvironment({}), targets_tree={}) + compute_taxes_and_transfers( + data_tree=data, + environment=PolicyEnvironment({}), + targets_tree={}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), + ) -def test_fail_if_non_unique_pid(minimal_input_data): +def test_fail_if_non_unique_p_id(minimal_input_data): data = copy.deepcopy(minimal_input_data) data["p_id"][:] = 1 @@ -283,7 +333,13 @@ def test_fail_if_non_unique_pid(minimal_input_data): ValueError, match="The following p_ids are non-unique", ): - compute_taxes_and_transfers(data, PolicyEnvironment({}), targets_tree={}) + compute_taxes_and_transfers( + data_tree=data, + environment=PolicyEnvironment({}), + targets_tree={}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), + ) def test_partial_parameters_to_functions(): @@ -312,20 +368,33 @@ def test_user_provided_aggregate_by_group_specs(): }, } - aggregation_specs_tree = { + inputs = { + "p_id": p_id, + "hh_id": hh_id, "module_name": { - "betrag_m_hh": AggregateByGroupSpec( + "betrag_m": betrag_m, + }, + } + + aggregation_specs_tree = { + "module_name": ( + AggregateByGroupSpec( + target="betrag_m_hh", source="betrag_m", - aggr=AggregationType.SUM, - ) - } + agg=AggregationType.SUM, + ), + ) } expected_res = pd.Series([200, 200, 100]) out = compute_taxes_and_transfers( - data, - PolicyEnvironment({}, aggregation_specs_tree=aggregation_specs_tree), + data_tree=data, + environment=PolicyEnvironment( + raw_objects_tree=inputs, aggregation_specs_tree=aggregation_specs_tree + ), targets_tree={"module_name": {"betrag_m_hh": None}}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) numpy.testing.assert_array_almost_equal( @@ -339,18 +408,11 @@ def test_user_provided_aggregate_by_group_specs(): { "module_name": { "betrag_double_m_hh": AggregateByGroupSpec( + target="betrag_double_m_hh", source="betrag_m_double", - aggr=AggregationType.MAX, + agg=AggregationType.MAX, ), - }, - }, - { - "module_name": { - "betrag_double_m_hh": AggregateByGroupSpec( - source="module_name__betrag_m_double", - aggr=AggregationType.MAX, - ), - }, + } }, ], ) @@ -364,11 +426,14 @@ def test_user_provided_aggregate_by_group_specs_function(aggregation_specs_tree) } expected_res = pd.Series([400, 400, 200]) + @policy_function() def betrag_m_double(betrag_m): return 2 * betrag_m environment = PolicyEnvironment( { + "p_id": p_id, + "hh_id": hh_id, "module_name": { "betrag_m_double": policy_function(leaf_name="betrag_m_double")( betrag_m_double @@ -377,11 +442,12 @@ def betrag_m_double(betrag_m): }, aggregation_specs_tree=aggregation_specs_tree, ) - out = compute_taxes_and_transfers( - data, - environment, + data_tree=data, + environment=environment, targets_tree={"module_name": {"betrag_double_m_hh": None}}, + foreign_keys=FOREIGN_KEYS, + supported_groupings=("hh",), ) numpy.testing.assert_array_almost_equal( @@ -389,7 +455,7 @@ def betrag_m_double(betrag_m): ) -def test_aggregate_by_group_specs_missing_group_sufix(): +def test_aggregate_by_group_specs_missing_group_suffix(): data = { "p_id": pd.Series([1, 2, 3], name="p_id"), "hh_id": pd.Series([1, 1, 2], name="hh_id"), @@ -400,10 +466,11 @@ def test_aggregate_by_group_specs_missing_group_sufix(): aggregation_specs_tree = { "module_name": { "betrag_agg_m": AggregateByGroupSpec( + target="betrag_agg_m", source="betrag_m", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ) - }, + } } with pytest.raises( ValueError, @@ -413,17 +480,20 @@ def test_aggregate_by_group_specs_missing_group_sufix(): data, PolicyEnvironment({}, aggregation_specs_tree=aggregation_specs_tree), targets_tree={"module_name": {"betrag_agg_m": None}}, + supported_groupings=("hh",), + foreign_keys=FOREIGN_KEYS, ) def test_aggregate_by_group_specs_agg_not_impl(): with pytest.raises( - ValueError, - match="aggr must be of type AggregationType, not ", + TypeError, + match="agg must be of type AggregationType, not ", ): AggregateByGroupSpec( + target="betrag_agg_m", source="betrag_m", - aggr="sum", + agg="sum", ) @@ -434,9 +504,10 @@ def test_aggregate_by_group_specs_agg_not_impl(): { "module": { "target_func": AggregateByPIDSpec( + target="target_func", p_id_to_aggregate_by="hh_id", source="source_func", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ) } }, @@ -448,9 +519,10 @@ def test_aggregate_by_group_specs_agg_not_impl(): { "module": { "target_func_m": AggregateByPIDSpec( + target="target_func_m", p_id_to_aggregate_by="hh_id", source="source_func_m", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ) } }, @@ -462,9 +534,10 @@ def test_aggregate_by_group_specs_agg_not_impl(): { "module": { "target_func_m": AggregateByPIDSpec( + target="target_func_m", p_id_to_aggregate_by="hh_id", source="source_func_m", - aggr=AggregationType.SUM, + agg=AggregationType.SUM, ) } }, @@ -487,7 +560,11 @@ def test_user_provided_aggregate_by_p_id_specs( def source_func(p_id: int) -> int: # noqa: ARG001 return 100 - functions_tree = {"module": {leaf_name: source_func}} + functions_tree = { + "module": {leaf_name: source_func}, + "p_id": p_id, + "hh_id": hh_id, + } environment = PolicyEnvironment( functions_tree, @@ -497,6 +574,8 @@ def source_func(p_id: int) -> int: # noqa: ARG001 minimal_input_data_shared_hh, environment, targets_tree=target_tree, + supported_groupings=("hh",), + foreign_keys=FOREIGN_KEYS, )["module"][next(iter(target_tree["module"].keys()))] numpy.testing.assert_array_almost_equal(out, expected) @@ -607,6 +686,7 @@ def test_fail_if_cannot_be_converted_to_internal_type( convert_series_to_internal_type(input_data, expected_type) +@pytest.mark.skip @pytest.mark.parametrize( "data, functions_overridden", [ @@ -626,6 +706,7 @@ def test_provide_endogenous_groupings(data, functions_overridden): _convert_data_to_correct_types(data, functions_overridden) +@pytest.mark.skip @pytest.mark.parametrize( "data, functions_overridden, error_match", [ @@ -697,3 +778,59 @@ def test_fail_if_cannot_be_converted_to_correct_type( def test_assert_valid_ttsim_pytree(tree, leaf_checker, err_substr): with pytest.raises(TypeError, match=re.escape(err_substr)): assert_valid_ttsim_pytree(tree, leaf_checker, "tree") + + +@pytest.mark.parametrize( + ( + "environment", + "supported_time_conversions", + "supported_groupings", + "expected", + ), + [ + ( + PolicyEnvironment( + raw_objects_tree={ + "foo_m": policy_function(leaf_name="foo_m")(lambda x: x) + }, + aggregation_specs_tree={}, + ), + ["m", "y"], + ["hh"], + {"foo_m", "foo_y", "foo_m_hh", "foo_y_hh"}, + ), + ( + PolicyEnvironment( + raw_objects_tree={"foo": policy_function(leaf_name="foo")(lambda x: x)}, + aggregation_specs_tree={}, + ), + ["m", "y"], + ["hh"], + {"foo", "foo_hh"}, + ), + ( + PolicyEnvironment( + raw_objects_tree={}, + aggregation_specs_tree={ + "foo_hh": AggregateByGroupSpec( + target="foo_hh", + source="foo", + agg=AggregationType.SUM, + ), + }, + ), + ["m", "y"], + ["hh"], + {"foo", "foo_hh"}, + ), + ], +) +def test_get_top_level_namespace( + environment, supported_time_conversions, supported_groupings, expected +): + result = _get_top_level_namespace( + environment=environment, + supported_time_conversions=supported_time_conversions, + supported_groupings=supported_groupings, + ) + assert result == expected diff --git a/tests/ttsim/test_dates_active.py b/tests/ttsim/test_dates_active.py index 5b297cde23..753f02aa2d 100644 --- a/tests/ttsim/test_dates_active.py +++ b/tests/ttsim/test_dates_active.py @@ -4,8 +4,8 @@ from ttsim.function_types import policy_function from ttsim.loader import ( - ConflictingTimeDependentFunctionsError, - _fail_if_multiple_policy_functions_are_active_at_the_same_time, + ConflictingTimeDependentObjectsError, + _fail_if_multiple_ttsim_objects_are_active_at_the_same_time, ) # Start date ----------------------------------------------- @@ -153,8 +153,8 @@ def test_func(): ], ) def test_dates_active_no_conflicts(functions): - _fail_if_multiple_policy_functions_are_active_at_the_same_time( - policy_functions=functions, module_name="" + _fail_if_multiple_ttsim_objects_are_active_at_the_same_time( + ttsim_objects=functions, module_name="" ) @@ -200,7 +200,7 @@ def test_dates_active_no_conflicts(functions): ], ) def test_dates_active_with_conflicts(functions): - with pytest.raises(ConflictingTimeDependentFunctionsError): - _fail_if_multiple_policy_functions_are_active_at_the_same_time( - policy_functions=functions, module_name="" + with pytest.raises(ConflictingTimeDependentObjectsError): + _fail_if_multiple_ttsim_objects_are_active_at_the_same_time( + ttsim_objects=functions, module_name="" ) diff --git a/tests/ttsim/test_docs.py b/tests/ttsim/test_docs.py index 4448b05d81..54d3048c03 100644 --- a/tests/ttsim/test_docs.py +++ b/tests/ttsim/test_docs.py @@ -7,11 +7,10 @@ from _gettsim.config import ( RESOURCE_DIR, - TYPES_INPUT_VARIABLES, ) from ttsim.loader import ( load_aggregation_specs_tree, - load_functions_tree_for_date, + load_objects_tree_for_date, ) from ttsim.shared import remove_group_suffix @@ -23,7 +22,7 @@ def _nice_output_list_of_strings(list_of_strings): @pytest.fixture(scope="module") def default_input_variables(): - return sorted(TYPES_INPUT_VARIABLES.keys()) + return sorted(f for f in todo_functions_tree if isinstance(f, PolicyInput)) @pytest.fixture(scope="module") @@ -41,8 +40,9 @@ def aggregation_dict(): def time_indep_function_names(all_function_names): time_dependent_functions = {} for year in range(1990, 2023): - year_functions = load_functions_tree_for_date( - datetime.date(year=year, month=1, day=1) + year_functions = load_objects_tree_for_date( + resource_dir=RESOURCE_DIR, + date=datetime.date(year=year, month=1, day=1), ) new_dict = {func.function.__name__: func.leaf_name for func in year_functions} time_dependent_functions = {**time_dependent_functions, **new_dict} diff --git a/tests/ttsim/test_function_types.py b/tests/ttsim/test_function_types.py new file mode 100644 index 0000000000..fe22edbe43 --- /dev/null +++ b/tests/ttsim/test_function_types.py @@ -0,0 +1,85 @@ +import pytest + +from ttsim.function_types import ( + PolicyFunction, + PolicyInput, + policy_function, + policy_input, +) + +# ====================================================================================== +# PolicyFunction and policy_function +# ====================================================================================== + + +@policy_function() +def simple_policy_function(x): + return x + + +@policy_function(leaf_name="simple_policy_function") +def policy_function_with_internal_name(x): + return x + + +@policy_function(start_date="2007-01-01", end_date="2011-12-31") +def policy_function_with_dates(x): + return x + + +@pytest.mark.parametrize( + "function", + [ + simple_policy_function, + policy_function_with_internal_name, + ], +) +def test_policy_function_type(function): + assert isinstance(function, PolicyFunction) + + +@pytest.mark.parametrize( + "function", + [ + simple_policy_function, + policy_function_with_internal_name, + ], +) +def test_policy_function_name(function): + assert function.leaf_name == "simple_policy_function" + + +def test_policy_function_with_dates(): + assert str(policy_function_with_dates.start_date) == "2007-01-01" + assert str(policy_function_with_dates.end_date) == "2011-12-31" + + +# ====================================================================================== +# PolicyInput and policy_input +# ====================================================================================== + + +@policy_input() +def simple_policy_input() -> float: + pass + + +@policy_input(start_date="2007-01-01", end_date="2011-12-31") +def policy_input_with_dates() -> float: + pass + + +@pytest.mark.parametrize( + "function", + [ + simple_policy_input, + policy_input_with_dates, + ], +) +def test_policy_input_type(function): + assert isinstance(function, PolicyInput) + + +def test_policy_input_with_dates(): + assert str(policy_input_with_dates.start_date) == "2007-01-01" + assert str(policy_input_with_dates.end_date) == "2011-12-31" diff --git a/tests/ttsim/test_loader.py b/tests/ttsim/test_loader.py index ad2e21762d..e6916fe5e5 100644 --- a/tests/ttsim/test_loader.py +++ b/tests/ttsim/test_loader.py @@ -4,7 +4,7 @@ import numpy import pytest -from mettsim.config import METTSIM_RESSOURCE_DIR +from mettsim.config import RESOURCE_DIR from ttsim.function_types import _vectorize_func, policy_function from ttsim.loader import ( @@ -20,15 +20,15 @@ def test_load_path(): assert _load_module( - METTSIM_RESSOURCE_DIR / "payroll_tax" / "amount.py", - METTSIM_RESSOURCE_DIR, + RESOURCE_DIR / "payroll_tax" / "amount.py", + RESOURCE_DIR, ) def test_dont_load_init_py(): """Don't load __init__.py files as sources for PolicyFunctions and AggregationSpecs.""" - all_files = _find_python_files_recursively(METTSIM_RESSOURCE_DIR) + all_files = _find_python_files_recursively(RESOURCE_DIR) assert "__init__.py" not in [file.name for file in all_files] @@ -62,15 +62,12 @@ def test_vectorize_func(vectorized_function: Callable) -> None: ), [ ( - METTSIM_RESSOURCE_DIR - / "payroll_tax" - / "child_tax_credit" - / "child_tax_credit.py", - METTSIM_RESSOURCE_DIR, + RESOURCE_DIR / "payroll_tax" / "child_tax_credit" / "child_tax_credit.py", + RESOURCE_DIR, ("payroll_tax", "child_tax_credit"), ), - (METTSIM_RESSOURCE_DIR / "foo" / "bar.py", METTSIM_RESSOURCE_DIR, ("foo",)), - (METTSIM_RESSOURCE_DIR / "foo.py", METTSIM_RESSOURCE_DIR, tuple()), # noqa: C408 + (RESOURCE_DIR / "foo" / "bar.py", RESOURCE_DIR, ("foo",)), + (RESOURCE_DIR / "foo.py", RESOURCE_DIR, tuple()), # noqa: C408 ], ) def test_convert_path_to_tree_path( diff --git a/tests/ttsim/test_mettsim.py b/tests/ttsim/test_mettsim.py new file mode 100644 index 0000000000..a8dcb6978a --- /dev/null +++ b/tests/ttsim/test_mettsim.py @@ -0,0 +1,17 @@ +import pytest +from utils import ( + PolicyTest, + execute_test, + get_policy_test_ids_and_cases, +) + +policy_test_ids_and_cases = get_policy_test_ids_and_cases() + + +@pytest.mark.parametrize( + "test", + policy_test_ids_and_cases.values(), + ids=policy_test_ids_and_cases.keys(), +) +def test_mettsim(test: PolicyTest): + execute_test(test) diff --git a/tests/ttsim/test_policy_environment.py b/tests/ttsim/test_policy_environment.py index b321dc31c2..e46f1802f8 100644 --- a/tests/ttsim/test_policy_environment.py +++ b/tests/ttsim/test_policy_environment.py @@ -9,6 +9,7 @@ import optree import pandas as pd import pytest +from mettsim.config import RESOURCE_DIR from ttsim.function_types import ( GroupByFunction, @@ -19,12 +20,12 @@ PolicyEnvironment, _fail_if_name_of_last_branch_element_not_leaf_name_of_function, _load_parameter_group_from_yaml, - load_functions_tree_for_date, + load_objects_tree_for_date, set_up_policy_environment, ) if TYPE_CHECKING: - from ttsim.typing import NestedFunctionDict + from ttsim.typing import NestedTTSIMObjectDict YAML_PATH = Path(__file__).parent / "test_parameters" @@ -34,12 +35,12 @@ def test_func_exists_in_tree(self): function = policy_function(leaf_name="foo")(lambda: 1) environment = PolicyEnvironment({"foo": function}) - assert environment.functions_tree["foo"] == function + assert environment.raw_objects_tree["foo"] == function def test_func_does_not_exist_in_tree(self): environment = PolicyEnvironment({}, {}) - assert "foo" not in environment.functions_tree + assert "foo" not in environment.raw_objects_tree @pytest.mark.parametrize( "environment", @@ -56,9 +57,9 @@ def test_func_does_not_exist_in_tree(self): ) def test_upsert_functions(self, environment: PolicyEnvironment): new_function = policy_function(leaf_name="foo")(lambda: 3) - new_environment = environment.upsert_policy_functions({"foo": new_function}) + new_environment = environment.upsert_objects({"foo": new_function}) - assert new_environment.functions_tree["foo"] == new_function + assert new_environment.raw_objects_tree["foo"] == new_function @pytest.mark.parametrize( "environment", @@ -75,12 +76,12 @@ def test_replace_all_parameters(self, environment: PolicyEnvironment): def test_leap_year_correctly_handled(): - set_up_policy_environment(date="02-29-2020") + set_up_policy_environment(date="02-29-2020", resource_dir=RESOURCE_DIR) def test_fail_if_invalid_date(): with pytest.raises(ValueError): - set_up_policy_environment(date="02-30-2020") + set_up_policy_environment(date="02-30-2020", resource_dir=RESOURCE_DIR) def test_fail_if_invalid_access_different_date(): @@ -119,22 +120,10 @@ def test_access_different_date_jahresanfang(): "tree, last_day, function_name_last_day, function_name_next_day", [ ( - {"einkommensteuer": {"abzüge": {"altersfreibetrag_y": None}}}, - date(2004, 12, 31), - "altersfreibetrag_y_bis_2004", - "altersfreibetrag_y_ab_2005", - ), - ( - {"einkommensteuer": {"abzüge": {"alleinerziehend_betrag_y": None}}}, - date(2014, 12, 31), - "alleinerziehend_betrag_y_pauschal", - "alleinerziehend_betrag_y_nach_kinderzahl", - ), - ( - {"einkommensteuer": {"gesamteinkommen_ohne_abzüge_y": None}}, - date(2008, 12, 31), - "gesamteinkommen_ohne_abzüge_mit_kapitaleinkünften_y", - "gesamteinkommen_ohne_abzüge_ohne_kapitaleinkünfte_y", + {"housing_benefits": {"eligibility": {"requirement_fulfilled_fam": None}}}, + date(2019, 12, 31), + "requirement_fulfilled_fam_not_considering_children", + "requirement_fulfilled_fam_considering_children", ), ], ) @@ -144,8 +133,12 @@ def test_load_functions_tree_for_date( function_name_last_day: str, function_name_next_day: str, ): - functions_last_day = load_functions_tree_for_date(date=last_day) - functions_next_day = load_functions_tree_for_date(date=last_day + timedelta(days=1)) + functions_last_day = load_objects_tree_for_date( + resource_dir=RESOURCE_DIR, date=last_day + ) + functions_next_day = load_objects_tree_for_date( + resource_dir=RESOURCE_DIR, date=last_day + timedelta(days=1) + ) accessor = optree.tree_accessors(tree, none_is_leaf=True)[0] @@ -160,7 +153,7 @@ def test_load_functions_tree_for_date( ], ) def test_fail_if_name_of_last_branch_element_not_leaf_name_of_function( - functions_tree: NestedFunctionDict, + functions_tree: NestedTTSIMObjectDict, ): with pytest.raises(KeyError): _fail_if_name_of_last_branch_element_not_leaf_name_of_function(functions_tree) @@ -171,4 +164,4 @@ def test_dont_destroy_group_by_functions(): "foo": group_by_function()(lambda: 1), } environment = PolicyEnvironment(functions_tree) - assert isinstance(environment.functions_tree["foo"], GroupByFunction) + assert isinstance(environment.raw_objects_tree["foo"], GroupByFunction) diff --git a/tests/ttsim/test_policy_function.py b/tests/ttsim/test_policy_function.py deleted file mode 100644 index 05d57d95ba..0000000000 --- a/tests/ttsim/test_policy_function.py +++ /dev/null @@ -1,35 +0,0 @@ -import pytest - -from ttsim.function_types import PolicyFunction, policy_function - - -@policy_function() -def simple_policy_function(x): - return x - - -@policy_function(leaf_name="simple_policy_function") -def policy_function_with_internal_name(x): - return x - - -@pytest.mark.parametrize( - "function", - [ - simple_policy_function, - policy_function_with_internal_name, - ], -) -def test_function_type(function): - assert isinstance(function, PolicyFunction) - - -@pytest.mark.parametrize( - "function", - [ - simple_policy_function, - policy_function_with_internal_name, - ], -) -def test_function_name(function): - assert function.leaf_name == "simple_policy_function" diff --git a/tests/ttsim/test_rounding.py b/tests/ttsim/test_rounding.py index 7aa0b6eb26..9546601cc3 100644 --- a/tests/ttsim/test_rounding.py +++ b/tests/ttsim/test_rounding.py @@ -5,57 +5,64 @@ from ttsim.compute_taxes_and_transfers import ( compute_taxes_and_transfers, ) -from ttsim.function_types import policy_function +from ttsim.function_types import policy_function, policy_input from ttsim.policy_environment import PolicyEnvironment -from ttsim.rounding import RoundingDirection, RoundingSpec +from ttsim.rounding import RoundingSpec + + +@policy_input() +def x() -> int: + pass + + +@policy_input() +def p_id() -> int: + pass + rounding_specs_and_exp_results = [ ( - RoundingSpec(base=1, direction=RoundingDirection.UP), + RoundingSpec(base=1, direction="up"), [100.24, 100.78], [101.0, 101.0], ), ( - RoundingSpec(base=1, direction=RoundingDirection.DOWN), + RoundingSpec(base=1, direction="down"), [100.24, 100.78], [100.0, 100.0], ), ( - RoundingSpec(base=1, direction=RoundingDirection.NEAREST), + RoundingSpec(base=1, direction="nearest"), [100.24, 100.78], [100.0, 101.0], ), ( - RoundingSpec(base=5, direction=RoundingDirection.UP), + RoundingSpec(base=5, direction="up"), [100.24, 100.78], [105.0, 105.0], ), ( - RoundingSpec(base=0.1, direction=RoundingDirection.DOWN), + RoundingSpec(base=0.1, direction="down"), [100.24, 100.78], [100.2, 100.7], ), ( - RoundingSpec(base=0.001, direction=RoundingDirection.NEAREST), + RoundingSpec(base=0.001, direction="nearest"), [100.24, 100.78], [100.24, 100.78], ), ( - RoundingSpec(base=1, direction=RoundingDirection.UP, to_add_after_rounding=10), + RoundingSpec(base=1, direction="up", to_add_after_rounding=10), [100.24, 100.78], [111.0, 111.0], ), ( - RoundingSpec( - base=1, direction=RoundingDirection.DOWN, to_add_after_rounding=10 - ), + RoundingSpec(base=1, direction="down", to_add_after_rounding=10), [100.24, 100.78], [110.0, 110.0], ), ( - RoundingSpec( - base=1, direction=RoundingDirection.NEAREST, to_add_after_rounding=10 - ), + RoundingSpec(base=1, direction="nearest", to_add_after_rounding=10), [100.24, 100.78], [110.0, 111.0], ), @@ -63,7 +70,7 @@ def test_decorator(): - rs = RoundingSpec(base=1, direction=RoundingDirection.UP) + rs = RoundingSpec(base=1, direction="up") @policy_function(rounding_spec=rs) def test_func(): @@ -91,20 +98,24 @@ def test_rounding(rounding_spec, input_values, exp_output): # Define function that should be rounded @policy_function(rounding_spec=rounding_spec) - def test_func(income): - return income + def test_func(x): + return x data = { "p_id": pd.Series([1, 2]), - "namespace": {"income": pd.Series(input_values)}, + "namespace": {"x": pd.Series(input_values)}, } - environment = PolicyEnvironment({"namespace": {"test_func": test_func}}) + environment = PolicyEnvironment( + {"namespace": {"test_func": test_func, "x": x}, "p_id": p_id} + ) calc_result = compute_taxes_and_transfers( data_tree=data, environment=environment, targets_tree={"namespace": {"test_func": None}}, + foreign_keys=((),), + supported_groupings=(), ) assert_series_equal( pd.Series(calc_result["namespace"]["test_func"]), @@ -117,23 +128,29 @@ def test_rounding_with_time_conversion(): """Check if rounding is correct for time-converted functions.""" # Define function that should be rounded - @policy_function( - rounding_spec=RoundingSpec(base=1, direction=RoundingDirection.DOWN) - ) - def test_func_m(income): - return income + @policy_function(rounding_spec=RoundingSpec(base=1, direction="down")) + def test_func_m(x): + return x data = { "p_id": pd.Series([1, 2]), - "income": pd.Series([1.2, 1.5]), + "x": pd.Series([1.2, 1.5]), } - environment = PolicyEnvironment({"test_func_m": test_func_m}) + environment = PolicyEnvironment( + { + "test_func_m": test_func_m, + "x": x, + "p_id": p_id, + } + ) calc_result = compute_taxes_and_transfers( data_tree=data, environment=environment, targets_tree={"test_func_y": None}, + foreign_keys=((),), + supported_groupings=(), ) assert_series_equal( pd.Series(calc_result["test_func_y"]), @@ -153,17 +170,25 @@ def test_no_rounding( ): # Define function that should be rounded @policy_function(rounding_spec=rounding_spec) - def test_func(income): - return income + def test_func(x): + return x data = {"p_id": pd.Series([1, 2])} - data["income"] = pd.Series(input_values_exp_output) - environment = PolicyEnvironment({"test_func": test_func}) + data["x"] = pd.Series(input_values_exp_output) + environment = PolicyEnvironment( + { + "test_func": test_func, + "x": x, + "p_id": p_id, + } + ) calc_result = compute_taxes_and_transfers( data_tree=data, environment=environment, targets_tree={"test_func": None}, + foreign_keys=((),), + supported_groupings=(), rounding=False, ) assert_series_equal( @@ -216,9 +241,9 @@ def test_func(income): "base, direction, to_add_after_rounding", [ (1, "upper", 0), - ("0.1", RoundingDirection.DOWN, 0), + ("0.1", "down", 0), (5, "closest", 0), - (5, RoundingDirection.UP, "0"), + (5, "up", "0"), ], ) def test_rounding_spec_validation(base, direction, to_add_after_rounding): diff --git a/tests/ttsim/test_shared.py b/tests/ttsim/test_shared.py index 68f4d1b97e..ece9066d44 100644 --- a/tests/ttsim/test_shared.py +++ b/tests/ttsim/test_shared.py @@ -3,8 +3,11 @@ import pytest from ttsim.shared import ( + all_variations_of_base_name, create_tree_from_path_and_value, get_name_of_group_by_id, + get_re_pattern_for_all_time_units_and_groupings, + get_re_pattern_for_specific_time_units_and_groupings, insert_path_and_value, merge_trees, partition_tree_by_reference_tree, @@ -228,6 +231,7 @@ def test_get_name_of_group_by_id(target_name, group_by_functions, expected): get_name_of_group_by_id( target_name=target_name, group_by_functions=group_by_functions, + supported_groupings=("hh", "bg", "eg"), ) == expected ) @@ -281,5 +285,125 @@ def test_get_name_of_group_by_id_fails( ): with pytest.raises(ValueError, match=expected_error_match): get_name_of_group_by_id( - target_name=target_name, group_by_functions=group_by_functions + target_name=target_name, + group_by_functions=group_by_functions, + supported_groupings=("hh", "bg", "eg"), + ) + + +@pytest.mark.parametrize( + ( + "base_name", + "supported_time_conversions", + "supported_groupings", + "create_conversions_for_time_units", + "expected", + ), + [ + ( + "income", + ["y", "m"], + ["hh"], + True, + {"income_m", "income_y", "income_m_hh", "income_y_hh"}, + ), + ( + "income", + ["y", "m"], + ["hh", "x"], + True, + { + "income_m", + "income_y", + "income_m_hh", + "income_y_hh", + "income_m_x", + "income_y_x", + }, + ), + ( + "claims_benefits", + ["y", "m"], + ["hh", "x"], + False, + {"claims_benefits", "claims_benefits_hh", "claims_benefits_x"}, + ), + ], +) +def test_all_variations_of_base_name( + base_name, + supported_time_conversions, + supported_groupings, + create_conversions_for_time_units, + expected, +): + assert ( + all_variations_of_base_name( + base_name=base_name, + supported_time_conversions=supported_time_conversions, + supported_groupings=supported_groupings, + create_conversions_for_time_units=create_conversions_for_time_units, ) + == expected + ) + + +@pytest.mark.parametrize( + ( + "func_name", + "supported_time_units", + "supported_groupings", + "expected_base_name", + "expected_time_unit", + "expected_aggregation", + ), + [ + ("foo", ("m", "y"), ["hh"], "foo", None, None), + ("foo_m_hh", ("m", "y"), ["hh"], "foo", "m", "hh"), + ("foo_y_hh", ("m", "y"), ["hh"], "foo", "y", "hh"), + ("foo_m", ("m", "y"), ["hh"], "foo", "m", None), + ("foo_y", ("m", "y"), ["hh"], "foo", "y", None), + ("foo_hh", ("m", "y"), ["hh"], "foo", None, "hh"), + ("foo_hh_bar", ("m", "y"), ["hh"], "foo_hh_bar", None, None), + ], +) +def test_get_re_pattern_for_time_units_and_groupings( + func_name, + supported_time_units, + supported_groupings, + expected_base_name, + expected_time_unit, + expected_aggregation, +): + result = get_re_pattern_for_all_time_units_and_groupings( + supported_time_units=supported_time_units, + supported_groupings=supported_groupings, + ) + match = result.fullmatch(func_name) + assert match.group("base_name") == expected_base_name + assert match.group("time_unit") == expected_time_unit + assert match.group("aggregation") == expected_aggregation + + +@pytest.mark.parametrize( + ( + "base_name", + "supported_time_units", + "supported_groupings", + "expected_match", + ), + [ + ("foo", ["m", "y"], ["hh"], "foo_m_hh"), + ("foo", ["m", "y"], ["hh", "x"], "foo_m"), + ("foo", ["m", "y"], ["hh", "x"], "foo_hh"), + ], +) +def test_get_re_pattern_for_some_base_name( + base_name, supported_time_units, supported_groupings, expected_match +): + re_pattern = get_re_pattern_for_specific_time_units_and_groupings( + base_name=base_name, + supported_time_units=supported_time_units, + supported_groupings=supported_groupings, + ) + assert re_pattern.fullmatch(expected_match) diff --git a/tests/ttsim/test_time_conversion.py b/tests/ttsim/test_time_conversion.py index 8ac0398148..1652de0736 100644 --- a/tests/ttsim/test_time_conversion.py +++ b/tests/ttsim/test_time_conversion.py @@ -280,34 +280,6 @@ def test_should_create_functions_for_other_time_units_for_functions( for expected_name in expected: assert expected_name in time_conversion_functions - @pytest.mark.parametrize( - ("name", "expected"), - [ - ("test_y", ["test_m", "test_q", "test_w", "test_d"]), - ("test_y_hh", ["test_m_hh", "test_q_hh", "test_w_hh", "test_d_hh"]), - ("test_y_sn", ["test_m_sn", "test_q_sn", "test_w_sn", "test_d_sn"]), - ("test_q", ["test_y", "test_m", "test_w", "test_d"]), - ("test_q_hh", ["test_y_hh", "test_m_hh", "test_w_hh", "test_d_hh"]), - ("test_q_sn", ["test_y_sn", "test_m_sn", "test_w_sn", "test_d_sn"]), - ("test_m", ["test_y", "test_q", "test_w", "test_d"]), - ("test_m_hh", ["test_y_hh", "test_q_hh", "test_w_hh", "test_d_hh"]), - ("test_m_sn", ["test_y_sn", "test_q_sn", "test_w_sn", "test_d_sn"]), - ("test_w", ["test_y", "test_q", "test_m", "test_d"]), - ("test_w_hh", ["test_y_hh", "test_q_hh", "test_m_hh", "test_d_hh"]), - ("test_w_sn", ["test_y_sn", "test_q_sn", "test_m_sn", "test_d_sn"]), - ("test_d", ["test_y", "test_q", "test_m", "test_w"]), - ("test_d_hh", ["test_y_hh", "test_q_hh", "test_m_hh", "test_w_hh"]), - ("test_d_sn", ["test_y_sn", "test_q_sn", "test_m_sn", "test_w_sn"]), - ], - ) - def test_should_create_functions_for_other_time_units_for_data_cols( - self, name: str, expected: list[str] - ) -> None: - time_conversion_functions = create_time_conversion_functions({}, {name: None}) - - for expected_name in expected: - assert expected_name in time_conversion_functions - def test_should_not_create_functions_automatically_that_exist_already(self) -> None: time_conversion_functions = create_time_conversion_functions( {"test1_d": policy_function(leaf_name="test1_d")(lambda: 1)}, diff --git a/tests/ttsim/test_vectorization.py b/tests/ttsim/test_vectorization.py index fa83a782c7..298f361183 100644 --- a/tests/ttsim/test_vectorization.py +++ b/tests/ttsim/test_vectorization.py @@ -1,6 +1,7 @@ import datetime import inspect import string +from pathlib import Path import dags.tree as dt import numpy @@ -12,8 +13,8 @@ import jax.numpy from numpy.testing import assert_array_equal -from ttsim.function_types import GroupByFunction -from ttsim.loader import load_functions_tree_for_date +from ttsim.function_types import GroupByFunction, PolicyInput +from ttsim.loader import load_objects_tree_for_date from ttsim.vectorization import ( TranslateToVectorizableError, make_vectorizable, @@ -369,15 +370,17 @@ def test_unallowed_operation_wrapper(func): # https://github.com/iza-institute-of-labor-economics/gettsim/issues/515 for year in range(1990, 2023): - @pytest.mark.skip(reason="@Tim:Need to take care of RoundingDirection issue first.") @pytest.mark.parametrize( "func", [ pf.function for pf in dt.flatten_to_tree_paths( - load_functions_tree_for_date(datetime.date(year=year, month=1, day=1)) + load_objects_tree_for_date( + resource_dir=Path(__file__).parent / "mettsim", + date=datetime.date(year=year, month=1, day=1), + ) ).values() - if not isinstance(pf, GroupByFunction) + if not isinstance(pf, GroupByFunction | PolicyInput) ], ) @pytest.mark.parametrize("backend", backends) @@ -451,7 +454,7 @@ def test_geschwisterbonus_m(backend): assert_array_equal(got, full(shape, exp)) -def mock__elterngeld__grundsätzlich_anspruchsberechtigt( # noqa: PLR0913 +def mock__elterngeld__grundsätzlich_anspruchsberechtigt( claimed: bool, arbeitsstunden_w: float, kind_grundsätzlich_anspruchsberechtigt_fg: bool, diff --git a/tests/ttsim/test_visualizations.py b/tests/ttsim/test_visualizations.py index 00b95135ae..2bca827fc3 100644 --- a/tests/ttsim/test_visualizations.py +++ b/tests/ttsim/test_visualizations.py @@ -1,8 +1,8 @@ import networkx as nx import pytest -from _helpers import cached_set_up_policy_environment +from mettsim.config import RESOURCE_DIR -from ttsim.policy_environment import PolicyEnvironment +from ttsim.policy_environment import PolicyEnvironment, set_up_policy_environment from ttsim.visualization import ( _get_selected_nodes, _kth_order_neighbors, @@ -12,7 +12,7 @@ plot_dag, ) -environment = cached_set_up_policy_environment(date=2020) +environment = set_up_policy_environment(date=2020, resource_dir=RESOURCE_DIR) @pytest.mark.xfail(reason="Visualization module was not updated to the new interface.") diff --git a/src/_gettsim_tests/_policy_test_utils.py b/tests/ttsim/utils.py similarity index 91% rename from src/_gettsim_tests/_policy_test_utils.py rename to tests/ttsim/utils.py index d9653427e9..c6fb7813de 100644 --- a/src/_gettsim_tests/_policy_test_utils.py +++ b/tests/ttsim/utils.py @@ -1,18 +1,19 @@ from __future__ import annotations import datetime +from pathlib import Path from typing import TYPE_CHECKING import dags.tree as dt import pandas as pd import yaml +from mettsim.config import FOREIGN_KEYS, SUPPORTED_GROUPINGS -from _gettsim_tests import TEST_DIR -from ttsim import merge_trees +from ttsim import merge_trees, set_up_policy_environment -if TYPE_CHECKING: - from pathlib import Path +TEST_DIR = Path(__file__).parent / "test_data" +if TYPE_CHECKING: from ttsim import NestedDataDict, NestedInputStructureDict @@ -48,15 +49,16 @@ def test_name(self) -> str: def execute_test(test: PolicyTest): from pandas.testing import assert_frame_equal - from _gettsim_tests._helpers import cached_set_up_policy_environment from ttsim import compute_taxes_and_transfers - environment = cached_set_up_policy_environment(date=test.date) + environment = set_up_policy_environment(date=test.date) result = compute_taxes_and_transfers( data_tree=test.input_tree, environment=environment, targets_tree=test.target_structure, + supported_groupings=SUPPORTED_GROUPINGS, + foreign_keys=FOREIGN_KEYS, ) flat_result = dt.flatten_to_qual_names(result) @@ -88,11 +90,6 @@ def load_policy_test_data(policy_name: str) -> list[PolicyTest]: with path_to_yaml.open("r", encoding="utf-8") as file: raw_test_data: NestedDataDict = yaml.safe_load(file) - # TODO(@MImmesberger): Remove this before merging this PR. - raw_test_data["inputs"], raw_test_data["outputs"] = get_test_data_as_tree( - raw_test_data - ) - out.extend( _get_policy_tests_from_raw_test_data( raw_test_data=raw_test_data, @@ -165,7 +162,7 @@ def _get_policy_tests_from_raw_test_data( } ) - date: datetime.date = _parse_date(path_to_yaml.parent.name) + date: datetime.date = _parse_date_from_dir_name(path_to_yaml.parent.name) out = [] if expected_output_tree == {}: @@ -198,7 +195,7 @@ def _get_policy_tests_from_raw_test_data( return out -def _parse_date(date: str) -> datetime.date: +def _parse_date_from_dir_name(date: str) -> datetime.date: parts = date.split("-") if len(parts) == 1: