3 commits - pykolab/xml tests/functional tests/unit wallace/module_resources.py

Thomas Brüderli bruederli at kolabsys.com
Wed Feb 26 19:12:21 CET 2014


 pykolab/xml/event.py                                          |   30 +++++-----
 tests/functional/test_wallace/test_005_resource_invitation.py |   25 ++++----
 tests/unit/test-003-event.py                                  |   12 ++++
 wallace/module_resources.py                                   |   25 ++++++--
 4 files changed, 59 insertions(+), 33 deletions(-)

New commits:
commit c1153b7c7214af35d7057f93592bb08d7de63f88
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 20 06:09:15 2014 -0500

    Add the resource's common name when delegating; allow customized subject and text for iTip replies

diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index 4f3ca0a..2dea819 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -153,25 +153,28 @@ class Event(object):
         elif hasattr(cal, 'as_string'):
             return cal.as_string()
 
-    def delegate(self, delegators, delegatees):
+    def delegate(self, delegators, delegatees, names=None):
         if not isinstance(delegators, list):
             delegators = [delegators]
 
         if not isinstance(delegatees, list):
             delegatees = [delegatees]
 
+        if not isinstance(names, list):
+            names = [names]
+
         _delegators = []
         for delegator in delegators:
             _delegators.append(self.get_attendee(delegator))
 
         _delegatees = []
 
-        for delegatee in delegatees:
+        for i,delegatee in enumerate(delegatees):
             try:
                 _delegatees.append(self.get_attendee(delegatee))
             except:
                 # TODO: An iTip needs to be sent out to the new attendee
-                self.add_attendee(delegatee)
+                self.add_attendee(delegatee, names[i] if i < len(names) else None)
                 _delegatees.append(self.get_attendee(delegatee))
 
         for delegator in _delegators:
@@ -916,7 +919,7 @@ class Event(object):
 
         return msg
 
-    def to_message_itip(self, from_address, method="REQUEST", participant_status="ACCEPTED"):
+    def to_message_itip(self, from_address, method="REQUEST", participant_status="ACCEPTED", subject=None, message_text=None):
         from email.MIMEMultipart import MIMEMultipart
         from email.MIMEBase import MIMEBase
         from email.MIMEText import MIMEText
@@ -979,19 +982,19 @@ class Event(object):
 
         msg['Date'] = formatdate(localtime=True)
 
-        # TODO: Should allow for localization
-        text = utils.multiline_message("""
-                    This is a response to one of your event requests.
-            """)
+        if subject is None:
+            subject = _("Reservation Request for %s was %s") % (self.get_summary(), participant_status)
 
-        msg.attach( MIMEText(text) )
+        msg["Subject"] = subject
+
+        if message_text is None:
+            message_text = _("""This is an automated response to one of your event requests.""")
+
+        msg.attach(MIMEText(utils.multiline_message(message_text)))
 
         part = MIMEBase('text', 'calendar', charset='UTF-8', method=method)
         del part['MIME-Version']  # mime parts don't need this
 
-        # TODO: Should allow for localization
-        msg["Subject"] = "Meeting Request %s" % (participant_status)
-
         part.set_payload(self.as_string_itip(method=method))
 
         part.add_header('Content-Disposition', 'attachment; filename="event.ics"')
diff --git a/tests/functional/test_wallace/test_005_resource_invitation.py b/tests/functional/test_wallace/test_005_resource_invitation.py
index 780ca0f..8d6803d 100644
--- a/tests/functional/test_wallace/test_005_resource_invitation.py
+++ b/tests/functional/test_wallace/test_005_resource_invitation.py
@@ -52,7 +52,7 @@ DTEND;TZID=Europe/London:%s
 SUMMARY:test
 DESCRIPTION:test
 ORGANIZER;CN="Doe, John":mailto:john.doe at example.org
-ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s
+ATTENDEE;ROLE=REQ-PARTICIPANT;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s
 TRANSP:OPAQUE
 END:VEVENT
 END:VCALENDAR
@@ -94,7 +94,7 @@ SEQUENCE:2
 SUMMARY:test
 DESCRIPTION:test
 ORGANIZER;CN="Doe, John":mailto:john.doe at example.org
-ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s
+ATTENDEE;ROLE=REQ-PARTICIPANT;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s
 TRANSP:OPAQUE
 END:VEVENT
 END:VCALENDAR
@@ -305,7 +305,7 @@ class TestResourceInvitation(unittest.TestCase):
     def test_002_invite_resource(self):
         uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,7,13, 10,0,0))
 
-        response = self.check_message_received("Meeting Request ACCEPTED", self.audi['mail'])
+        response = self.check_message_received("Reservation Request for test was ACCEPTED", self.audi['mail'])
         self.assertIsInstance(response, email.message.Message)
 
         event = self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid)
@@ -316,7 +316,7 @@ class TestResourceInvitation(unittest.TestCase):
     def test_003_invite_resource_conflict(self):
         uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,7,13, 12,0,0))
 
-        response = self.check_message_received("Meeting Request DECLINED", self.audi['mail'])
+        response = self.check_message_received("Reservation Request for test was DECLINED", self.audi['mail'])
         self.assertIsInstance(response, email.message.Message)
 
         self.assertEqual(self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid), None)
@@ -328,29 +328,30 @@ class TestResourceInvitation(unittest.TestCase):
         uid = self.send_itip_invitation(self.cars['mail'], datetime.datetime(2014,7,13, 12,0,0))
 
         # one of the collection members accepted the reservation
-        accept = self.check_message_received("Meeting Request ACCEPTED")
+        accept = self.check_message_received("Reservation Request for test was ACCEPTED")
         self.assertIsInstance(accept, email.message.Message)
-        self.assertIn(accept['from'], [ self.passat['mail'], self.boxter['mail'] ])
+
+        delegatee = self.passat if accept['from'].find(self.passat['mail']) >= 0 else self.boxter
+        self.assertIn(delegatee['mail'], accept['from'])
 
         # check booking in the delegatee's resource calendar
-        delegatee = self.passat if accept['from'] == self.passat['mail'] else self.boxter
         self.assertIsInstance(self.check_resource_calendar_event(delegatee['kolabtargetfolder'], uid), pykolab.xml.Event)
 
-        # resource collection respons with a DELEGATED message
-        response = self.check_message_received("Meeting Request DELEGATED", self.cars['mail'])
+        # resource collection responds with a DELEGATED message
+        response = self.check_message_received("Reservation Request for test was DELEGATED", self.cars['mail'])
         self.assertIsInstance(response, email.message.Message)
 
 
     def test_005_rescheduling_reservation(self):
         uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,5,1, 10,0,0))
 
-        response = self.check_message_received("Meeting Request ACCEPTED", self.audi['mail'])
+        response = self.check_message_received("Reservation Request for test was ACCEPTED", self.audi['mail'])
         self.assertIsInstance(response, email.message.Message)
 
         self.purge_mailbox(self.john['mailbox'])
         self.send_itip_update(self.audi['mail'], uid, datetime.datetime(2014,5,1, 12,0,0)) # conflict with myself
 
-        response = self.check_message_received("Meeting Request ACCEPTED", self.audi['mail'])
+        response = self.check_message_received("Reservation Request for test was ACCEPTED", self.audi['mail'])
         self.assertIsInstance(response, email.message.Message)
 
         event = self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid)
@@ -371,5 +372,5 @@ class TestResourceInvitation(unittest.TestCase):
         # make new reservation to the now free'd slot
         self.send_itip_invitation(self.boxter['mail'], datetime.datetime(2014,5,1, 9,0,0))
 
-        response = self.check_message_received("Meeting Request ACCEPTED", self.boxter['mail'])
+        response = self.check_message_received("Reservation Request for test was ACCEPTED", self.boxter['mail'])
         self.assertIsInstance(response, email.message.Message)
diff --git a/wallace/module_resources.py b/wallace/module_resources.py
index dfd419a..b718ac4 100644
--- a/wallace/module_resources.py
+++ b/wallace/module_resources.py
@@ -358,7 +358,7 @@ def execute(*args, **kw):
                         # - delegator: the original resource collection
                         # - delegatee: the target resource
                         #
-                        itip_event['xml'].delegate(original_resource['mail'], _target_resource['mail'])
+                        itip_event['xml'].delegate(original_resource['mail'], _target_resource['mail'], _target_resource['cn'])
 
                         accept_reservation_request(itip_event, _target_resource, original_resource)
                         done = True
@@ -822,6 +822,7 @@ def send_response(from_address, itip_events):
     for itip_event in itip_events:
         attendee = itip_event['xml'].get_attendee_by_email(from_address)
         participant_status = itip_event['xml'].get_ical_attendee_participant_status(attendee)
+        message_text = None
 
         if participant_status == "DELEGATED":
             # Extra actions to take
@@ -834,9 +835,14 @@ def send_response(from_address, itip_events):
             # restore list of attendees after to_message_itip()
             itip_event['xml']._attendees = [ delegator, delegatee ]
             itip_event['xml'].event.setAttendees(itip_event['xml']._attendees)
+
             participant_status = "DELEGATED"
+            message_text = _("""
+                Your reservation request was delegated to "%s"
+                which is available for the requested time.
+            """) % (delegatee.get_name())
 
-        message = itip_event['xml'].to_message_itip(from_address, method="REPLY", participant_status=participant_status)
+        message = itip_event['xml'].to_message_itip(from_address, method="REPLY", participant_status=participant_status, message_text=message_text)
         smtp.sendmail(message['From'], message['To'], message.as_string())
 
     smtp.quit()


commit 4d460abbc06e0cc54f0a0b285708102829b618cc
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 20 05:59:00 2014 -0500

    Make sure the iCal version of an event has a valid dtstamp property

diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index 482b1bc..4f3ca0a 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -404,9 +404,8 @@ class Event(object):
         return self.get_end()
 
     def get_ical_dtstamp(self):
-        return
         try:
-            retval = self.event.lastModified()
+            retval = self.get_lastmodified()
             if retval == None or retval == "":
                 return datetime.datetime.now()
         except:
diff --git a/tests/unit/test-003-event.py b/tests/unit/test-003-event.py
index 59f131d..b90333a 100644
--- a/tests/unit/test-003-event.py
+++ b/tests/unit/test-003-event.py
@@ -135,5 +135,17 @@ END:VCALENDAR
         self.assertEqual(event.get_attendee_by_email("max at imum.com").get_cutype(), kolabformat.CutypeResource)
         self.assertEqual(event.get_sequence(), 2)
 
+    def test_019_as_string_itip(self):
+        self.event.set_summary("test")
+        self.event.set_start(datetime.datetime(2014, 05, 23, 11, 00, 00, tzinfo=pytz.timezone("Europe/London")))
+        self.event.set_end(datetime.datetime(2014, 05, 23, 12, 30, 00, tzinfo=pytz.timezone("Europe/London")))
+
+        ical = icalendar.Calendar.from_ical(self.event.as_string_itip())
+        event = ical.walk('VEVENT')[0]
+
+        self.assertEqual(event['uid'], self.event.get_uid())
+        self.assertEqual(event['summary'], "test")
+        self.assertIsInstance(event['dtstamp'].dt, datetime.datetime)
+
 if __name__ == '__main__':
     unittest.main()


commit cffe8974b83342773fd28b5ce14540e34a88cf64
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 20 03:37:09 2014 -0500

    Minor fixes for passing unit tests

diff --git a/wallace/module_resources.py b/wallace/module_resources.py
index 84447c2..dfd419a 100644
--- a/wallace/module_resources.py
+++ b/wallace/module_resources.py
@@ -560,7 +560,7 @@ def itip_events_from_message(message):
         # The iTip part MUST be Content-Type: text/calendar (RFC 6047, section 2.4)
         # But in real word, other mime-types are used as well
         if part.get_content_type() in [ "text/calendar", "text/x-vcalendar", "application/ics" ]:
-            if not part.get_param('method').upper() in itip_methods:
+            if not str(part.get_param('method')).upper() in itip_methods:
                 log.error(
                         _("Method %r not really interesting for us.") % (
                                 part.get_param('method')
@@ -664,9 +664,12 @@ def resource_record_from_email_address(email_address):
     """
         Resolves the given email address to a resource entity
     """
+    global auth
+
+    if not auth:
+        auth = Auth()
+        auth.connect()
 
-    auth = Auth()
-    auth.connect()
     resource_records = []
 
     log.debug(
@@ -696,9 +699,11 @@ def resource_records_from_itip_events(itip_events, recipient_email=None):
         Given a list of itip_events, determine which resources have been
         invited as attendees and/or resources.
     """
+    global auth
 
-    auth = Auth()
-    auth.connect()
+    if not auth:
+        auth = Auth()
+        auth.connect()
 
     resource_records = []
 




More information about the commits mailing list