X-Git-Url: https://git.madduck.net/etc/taskwarrior.git/blobdiff_plain/5c9fc321bdfb21abe02fe5df654778b7c7befb19..57704ed4307af96a5216711fd9bfdc6e170b4a66:/tasklib/task.py?ds=inline diff --git a/tasklib/task.py b/tasklib/task.py index aa26729..0f8d963 100644 --- a/tasklib/task.py +++ b/tasklib/task.py @@ -5,6 +5,7 @@ import json import logging import os import pytz +import re import six import sys import subprocess @@ -297,7 +298,7 @@ class TaskResource(SerializingObject): # are not propagated. self._original_data = copy.deepcopy(self._data) - def _update_data(self, data, update_original=False): + def _update_data(self, data, update_original=False, remove_missing=False): """ Low level update of the internal _data dict. Data which are coming as updates should already be serialized. If update_original is True, the @@ -306,6 +307,11 @@ class TaskResource(SerializingObject): self._data.update(dict((key, self._deserialize(key, value)) for key, value in data.items())) + # In certain situations, we want to treat missing keys as removals + if remove_missing: + for key in set(self._data.keys()) - set(data.keys()): + self._data[key] = None + if update_original: self._original_data = copy.deepcopy(self._data) @@ -416,6 +422,18 @@ class Task(TaskResource): """ pass + class ActiveTask(Exception): + """ + Raised when the operation cannot be performed on the active task. + """ + pass + + class InactiveTask(Exception): + """ + Raised when the operation cannot be performed on an inactive task. + """ + pass + class NotSaved(Exception): """ Raised when the operation cannot be performed on the task, because @@ -458,7 +476,8 @@ class Task(TaskResource): # If this is a on-modify event, we are provided with additional # line of input, which provides updated data if modify: - task._update_data(json.loads(input_file.readline().strip())) + task._update_data(json.loads(input_file.readline().strip()), + remove_missing=True) return task @@ -588,12 +607,29 @@ class Task(TaskResource): raise Task.CompletedTask("Cannot start a completed task") elif self.deleted: raise Task.DeletedTask("Deleted task cannot be started") + elif self.active: + raise Task.ActiveTask("Task is already active") self.warrior.execute_command([self['uuid'], 'start']) # Refresh the status again, so that we have updated info stored self.refresh(only_fields=['status', 'start']) + def stop(self): + if not self.saved: + raise Task.NotSaved("Task needs to be saved before it can be stopped") + + # Refresh, and raise exception if task is already completed/deleted + self.refresh(only_fields=['status']) + + if not self.active: + raise Task.InactiveTask("Cannot stop an inactive task") + + self.warrior.execute_command([self['uuid'], 'stop']) + + # Refresh the status again, so that we have updated info stored + self.refresh(only_fields=['status', 'start']) + def done(self): if not self.saved: raise Task.NotSaved("Task needs to be saved before it can be completed") @@ -606,6 +642,10 @@ class Task(TaskResource): elif self.deleted: raise Task.DeletedTask("Deleted task cannot be completed") + # Older versions of TW do not stop active task at completion + if self.warrior.version < VERSION_2_4_0 and self.active: + self.stop() + self.warrior.execute_command([self['uuid'], 'done']) # Refresh the status again, so that we have updated info stored @@ -910,7 +950,24 @@ class TaskWarrior(object): stdout, stderr = [x.decode('utf-8') for x in p.communicate()] return stdout.strip('\n') - def execute_command(self, args, config_override={}, allow_failure=True): + def get_config(self): + raw_output = self.execute_command( + ['show'], + config_override={'verbose': 'nothing'} + ) + + config = dict() + config_regex = re.compile(r'^(?P[_a-z\.]+)\s+(?P[^\s].+$)') + + for line in raw_output: + match = config_regex.match(line) + if match: + config[match.group('key')] = match.group('value').strip() + + return config + + def execute_command(self, args, config_override={}, allow_failure=True, + return_all=False): command_args = self._get_command_args( args, config_override=config_override) logger.debug(' '.join(command_args)) @@ -923,7 +980,14 @@ class TaskWarrior(object): else: error_msg = stdout.strip() raise TaskWarriorException(error_msg) - return stdout.rstrip().split('\n') + + # Return all whole triplet only if explicitly asked for + if not return_all: + return stdout.rstrip().split('\n') + else: + return (stdout.rstrip().split('\n'), + stderr.rstrip().split('\n'), + p.returncode) def enforce_recurrence(self): # Run arbitrary report command which will trigger generation