mirror of
				https://git.webmeisterei.com/webmeisterei/todoist-taskwarrior.git
				synced 2025-10-31 10:02:42 +01:00 
			
		
		
		
	Support every *other* DOW and ignore timestamps in recurrences (e.g. every other day at 9:00)
This commit is contained in:
		| @@ -25,6 +25,9 @@ def test_every_n_days(): | |||||||
|     assert utils.parse_recur('every 3 day') == '3 days' |     assert utils.parse_recur('every 3 day') == '3 days' | ||||||
|     assert utils.parse_recur('every 3 days') == '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(): | def test_special(): | ||||||
|     # Indicates daily at 9am - the time is saved in the `due` property |     # 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 week') == 'weekly' | ||||||
|     assert utils.parse_recur('every 1 weeks') == 'weekly' |     assert utils.parse_recur('every 1 weeks') == 'weekly' | ||||||
|     assert utils.parse_recur('weekly') == '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 week') == '3 weeks' | ||||||
|     assert utils.parse_recur('every 3 weeks') == '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 month') == 'monthly' | ||||||
|     assert utils.parse_recur('every 1 months') == 'monthly' |     assert utils.parse_recur('every 1 months') == 'monthly' | ||||||
|     assert utils.parse_recur('monthly') == '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' |     assert utils.parse_recur('every 2 months') == '2 months' | ||||||
|  |  | ||||||
|     # ordinal |     # ordinal | ||||||
|     assert utils.parse_recur('every 2nd month') == '2 months' |     assert utils.parse_recur('every 2nd month') == '2 months' | ||||||
|     assert utils.parse_recur('every 3rd month') == '3 months' |     assert utils.parse_recur('every 3rd month') == '3 months' | ||||||
|  |  | ||||||
|  |  | ||||||
| DAYS_OF_WEEK = [ | DAYS_OF_WEEK = [ | ||||||
|     # Monday |     # Monday | ||||||
|     'mo', |     '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'ev {dow}') == 'weekly' | ||||||
|     assert utils.parse_recur(f'every {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', [ | @pytest.mark.parametrize('ordinal', [ | ||||||
|     ('2', 2), |     ('2', 2), | ||||||
|   | |||||||
| @@ -103,6 +103,7 @@ def parse_recur(date_string): | |||||||
| _PERIOD = r'(?P<period>hour|day|week|month|year)s?' | _PERIOD = r'(?P<period>hour|day|week|month|year)s?' | ||||||
| _EVERY = r'ev(ery)?' | _EVERY = r'ev(ery)?' | ||||||
| _CYCLES = r'((?P<cycles>\d+)(st|nd|rd|th)?)' | _CYCLES = r'((?P<cycles>\d+)(st|nd|rd|th)?)' | ||||||
|  | _OTHER  = r'(?P<other>other)' | ||||||
| _SIMPLE = r'(?P<simple>daily|weekly|monthly|yearly)' | _SIMPLE = r'(?P<simple>daily|weekly|monthly|yearly)' | ||||||
| _DOW = ( | _DOW = ( | ||||||
|     r'((?P<dayofweek>(' |     r'((?P<dayofweek>(' | ||||||
| @@ -114,18 +115,19 @@ _DOW = ( | |||||||
|     r'|su(n(day)?)?' |     r'|su(n(day)?)?' | ||||||
|     r')))' |     r')))' | ||||||
| ) | ) | ||||||
|  | _IGNORED = r'(\sat (\d{1,2}:\d{1,2})|(\d{1,2}(am|pm)))?' | ||||||
|  |  | ||||||
| # A single cycle recurrence is one of: | # A single cycle recurrence is one of: | ||||||
| # - daily, weekly, monthly, yearly | # - daily, weekly, monthly, yearly | ||||||
| # - every day, every week, every month, every year | # - every day, every week, every month, every year | ||||||
| # - every 1 day, every 1 week, every 1 month, every 1 year | # - every 1 day, every 1 week, every 1 month, every 1 year | ||||||
| RE_SINGLE_CYCLE = re.compile( | 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 <period>s | # A multi cycle recurrence is of the form: every N <period>s | ||||||
| RE_MULTI_CYCLE = re.compile( | 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 (monday | tuesday | ...) | ||||||
| # - every Nth (monday | tuesday | ...) | # - every Nth (monday | tuesday | ...) | ||||||
| RE_EVERY_DOW = re.compile( | 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 | # A day of month recurrence is of the form: every Nth | ||||||
| RE_EVERY_DOM = re.compile( | 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() |     groups = match.groupdict() | ||||||
|     day_of_week = groups['dayofweek'] |     day_of_week = groups['dayofweek'] | ||||||
|  |  | ||||||
|     if groups['cycles']: |     if groups['cycles']: | ||||||
|         cycles = groups['cycles'] |         cycles = groups['cycles'] | ||||||
|  |     elif groups['other']: | ||||||
|  |         cycles = 2 | ||||||
|     else: |     else: | ||||||
|         cycles = 1 |         cycles = 1 | ||||||
|  |  | ||||||
|     return 'weekly' if cycles == 1 else f'{cycles} weeks' |     return 'weekly' if cycles == 1 else f'{cycles} weeks' | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Matt Snider
					Matt Snider