2 commits - pykolab/itip pykolab/xml tests/unit

Thomas Brüderli bruederli at kolabsys.com
Wed Feb 18 17:18:44 CET 2015


 pykolab/itip/__init__.py     |   16 +++++++++++++++-
 pykolab/xml/event.py         |   42 ++++++++++++++++++++++++++++++++++++++----
 tests/unit/test-003-event.py |   24 ++++++++++++++++++++++++
 tests/unit/test-011-itip.py  |   17 +++++++++++++++++
 4 files changed, 94 insertions(+), 5 deletions(-)

New commits:
commit e25e91e5c23621c53e1c0034cf5d3c42e939363e
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Tue Feb 17 09:43:42 2015 +0100

    Add utility methods to update attendees with propagation to recurrence exceptions

diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index a054053..325cbc5 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -150,10 +150,14 @@ class Event(object):
             exception._load_attendees()
             self._exceptions.append(exception)
 
-    def add_attendee(self, email, name=None, rsvp=False, role=None, participant_status=None, cutype="INDIVIDUAL", params=None):
-        attendee = Attendee(email, name, rsvp, role, participant_status, cutype, params)
-        self._attendees.append(attendee)
-        self.event.setAttendees(self._attendees)
+    def add_attendee(self, email_or_attendee, name=None, rsvp=False, role=None, participant_status=None, cutype="INDIVIDUAL", params=None):
+        if isinstance(email_or_attendee, Attendee):
+            attendee = email_or_attendee
+        else:
+            attendee = Attendee(email_or_attendee, name, rsvp, role, participant_status, cutype, params)
+
+        # apply update to self and all exceptions
+        self.update_attendees([attendee])
 
     def add_category(self, category):
         self._categories.append(ustr(category))
@@ -695,6 +699,14 @@ class Event(object):
     def get_transparency(self):
         return self.event.transparency()
 
