def _load_data(self, data):
self._data = data
# We need to use a copy for original data, so that changes
- # are not propagated
+ # are not propagated. Shallow copy is alright, since data dict uses only
+ # primitive data types
self._original_data = data.copy()
+ def _update_data(self, data, update_original=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
+ original_data dict is updated as well.
+ """
+
+ self._data.update(data)
+
+ if update_original:
+ self._original_data.update(data)
+
def __getitem__(self, key):
# This is a workaround to make TaskResource non-iterable
# over simple index-based iteration
class Task(TaskResource):
- read_only_fields = ['id', 'entry', 'urgency', 'uuid']
+ read_only_fields = ['id', 'entry', 'urgency', 'uuid', 'modified']
class DoesNotExist(Exception):
pass
"""
pass
- def __init__(self, warrior, data={}, **kwargs):
+ def __init__(self, warrior, **kwargs):
self.warrior = warrior
# We serialize the data in kwargs so that users of the library
self._load_data(dict((key, self._serialize(key, value))
for (key, value) in six.iteritems(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']
@property
def _modified_fields(self):
- for key in self._data.keys():
+ 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
removed = old_dependencies - self['depends']
# Removed dependencies need to be prefixed with '-'
- return ','.join(
+ return 'depends:' + ','.join(
[t['uuid'] for t in added] +
['-' + t['uuid'] for t in removed]
)
def serialize_tags(self, tags):
return ','.join(tags) if tags else ''
+ def format_description(self):
+ # Task version older than 2.4.0 ignores first word of the
+ # task description if description: prefix is used
+ if self.warrior.version < VERSION_2_4_0:
+ return self._data['description']
+ else:
+ return "description:'{0}'".format(self._data['description'] or '')
+
def delete(self):
if not self.saved:
raise Task.NotSaved("Task needs to be saved before it can be deleted")
args = []
def add_field(field):
- # Task version older than 2.4.0 ignores first word of the
- # 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:
- # Use empty string to substitute for None value
- args.append('{0}:{1}'.format(field, self._data[field] or ''))
+ # Add the output of format_field method to args list (defaults to
+ # field:value)
+ format_default = lambda k: "{0}:'{1}'".format(k, self._data[k] or '')
+ format_func = getattr(self, 'format_{0}'.format(field),
+ lambda: format_default(field))
+ args.append(format_func())
# If we're modifying saved task, simply pass on all modified fields
if self.saved:
if only_fields:
to_update = dict(
[(k, new_data.get(k)) for k in only_fields])
- self._data.update(to_update)
- self._original_data.update(to_update)
+ self._update_data(to_update, update_original=True)
else:
- self._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()
+ self._load_data(new_data)
class TaskFilter(object):
if line:
data = line.strip(',')
try:
- tasks.append(Task(self, json.loads(data)))
+ filtered_task = Task(self)
+ filtered_task._load_data(json.loads(data))
+ tasks.append(filtered_task)
except ValueError:
raise TaskWarriorException('Invalid JSON: %s' % data)
return tasks