11"""Archive command for moving tasks to archive folder."""
22
3+ from typing import Tuple
4+
35import click
46from prompt_toolkit .shortcuts import prompt
57from prompt_toolkit .validation import Validator
68
79from taskrepo .core .repository import RepositoryManager
810from taskrepo .tui .display import display_tasks_table
911from taskrepo .utils .display_constants import STATUS_EMOJIS
10- from taskrepo .utils .helpers import find_task_by_title_or_id , select_task_from_result
12+ from taskrepo .utils .helpers import process_tasks_batch
1113from taskrepo .utils .id_mapping import get_cache_size
1214
1315
1618@click .option ("--repo" , "-r" , help = "Repository name (will search all repos if not specified)" )
1719@click .option ("--yes" , "-y" , is_flag = True , help = "Automatically archive subtasks (skip prompt)" )
1820@click .pass_context
19- def archive (ctx , task_ids , repo , yes ):
21+ def archive (ctx , task_ids : Tuple [ str , ...] , repo , yes ):
2022 """Archive one or more tasks, or list archived tasks if no task IDs are provided.
2123
2224 TASK_IDS: One or more task IDs to archive (optional - if omitted, lists archived tasks)
@@ -32,14 +34,14 @@ def archive(ctx, task_ids, repo, yes):
3234 if not repository :
3335 click .secho (f"Error: Repository '{ repo } ' not found" , fg = "red" , err = True )
3436 ctx .exit (1 )
35- archived_tasks = repository .list_archived_tasks ()
37+ archived_tasks_list = repository .list_archived_tasks ()
3638 else :
3739 # Get archived tasks from all repos
38- archived_tasks = []
40+ archived_tasks_list = []
3941 for r in manager .discover_repositories ():
40- archived_tasks .extend (r .list_archived_tasks ())
42+ archived_tasks_list .extend (r .list_archived_tasks ())
4143
42- if not archived_tasks :
44+ if not archived_tasks_list :
4345 repo_msg = f" in repository '{ repo } '" if repo else ""
4446 click .echo (f"No archived tasks found{ repo_msg } ." )
4547 return
@@ -49,126 +51,79 @@ def archive(ctx, task_ids, repo, yes):
4951
5052 # Display archived tasks with IDs starting after active tasks
5153 display_tasks_table (
52- archived_tasks ,
54+ archived_tasks_list ,
5355 config ,
54- title = f"Archived Tasks ({ len (archived_tasks )} found)" ,
56+ title = f"Archived Tasks ({ len (archived_tasks_list )} found)" ,
5557 save_cache = False ,
5658 id_offset = active_task_count ,
5759 )
5860 return
5961
60- # Process multiple task IDs
61- archived_tasks = []
62- failed_tasks = []
63- repositories_to_update = set ()
64-
65- for task_id in task_ids :
66- try :
67- # Try to find task by ID or title
68- result = find_task_by_title_or_id (manager , task_id , repo )
69-
70- # Handle the result manually for batch processing
71- if result [0 ] is None :
72- # Not found
73- if len (task_ids ) > 1 :
74- click .secho (f"✗ No task found matching '{ task_id } '" , fg = "red" )
75- failed_tasks .append (task_id )
76- continue
77- else :
78- click .secho (f"Error: No task found matching '{ task_id } '" , fg = "red" , err = True )
79- ctx .exit (1 )
80-
81- elif isinstance (result [0 ], list ):
82- # Multiple matches
83- if len (task_ids ) > 1 :
84- click .secho (f"✗ Multiple tasks found matching '{ task_id } ' - skipping" , fg = "red" )
85- failed_tasks .append (task_id )
86- continue
87- else :
88- # Let select_task_from_result handle the interactive selection
89- task , repository = select_task_from_result (ctx , result , task_id )
90- else :
91- # Single match found
92- task , repository = result
93-
94- # Check for subtasks and prompt (only for single task operations)
95- if len (task_ids ) == 1 :
96- subtasks_with_repos = manager .get_all_subtasks_cross_repo (task .id )
97-
98- if subtasks_with_repos :
99- count = len (subtasks_with_repos )
100- subtask_word = "subtask" if count == 1 else "subtasks"
101-
102- # Determine whether to archive subtasks
103- archive_subtasks = yes # Default to --yes flag value
104-
105- if not yes :
106- # Show subtasks and prompt
107- click .echo (f"\n This task has { count } { subtask_word } :" )
108- for subtask , subtask_repo in subtasks_with_repos :
109- status_emoji = STATUS_EMOJIS .get (subtask .status , "" )
110- click .echo (f" • { status_emoji } { subtask .title } (repo: { subtask_repo .name } )" )
111-
112- # Prompt for confirmation with Y as default
113- yn_validator = Validator .from_callable (
114- lambda text : text .lower () in ["y" , "n" , "yes" , "no" ],
115- error_message = "Please enter 'y' or 'n'" ,
116- move_cursor_to_end = True ,
117- )
118-
119- response = prompt (
120- f"Archive all { count } { subtask_word } too? (Y/n) " ,
121- default = "y" ,
122- validator = yn_validator ,
123- ).lower ()
124-
125- archive_subtasks = response in ["y" , "yes" ]
126-
127- if archive_subtasks :
128- # Archive all subtasks
129- archived_count = 0
130- for subtask , subtask_repo in subtasks_with_repos :
131- if subtask_repo .archive_task (subtask .id ):
132- archived_count += 1
133-
134- if archived_count > 0 :
135- click .secho (f"✓ Archived { archived_count } { subtask_word } " , fg = "green" )
136-
137- # Archive the task
138- success = repository .archive_task (task .id )
139-
140- if success :
141- archived_tasks .append ((task , repository ))
142- repositories_to_update .add (repository )
143- else :
144- failed_tasks .append (task_id )
145- if len (task_ids ) > 1 :
146- click .secho (f"✗ Could not archive task '{ task_id } '" , fg = "red" )
147- else :
148- click .secho (f"Error: Could not archive task '{ task_id } '" , fg = "red" , err = True )
149- ctx .exit (1 )
150-
151- except Exception as e :
152- # Unexpected error - show message and continue with next task
153- failed_tasks .append (task_id )
154- if len (task_ids ) > 1 :
155- click .secho (f"✗ Could not archive task '{ task_id } ': { e } " , fg = "red" )
156- else :
157- raise
158-
159- # Show summary
62+ def archive_task_handler (task , repository ):
63+ """Handler to archive a task."""
64+ success = repository .archive_task (task .id )
65+ if success :
66+ return True , None
67+ else :
68+ return False , f"Could not archive task '{ task .id } '"
69+
70+ # Use batch processor
71+ archived_tasks , failed_tasks = process_tasks_batch (
72+ ctx , manager , task_ids , repo , task_handler = archive_task_handler , operation_name = "archived"
73+ )
74+
75+ # Handle subtask prompting for single task
76+ if len (archived_tasks ) == 1 and len (task_ids ) == 1 :
77+ task , _ = archived_tasks [0 ]
78+ subtasks_with_repos = manager .get_all_subtasks_cross_repo (task .id )
79+
80+ if subtasks_with_repos :
81+ count = len (subtasks_with_repos )
82+ subtask_word = "subtask" if count == 1 else "subtasks"
83+
84+ # Determine whether to archive subtasks
85+ archive_subtasks = yes # Default to --yes flag value
86+
87+ if not yes :
88+ # Show subtasks and prompt
89+ click .echo (f"\n This task has { count } { subtask_word } :" )
90+ for subtask , subtask_repo in subtasks_with_repos :
91+ status_emoji = STATUS_EMOJIS .get (subtask .status , "" )
92+ click .echo (f" • { status_emoji } { subtask .title } (repo: { subtask_repo .name } )" )
93+
94+ # Prompt for confirmation with Y as default
95+ yn_validator = Validator .from_callable (
96+ lambda text : text .lower () in ["y" , "n" , "yes" , "no" ],
97+ error_message = "Please enter 'y' or 'n'" ,
98+ move_cursor_to_end = True ,
99+ )
100+
101+ response = prompt (
102+ f"Archive all { count } { subtask_word } too? (Y/n) " ,
103+ default = "y" ,
104+ validator = yn_validator ,
105+ ).lower ()
106+
107+ archive_subtasks = response in ["y" , "yes" ]
108+
109+ if archive_subtasks :
110+ # Archive all subtasks
111+ archived_count = 0
112+ for subtask , subtask_repo in subtasks_with_repos :
113+ if subtask_repo .archive_task (subtask .id ):
114+ archived_count += 1
115+
116+ if archived_count > 0 :
117+ click .secho (f"✓ Archived { archived_count } { subtask_word } " , fg = "green" )
118+
119+ # Show individual success messages
160120 if archived_tasks :
161121 click .echo ()
162122 for task , _ in archived_tasks :
163123 click .secho (f"✓ Task archived: { task } " , fg = "green" )
164124
165- # Show summary for batch operations
166- if len (task_ids ) > 1 :
167- click .echo ()
168- click .secho (f"Archived { len (archived_tasks )} of { len (task_ids )} tasks" , fg = "green" )
169-
170125 # Update cache and display archived tasks from all repos
171- if repositories_to_update :
126+ if archived_tasks :
172127 from taskrepo .utils .id_mapping import save_id_cache
173128 from taskrepo .utils .sorting import sort_tasks
174129
0 commit comments