except ValueError:
pass
- hydrate_func = getattr(self, 'deserialize_{0}'.format(key),
- lambda x: x)
- return hydrate_func(self._data.get(key))
+ return self._deserialize(key, self._data.get(key))
def __setitem__(self, key, value):
if key in self.read_only_fields:
raise RuntimeError('Field \'%s\' is read-only' % key)
+ self._data[key] = self._serialize(key, value)
+
+ def _deserialize(self, key, value):
+ hydrate_func = getattr(self, 'deserialize_{0}'.format(key),
+ lambda x: x)
+ return hydrate_func(value)
+
+ def _serialize(self, key, value):
dehydrate_func = getattr(self, 'serialize_{0}'.format(key),
lambda x: x)
- self._data[key] = dehydrate_func(value)
+ return dehydrate_func(value)
def __str__(self):
s = six.text_type(self.__unicode__())
def __init__(self, warrior, data={}, **kwargs):
self.warrior = warrior
- # We keep data for backwards compatibility
- kwargs.update(data)
+ # 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)))
- self._load_data(kwargs)
+ # We keep data for backwards compatibility
+ # TODO: Should we keep this using unserialized access to _data dict?
+ self._data.update(data)
def __unicode__(self):
return self['description']
def __eq__(self, other):
- return self['uuid'] == other['uuid']
+ 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):
- return self['uuid'].__hash__()
+ 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):
return self['uuid'] is not None or self['id'] is not None
def serialize_due(self, date):
+ if not date:
+ return None
return date.strftime(DATE_FORMAT)
def deserialize_due(self, date_str):
return None
return datetime.datetime.strptime(date_str, DATE_FORMAT)
+ def serialize_depends(self, cur_dependencies):
+ # Check that all the tasks are saved
+ for task in cur_dependencies:
+ if not task.saved:
+ raise Task.NotSaved('Task \'%s\' needs to be saved before '
+ 'it can be set as dependency.' % task)
+
+ # Return the list of uuids
+ return ','.join(task['uuid'] for task in cur_dependencies)
+
+ def deserialize_depends(self, raw_uuids):
+ raw_uuids = raw_uuids or '' # Convert None to empty string
+ uuids = raw_uuids.split(',')
+ return set(self.warrior.tasks.get(uuid=uuid) for uuid in uuids if uuid)
+
+ def format_depends(self):
+ # We need to generate added and removed dependencies list,
+ # since Taskwarrior does not accept redefining dependencies.
+
+ # This cannot be part of serialize_depends, since we need
+ # to keep a list of all depedencies in the _data dictionary,
+ # not just currently added/removed ones
+
+ old_dependencies_raw = self._original_data.get('depends','')
+ old_dependencies = self.deserialize_depends(old_dependencies_raw)
+
+ added = self['depends'] - old_dependencies
+ removed = old_dependencies - self['depends']
+
+ # Removed dependencies need to be prefixed with '-'
+ return ','.join(
+ [t['uuid'] for t in added] +
+ ['-' + t['uuid'] for t in removed]
+ )
+
def deserialize_annotations(self, data):
return [TaskAnnotation(self, d) for d in data] if data else []
# task description if description: prefix is used
if self.warrior.version < VERSION_2_4_0 and field == 'description':
args.append(self._data[field])
+ elif field == 'depends':
+ args.append('{0}:{1}'.format(field, self.format_depends()))
else:
- args.append('{0}:{1}'.format(field, self._data[field]))
+ # Use empty string to substitute for None value
+ args.append('{0}:{1}'.format(field, self._data[field] or ''))
# If we're modifying saved task, simply pass on all modified fields
if self.saved: