[CalendarServer-changes] [1677] CalendarClientSimulator/trunk/src

source_changes at macosforge.org source_changes at macosforge.org
Sat Jul 14 18:42:54 PDT 2007


Revision: 1677
          http://trac.macosforge.org/projects/calendarserver/changeset/1677
Author:   cdaboo at apple.com
Date:     2007-07-14 18:42:53 -0700 (Sat, 14 Jul 2007)

Log Message:
-----------
Now does iTIP process and dyanmically generates iCalendar data using the pycalendar library.

Modified Paths:
--------------
    CalendarClientSimulator/trunk/src/calendarclient.py

Added Paths:
-----------
    CalendarClientSimulator/trunk/src/icalutils.py

Modified: CalendarClientSimulator/trunk/src/calendarclient.py
===================================================================
--- CalendarClientSimulator/trunk/src/calendarclient.py	2007-07-15 01:42:05 UTC (rev 1676)
+++ CalendarClientSimulator/trunk/src/calendarclient.py	2007-07-15 01:42:53 UTC (rev 1677)
@@ -18,7 +18,7 @@
 
 from xml.etree import ElementTree
 from random import randint
-import datetime
+import icalutils
 import uuid
 import os
 import plistlib
@@ -57,56 +57,8 @@
 </C:calendar-multiget>
 """
 
-CALDATA = """BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-PRODID:-//example.com//Calendar Client//EN
-VERSION:2.0
-BEGIN:VTIMEZONE
-LAST-MODIFIED:20040110T032845Z
-TZID:US/Eastern
-BEGIN:DAYLIGHT
-DTSTART:20000404T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20001026T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-DTSTAMP:%(dtstamp)s
-DTSTART;TZID=US/Eastern:%(dtstart)s
-DURATION:PT1H
-SUMMARY:%(summary)s
-UID:%(uid)s
-%(otherprops)sEND:VEVENT
-END:VCALENDAR
-"""
+PLIST_VERSION = "1"
 
-ATTENDEE_NEEDSACTION = "ATTENDEE;PARTSTAT=NEEDS-ACTION:%s"
-
-FREEBUSYDATA = """BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-PRODID:-//example.com//Calendar Client//EN
-VERSION:2.0
-METHOD:REQUEST
-BEGIN:VFREEBUSY
-DTSTAMP:%(dtstamp)s
-DTSTART:%(dtstart)s
-DTEND:%(dtend)s
-SUMMARY:%(summary)s
-UID:%(uid)s
-%(otherprops)sEND:VFREEBUSY
-END:VCALENDAR
-"""
-
-
 def unq(s):
     if s[0] == '"' and s[-1] == '"':
         return s[1:-1]
@@ -129,6 +81,9 @@
         self.server = None
         self.user = None
         self.password = None
+        #self.interval = 15 / 60.0
+        #self.eventsperday = 10 * 24 * 60
+        #self.invitesperday = 5 * 24 * 60
         self.interval = 15
         self.eventsperday = 10
         self.invitesperday = 5
@@ -136,14 +91,12 @@
         self.clearcache = False
         self.verbose = False
         self.data = {
+            "version":PLIST_VERSION,
             "ctags": {
                 "calendar":"",
                 "inbox":""
             },
-            "calendar_data": {
-            "calendar":{},
-            "inbox":{}
-            }
+            "calendar_data": {}
         }
 
     def valid(self):
@@ -195,10 +148,10 @@
             time.sleep(CalendarClient.sleep)
     
     def doPoll(self):
-        self.log("Polling for user %s" % (self.user,))
-        status, headers, data = self.doRequest(self.home, "PROPFIND", {"Content-Type": "application/xml", "Depth":"1"}, PROPFIND_ctag)
+        self.log("Polling: %s" % (self.user,))
+        status, _ignore_headers, data = self.doRequest(self.home, "PROPFIND", {"Content-Type": "application/xml", "Depth":"1"}, PROPFIND_ctag)
         if status != 207:
-            self.log("Polling failed with status: %d for user: %s" % (status, self.user,))
+            self.log("Polling: %s failed: %d" % (self.user, status,))
             return
             
         # Parse the XML to find changed ctags
@@ -218,25 +171,25 @@
             if ctag and calname in ["calendar", "inbox"]:
                 if self.data["ctags"][calname] != ctag:
                     changed.append((href, calname, ctag))
-                    self.log("Detected a change in calendar: %s for user: %s" % (href, self.user,))
+                    self.log("  Calendar changed: %s" % (href,))
                     
         # Now do Depth:1 poll of changed calendars
         did_change = False
         for href, calname, ctag in changed:
             if calname == "calendar":
-                self.doFullCalendarPoll(href, calname)
+                self.doFullCalendarPoll(href)
                 self.data["ctags"][calname] = ctag
                 did_change = True
             elif calname == "inbox":
-                self.doInboxPoll(href, calname)
+                self.doInboxPoll(href)
                 self.data["ctags"][calname] = ctag
                 did_change = True
 
         if did_change:
             self.writeCache()
     
-    def doFullCalendarPoll(self, uri, calname):
-        self.log("Polling calendar: %s for user %s looking for new data" % (uri, self.user,))
+    def doFullCalendarPoll(self, uri):
+        self.log("    Getting calendar: %s for: %s" % (uri, self.user,))
         
         # Get href/etag map
         hrefs = self.doPropfindDepth1(uri)
@@ -245,179 +198,171 @@
             
         # Find new ones
         server_hrefs = set(hrefs.keys())
-        local_hrefs = set(self.data["calendar_data"][calname].keys())
+        local_hrefs = set(self.data["calendar_data"].keys())
         new_hrefs = server_hrefs.difference(local_hrefs)
         
         # Delete old ones
         delete_hrefs = local_hrefs.difference(server_hrefs)
         for href in delete_hrefs:
-            del self.data["calendar_data"][calname][href]
+            del self.data["calendar_data"][href]
 
         # Find changed ones
         changed = set()
-        for href, (etag, data) in self.data["calendar_data"][calname].iteritems():
-            if hrefs[href] != etag:
+        for href, entry in self.data["calendar_data"].iteritems():
+            if hrefs[href] != entry["etag"]:
                 changed.add(href)
                 
         # Now do multiget of all new/changed items
+        if len(changed) or len(new_hrefs):
+            self.log("      Changed: %d  New: %d" % (len(changed), len(new_hrefs)))
         changed.update(new_hrefs)
         if changed:
-            results = self.doMultiget(uri, calname, changed)
+            results = self.doMultiget(uri, changed)
             
             # Cache the new etags - we don't care about the data
             for href, (etag, data) in results.iteritems():
-                 self.data["calendar_data"][calname][href] = (etag, data)
+                self.data["calendar_data"][href] = {
+                    "etag": etag,
+                    "uid" : icalutils.parseUID(data),
+                    "data": data,
+                }
 
-    def doInboxPoll(self, uri, calname):
+    def doInboxPoll(self, uri):
+        self.log("    Getting calendar: %s for: %s" % (uri, self.user,))
 
         # Get href/etag map
         hrefs = self.doPropfindDepth1(uri)
-        if hrefs is None:
+        if not hrefs:
             return
         
         # Process each new item
         for href in hrefs:
-            self.doProcessiTIP(href)
+            data = self.getEvent(href)
+            if data:
+                self.doProcessiTIP(data)
 
-    def doProcessiTIP(self, href):
+            # Always delete the inbox item
+            self.doDelete(href)
+
+    def doProcessiTIP(self, caldata):
         
         # Get the data
-        
-        # Determine method
-        pass
+        calendar = icalutils.parseCalendar(caldata)
+        if calendar:        
+            # Determine method
+            method = calendar.getMethod()
+            if method == "REQUEST":
+                self.processiTIPRequest(calendar)
+            elif method == "REPLY":
+                self.processiTIPReply(calendar)
     
-    def processiTIPRequest(self, href, data):
-        
+    def processiTIPRequest(self, calendar):
+        organizer = icalutils.getORGANZIER(calendar)
+        self.log("      Processing iTIP Request for:  %s from: %s" % (self.user, organizer.split("/")[-2],))
+       
+        # Generate data for this user
+        me = "/principals/users/%s/" % (self.user,)
+
         # Generate accepted data and write to main calendar
+        caldata = icalutils.generateiTIPSave(calendar, me)
+        self.createEvent(caldata)
         
         # Generate reply and POST
-        pass
+        self.log("        Sending  iTIP Reply   from: %s to:   %s" % (self.user, organizer.split("/")[-2],))
+        caldata, recipient = icalutils.generateiTIPReply(calendar, me)
+        self.doSchedule(self.outbox, caldata, headers={"originator":me, "recipient":recipient})
 
-    def processiTIPReply(self, href, data):
-        
+    def processiTIPReply(self, calendar):
+        # Extract attendee from iTIP reply
+        attendee = icalutils.getATTENDEE(calendar)
+        self.log("      Processing iTIP Reply   to:   %s from: %s" % (self.user, attendee.split("/")[-2],))
+               
         # Find matching UID on calendar
+        uid = icalutils.getUID(calendar)
+        for href, entry in self.data["calendar_data"].iteritems():
+            if entry["uid"] == uid:
+                break
+        else:
+            return
         
         # Update calendar data with attendee status
-        pass
+        caldata = icalutils.updateAttendeeStatus(entry["data"], attendee)
+        self.putEvent(href, caldata)
 
-    def doCreateEvent(self, otherprops=""):
-        self.log("Creating event for user %s" % (self.user,))
-        
-        nowutc = datetime.datetime.utcnow()
-        now = datetime.datetime.now()
-        
-        dtstamp = "%d%02d%02dT%02d%02d%02dZ" % (
-            nowutc.year,
-            nowutc.month,
-            nowutc.day,
-            nowutc.hour,
-            nowutc.minute,
-            nowutc.second
-        )
-        dtstart = "%d%02d%02dT%02d%02d%02d" % (
-            now.year,
-            now.month,
-            now.day,
-            now.hour,
-            now.minute,
-            0
-        )
-        summary = "Event at %s" % (now,)
-        uid = uuid.uuid4()
-        caldata = CALDATA % {
-            "dtstamp":dtstamp,
-            "dtstart":dtstart,
-            "summary":summary,
-            "uid":uid,
-            "otherprops":otherprops
-        }
-        uri = "%s%s.ics" % (self.calendar, uid,)
+    def doCreateEvent(self, organizer=None, attendees=None):
+        caldata = icalutils.generateVEVENT(organizer, attendees)
+        return self.createEvent(caldata)
 
-        status, headers, data = self.doRequest(uri, "PUT", {"Content-Type": "text/calendar"}, caldata)
-        if status != 201:
-            self.log("Event creation failed with status: %d for user: %s" % (status, self.user,))
+    def createEvent(self, caldata):
+        uri = "%s%s.ics" % (self.calendar, uuid.uuid4(),)
+        self.putEvent(uri, caldata, True)
+        return uri
+
+    def putEvent(self, uri, caldata, creating=False):
+        status, _ignore_headers, _ignore_data = self.doRequest(uri, "PUT", {"Content-Type": "text/calendar"}, caldata)
+        if creating and (status != 201) or not creating and status not in (200, 204):
+            self.log("Event write failed with status: %d for user: %s" % (status, self.user,))
             return
         
         status, headers, data = self.doRequest(uri, "GET")
         if status != 200:
-            self.log("Event creation (read) failed with status: %d for user: %s" % (status, self.user,))
+            self.log("Event read failed with status: %d for user: %s" % (status, self.user,))
             return
         for header, value in headers:
             if header == "etag":
                 etag = unq(value)
-                self.data["calendar_data"]["calendar"][uri] = (etag, data,)
+                self.data["calendar_data"][uri] = {
+                    "etag": etag,
+                    "uid" : icalutils.parseUID(data),
+                    "data": data,
+                }
+                self.writeCache()
                 break
-            
-        return uri
 
-    def doInvite(self):
-        self.log("Sending invite from user %s" % (self.user,))
+    def getEvent(self, uri):
         
+        status, _ignore_headers, data = self.doRequest(uri, "GET")
+        if status != 200:
+            self.log("Event read of %s failed with status: %d for user: %s" % (uri, status, self.user,))
+            return None
+
+        return data
+
+    def generateInviteAttendees(self):
+        attendees = []
+#        for _ignore_ctr in range(randint(1, 10)):
+#            attendee = "user%02d" % randint(1,99)
+#            while attendee == self.user:
+#                attendee = "user%02d" % randint(1,99)
+#            attendees.append("/principals/users/%s/" % (attendee,))
+        
+        if self.user == "user01":
+            attendees.append("/principals/users/user02/")
+        elif self.user == "user02":
+            attendees.append("/principals/users/user01/")
+        return attendees
+
+    def doInvite(self):
         # Generate data for this user
-        organizer = "ORGANIZER:/principals/users/%s/" % (self.user,)
-        attendee_me = "ATTENDEE;PARTSTAT=ACCEPTED:/principals/users/%s/" % (self.user,)
-        originator = "/principals/users/%s/" % (self.user,)
+        organizer = "/principals/users/%s/" % (self.user,)
 
         # Generate a set of users
-        users = []
-        for ctr in range(randint(1, 10)):
-            user = "user%02d" % randint(1,99)
-            while user == self.user:
-                user = "user%02d" % randint(1,99)
-            users.append("/principals/users/%s/" % (user,))
-        
+        attendees = self.generateInviteAttendees()
+        self.log("Sending invite from: %s to:   %s" % (self.user, ", ".join([attendee.split("/")[-2] for attendee in attendees])))
+                
         # Create invite on main calendar
-        attendees = "\n".join([ATTENDEE_NEEDSACTION % user for user in users]) + "\n"
-        new_uri = self.doCreateEvent(organizer + "\n" + attendees)
+        new_uri = self.doCreateEvent(organizer, attendees)
 
         # Do free busy lookup
-        nowutc = datetime.datetime.utcnow()
-        now = datetime.datetime.now().replace(hour=0, minute=0, second=0)
-        now = now + datetime.timedelta(hours=5)
-        nextday = now + datetime.timedelta(days=1)
-        
-        dtstamp = "%d%02d%02dT%02d%02d%02dZ" % (
-            nowutc.year,
-            nowutc.month,
-            nowutc.day,
-            nowutc.hour,
-            nowutc.minute,
-            nowutc.second
-        )
-        dtstart = "%d%02d%02dT%02d%02d%02dZ" % (
-            now.year,
-            now.month,
-            now.day,
-            0,
-            0,
-            0
-        )
-        dtend = "%d%02d%02dT%02d%02d%02dZ" % (
-            nextday.year,
-            nextday.month,
-            nextday.day,
-            0,
-            0,
-            0
-        )
-        summary = "Free busy at %s" % (now,)
-        uid = uuid.uuid4()
-        fbdata = FREEBUSYDATA % {
-            "dtstamp":dtstamp,
-            "dtstart":dtstart,
-            "dtend":dtend,
-            "summary":summary,
-            "uid":uid,
-            "otherprops":organizer + "\n" + attendees + attendee_me + "\n"
-        }
-        self.doSchedule(self.outbox, fbdata, headers={"originator":originator, "recipient":", ".join((originator,) + tuple(users))})
+        fbdata = icalutils.generateVFREEBUSY(organizer, attendees)
+        self.doSchedule(self.outbox, fbdata, headers={"originator":organizer, "recipient":", ".join((organizer,) + tuple(attendees))})
 
         # TODO: Update invite (simulates a time shift after f-b lookup)
         
         # Do outbox POST
-        _ignore_etag, caldata = self.data["calendar_data"]["calendar"][new_uri]
-        caldata = caldata.replace("VERSION:", "METHOD:REQUEST\nVERSION:")
-        self.doSchedule(self.outbox, caldata, headers={"originator":originator, "recipient":", ".join(users)})
+        entry = self.data["calendar_data"][new_uri]
+        caldata = entry["data"].replace("VERSION:", "METHOD:REQUEST\nVERSION:")
+        self.doSchedule(self.outbox, caldata, headers={"originator":organizer, "recipient":", ".join(attendees)})
 
     def doRequest(self, ruri, method='GET', headers={}, data=None):
         
@@ -441,7 +386,7 @@
         return response.status, response.getheaders(), data
 
     def doPropfindDepth1(self, uri):
-        status, headers, data = self.doRequest(uri, "PROPFIND", {"Content-Type": "application/xml", "Depth": "1"}, PROPFIND_etag)
+        status, _ignore_headers, data = self.doRequest(uri, "PROPFIND", {"Content-Type": "application/xml", "Depth": "1"}, PROPFIND_etag)
         if status != 207:
             self.log("Polling failed with status: %d for user: %s" % (status, self.user,))
             return None
@@ -465,10 +410,9 @@
                 
         return hrefs
 
-    def doMultiget(self, uri, calname, hrefs):
-        self.log("Getting %d resources in calendar: %s for user %s" % (len(hrefs), uri, self.user,))
+    def doMultiget(self, uri, hrefs):
         hreftxt = "\n".join(["<D:href>%s</D:href>" % href for href in hrefs])
-        status, headers, data = self.doRequest(uri, "REPORT", {"Content-Type": "application/xml"}, REPORT_multiget % (hreftxt,))
+        status, _ignore_headers, data = self.doRequest(uri, "REPORT", {"Content-Type": "application/xml"}, REPORT_multiget % (hreftxt,))
         if status != 207:
             self.log("Polling failed with status: %d for user: %s" % (status, self.user,))
             return
@@ -496,17 +440,21 @@
         return results
 
     def doSchedule(self, uri, data, headers):
-        self.log("Scheduling POST for user %s" % (self.user,))
         headers["Content-Type"] = "text/calendar"
         status, headers, data = self.doRequest(uri, "POST", headers, data)
         if status != 200:
-            self.log("Polling failed with status: %d for user: %s" % (status, self.user,))
+            self.log("POST failed with status: %d for user: %s" % (status, self.user,))
             return
             
         # Parse the XML to find etags
         results = {}
         xml = ElementTree.XML(data)
-        for response in xml.getiterator("{urn:ietf:params:xml:ns:caldav}response"):
+        for _ignore_response in xml.getiterator("{urn:ietf:params:xml:ns:caldav}response"):
             pass
             
         return results
+
+    def doDelete(self, uri):
+        status, _ignore_headers, _ignore_data = self.doRequest(uri, "DELETE")
+        if status != 204:
+            self.log("Delete of %s failed with status: %d for user: %s" % (uri, status, self.user,))

Added: CalendarClientSimulator/trunk/src/icalutils.py
===================================================================
--- CalendarClientSimulator/trunk/src/icalutils.py	                        (rev 0)
+++ CalendarClientSimulator/trunk/src/icalutils.py	2007-07-15 01:42:53 UTC (rev 1677)
@@ -0,0 +1,194 @@
+##
+# Copyright (c) 2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# DRI: Cyrus Daboo, cdaboo at apple.com
+##
+
+from pycalendar import definitions
+from pycalendar import itipdefinitions
+from pycalendar.calendar import PyCalendar
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.property import PyCalendarProperty
+from pycalendar.value import PyCalendarValue
+from pycalendar.vfreebusy import PyCalendarVFreeBusy
+import cStringIO as StringIO
+from pycalendar.vevent import PyCalendarVEvent
+from pycalendar.attribute import PyCalendarAttribute
+import datetime
+
+def parseCalendar(caldata):
+
+    cal = PyCalendar()
+    if cal.parse(StringIO.StringIO(caldata)):
+        return cal
+    else:
+        return None
+
+def parseUID(caldata):
+
+    calendar = parseCalendar(caldata)
+    return getUID(calendar)
+
+def getUID(calendar):
+
+    for item in calendar.getVEventsDB():
+        return item.getUID()
+    else:
+        return None
+
+def getORGANZIER(calendar):
+
+    for item in calendar.getVEventsDB():
+        return item.getPropertyString(definitions.cICalProperty_ORGANIZER)
+
+    return None
+
+def getATTENDEE(calendar):
+
+    for item in calendar.getVEventsDB():
+        return item.getPropertyString(definitions.cICalProperty_ATTENDEE)
+
+    return None
+
+def generateVEVENT(organizer=None, attendees=None):
+    
+    now = datetime.datetime.now()
+
+    cal = PyCalendar()
+    
+    vev = PyCalendarVEvent(calendar=cal.getRef())
+    
+    start = PyCalendarDateTime(year=now.year, month=now.month, day=now.day, hours=now.hour, minutes=now.minute, seconds=0)
+    end = PyCalendarDateTime(copyit=start)
+    end.offsetHours(1)
+    vev.editTimingStartEnd(start, end)
+
+    if organizer:
+        prop = PyCalendarProperty(definitions.cICalProperty_ORGANIZER, organizer, PyCalendarValue.VALUETYPE_CALADDRESS)
+        vev.addProperty(prop)
+        prop = PyCalendarProperty(definitions.cICalProperty_ATTENDEE, organizer, PyCalendarValue.VALUETYPE_CALADDRESS)
+        prop.addAttribute(PyCalendarAttribute(definitions.cICalAttribute_PARTSTAT, definitions.cICalAttribute_PARTSTAT_ACCEPTED))
+        vev.addProperty(prop)
+
+    if attendees:
+        for attendee in attendees:
+            prop = PyCalendarProperty(definitions.cICalProperty_ATTENDEE, attendee, PyCalendarValue.VALUETYPE_CALADDRESS)
+            prop.addAttribute(PyCalendarAttribute(definitions.cICalAttribute_PARTSTAT, definitions.cICalAttribute_PARTSTAT_NEEDSACTION))
+            prop.addAttribute(PyCalendarAttribute(definitions.cICalAttribute_RSVP, definitions.cICalAttribute_RSVP_TRUE))
+            vev.addProperty(prop)
+
+    vev.setUID("")
+
+    vev.initDTSTAMP()
+    
+    cal.addComponent(vev)
+
+    os = StringIO.StringIO()
+    cal.generate(os)
+    return os.getvalue().replace("\n", "\r\n")
+
+def generateVFREEBUSY(organizer, attendees):
+    
+    now = datetime.datetime.now()
+
+    cal = PyCalendar()
+    cal.addProperty(PyCalendarProperty(definitions.cICalProperty_METHOD, itipdefinitions.cICalMethod_REQUEST))
+    
+    vfb = PyCalendarVFreeBusy(calendar=cal.getRef())
+    
+    start = PyCalendarDateTime(year=now.year, month=now.month, day=now.day, hours=0, minutes=0, seconds=0)
+    end = PyCalendarDateTime(copyit=start)
+    end.offsetDay(1)
+    vfb.editTimingStartEnd(start, end)
+    
+
+    prop = PyCalendarProperty(definitions.cICalProperty_ORGANIZER, organizer, PyCalendarValue.VALUETYPE_CALADDRESS)
+    vfb.addProperty(prop)
+    prop = PyCalendarProperty(definitions.cICalProperty_ATTENDEE, organizer, PyCalendarValue.VALUETYPE_CALADDRESS)
+    vfb.addProperty(prop)
+
+    for attendee in attendees:
+        prop = PyCalendarProperty(definitions.cICalProperty_ATTENDEE, attendee, PyCalendarValue.VALUETYPE_CALADDRESS)
+        vfb.addProperty(prop)
+
+    vfb.setUID("")
+
+    vfb.initDTSTAMP()
+    
+    cal.addComponent(vfb)
+
+    os = StringIO.StringIO()
+    cal.generate(os)
+    return os.getvalue().replace("\n", "\r\n")
+
+def generateiTIPSave(calendar, me):
+    # Generate accepted data and write to main calendar
+    calendar.removeProperties(definitions.cICalProperty_METHOD)
+    for vev in calendar.getComponents(PyCalendar.VEVENT).mItems.itervalues():
+        break
+    else:
+        return
+    attendees = vev.getProperties()[definitions.cICalProperty_ATTENDEE]
+    for attendee in attendees:
+        if attendee.getTextValue().getValue() == me:
+            attendee.removeAttributes(definitions.cICalAttribute_PARTSTAT)
+            attendee.removeAttributes(definitions.cICalAttribute_RSVP)
+            attendee.addAttribute(PyCalendarAttribute(definitions.cICalAttribute_PARTSTAT, definitions.cICalAttribute_PARTSTAT_ACCEPTED))
+
+    os = StringIO.StringIO()
+    calendar.generate(os)
+    return os.getvalue().replace("\n", "\r\n")
+
+def generateiTIPReply(calendar, me):
+    # Generate accepted data and write to main calendar
+    calendar.removeProperties(definitions.cICalProperty_METHOD)
+    calendar.addProperty(PyCalendarProperty(definitions.cICalProperty_METHOD, itipdefinitions.cICalMethod_REPLY))
+
+    for vev in calendar.getComponents(PyCalendar.VEVENT).mItems.itervalues():
+        break
+    else:
+        return
+    attendees = vev.getProperties()[definitions.cICalProperty_ATTENDEE]
+    for attendee in attendees:
+        if attendee.getTextValue().getValue() == me:
+            break
+    vev.removeProperties(definitions.cICalProperty_ATTENDEE)
+    vev.addProperty(attendee)
+    organizers = vev.getProperties()[definitions.cICalProperty_ORGANIZER]
+    organizer = organizers[0].getTextValue().getValue()
+
+    os = StringIO.StringIO()
+    calendar.generate(os)
+    return os.getvalue().replace("\n", "\r\n"), organizer
+
+def updateAttendeeStatus(caldata, update):
+
+    calendar = parseCalendar(caldata)
+
+    for vev in calendar.getComponents(PyCalendar.VEVENT).mItems.itervalues():
+        break
+    else:
+        return
+    attendees = vev.getProperties()[definitions.cICalProperty_ATTENDEE]
+    for attendee in attendees:
+        if attendee.getTextValue().getValue() == update:
+            attendee.removeAttributes(definitions.cICalAttribute_PARTSTAT)
+            attendee.removeAttributes(definitions.cICalAttribute_RSVP)
+            attendee.addAttribute(PyCalendarAttribute(definitions.cICalAttribute_PARTSTAT, definitions.cICalAttribute_PARTSTAT_ACCEPTED))
+            break
+
+    os = StringIO.StringIO()
+    calendar.generate(os)
+    return os.getvalue().replace("\n", "\r\n")

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070714/61018831/attachment.html


More information about the calendarserver-changes mailing list