policyengine-uk 2.45.4__py3-none-any.whl → 2.47.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of policyengine-uk might be problematic. Click here for more details.
- policyengine_uk/__init__.py +1 -0
- policyengine_uk/data/dataset_schema.py +6 -3
- policyengine_uk/data/economic_assumptions.py +1 -1
- policyengine_uk/dynamics/labour_supply.py +306 -0
- policyengine_uk/dynamics/participation.py +629 -0
- policyengine_uk/dynamics/progression.py +376 -0
- policyengine_uk/microsimulation.py +23 -1
- policyengine_uk/parameters/gov/dynamic/obr_labour_supply_assumptions.yaml +9 -0
- policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml +270 -32
- policyengine_uk/simulation.py +184 -9
- policyengine_uk/tax_benefit_system.py +4 -1
- policyengine_uk/tests/microsimulation/reforms_config.yaml +7 -7
- policyengine_uk/tests/microsimulation/test_validity.py +2 -3
- policyengine_uk/tests/microsimulation/update_reform_impacts.py +104 -40
- policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement.yaml +8 -8
- policyengine_uk/utils/__init__.py +1 -0
- policyengine_uk/utils/compare.py +28 -0
- policyengine_uk/utils/scenario.py +37 -2
- policyengine_uk/utils/solve_private_school_attendance_factor.py +4 -6
- policyengine_uk/variables/gov/dwp/additional_state_pension.py +1 -1
- policyengine_uk/variables/gov/dwp/basic_state_pension.py +1 -1
- policyengine_uk/variables/gov/gov_tax.py +0 -1
- policyengine_uk/variables/household/demographic/benunit/benunit_count_adults.py +11 -0
- policyengine_uk/variables/input/consumption/property/council_tax.py +0 -35
- policyengine_uk/variables/input/rent.py +0 -40
- {policyengine_uk-2.45.4.dist-info → policyengine_uk-2.47.3.dist-info}/METADATA +5 -4
- {policyengine_uk-2.45.4.dist-info → policyengine_uk-2.47.3.dist-info}/RECORD +29 -23
- {policyengine_uk-2.45.4.dist-info → policyengine_uk-2.47.3.dist-info}/WHEEL +0 -0
- {policyengine_uk-2.45.4.dist-info → policyengine_uk-2.47.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,6 +8,18 @@ from pathlib import Path
|
|
|
8
8
|
from policyengine_uk import Microsimulation
|
|
9
9
|
import argparse
|
|
10
10
|
from datetime import datetime
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
from rich.progress import (
|
|
14
|
+
Progress,
|
|
15
|
+
SpinnerColumn,
|
|
16
|
+
TextColumn,
|
|
17
|
+
BarColumn,
|
|
18
|
+
TaskProgressColumn,
|
|
19
|
+
)
|
|
20
|
+
from rich.panel import Panel
|
|
21
|
+
from rich import print as rprint
|
|
22
|
+
import traceback
|
|
11
23
|
|
|
12
24
|
baseline = Microsimulation()
|
|
13
25
|
|
|
@@ -40,57 +52,97 @@ def update_impacts(
|
|
|
40
52
|
dry_run: If True, show changes without writing to file
|
|
41
53
|
verbose: If True, show detailed output
|
|
42
54
|
"""
|
|
55
|
+
console = Console()
|
|
56
|
+
|
|
43
57
|
# Load current configuration
|
|
44
58
|
with open(config_path, "r") as f:
|
|
45
59
|
config = yaml.safe_load(f)
|
|
46
60
|
|
|
47
61
|
if verbose:
|
|
48
|
-
print(
|
|
49
|
-
|
|
62
|
+
console.print(
|
|
63
|
+
Panel.fit(
|
|
64
|
+
f"[bold cyan]Loaded configuration from {config_path}[/bold cyan]\n"
|
|
65
|
+
f"[green]Found {len(config['reforms'])} reforms to update[/green]",
|
|
66
|
+
title="Configuration loaded",
|
|
67
|
+
)
|
|
68
|
+
)
|
|
50
69
|
|
|
51
70
|
# Track changes
|
|
52
71
|
changes = []
|
|
53
72
|
|
|
54
|
-
# Update each reform's expected impact
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"name": reform["name"],
|
|
66
|
-
"old": old_impact,
|
|
67
|
-
"new": new_impact,
|
|
68
|
-
"diff": new_impact - old_impact,
|
|
69
|
-
}
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
reform["expected_impact"] = new_impact
|
|
73
|
+
# Update each reform's expected impact with progress bar
|
|
74
|
+
with Progress(
|
|
75
|
+
SpinnerColumn(),
|
|
76
|
+
TextColumn("[progress.description]{task.description}"),
|
|
77
|
+
BarColumn(),
|
|
78
|
+
TaskProgressColumn(),
|
|
79
|
+
console=console,
|
|
80
|
+
) as progress:
|
|
81
|
+
task = progress.add_task(
|
|
82
|
+
"[cyan]Processing reforms...", total=len(config["reforms"])
|
|
83
|
+
)
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
for reform in config["reforms"]:
|
|
86
|
+
progress.update(
|
|
87
|
+
task, description=f"[cyan]Processing: {reform['name'][:40]}..."
|
|
88
|
+
)
|
|
89
|
+
old_impact = reform["expected_impact"]
|
|
90
|
+
new_impact = round(get_fiscal_impact(reform["parameters"]), 1)
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
abs(old_impact - new_impact) > 0.01
|
|
94
|
+
): # Only record meaningful changes
|
|
95
|
+
changes.append(
|
|
96
|
+
{
|
|
97
|
+
"name": reform["name"],
|
|
98
|
+
"old": old_impact,
|
|
99
|
+
"new": new_impact,
|
|
100
|
+
"diff": new_impact - old_impact,
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
reform["expected_impact"] = new_impact
|
|
105
|
+
progress.advance(task)
|
|
106
|
+
|
|
107
|
+
# Show detailed output if verbose
|
|
108
|
+
if verbose and changes:
|
|
109
|
+
console.print("\n[bold]Detailed changes:[/bold]")
|
|
110
|
+
for change in changes:
|
|
111
|
+
color = "red" if change["diff"] < 0 else "green"
|
|
112
|
+
console.print(
|
|
113
|
+
f" [yellow]{change['name']}[/yellow]\n"
|
|
114
|
+
f" Old impact: [dim]{change['old']:.1f} billion[/dim]\n"
|
|
115
|
+
f" New impact: [bold]{change['new']:.1f} billion[/bold]\n"
|
|
116
|
+
f" Change: [{color}]{change['diff']:+.1f} billion[/{color}]\n"
|
|
117
|
+
)
|
|
81
118
|
|
|
82
119
|
# Show summary of changes
|
|
83
120
|
if changes:
|
|
84
|
-
|
|
85
|
-
|
|
121
|
+
table = Table(
|
|
122
|
+
title="Summary of changes",
|
|
123
|
+
show_header=True,
|
|
124
|
+
header_style="bold magenta",
|
|
125
|
+
)
|
|
126
|
+
table.add_column("Reform", style="cyan", no_wrap=False)
|
|
127
|
+
table.add_column("Old impact (£bn)", justify="right")
|
|
128
|
+
table.add_column("New impact (£bn)", justify="right")
|
|
129
|
+
table.add_column("Change (£bn)", justify="right")
|
|
130
|
+
|
|
86
131
|
for change in changes:
|
|
87
|
-
|
|
88
|
-
|
|
132
|
+
color = "red" if change["diff"] < 0 else "green"
|
|
133
|
+
table.add_row(
|
|
134
|
+
change["name"],
|
|
135
|
+
f"{change['old']:.1f}",
|
|
136
|
+
f"{change['new']:.1f}",
|
|
137
|
+
f"[{color}]{change['diff']:+.1f}[/{color}]",
|
|
89
138
|
)
|
|
90
|
-
|
|
91
|
-
print(
|
|
139
|
+
|
|
140
|
+
console.print("\n", table)
|
|
141
|
+
console.print(
|
|
142
|
+
f"\n[bold cyan]Total changes: {len(changes)}[/bold cyan]"
|
|
143
|
+
)
|
|
92
144
|
else:
|
|
93
|
-
print("\
|
|
145
|
+
console.print("\n[green]✓ No significant changes detected.[/green]")
|
|
94
146
|
|
|
95
147
|
# Write updated configuration
|
|
96
148
|
if not dry_run:
|
|
@@ -109,13 +161,22 @@ def update_impacts(
|
|
|
109
161
|
with open(config_path, "w") as f:
|
|
110
162
|
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
111
163
|
|
|
112
|
-
print(
|
|
113
|
-
|
|
164
|
+
console.print(
|
|
165
|
+
Panel.fit(
|
|
166
|
+
f"[green]✓ Configuration updated successfully![/green]\n"
|
|
167
|
+
f"[dim]Backup saved to: {backup_path}[/dim]",
|
|
168
|
+
title="Success",
|
|
169
|
+
border_style="green",
|
|
170
|
+
)
|
|
171
|
+
)
|
|
114
172
|
else:
|
|
115
|
-
print(
|
|
173
|
+
console.print(
|
|
174
|
+
"\n[yellow]⚠ Dry run - no changes written to file.[/yellow]"
|
|
175
|
+
)
|
|
116
176
|
|
|
117
177
|
|
|
118
178
|
def main():
|
|
179
|
+
console = Console()
|
|
119
180
|
parser = argparse.ArgumentParser(
|
|
120
181
|
description="Update reform impact expectations with current model values"
|
|
121
182
|
)
|
|
@@ -143,14 +204,17 @@ def main():
|
|
|
143
204
|
args = parser.parse_args()
|
|
144
205
|
|
|
145
206
|
if not args.config.exists():
|
|
146
|
-
print(
|
|
207
|
+
console.print(
|
|
208
|
+
f"[bold red]Error:[/bold red] Configuration file '{args.config}' not found!"
|
|
209
|
+
)
|
|
147
210
|
return 1
|
|
148
211
|
|
|
149
212
|
try:
|
|
150
213
|
update_impacts(args.config, dry_run=args.dry_run, verbose=args.verbose)
|
|
151
214
|
return 0
|
|
152
215
|
except Exception as e:
|
|
153
|
-
print(f"Error updating impacts: {e}")
|
|
216
|
+
console.print(f"[bold red]Error updating impacts:[/bold red] {e}")
|
|
217
|
+
console.print(traceback.format_exc())
|
|
154
218
|
return 1
|
|
155
219
|
|
|
156
220
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
- name: Eligible for 30 hours - All conditions met
|
|
2
2
|
period: 2025
|
|
3
|
-
absolute_error_margin:
|
|
3
|
+
absolute_error_margin: 3
|
|
4
4
|
input:
|
|
5
5
|
people:
|
|
6
6
|
child1:
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
- name: Eligible for 15 hours - All first conditions met
|
|
16
16
|
period: 2025
|
|
17
|
-
absolute_error_margin:
|
|
17
|
+
absolute_error_margin: 2
|
|
18
18
|
input:
|
|
19
19
|
people:
|
|
20
20
|
child1:
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
- name: Eligible for mixed hours - Family with multiple children
|
|
44
44
|
period: 2025
|
|
45
|
-
absolute_error_margin:
|
|
45
|
+
absolute_error_margin: 4
|
|
46
46
|
input:
|
|
47
47
|
people:
|
|
48
48
|
child1:
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
|
|
75
75
|
- name: Eligible with one working parent and one disabled parent
|
|
76
76
|
period: 2025
|
|
77
|
-
absolute_error_margin:
|
|
77
|
+
absolute_error_margin: 6
|
|
78
78
|
input:
|
|
79
79
|
people:
|
|
80
80
|
child1:
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
|
|
108
108
|
- name: Child using fewer hours than maximum entitlement
|
|
109
109
|
period: 2025
|
|
110
|
-
absolute_error_margin:
|
|
110
|
+
absolute_error_margin: 2
|
|
111
111
|
input:
|
|
112
112
|
people:
|
|
113
113
|
child1:
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
|
|
123
123
|
- name: Child using fewer hours than maximum entitlement - multiple children
|
|
124
124
|
period: 2025
|
|
125
|
-
absolute_error_margin:
|
|
125
|
+
absolute_error_margin: 2
|
|
126
126
|
input:
|
|
127
127
|
people:
|
|
128
128
|
child1:
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
|
|
141
141
|
- name: Benefit unit maximum hours cap applied
|
|
142
142
|
period: 2025
|
|
143
|
-
absolute_error_margin:
|
|
143
|
+
absolute_error_margin: 5
|
|
144
144
|
input:
|
|
145
145
|
people:
|
|
146
146
|
child1:
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
|
|
178
178
|
- name: Benefit unit without maximum hours cap applied with 3 years old child
|
|
179
179
|
period: 2025
|
|
180
|
-
absolute_error_margin:
|
|
180
|
+
absolute_error_margin: 2
|
|
181
181
|
input:
|
|
182
182
|
people:
|
|
183
183
|
child1:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .compare import compare_simulations
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from policyengine_uk.simulation import Microsimulation
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def compare_simulations(
|
|
9
|
+
simulations: list["Microsimulation"],
|
|
10
|
+
names: list[str],
|
|
11
|
+
year: int,
|
|
12
|
+
variables: list[str],
|
|
13
|
+
):
|
|
14
|
+
dfs = [
|
|
15
|
+
sim.calculate_dataframe(variables, year).rename(
|
|
16
|
+
columns=lambda x: f"{x}_{name}"
|
|
17
|
+
)
|
|
18
|
+
for sim, name in zip(simulations, names)
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
df = pd.concat(dfs, axis=1).reset_index().rename(columns={"index": "id"})
|
|
22
|
+
|
|
23
|
+
# Sort columns by variable
|
|
24
|
+
columns = []
|
|
25
|
+
for var in variables:
|
|
26
|
+
columns.extend([col for col in df.columns if col.startswith(var)])
|
|
27
|
+
|
|
28
|
+
return df[columns]
|
|
@@ -2,6 +2,7 @@ from pydantic import BaseModel
|
|
|
2
2
|
from typing import Optional, Callable, Dict, Type, Union
|
|
3
3
|
from policyengine_core.simulations import Simulation
|
|
4
4
|
from policyengine_core.reforms import Reform
|
|
5
|
+
from policyengine_core.periods import period, instant
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class Scenario(BaseModel):
|
|
@@ -111,8 +112,42 @@ class Scenario(BaseModel):
|
|
|
111
112
|
|
|
112
113
|
elif isinstance(reform, dict):
|
|
113
114
|
# Dictionary of parameter changes
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
# Make sure to capture YYYY-MM-DD.YYYY-MM-DD.
|
|
116
|
+
|
|
117
|
+
def modifier(sim: Simulation):
|
|
118
|
+
for parameter in reform:
|
|
119
|
+
if isinstance(reform[parameter], dict):
|
|
120
|
+
for period_str, value in reform[parameter].items():
|
|
121
|
+
if "." in period_str:
|
|
122
|
+
start = instant(period_str.split(".")[0])
|
|
123
|
+
stop = instant(period_str.split(".")[1])
|
|
124
|
+
period_ = None
|
|
125
|
+
else:
|
|
126
|
+
period_ = period(period_str)
|
|
127
|
+
sim.tax_benefit_system.parameters.get_child(
|
|
128
|
+
parameter
|
|
129
|
+
).update(
|
|
130
|
+
start=start,
|
|
131
|
+
stop=stop,
|
|
132
|
+
period=period_,
|
|
133
|
+
value=value,
|
|
134
|
+
)
|
|
135
|
+
else:
|
|
136
|
+
start = instant("2023-01-01")
|
|
137
|
+
stop = None
|
|
138
|
+
period_ = None
|
|
139
|
+
|
|
140
|
+
sim.tax_benefit_system.parameters.get_child(
|
|
141
|
+
parameter
|
|
142
|
+
).update(
|
|
143
|
+
start=start,
|
|
144
|
+
stop=stop,
|
|
145
|
+
period=period_,
|
|
146
|
+
value=reform[parameter],
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return Scenario(
|
|
150
|
+
simulation_modifier=modifier,
|
|
116
151
|
)
|
|
117
152
|
|
|
118
153
|
elif isinstance(reform, tuple):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from
|
|
1
|
+
from policyengine_uk import Microsimulation
|
|
2
2
|
from policyengine_core.reforms import Reform
|
|
3
3
|
from tqdm import tqdm
|
|
4
4
|
|
|
@@ -19,11 +19,9 @@ for factor in tqdm([round(x * 0.01, 2) for x in range(70, 91)]):
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
# Run the reformed microsimulation
|
|
22
|
-
reformed =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
country="uk",
|
|
26
|
-
).baseline_simulation
|
|
22
|
+
reformed = Microsimulation(
|
|
23
|
+
reform=reform,
|
|
24
|
+
)
|
|
27
25
|
reformed.baseline_simulation.get_holder(
|
|
28
26
|
"attends_private_school"
|
|
29
27
|
).delete_arrays()
|
|
@@ -13,7 +13,7 @@ class additional_state_pension(Variable):
|
|
|
13
13
|
if simulation.dataset is None:
|
|
14
14
|
return 0
|
|
15
15
|
|
|
16
|
-
data_year =
|
|
16
|
+
data_year = 2023
|
|
17
17
|
reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR
|
|
18
18
|
type = person("state_pension_type", data_year)
|
|
19
19
|
maximum_basic_sp = parameters(
|
|
@@ -13,7 +13,7 @@ class basic_state_pension(Variable):
|
|
|
13
13
|
if simulation.dataset is None:
|
|
14
14
|
return 0
|
|
15
15
|
|
|
16
|
-
data_year =
|
|
16
|
+
data_year = 2023
|
|
17
17
|
reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR
|
|
18
18
|
type = person("state_pension_type", period)
|
|
19
19
|
maximum_basic_sp = parameters(
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from policyengine_uk.model_api import *
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class benunit_count_adults(Variable):
|
|
5
|
+
value_type = int
|
|
6
|
+
entity = BenUnit
|
|
7
|
+
label = "number of adults in the benefit unit"
|
|
8
|
+
definition_period = YEAR
|
|
9
|
+
|
|
10
|
+
def formula(benunit, period, parameters):
|
|
11
|
+
return add(benunit, period, ["is_adult"])
|
|
@@ -9,38 +9,3 @@ class council_tax(Variable):
|
|
|
9
9
|
definition_period = YEAR
|
|
10
10
|
unit = GBP
|
|
11
11
|
quantity_type = FLOW
|
|
12
|
-
|
|
13
|
-
def formula(household, period, parameters):
|
|
14
|
-
if period.start.year < 2023:
|
|
15
|
-
# We don't have growth rates for council tax by nation before this.
|
|
16
|
-
return 0
|
|
17
|
-
|
|
18
|
-
if household.simulation.dataset is None:
|
|
19
|
-
return 0
|
|
20
|
-
|
|
21
|
-
data_year = household.simulation.dataset.time_period
|
|
22
|
-
|
|
23
|
-
original_ct = household("council_tax", data_year)
|
|
24
|
-
|
|
25
|
-
ct = parameters.gov.economic_assumptions.indices.obr.council_tax
|
|
26
|
-
|
|
27
|
-
def get_growth(country):
|
|
28
|
-
param = getattr(ct, country)
|
|
29
|
-
return param(period.start.year) / param(data_year)
|
|
30
|
-
|
|
31
|
-
country = household("country", period).decode_to_str()
|
|
32
|
-
|
|
33
|
-
return select(
|
|
34
|
-
[
|
|
35
|
-
country == "ENGLAND",
|
|
36
|
-
country == "WALES",
|
|
37
|
-
country == "SCOTLAND",
|
|
38
|
-
True,
|
|
39
|
-
],
|
|
40
|
-
[
|
|
41
|
-
original_ct * get_growth("england"),
|
|
42
|
-
original_ct * get_growth("wales"),
|
|
43
|
-
original_ct * get_growth("scotland"),
|
|
44
|
-
original_ct,
|
|
45
|
-
],
|
|
46
|
-
)
|
|
@@ -11,43 +11,3 @@ class rent(Variable):
|
|
|
11
11
|
value_type = float
|
|
12
12
|
unit = GBP
|
|
13
13
|
quantity_type = FLOW
|
|
14
|
-
|
|
15
|
-
def formula(household, period, parameters):
|
|
16
|
-
if period.start.year < 2023:
|
|
17
|
-
# We don't have growth rates for rent before this.
|
|
18
|
-
return 0
|
|
19
|
-
|
|
20
|
-
if household.simulation.dataset is None:
|
|
21
|
-
return 0
|
|
22
|
-
|
|
23
|
-
data_year = household.simulation.dataset.time_period
|
|
24
|
-
original_rent = household("rent", data_year)
|
|
25
|
-
tenure_type = household("tenure_type", period).decode_to_str()
|
|
26
|
-
|
|
27
|
-
is_social_rent = (tenure_type == "RENT_FROM_COUNCIL") | (
|
|
28
|
-
tenure_type == "RENT_FROM_HA"
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
is_private_rent = tenure_type == "RENT_PRIVATELY"
|
|
32
|
-
|
|
33
|
-
obr = parameters.gov.economic_assumptions.indices.obr
|
|
34
|
-
|
|
35
|
-
private_rent_uprating = obr.lagged_average_earnings(
|
|
36
|
-
period
|
|
37
|
-
) / obr.lagged_average_earnings(data_year)
|
|
38
|
-
social_rent_uprating = obr.social_rent(period) / obr.social_rent(
|
|
39
|
-
data_year
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
return select(
|
|
43
|
-
[
|
|
44
|
-
is_social_rent,
|
|
45
|
-
is_private_rent,
|
|
46
|
-
True,
|
|
47
|
-
],
|
|
48
|
-
[
|
|
49
|
-
original_rent * social_rent_uprating,
|
|
50
|
-
original_rent * private_rent_uprating,
|
|
51
|
-
original_rent,
|
|
52
|
-
],
|
|
53
|
-
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: policyengine-uk
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: PolicyEngine tax and benefit system for the UK
|
|
3
|
+
Version: 2.47.3
|
|
4
|
+
Summary: PolicyEngine tax and benefit system for the UK.
|
|
5
5
|
Project-URL: Homepage, https://github.com/PolicyEngine/policyengine-uk
|
|
6
6
|
Project-URL: Repository, https://github.com/PolicyEngine/policyengine-uk
|
|
7
7
|
Project-URL: Issues, https://github.com/PolicyEngine/policyengine-uk/issues
|
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
23
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.13
|
|
24
24
|
Requires-Dist: microdf-python>=1.0.2
|
|
25
25
|
Requires-Dist: policyengine-core>=3.19.3
|
|
26
26
|
Requires-Dist: pydantic>=2.11.7
|
|
@@ -28,10 +28,11 @@ Provides-Extra: dev
|
|
|
28
28
|
Requires-Dist: black; extra == 'dev'
|
|
29
29
|
Requires-Dist: coverage; extra == 'dev'
|
|
30
30
|
Requires-Dist: furo<2023; extra == 'dev'
|
|
31
|
-
Requires-Dist: jupyter-book; extra == 'dev'
|
|
31
|
+
Requires-Dist: jupyter-book>=2.0.0a0; extra == 'dev'
|
|
32
32
|
Requires-Dist: linecheck; extra == 'dev'
|
|
33
33
|
Requires-Dist: pytest; extra == 'dev'
|
|
34
34
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
35
|
+
Requires-Dist: rich; extra == 'dev'
|
|
35
36
|
Requires-Dist: setuptools; extra == 'dev'
|
|
36
37
|
Requires-Dist: snowballstemmer<3,>=2; extra == 'dev'
|
|
37
38
|
Requires-Dist: sphinx-argparse<1,>=0.3.2; extra == 'dev'
|