diff --git a/tests/test_recur.py b/tests/test_recur.py index 66addb3..93cebce 100644 --- a/tests/test_recur.py +++ b/tests/test_recur.py @@ -25,6 +25,9 @@ def test_every_n_days(): assert utils.parse_recur('every 3 day') == '3 days' assert utils.parse_recur('every 3 days') == '3 days' + # With time (which should be ignored since it's encoded in due_date anyways) + assert utils.parse_recur('every day at 19:00') == 'daily' + def test_special(): # Indicates daily at 9am - the time is saved in the `due` property @@ -42,6 +45,7 @@ def test_weekly(): assert utils.parse_recur('every 1 week') == 'weekly' assert utils.parse_recur('every 1 weeks') == 'weekly' assert utils.parse_recur('weekly') == 'weekly' + assert utils.parse_recur('every other week') == '2 weeks' assert utils.parse_recur('every 3 week') == '3 weeks' assert utils.parse_recur('every 3 weeks') == '3 weeks' @@ -51,12 +55,14 @@ def test_monthly(): assert utils.parse_recur('every 1 month') == 'monthly' assert utils.parse_recur('every 1 months') == 'monthly' assert utils.parse_recur('monthly') == 'monthly' + assert utils.parse_recur('every other month') == '2 months' assert utils.parse_recur('every 2 months') == '2 months' # ordinal assert utils.parse_recur('every 2nd month') == '2 months' assert utils.parse_recur('every 3rd month') == '3 months' + DAYS_OF_WEEK = [ # Monday 'mo', @@ -105,7 +111,10 @@ def test_every_dow_has_weekly_recurrence(dow): """ assert utils.parse_recur(f'ev {dow}') == 'weekly' assert utils.parse_recur(f'every {dow}') == 'weekly' + assert utils.parse_recur(f'every other {dow}') == '2 weeks' + # With time (which should be ignored since it's encoded in due_date anyways) + assert utils.parse_recur(f'every {dow} at 17:00') == 'weekly' @pytest.mark.parametrize('ordinal', [ ('2', 2), diff --git a/todoist_taskwarrior/utils.py b/todoist_taskwarrior/utils.py index 0635cca..edfbdd4 100644 --- a/todoist_taskwarrior/utils.py +++ b/todoist_taskwarrior/utils.py @@ -103,6 +103,7 @@ def parse_recur(date_string): _PERIOD = r'(?Phour|day|week|month|year)s?' _EVERY = r'ev(ery)?' _CYCLES = r'((?P\d+)(st|nd|rd|th)?)' +_OTHER = r'(?Pother)' _SIMPLE = r'(?Pdaily|weekly|monthly|yearly)' _DOW = ( r'((?P(' @@ -114,18 +115,19 @@ _DOW = ( r'|su(n(day)?)?' r')))' ) +_IGNORED = r'(\sat (\d{1,2}:\d{1,2})|(\d{1,2}(am|pm)))?' # 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})$' + fr'^(({_EVERY}\s(1\s)?{_PERIOD})|{_SIMPLE}){_IGNORED}$' ) # A multi cycle recurrence is of the form: every N s RE_MULTI_CYCLE = re.compile( - fr'^{_EVERY}\s({_CYCLES}|other)\s{_PERIOD}$' + fr'^{_EVERY}\s({_CYCLES}|other)\s{_PERIOD}{_IGNORED}$' ) @@ -133,13 +135,13 @@ RE_MULTI_CYCLE = re.compile( # - every (monday | tuesday | ...) # - every Nth (monday | tuesday | ...) RE_EVERY_DOW = re.compile( - fr'^{_EVERY}\s({_CYCLES}\s)?{_DOW}$' + fr'^{_EVERY}\s(({_CYCLES}|{_OTHER})\s)?{_DOW}{_IGNORED}$' ) # A day of month recurrence is of the form: every Nth RE_EVERY_DOM = re.compile( - fr'^{_EVERY}\s{_CYCLES}$' + fr'^{_EVERY}\s{_CYCLES}{_IGNORED}$' ) @@ -194,10 +196,14 @@ def _recur_day_of_week(date_string): groups = match.groupdict() day_of_week = groups['dayofweek'] + if groups['cycles']: cycles = groups['cycles'] + elif groups['other']: + cycles = 2 else: cycles = 1 + return 'weekly' if cycles == 1 else f'{cycles} weeks'