def _advance_recurring_todo_helper(p_todo, p_offset): """ Given a Todo item, return a new instance of a Todo item with the dates shifted according to the recurrence rule. The new date is calculated from the given p_offset value. When no recurrence tag is present, an exception is raised. """ todo = Todo(p_todo.source()) pattern = todo.tag_value('rec') if not pattern: raise NoRecurrenceException() length = todo.length() new_due = relative_date_to_date(pattern, p_offset) if not new_due: raise NoRecurrenceException() # pylint: disable=E1103 todo.set_tag(config().tag_due(), new_due.isoformat()) if todo.start_date(): new_start = new_due - timedelta(length) todo.set_tag(config().tag_start(), new_start.isoformat()) todo.set_creation_date(date.today()) return todo
def match(self, p_todo): """ Performs a match on a key:value tag in the todo. First it tries to convert the value and the user-entered expression to a date and makes a comparison if it succeeds, based on the given operator (default ==). Upon failure, it falls back to converting value and user-entered expression to an integer and makes a numerical comparison based on the given operator (default ==) As a last resort, it falls back to using a Grep filter to see if the user given expression is contained in the todo text. """ if not self.key or not p_todo.has_tag(self.key): return False try: operand1 = date_string_to_date(p_todo.tag_value(self.key)) operand2 = relative_date_to_date(self.value) if not operand2: operand2 = date_string_to_date(self.value) except ValueError: operand1 = p_todo.tag_value(self.key) operand2 = self.value try: operand1 = int(operand1) operand2 = int(operand2) except ValueError: grep = GrepFilter(self.expression) return grep.match(p_todo) return self.compare_operands(operand1, operand2)
def test_weekday_next_week(self): """ When entering "Friday" on a Friday, return next week Friday instead of today. """ result = relative_date_to_date("fri") self.assertTrue(result, self.friday)
def convert_date(p_tag): value = self.todo.tag_value(p_tag) if value: dateobj = relative_date_to_date(value) if dateobj: self.todo.set_tag(p_tag, dateobj.isoformat())
def _execute_multi_specific(self): def _get_offset(p_todo): offset = p_todo.tag_value( config().tag_due(), date.today().isoformat()) offset_date = date_string_to_date(offset) if offset_date < date.today(): offset_date = date.today() return offset_date pattern = self.args[-1] self.printer.add_filter(PrettyPrinterNumbers(self.todolist)) for todo in self.todos: offset = _get_offset(todo) new_due = relative_date_to_date(pattern, offset) if new_due: if self.move_start_date and todo.has_tag(config().tag_start()): length = todo.length() new_start = new_due - timedelta(length) # pylint: disable=E1103 todo.set_tag(config().tag_start(), new_start.isoformat()) # pylint: disable=E1103 todo.set_tag(config().tag_due(), new_due.isoformat()) self.todolist.set_dirty() self.out(self.printer.print_todo(todo)) else: self.error("Invalid date pattern given.") break
def _execute_multi_specific(self): def _get_offset(p_todo): offset = p_todo.tag_value(config().tag_due(), date.today().isoformat()) offset_date = date_string_to_date(offset) if offset_date < date.today(): offset_date = date.today() return offset_date pattern = self.args[-1] self.printer.add_filter(PrettyPrinterNumbers(self.todolist)) for todo in self.todos: offset = _get_offset(todo) new_due = relative_date_to_date(pattern, offset) if new_due: if self.move_start_date and todo.has_tag(config().tag_start()): length = todo.length() new_start = new_due - timedelta(length) # pylint: disable=E1103 todo.set_tag(config().tag_start(), new_start.isoformat()) # pylint: disable=E1103 todo.set_tag(config().tag_due(), new_due.isoformat()) self.todolist.set_dirty() self.out(self.printer.print_todo(todo)) else: self.error("Invalid date pattern given.") break
def match(self, p_todo): if not self.key or not p_todo.has_tag(self.key): return False try: operand1 = date_string_to_date(p_todo.tag_value(self.key)) operand2 = relative_date_to_date(self.value) if not operand2: operand2 = date_string_to_date(self.value) except ValueError: try: operand1 = int(p_todo.tag_value(self.key)) operand2 = int(self.value) except ValueError: return False if self.operator == '<': return operand1 < operand2 elif self.operator == '<=': return operand1 <= operand2 elif self.operator == '=': return operand1 == operand2 elif self.operator == '>=': return operand1 >= operand2 elif self.operator == '>': return operand1 > operand2 elif self.operator == '!': return operand1 != operand2 return False
def convert_date(p_tag): value = p_todo.tag_value(p_tag) if value: dateobj = relative_date_to_date(value) if dateobj: p_todo.set_tag(p_tag, dateobj.isoformat())
def test_one_month_ext(self): test_date1 = date(2015, 1, 29) test_date2 = date(2016, 1, 31) test_date3 = date(2015, 12, 31) test_date4 = date(2015, 7, 31) test_date5 = date(2015, 10, 31) result1 = relative_date_to_date('1m', test_date1) result2 = relative_date_to_date('1m', test_date2) result3 = relative_date_to_date('1m', test_date3) result4 = relative_date_to_date('1m', test_date4) result5 = relative_date_to_date('1m', test_date5) self.assertEqual(result1, date(2015, 2, 28)) self.assertEqual(result2, date(2016, 2, 29)) self.assertEqual(result3, date(2016, 1, 31)) self.assertEqual(result4, date(2015, 8, 31)) self.assertEqual(result5, date(2015, 11, 30))
def _convert_relative_dates(self): is_start_tag = self.tag == config().tag_start() is_due_tag = self.tag == config().tag_due() if self.relative_date or is_start_tag or is_due_tag: real_date = relative_date_to_date(self.value) if real_date: self.value = real_date.isoformat()
def match(self, p_todo): operand1 = self.getter(p_todo) operand2 = relative_date_to_date(self.value) if not operand2: operand2 = date_string_to_date(self.value) if operand1 and operand2: return self.compare_operands(operand1, operand2) else: return False
def _dates(self, p_word_before_cursor): to_absolute = lambda s: relative_date_to_date(s).isoformat() start_value_pos = p_word_before_cursor.find(':') + 1 value = p_word_before_cursor[start_value_pos:] for reldate in _date_suggestions(): if not reldate.startswith(value): continue yield Completion(reldate, -len(value), display_meta=to_absolute(reldate))
def process_flag(self, p_opt, p_value): super().process_flag(p_opt, p_value) if p_opt == "-s" or p_opt == "--strict": self.strict_recurrence = True elif p_opt == "-d" or p_opt == "--date": try: self.completion_date = relative_date_to_date(p_value) if not self.completion_date: self.completion_date = date_string_to_date(p_value) except ValueError: self.completion_date = date.today()
def match(self, p_todo): """ Performs a match on a key:value tag in the todo. First it tries to convert the value and the user-entered expression to a date and makes a comparison if it succeeds, based on the given operator (default ==). Upon failure, it falls back to converting value and user-entered expression to an integer and makes a numerical comparison based on the given operator (default ==) As a last resort, it falls back to using a Grep filter to see if the user given expression is contained in the todo text. """ if not self.key or not p_todo.has_tag(self.key): return False try: operand1 = date_string_to_date(p_todo.tag_value(self.key)) operand2 = relative_date_to_date(self.value) if not operand2: operand2 = date_string_to_date(self.value) except ValueError: operand1 = p_todo.tag_value(self.key) operand2 = self.value try: operand1 = int(operand1) operand2 = int(operand2) except ValueError: grep = GrepFilter(self.expression) return grep.match(p_todo) if self.operator == '<': return operand1 < operand2 elif self.operator == '<=': return operand1 <= operand2 elif self.operator == '=': return operand1 == operand2 elif self.operator == '>=': return operand1 >= operand2 elif self.operator == '>': return operand1 > operand2 elif self.operator == '!': return operand1 != operand2 return False
def advance_recurring_todo(p_todo, p_offset=None, p_strict=False): """ Given a Todo item, return a new instance of a Todo item with the dates shifted according to the recurrence rule. Strict means that the real due date is taken as a offset, not today or a future date to determine the offset. When the todo item has no due date, then the date is used passed by the caller (defaulting to today). When no recurrence tag is present, an exception is raised. """ todo = Todo(p_todo.source()) pattern = todo.tag_value('rec') if not pattern: raise NoRecurrenceException() elif pattern.startswith('+'): p_strict = True # strip off the + pattern = pattern[1:] if p_strict: offset = p_todo.due_date() or p_offset or date.today() else: offset = p_offset or date.today() length = todo.length() new_due = relative_date_to_date(pattern, offset) if not new_due: raise NoRecurrenceException() # pylint: disable=E1103 todo.set_tag(config().tag_due(), new_due.isoformat()) if todo.start_date(): new_start = new_due - timedelta(length) todo.set_tag(config().tag_start(), new_start.isoformat()) if config().auto_creation_date(): todo.set_creation_date(date.today()) return todo
def advance_recurring_todo(p_todo, p_offset=None, p_strict=False): """ Given a Todo item, return a new instance of a Todo item with the dates shifted according to the recurrence rule. Strict means that the real due date is taken as a offset, not today or a future date to determine the offset. When the todo item has no due date, then the date is used passed by the caller (defaulting to today). When no recurrence tag is present, an exception is raised. """ todo = Todo(p_todo.source()) pattern = todo.tag_value('rec') if not pattern: raise NoRecurrenceException() elif pattern.startswith('+'): p_strict = True # strip off the + pattern = pattern[1:] if p_strict: offset = p_todo.due_date() or p_offset or date.today() else: offset = p_offset or date.today() length = todo.length() new_due = relative_date_to_date(pattern, offset) if not new_due: raise NoRecurrenceException() # pylint: disable=E1103 todo.set_tag(config().tag_due(), new_due.isoformat()) if todo.start_date(): new_start = new_due - timedelta(length) todo.set_tag(config().tag_start(), new_start.isoformat()) todo.set_creation_date(date.today()) return todo
def _dates(p_word_before_cursor): """ Generator for date completion. """ def _date_suggestions(): """ Returns a list of relative date that is presented to the user as auto complete suggestions. """ # don't use strftime, prevent locales to kick in days_of_week = { 0: "Monday", 1: "Tuesday", 2: "Wednesday", 3: "Thursday", 4: "Friday", 5: "Saturday", 6: "Sunday" } dates = [ 'today', 'tomorrow', ] # show days of week up to next week dow = datetime.date.today().weekday() for i in range(dow + 2 % 7, dow + 7): dates.append(days_of_week[i % 7]) # and some more relative days starting from next week dates += ["1w", "2w", "1m", "2m", "3m", "1y"] return dates to_absolute = lambda s: relative_date_to_date(s).isoformat() start_value_pos = p_word_before_cursor.find(':') + 1 value = p_word_before_cursor[start_value_pos:] for reldate in _date_suggestions(): if not reldate.startswith(value): continue yield Completion(reldate, -len(value), display_meta=to_absolute(reldate))
def _execute_multi_specific(self): def _get_offset(p_todo): offset = p_todo.tag_value(config().tag_due(), date.today().isoformat()) offset_date = date_string_to_date(offset) if offset_date < date.today(): offset_date = date.today() return offset_date pattern = self.args[-1] self.printer.add_filter(PrettyPrinterNumbers(self.todolist)) for todo in self.todos: try: offset = _get_offset(todo) except ValueError: self.error("Postponing todo item failed: invalid due date.") break new_due = relative_date_to_date(pattern, offset) if new_due: if self.move_start_date and todo.start_date(): length = todo.length() new_start = new_due - timedelta(length) # pylint: disable=E1103 todo.set_tag(config().tag_start(), new_start.isoformat()) elif self.move_start_date and not todo.start_date(): self.error( "Warning: todo item has no (valid) start date, therefore it was not adjusted." ) # pylint: disable=E1103 todo.set_tag(config().tag_due(), new_due.isoformat()) self.todolist.dirty = True self.out(self.printer.print_todo(todo)) else: self.error("Invalid date pattern given.") break
def match(self, p_todo): """ Performs a match on a key:value tag in the todo. First it tries to convert the value and the user-entered expression to a date and makes a comparison if it succeeds, based on the given operator (default ==). Upon failure, it falls back to converting value and user-entered expression to an integer and makes a numerical comparison based on the given operator (default ==) As a last resort, it falls back to using a Grep filter to see if the user given expression is contained in the todo text. """ def resort_to_grep_filter(): grep = GrepFilter(self.expression) return grep.match(p_todo) if not self.key or not p_todo.has_tag(self.key): return False if len(p_todo.tag_values(self.key)) > 1: # we cannot apply an ordening on a tag that appears more than once # in this todo item, therefore use a simple value match return resort_to_grep_filter() try: operand1 = date_string_to_date(p_todo.tag_value(self.key)) operand2 = relative_date_to_date(self.value) if not operand2: operand2 = date_string_to_date(self.value) except ValueError: operand1 = p_todo.tag_value(self.key) operand2 = self.value try: operand1 = int(operand1) operand2 = int(operand2) except ValueError: return resort_to_grep_filter() return self.compare_operands(operand1, operand2)
def execute(self): def _get_offset(p_todo): offset = p_todo.tag_value(config().tag_due(), date.today().isoformat()) offset_date = date_string_to_date(offset) if offset_date < date.today(): offset_date = date.today() return offset_date if not super(PostponeCommand, self).execute(): return False self._process_flags() try: todo = self.todolist.todo(self.argument(0)) pattern = self.argument(1) offset = _get_offset(todo) new_due = relative_date_to_date(pattern, offset) if new_due: if self.move_start_date and todo.has_tag(config().tag_start()): length = todo.length() new_start = new_due - timedelta(length) todo.set_tag(config().tag_start(), new_start.isoformat()) todo.set_tag(config().tag_due(), new_due.isoformat()) self.todolist.set_dirty() self.printer.add_filter(PrettyPrinterNumbers(self.todolist)) self.out(self.printer.print_todo(todo)) else: self.error("Invalid date pattern given.") except InvalidCommandArgument: self.error(self.usage()) except (InvalidTodoException): self.error("Invalid todo number given.")
def test_zero_years(self): result = relative_date_to_date('0y') self.assertEqual(result, self.today)
def test_leap_year(self): test_date = date(2016, 2, 29) result1 = relative_date_to_date('1y', test_date) result2 = relative_date_to_date('4y', test_date) self.assertEqual(result1, date(2017, 2, 28)) self.assertEqual(result2, date(2020, 2, 29))
def test_monday3(self): result = relative_date_to_date('mon') self.assertEqual(result, self.monday)
def test_one_week(self): result = relative_date_to_date('1w') self.assertEqual(result, date(2015, 11, 13))
def test_one_bday(self): result = relative_date_to_date('1b') self.assertEqual(result, self.monday)
def test_monday4(self): result = relative_date_to_date('mondayy') self.assertFalse(result)
def test_negative_period3(self): result = relative_date_to_date('-1b') self.assertEqual(result, date(2015, 11, 5))
def test_offset1(self): result = relative_date_to_date('1d', self.tomorrow) self.assertEqual(result, date(2015, 11, 8))
def test_zero_bdays(self): result = relative_date_to_date('0b') self.assertEqual(result, self.today)
def test_one_bweek(self): result = relative_date_to_date('5b') self.assertEqual(result, self.friday)
def test_one_month(self): test_date = date(2015, 1, 10) result = relative_date_to_date('1m', test_date) self.assertEqual(result, date(2015, 2, 10))
def test_one_year(self): test_date = date(2015, 1, 10) result = relative_date_to_date('1y', test_date) self.assertEqual(result, date(2016, 1, 10))
def test_one_day(self): result = relative_date_to_date('1d') self.assertEqual(result, self.tomorrow)
def test_negative_period2(self): result = relative_date_to_date('-0d') self.assertTrue(result, self.today)
def test_negative_period4(self): result = relative_date_to_date('-5b') self.assertEqual(result, date(2015, 10, 30))
def test_today2(self): result = relative_date_to_date('tod') self.assertEqual(result, self.today)
def test_tomorrow2(self): result = relative_date_to_date('tom') self.assertEqual(result, self.tomorrow)
def test_zero_months(self): result = relative_date_to_date('0m') self.assertEqual(result, self.today)
def test_yesterday2(self): result = relative_date_to_date('yes') self.assertEqual(result, self.yesterday)