pykolab/itip pykolab/xml tests/unit

Thomas Brüderli bruederli at kolabsys.com
Thu Jul 24 13:16:09 CEST 2014


 pykolab/itip/__init__.py         |    2 
 pykolab/xml/__init__.py          |    2 
 pykolab/xml/attendee.py          |   50 +++++++
 pykolab/xml/contact.py           |    4 
 pykolab/xml/contact_reference.py |   21 +++
 pykolab/xml/event.py             |   72 +++++++++++
 pykolab/xml/recurrence_rule.py   |  117 ++++++++++++++++++
 tests/unit/test-002-attendee.py  |   20 +++
 tests/unit/test-003-event.py     |  244 ++++++++++++++++++++++++++-------------
 9 files changed, 446 insertions(+), 86 deletions(-)

New commits:
commit 524849338fcb0cb40bcdb18f4dbe7e9660074f20
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jul 23 08:23:49 2014 -0400

    Add methods to dump Kolab XML objects as dict()

diff --git a/pykolab/itip/__init__.py b/pykolab/itip/__init__.py
index 17da24e..816ee1d 100644
--- a/pykolab/itip/__init__.py
+++ b/pykolab/itip/__init__.py
@@ -144,6 +144,8 @@ def check_event_conflict(kolab_event, itip_event):
     if kolab_event.uid == itip_event['uid']:
         return conflict
 
+    # TODO: don't consider conflict if event has TRANSP:TRANSPARENT
+
     _es = to_dt(kolab_event.get_start())
     _ee = to_dt(kolab_event.get_ical_dtend())  # use iCal style end date: next day for all-day events
 
diff --git a/pykolab/xml/__init__.py b/pykolab/xml/__init__.py
index 2bb42d4..3e12716 100644
--- a/pykolab/xml/__init__.py
+++ b/pykolab/xml/__init__.py
@@ -4,6 +4,7 @@ from attendee import participant_status_label
 
 from contact import Contact
 from contact_reference import ContactReference
+from recurrence_rule import RecurrenceRule
 
 from event import Event
 from event import EventIntegrityError
@@ -19,6 +20,7 @@ __all__ = [
         "Contact",
         "ContactReference",
         "Event",
+        "RecurrenceRule",
         "event_from_ical",
         "event_from_string",
         "to_dt",
diff --git a/pykolab/xml/attendee.py b/pykolab/xml/attendee.py
index 5d469c2..7921280 100644
--- a/pykolab/xml/attendee.py
+++ b/pykolab/xml/attendee.py
@@ -56,6 +56,13 @@ class Attendee(kolabformat.Attendee):
             "FALSE": False,
         }
 
