diff --git a/Wunderlist.alfredworkflow b/Wunderlist.alfredworkflow index b65b43c..22859db 100644 Binary files a/Wunderlist.alfredworkflow and b/Wunderlist.alfredworkflow differ diff --git a/bower.json b/bower.json index fe5ad26..ec5f42b 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "alfred-wunderlist-workflow", - "version": "0.5.2", + "version": "0.5.3", "dependencies": {}, "private": true, "devDependencies": {} diff --git a/lib/alfred-workflow b/lib/alfred-workflow index a81094e..2fd17c6 160000 --- a/lib/alfred-workflow +++ b/lib/alfred-workflow @@ -1 +1 @@ -Subproject commit a81094e9fbefd80ac23e9348f8b12d2dde84f7ee +Subproject commit 2fd17c62f38959126e19f6d7d77b0662d01671a7 diff --git a/package.json b/package.json index c762aa6..1d79495 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alfred-wunderlist-workflow", - "version": "0.5.2", + "version": "0.5.3", "dependencies": {}, "private": true, "devDependencies": { diff --git a/src/wunderlist/handlers/preferences.py b/src/wunderlist/handlers/preferences.py index ac38c1e..f372ba1 100644 --- a/src/wunderlist/handlers/preferences.py +++ b/src/wunderlist/handlers/preferences.py @@ -2,7 +2,7 @@ from wunderlist.models.user import User from wunderlist.models.preferences import Preferences -from wunderlist.util import workflow, parsedatetime_calendar, parsedatetime_constants, format_time +from wunderlist.util import workflow, parsedatetime_calendar, parsedatetime_constants, format_time, update_prerelease_channel from wunderlist import icons def _parse_time(phrase): @@ -131,6 +131,12 @@ def filter(args): arg=':pref explicit_keywords', valid=True, icon=icons.TASK_COMPLETED if prefs.explicit_keywords else icons.TASK ) + workflow().add_item( + 'Check for experimental updates to this workflow', + 'The workflow automatically checks for updates; enable this to include pre-releases', + arg=':pref prerelease_channel', valid=True, icon=icons.TASK_COMPLETED if prefs.prerelease_channel else icons.TASK + ) + workflow().add_item( 'Force sync', 'The workflow syncs automatically, but feel free to be forcible.', @@ -198,7 +204,19 @@ def commit(args): prefs.icon_theme = 'light' if icons.icon_theme() == 'dark' else 'dark' print 'The workflow is now using the %s icon theme' % (prefs.icon_theme) + elif 'prerelease_channel' in args: + relaunch_alfred = True + prefs.prerelease_channel = not prefs.prerelease_channel + + # Update the workflow settings and reverify the update data + update_prerelease_channel() + workflow().check_update(True) + + if prefs.prerelease_channel: + print 'The workflow will prompt you to update to experimental pre-releases' + else: + print 'The workflow will only prompt you to update to final releases' if relaunch_alfred: import subprocess subprocess.call(['/usr/bin/env', 'osascript', 'bin/launch_alfred.scpt', 'wl:pref']) diff --git a/src/wunderlist/models/preferences.py b/src/wunderlist/models/preferences.py index 5312112..a1bfde3 100644 --- a/src/wunderlist/models/preferences.py +++ b/src/wunderlist/models/preferences.py @@ -6,6 +6,7 @@ EXPLICIT_KEYWORDS_KEY = 'explicit_keywords' AUTOMATIC_REMINDERS_KEY = 'automatic_reminders' REMINDER_TODAY_OFFSET_KEY = 'reminder_today_offset' +PRERELEASE_CHANNEL_KEY = 'prerelease_channel' class Preferences(object): @@ -98,3 +99,12 @@ def automatic_reminders(self): @automatic_reminders.setter def automatic_reminders(self, automatic_reminders): self._set(AUTOMATIC_REMINDERS_KEY, automatic_reminders) + + @property + def prerelease_channel(self): + return self._get(PRERELEASE_CHANNEL_KEY, False) + + @prerelease_channel.setter + def prerelease_channel(self, prerelease_channel): + self._set(PRERELEASE_CHANNEL_KEY, prerelease_channel) + diff --git a/src/wunderlist/models/task_parser.py b/src/wunderlist/models/task_parser.py index 4e50ed8..2f69905 100644 --- a/src/wunderlist/models/task_parser.py +++ b/src/wunderlist/models/task_parser.py @@ -218,7 +218,10 @@ def _parse(self): # Set due_date if a datetime was found and it is not time only if datetime_info[1].hasDate: self.due_date = datetime_info[0].date() + elif datetime_info[1].hasTime: + self.due_date = date.today() + if self.due_date: # Pull in any words between the `due` keyword and the # actual date text date_pattern = re.escape(datetime_info[4]) diff --git a/src/wunderlist/util.py b/src/wunderlist/util.py index bcd670d..9814112 100644 --- a/src/wunderlist/util.py +++ b/src/wunderlist/util.py @@ -1,25 +1,43 @@ from workflow import Workflow _workflow = None +_update_settings = None def workflow(): - global _workflow + global _workflow, _update_settings if _workflow is None: + version = '__VERSION__' + _update_settings = { + 'github_slug': 'idpaterson/alfred-wunderlist-workflow', + 'version': version, + # Check for updates daily + # TODO: check less frequently as the workflow becomes more + # stable + 'frequency': 1, + # Download pre-release updates if enabled in preferences or if + # a prerelease is currently installed + 'prerelease': '-' in version + } + _workflow = Workflow( capture_args=False, - update_settings={ - 'github_slug': 'idpaterson/alfred-wunderlist-workflow', - 'version': '__VERSION__', - # Check for updates daily - # TODO: check less frequently as the workflow becomes more - # stable - 'frequency': 1 - } + update_settings=_update_settings ) + # Check preferences after initializing the workflow since the workflow + # is required for checking preferences + update_prerelease_channel() + return _workflow +def update_prerelease_channel(): + from wunderlist.models.preferences import Preferences + + prefs = Preferences.current_prefs() + + _update_settings['prerelease'] = prefs.prerelease_channel + def parsedatetime_calendar(): from parsedatetime import Calendar, Constants diff --git a/tests/wunderlist/models/test_task_parser.py b/tests/wunderlist/models/test_task_parser.py index aec45a4..b2dfd5e 100644 --- a/tests/wunderlist/models/test_task_parser.py +++ b/tests/wunderlist/models/test_task_parser.py @@ -63,7 +63,7 @@ 'daily': 'day' } -@pytest.fixture(autouse=True) +@pytest.fixture def mock_lists(mocker): """ Causes stored_data to return the lists specified for this test suite @@ -283,6 +283,7 @@ def test_default_time_today_disabled_offset(self): # Lists # +@pytest.mark.usefixtures("mock_lists") class TestLists(): def test_list_name_exact_match(self): @@ -541,19 +542,25 @@ def test_due_next_weekday(self): assert_task(task, phrase=phrase, title=title, due_date=due_date) - def test_due_date_ignores_time_only(self): - title = 'a sample task due 4:00' - phrase = title + def test_time_only_due_today_with_reminder(self): + title = 'a sample task' + due_phrase = 'due 12:00' + due_date = _today + reminder_date = datetime.combine(_today, _noon) + phrase = '%s %s' % (title, due_phrase) task = TaskParser(phrase) - assert_task(task, phrase=phrase, title=title) + assert_task(task, phrase=phrase, title=title, due_date=due_date, reminder_date=reminder_date) - def test_due_date_ignores_time_only_no_keyword(self): - title = 'a sample task 4:00' - phrase = title + def test_time_only_due_today_with_reminder_no_keyword(self): + title = 'a sample task' + due_phrase = 'at noon' + due_date = _today + reminder_date = datetime.combine(_today, _noon) + phrase = '%s %s' % (title, due_phrase) task = TaskParser(phrase) - assert_task(task, phrase=phrase, title=title) + assert_task(task, phrase=phrase, title=title, due_date=due_date, reminder_date=reminder_date) def test_due_date_with_time_sets_reminder(self): title = 'a sample task' @@ -791,8 +798,17 @@ def test_reminder_explicit_date(self): phrase = '%s %s' % (title, reminder_phrase) task = TaskParser(phrase) - assert_task(task, phrase=phrase, title=title, reminder_date=reminder_date) + def test_reminder_with_time_implicitly_due_today(self): + title = 'a sample task' + reminder_phrase = 'at noon' + reminder_date = datetime.combine(_today, _noon) + due_date = _today + phrase = '%s %s' % (title, reminder_phrase) + task = TaskParser(phrase) + assert_task(task, phrase=phrase, title=title, due_date=due_date, reminder_date=reminder_date) + + @pytest.mark.usefixtures("mock_lists") def test_reminder_with_list(self): target_list = _single_word_list title = 'a sample task' @@ -879,7 +895,7 @@ def test_simple_unchanged_phrase(self): assert phrase == new_phrase def test_complex_unchanged_phrase(self): - phrase = 'fin: Oil change next Tuesday repeat every 3mo *' + phrase = 'fin: Oil change next Tuesday at noon repeat every 3mo *' task = TaskParser(phrase) new_phrase = task.phrase_with() @@ -890,8 +906,8 @@ def test_phrase_reordering(self): This is not necessarily a desired feature compared to returning the same phrase the user entered, but it works for now. """ - phrase = 'finances: every 3mo Oil change next Tuesday *' - target_phrase = 'finances: Oil change next Tuesday every 3mo *' + phrase = 'finances: r noon every 3mo Oil change next Tuesday *' + target_phrase = 'finances: Oil change next Tuesday every 3mo r noon *' task = TaskParser(phrase) new_phrase = task.phrase_with() @@ -921,6 +937,18 @@ def test_change_recurrence(self): assert new_phrase == '%s %s' % (self.title, new_recurrence) + def test_change_reminder(self): + new_reminder = 'r tomorrow at noon' + new_phrase = self.task.phrase_with(reminder_date=new_reminder) + + assert new_phrase == '%s %s' % (self.title, new_reminder) + + def test_add_reminder_prompt(self): + new_reminder = 'remind me ' + new_phrase = self.task.phrase_with(reminder_date=True) + + assert new_phrase == '%s %s' % (self.title, new_reminder) + def test_change_star(self): new_phrase = self.task.phrase_with(starred=True)