@@ -195,6 +195,53 @@ def get_report_output(country_id: str, report_id: int) -> Response:
195195 )
196196
197197
198+ @report_output_bp .route ("/<country_id>/report/<int:report_id>/rerun" , methods = ["POST" ])
199+ @validate_country
200+ def create_report_rerun (country_id : str , report_id : int ) -> Response :
201+ """
202+ Create a new pending run for an existing report.
203+
204+ The requested report ID may be a legacy ID; the run is always created under
205+ the resolved canonical report output.
206+ """
207+ payload = request .json or {}
208+ if not isinstance (payload , dict ):
209+ raise BadRequest ("Payload must be an object" )
210+
211+ version_manifest_overrides = _parse_report_run_metadata (payload )
212+
213+ try :
214+ if not report_output_service .report_output_exists (country_id , report_id ):
215+ raise NotFound (f"Report #{ report_id } not found." )
216+
217+ rerun = report_output_service .create_report_rerun (
218+ country_id = country_id ,
219+ report_output_id = report_id ,
220+ version_manifest_overrides = version_manifest_overrides ,
221+ )
222+ except HTTPException :
223+ raise
224+ except ValueError as e :
225+ current_app .logger .warning (
226+ "Bad request creating report rerun #%s for country %s: %s" ,
227+ report_id ,
228+ country_id ,
229+ e ,
230+ )
231+ raise BadRequest (f"Failed to create report rerun: { e } " ) from e
232+
233+ response_body = dict (
234+ status = "ok" ,
235+ message = "Report rerun created successfully" ,
236+ result = rerun ,
237+ )
238+ return Response (
239+ json .dumps (response_body ),
240+ status = 201 ,
241+ mimetype = "application/json" ,
242+ )
243+
244+
198245@report_output_bp .route ("/<country_id>/report" , methods = ["PATCH" ])
199246@validate_country
200247def update_report_output (country_id : str ) -> Response :
@@ -206,6 +253,7 @@ def update_report_output(country_id: str) -> Response:
206253
207254 Request body can contain:
208255 - id (int): The report output ID.
256+ - report_output_run_id (str | None): Specific report run to update.
209257 - status (str): The new status ('pending', 'running', 'complete', or 'error')
210258 - output (dict): The result output (for complete status)
211259 - api_version (str): The API version of the report
@@ -221,6 +269,7 @@ def update_report_output(country_id: str) -> Response:
221269 report_id = payload .get ("id" )
222270 output = payload .get ("output" )
223271 error_message = payload .get ("error_message" )
272+ report_output_run_id = payload .get ("report_output_run_id" )
224273 version_manifest_overrides = _parse_report_run_metadata (payload )
225274 print (f"Updating report #{ report_id } for country { country_id } " )
226275
@@ -236,6 +285,8 @@ def update_report_output(country_id: str) -> Response:
236285 # Validate that complete status has output
237286 if status == "complete" and output is None :
238287 raise BadRequest ("output is required when status is 'complete'" )
288+ if report_output_run_id is not None and not isinstance (report_output_run_id , str ):
289+ raise BadRequest ("report_output_run_id must be a string" )
239290
240291 try :
241292 # First check if the report output exists without running pointer sync:
@@ -251,17 +302,14 @@ def update_report_output(country_id: str) -> Response:
251302 status = status ,
252303 output = output ,
253304 error_message = error_message ,
305+ report_output_run_id = report_output_run_id ,
254306 version_manifest_overrides = version_manifest_overrides ,
255307 )
256308
257309 if not success :
258310 raise BadRequest ("No fields to update" )
259311
260- # Get the updated stored record so stale-runtime jobs do not appear to
261- # complete the current runtime lineage in the PATCH response.
262- updated_report = report_output_service .get_stored_report_output (
263- country_id , report_id
264- )
312+ updated_report = report_output_service .get_report_output (country_id , report_id )
265313
266314 response_body = dict (
267315 status = "ok" ,
0 commit comments