+    properties_map = {
+            'role': 'get_role',
+            'rsvp':  'rsvp',
+            'partstat':  'get_participant_status',
+            'cutype':   'get_cutype',
+        }
+
     def __init__(
             self,
             email,
@@ -97,6 +104,12 @@ class Attendee(kolabformat.Attendee):
         if not participant_status == None:
             self.set_participant_status(participant_status)
 
+    def copy_from(self, obj):
+        if isinstance(obj, kolabformat.Attendee):
+            kolabformat.Attendee.__init__(self, obj)
+            self.contactreference = ContactReference(obj.contact())
+            self.email = self.contactreference.get_email()
+
     def delegate_from(self, delegators):
         crefs = []
 
@@ -138,8 +151,11 @@ class Attendee(kolabformat.Attendee):
 
         self.setDelegatedTo(list(set(crefs)))
 
-    def get_cutype(self):
-        return self.cutype()
+    def get_cutype(self, translated=False):
+        cutype = self.cutype()
+        if translated:
+            return self._translate_value(cutype, self.cutype_map)
+        return cutype
 
     def get_delegated_from(self):
         return self.delegatedFrom()
@@ -161,16 +177,22 @@ class Attendee(kolabformat.Attendee):
     def get_participant_status(self, translated=False):
         partstat = self.partStat()
         if translated:
-            partstat_name_map = dict([(v, k) for (k, v) in self.participant_status_map.iteritems()])
-            return partstat_name_map[partstat] if partstat_name_map.has_key(partstat) else 'UNKNOWN'
+            return self._translate_value(partstat, self.participant_status_map)
         return partstat
 
-    def get_role(self):
-        return self.role()
+    def get_role(self, translated=False):
+        role = self.role()
+        if translated:
+            return self._translate_value(role, self.role_map)
+        return role
 
     def get_rsvp(self):
         return self.rsvp()
 
+    def _translate_value(self, val, map):
+        name_map = dict([(v, k) for (k, v) in map.iteritems()])
+        return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
+
     def set_cutype(self, cutype):
         if cutype in self.cutype_map.keys():
             self.setCutype(self.cutype_map[cutype])
@@ -202,6 +224,22 @@ class Attendee(kolabformat.Attendee):
     def set_rsvp(self, rsvp):
         self.setRSVP(rsvp)
 
+    def to_dict(self):
+        data = self.contactreference.to_dict()
+        data.pop('type', None)
+
+        for p, getter in self.properties_map.iteritems():
+            val = None
+            args = {}
+            if hasattr(self, getter):
+                if getter.startswith('get_'):
+                    args = dict(translated=True)
+                val = getattr(self, getter)(**args)
+            if val is not None:
+                data[p] = val
+
+        return data
+
     def __str__(self):
         return self.email
 
diff --git a/pykolab/xml/contact.py b/pykolab/xml/contact.py
index 1577b58..9a2c103 100644
--- a/pykolab/xml/contact.py
+++ b/pykolab/xml/contact.py
@@ -39,5 +39,9 @@ class Contact(kolabformat.Contact):
     def set_name(self, name):
         self.setName(name)
 
+    def to_ditc(self):
+        # TODO: implement this
+        return dict(name=self.name())
+
     def __str__(self):
         return kolabformat.writeContact(self)
diff --git a/pykolab/xml/contact_reference.py b/pykolab/xml/contact_reference.py
index 0d6dec5..5a832da 100644
--- a/pykolab/xml/contact_reference.py
+++ b/pykolab/xml/contact_reference.py
@@ -11,9 +11,18 @@ import kolabformat
 """
 
 class ContactReference(kolabformat.ContactReference):
+    properties_map = {
+        'email': 'email',
+        'name':  'name',
+        'type':  'type',
+        'uid':   'uid',
+    }
+
     def __init__(self, email=None):
         if email == None:
             kolabformat.ContactReference.__init__(self)
+        elif isinstance(email, kolabformat.ContactReference):
+            kolabformat.ContactReference.__init__(self, email.email(), email.name(), email.uid())
         else:
             kolabformat.ContactReference.__init__(self, email)
 
@@ -31,3 +40,15 @@ class ContactReference(kolabformat.ContactReference):
 
     def set_name(self, name):
         self.setName(name)
+
+    def to_dict(self):
+        data = dict()
+
+        for p, getter in self.properties_map.iteritems():
+            val = None
+            if hasattr(self, getter):
+                val = getattr(self, getter)()
+            if val is not None:
+                data[p] = val
+
+        return data
diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index 4ac4997..8e41a92 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -18,6 +18,7 @@ from pykolab.translate import _
 from os import path
 from attendee import Attendee
 from contact_reference import ContactReference
+from recurrence_rule import RecurrenceRule
 
 log = pykolab.getLogger('pykolab.xml_event')
 
@@ -55,6 +56,37 @@ class Event(object):
             "CONFIDENTIAL": kolabformat.ClassConfidential,
         }
 
+    properties_map = {
+        # property: getter
+        "uid": "get_uid",
+        "created": "get_created",
+        "lastmodified-date": "get_lastmodified",
+        "sequence": "sequence",
+        "classification": "get_classification",
+        "categories": "categories",
+        "start": "get_start",
+        "end": "get_end",
+        "duration": "get_duration",
+        "transparency": "transparency",
+        "rrule": "recurrenceRule",
+        "rdate": "recurrenceDates",
+        "exdate": "exceptionDates",
+        "recurrence-id": "recurrenceID",
+        "summary": "summary",
+        "description": "description",
+        "priority": "priority",
+        "status": "get_status",
+        "location": "location",
+        "organizer": "organizer",
+        "attendee": "get_attendees",
+        "attach": "attachments",
+        "url": "url",
+        "alarm": "alarms",
+        "x-custom": "customProperties",
+        # TODO: add to_dict() support for these
+        # "exception": "exceptions",
+    }
+
     def __init__(self, from_ical="", from_string=""):
         self._attendees = []
         self._categories = []
@@ -271,7 +303,7 @@ class Event(object):
 
     def get_created(self):
         try:
-            return xmlutils.from_cdatetime(self.event.created(), False)
+            return xmlutils.from_cdatetime(self.event.created(), True)
         except ValueError:
             return datetime.datetime.now()
 
@@ -479,7 +511,7 @@ class Event(object):
         except:
             self.__str__()
 
-        return xmlutils.from_cdatetime(self.event.lastModified(), False)
+        return xmlutils.from_cdatetime(self.event.lastModified(), True)
 
     def get_organizer(self):
         organizer = self.event.organizer()
@@ -780,6 +812,42 @@ class Event(object):
         else:
             raise EventIntegrityError, kolabformat.errorMessage()
 
+    def to_dict(self):
+        data = dict()
+
+        for p, getter in self.properties_map.iteritems():
+            val = None
+            if hasattr(self, getter):
+                val = getattr(self, getter)()
+            elif hasattr(self.event, getter):
+                val = getattr(self.event, getter)()
+
+            if isinstance(val, kolabformat.cDateTime):
+                val = xmlutils.from_cdatetime(val, True)
+            elif isinstance(val, kolabformat.vectordatetime):
+                val = [xmlutils.from_cdatetime(x, True) for x in val]
+            elif isinstance(val, kolabformat.vectors):
+                val = [str(x) for x in val]
+            elif isinstance(val, kolabformat.vectorcs):
+                for x in val:
+                    data[x.identifier] = x.value
+                val = None
+            elif isinstance(val, kolabformat.ContactReference):
+                val = ContactReference(val).to_dict()
+            elif isinstance(val, kolabformat.RecurrenceRule):
+                val = RecurrenceRule(val).to_dict()
+            elif isinstance(val, kolabformat.vectorattachment):
+                val = [dict(fmttype=x.mimetype(), label=x.label(), uri=x.uri()) for x in val]
+            elif isinstance(val, kolabformat.vectoralarm):
+                val = [dict(type=x.type()) for x in val]
+            elif isinstance(val, list):
+                val = [x.to_dict() for x in val if hasattr(x, 'to_dict')]
+
+            if val is not None:
+                data[p] = val
+
+        return data
+
     def to_message(self):
         from email.MIMEMultipart import MIMEMultipart
         from email.MIMEBase import MIMEBase
diff --git a/pykolab/xml/recurrence_rule.py b/pykolab/xml/recurrence_rule.py
new file mode 100644
index 0000000..eb17fd5
--- /dev/null
+++ b/pykolab/xml/recurrence_rule.py
@@ -0,0 +1,117 @@
+import kolabformat
+from pykolab.xml import utils as xmlutils
+
+"""
+    def setFrequency(self, *args): return _kolabformat.RecurrenceRule_setFrequency(self, *args)
+    def frequency(self): return _kolabformat.RecurrenceRule_frequency(self)
+    def setWeekStart(self, *args): return _kolabformat.RecurrenceRule_setWeekStart(self, *args)
+    def weekStart(self): return _kolabformat.RecurrenceRule_weekStart(self)
+    def setEnd(self, *args): return _kolabformat.RecurrenceRule_setEnd(self, *args)
+    def end(self): return _kolabformat.RecurrenceRule_end(self)
+    def setCount(self, *args): return _kolabformat.RecurrenceRule_setCount(self, *args)
+    def count(self): return _kolabformat.RecurrenceRule_count(self)
+    def setInterval(self, *args): return _kolabformat.RecurrenceRule_setInterval(self, *args)
+    def interval(self): return _kolabformat.RecurrenceRule_interval(self)
+    def setBysecond(self, *args): return _kolabformat.RecurrenceRule_setBysecond(self, *args)
+    def bysecond(self): return _kolabformat.RecurrenceRule_bysecond(self)
+    def setByminute(self, *args): return _kolabformat.RecurrenceRule_setByminute(self, *args)
+    def byminute(self): return _kolabformat.RecurrenceRule_byminute(self)
+    def setByhour(self, *args): return _kolabformat.RecurrenceRule_setByhour(self, *args)
+    def byhour(self): return _kolabformat.RecurrenceRule_byhour(self)
+    def setByday(self, *args): return _kolabformat.RecurrenceRule_setByday(self, *args)
+    def byday(self): return _kolabformat.RecurrenceRule_byday(self)
+    def setBymonthday(self, *args): return _kolabformat.RecurrenceRule_setBymonthday(self, *args)
+    def bymonthday(self): return _kolabformat.RecurrenceRule_bymonthday(self)
+    def setByyearday(self, *args): return _kolabformat.RecurrenceRule_setByyearday(self, *args)
+    def byyearday(self): return _kolabformat.RecurrenceRule_byyearday(self)
+    def setByweekno(self, *args): return _kolabformat.RecurrenceRule_setByweekno(self, *args)
+    def byweekno(self): return _kolabformat.RecurrenceRule_byweekno(self)
+    def setBymonth(self, *args): return _kolabformat.RecurrenceRule_setBymonth(self, *args)
+    def bymonth(self): return _kolabformat.RecurrenceRule_bymonth(self)
+    def isValid(self): return _kolabformat.RecurrenceRule_isValid(self)
+"""
+
+class RecurrenceRule(kolabformat.RecurrenceRule):
+    frequency_map = {
+        None: kolabformat.RecurrenceRule.FreqNone,
+        "YEARLY": kolabformat.RecurrenceRule.Yearly,
+        "MONTHLY": kolabformat.RecurrenceRule.Monthly,
+        "WEEKLY": kolabformat.RecurrenceRule.Weekly,
+        "DAILY": kolabformat.RecurrenceRule.Daily,
+        "HOURLY": kolabformat.RecurrenceRule.Hourly,
+        "MINUTELY": kolabformat.RecurrenceRule.Minutely,
+        "SECONDLY": kolabformat.RecurrenceRule.Secondly
+    }
+
+    weekday_map = {
+        "MO": kolabformat.Monday,
+        "TU": kolabformat.Tuesday,
+        "WE": kolabformat.Wednesday,
+        "TH": kolabformat.Thursday,
+        "FR": kolabformat.Friday,
+        "SA": kolabformat.Saturday,
+        "SU": kolabformat.Sunday
+    }
+
+    properties_map = {
+        'frequency': 'get_frequency',
+        'interval':  'interval',
+        'count':     'count',
+        'until':     'end',
+        'bymonth':   'bymonth',
+        'byday':     'byday',
+        'byyearday': 'byyearday',
+        'byweekno':  'byweekno',
+        'byhour':    'byhour',
+        'byminute':  'byminute',
+        'wkst':      'get_weekstart'
+    }
+
+    def __init__(self, rrule=None):
+        if rrule == None:
+            kolabformat.RecurrenceRule.__init__(self)
+        else:
+            kolabformat.RecurrenceRule.__init__(self, rrule)
+
+    def get_frequency(self, translated=False):
+        freq = self.frequency()
+        if translated:
+            return self._translate_value(freq, self.frequency_map)
+        return freq
+
+    def get_weekstart(self, translated=False):
+        wkst = self.weekStart()
+        if translated:
+            return self._translate_value(wkst, self.weekday_map)
+        return wkst
+
+    def _translate_value(self, val, map):
+        name_map = dict([(v, k) for (k, v) in map.iteritems()])
+        return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
+
+    def to_dict(self):
+        if not self.isValid() or self.frequency() == kolabformat.RecurrenceRule.FreqNone:
+            return None
+
+        data = dict()
+
+        for p, getter in self.properties_map.iteritems():
+            val = None
+            args = {}
+            if hasattr(self, getter):
+                if getter.startswith('get_'):
+                    args = dict(translated=True)
+            if hasattr(self, getter):
+                val = getattr(self, getter)(**args)
+            if isinstance(val, kolabformat.cDateTime):
+                val = xmlutils.from_cdatetime(val, True)
+            elif isinstance(val, kolabformat.vectori):
+                val = [int(v) for x in val]
+            elif isinstance(val, kolabformat.vectordaypos):
+                val = ["%d%s" % (x.occurence, self._translate_value(x.weekday)) for x in val]
+            if val is not None:
+                data[p] = val
+
+        return data
+
+
diff --git a/tests/unit/test-002-attendee.py b/tests/unit/test-002-attendee.py
index 8bcee3c..d7584e3 100644
--- a/tests/unit/test-002-attendee.py
+++ b/tests/unit/test-002-attendee.py
@@ -108,5 +108,25 @@ class TestEventXML(unittest.TestCase):
         self.assertEqual(participant_status_label(kolabformat.PartTentative), "Tentatively Accepted")
         self.assertEqual(participant_status_label('UNKNOWN'), "UNKNOWN")
 
+    def test_020_to_dict(self):
+        name = "Doe, Jane"
+        role = 'OPT-PARTICIPANT'
+        cutype = 'RESOURCE'
+        partstat = 'ACCEPTED'
+        self.attendee.set_name(name)
+        self.attendee.set_rsvp(True)
+        self.attendee.set_role(role)
+        self.attendee.set_cutype(cutype)
+        self.attendee.set_participant_status(partstat)
+
+        data = self.attendee.to_dict()
+        self.assertIsInstance(data, dict)
+        self.assertEqual(data['role'], role)
+        self.assertEqual(data['cutype'], cutype)
+        self.assertEqual(data['partstat'], partstat)
+        self.assertEqual(data['name'], name)
+        self.assertEqual(data['email'], 'jane at doe.org')
+        self.assertTrue(data['rsvp'])
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/unit/test-003-event.py b/tests/unit/test-003-event.py
index 2c5a478..5017091 100644
--- a/tests/unit/test-003-event.py
+++ b/tests/unit/test-003-event.py
@@ -65,6 +65,136 @@ END:VALARM
 END:VEVENT
 """
 
+xml_event = """
+<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">
+  <vcalendar>
+    <properties>
+      <prodid>
+        <text>Libkolabxml-1.1</text>
+      </prodid>
+      <version>
+        <text>2.0</text>
+      </version>
+      <x-kolab-version>
+        <text>3.1.0</text>
+      </x-kolab-version>
+    </properties>
+    <components>
+      <vevent>
+        <properties>
+          <uid>
+            <text>75c740bb-b3c6-442c-8021-ecbaeb0a025e</text>
+          </uid>
+          <created>
+            <date-time>2014-07-07T01:28:23Z</date-time>
+          </created>
+          <dtstamp>
+            <date-time>2014-07-07T01:28:23Z</date-time>
+          </dtstamp>
+          <sequence>
+            <integer>1</integer>
+          </sequence>
+          <class>
+            <text>PUBLIC</text>
+          </class>
+          <dtstart>
+            <parameters>
+              <tzid>
+                <text>/kolab.org/Europe/London</text>
+              </tzid>
+            </parameters>
+            <date-time>2014-08-13T10:00:00</date-time>
+          </dtstart>
+          <dtend>
+            <parameters>
+              <tzid><text>/kolab.org/Europe/London</text></tzid>
+            </parameters>
+            <date-time>2014-08-13T14:00:00</date-time>
+          </dtend>
+          <rrule>
+            <recur>
+              <freq>DAILY</freq>
+              <until>
+                <date>2014-07-25</date>
+              </until>
+            </recur>
+          </rrule>
+          <exdate>
+            <parameters>
+              <tzid>
+                <text>/kolab.org/Europe/Berlin</text>
+              </tzid>
+            </parameters>
+            <date>2014-07-19</date>
+            <date>2014-07-26</date>
+            <date>2014-07-12</date>
+            <date>2014-07-13</date>
+            <date>2014-07-20</date>
+            <date>2014-07-27</date>
+            <date>2014-07-05</date>
+            <date>2014-07-06</date>
+          </exdate>
+          <summary>
+            <text>test</text>
+          </summary>
+          <description>
+            <text>test</text>
+          </description>
+          <priority>
+            <integer>5</integer>
+          </priority>
+          <status>
+            <text>CANCELLED</text>
+          </status>
+          <location>
+            <text>Room 101</text>
+          </location>
+          <organizer>
+            <parameters>
+              <cn><text>Doe, John</text></cn>
+            </parameters>
+            <cal-address>mailto:%3Cjohn%40example.org%3E</cal-address>
+          </organizer>
+          <attendee>
+            <parameters>
+              <partstat><text>ACCEPTED</text></partstat>
+              <role><text>REQ-PARTICIPANT</text></role>
+              <rsvp><boolean>true</boolean></rsvp>
+            </parameters>
+            <cal-address>mailto:%3Cjane%40example.org%3E</cal-address>
+          </attendee>
+          <attendee>
+            <parameters>
+              <partstat><text>TENTATIVE</text></partstat>
+              <role><text>OPT-PARTICIPANT</text></role>
+            </parameters>
+            <cal-address>mailto:%3Csomebody%40else.com%3E</cal-address>
+          </attendee>
+          <attach>
+            <parameters>
+              <fmttype>
+                <text>text/html</text>
+              </fmttype>
+              <x-label>
+                <text>noname.1395223627.5555</text>
+              </x-label>
+            </parameters>
+            <uri>cid:noname.1395223627.5555</uri>
+          </attach>
+          <x-custom>
+            <identifier>X-MOZ-RECEIVED-DTSTAMP</identifier>
+            <value>20140224T155612Z</value>
+          </x-custom>
+          <x-custom>
+            <identifier>X-GWSHOW-AS</identifier>
+            <value>BUSY</value>
+          </x-custom>
+        </properties>
+      </vevent>
+    </components>
+  </vcalendar>
+</icalendar>
+"""
 
 class TestEventXML(unittest.TestCase):
     event = Event()
@@ -181,7 +311,7 @@ METHOD:REQUEST
         event = event_from_ical(ical.walk('VEVENT')[0].to_ical())
 
         self.assertEqual(event.get_location(), "Location")
-        self.assertEqual(str(event.get_lastmodified()), "2014-04-07 12:23:11")
+        self.assertEqual(str(event.get_lastmodified()), "2014-04-07 12:23:11+00:00")
         self.assertEqual(event.get_description(), "Description\n2 lines")
         self.assertEqual(event.get_url(), "http://somelink.com/foo")
         self.assertEqual(event.get_transparency(), False)
@@ -310,83 +440,7 @@ END:VEVENT
         self.assertEqual(self.event.get_last_occurrence(), None)
 
     def test_022_load_from_xml(self):
-        xml = """
-<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">
-  <vcalendar>
-    <properties>
-      <prodid>
-        <text>Libkolabxml-1.1</text>
-      </prodid>
-      <version>
-        <text>2.0</text>
-      </version>
-      <x-kolab-version>
-        <text>3.1.0</text>
-      </x-kolab-version>
-    </properties>
-    <components>
-      <vevent>
-        <properties>
-          <uid>
-            <text>75c740bb-b3c6-442c-8021-ecbaeb0a025e</text>
-          </uid>
-          <created>
-            <date-time>2014-07-07T01:28:23Z</date-time>
-          </created>
-          <dtstamp>
-            <date-time>2014-07-07T01:28:23Z</date-time>
-          </dtstamp>
-          <sequence>
-            <integer>1</integer>
-          </sequence>
-          <class>
-            <text>PUBLIC</text>
-          </class>
-          <dtstart>
-            <parameters>
-              <tzid>
-                <text>/kolab.org/Europe/London</text>
-              </tzid>
-            </parameters>
-            <date-time>2014-08-13T10:00:00</date-time>
-          </dtstart>
-          <dtend>
-            <parameters>
-              <tzid><text>/kolab.org/Europe/London</text></tzid>
-            </parameters>
-            <date-time>2014-08-13T14:00:00</date-time>
-          </dtend>
-          <summary>
-            <text>test</text>
-          </summary>
-          <organizer>
-            <parameters>
-              <cn><text>Doe, John</text></cn>
-            </parameters>
-            <cal-address>mailto:%3Cjohn%40example.org%3E</cal-address>
-          </organizer>
-          <attendee>
-            <parameters>
-              <partstat><text>ACCEPTED</text></partstat>
-              <role><text>REQ-PARTICIPANT</text></role>
-              <rsvp><boolean>true</boolean></rsvp>
-            </parameters>
-            <cal-address>mailto:%3Cjane%40example.org%3E</cal-address>
-          </attendee>
-          <attendee>
-            <parameters>
-              <partstat><text>TENTATIVE</text></partstat>
-              <role><text>OPT-PARTICIPANT</text></role>
-            </parameters>
-            <cal-address>mailto:%3Csomebody%40else.com%3E</cal-address>
-          </attendee>
-        </properties>
-      </vevent>
-    </components>
-  </vcalendar>
-</icalendar>
-"""
-        event = event_from_string(xml)
+        event = event_from_string(xml_event)
         self.assertEqual(event.uid, '75c740bb-b3c6-442c-8021-ecbaeb0a025e')
         self.assertEqual(event.get_attendee_by_email("jane at example.org").get_participant_status(), kolabformat.PartAccepted)
         self.assertEqual(event.get_sequence(), 1)
@@ -432,6 +486,40 @@ END:VEVENT
         event = event_from_ical(vevent)
         self.assertRaises(EventIntegrityError, event.to_message)
 
+    def test_025_to_dict(self):
+        data = event_from_string(xml_event).to_dict()
+
+        self.assertIsInstance(data, dict)
+        self.assertIsInstance(data['start'], datetime.datetime)
+        self.assertIsInstance(data['end'], datetime.datetime)
+        self.assertIsInstance(data['created'], datetime.datetime)
+        self.assertIsInstance(data['lastmodified-date'], datetime.datetime)
+        self.assertEqual(data['uid'], '75c740bb-b3c6-442c-8021-ecbaeb0a025e')
+        self.assertEqual(data['summary'], 'test')
+        self.assertEqual(data['location'], 'Room 101')
+        self.assertEqual(data['description'], 'test')
+        self.assertEqual(data['priority'], 5)
+        self.assertEqual(data['status'], 'CANCELLED')
+        self.assertEqual(data['sequence'], 1)
+        self.assertEqual(data['transparency'], False)
+        self.assertEqual(data['X-GWSHOW-AS'], 'BUSY')
+
+        self.assertIsInstance(data['organizer'], dict)
+        self.assertEqual(data['organizer']['email'], 'john at example.org')
+
+        self.assertEqual(len(data['attendee']), 2)
+        self.assertIsInstance(data['attendee'][0], dict)
+
+        self.assertEqual(len(data['attach']), 1)
+        self.assertIsInstance(data['attach'][0], dict)
+        self.assertEqual(data['attach'][0]['fmttype'], 'text/html')
+
+        self.assertIsInstance(data['rrule'], dict)
+        self.assertEqual(data['rrule']['frequency'], 'DAILY')
+        self.assertEqual(data['rrule']['interval'], 1)
+        self.assertEqual(data['rrule']['wkst'], 'MO')
+        self.assertIsInstance(data['rrule']['until'], datetime.date)
+
 
 if __name__ == '__main__':
     unittest.main()




More information about the commits mailing list