def test_save_list_to_file(self): """This is where the output is written. The checks for the valididity of the data are being done manually """ task_list = TaskList() task1 = Task() output_file_path = self.testing_files[0] task1.set_name('Test Task One') task1.set_minutes(30) task1.set_notes('This is a great test task') task_list.add_task(task1) self.assert_equal(True, task_list.save_to_file(output_file_path)) self.assert_equal(True, os.path.isfile(output_file_path))
class Worklog: def __init__(self): self.task_list = TaskList() self.data_file = 'data.csv' self.task_list.read_from_file(self.data_file) def add_item(self): task = Task() print("What should the name of this task be?") task.input_name() print("How many minutes did you spend on it?") task.input_minutes() print("Add any other notes (or hit Enter/Return to skip notes).") task.input_notes() self.task_list.add_task(task) self.task_list.save_to_file(self.data_file) print("\033c", end="") print("Your task has been added!") def initial_prompt(self): print("\033c", end="") print("### Welcome to Worklogger ###") while True: print() print("Choose an option:\n") print(" 1. Add a new item") print(" 2. Lookup an item") print(" 3. Quit\n") get_input = input( "Enter the number of your selection (1-3): ").strip().lower() if get_input == '1': print("\033c", end="") self.add_item() elif get_input == '2': self.lookup_menu() elif get_input == '3': print("Thanks for using Worklogger!") break else: print("\033c", end="") print("That wasn't a valid option. Must be: 1-3. Try again.") def lookup_menu(self): print("\033c", end="") if len(self.task_list.tasks) == 0: print("Nothing to lookup. Your task log is empty.\n") input("Hit Enter/Return to go back and add a task.") else: while True: print("Lookup task by:\n") print(" 1. Date") print(" 2. Time spent") print(" 3. Exact text search") print(" 4. Pattern text search") print() get_input = input("Enter the number of your selection (1-4): " ).strip().lower() if get_input == '1': self.search_by_date() break elif get_input == '2': self.search_by_duration() break elif get_input == '3': self.search_by_text() break elif get_input == '4': self.search_by_regex() break else: print("\033c", end="") print( "That wasn't a valid option. Must be: 1-4. Try again") def search_by_date(self): print("\033c", end="") print("Choose from one of the following dates: ") print() for date_index, date_string in enumerate(self.task_list.date_list()): print(" {}. {}".format(date_index + 1, date_string)) while True: print() get_input = input( "Enter the number for the date you wish to see (1-{}): ". format(len(self.task_list.date_list()))).strip().lower() try: input_as_zero_based_int = int(get_input) - 1 except ValueError: print("That isn't a valid option. Try again.") continue else: if input_as_zero_based_int >= 0 and input_as_zero_based_int < len( self.task_list.date_list()): print("\033c", end="") request_date = self.task_list.date_list( )[input_as_zero_based_int] print("Here are the tasks you did on {}:\n".format( request_date)) for task in self.task_list.tasks_for_date(request_date): task.display() input("\nPress Enter/Return to return to the main menu") print("\033c", end="") break else: print("That isn't a valid option. Try again.") continue def search_by_duration(self): print("\033c", end="") print("Choose from one of the following durations: ") print() for duration_index, duration in enumerate(self.task_list.durations()): print(" {}. {}".format(duration_index + 1, duration)) while True: print() get_input = input( "Enter the number for the duration you wish to see (1-{}): ". format(len(self.task_list.durations()))).strip().lower() try: input_as_zero_based_int = int(get_input) - 1 except ValueError: print("That isn't a valid option. Try again.") continue else: if input_as_zero_based_int >= 0 and input_as_zero_based_int < len( self.task_list.durations()): print("\033c", end="") request_minutes = int( self.task_list.durations()[input_as_zero_based_int]) print("Here are the tasks that took {} minutes:\n".format( request_minutes)) for task in self.task_list.tasks_for_duration( request_minutes): task.display() input("\nPress Enter/Return to return to the main menu") print("\033c", end="") break else: print("That isn't a valid option. Try again.") continue def search_by_regex(self): print("\033c", end="") print("Enter the regular expression you want to search with:") get_input = input("> ") list_of_tasks = self.task_list.tasks_with_regex(get_input) if len(list_of_tasks) > 0: print("\033c", end="") print( "Here are the tasks who's name or notes match the '{}' pattern:\n" .format(get_input)) for task in list_of_tasks: task.display() else: print( "\nThe pattern '{}' doesn't match any task names or notes.\n") input("\nPress Enter/Return to return to the main menu") print("\033c", end="") def search_by_text(self): print("\033c", end="") print( "Enter the exact text you want to search for (case insensitive):") get_input = input("> ") list_of_tasks = self.task_list.tasks_with_string(get_input) if len(list_of_tasks) > 0: print("\033c", end="") print( "Here are the tasks with '{}' in the name or notes:\n".format( get_input)) for task in list_of_tasks: task.display() else: print() print("The string '{}' is not in any of your task names or notes.". format(get_input)) print() input("\nPress Enter/Return to return to the main menu") print("\033c", end="") def run_test(self, file_name): import sys system_input = sys.stdin test_input = open('tests/{}'.format(file_name), 'r') sys.stdin = test_input self.initial_prompt() test_input.close() sys.stdin = system_input
def main(): data_file = os.path.join(get_data_path(), 'tasks.JSON') last_data_file = data_file + ".last" task_list = TaskList(data_file) task_list_cpy = TaskList(data_file) is_changed = False # Assume the task list is not modified parser = argparse.ArgumentParser(description='Manages task list.') parser.add_argument('-v','--verbose', action='count', help='display more information') # TODO: connect parser subparsers = parser.add_subparsers(required=True, dest='subcommand') ### List parser_list = subparsers.add_parser('list', help='List all tasks.', aliases=['ls','l']) parser_list.add_argument('-m', '--merge', action='store_true', dest='list_merge', help='merges all tracks into one large list') parser_list.add_argument('--nocolor','-n', action='store_false', dest='list_is_color', default=True, help='turns off terminal coloring') parser_list.add_argument('-t','--track', action='append', dest='list_tracks', choices=task_list.get_tracks(), help='the track to list') parser_list.add_argument('-e', '--enumerate', action='store_true', dest='list_is_enum', help='lists the tasks by their index within a track') parser_list.add_argument('-a','--all', action='store_true', help='show all tasks, including completed') # TODO connect parser parser_list.add_argument('-c','--complete', action='store_true', help='show only complete tasks') #TODO connect parser parser_list.add_argument('-r','--random', action='store_true', dest='list_is_rand', help='randomize task order (cannot be used with the -e flag)') #TODO connect parser ### Add parser_add = subparsers.add_parser('add', help='Add a new task to the task list', aliases=['a']) parser_add.add_argument('new_task', help='the new task to add') parser_add.add_argument('-t','--track', dest='add_track', choices=task_list.get_tracks(), help='the track this task belongs to') ### Delete parser_del = subparsers.add_parser('delete', help='Delete a task', aliases=['del','d']) parser_del.add_argument('del_task_txt', nargs='?', type=str, help='the text of the task to delete') parser_del.add_argument('-n','--num', dest='del_task_num', type=int, help='delete a task by its index within a track instead of by text') parser_del.add_argument('-t','--track', dest='del_track', choices=task_list.get_tracks(), help='the track this task belongs to') parser_del.add_argument('--ignore_collisions', action='store_true', help='ignore task collisions, deleting the first found entry') #TODO ### Complete parser_comp = subparsers.add_parser('complete', help='Mark a task complete', aliases=['com','comp','c']) parser_comp.add_argument('comp_task_txt', nargs='?', type=str, help='the text of the task to complete') parser_comp.add_argument('-n','--num', dest='comp_task_num', type=int, help='complete a task by its index within a track instead of by text') parser_comp.add_argument('-t','--track', dest='comp_track', choices=task_list.get_tracks(), help='the track this task belongs to') parser_comp.add_argument('-u','--uncomplete', dest='is_incomplete', help='mark a task as incomplete') # TODO connect parser parser_comp.add_argument('--ignore_collisions', action='store_true', help='ignore task collisions, deleting the first found entry') #TODO ### Tracks #TODO connect parser parser_tracks = subparsers.add_parser('tracks', help='Manipulate task tracks', aliases=['track','tr']) track_subparsers = parser_tracks.add_subparsers(required=True, dest='tracks_action') # List tracks parser_list_track = track_subparsers.add_parser('list', help='List tracks', aliases=['ls','l']) parser_list_track.add_argument('-n','--nocolor', action='store_false', default=True, dest='color_track_list', help='flag to display colors of tracks.') # Add track #TODO connect parser parser_add_track = track_subparsers.add_parser('add', help='Add new task tracks', aliases=['a']) parser_add_track.add_argument('new_track', help='the new track to add') parser_add_track.add_argument('-d','--desc', dest='track_desc', help='description of the new track') parser_add_track.add_argument('-c','--color', dest='track_color', choices=color.COLORS, help='color of the new track') # Delete track #TODO connect parser parser_del_track = track_subparsers.add_parser('delete', help="Delete a task track. Fails if tasks are still inside unless forced; consider first transfering with 'tracks transfer'", aliases=['del','d']) parser_del_track.add_argument('del_track', help='the track to delete') parser_del_track.add_argument('-f','--force', action='store_true', dest='track_force_del', help='forces deletion, even if the given track still has tasks') # Transfer track #TODO make, connect parser # Rename track #TODO make, connect parser # Modify track parser_track_mod = track_subparsers.add_parser('modify', help='Modify a task track', aliases=['mod','m']) parser_track_mod.add_argument('mod_track', help='the track to modify') parser_track_mod.add_argument('-n','--name', dest='track_new_name', choices=color.COLORS, help='new name of the track') parser_track_mod.add_argument('-c','--color', dest='track_new_color', choices=color.COLORS, help='new color of the track') parser_track_mod.add_argument('-d','--desc', dest='track_new_desc', help='new description of the track') ### UNDO parser_undo = subparsers.add_parser('undo', help='Undos the last successful action') args = parser.parse_args() dargs = vars(args) print(args) print("") if not data_dir_exists(): generate_data_dir() # Connect 'list' parser if args.subcommand.find('l') == 0: # Print task list print_header("TASKS") task_tracks = agrs.list_tracks if args.list_tracks else task_list.get_tracks() for track in task_tracks: if not args.list_merge: print_track_header(task_list, track, is_color=args.list_is_color) if args.list_is_color: print(task_list.get_track_color(track), end="") if not args.list_merge: print("") for i,task in enumerate(task_list.get_tasks_in_track(track)): line_marker = "{}.".format(str(i)) if args.list_is_enum else "-" line = " {} {}".format(line_marker, task.txt) if task.complete: line = color.FAINT + line + color.NOT_FAINT print(line) print(color.END, end='' if args.list_merge else '\n') # Connect 'add' parser elif 'new_task' in dargs: task_list.add_task(args.new_task, track=args.add_track) track = TaskList.UNSORTED_TRACK if not args.add_track else args.add_track print("Wrote new task: '{}' to track '{}{}{}'".format(args.new_task, task_list.get_track_color(track),track,color.END)) is_changed = True # Connect 'del' parser elif args.subcommand.find('d') == 0: if args.del_task_txt and args.del_task_num: parser.error('delete: conflicting args: specify exactly one of -n DEL_TASK_NUM or DEL_TASK_TXT') elif args.del_task_num: if not args.del_track: parser.error('delete: if delete index -n specified, --track is required') try: success = task_list.del_task(t_idx=args.del_task_num, track=args.del_track) except ValueError as err: print("Failed to delete task: {}".format(err)) exit(0) if(success): print("Deleted task: #{} in track '{}': '{}'".format(args.del_task_num, args.del_track, success)) is_changed = True else: print("Task #{} doesn't exist in track '{}'".format(args.del_task_num, args.del_track)) elif args.del_task_txt: success = task_list.del_task(task=args.del_task_txt) if(success): print("Deleted task: '{}'".format(success)) is_changed = True else: print("Could not find task: '{}'".format(args.del_task_txt)) else: parser.error('delete: missing args: specify exactly one of -n DEL_TASK_NUM or DEL_TASK_TXT') # Connect 'comp' parser elif args.subcommand.find('c') == 0: if args.comp_task_txt and args.comp_task_num: parser.error('delete: conflicting args: specify exactly one of -n DEL_TASK_NUM or DEL_TASK_TXT') elif args.comp_task_num: if not args.comp_track: parser.error('complete: if delete index -n specified, --track is required') try: success = task_list.complete_task(t_idx=args.comp_task_num, track=args.comp_track) except ValueError as err: exit("Failed to complete task: {}".format(err)) if(success): print("Completed task: #{} in track '{}': '{}'".format(args.comp_task_num, args.comp_track, success)) is_changed = True else: print("Task #{} doesn't exist in track '{}'".format(args.comp_task_num, args.comp_track)) elif args.comp_task_txt: success = task_list.complete_task(task=args.comp_task_txt) if(success): print("Completed task: '{}'".format(success)) is_changed = True else: print("Could not find task: '{}'".format(args.comp_task_txt)) else: parser.error('complete: missing args: specify exactly one of -n DEL_TASK_NUM or DEL_TASK_TXT') # Connect 'tracks' parser elif args.subcommand.find('tr') == 0: # List tracks if args.tracks_action.find('l') == 0: # TODO: Verbosity # TODO: #complete count print_header("TRACKS") raw_tracks = task_list.get_tracks() if args.color_track_list: tracks = [task_list.get_track_color(track) + track + color.END for track in raw_tracks] else: tracks = raw_tracks row = "\t{}\t\t{}\t{}\n" print(row.format(color.UNDERLINE + "NAME", "#TASKS", "DESCRIPTION" + color.END)) for i,track in enumerate(tracks): print( row.format(track, len(task_list.get_tasks_in_track(raw_tracks[i])), task_list.get_track_desc(raw_tracks[i])) ) # Add track elif args.tracks_action.find('a') == 0: color_code = color.__dict__[args.track_color] if args.track_color else color.PLAIN try: task_list.add_track(args.new_track, desc=args.track_desc, color=color_code) except ValueError as err: exit("tracks add: {}".format(err)) print("Added track: '{}{}{}'".format(color_code,args.new_track.upper(), color.END)) is_changed = True # Del track elif args.tracks_action.find('d') == 0: task_cnt = len(task_list.get_tasks_in_track(args.del_track)) color_code = task_list.get_track_color(args.del_track) if task_cnt == 0 or args.track_force_del: try: task_list.del_track(args.del_track) except ValueError as err: exit("tracks add: {}".format(err)) if args.track_force_del and task_cnt > 0: print("Deleted {} tasks".format(task_cnt)) print("Deleted track: '{}{}{}'".format(color_code, args.del_track, color.END)) is_changed = True else: exit("tracks delete: track '{}{}{}' still has {} task(s); transfer task(s) or rerun with 'tracks delete --force'".format(color_code,args.del_track,color.END, task_cnt)) # Mod track elif args.tracks_action.find('m') == 0: if args.track_new_name or args.track_new_color or agrs.track_new_desc: color_code = color.__dict__[args.track_new_color] if args.track_new_color else None task_list.set_track_attr(args.mod_track, new_color=color_code, new_name=args.track_new_name, new_desc=args.track_new_desc) name = args.track_new_name if args.track_new_name else args.mod_track color_code = color_code if color_code else task_list.get_track_color(name) print("Modified track: '{}{}{}'".format(color_code, name, color.END)) is_changed = True else: parser.err("tracks modify: must specify some attributes to modify") elif args.subcommand == 'undo': if os.path.exists(last_data_file) and os.path.isfile(last_data_file): task_list = TaskList(last_data_file) os.remove(last_data_file) print("Undo action complete") is_changed = True else: exit("undo: no actions to undo") print("") if is_changed: task_list.write_to_file(data_file) task_list_cpy.write_to_file(last_data_file)
def test_add_a_task(self): task_list = TaskList() task = Task() self.assert_equal(True, task_list.add_task(task)) self.assert_equal(1, len(task_list.tasks))