def _load_data(self, data):
self._data = data
- self._original_data = data
+ # We need to use a copy for original data, so that changes
+ # are not propagated
+ self._original_data = data.copy()
def __getitem__(self, key):
+ # This is a workaround to make TaskResource non-iterable
+ # over simple index-based iteration
+ try:
+ int(key)
+ raise StopIteration
+ except ValueError:
+ pass
+
hydrate_func = getattr(self, 'deserialize_{0}'.format(key),
lambda x: x)
return hydrate_func(self._data.get(key))
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 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]))
self._original_data.update(to_update)
else:
self._data = new_data
- self._original_data = new_data
+ # We need to create a clone for original_data though
+ # Shallow copy is alright, since data dict uses only
+ # primitive data types
+ self._original_data = new_data.copy()
class TaskFilter(object):