Repository navigation
Conversation
…failing)
A budget-window report's calculation is a budget_window_runs row and one
report run per year. GET /v2/reports/{id} and the report listing should
answer the newest window run with its years, and, once that run has
succeeded, the window's result in today's budget-window format, built from
each year's budget rows. The listing test now expects budget windows
beside economy reports.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /v2/reports/{id} now reads a budget window: the report row whose
budget_window_size is set, its newest budget_window_runs row (created
last, ties going to the larger ID) and that run's report runs, one per
year. Once the window run has succeeded, the result is today's
budget-window format: each year's budget figures, read from its
budget_summary rows as an economy result's budget is, with its SPM
receipts, and the totals summed as exact decimals. A succeeded window
whose years cannot give the result answers 500. Reading the definition
and runs never reads the figures.
The listing answers budget windows beside economy reports, with their
newest runs and years, in two queries; household reports are still left
out. Both kinds are told apart by kind in the OpenAPI document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ailing) POST /v2/reports should take a budget window as kind budget_window, with the first year and budget_window_size (1 to 75 years, ending by 2200), and no include_cliffs. It is checked as an economy report is, then sent to the simulation service's POST /reports as report_type budget_window, whose created answer names the report and its budget-window run, and is read back from the tables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /v2/reports takes kind budget_window beside kind economy, told apart by kind in the OpenAPI document. A budget window names its first year and budget_window_size (1 to 75 years, ending in 2200 or earlier) and takes no include_cliffs, since cliff impacts are not calculated for a window. It is checked as an economy report is (economy calculations in the country, each policy stored with every parameter it sets), then sent to the simulation service's POST /reports as report_type budget_window. Its created answer names the report and its budget-window run; refusals and failures are answered as a report's. The window is then read back from the tables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing) A detailed-save report stores its own window in budget_window_size, so a saved report naming one should give the same budget_window_years, or none for a report without a window of two or more years, on create and on update; a disagreeing one is refused as contradicting references are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A saved report naming a detailed-save report now gives that report's own budget window as budget_window_years, read from budget_window_size in the write's transaction: the same number for a window of two or more years, and none otherwise, since a saved report stores a one-year window as none. A disagreeing create or update is refused with 400, as contradicting references are. An update naming neither the report nor the window is not checked, so older records can still be renamed. A v1 saved report keeps its own window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The report step runs the budget-window read and creation tests, and the saved-record step the saved budget-window tests, against the job's disposable PostgreSQL as the managed roles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rules Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed answers (failing) Review round 1's test findings: a succeeded window whose years stored no result rows is still listed and saved, since neither reads its figures; the listing reads any number of windows in two queries; a saved report whose window disagrees with its report answers 400 through the route. The two failing tests expect the OpenAPI document to describe a window's year as its first year, and the saved-report 400 to name the window disagreement. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Economy reports and budget windows share ReportDefinitionFields, so their common fields and the spm check are defined once; a window's year is described as its first year in the OpenAPI document. - The report client builds both kinds' bodies from one definition, and reads every created answer in one place; its answers are unchanged. - Both summary reads share ReportDefinitionRead. - A window year's status is a ReportRunStatus, and the totals are summed through typed figure getters rather than field names. - The saved-record 400 text names the budget window disagreement, and the docstrings say that a window's years each name their own simulation runs, that a window run can be running, and that the report service creates windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…window reports Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2: the shared user-route 400 text named budget_window_years on every user route, though only POST and PATCH /v2/user-reports can refuse a saved report's window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…simulations - The shared user-route 400 text is as before. POST and PATCH /v2/user-reports, the only routes that can refuse a saved report's budget window, carry a 400 entry of their own that names it, and the create's description says what the window must be. - The SimulationRun docstring and the alembic-migrations skill no longer say a budget window compares the report's same two simulations in every year: the simulation service gives each year simulations of that year, or reuses succeeded runs, and the report's own simulations are its first year's. ReportDefinitionFields says the same. - The integration helpers store each later window year under simulations of its own year, as the simulation service does, and the window read test checks each year names its own simulation runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The integration helpers' store_year_simulations, named for the rows it stores, compares a year with the stored report's own year; store_report takes the year, and the helpers' 2026 defaults come from one constant. The creation stand-in stores the window's submitted first year. - The window read test's comment says what its assertion checks. - The saved-report write 400 builds on the shared user-route text, and the create's description says what a request must give. - The SimulationRun docstring opens with what a run calculates. No behavior changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## v2plan/c1-11-refuse-missing-parameters #4290 +/- ##
==========================================================================
+ Coverage 93.12% 93.17% +0.05%
==========================================================================
Files 357 360 +3
Lines 20480 20762 +282
Branches 3302 3343 +41
==========================================================================
+ Hits 19072 19345 +273
- Misses 838 840 +2
- Partials 570 577 +7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4289
Summary
Budget windows in the detailed-save API's
/v2/reports. A budget window compares a baseline and a reform simulation in each year of a window of 1 to 75 consecutive years, ending in 2200 or earlier. Its row hasreports.budget_window_sizeandreports.year(the first year). Each calculation of it is abudget_window_runsrow, and each year of that calculation areport_runsrow naming the window run and its year.Stacked on #4286, which stacks on #4281. The base is
v2plan/c1-11-refuse-missing-parameters; retarget as the bases merge.GET /v2/reports/{report_id}): a budget window answerskindbudget_window, its window (year,budget_window_size) and its newest budget-window run (created last, ties going to the larger ID). The run has its status, trigger,recalculate, versions and times, and itsyearsin year order, each a report run with its own status, versions and error. Only once that run has succeeded does the read buildresult, in today's budget-window format. Each year's figures come from itsbudget_summaryrows, read as an economy result'sbudgetis, with that year's SPM receipts. The totals are summed as exact decimals, as the old simulation gateway'sbudget_window_results.pysums them. A succeeded window with a year missing, not succeeded or without budget figures answers 500, "The report's stored result cannot be read".GET /v2/reports?report_id=…): budget windows are listed beside economy reports, with their newest runs and years but no results, in two queries. Household reports are still left out.POST /v2/reports): the body is told apart bykind. A budget window takes the firstyearandbudget_window_size, and noinclude_cliffs, since cliff impacts are not calculated for a window. It is checked as an economy report is: the country has economy calculations, and each named policy is stored with every parameter it sets. It is then sent to the simulation service'sPOST /reportsasreport_typebudget_window. The created answer'sreport_idandbudget_window_run_idare read, and the window is read back from the tables. Refusals and failures are answered as an economy report's: the same 422, 502 and 503 rules, including thereport_idof a window the service created and then failed./v2/user-reports): a saved report naming a detailed-save report must give that report's own window asbudget_window_years: the same number for a window of two or more years, and null otherwise, since a saved report stores a one-year window as null. Anything else is 400, on create and on any update namingreport_idorbudget_window_years. A saved v1 report keeps the window it is given._budgetand_spm_provenanceare nowbudget_impactandstored_spm_provenance, unchanged, so the window rebuild reuses them.read_report_rowsalso joins each report's newest budget-window run and reads its years.require_economy_reportbecamerequire_readable_report, which refuses only household reports.listed_economy_reportsbecamelisted_reports.EconomyReportCreateRequest, andReportCreateRequestis the union of both kinds.Needed from the simulation service
The simulation service's budget-window
POST /reports(C1 · 13) is not built yet; its session confirmed this shape. This PR sends:It reads a 201 or 202 with
report_idandbudget_window_run_id(other fields are ignored), and every error answer as for an economy report (see #4281). A request withinclude_cliffsis refused by sim with 422. The rows it expects:typeeconomy, withyear(the first year) andbudget_window_size, itsinputs{"include_cliffs": false}plusspmwhen given, and the first year's two simulations;budget_window_runsrow;budget_window_run_idandyear, all on one country package and PolicyEngine.py version. A window run's versions are read from its first year;Until sim's
/reportsis deployed, a budget-window create answers 503, as an economy create does. Once the economy route is deployed but not the budget-window one, sim refusesreport_typebudget_windowwith its 422, which is answered as 422.Route contracts
No route, workflow or stable response field changed, so the migration contracts regenerate unchanged.
GET /v2/reports/{report_id}, the listing andPOST /v2/reportsgain the budget-window variant of their item and body, told apart bykind.CI
This PR is the third of a stack (#4281, then #4286). The full
pr.ymlrun started on it. If a later run skips jobs because the PR is stacked, the full run starts once it is the lowest unmerged PR.v2-integration-check.ymlgains three test files in two existing steps. No new environment variable or secret: the steps use the job's literal throwaway database values.Testing
Each new or changed test was run by node ID, against PostgreSQL 17 as the managed runtime role for the integration tests:
tests/unit/v2/test_budget_window_result_rebuild.py;tests/unit/v2/test_budget_window_report_read_routes.py;tests/unit/v2/test_budget_window_report_create_routes.py;tests/unit/v2/test_budget_window_submissions.py;tests/unit/libs/test_simulation_budget_window_reports.py;tests/unit/v2/test_saved_budget_window_reports.py;tests/integration/test_v2_budget_window_report_reads.py;tests/integration/test_v2_budget_window_report_creation.py;tests/integration/test_v2_saved_budget_window_reports.py;tests/integration/test_v2_report_reads.py::test_listing_reads_named_reports_in_the_order_askedandtests/unit/test_alembic_workflows.py::test_reusable_v2_integration_check_uses_disposable_databases_and_coverage.Each failing test was committed first and seen failing for the expected reason.
Checks:
alembic -c alembic-v2.ini check(no operations),lint-imports,python -m scripts.rules validate,scripts/run_quality_guards.py, ruff, ruff format and mypy. The only file changed underpolicyengine_api/data/v2/is a docstring inmodels/simulations.py. Explicit mypy on it reports the same 4sa_type=enum_type(...)errors as onmaster.The economy report and simulation submissions now share the client's body building and created-answer reading with budget windows. The old and new clients were compared over 760 cases of status, answer body and input, with no difference in what they send or answer. Moving the economy fields into the shared
ReportDefinitionFieldschanges only the OpenAPI field order:include_cliffsnow followsspm.Review
Three read-only review rounds found no substantiated bug. Their quality findings are fixed, each code change test-first:
POSTandPATCH /v2/user-reports, so other user routes no longer show it;One finding is left as is: a saved-report write reads the
reportsrow twice, by primary key in one transaction. #4281's tests patch the first read to simulate a deleted report, and merging the reads would change those tests.The branch merged #4286's update (master's admin user profiles) with no conflicts.
🤖 Generated with Claude Code