22
33import json
44import sys
5+ from typing import Dict
6+
7+ # fix bugs
58
69
710def main ():
8- args = extract_args ()
11+ args , do = extract_args ()
12+ print (args ) # debug
913
1014 # perform the task
11- do_task (args )
15+ execute_command (args , do )
1216
1317
14- def extract_args ():
18+ def extract_args (): # Needs refactoring
1519 raw_args = sys .argv [1 :]
16- args = {}
20+ args : Dict = {}
1721 # Create a parser
1822 if raw_args [0 ] == "-h" :
1923 print ("""CLI task tracker""" ) # Add the help message
24+
2025 elif raw_args [0 ] == "add" :
2126 args ["command" ] = "add"
2227 if "-h" not in raw_args [1 :] or "--help" not in raw_args [1 :]:
2328 if len (raw_args ) != 2 :
2429 report_error (f"Wrong number of arguments is provided { len (raw_args ) - 1 } ."
2530 + " Provide only 1 argument." , "Argument" )
31+ return args , False
2632 else :
2733 task = raw_args [1 ]
2834 args ["args" ] = [task ]
2935 else :
3036 if len (raw_args ) != 2 :
3137 report_error (f"Arbitrary arguments are provided with help option." , "Argument" )
38+ return args , False
3239 else :
3340 print ("Add a new task\n " + "Usage: add task\n "
3441 + "Note if there is a space in the input task wrap it in a quotes" )
42+
3543 elif raw_args [0 ] == "delete" :
3644 args ["command" ] = "delete"
3745 if "-h" not in raw_args [1 :] or "--help" not in raw_args [1 :]:
3846 if len (raw_args ) != 2 :
3947 report_error (f"Wrong number of arguments is provided { len (raw_args ) - 1 } ."
4048 + " Provide only 1 argument." , "Argument" )
49+ return args , False
4150 else :
4251 task_id_to_delete = raw_args [1 ]
4352 try :
4453 task_id_to_delete = int (task_id_to_delete )
4554 except ValueError :
4655 report_error ("ID must be an integer" , "Type" )
56+ return args , False
4757 else :
4858 args ["args" ] = [task_id_to_delete ]
4959 else :
5060 if len (raw_args ) != 2 :
5161 report_error (f"Arbitrary arguments are provided with help option." , "Argument" )
62+ return args , False
5263 else :
5364 print ("Delete an existing task by its ID\n " + "Usage: delete ID" )
5465
@@ -58,13 +69,15 @@ def extract_args():
5869 if len (raw_args ) != 3 :
5970 report_error (f"Wrong number of arguments is provided { len (raw_args ) - 1 } ."
6071 + " Provide only 2 arguments." , "Argument" )
72+ return args , False
6173 else :
6274 args ["args" ] = []
6375 task_id = raw_args [1 ]
6476 try :
6577 task_id = int (task_id )
6678 except ValueError :
6779 report_error ("ID must be an integer" , "Type" )
80+ return args , False
6881 else :
6982 args ["args" ].append (task_id )
7083
@@ -73,67 +86,124 @@ def extract_args():
7386 else :
7487 if len (raw_args ) != 2 :
7588 report_error (f"Arbitrary arguments are provided with help option." , "Argument" )
89+ return args , False
7690 else :
7791 print ("Update an existing task by its id. The new task will replace the old one.\n "
7892 + "Usage: update ID new-task" )
93+
7994 elif raw_args [0 ] == "list" :
80- if len (raw_args ) == 1 :
81- args ["command" ] = "list"
82- elif len (raw_args ) == 2 :
83- args ["args" ] = [raw_args [1 ]]
95+ args ["command" ] = "list"
96+ if "-h" not in raw_args [1 :] or "--help" not in raw_args [1 :]:
97+ if len (raw_args ) == 1 :
98+ args ["args" ] = ["all" ] # default "all"
99+ elif len (raw_args ) == 2 :
100+ choices = ["all" , "in-progress" , "done" , "todo" ] # "to-do" == "undone"
101+ if raw_args [1 ] not in choices :
102+ report_error ("Invalid status choose from: [all, in-progress, done, todo]" ,
103+ "InvalidChoice" )
104+ return args , False
105+ else :
106+ args ["args" ] = [raw_args [1 ]] # Optional
107+ else :
108+ report_error (f"Wrong number of arguments is provided." , "Argument" )
109+ return args , False
84110 else :
85- pass # error
111+ if len (raw_args ) != 2 :
112+ report_error (f"Arbitrary arguments are provided with help option." , "Argument" )
113+ return args , False
114+ else :
115+ print ("List the tasks by the given status. If no status provided all tasks will be listed\n "
116+ + "Usage: list [status]\n " + "Available status choices: in-progress, done, todo, all" )
117+
86118 elif raw_args [0 ] == "mark" :
87- pass
88- else :
89- report_error ("No such argument.\n " + "Choose from: [add, delete, update, list, or mark]" , "Argument" )
119+ args ["command" ] = "mark"
120+ if "-h" not in raw_args [1 :] or "--help" not in raw_args [1 :]:
121+ if len (raw_args ) == 2 :
122+ # ID argument
123+ task_id_to_mark = raw_args [1 ]
124+ try :
125+ task_id_to_mark = int (task_id_to_mark )
126+ except ValueError :
127+ report_error ("ID must be an integer" , "Type" )
128+ return args , False
129+ else :
130+ args ["args" ] = [task_id_to_mark ]
90131
91- """list_parser = subparsers.add_parser("list", help="Delete an existing task by its id", usage=SUPPRESS)
92- list_parser.add_argument("options", type=str, choices=["in-progress", "done", "undone", "all"],
93- help="Options: in-progress, done, undone, all", nargs="?" # to make it optional, or add -
94- )
132+ # Status argument
133+ args ["args" ].append ("done" ) # Optional, default "done"
95134
96- mark_parser = subparsers.add_parser("mark", usage=SUPPRESS,
97- help="Mark an existing item by its id as either in-progress or done")
98- mark_parser.add_argument("task_id", type=int, help='task_id to be marked')
99- mark_parser.add_argument("as", type=str, choices=["in-progress", "done"],
100- help="Options: done, in-progress", nargs="?")
135+ elif len (raw_args ) == 3 :
136+ args ["command" ] = "mark"
101137
102- # Parse arguments
103- args = parser.parse_args()"""
138+ # ID argument
139+ task_id_to_mark = raw_args [1 ]
140+ try :
141+ task_id_to_mark = int (task_id_to_mark )
142+ except ValueError :
143+ report_error ("ID must be an integer" , "Type" )
144+ return args , False
145+ else :
146+ args ["args" ] = [task_id_to_mark ]
147+
148+ # Status argument
149+ choices = ["in-progress" , "done" , "todo" ]
150+ # the last one to return the task to the basic state if it was marked by mistake
151+ if raw_args [2 ] not in choices :
152+ report_error ("Invalid status choose from: [in-progress, done, todo]" ,
153+ "InvalidChoice" )
154+ return args , False
155+ else :
156+ args ["args" ].append (raw_args [2 ]) # Optional
157+ else :
158+ report_error (f"Wrong number of arguments is provided." , "Argument" )
159+ return args , False
160+ else :
161+ if len (raw_args ) != 2 :
162+ report_error (f"Arbitrary arguments are provided with help option." , "Argument" )
163+ return args , False
164+ else :
165+ print ("Mark the tasks as in-progress, done, or todo." +
166+ " If no choice provided the task will be marked as done\n "
167+ + "Usage: mark [as]\n " + "Available choices: in-progress, done, or todo" )
168+ else :
169+ report_error ("No such argument.\n " + "Choose from: [add, delete, update, list, or mark]" , "Argument" )
104170
105- return args
171+ return args , True
106172
107173
108- def do_task (args ):
174+ def execute_command (args , do ):
109175 data = get_data ()
110176 task_id = get_id (data )
111177 if args ["command" ] == "add" :
112- add_task (args ["args" ][0 ], task_id , data )
113- elif args ["command" ] == "delete" : # then (6) test delete when it is as done
114- delete_task (args ["args" ][0 ], data )
115- elif args ["command" ] == "update" : # then (3) add this
116- update_task (args ["args" ][0 ], args ["args" ][1 ], data )
178+ if do :
179+ add_task (args ["args" ][0 ], task_id , data )
180+ elif args ["command" ] == "delete" :
181+ if do :
182+ delete_task (args ["args" ][0 ], data )
183+ elif args ["command" ] == "update" :
184+ if do :
185+ update_task (args ["args" ][0 ], args ["args" ][1 ], data )
117186 elif args ["command" ] == "list" :
118- list_tasks (data )
119- elif args ["command" ] == "mark" : # then (5) add this -as done-, then (7) add - as in progress-
120- item = data .pop (args .task_id )
121- print (f"{ item } is marked as... successfully" )
187+ if do :
188+ list_tasks (data , args ["args" ][0 ])
189+ elif args ["command" ] == "mark" :
190+ if do :
191+ mark_task (data , args ["args" ][0 ], args ["args" ][1 ])
122192 else :
123193 pass
194+ # No need to do anything, because extract_args reports error and ignore the invalid command
124195
125196 # then (8) add the rest of the features from roadmap like adding time ...
126197
127198
128- def get_data (): # and test this
199+ def get_data ():
129200 try :
130201 with open ("tasks.json" , "r" ) as file :
131202 data = json .load (file )
132203 return data
133204 except (FileNotFoundError , json .JSONDecodeError ):
134- with open ("tasks.json" , "w" ) as file :
135- json .dump ({}, file , indent = 4 )
136- return {}
205+ write_to_json ("tasks.json" , {})
206+ return {}
137207
138208
139209def get_id (data ):
@@ -144,10 +214,14 @@ def get_id(data):
144214 return len (data ) + 1 # If no missing values
145215
146216
147- def add_task (item , task_id , data ):
148- data [f"t{ task_id } " ] = {"id" : task_id , "task" : item }
149- with open ("tasks.json" , "w" ) as file :
217+ def write_to_json (file_name , data ):
218+ with open (file_name , "w" ) as file :
150219 json .dump (data , file , indent = 4 )
220+
221+
222+ def add_task (item , task_id , data ):
223+ data [f"t{ task_id } " ] = {"id" : task_id , "task" : item , "status" : "todo" }
224+ write_to_json ("tasks.json" , data )
151225 print (f"Successfully added { item } (ID: { task_id } )" )
152226
153227
@@ -157,8 +231,7 @@ def delete_task(task_id, data):
157231 except KeyError :
158232 report_error (f"No task is associated with the ID { task_id } ." , "ID" ) # error
159233 else :
160- with open ("tasks.json" , "w" ) as file :
161- json .dump (data , file , indent = 4 )
234+ write_to_json ("tasks.json" , data )
162235 print (f"Successfully deleted { item ['task' ]} " )
163236
164237
@@ -169,19 +242,37 @@ def update_task(task_id, new_task, data):
169242 except KeyError :
170243 report_error (f"No task is associated with the ID { task_id } ." , "ID" )
171244 else :
172- with open ("tasks.json" , "w" ) as file :
173- json .dump (data , file , indent = 4 )
245+ write_to_json ("tasks.json" , data )
174246 print (f"Successfully updated '{ old } ' to '{ data [f't{ task_id } ' ]['task' ]} '!" )
175247
176248
177249def sort_dict_data (task_dict ):
178250 return int (task_dict [0 ][1 :])
179251
180252
181- def list_tasks (data ):
253+ def list_tasks (data , status ):
182254 # noinspection PyTypeChecker
183- for _ , task_dict in sorted (data .items (), key = sort_dict_data ):
184- print (f"{ task_dict ["id" ]} . { task_dict ["task" ]} " )
255+ sorted_data = sorted (data .items (), key = sort_dict_data )
256+ if status == "all" :
257+ for _ , task_dict in sorted_data :
258+ print (f"{ task_dict ["id" ]} . { task_dict ["task" ]} - { task_dict ["status" ]} " )
259+ else :
260+ tasks = []
261+ for _ , task_dict in sorted_data :
262+ if task_dict ["status" ] == status :
263+ tasks .append (task_dict )
264+
265+ if tasks :
266+ for task in tasks :
267+ print (f"{ task ["id" ]} . { task ["task" ]} " )
268+ else :
269+ print (f"No tasks marked as { status } to be listed." )
270+
271+
272+ def mark_task (data , task_id , new_status ):
273+ data [f"t{ task_id } " ]["status" ] = new_status
274+ write_to_json ("tasks.json" , data )
275+ print (f"Successfully marked { data [f"t{ task_id } " ]["task" ]} as { new_status } !" )
185276
186277
187278def report_error (message , error_type = None ):
0 commit comments