diff --git a/ext/on-modify.timewarrior b/ext/on-modify.timewarrior index 403fea80..6ca0b08d 100644 --- a/ext/on-modify.timewarrior +++ b/ext/on-modify.timewarrior @@ -42,6 +42,18 @@ old = json.loads(sys.stdin.readline()) new = json.loads(sys.stdin.readline()) print(json.dumps(new)) +def get_timew_name_from_json(json_obj): + # Extract attributes for use as tags. + tags = [json_obj['description']] + + if 'project' in json_obj: + tags.append(json_obj['project']) + + if 'tags' in json_obj: + tags.extend(json_obj['tags']) + + return ' '.join(['"%s"' % tag for tag in tags]).encode('utf-8').strip() + start_or_stop = '' # Started task. @@ -53,16 +65,19 @@ elif 'start' not in new and 'start' in old: start_or_stop = 'stop' if start_or_stop: - # Extract attributes for use as tags. - tags = [new['description']] - - if 'project' in new: - tags.append(new['project']) - - if 'tags' in new: - tags.extend(new['tags']) - - combined = ' '.join(['"%s"' % tag for tag in tags]).encode('utf-8').strip() + combined = get_timew_name_from_json(new) command = 'timew {0} {1} :yes'.format(start_or_stop, combined) subprocess.call(shlex.split(command)) + +# Modifications to task other than start/stop +elif 'start' in new and 'start' in old: + old_combined = get_timew_name_from_json(old) + new_combined = get_timew_name_from_json(new) + + if old_combined != new_combined: + command = 'timew stop {0} :yes'.format(old_combined) + subprocess.call(shlex.split(command)) + + command = 'timew start {0} :yes'.format(new_combined) + subprocess.call(shlex.split(command)) diff --git a/test/test_on-modify.timewarrior.t.py b/test/test_on-modify.timewarrior.t.py index 273b41d0..93e5d630 100755 --- a/test/test_on-modify.timewarrior.t.py +++ b/test/test_on-modify.timewarrior.t.py @@ -110,14 +110,90 @@ class TestOnModifyHookScript(TestCase): self.assertEqual('{"status": "pending", "entry": "20180316T231125Z", "uuid": "e11d7d19-20a7-47bb-999e-6554a70ea094", "description": "FooBar", "modified": "20180316T231055Z"}\n', out) self.assertEqual('', err) - def test_hook_should_process_modify(self): - """on-modify hook should process 'task modify'""" + def test_hook_should_process_modify_desc(self): + """on-modify hook should process 'task modify' for changing description""" + + self.t("start dummy") out, err = self.process.communicate(input="""\ {"description":"dummy","entry":"20180317T092629Z","modified":"20180317T092629Z","start":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534"} {"description":"foo","entry":"20180317T092629Z","modified":"20180317T092629Z","start":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534"} """) - self.assertEqual('{"status": "pending", "uuid": "3422d76c-c087-4ecd-9c62-1246b078e534", "modified": "20180317T092629Z", "start": "20180317T092629Z", "entry": "20180317T092629Z", "description": "foo"}\n', out) + self.assertRegexpMatches(out, """\ +Tracking foo + Started \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2} + Current (\d{2}:| )\d{2} + Total ?\d{1,2}:\d{2}:\d{2} +\{"status": "pending", "uuid": "3422d76c-c087-4ecd-9c62-1246b078e534", "modified": "20180317T092629Z", "start": "20180317T092629Z", "entry": "20180317T092629Z", "description": "foo"\} +""") + self.assertEqual('', err) + + def test_hook_should_process_modify_tags(self): + """on-modify hook should process 'task modify' for changing tags""" + + self.t("start dummy tag1 tag2") + out, err = self.process.communicate(input="""\ +{"description":"dummy","entry":"20180317T092629Z","modified":"20180317T092629Z","start":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534","tags":["tag1","tag2"]} +{"description":"dummy","entry":"20180317T092629Z","modified":"20180317T092629Z","start":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534","tags":["tag3","tag4"]} +""") + + self.assertRegexpMatches(out, """\ +Recorded dummy tag1 tag2 + Started \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2} + Ended (\d{2}:| )\d{2} + Total ?\d{1,2}:\d{2}:\d{2} +Tracking dummy tag3 tag4 + Started \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2} + Current (\d{2}:| )\d{2} + Total ?\d{1,2}:\d{2}:\d{2} +\{"status": "pending", "uuid": "3422d76c-c087-4ecd-9c62-1246b078e534", "tags": \["tag3", "tag4"\], "modified": "20180317T092629Z", "start": "20180317T092629Z", "entry": "20180317T092629Z", "description": "dummy"\} +""") + self.assertEqual('', err) + + def test_hook_should_process_modify_project(self): + """on-modify hook should process 'task modify' for changing project""" + + self.t("start dummy proj1") + out, err = self.process.communicate(input="""\ +{"description":"dummy","entry":"20180317T092629Z","modified":"20180317T092629Z","start":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534","project":"proj1"} +{"description":"dummy","entry":"20180317T092629Z","modified":"20180317T092629Z","start":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534","project":"proj2"} +""") + + self.assertRegexpMatches(out, """\ +Recorded dummy proj1 + Started \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2} + Ended (\d{2}:| )\d{2} + Total ?\d{1,2}:\d{2}:\d{2} +Tracking dummy proj2 + Started \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2} + Current (\d{2}:| )\d{2} + Total ?\d{1,2}:\d{2}:\d{2} +\{"status": "pending", "uuid": "3422d76c-c087-4ecd-9c62-1246b078e534", "modified": "20180317T092629Z", "project": "proj2", "start": "20180317T092629Z", "entry": "20180317T092629Z", "description": "dummy"\} +""") + self.assertEqual('', err) + + def test_hook_should_process_modify_not_tracked(self): + """on-modify hook should process 'task modify' for tasks which are not time-tracked""" + out, err = self.process.communicate(input="""\ +{"description":"dummy","entry":"20180317T092629Z","modified":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534","project":"proj1"} +{"description":"dummy","entry":"20180317T092629Z","modified":"20180317T092629Z","status":"pending","uuid":"3422d76c-c087-4ecd-9c62-1246b078e534","project":"proj2"} +""") + + self.assertEqual('{"status": "pending", "uuid": "3422d76c-c087-4ecd-9c62-1246b078e534", "modified": "20180317T092629Z", "project": "proj2", "entry": "20180317T092629Z", "description": "dummy"}\n', out) + self.assertEqual('', err) + + # This is needed to test the case for task which are time-tracked and modify is called for changes other than project/tag/description + def test_hook_should_process_modify_due_date(self): + """on-modify hook should process 'task modify' for time-tracked task for changing annotations""" + + self.t("start dummy") + out, err = self.process.communicate(input="""\ +{"status": "pending", "description": "dummy", "modified": "20180513T073915Z", "due": "20180514T000000Z", "start": "20180513T073915Z", "entry": "20180513T054634Z", "uuid": "09e9620e-8906-47ad-92c5-305894534aac"} +{"status": "pending", "description": "dummy", "modified": "20180513T073915Z", "due": "20180512T000000Z", "start": "20180513T073915Z", "entry": "20180513T054634Z", "uuid": "09e9620e-8906-47ad-92c5-305894534aac"} +""") + + self.assertEqual('{"status": "pending", "description": "dummy", "due": "20180512T000000Z", "modified": "20180513T073915Z", "start": "20180513T073915Z", "entry": "20180513T054634Z", "uuid": "09e9620e-8906-47ad-92c5-305894534aac"}\n', out) + self.assertEqual('', err) def test_hook_should_process_prepend(self):