From 3d8c91e4966e8a97e834f46b957e11908f26ef7b Mon Sep 17 00:00:00 2001 From: Matt Snider Date: Sat, 24 Nov 2018 15:15:06 +0100 Subject: [PATCH] Refactor recur parsing and cover more cases --- todoist_taskwarrior/utils.py | 142 ++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 28 deletions(-) diff --git a/todoist_taskwarrior/utils.py b/todoist_taskwarrior/utils.py index 34b98ef..3f8b89b 100644 --- a/todoist_taskwarrior/utils.py +++ b/todoist_taskwarrior/utils.py @@ -47,43 +47,129 @@ def parse_recur(date_string): """ if not date_string: return - return _match_every(date_string) or _match_weekly(date_string) + # 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) + ) -RE_INTERVAL = 'other|\d+' -RE_PERIOD = 'day|week|month|year|morning|evening|weekday|workday|last\s+day' -RE_REPEAT_EVERY = re.compile( - f'^\s*ev(ery)?\s+((?P{RE_INTERVAL})\s+)?(?P{RE_PERIOD})s?\s*$' +# 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})$' ) -def _match_every(desc): - match = RE_REPEAT_EVERY.match(desc.lower()) +# 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