Skip to content

Commit

Permalink
Preserve all annotation information should we have it, but still hand…
Browse files Browse the repository at this point in the history
…le outgoing and incoming values as if they were strings.
  • Loading branch information
coddingtonbear committed Apr 15, 2014
1 parent ef3aca6 commit e1f4972
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 6 deletions.
69 changes: 65 additions & 4 deletions taskw/fields/annotationarray.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
import sys

from dateutil.parser import parse
import six

from .array import ArrayField
from .base import DirtyableList


class Annotation(object):
""" A special type of string that we'll use for storing annotations.
This is, for all intents and purposes, really just a string, but
it does allow us to store additional information if we have it -- in
this application: the annotation's entry date.
"""
def __init__(self, description, entry=None):
self._description = description
self._entry = entry
super(Annotation, self).__init__()

@property
def entry(self):
if self._entry:
return parse(self._entry)
return self._entry

def __str__(self):
if six.PY3:
return six.text_type(self._description)
return self.__unicode__().encode(sys.getdefaultencoding())

def __eq__(self, other):
value = other
if isinstance(other, Annotation):
value = other._description
return self._description == value

def __ne__(self, other):
value = other
if isinstance(other, Annotation):
value = other._description
return self._description != value

def __unicode__(self):
return six.text_type(self._description)

def __repr__(self):
return repr(six.text_type(self._description))


class AnnotationArrayField(ArrayField):
""" A special case of the ArrayField handling idiosyncrasies of Annotations
Taskwarrior will currently return to you a dictionary of values --
the annotation's date and description -- for each annotation, but
given that we cannot create annotations with a date, let's pretend
that they aren't giving us a date. It'll simplify things a bit.
given that we cannot create annotations with a date, let's instead
return something that behaves like a string (but from which you can
extract an entry date if one exists).
"""
def deserialize(self, value):
Expand All @@ -18,8 +67,20 @@ def deserialize(self, value):
elements = []
for annotation in value:
if isinstance(annotation, dict):
elements.append(annotation['description'])
elements.append(
Annotation(
annotation['description'],
annotation['entry'],
)
)
else:
elements.append(annotation)
elements.append(Annotation(annotation))

return super(AnnotationArrayField, self).deserialize(elements)

def serialize(self, value):
if not value:
value = []
return super(AnnotationArrayField, self).serialize(
[six.text_type(entry) for entry in value]
)
79 changes: 77 additions & 2 deletions taskw/test/test_fields.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,93 @@
import datetime
import sys
import uuid

from dateutil.tz import tzlocal
from pytz import UTC, timezone
import six

from taskw import fields
from taskw.fields.annotationarray import Annotation

if sys.version_info >= (3, ):
if six.PY3:
from unittest import TestCase
else:
from unittest2 import TestCase


class TestAnnotationArrayField(TestCase):
def setUp(self):
self.field = fields.AnnotationArrayField()

def test_serialize_none(self):
actual_result = self.field.serialize(None)
expected_result = []

self.assertEqual(actual_result, expected_result)

def test_serialize_annotations_into_strings(self):
value = [
Annotation("something", "20240101T010101Z"),
Annotation("something else")
]

expected_serialized = ["something", "something else"]
actual_serialized = self.field.serialize(value)

self.assertEqual(actual_serialized, expected_serialized)
for entry in actual_serialized:
self.assertTrue(
isinstance(entry, six.text_type)
)

def test_deserialize_fully_formed_entries_to_stringey_things(self):
# Note that this test is *identical* in conditions and actions
# to the below, but we are asserting that we can extract treat
# the returned entries just as if they were strings.
value = [
{
'description': 'Coddingtonbear\'s birthday',
'entry': '19840302T000000Z'
},
{
'description': 'Coddingtonbear\'s partner\'s birthday',
'entry': '19850711T000000Z',
}
]

expected_results = [
value[0]['description'],
value[1]['description'],
]
actual_results = self.field.deserialize(value)

self.assertEqual(expected_results, actual_results)

def test_deserialize_fully_formed_entries_to_annotation_objects(self):
# Note that this test is *identical* in conditions and actions
# to the above, but we are asserting that we can extract a little
# bit more information from the returned objects.
value = [
{
'description': 'Coddingtonbear\'s birthday',
'entry': '19840302T000000Z'
},
{
'description': 'Coddingtonbear\'s partner\'s birthday',
'entry': '19850711T000000Z',
}
]

expected_results = [
Annotation(value[0]['description'], value[0]['entry']),
Annotation(value[1]['description'], value[1]['entry']),
]
actual_results = self.field.deserialize(value)

self.assertEqual(expected_results, actual_results)
self.assertEqual(expected_results[0].entry.year, 1984)
self.assertEqual(expected_results[1].entry.year, 1985)


class TestArrayField(TestCase):
def setUp(self):
self.field = fields.ArrayField()
Expand Down

0 comments on commit e1f4972

Please sign in to comment.