+ return dehydrate_func(value)
+
+ def __str__(self):
+ s = six.text_type(self.__unicode__())
+ if not six.PY3:
+ s = s.encode('utf-8')
+ return s
+
+ def __repr__(self):
+ return str(self)
+
+
+class TaskAnnotation(TaskResource):
+ read_only_fields = ['entry', 'description']
+
+ def __init__(self, task, data={}):
+ self.task = task
+ self._load_data(data)
+
+ def deserialize_entry(self, data):
+ return datetime.datetime.strptime(data, DATE_FORMAT) if data else None
+
+ def serialize_entry(self, date):
+ return date.strftime(DATE_FORMAT) if date else ''
+
+ def remove(self):
+ self.task.remove_annotation(self)
+
+ def __unicode__(self):
+ return self['description']
+
+ __repr__ = __unicode__
+
+
+class Task(TaskResource):
+ read_only_fields = ['id', 'entry', 'urgency', 'uuid', 'modified']
+
+ class DoesNotExist(Exception):
+ pass
+
+ class CompletedTask(Exception):
+ """
+ Raised when the operation cannot be performed on the completed task.
+ """
+ pass
+
+ class DeletedTask(Exception):
+ """
+ Raised when the operation cannot be performed on the deleted task.
+ """
+ pass
+
+ class NotSaved(Exception):
+ """
+ Raised when the operation cannot be performed on the task, because
+ it has not been saved to TaskWarrior yet.
+ """
+ pass
+
+ def __init__(self, warrior, **kwargs):
+ self.warrior = warrior
+
+ # Check that user is not able to set read-only value in __init__
+ for key in kwargs.keys():
+ if key in self.read_only_fields:
+ raise RuntimeError('Field \'%s\' is read-only' % key)
+
+ # We serialize the data in kwargs so that users of the library
+ # do not have to pass different data formats via __setitem__ and
+ # __init__ methods, that would be confusing
+
+ # Rather unfortunate syntax due to python2.6 comaptiblity
+ self._load_data(dict((key, self._serialize(key, value))
+ for (key, value) in six.iteritems(kwargs)))
+
+ def __unicode__(self):
+ return self['description']
+
+ def __eq__(self, other):
+ if self['uuid'] and other['uuid']:
+ # For saved Tasks, just define equality by equality of uuids
+ return self['uuid'] == other['uuid']
+ else:
+ # If the tasks are not saved, compare the actual instances
+ return id(self) == id(other)
+
+
+ def __hash__(self):
+ if self['uuid']:
+ # For saved Tasks, just define equality by equality of uuids
+ return self['uuid'].__hash__()
+ else:
+ # If the tasks are not saved, return hash of instance id
+ return id(self).__hash__()
+
+ @property
+ def _modified_fields(self):
+ writable_fields = set(self._data.keys()) - set(self.read_only_fields)
+ for key in writable_fields:
+ if self._data.get(key) != self._original_data.get(key):
+ yield key
+
+ @property
+ def completed(self):
+ return self['status'] == six.text_type('completed')
+
+ @property
+ def deleted(self):
+ return self['status'] == six.text_type('deleted')
+
+ @property
+ def waiting(self):
+ return self['status'] == six.text_type('waiting')
+
+ @property
+ def pending(self):
+ return self['status'] == six.text_type('pending')
+
+ @property
+ def saved(self):
+ return self['uuid'] is not None or self['id'] is not None