[CalendarServer-changes] [7862] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Aug 9 12:35:22 PDT 2011


Revision: 7862
          http://trac.macosforge.org/projects/calendarserver/changeset/7862
Author:   cdaboo at apple.com
Date:     2011-08-09 12:35:21 -0700 (Tue, 09 Aug 2011)
Log Message:
-----------
No more vobject dependency.

Modified Paths:
--------------
    CalendarServer/trunk/contrib/performance/_event_create.py
    CalendarServer/trunk/contrib/performance/benchmarks/vfreebusy.py
    CalendarServer/trunk/contrib/performance/loadtest/ical.py
    CalendarServer/trunk/contrib/performance/loadtest/population.py
    CalendarServer/trunk/contrib/performance/loadtest/profiles.py
    CalendarServer/trunk/contrib/performance/loadtest/test_ical.py
    CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
    CalendarServer/trunk/contrib/performance/stats.py
    CalendarServer/trunk/contrib/performance/test_stats.py
    CalendarServer/trunk/support/build.sh
    CalendarServer/trunk/support/patchapply
    CalendarServer/trunk/support/patchmaker
    CalendarServer/trunk/twistedcaldav/ical.py

Removed Paths:
-------------
    CalendarServer/trunk/lib-patches/vobject/

Modified: CalendarServer/trunk/contrib/performance/_event_create.py
===================================================================
--- CalendarServer/trunk/contrib/performance/_event_create.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/_event_create.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -33,7 +33,7 @@
 from httpclient import StringProducer
 
 
-# XXX Represent these as vobjects?  Would make it easier to add more vevents.
+# XXX Represent these as pycalendar objects?  Would make it easier to add more vevents.
 event = """\
 BEGIN:VCALENDAR
 VERSION:2.0
@@ -107,7 +107,7 @@
         rrule = ""
     else:
         rrule = recurrence + "\n"
-    return event % {
+    cal = event % {
         'VEVENTS': vevent % {
             'UID': uid,
             'START': formatDate(start),
@@ -118,6 +118,7 @@
             'RRULE': rrule,
             },
         }
+    return cal.replace("\n", "\r\n")
 
 
 def makeEvent(i, organizerSequence, attendeeCount):

Modified: CalendarServer/trunk/contrib/performance/benchmarks/vfreebusy.py
===================================================================
--- CalendarServer/trunk/contrib/performance/benchmarks/vfreebusy.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/benchmarks/vfreebusy.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -33,7 +33,7 @@
 from contrib.performance.httpclient import StringProducer
 from contrib.performance.benchlib import initialize, sample
 
-# XXX Represent these as vobjects?  Would make it easier to add more vevents.
+# XXX Represent these as pycalendar objects?  Would make it easier to add more vevents.
 event = """\
 BEGIN:VCALENDAR
 VERSION:2.0
@@ -102,7 +102,7 @@
 """
     interval = timedelta(hours=2)
     duration = timedelta(hours=1)
-    return event % {
+    data = event % {
         'VEVENTS': s % {
             'UID': uuid4(),
             'START': formatDate(base + i * interval),
@@ -110,8 +110,8 @@
             'SEQUENCE': i,
             },
         }
+    return data.replace("\n", "\r\n")
 
-
 def makeEvents(base, n):
     return [makeEventNear(base, i) for i in range(n)]
 
@@ -146,13 +146,15 @@
             "content-type": ["text/calendar"],
             "originator": ["mailto:%s at example.com" % (user,)],
             "recipient": ["urn:uuid:%s, urn:uuid:user02" % (user,)]})
-    body = StringProducer(VFREEBUSY % {
+    
+    vfb = VFREEBUSY % {
             "attendees": "".join([
                     "ATTENDEE:urn:uuid:%s\n" % (user,),
                     "ATTENDEE:urn:uuid:user02\n"]),
             "start": formatDate(baseTime.replace(hour=0, minute=0)) + 'Z',
             "end": formatDate(
-                baseTime.replace(hour=0, minute=0) + timedelta(days=1)) + 'Z'})
+                baseTime.replace(hour=0, minute=0) + timedelta(days=1)) + 'Z'}
+    body = StringProducer(vfb.replace("\n", "\r\n"))
 
     samples = yield sample(
         dtrace, samples,

Modified: CalendarServer/trunk/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/ical.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/loadtest/ical.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -16,16 +16,15 @@
 ##
 
 from uuid import uuid4
-from datetime import timedelta, datetime
 from urlparse import urlparse, urlunparse
 
 from xml.etree import ElementTree
+from twistedcaldav.ical import Component, Property
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import PyCalendarDateTime
 ElementTree.QName.__repr__ = lambda self: '<QName %r>' % (self.text,)
 
-from vobject import readComponents
-from vobject.base import ContentLine
-from vobject.icalendar import VEvent, dateTimeToString
-
 from twisted.python.log import addObserver, err, msg
 from twisted.python.filepath import FilePath
 from twisted.python.failure import Failure
@@ -98,8 +97,7 @@
         Return the UID from the vevent, if there is one.
         """
         if self.vevent is not None:
-            uid = self.vevent.contents['vevent'][0].contents['uid'][0]
-            return uid.value
+            return self.vevent.resourceUID()
         return None
 
 
@@ -443,7 +441,7 @@
         event.etag = etag
         if scheduleTag is not None:
             event.scheduleTag = scheduleTag
-        event.vevent = list(readComponents(body))[0]
+        event.vevent = Component.fromString(body)
         self.catalog["eventChanged"].issue(href)
 
                 
@@ -623,33 +621,33 @@
 
 
     def _makeSelfAttendee(self):
-        attendee = ContentLine(
-            name=u'ATTENDEE', params=[
-                [u'CN', self.record.commonName],
-                [u'CUTYPE', u'INDIVIDUAL'],
-                [u'PARTSTAT', u'ACCEPTED'],
-                ],
+        attendee = Property(
+            name=u'ATTENDEE',
             value=self.uuid,
-            encoded=True)
-        attendee.parentBehavior = VEvent
+            params={
+                'CN': self.record.commonName,
+                'CUTYPE': 'INDIVIDUAL',
+                'PARTSTAT': 'ACCEPTED',
+            },
+        )
         return attendee
 
 
     def _makeSelfOrganizer(self):
-        organizer = ContentLine(
-            name=u'ORGANIZER', params=[
-                [u'CN', self.record.commonName],
-                ],
+        organizer = Property(
+            name=u'ORGANIZER',
             value=self.uuid,
-            encoded=True)
-        organizer.parentBehavior = VEvent
+            params={
+                'CN': self.record.commonName,
+            },
+        )
         return organizer
 
 
     def addEventAttendee(self, href, attendee):