+    def set_attendees(self, _attendees):
+        self._attendees = _attendees
+        self.event.setAttendees(self._attendees)
+
+        # apply update to all exceptions
+        for exception in self._exceptions:
+            exception.merge_attendee_data(_attendees)
+
     def set_attendee_participant_status(self, attendee, status, rsvp=None):
         """
             Set the participant status of an attendee to status.
@@ -709,6 +721,28 @@ class Event(object):
         if rsvp is not None:
             attendee.set_rsvp(rsvp)
 
+        # apply update to self and all exceptions
+        self.update_attendees([attendee])
+
+    def update_attendees(self, _attendees):
+        self.merge_attendee_data(_attendees)
+
+        for exception in self._exceptions:
+            exception.merge_attendee_data(_attendees)
+
+    def merge_attendee_data(self, _attendees):
+        for attendee in _attendees:
+            found = False
+
+            for candidate in self._attendees:
+                if candidate.get_email() == attendee.get_email():
+                    candidate.copy_from(attendee)
+                    found = True
+                    break
+
+            if not found:
+                self._attendees.append(attendee)
+
         self.event.setAttendees(self._attendees)
 
     def set_classification(self, classification):
diff --git a/tests/unit/test-003-event.py b/tests/unit/test-003-event.py
index 042d56a..1d0ad10 100644
--- a/tests/unit/test-003-event.py
+++ b/tests/unit/test-003-event.py
@@ -404,6 +404,13 @@ class TestEventXML(unittest.TestCase):
     def test_009_invalid_participant_status(self):
         self.assertRaises(InvalidAttendeeParticipantStatusError, self.event.set_attendee_participant_status, "jane at doe.org", "INVALID")
 
+    def test_009_update_attendees(self):
+        jane = self.event.get_attendee("jane at doe.org")
+        jane.set_name("Jane (GI) Doe")
+        self.event.update_attendees([jane])
+        self.assertEqual(len(self.event.get_attendees()), 2)
+        self.assertEqual(self.event.get_attendee("jane at doe.org").get_name(), "Jane (GI) Doe")
+
     def test_010_datetime_from_string(self):
         self.assertRaises(InvalidEventDateError, self.event.set_start, "2012-05-23 11:58:00")
 
@@ -842,6 +849,23 @@ END:VEVENT
         self.assertEqual(property_to_string('attach', data['attach'][0]), "noname.1395223627.5555")
 
 
+    def test_027_merge_attendee_data(self):
+        event = event_from_string(xml_event)
+
+        jane = event.get_attendee("jane at example.org")
+        jane.set_participant_status('TENTATIVE')
+        jack = Attendee("jack at example.org", name="Jack", role='OPT-PARTICIPANT')
+
+        # update jane + add jack
+        event.update_attendees([jane,jack])
+        self.assertEqual(len(event.get_attendees()), 3)
+        self.assertEqual(event.get_attendee("jane at example.org").get_participant_status(), kolabformat.PartTentative)
+
+        exception = event.get_exceptions()[0]
+        self.assertEqual(len(exception.get_attendees()), 2)
+        self.assertEqual(event.get_attendee("jane at example.org").get_participant_status(), kolabformat.PartTentative)
+        self.assertEqual(event.get_attendee("jack at example.org").get_name(), "Jack")
+
     def _find_prop_in_list(self, diff, name):
         for prop in diff:
             if prop['property'] == name:


commit 28cff75a01981c596051129e7b7f5f3a14781d62
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Tue Feb 17 07:16:37 2015 +0100

    Take recurrence exceptions into accout when checking for conflicts

diff --git a/pykolab/itip/__init__.py b/pykolab/itip/__init__.py
index cae8075..848b2d7 100644
--- a/pykolab/itip/__init__.py
+++ b/pykolab/itip/__init__.py
@@ -166,12 +166,26 @@ def check_event_conflict(kolab_event, itip_event):
         while not conflict and _is is not None:
             # log.debug("* Comparing event dates at %s/%s with %s/%s" % (_es, _ee, _is, _ie), level=9)
             conflict = check_date_conflict(_es, _ee, _is, _ie)
-            _is = to_dt(itip_event['xml'].get_next_occurence(_is)) if kolab_event.is_recurring() else None
+            _is = to_dt(itip_event['xml'].get_next_occurence(_is)) if itip_event['xml'].is_recurring() else None
             _ie = to_dt(itip_event['xml'].get_occurence_end_date(_is))
 
+            # get full occurrence to compare the dates from a possible exception
+            if _is is not None and len(itip_event['xml'].get_exceptions()):
+                _ix = itip_event['xml'].get_instance(_is)
+                if _ix is not None:
+                    _is = to_dt(_ix.get_start())
+                    _ie = to_dt(_ix.get_end())
+
         _es = to_dt(kolab_event.get_next_occurence(_es)) if kolab_event.is_recurring() else None
         _ee = to_dt(kolab_event.get_occurence_end_date(_es))
 
+        # get full instance to compare the dates from a possible exception
+        if _es is not None and len(kolab_event.get_exceptions()):
+            _ex = kolab_event.get_instance(_es)
+            if _ex is not None:
+                _es = to_dt(_ex.get_start())
+                _ee = to_dt(_ex.get_end())
+
     return conflict
 
 
diff --git a/tests/unit/test-011-itip.py b/tests/unit/test-011-itip.py
index e5edee5..dafa645 100644
--- a/tests/unit/test-011-itip.py
+++ b/tests/unit/test-011-itip.py
@@ -451,6 +451,23 @@ class TestITip(unittest.TestCase):
         event4.set_end(datetime.datetime(2012,7,1, 10,30,0, tzinfo=pytz.utc))
         self.assertFalse(itip.check_event_conflict(event4, itip_event), "No conflict in two recurring events")
 
+        itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0]
+
+        rrule.setFrequency(kolabformat.RecurrenceRule.Daily)
+        rrule.setCount(10)
+
+        event5 = Event()
+        event5.set_recurrence(rrule);
+        event5.set_start(datetime.datetime(2012,7,9, 10,0,0, tzinfo=pytz.timezone("Europe/London")))
+        event5.set_end(datetime.datetime(2012,7,9, 11,0,0, tzinfo=pytz.timezone("Europe/London")))
+
+        exception = Event(from_string=str(event5))
+        exception.set_start(datetime.datetime(2012,7,13, 14,0,0, tzinfo=pytz.timezone("Europe/London")))
+        exception.set_end(datetime.datetime(2012,7,13, 16,0,0, tzinfo=pytz.timezone("Europe/London")))
+        exception.set_recurrence_id(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London")), False)
+        event5.add_exception(exception)
+        self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with exception date")
+
 
     def test_003_send_reply(self):
         itip_events = itip.events_from_message(message_from_string(itip_non_multipart))




More information about the commits mailing list