def cmd_delay(tl, args): """delays the due date of one or more todo items """ with ColorRenderer() as cr: item = tl.get_item_by_index(args.item) if not item: print(u"Could not find item '{item_id}'".format(item_id = args.item)) return if item.due_date: new_date = to_date(args.date, item.due_date) if isinstance(new_date, basestring): # remove first character, as it is "?" with a non-parsable date print(u"The given relative date could not be parsed: {date}".format(date = new_date[1:])) else: # ask for confirmation if not args.force: print(" ", cr.render(item)) if not confirm_action(u"Delaying the preceding item's date from {from_date} to {to_date} (y/N)?".format( from_date = from_date(item.due_date), to_date = from_date(new_date))): return # do the actual replacement tl.replace_or_add_prop(item, conf.DUE, from_date(new_date), new_date) else: new_date = to_date(args.date) if not args.force: print(u" ", cr.render(item)) if not confirm_action(u"The preceding item has no due date set, set to {date} (y/N)?".format(date = from_date(new_date))): return tl.replace_or_add_prop(item, conf.DUE, from_date(new_date), new_date) suppress_if_quiet(u" {item}".format(item = cr.render(item)), args)
def cmd_repeat(tl, args): """marks the todo item as done and reenters it after the specified time :description: This command is for frequently occurring todo items, like e.g. a bi-weekly status report. Required fields of :param:`args`: * item: the index number of the item from which something should be detached * date: the relative or absolute date when the item is due again """ with ColorRenderer() as cr: item = tl.get_item_by_index(args.item) # create a copy new_item = tl.add_item(item.text) # we have to create a new ID for the copied item new_item.remove_prop(conf.ID) tl.replace_or_add_prop(new_item, conf.ID, tl.create_tid(new_item)) # set the due date of the new item to the specified date tl.replace_or_add_prop(new_item, conf.DUE, args.date, to_date(args.date)) # set old item to done item.set_to_done() suppress_if_quiet( u"Marked todo item as 'done' and reinserted:\n {item}".format( item=cr.render(new_item)), args)
def cmd_agenda(tl, args): """displays an agenda for a given date """ with ColorRenderer() as cr: agenda_items = [] # if not set, get agenda for today list_all = False if not args.date: args.date = datetime.datetime.now() elif args.date == "*": list_all = True else: args.date = to_date(args.date) if isinstance(args.date, basestring): print(u"Could not parse date argument '{date_str}'".format(date_str = args.date)) quit(-1) for item in tl.list_items(lambda x: True if x.due_date else False): if is_same_day(args.date, item.due_date) or list_all: agenda_items.append(item) # default date used when no done date is specified na_date = datetime.datetime(1970, 1, 1) # sort filtered list by "due" date and whether they are already marked as "done" agenda_items.sort(key=lambda x: (x.done, x.due_date) or (x.done, na_date)) # group report/done items by date for keys, groups in groupby(agenda_items, lambda x: ((x.due_date or na_date).year, (x.due_date or na_date).month, (x.due_date or na_date).day) ): # filter out default dates again if (na_date.year, na_date.month, na_date.day) == keys: print(u"No done date attached") else: print(u"Agenda for {0:d}-{1:02d}-{2:02d}:".format(*keys)) for item in groups: print(" ", cr.render(item)) suppress_if_quiet(u"{nr} todo items displayed.".format(nr = len(agenda_items)), args)
def cmd_delay(tl, args): """delays the due date of one or more todo items Required fields of :param:`args`: * item: the index number of the item to delay * date: either a date or a string like 'tomorrow', default '1d' (delays for 1 day) * force: if given, confirmation is not requested """ with ColorRenderer() as cr: item = tl.get_item_by_index(args.item) if not item: print(u"Could not find item '{item_id}'".format(item_id=args.item)) return if item.due_date: new_date = to_date(args.date, item.due_date) if isinstance(new_date, basestring): # remove first character, as it is "?" with a non-parsable date print(u"The given relative date could not be parsed: {date}". format(date=new_date[1:])) else: # ask for confirmation if not args.force: print(" ", cr.render(item)) if not confirm_action( u"Delaying the preceding item's date from {from_date} to {to_date} (y/N)?" .format(from_date=from_date(item.due_date), to_date=from_date(new_date))): return # do the actual replacement tl.replace_or_add_prop(item, conf.DUE, from_date(new_date), new_date) else: new_date = to_date(args.date) if not args.force: print(u" ", cr.render(item)) if not confirm_action( u"The preceding item has no due date set, set to {date} (y/N)?" .format(date=from_date(new_date))): return tl.replace_or_add_prop(item, conf.DUE, from_date(new_date), new_date) suppress_if_quiet(u" {item}".format(item=cr.render(item)), args)
def cmd_repeat(tl, args): """marks the todo item as done and reenters it after the specified time """ with ColorRenderer() as cr: item = tl.get_item_by_index(args.item) # create a copy new_item = tl.add_item(item.text) # we have to create a new ID for the copied item new_item.remove_prop(conf.ID) tl.replace_or_add_prop(new_item, conf.ID, tl.create_tid(new_item)) # set the due date of the new item to the specified date tl.replace_or_add_prop(new_item, conf.DUE, args.date, to_date(args.date)) # set old item to done item.set_to_done() suppress_if_quiet(u"Marked todo item as 'done' and reinserted:\n {item}".format(item = cr.render(new_item)), args)
def cmd_agenda(tl, args): """displays an agenda for a given date Required fields of :param:`args`: * date: either a date or a string like 'tomorrow' or '*', default 'today' """ with ColorRenderer() as cr: agenda_items = [] # if not set, get agenda for today list_all = False if not args.date: args.date = datetime.datetime.now() elif args.date == "*": list_all = True else: args.date = to_date(args.date) if isinstance(args.date, basestring): print(u"Could not parse date argument '{date_str}'".format( date_str=args.date)) quit(-1) for item in tl.list_items(lambda x: True if x.due_date else False): if is_same_day(args.date, item.due_date) or list_all: agenda_items.append(item) # default date used when no done date is specified na_date = datetime.datetime(1970, 1, 1) # sort filtered list by "due" date and whether they are already marked as "done" agenda_items.sort( key=lambda x: (x.done, x.due_date) or (x.done, na_date)) # group report/done items by date for keys, groups in groupby( agenda_items, lambda x: ((x.due_date or na_date).year, (x.due_date or na_date).month, (x.due_date or na_date).day)): # filter out default dates again if (na_date.year, na_date.month, na_date.day) == keys: print(u"No done date attached") else: print(u"Agenda for {0:d}-{1:02d}-{2:02d}:".format(*keys)) for item in groups: print(" ", cr.render(item)) suppress_if_quiet( u"{nr} todo items displayed.".format(nr=len(agenda_items)), args)
def cmd_repeat(tl, args): """marks the todo item as done and reenters it after the specified time :description: This command is for frequently occurring todo items, like e.g. a bi-weekly status report. Required fields of :param:`args`: * item: the index number of the item from which something should be detached * date: the relative or absolute date when the item is due again """ with ColorRenderer() as cr: item = tl.get_item_by_index(args.item) # create a copy new_item = tl.add_item(item.text) # we have to create a new ID for the copied item new_item.remove_prop(conf.ID) tl.replace_or_add_prop(new_item, conf.ID, tl.create_tid(new_item)) # set the due date of the new item to the specified date tl.replace_or_add_prop(new_item, conf.DUE, args.date, to_date(args.date)) # set old item to done item.set_to_done() suppress_if_quiet(u"Marked todo item as 'done' and reinserted:\n {item}".format(item = cr.render(new_item)), args)
def cmd_report(tl, args): """shows a daily report of all done and report items :description: This command lists all done and report items for a given date or date range. If no arguments are given, the items of the last 7 days are displayed. Required fields of :param:`args`: * from_date: either a date or a string like 'tomorrow' or '*' * to_date: either a date or a string like 'tomorrow' """ with ColorRenderer() as cr: # default date used when no done date is specified na_date = datetime.datetime(1970, 1, 1, 0, 0, 0, 0) # today now = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) # check from and to date, make them datetime or None # what mode are we in? mode = None if args.from_date in ("*", "all"): mode, args.from_date, args.to_date = "ALL", na_date, now else: args.from_date = to_date(args.from_date) args.to_date = to_date(args.to_date) if isinstance(args.from_date, datetime.datetime): args.from_date = args.from_date.replace(hour=0, minute=0, second=0, microsecond=0) else: logger.debug( u"Cannot parse {date}".format(date=args.from_date)) args.from_date = None if isinstance(args.to_date, datetime.datetime): args.to_date = args.to_date.replace(hour=0, minute=0, second=0, microsecond=0) else: logger.debug(u"Cannot parse {date}".format(date=args.to_date)) args.to_date = None if args.from_date and args.to_date and not mode: mode = "RANGE" elif args.from_date and not args.to_date: mode, args.to_date = "DAY", args.from_date elif not mode: # last 7 days mode, args.from_date, args.to_date = "LASTWEEK", now - datetime.timedelta( days=7), now # swap dates, if necessary if args.from_date > args.to_date: args.from_date, args.to_date = args.to_date, args.from_date # set end date to end of day args.to_date = args.to_date.replace(hour=23, minute=59, second=59) logger.debug(u"Report mode {0}: from {1} to {2}".format( mode, args.from_date, args.to_date)) # get list of done and report items from current todo list report_list = list(tl.list_items(lambda x: (x.done or x.is_report))) # get list of done and report items from un-dated archive file root_dir = os.path.dirname(conf.todo_file) unsorted_fn = os.path.join(root_dir, conf.archive_unsorted_filename) if os.path.exists(unsorted_fn): res = TodoList(unsorted_fn) report_list.extend(res.todolist) # get all archive file names in list file_pattern = re_replace_archive_vars.sub( "*", conf.archive_filename_scheme) file_list = glob.glob(os.path.join(root_dir, file_pattern)) # regex for finding all replaced parts in archive filename scheme re_find_date_str = re_replace_archive_vars.sub( "(.+)", conf.archive_filename_scheme).replace("\\", "\\\\") re_find_date = re.compile(re_find_date_str, re.UNICODE) # loop through all files and see, whether they match the given date range for fn in file_list: # get all replaced values in filename parts = re_find_date.findall(fn)[0] # get the variables responsible for this substitution (e.archived_items. "%Y", "%m", ...) tvars = re_replace_archive_vars.findall( conf.archive_filename_scheme) # create mapping, removing duplicates mapping = dict(zip(tvars, parts)) # create date from mapping tdate = datetime.datetime.strptime(" ".join(mapping.values()), " ".join(mapping)) # if filename matches date range if args.from_date <= tdate <= args.to_date: # load todo list res = TodoList(fn) # get items directly if they are done or report items archived_items = [ item for item in res.todolist if item.done or item.is_report ] for item in archived_items: # replace id with (A) to mark it as archived item.replace_or_add_prop(conf.ID, "(A)") # append it to candidates report_list.extend(archived_items) # sort filtered list by "done" date report_list.sort(key=lambda x: x.done_date or na_date) nr = 0 # group report/done items by date for keys, groups in groupby( report_list, lambda x: ((x.done_date or na_date).year, (x.done_date or na_date).month, (x.done_date or na_date).day)): # we are looking at that date right now temp_date = datetime.datetime(year=keys[0], month=keys[1], day=keys[2]) # that date does not match the requested date range: skip if not args.from_date <= temp_date <= args.to_date: continue # filter out default dates again if is_same_day(na_date, temp_date): print(u"Report for unknown date:") else: print(u"Report for {date}:".format( date=temp_date.strftime("%A, %Y-%m-%d"))) # print the items, finally for item in groups: print(" ", cr.render(item)) nr += 1 suppress_if_quiet(u"{nr} todo items displayed.".format(nr=nr), args)
def cmd_report(tl, args): """shows a daily report of all done and report items """ with ColorRenderer() as cr: # default date used when no done date is specified na_date = datetime.datetime(1970, 1, 1, 0, 0, 0, 0) # today now = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) # check from and to date, make them datetime or None # what mode are we in? mode = None if args.from_date in ("*", "all"): mode, args.from_date, args.to_date = "ALL", na_date, now else: args.from_date = to_date(args.from_date) args.to_date = to_date(args.to_date) if isinstance(args.from_date, datetime.datetime): args.from_date = args.from_date.replace(hour=0, minute=0, second=0, microsecond=0) else: logger.debug(u"Cannot parse {date}".format(date = args.from_date)) args.from_date = None if isinstance(args.to_date, datetime.datetime): args.to_date = args.to_date.replace(hour=0, minute=0, second=0, microsecond=0) else: logger.debug(u"Cannot parse {date}".format(date = args.to_date)) args.to_date = None if args.from_date and args.to_date and not mode: mode = "RANGE" elif args.from_date and not args.to_date: mode, args.to_date = "DAY", args.from_date elif not mode: # last 7 days mode, args.from_date, args.to_date = "LASTWEEK", now - datetime.timedelta(days=7), now # swap dates, if necessary if args.from_date > args.to_date: args.from_date, args.to_date = args.to_date, args.from_date # set end date to end of day args.to_date = args.to_date.replace(hour=23, minute=59, second=59) logger.debug(u"Report mode {0}: from {1} to {2}".format(mode, args.from_date, args.to_date)) # get list of done and report items from current todo list report_list = list(tl.list_items(lambda x: (x.done or x.is_report))) # get list of done and report items from un-dated archive file root_dir = os.path.dirname(conf.todo_file) unsorted_fn = os.path.join(root_dir, conf.archive_unsorted_filename) if os.path.exists(unsorted_fn): res = TodoList(unsorted_fn) report_list.extend(res.todolist) # get all archive file names in list file_pattern = re_replace_archive_vars.sub("*", conf.archive_filename_scheme) file_list = glob.glob(os.path.join(root_dir, file_pattern)) # regex for finding all replaced parts in archive filename scheme re_find_date_str = re_replace_archive_vars.sub("(.+)", conf.archive_filename_scheme).replace("\\", "\\\\") re_find_date = re.compile(re_find_date_str, re.UNICODE) # loop through all files and see, whether they match the given date range for fn in file_list: # get all replaced values in filename parts = re_find_date.findall(fn)[0] # get the variables responsible for this substitution (e.archived_items. "%Y", "%m", ...) tvars = re_replace_archive_vars.findall(conf.archive_filename_scheme) # create mapping, removing duplicates mapping = dict(zip(tvars, parts)) # create date from mapping tdate = datetime.datetime.strptime(" ".join(mapping.values()), " ".join(mapping)) # if filename matches date range if args.from_date <= tdate <= args.to_date: # load todo list res = TodoList(fn) # get items directly if they are done or report items archived_items = [item for item in res.todolist if item.done or item.is_report] for item in archived_items: # replace id with (A) to mark it as archived item.replace_or_add_prop(conf.ID, "(A)") # append it to candidates report_list.extend(archived_items) # sort filtered list by "done" date report_list.sort(key=lambda x: x.done_date or na_date) nr = 0 # group report/done items by date for keys, groups in groupby(report_list, lambda x: ((x.done_date or na_date).year, (x.done_date or na_date).month, (x.done_date or na_date).day) ): # we are looking at that date right now temp_date = datetime.datetime(year=keys[0], month=keys[1], day=keys[2]) # that date does not match the requested date range: skip if not args.from_date <= temp_date <= args.to_date: continue # filter out default dates again if is_same_day(na_date, temp_date): print(u"Report for unknown date:") else: print(u"Report for {date}:".format(date = temp_date.strftime("%A, %Y-%m-%d"))) # print the items, finally for item in groups: print(" ", cr.render(item)) nr += 1 suppress_if_quiet(u"{nr} todo items displayed.".format(nr = nr), args)