-        name = attendee.params[u'CN'][0].encode('utf-8')
+        name = attendee.parameterValue('CN')
         prefix = name[:4].lower()
-        email = attendee.params[u'EMAIL'][0].encode('utf-8')
+        email = attendee.parameterValue('EMAIL')
 
         event = self._events[href]
         vevent = event.vevent
@@ -683,21 +681,21 @@
         def specific(ignored):
             # Now learn about the attendee's availability
             return self.requestAvailability(
-                vevent.contents[u'vevent'][0].contents[u'dtstart'][0].value,
-                vevent.contents[u'vevent'][0].contents[u'dtend'][0].value,
+                vevent.mainComponent.getStartDateUTC(),
+                vevent.mainComponent.getEndDateUTC(),
                 [self.email, u'mailto:' + email],
-                [vevent.contents[u'vevent'][0].contents[u'uid'][0].value])
+                [vevent.resourceUID()])
             return d
         d.addCallback(specific)
         def availability(ignored):
             # If the event has no attendees, add ourselves as an attendee.
-            attendees = vevent.contents[u'vevent'][0].contents.setdefault(u'attendee', [])
+            attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
             if len(attendees) == 0:
                 # First add ourselves as a participant and as the
                 # organizer.  In the future for this event we should
                 # already have those roles.
-                attendees.append(self._makeSelfAttendee())
-                vevent.contents[u'vevent'][0].contents[u'organizer'] = [self._makeSelfOrganizer()]
+                vevent.mainComponent().addProperty(self._makeSelfOrganizer())
+                vevent.mainComponent().addProperty(self._makeSelfAttendee())
             attendees.append(attendee)
 
             # At last, upload the new event definition
@@ -719,9 +717,8 @@
         vevent = event.vevent
 
         # Change the event to have the new attendee instead of the old attendee
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
-        attendees.remove(oldAttendee)
-        attendees.append(newAttendee)
+        vevent.mainComponent().removeProperty(oldAttendee)
+        vevent.mainComponent().addProperty(newAttendee)
         headers = Headers({
                 'content-type': ['text/calendar'],
                 })
@@ -730,7 +727,7 @@
 
         d = self._request(
             NO_CONTENT, 'PUT', self.root + href[1:].encode('utf-8'),
-            headers, StringProducer(vevent.serialize()))
+            headers, StringProducer(vevent.getTextWithTimezones(includeTimezones=True)))
         d.addCallback(self._updateEvent, href)
         return d
 
@@ -756,7 +753,7 @@
                 })
         d = self._request(
             CREATED, 'PUT', self.root + href[1:].encode('utf-8'),
-            headers, StringProducer(vcalendar.serialize()))
+            headers, StringProducer(vcalendar.getTextWithTimezones(includeTimezones=True)))
         d.addCallback(self._localUpdateEvent, href, vcalendar)
         return d
 
@@ -824,25 +821,18 @@
                                    for uuid in users]) + '\r\n'
 
         # iCal issues 24 hour wide vfreebusy requests, starting and ending at 4am.
-        if start.date() != end.date():
+        if start.compareDate(end):
             msg("Availability request spanning multiple days (%r to %r), "
                 "dropping the end date." % (start, end))
 
-        start = start.replace(hour=0, minute=0, second=0, microsecond=0)
-        end = start + timedelta(hours=24)
+        start.setTimezone(PyCalendarTimezone(utc=True))
+        start.setHHMMSS(0, 0, 0)
+        end = start + PyCalendarDuration(hours=24)
 
-        start = dateTimeToString(start, convertToUTC=True)
-        end = dateTimeToString(end, convertToUTC=True)
-        now = dateTimeToString(datetime.now(), convertToUTC=True)
+        start = start.getText()
+        end = end.getText()
+        now = PyCalendarDateTime.getNowUTC().getText()
 
