Beispiel #1
0
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)
Beispiel #2
0
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)
Beispiel #3
0
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)
Beispiel #4
0
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)
Beispiel #5
0
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)
Beispiel #6
0
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)
Beispiel #7
0
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)
Beispiel #8
0
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)
Beispiel #9
0
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)