[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