import re from datetime import datetime """ Priorities """ PRIORITY_MAP = {1: None, 2: 'L', 3: 'M', 4: 'H'} def parse_priority(priority): """ Converts a priority from Todoist to Taskwarrior. Todoist saves priorities as 1, 2, 3, 4, whereas Taskwarrior uses L, M, H. These values map very easily to eachother, as Todoist priority 1 indicates that no priority has been set. """ return PRIORITY_MAP[priority] """ Dates """ def parse_date(date): """ Converts a date from Todoist to Taskwarrior. Todoist: Fri 26 Sep 2014 08:25:05 +0000 (what is this called)? taskwarrior: ISO-8601 """ if not date: return None return datetime.strptime(date, '%a %d %b %Y %H:%M:%S %z').isoformat() def parse_recur(date_string): """ Parses a Todoist `date_string` to extract a `recur` string for Taskwarrior. Field: - Todoist: date_string - taskwarrior: recur Examples: - every other `interval` `period` -> 2 `period` - every `interval` `period` -> `interval` `period` - every `day of week` -> weekly _Note_: just because Todoist sets `date_string` doesn't mean that the task is repeating. Mostly it just indicates that the user input via string and not date selector. """ if not date_string: return # Normalize: # - trim leading, trailing, and, duplicate spaces # - convert to lowercase date_string = ' '.join(date_string.lower().strip().split()) return ( _recur_single_cycle(date_string) or _recur_multi_cycle(date_string) or _recur_day_of_week(date_string) or _recur_day_of_month(date_string) or _recur_special(date_string) ) # Atoms _PERIOD = r'(?Phour|day|week|month|year)s?' _EVERY = r'ev(ery)?' _CYCLES = r'((?P\d+)(st|nd|rd|th)?)' _SIMPLE = r'(?Pdaily|weekly|monthly|yearly)' _DOW = r'((?P(mon|tues|weds|thurs|fri|sat|sun))(day)?)' # A single cycle recurrence is one of: # - daily, weekly, monthly, yearly # - every day, every week, every month, every year # - every 1 day, every 1 week, every 1 month, every 1 year RE_SINGLE_CYCLE = re.compile( fr'^(({_EVERY}\s(1\s)?{_PERIOD})|{_SIMPLE})$' ) # A multi cycle recurrence is of the form: every N s RE_MULTI_CYCLE = re.compile( fr'^{_EVERY}\s({_CYCLES}|other)\s{_PERIOD}$' ) # A day of week recurrence is of the form: # - every (monday | tuesday | ...) # - every Nth (monday | tuesday | ...) RE_EVERY_DOW = re.compile( fr'^{_EVERY}\s({_CYCLES}\s)?{_DOW}$' ) # A day of month recurrence is of the form: every Nth RE_EVERY_DOM = re.compile( fr'^{_EVERY}\s{_CYCLES}$' ) # Other patterns that don't fit in with the others RE_SPECIAL = re.compile( fr'^{_EVERY}\s(?P