-        # XXX Why does it not end up UTC sometimes?
-        if not start.endswith('Z'):
-            start = start + 'Z'
-        if not end.endswith('Z'):
-            end = end + 'Z'
-        if not now.endswith('Z'):
-            now = now + 'Z'
-
         d = self._request(
             OK, 'POST', outbox,
             Headers({

Modified: CalendarServer/trunk/contrib/performance/loadtest/population.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/population.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/loadtest/population.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -29,6 +29,8 @@
 from twisted.python.util import FancyEqMixin
 from twisted.python.log import msg, err
 
+from twistedcaldav.timezones import TimezoneCache
+
 from contrib.performance.stats import mean, median, stddev, mad
 from contrib.performance.loadtest.trafficlogger import loggedReactor
 from contrib.performance.loadtest.logger import SummarizingMixin
@@ -156,6 +158,7 @@
         self._user = 0
         self._stopped = False
 
+        TimezoneCache.create()
 
     def getUserRecord(self, index):
         return self._records[index]

Modified: CalendarServer/trunk/contrib/performance/loadtest/profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/profiles.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/loadtest/profiles.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -24,12 +24,6 @@
 import sys, random
 from uuid import uuid4
 
-from datetime import datetime, timedelta
-
-from vobject import readComponents
-from vobject.base import Component, ContentLine
-from vobject.icalendar import VEvent
-
 from caldavclientlibrary.protocol.caldav.definitions import caldavxml
 
 from twisted.python import context
@@ -39,10 +33,14 @@
 from twisted.internet.task import LoopingCall
 from twisted.web.http import PRECONDITION_FAILED
 
+from twistedcaldav.ical import Property, Component
+
 from contrib.performance.stats import NearFutureDistribution, NormalDistribution, UniformDiscreteDistribution, mean, median
 from contrib.performance.loadtest.logger import SummarizingMixin
 from contrib.performance.loadtest.ical import IncorrectResponseCode
 
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
 
 class ProfileBase(object):
     """
@@ -77,7 +75,7 @@
         C{self._client}'s identifiers.  Return C{True} if something matches,
         C{False} otherwise.
         """
-        return attendee.params[u'EMAIL'][0] == self._client.email[len('mailto:'):]
+        return attendee.parameterValue('EMAIL') == self._client.email[len('mailto:'):]
 
 
     def _newOperation(self, label, deferred):
@@ -158,7 +156,7 @@
         selfRecord = self._sim.getUserRecord(self._number)
         invitees = set([u'urn:uuid:%s' % (selfRecord.uid,)])
         for att in attendees:
-            invitees.add(att.value)
+            invitees.add(att.value())
 
         for _ignore_i in range(10):
             invitee = max(
@@ -173,19 +171,19 @@
         else:
             return fail(CannotAddAttendee("Can't find uninvited user to invite."))
 
-        attendee = ContentLine(
-            name=u'ATTENDEE', params=[
-                [u'CN', record.commonName],
-                [u'CUTYPE', u'INDIVIDUAL'],
-                [u'EMAIL', record.email],
-                [u'PARTSTAT', u'NEEDS-ACTION'],
-                [u'ROLE', u'REQ-PARTICIPANT'],
-                [u'RSVP', u'TRUE'],
-                # [u'SCHEDULE-STATUS', u'1.2'],
-                ],
+        attendee = Property(
+            name=u'ATTENDEE',
             value=uuid,
-            encoded=True)
-        attendee.parentBehavior = VEvent
+            params={
+            'CN': record.commonName,
+            'CUTYPE': 'INDIVIDUAL',
+            'EMAIL': record.email,
+            'PARTSTAT': 'NEEDS-ACTION',
+            'ROLE': 'REQ-PARTICIPANT',
+            'RSVP': 'TRUE',
+            #'SCHEDULE-STATUS': '1.2',
+            },
+        )
 
         return succeed(attendee)
 
@@ -219,8 +217,8 @@
                 if event is None:
                     continue
 
-                vevent = event.contents[u'vevent'][0]
-                organizer = vevent.contents.get('organizer', [None])[0]
+                vevent = event.mainComponent()
+                organizer = vevent.getOrganizerProperty()
                 if organizer is not None and not self._isSelfAttendee(organizer):
                     # This event was organized by someone else, don't try to invite someone to it.
                     continue
@@ -228,7 +226,7 @@
                 href = calendar.url + uuid
 
                 # Find out who might attend
-                attendees = vevent.contents.get('attendee', [])
+                attendees = tuple(vevent.properties('ATTENDEE'))
 
                 d = self._addAttendee(event, attendees)
                 d.addCallbacks(
@@ -287,10 +285,10 @@
         vevent = self._client._events[href].vevent
         # Check to see if this user is in the attendee list in the
         # NEEDS-ACTION PARTSTAT.
-        attendees = vevent.contents['vevent'][0].contents.get('attendee', [])
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         for attendee in attendees:
             if self._isSelfAttendee(attendee):
-                if attendee.params[u'PARTSTAT'][0] == 'NEEDS-ACTION':
+                if attendee.parameterValue('PARTSTAT') == 'NEEDS-ACTION':
                     delay = self._acceptDelayDistribution.sample()
                     self._accepting.add(href)
                     self._reactor.callLater(
@@ -302,7 +300,7 @@
             return
 
         vevent = self._client._events[href].vevent
-        method = vevent.contents.get('method')[0].value
+        method = vevent.propertyValue('METHOD')
         if method == "REPLY":
             # Replies are immediately deleted
             self._accepting.add(href)
@@ -391,12 +389,9 @@
 
 
     def _makeAcceptedAttendee(self, attendee):
-        accepted = ContentLine.duplicate(attendee)
-        accepted.params[u'PARTSTAT'] = [u'ACCEPTED']
-        try:
-            del accepted.params[u'RSVP']
-        except KeyError:
-            msg("Duplicated an attendee with no RSVP: %r" % (attendee,))
+        accepted = attendee.duplicate()
+        accepted.setParameter('PARTSTAT', 'ACCEPTED')
+        accepted.removeParameter('RSVP')
         return accepted
 
 
@@ -405,28 +400,11 @@
     """
     A Calendar user who creates new events.
     """
-    _eventTemplate = list(readComponents("""\
+    _eventTemplate = Component.fromString("""\
 BEGIN:VCALENDAR
 VERSION:2.0
 PRODID:-//Apple Inc.//iCal 4.0.3//EN
 CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0500
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-DTSTART:20070311T020000
-TZNAME:EDT
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:-0400
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-DTSTART:20071104T020000
-TZNAME:EST
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
 BEGIN:VEVENT
 CREATED:20101018T155431Z
 UID:C98AD237-55AD-4F7D-9009-0D355D835822
@@ -438,7 +416,7 @@
 SEQUENCE:2
 END:VEVENT
 END:VCALENDAR
-"""))[0]
+""".replace("\n", "\r\n"))
 
     def setParameters(
         self,
@@ -472,20 +450,18 @@
 
             # Copy the template event and fill in some of its fields
             # to make a new event to create on the calendar.
-            vcalendar = Component.duplicate(self._eventTemplate)
-            vevent = vcalendar.contents[u'vevent'][0]
-            tz = vevent.contents[u'created'][0].value.tzinfo
-            dtstamp = datetime.now(tz)
-            dtstart = datetime.fromtimestamp(self._eventStartDistribution.sample(), tz)
-            dtend = dtstart + timedelta(seconds=self._eventDurationDistribution.sample())
-            vevent.contents[u'created'][0].value = dtstamp
-            vevent.contents[u'dtstamp'][0].value = dtstamp
-            vevent.contents[u'dtstart'][0].value = dtstart
-            vevent.contents[u'dtend'][0].value = dtend
-            vevent.contents[u'uid'][0].value = unicode(uuid4())
+            vcalendar = self._eventTemplate.duplicate()
+            vevent = vcalendar.mainComponent()
+            uid = str(uuid4())
+            dtstart = self._eventStartDistribution.sample()
+            dtend = dtstart + PyCalendarDuration(seconds=self._eventDurationDistribution.sample())
+            vevent.replaceProperty(Property("CREATED", PyCalendarDateTime.getNowUTC()))
+            vevent.replaceProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
+            vevent.replaceProperty(Property("DTSTART", dtstart))
+            vevent.replaceProperty(Property("DTEND", dtend))
+            vevent.replaceProperty(Property("UID", uid))
 
-            href = '%s%s.ics' % (
-                calendar.url, vevent.contents[u'uid'][0].value)
+            href = '%s%s.ics' % (calendar.url, uid)
             d = self._client.addEvent(href, vcalendar)
             return self._newOperation("create", d)
 

Modified: CalendarServer/trunk/contrib/performance/loadtest/test_ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_ical.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_ical.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -15,12 +15,6 @@
 #
 ##
 
-from datetime import datetime
-
-from vobject import readComponents
-from vobject.base import Component, ContentLine
-from vobject.icalendar import dateTimeToString
-
 from twisted.python.failure import Failure
 from twisted.internet.defer import Deferred
 from twisted.trial.unittest import TestCase
@@ -37,6 +31,10 @@
 from contrib.performance.loadtest.ical import XMPPPush, Event, Calendar, SnowLeopard
 from contrib.performance.loadtest.sim import _DirectoryRecord
 from contrib.performance.httpclient import MemoryConsumer, StringProducer
+from twistedcaldav.ical import Component
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
+from twistedcaldav.timezones import TimezoneCache
 
 EVENT_UID = 'D94F247D-7433-43AF-B84B-ADD684D023B0'
 
@@ -45,23 +43,6 @@
 VERSION:2.0
 PRODID:-//Apple Inc.//iCal 4.0.3//EN
 CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0500
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-DTSTART:20070311T020000
-TZNAME:EDT
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:-0400
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-DTSTART:20071104T020000
-TZNAME:EST
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
 BEGIN:VEVENT
 CREATED:20101018T155454Z
 UID:%(UID)s
@@ -79,9 +60,176 @@
 SEQUENCE:3
 END:VEVENT
 END:VCALENDAR
-""" % {'UID': EVENT_UID}
+""".replace("\n", "\r\n") % {'UID': EVENT_UID}
 
+EVENT_AND_TIMEZONE = """\
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:America/New_York
+X-LIC-LOCATION:America/New_York
+BEGIN:STANDARD
+DTSTART:18831118T120358
+RDATE:18831118T120358
+TZNAME:EST
+TZOFFSETFROM:-045602
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19180331T020000
+RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19181027T020000
+RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19200101T000000
+RDATE:19200101T000000
+RDATE:19420101T000000
+RDATE:19460101T000000
+RDATE:19670101T000000
+TZNAME:EST
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19200328T020000
+RDATE:19200328T020000
+RDATE:19740106T020000
+RDATE:19750223T020000
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19201031T020000
+RDATE:19201031T020000
+RDATE:19450930T020000
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19210424T020000
+RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19210925T020000
+RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19420209T020000
+RDATE:19420209T020000
+TZNAME:EWT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:19450814T190000
+RDATE:19450814T190000
+TZNAME:EPT
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:19460428T020000
+RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19460929T020000
+RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19551030T020000
+RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19670430T020000
+RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19671029T020000
+RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19760425T020000
+RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:19870405T020000
+RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20101018T155454Z
+UID:%(UID)s
+DTEND;TZID=America/New_York:20101028T130000
+ATTENDEE;CN="User 03";CUTYPE=INDIVIDUAL;EMAIL="user03 at example.com";PARTS
+ TAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:user03 at example.co
+ m
+ATTENDEE;CN="User 01";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:user01@
+ example.com
+TRANSP:OPAQUE
+SUMMARY:Attended Event
+DTSTART;TZID=America/New_York:20101028T120000
+DTSTAMP:20101018T155513Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+SEQUENCE:3
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {'UID': EVENT_UID}
 
+
 class EventTests(TestCase):
     """
     Tests for L{Event}.
@@ -91,7 +239,7 @@
         When the C{vevent} attribute of an L{Event} instance is set,
         L{Event.getUID} returns the UID value from it.
         """
-        event = Event(u'/foo/bar', u'etag', list(readComponents(EVENT))[0])
+        event = Event(u'/foo/bar', u'etag', Component.fromString(EVENT))
         self.assertEquals(event.getUID(), EVENT_UID)
 
 
@@ -834,6 +982,7 @@
     Mixin for L{TestCase}s for L{SnowLeopard}.
     """
     def setUp(self):
+        TimezoneCache.create()
         self.record = _DirectoryRecord(
             u"user91", u"user91", u"User 91", u"user91 at example.org")
         self.client = SnowLeopard(None, "http://127.0.0.1/", self.record, None)
@@ -962,11 +1111,11 @@
         """
         requests = self.interceptRequests()
 
-        vevent = list(readComponents(EVENT))[0]
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+        vevent = Component.fromString(EVENT)
+        attendees = tuple(vevent.mainComponent().properties("ATTENDEE"))
         old = attendees[0]
-        new = ContentLine.duplicate(old)
-        new.params[u'CN'] = [u'Some Other Guy']
+        new = old.duplicate()
+        new.setParameter('CN', 'Some Other Guy')
         event = Event(u'/some/calendar/1234.ics', None, vevent)
         self.client._events[event.url] = event
         self.client.changeEventAttendee(event.url, old, new)
@@ -983,11 +1132,11 @@
         consumer = MemoryConsumer()
         finished = body.startProducing(consumer)
         def cbFinished(ignored):
-            vevent = list(readComponents(consumer.value()))[0]
-            attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+            vevent = Component.fromString(consumer.value())
+            attendees = tuple(vevent.mainComponent().properties("ATTENDEE"))
             self.assertEquals(len(attendees), 2)
-            self.assertEquals(attendees[0].params[u'CN'], [u'User 01'])
-            self.assertEquals(attendees[1].params[u'CN'], [u'Some Other Guy'])
+            self.assertEquals(attendees[0].parameterValue('CN'), 'User 01')
+            self.assertEquals(attendees[1].parameterValue('CN'), 'Some Other Guy')
         finished.addCallback(cbFinished)
         return finished
 
@@ -1002,7 +1151,7 @@
         calendar = Calendar(caldavxml.calendar, u'calendar', u'/mumble/', None)
         self.client._calendars[calendar.url] = calendar
 
-        vcalendar = list(readComponents(EVENT))[0]
+        vcalendar = Component.fromString(EVENT)
         d = self.client.addEvent(u'/mumble/frotz.ics', vcalendar)
 
         result, req = requests.pop(0)
@@ -1018,9 +1167,9 @@
         consumer = MemoryConsumer()
         finished = body.startProducing(consumer)
         def cbFinished(ignored):
-            self.assertComponentsEqual(
-                list(readComponents(consumer.value()))[0],
-                vcalendar)
+            self.assertEqual(
+                Component.fromString(consumer.value()),
+                Component.fromString(EVENT_AND_TIMEZONE))
         finished.addCallback(cbFinished)
 
         def requested(ignored):
@@ -1067,39 +1216,6 @@
         return d
 
 
-    def assertComponentsEqual(self, first, second):
-        self.assertEquals(first.name, second.name, "Component names not equal")
-        self.assertEquals(first.behavior, second.behavior, "Component behaviors not equal")
-
-        for k in first.contents:
-            if k not in second.contents:
-                self.fail("Content %r present in first but not second" % (k,))
-            self.assertEquals(
-                len(first.contents[k]), len(second.contents[k]), "Different length content %r" % (k,))
-            for (a, b) in zip(first.contents[k], second.contents[k]):
-                if isinstance(a, ContentLine):
-                    f = self.assertContentLinesEqual
-                elif isinstance(a, Component):
-                    f = self.assertComponentsEqual
-                else:
-                    f = self.assertEquals
-                f(a, b)
-        for k in second.contents:
-            if k not in first.contents:
-                self.fail("Content %r present in second but not first" % (k,))
-
-
-    def assertContentLinesEqual(self, first, second):
-        self.assertEquals(first.name, second.name, "ContentLine names not equal")
-        self.assertEquals(first.behavior, second.behavior, "ContentLine behaviors not equal")
-        self.assertEquals(first.value, second.value, "ContentLine values not equal")
-        self.assertEquals(first.params, second.params, "ContentLine params not equal")
-        self.assertEquals(
-            first.singletonparams, second.singletonparams,
-            "ContentLine singletonparams not equal")
-
-
-
 class UpdateCalendarTests(SnowLeopardMixin, TestCase):
     """
     Tests for L{SnowLeopard._updateCalendar}.
@@ -1336,8 +1452,8 @@
         self.client.email = u'mailto:user01 at example.com'
         requests = self.interceptRequests()
 
-        start = datetime(2011, 6, 10, 10, 45, 0)
-        end = datetime(2011, 6, 10, 11, 15, 0)
+        start = PyCalendarDateTime(2011, 6, 10, 10, 45, 0, tzid=PyCalendarTimezone(utc=True))
+        end = PyCalendarDateTime(2011, 6, 10, 11, 15, 0, tzid=PyCalendarTimezone(utc=True))
         d = self.client.requestAvailability(
             start, end, [u"urn:uuid:user05", u"urn:uuid:user10"])
 
@@ -1357,10 +1473,10 @@
         consumer = MemoryConsumer()
         finished = body.startProducing(consumer)
         def cbFinished(ignored):
-            vevent = list(readComponents(consumer.value()))[0]
-            uid = vevent.contents[u'vfreebusy'][0].contents[u'uid'][0].value.encode('utf-8')
-            dtstamp = vevent.contents[u'vfreebusy'][0].contents[u'dtstamp'][0].value
-            dtstamp = dateTimeToString(dtstamp, False)
+            vevent = Component.fromString(consumer.value())
+            uid = vevent.resourceUID()
+            dtstamp = vevent.mainComponent().propertyValue("DTSTAMP")
+            dtstamp = dtstamp.getText()
             self.assertEqual(
 """\
 BEGIN:VCALENDAR

Modified: CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -21,8 +21,6 @@
 
 from StringIO import StringIO
 
-from vobject import readComponents
-
 from caldavclientlibrary.protocol.caldav.definitions import caldavxml, csxml
 
 from twisted.trial.unittest import TestCase
@@ -31,6 +29,8 @@
 from twisted.web.http import NO_CONTENT, PRECONDITION_FAILED
 from twisted.web.client import Response
 
+from twistedcaldav.ical import Component
+
 from contrib.performance.loadtest.profiles import Eventer, Inviter, Accepter, OperationLogger
 from contrib.performance.loadtest.population import Populator, CalendarClientSimulator
 from contrib.performance.loadtest.ical import IncorrectResponseCode, Calendar, Event, BaseClient
@@ -225,8 +225,7 @@
 
     def addEventAttendee(self, href, attendee):
         vevent = self._events[href].vevent
-        attendees = vevent.contents[u'vevent'][0].contents.setdefault(u'attendee', [])
-        attendees.append(attendee)
+        vevent.mainComponent().addProperty(attendee)
 
 
     def changeEventAttendee(self, href, old, new):
@@ -238,9 +237,8 @@
                         'Precondition Failed', None, None)))
 
         vevent = self._events[href].vevent
-        attendees = vevent.contents[u'vevent'][0].contents.setdefault(u'attendee', [])
-        attendees.remove(old)
-        attendees.append(new)
+        vevent.mainComponent().removeProperty(old)
+        vevent.mainComponent().addProperty(new)
         return succeed(None)
 
 
@@ -265,7 +263,7 @@
 
 
     def _simpleAccount(self, userNumber, eventText):
-        vevent = list(readComponents(eventText))[0]
+        vevent = Component.fromString(eventText)
         calendar = Calendar(
             caldavxml.calendar, u'calendar', u'/cal/', None)
         event = Event(calendar.url + u'1234.ics', None, vevent)
@@ -293,12 +291,12 @@
         attempt is made to add attendees to an event on that calendar.
         """
         userNumber = 10
-        vevent, event, calendar, client = self._simpleAccount(
+        vevent, _ignore_event, calendar, client = self._simpleAccount(
             userNumber, SIMPLE_EVENT)
         calendar.resourceType = caldavxml.schedule_inbox
         inviter = Inviter(None, self.sim, client, userNumber)
         inviter._invite()
-        self.assertNotIn(u'attendee', vevent.contents[u'vevent'][0].contents)
+        self.assertFalse(vevent.mainComponent().hasProperty('ATTENDEE'))
 
 
     def test_doNotAddAttendeeToNoCalendars(self):
@@ -321,7 +319,7 @@
         invitees to that event.
         """
         userNumber = 19
-        vevent, event, calendar, client = self._simpleAccount(
+        _ignore_vevent, event, calendar, client = self._simpleAccount(
             userNumber, SIMPLE_EVENT)
         event.vevent = event.etag = event.scheduleTag = None
         inviter = Inviter(None, self.sim, client, userNumber)
@@ -336,20 +334,23 @@
         attendee to it.
         """
         userNumber = 16
-        vevent, event, calendar, client = self._simpleAccount(
+        vevent, _ignore_event, _ignore_calendar, client = self._simpleAccount(
             userNumber, SIMPLE_EVENT)
         inviter = Inviter(Clock(), self.sim, client, userNumber)
         inviter.setParameters(inviteeDistanceDistribution=Deterministic(1))
         inviter._invite()
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         self.assertEquals(len(attendees), 1)
-        self.assertEquals(attendees[0].params, {
-                u'CN': [u'User %d' % (userNumber + 1,)],
-                u'CUTYPE': [u'INDIVIDUAL'],
-                u'EMAIL': [u'user%d at example.com' % (userNumber + 1,)],
-                u'PARTSTAT': [u'NEEDS-ACTION'],
-                u'ROLE': [u'REQ-PARTICIPANT'],
-                u'RSVP': [u'TRUE']})
+        for paramname, paramvalue in {
+            'CN': 'User %d' % (userNumber + 1,),
+            'CUTYPE': 'INDIVIDUAL',
+            'EMAIL': 'user%d at example.com' % (userNumber + 1,),
+            'PARTSTAT': 'NEEDS-ACTION',
+            'ROLE': 'REQ-PARTICIPANT',
+            'RSVP': 'TRUE'
+        }.items():
+            self.assertTrue(attendees[0].hasParameter(paramname))
+            self.assertEqual(attendees[0].parameterValue(paramname), paramvalue)
 
 
 
@@ -359,7 +360,7 @@
         the attendee list, a different user is added instead.
         """
         selfNumber = 12
-        vevent, event, calendar, client = self._simpleAccount(
+        vevent, _ignore_event, _ignore_calendar, client = self._simpleAccount(
             selfNumber, SIMPLE_EVENT)
 
         otherNumber = 20
@@ -368,15 +369,18 @@
         inviter = Inviter(Clock(), self.sim, client, selfNumber)
         inviter.setParameters(inviteeDistanceDistribution=SequentialDistribution(values))
         inviter._invite()
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         self.assertEquals(len(attendees), 1)
-        self.assertEquals(attendees[0].params, {
-                u'CN': [u'User %d' % (otherNumber,)],
-                u'CUTYPE': [u'INDIVIDUAL'],
-                u'EMAIL': [u'user%d at example.com' % (otherNumber,)],
-                u'PARTSTAT': [u'NEEDS-ACTION'],
-                u'ROLE': [u'REQ-PARTICIPANT'],
-                u'RSVP': [u'TRUE']})
+        for paramname, paramvalue in {
+            'CN': 'User %d' % (otherNumber,),
+            'CUTYPE': 'INDIVIDUAL',
+            'EMAIL': 'user%d at example.com' % (otherNumber,),
+            'PARTSTAT': 'NEEDS-ACTION',
+            'ROLE': 'REQ-PARTICIPANT',
+            'RSVP': 'TRUE'
+        }.items():
+            self.assertTrue(attendees[0].hasParameter(paramname))
+            self.assertEqual(attendees[0].parameterValue(paramname), paramvalue)
 
 
 
@@ -386,26 +390,29 @@
         invitee on the event, a different user is added instead.
         """
         selfNumber = 1
-        vevent, event, calendar, client = self._simpleAccount(
+        vevent, _ignore_event, _ignore_calendar, client = self._simpleAccount(
             selfNumber, INVITED_EVENT)
 
-        invitee = vevent.contents[u'vevent'][0].contents[u'attendee'][0]
-        inviteeNumber = int(invitee.params[u'CN'][0].split()[1])
+        invitee = tuple(vevent.mainComponent().properties('ATTENDEE'))[0]
+        inviteeNumber = int(invitee.parameterValue('CN').split()[1])
         anotherNumber = inviteeNumber + 5
         values = [inviteeNumber - selfNumber, anotherNumber - selfNumber]
 
         inviter = Inviter(Clock(), self.sim, client, selfNumber)
         inviter.setParameters(inviteeDistanceDistribution=SequentialDistribution(values))
         inviter._invite()
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         self.assertEquals(len(attendees), 3)
-        self.assertEquals(attendees[2].params, {
-                u'CN': [u'User %02d' % (anotherNumber,)],
-                u'CUTYPE': [u'INDIVIDUAL'],
-                u'EMAIL': [u'user%02d at example.com' % (anotherNumber,)],
-                u'PARTSTAT': [u'NEEDS-ACTION'],
-                u'ROLE': [u'REQ-PARTICIPANT'],
-                u'RSVP': [u'TRUE']})
+        for paramname, paramvalue in {
+            'CN': 'User %02d' % (anotherNumber,),
+            'CUTYPE': 'INDIVIDUAL',
+            'EMAIL': 'user%02d at example.com' % (anotherNumber,),
+            'PARTSTAT': 'NEEDS-ACTION',
+            'ROLE': 'REQ-PARTICIPANT',
+            'RSVP': 'TRUE'
+        }.items():
+            self.assertTrue(attendees[2].hasParameter(paramname))
+            self.assertEqual(attendees[2].parameterValue(paramname), paramvalue)
 
 
     def test_everybodyInvitedAlready(self):
@@ -415,13 +422,13 @@
         abandoned.
         """
         selfNumber = 1
-        vevent, event, calendar, client = self._simpleAccount(
+        vevent, _ignore_event, _ignore_calendar, client = self._simpleAccount(
             selfNumber, INVITED_EVENT)
         inviter = Inviter(Clock(), self.sim, client, selfNumber)
         # Always return a user number which has already been invited.
         inviter.setParameters(inviteeDistanceDistribution=Deterministic(2 - selfNumber))
         inviter._invite()
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         self.assertEquals(len(attendees), 2)
 
 
@@ -432,17 +439,17 @@
         users to them.
         """
         selfNumber = 2
-        vevent, event, calendar, client = self._simpleAccount(
+        vevent, _ignore_event, _ignore_calendar, client = self._simpleAccount(
             selfNumber, INVITED_EVENT)
         inviter = Inviter(None, self.sim, client, selfNumber)
         # Try to send an invitation, but with only one event on the
         # calendar, of which we are not the organizer.  It should be
         # unchanged afterwards.
         inviter._invite()
-        attendees = event.vevent.contents[u'vevent'][0].contents[u'attendee']
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         self.assertEqual(len(attendees), 2)
-        self.assertEqual(attendees[0].params['CN'], [u'User 01'])
-        self.assertEqual(attendees[1].params['CN'], [u'User 02'])
+        self.assertEqual(attendees[0].parameterValue('CN'), 'User 01')
+        self.assertEqual(attendees[1].parameterValue('CN'), 'User 02')
 
 
 
@@ -495,9 +502,9 @@
         If the client is an attendee on an event but the PARTSTAT is
         not NEEDS-ACTION, the event is ignored.
         """
-        vevent = list(readComponents(ACCEPTED_EVENT))[0]
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
-        userNumber = int(attendees[1].params[u'CN'][0].split(None, 1)[1])
+        vevent = Component.fromString(ACCEPTED_EVENT)
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
+        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
         calendarURL = '/some/calendar/'
         calendar = Calendar(
             caldavxml.calendar, u'calendar', calendarURL, None)
@@ -518,9 +525,9 @@
         """
         clock = Clock()
         randomDelay = 7
-        vevent = list(readComponents(INVITED_EVENT))[0]
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
-        userNumber = int(attendees[1].params[u'CN'][0].split(None, 1)[1])
+        vevent = Component.fromString(INVITED_EVENT)
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
+        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
         calendarURL = '/some/calendar/'
         calendar = Calendar(
             caldavxml.calendar, u'calendar', calendarURL, None)
@@ -545,9 +552,9 @@
         """
         clock = Clock()
         randomDelay = 7
-        vevent = list(readComponents(INVITED_EVENT))[0]
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
-        userNumber = int(attendees[1].params[u'CN'][0].split(None, 1)[1])
+        vevent = Component.fromString(INVITED_EVENT)
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
+        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
         client = StubClient(userNumber)
 
         calendarURL = '/some/calendar/'
@@ -572,13 +579,13 @@
         clock.advance(randomDelay)
 
         vevent = client._events[event.url].vevent
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         self.assertEquals(len(attendees), 2)
         self.assertEquals(
-            attendees[1].params[u'CN'], [u'User %02d' % (userNumber,)])
+            attendees[1].parameterValue('CN'), 'User %02d' % (userNumber,))
         self.assertEquals(
-            attendees[1].params[u'PARTSTAT'], [u'ACCEPTED'])
-        self.assertNotIn(u'RSVP', attendees[1].params)
+            attendees[1].parameterValue('PARTSTAT'), 'ACCEPTED')
+        self.assertFalse(attendees[1].hasParameter('RSVP'))
 
         self.assertNotIn(inboxEvent.url, client._events)
         self.assertNotIn('4321.ics', inbox.events)
@@ -592,9 +599,9 @@
         """
         clock = Clock()
         randomDelay = 7
-        vevent = list(readComponents(INVITED_EVENT))[0]
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
-        userNumber = int(attendees[1].params[u'CN'][0].split(None, 1)[1])
+        vevent = Component.fromString(INVITED_EVENT)
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
+        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
         calendarURL = '/some/calendar/'
         calendar = Calendar(
             caldavxml.calendar, u'calendar', calendarURL, None)
@@ -608,7 +615,7 @@
         clock.advance(randomDelay)
 
         # Now re-set the event so it has to be accepted again
-        event.vevent = list(readComponents(INVITED_EVENT))[0]
+        event.vevent = Component.fromString(INVITED_EVENT)
 
         # And now re-deliver it
         accepter.eventChanged(event.url)
@@ -616,13 +623,13 @@
 
         # And ensure that it was accepted again
         vevent = client._events[event.url].vevent
-        attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
         self.assertEquals(len(attendees), 2)
         self.assertEquals(
-            attendees[1].params[u'CN'], [u'User %02d' % (userNumber,)])
+            attendees[1].parameterValue('CN'), 'User %02d' % (userNumber,))
         self.assertEquals(
-            attendees[1].params[u'PARTSTAT'], [u'ACCEPTED'])
-        self.assertNotIn(u'RSVP', attendees[1].params)
+            attendees[1].parameterValue('PARTSTAT'), 'ACCEPTED')
+        self.assertFalse(attendees[1].hasParameter('RSVP'))
 
 
     def test_changeEventAttendeePreconditionFailed(self):
@@ -641,7 +648,7 @@
             caldavxml.calendar, u'calendar', calendarURL, None)
         client._calendars[calendarURL] = calendar
 
-        vevent = list(readComponents(INVITED_EVENT))[0]
+        vevent = Component.fromString(INVITED_EVENT)
         event = Event(calendarURL + u'1234.ics', None, vevent)
         client._setEvent(event.url, event)
 
@@ -754,7 +761,7 @@
         describing that issue.
         """
         logger = OperationLogger(outfile=StringIO())
-        for i in range(98):
+        for _ignore in range(98):
             logger.observe(dict(
                     type='operation', phase='end', user='user01',
                     duration=0.25, label='testing', success=True))

Modified: CalendarServer/trunk/contrib/performance/stats.py
===================================================================
--- CalendarServer/trunk/contrib/performance/stats.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/stats.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 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.
@@ -14,15 +14,16 @@
 # limitations under the License.
 ##
 
-import random, time, datetime
+import random, time
 
-import pytz
-
 from zope.interface import Interface, implements
 
 from twisted.python.util import FancyEqMixin
 
 import sqlparse
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.timezone import PyCalendarTimezone
 
 NANO = 1000000000.0
 
@@ -275,7 +276,9 @@
 
 
     def sample(self):
-        return time.time() + self._offset.sample()
+        now = PyCalendarDateTime.getNowUTC()
+        now.offsetSeconds(int(self._offset.sample()))
+        return now
 
 
 
@@ -313,20 +316,19 @@
 class WorkDistribution(object, FancyEqMixin):
     compareAttributes = ["_daysOfWeek", "_beginHour", "_endHour"]
 
-    _weekdayNames = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
+    _weekdayNames = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]
 
-    now = staticmethod(datetime.datetime.now)
-
     def __init__(self, daysOfWeek=["mon", "tue", "wed", "thu", "fri"], beginHour=8, endHour=17, tzname="UTC"):
         self._daysOfWeek = [self._weekdayNames.index(day) for day in daysOfWeek]
         self._beginHour = beginHour
         self._endHour = endHour
-        self._tzinfo = pytz.timezone(tzname)
+        self._tzname = tzname
         self._helperDistribution = NormalDistribution(
             # Mean 6 workdays in the future
             60 * 60 * 8 * 6,
             # Standard deviation of 4 workdays
             60 * 60 * 8 * 4)
+        self.now = PyCalendarDateTime.getNow
 
 
     def astimestamp(self, dt):
@@ -340,29 +342,29 @@
         be equal to when.
         """
         # Find a workday that follows the timestamp
-        weekday = when.weekday()
+        weekday = when.getDayOfWeek()
         for i in range(NUM_WEEKDAYS):
-            day = when + datetime.timedelta(days=i)
+            day = when + PyCalendarDuration(days=i)
             if (weekday + i) % NUM_WEEKDAYS in self._daysOfWeek:
                 # Joy, a day on which work might occur.  Find the first hour on
                 # this day when work may start.
-                begin = day.replace(
-                    hour=self._beginHour, minute=0, second=0, microsecond=0)
-                end = begin.replace(hour=self._endHour)
+                day.setHHMMSS(self._beginHour, 0, 0)
+                begin = day
+                end = begin.duplicate()
+                end.setHHMMSS(self._endHour, 0, 0)
                 if end > when:
                     return begin, end
 
 
     def sample(self):
-        offset = datetime.timedelta(seconds=self._helperDistribution.sample())
-        beginning = self.now(self._tzinfo)
+        offset = PyCalendarDuration(seconds=int(self._helperDistribution.sample()))
+        beginning = self.now(PyCalendarTimezone(tzid=self._tzname))
         while offset:
             start, end = self._findWorkAfter(beginning)
             if end - start > offset:
                 result = start + offset
-                return self.astimestamp(
-                    result.replace(
-                        minute=result.minute // 15 * 15,
-                        second=0, microsecond=0))
-            offset -= (end - start)
+                result.setMinutes(result.getMinutes() // 15 * 15)
+                result.setSeconds(0)
+                return result
+            offset.setDuration(offset.getTotalSeconds() - (end - start).getTotalSeconds())
             beginning = end

Modified: CalendarServer/trunk/contrib/performance/test_stats.py
===================================================================
--- CalendarServer/trunk/contrib/performance/test_stats.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/contrib/performance/test_stats.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -14,14 +14,13 @@
 # limitations under the License.
 ##
 
-import pytz
-from datetime import datetime
-
 from twisted.trial.unittest import TestCase
 
 from stats import (
     SQLDuration, LogNormalDistribution, UniformDiscreteDistribution,
     UniformIntegerDistribution, WorkDistribution, quantize)
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
 
 class SQLDurationTests(TestCase):
     def setUp(self):
@@ -50,7 +49,7 @@
 class DistributionTests(TestCase):
     def test_lognormal(self):
         dist = LogNormalDistribution(1, 1)
-        for i in range(100):
+        for _ignore_i in range(100):
             value = dist.sample()
             self.assertIsInstance(value, float)
             self.assertTrue(value >= 0.0, "negative value %r" % (value,))
@@ -61,35 +60,40 @@
         population = [1, 5, 6, 9]
         counts = dict.fromkeys(population, 0)
         dist = UniformDiscreteDistribution(population)
-        for i in range(len(population) * 10):
+        for _ignore_i in range(len(population) * 10):
             counts[dist.sample()] += 1
         self.assertEqual(dict.fromkeys(population, 10), counts)
 
 
     def test_workdistribution(self):
         tzname = "US/Eastern"
-        tzinfo = pytz.timezone(tzname)
         dist = WorkDistribution(["mon", "wed", "thu", "sat"], 10, 20, tzname)
         dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
-        dist.now = lambda tz=None: datetime(2011, 5, 29, 18, 5, 36, tzinfo=tz)
+        dist.now = lambda tzname=None: PyCalendarDateTime(2011, 5, 29, 18, 5, 36, tzid=tzname)
         value = dist.sample()
         self.assertEqual(
             # Move past three workdays - monday, wednesday, thursday - using 30
             # of the hours, and then five and a half hours into the fourth
             # workday, saturday.  Workday starts at 10am, so the sample value
             # is 3:30pm, ie 1530 hours.
-            datetime(2011, 6, 4, 15, 30, 0, tzinfo=tzinfo),
-            datetime.fromtimestamp(value, tzinfo))
+            PyCalendarDateTime(2011, 6, 4, 15, 30, 0, tzid=PyCalendarTimezone(tzname)),
+            value
+        )
 
+        dist = WorkDistribution(["mon", "tue", "wed", "thu", "fri"], 10, 20, tzname)
+        dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
+        value = dist.sample()
+        self.assertTrue(isinstance(value, PyCalendarDateTime))
+
     # twisted.trial.unittest.FailTest: not equal:
     # a = datetime.datetime(2011, 6, 4, 15, 30, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)
     # b = datetime.datetime(2011, 6, 4, 19, 30, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)
-    test_workdistribution.todo = "Somehow timezones mess this up"
+    #test_workdistribution.todo = "Somehow timezones mess this up"
 
 
     def test_uniform(self):
         dist = UniformIntegerDistribution(-5, 10)
-        for i in range(100):
+        for _ignore_i in range(100):
             value = dist.sample()
             self.assertTrue(-5 <= value < 10)
             self.assertIsInstance(value, int)

Modified: CalendarServer/trunk/support/build.sh
===================================================================
--- CalendarServer/trunk/support/build.sh	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/support/build.sh	2011-08-09 19:35:21 UTC (rev 7862)
@@ -418,7 +418,7 @@
   local  inplace="";      # Do development in-place; don't run setup.py to
                           # build, and instead add the source directory plus the
                           # given relative path directly to sys.path.  twisted
-                          # and vobject are developed often enough that this is
+                          # and pycalendar are developed often enough that this is
                           # convenient.
   local skip_egg="false"; # Skip even the 'egg_info' step, because nothing needs
                           # to be built.
@@ -717,13 +717,8 @@
     "python-ldap" "ldap" "${ld}" \
     "${pypi}/p/python-ldap/${ld}.tar.gz";
 
-  # XXX actually vObject should be imported in-place.
-  py_dependency -fe -i "" -r 219 \
-    "vobject" "vobject" "vobject" \
-    "http://svn.osafoundation.org/vobject/trunk";
-
   # XXX actually PyCalendar should be imported in-place.
-  py_dependency -fe -i "src" -r 169 \
+  py_dependency -fe -i "src" -r 171 \
     "pycalendar" "pycalendar" "pycalendar" \
     "http://svn.mulberrymail.com/repos/PyCalendar/branches/server";
 

Modified: CalendarServer/trunk/support/patchapply
===================================================================
--- CalendarServer/trunk/support/patchapply	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/support/patchapply	2011-08-09 19:35:21 UTC (rev 7862)
@@ -23,8 +23,8 @@
 # Apply patches to dependent projects.
 #
 
-#projects = ("Twisted", "vobject", "dateutil", "xattr")
-projects = ("vobject",)
+#projects = ("Twisted", "dateutil", "xattr")
+projects = ()
 cwd = os.getcwd()
 libpatches = os.path.join(cwd, "lib-patches")
 

Modified: CalendarServer/trunk/support/patchmaker
===================================================================
--- CalendarServer/trunk/support/patchmaker	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/support/patchmaker	2011-08-09 19:35:21 UTC (rev 7862)
@@ -23,8 +23,8 @@
 # Create a new set of patch files for dependent projects.
 #
 
-#projects = ("Twisted", "vobject", "dateutil", "xattr")
-projects = ("vobject",)
+#projects = ("Twisted", "dateutil", "xattr")
+projects = ()
 cwd = os.getcwd()
 libpatches = os.path.join(cwd, "lib-patches")
 

Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py	2011-08-09 02:30:51 UTC (rev 7861)
+++ CalendarServer/trunk/twistedcaldav/ical.py	2011-08-09 19:35:21 UTC (rev 7862)
@@ -195,6 +195,13 @@
     def __ge__(self, other): return self.__eq__(other) or self.__gt__(other)
     def __le__(self, other): return self.__eq__(other) or self.__lt__(other)
 
+    def duplicate(self):
+        """
+        Duplicate this object and all its contents.
+        @return: the duplicated calendar.
+        """
+        return Property(None, None, None, pycalendar=self._pycalendar.duplicate())
+        
     def name  (self): return self._pycalendar.getName()
 
     def value (self): return self._pycalendar.getValue().getValue()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110809/9f3ebe23/attachment-0001.html>


More information about the calendarserver-changes mailing list