@@ -59,6 +59,21 @@ def _load_pointers_from_file(pointers_file: str) -> list[str]:
5959 return [line .strip () for line in content .splitlines () if line .strip ()]
6060
6161
62+ def _write_result_file (result : Dict [str , Any ], output_file : str ) -> None :
63+ """
64+ Atomically write result dict to output_file as JSON.
65+ Raises on failure.
66+ """
67+ out_dir = os .path .dirname (os .path .abspath (output_file )) or "."
68+ with tempfile .NamedTemporaryFile (
69+ "w" , delete = False , dir = out_dir , prefix = ".tmp_delete_results_" , suffix = ".json"
70+ ) as tf :
71+ json .dump (result , tf , indent = 2 )
72+ tf .flush ()
73+ os .fsync (tf .fileno ())
74+ os .replace (tf .name , output_file )
75+
76+
6277def _check_pointers_match_ods_code (
6378 ods_code : str , pointer_ids : List [str ]
6479) -> tuple [List [str ], List [str ]]:
@@ -158,21 +173,6 @@ def _batch_delete_pointers(
158173 return pointers_deleted , sorted (failed_deletes_set )
159174
160175
161- def _write_result_file (result : Dict [str , Any ], output_file : str ) -> None :
162- """
163- Atomically write result dict to output_file as JSON.
164- Raises on failure.
165- """
166- out_dir = os .path .dirname (os .path .abspath (output_file )) or "."
167- with tempfile .NamedTemporaryFile (
168- "w" , delete = False , dir = out_dir , prefix = ".tmp_delete_results_" , suffix = ".json"
169- ) as tf :
170- json .dump (result , tf , indent = 2 )
171- tf .flush ()
172- os .fsync (tf .fileno ())
173- os .replace (tf .name , output_file )
174-
175-
176176def _print_summary (result : Dict [str , Any ]) -> None :
177177 """
178178 Print a concise summary of the result to stdout.
@@ -204,7 +204,6 @@ def _delete_pointers_by_id(
204204 ods_code : str ,
205205 pointers_to_delete : List [str ] | None = None ,
206206 pointers_file : str | None = None ,
207- output_file : str | None = None ,
208207) -> Dict [str , Any ]:
209208 """
210209 Delete pointers from DynamoDB table.
@@ -219,7 +218,6 @@ def _delete_pointers_by_id(
219218 - ods_code: ODS code of the organisation that the pointers belong to
220219 - pointers_to_delete: list of pointer ids to delete
221220 - pointers_file: path to JSON file (array of objects with "id" field) or text file (one id per line)
222- - output_file: optional path to write the result JSON to (atomic replace)
223221 """
224222 if pointers_to_delete is None and pointers_file is None :
225223 raise ValueError ("Provide either pointers_to_delete or pointers_file" )
@@ -231,6 +229,14 @@ def _delete_pointers_by_id(
231229 if pointers_file :
232230 pointers_to_delete = _load_pointers_from_file (pointers_file )
233231
232+ # establish start_time early so any early-return can use it for filename
233+ start_time = datetime .now (tz = timezone .utc )
234+ stamp = start_time .strftime ("%Y%m%dT%H%M%SZ" )
235+ script_dir = os .path .dirname (os .path .abspath (__file__ )) or "."
236+ output_file_path = os .path .join (
237+ script_dir , f"delete_results_{ ods_code } _{ stamp } .json"
238+ )
239+
234240 if len (pointers_to_delete ) == 0 :
235241 result = {
236242 "pointers_to_delete" : 0 ,
@@ -242,18 +248,15 @@ def _delete_pointers_by_id(
242248 "failed_deletes" : {"count" : 0 , "ids" : []},
243249 "deletes-took-secs" : 0 ,
244250 }
245- if output_file :
246- try :
247- _write_result_file (result , output_file )
248- except Exception as exc :
249- result ["_output_error" ] = (
250- f"Failed to write output_file { output_file } : { exc } "
251- )
251+ try :
252+ _write_result_file (result , output_file_path )
253+ except Exception as exc :
254+ result ["_output_error" ] = (
255+ f"Failed to write result file { output_file_path } : { exc } "
256+ )
252257 _print_summary (result )
253258 return result
254259
255- start_time = datetime .now (tz = timezone .utc )
256-
257260 print (
258261 f"Validating { len (pointers_to_delete )} pointers against ODS code { ods_code } ..."
259262 )
@@ -284,13 +287,12 @@ def _delete_pointers_by_id(
284287 "failed_deletes" : {"count" : 0 , "ids" : []},
285288 "deletes-took-secs" : timedelta .total_seconds (end_time - start_time ),
286289 }
287- if output_file :
288- try :
289- _write_result_file (result , output_file )
290- except Exception as exc :
291- result ["_output_error" ] = (
292- f"Failed to write output_file { output_file } : { exc } "
293- )
290+ try :
291+ _write_result_file (result , output_file_path )
292+ except Exception as exc :
293+ result ["_output_error" ] = (
294+ f"Failed to write result file { output_file_path } : { exc } "
295+ )
294296 _print_summary (result )
295297 return result
296298
@@ -325,13 +327,12 @@ def _delete_pointers_by_id(
325327 "failed_deletes" : {"count" : 0 , "ids" : []},
326328 "deletes-took-secs" : timedelta .total_seconds (end_time - start_time ),
327329 }
328- if output_file :
329- try :
330- _write_result_file (result , output_file )
331- except Exception as exc :
332- result ["_output_error" ] = (
333- f"Failed to write output_file { output_file } : { exc } "
334- )
330+ try :
331+ _write_result_file (result , output_file_path )
332+ except Exception as exc :
333+ result ["_output_error" ] = (
334+ f"Failed to write result file { output_file_path } : { exc } "
335+ )
335336 _print_summary (result )
336337 return result
337338
@@ -359,13 +360,12 @@ def _delete_pointers_by_id(
359360 "deletes-took-secs" : timedelta .total_seconds (end_time - start_time ),
360361 }
361362
362- if output_file :
363- try :
364- _write_result_file (result , output_file )
365- except Exception as exc :
366- result ["_output_error" ] = (
367- f"Failed to write output_file { output_file } : { exc } "
368- )
363+ try :
364+ _write_result_file (result , output_file_path )
365+ except Exception as exc :
366+ result ["_output_error" ] = (
367+ f"Failed to write result file { output_file_path } : { exc } "
368+ )
369369
370370 _print_summary (result )
371371 print (" Done" )
0 commit comments