@@ -328,9 +328,14 @@ def extras(nodes, keys, fmt, identifier, raw):
328
328
@click .argument ('identifier' , nargs = - 1 , metavar = 'NODES' )
329
329
@options .DRY_RUN ()
330
330
@options .FORCE ()
331
+ @click .option (
332
+ '--clean-workdir' ,
333
+ is_flag = True ,
334
+ help = 'Also clean the remote work directory, if applicable.' ,
335
+ )
331
336
@options .graph_traversal_rules (GraphTraversalRules .DELETE .value )
332
337
@with_dbenv ()
333
- def node_delete (identifier , dry_run , force , ** traversal_rules ):
338
+ def node_delete (identifier , dry_run , force , clean_workdir , ** traversal_rules ):
334
339
"""Delete nodes from the provenance graph.
335
340
336
341
This will not only delete the nodes explicitly provided via the command line, but will also include
@@ -356,10 +361,62 @@ def _dry_run_callback(pks):
356
361
echo .echo_info ('The nodes with the following pks would be deleted: ' + ' ' .join (map (str , pks )))
357
362
return not click .confirm ('Shall I continue?' , abort = True )
358
363
359
- _ , was_deleted = delete_nodes (pks , dry_run = dry_run or _dry_run_callback , ** traversal_rules )
364
+ def _perform_delete ():
365
+ _ , was_deleted = delete_nodes (pks , dry_run = dry_run or _dry_run_callback , ** traversal_rules )
366
+ if was_deleted :
367
+ echo .echo_success ('Finished deletion.' )
368
+
369
+ if clean_workdir :
370
+ from aiida .manage import get_manager
371
+ from aiida .orm import CalcJobNode , QueryBuilder
372
+ from aiida .orm .utils .remote import clean_mapping_remote_paths , get_calcjob_remote_paths
373
+ from aiida .tools .graph .graph_traversers import get_nodes_delete
374
+
375
+ backend = get_manager ().get_backend ()
376
+ # For here we ignore missing nodes will be raised via func:``delete_nodes`` in the next block
377
+ pks_set_to_delete = get_nodes_delete (
378
+ pks , get_links = False , missing_callback = lambda missing_pks : None , backend = backend , ** traversal_rules
379
+ )['nodes' ]
380
+
381
+ qb = QueryBuilder ()
382
+ qb .append (CalcJobNode , filters = {'id' : {'in' : pks_set_to_delete }}, project = 'id' )
383
+ calcjobs_pks = [result [0 ] for result in qb .all ()]
384
+
385
+ if not calcjobs_pks :
386
+ echo .echo_report ('--clean-workdir ignored. No CalcJobNode associated with the given node, found.' )
387
+ _perform_delete ()
388
+ return
389
+
390
+ path_mapping = get_calcjob_remote_paths (
391
+ calcjobs_pks ,
392
+ only_not_cleaned = True ,
393
+ )
394
+
395
+ if not path_mapping :
396
+ echo .echo_report ('--clean-workdir ignored. CalcJobNode work directories are already cleaned.' )
397
+ _perform_delete ()
398
+ return
399
+
400
+ descendant_pks = [remote_folder .pk for paths in path_mapping .values () for remote_folder in paths ]
401
+
402
+ if not force and not dry_run :
403
+ echo .echo_warning (
404
+ f'YOU ARE ABOUT TO CLEAN { len (descendant_pks )} REMOTE DIRECTORIES! ' 'THIS CANNOT BE UNDONE!'
405
+ )
406
+ echo .echo_info (
407
+ 'Remote directories of nodes with the following pks would be cleaned: '
408
+ + ' ' .join (map (str , descendant_pks ))
409
+ )
410
+ click .confirm ('Shall I continue?' , abort = True )
411
+
412
+ if dry_run :
413
+ echo .echo_report (
414
+ 'Remote folders of these node are marked for deletion: ' + ' ' .join (map (str , descendant_pks ))
415
+ )
416
+ else :
417
+ clean_mapping_remote_paths (path_mapping )
360
418
361
- if was_deleted :
362
- echo .echo_success ('Finished deletion.' )
419
+ _perform_delete ()
363
420
364
421
365
422
@verdi_node .command ('rehash' )
0 commit comments