[CalendarServer-changes] [5225] CalendarServer/branches/users/cdaboo/shared-calendars-5187
source_changes at macosforge.org
source_changes at macosforge.org
Mon Mar 1 10:49:49 PST 2010
Revision: 5225
http://trac.macosforge.org/projects/calendarserver/changeset/5225
Author: cdaboo at apple.com
Date: 2010-03-01 10:49:46 -0800 (Mon, 01 Mar 2010)
Log Message:
-----------
Merge from trunk.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/root.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/test/test_root.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/anonymize.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/fixcalendardata.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/gateway.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/test/test_gateway.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/contrib/migration/59_calendarmigrator.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/doc/Admin/ExtendedLogItems.txt
CalendarServer/branches/users/cdaboo/shared-calendars-5187/run
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/datetime.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/test/test_datetime.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/dateops.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/addressbook.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/wiki.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/instance.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/report_addressbook_findshared.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/icaldiff.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/scheduler.py
Added Paths:
-----------
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/test/test_wiki.py
Removed Paths:
-------------
CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_dateops.py
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/root.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/root.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/root.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -1,3 +1,4 @@
+# -*- test-case-name: calendarserver.provision.test.test_root -*-
##
# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
#
@@ -28,6 +29,7 @@
from twext.python.log import Logger
+from twistedcaldav.resource import CalDAVComplianceMixIn
from twistedcaldav.extensions import DAVFile, CachingPropertyStore
from twistedcaldav.extensions import DirectoryPrincipalPropertySearchMixIn
from twistedcaldav.extensions import ReadOnlyResourceMixIn
@@ -41,7 +43,7 @@
log = Logger()
-class RootResource (ReadOnlyResourceMixIn, DirectoryPrincipalPropertySearchMixIn, DAVFile):
+class RootResource (ReadOnlyResourceMixIn, DirectoryPrincipalPropertySearchMixIn, CalDAVComplianceMixIn, DAVFile):
"""
A special root resource that contains support checking SACLs
as well as adding responseFilters.
@@ -165,44 +167,48 @@
# server for the corresponding record name. If that maps to a
# principal, assign that to authnuser.
- wikiConfig = config.Authentication.Wiki
- cookies = request.headers.getHeader("cookie")
- if wikiConfig["Enabled"] and cookies is not None:
- for cookie in cookies:
- if cookie.name == wikiConfig["Cookie"]:
- token = cookie.value
- break
- else:
- token = None
+ if not hasattr(request, "checkedWiki"):
+ # Only do this once per request
+ request.checkedWiki = True
- if token is not None and token != "unauthenticated":
- log.debug("Wiki sessionID cookie value: %s" % (token,))
- proxy = Proxy(wikiConfig["URL"])
- try:
- username = (yield proxy.callRemote(wikiConfig["UserMethod"], token))
- except Exception, e:
- log.error("Failed to look up wiki token (%s)" % (e,))
- username = None
+ wikiConfig = config.Authentication.Wiki
+ cookies = request.headers.getHeader("cookie")
+ if wikiConfig["Enabled"] and cookies is not None:
+ for cookie in cookies:
+ if cookie.name == wikiConfig["Cookie"]:
+ token = cookie.value
+ break
+ else:
+ token = None
- if username is not None:
- log.debug("Wiki lookup returned user: %s" % (username,))
- principal = None
- directory = request.site.resource.getDirectory()
- record = directory.recordWithShortName("users", username)
- log.debug("Wiki user record for user %s : %s" % (username, record))
- if record:
- # Note: record will be None if it's a /Local/Default user
- for collection in self.principalCollections():
- principal = collection.principalForRecord(record)
- if principal is not None:
- break
+ if token is not None and token != "unauthenticated":
+ log.debug("Wiki sessionID cookie value: %s" % (token,))
+ proxy = Proxy(wikiConfig["URL"])
+ try:
+ username = (yield proxy.callRemote(wikiConfig["UserMethod"], token))
+ except Exception, e:
+ log.error("Failed to look up wiki token (%s)" % (e,))
+ username = None
- if principal:
- log.debug("Found wiki principal and setting authnuser and authzuser")
- request.authzUser = request.authnUser = davxml.Principal(
- davxml.HRef.fromString("/principals/__uids__/%s/" % (record.guid,))
- )
+ if username is not None:
+ log.debug("Wiki lookup returned user: %s" % (username,))
+ principal = None
+ directory = request.site.resource.getDirectory()
+ record = directory.recordWithShortName("users", username)
+ log.debug("Wiki user record for user %s : %s" % (username, record))
+ if record:
+ # Note: record will be None if it's a /Local/Default user
+ for collection in self.principalCollections():
+ principal = collection.principalForRecord(record)
+ if principal is not None:
+ break
+ if principal:
+ log.debug("Wiki-authenticated principal %s being assigned to authnUser" % (record.guid,))
+ request.authnUser = davxml.Principal(
+ davxml.HRef.fromString("/principals/__uids__/%s/" % (record.guid,))
+ )
+
# We don't want the /inbox resource to pay attention to SACLs because
# we just want it to use the hard-coded ACL for the imip reply user.
# The /timezones resource is used by the wiki web calendar, so open
@@ -223,6 +229,7 @@
else:
wikiName = segments[2][5:]
if wikiName:
+ log.debug("Wiki principal %s being assigned to authzUser" % (wikiName,))
request.authzUser = davxml.Principal(
davxml.HRef.fromString("/principals/wikis/%s/" % (wikiName,))
)
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/test/test_root.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/test/test_root.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/provision/test/test_root.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -17,7 +17,8 @@
import os
from twisted.cred.portal import Portal
-from twisted.internet.defer import inlineCallbacks, maybeDeferred
+from twisted.internet.defer import inlineCallbacks, maybeDeferred, returnValue
+
from twext.web2 import http_headers
from twext.web2 import responsecode
from twext.web2 import server
@@ -34,6 +35,7 @@
from twistedcaldav.directory.test.test_xmlfile import xmlFile, augmentsFile
from calendarserver.provision.root import RootResource
+from twistedcaldav.config import config
from twistedcaldav.directory import augment
class FakeCheckSACL(object):
@@ -54,6 +56,12 @@
def setUp(self):
super(RootTests, self).setUp()
+ # XXX make sure that the config hooks have been run, so that we get the
+ # default RootResourceACL key is set and traversal works. This is not
+ # great, and the ACLs supported by the root resource should really be
+ # an _attribute_ on the root resource. -glyph
+ config.update({})
+
self.docroot = self.mktemp()
os.mkdir(self.docroot)
@@ -81,6 +89,41 @@
self.site = server.Site(self.root)
+
+
+class ComplianceTests(RootTests):
+ """
+ Tests to verify CalDAV compliance of the root resource.
+ """
+
+ @inlineCallbacks
+ def issueRequest(self, segments, method='GET'):
+ """
+ Get a resource from a particular path from the root URI, and return a
+ Deferred which will fire with (something adaptable to) an HTTP response
+ object.
+ """
+ request = SimpleRequest(self.site, method, ('/'.join([''] + segments)))
+ rsrc = self.root
+ while segments:
+ rsrc, segments = (yield maybeDeferred(rsrc.locateChild, request, segments))
+
+ result = yield rsrc.renderHTTP(request)
+ returnValue(result)
+
+
+ @inlineCallbacks
+ def test_optionsIncludeCalendar(self):
+ """
+ OPTIONS request should include a DAV header that mentions the
+ addressbook capability.
+ """
+ self.patch(config, 'EnableCardDAV', True)
+ response = yield self.issueRequest([''], 'OPTIONS')
+ self.assertIn('addressbook', response.headers.getHeader('DAV'))
+
+
+
class SACLTests(RootTests):
@inlineCallbacks
@@ -324,3 +367,18 @@
d = self.send(request, gotResponse1)
return d
+
+class WikiTests(RootTests):
+
+ @inlineCallbacks
+ def test_oneTime(self):
+ """
+ Make sure wiki auth lookup is only done once per request;
+ request.checkedWiki will be set to True
+ """
+
+ request = SimpleRequest(self.site, "GET", "/principals/")
+
+ resrc, segments = (yield maybeDeferred(self.root.locateChild, request, ['principals']))
+ resrc, segments = (yield maybeDeferred(resrc.locateChild, request, ['principals']))
+ self.assertTrue(request.checkedWiki)
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/anonymize.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/anonymize.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/anonymize.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -23,7 +23,6 @@
import datetime
import hashlib
import os
-import plistlib
import random
import shutil
import sys
@@ -33,6 +32,8 @@
import xattr
import zlib
+from twext.python.plistlib import readPlistFromString
+
COPY_CAL_XATTRS = (
'WebDAV:{DAV:}resourcetype',
'WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-timezone',
@@ -375,7 +376,7 @@
if child.returncode:
raise DirectoryError(error)
else:
- records = plistlib.readPlistFromString(output)
+ records = readPlistFromString(output)
random.shuffle(records) # so we don't go alphabetically
for record in records:
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/fixcalendardata.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/fixcalendardata.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/fixcalendardata.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -16,7 +16,6 @@
# limitations under the License.
##
-from plistlib import readPlist
import re
import datetime
import getopt
@@ -26,6 +25,8 @@
import time
import xattr
+from twext.python.plistlib import readPlist
+
PLIST_FILE = "/etc/caldavd/caldavd.plist"
SCAN_FILE = "problems.txt"
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/gateway.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/gateway.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -20,10 +20,11 @@
from grp import getgrnam
from pwd import getpwnam
import os
-import plistlib
import sys
import xml
+from twext.python.plistlib import readPlistFromString, writePlistToString
+
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
from twisted.python.util import switchUID
@@ -115,7 +116,7 @@
#
rawInput = sys.stdin.read()
try:
- plist = plistlib.readPlistFromString(rawInput)
+ plist = readPlistFromString(rawInput)
except xml.parsers.expat.ExpatError, e:
respondWithError(str(e))
return
@@ -438,10 +439,10 @@
respond(command, result)
def respond(command, result):
- sys.stdout.write(plistlib.writePlistToString( { 'command' : command['command'], 'result' : result } ) )
+ sys.stdout.write(writePlistToString( { 'command' : command['command'], 'result' : result } ) )
def respondWithError(msg, status=1):
- sys.stdout.write(plistlib.writePlistToString( { 'error' : msg, } ) )
+ sys.stdout.write(writePlistToString( { 'error' : msg, } ) )
"""
try:
reactor.stop()
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/test/test_gateway.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/calendarserver/tools/test/test_gateway.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -15,7 +15,7 @@
##
import os
-import plistlib
+from twext.python.plistlib import readPlistFromString
import xml
from twext.python.filepath import CachingFilePath as FilePath
@@ -90,7 +90,7 @@
reactor.spawnProcess(CapturingProcessProtocol(deferred, command), python, args, env=os.environ, path=cwd)
output = yield deferred
try:
- plist = plistlib.readPlistFromString(output)
+ plist = readPlistFromString(output)
except xml.parsers.expat.ExpatError, e:
print "Error (%s) parsing (%s)" % (e, output)
raise
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/contrib/migration/59_calendarmigrator.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/contrib/migration/59_calendarmigrator.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/contrib/migration/59_calendarmigrator.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -23,11 +23,11 @@
import datetime
import optparse
import os
-import plistlib
import shutil
-import subprocess
import sys
+from twext.python.plistlib import readPlist, writePlist
+
LAUNCHD_KEY = "org.calendarserver.calendarserver"
LOG = "/Library/Logs/Migration/calendarmigrator.log"
SERVICE_NAME = "calendar"
@@ -134,10 +134,10 @@
if name == "caldavd.plist":
# Migrate certain settings from the old plist to new:
log("Parsing %s" % (oldPath,))
- oldPlist = plistlib.readPlist(oldPath)
+ oldPlist = readPlist(oldPath)
if os.path.exists(newPath):
log("Parsing %s" % (newPath,))
- newPlist = plistlib.readPlist(newPath)
+ newPlist = readPlist(newPath)
log("Removing %s" % (newPath,))
os.remove(newPath)
else:
@@ -145,7 +145,7 @@
log("Processing %s" % (oldPath,))
mergePlist(oldPlist, newPlist)
log("Writing %s" % (newPath,))
- plistlib.writePlist(newPlist, newPath)
+ writePlist(newPlist, newPath)
else:
# Copy the file over, overwriting copy in newConfigDir
@@ -208,7 +208,7 @@
overridesPath = os.path.join(source, LAUNCHD_OVERRIDES)
if os.path.isfile(overridesPath):
- overrides = plistlib.readPlist(overridesPath)
+ overrides = readPlist(overridesPath)
try:
return overrides[service]['Disabled']
except KeyError:
@@ -217,7 +217,7 @@
prefsPath = os.path.join(source, LAUNCHD_PREFS_DIR, "%s.plist" % service)
if os.path.isfile(prefsPath):
- prefs = plistlib.readPlist(prefsPath)
+ prefs = readPlist(prefsPath)
try:
return prefs['Disabled']
except KeyError:
@@ -238,11 +238,11 @@
overridesPath = os.path.join(target, LAUNCHD_OVERRIDES)
if os.path.isfile(overridesPath):
- overrides = plistlib.readPlist(overridesPath)
+ overrides = readPlist(overridesPath)
if not overrides.has_key(service):
overrides[service] = { }
overrides[service]['Disabled'] = disabled
- plistlib.writePlist(overrides, overridesPath)
+ writePlist(overrides, overridesPath)
class ServiceStateError(Exception):
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/doc/Admin/ExtendedLogItems.txt
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/doc/Admin/ExtendedLogItems.txt 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/doc/Admin/ExtendedLogItems.txt 2010-03-01 18:49:46 UTC (rev 5225)
@@ -63,6 +63,10 @@
Either ``reply`` or ``cancel`` depending on...???
+ ``fwd``
+
+ the value of the X-Forwarded-For header, if present
+
In the following example, we see a ``CalDAV:calendar-multiget``
``REPORT`` for 32 resources in a user's calendar, which was handled by
instance ``8459`` in 183.0ms, with one outstanding request (the one
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/run
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/run 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/run 2010-03-01 18:49:46 UTC (rev 5225)
@@ -157,7 +157,15 @@
fi;
if "${kill}" || "${restart}"; then
+ # mimic logic of 'fullServerPath' from twistedcaldav/config.py to find the pid file
pidfile="$(conf_read_key "PIDFile")";
+ serverroot="$(conf_read_key "ServerRoot")";
+ runroot="$(conf_read_key "RunRoot")";
+ # examine first character of $pidfile
+ if ( [ "${pidfile:0:1}" == "/" ] || [ "${pidfile:0:1}" == "." ]; ) then
+ pidfile=$pidfile;
+ else pidfile=${serverroot}/${runroot}/${pidfile};
+ fi
if [ ! -r "${pidfile}" ]; then
echo "Unreadable PID file: ${pidfile}";
exit 1
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/datetime.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/datetime.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/datetime.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -19,28 +19,36 @@
"""
__all__ = [
+ "utc",
+ "tzWithID",
"dateordatetime",
"timerange",
- "utc",
+ "asTimeZone",
+ "asUTC",
+ "iCalendarString",
]
date = __import__("datetime").date
datetime = __import__("datetime").datetime
-from vobject.icalendar import dateTimeToString, dateToString, utc
+from vobject.icalendar import dateTimeToString, dateToString
+from vobject.icalendar import utc, getTzid as tzWithID
+# FIXME, add constants for begining/end of time
+
class dateordatetime(object):
def __init__(self, dateOrDatetime, defaultTZ=None):
"""
@param dateOrDatetime: a L{date} or L{datetime}.
"""
- assert dateOrDatetime is not None
+ assert dateOrDatetime is not None, "dateOrDatetime is None"
self._dateOrDatetime = dateOrDatetime
if isinstance(dateOrDatetime, datetime):
self._isDatetime = True
else:
+ assert isinstance(dateOrDatetime, date)
self._isDatetime = False
self.defaultTZ = defaultTZ
@@ -68,22 +76,43 @@
def __eq__(self, other):
if isinstance(other, dateordatetime):
- other = other.dateOrDatetime
- return self._dateOrDatetime == other
+ other = other.dateOrDatetime()
+ dt1, dt2 = self._comparableDatetimes(other)
+ return dt1 == dt2
- def __cmp__(self, other):
- if not isinstance(other, (date, datetime, dateordatetime)):
+ def __ne__(self, other):
+ if isinstance(other, dateordatetime):
+ other = other.dateOrDatetime()
+ dt1, dt2 = self._comparableDatetimes(other)
+ return dt1 != dt2
+
+ def __lt__(self, other):
+ if not isinstance(other, comparableTypes):
return NotImplemented
+ dt1, dt2 = self._comparableDatetimes(other)
+ return dt1 < dt2
+ def __le__(self, other):
+ if not isinstance(other, comparableTypes):
+ return NotImplemented
dt1, dt2 = self._comparableDatetimes(other)
+ return dt1 <= dt2
- if dt1 == dt2:
- return 0
- elif dt1 < dt2:
- return -1
- else:
- return 1
+ def __gt__(self, other):
+ if not isinstance(other, comparableTypes):
+ return NotImplemented
+ dt1, dt2 = self._comparableDatetimes(other)
+ return dt1 > dt2
+ def __ge__(self, other):
+ if not isinstance(other, comparableTypes):
+ return NotImplemented
+ dt1, dt2 = self._comparableDatetimes(other)
+ return dt1 >= dt2
+
+ def __hash__(self):
+ return self._dateOrDatetime.__hash__()
+
def __sub__(self, other):
if not isinstance(other, (date, datetime, dateordatetime)):
return NotImplemented
@@ -126,7 +155,9 @@
def asUTC(self):
return self.asTimeZone(utc)
+comparableTypes = (date, datetime, dateordatetime)
+
class timerange(object):
def __init__(self, start=None, end=None, duration=None):
"""
@@ -135,7 +166,7 @@
@param duration: a L{timedelta}, L{date} or L{datetime}
@param tzinfo: a L{tzinfo}
"""
- assert end is None or duration is None
+ assert end is None or duration is None, "end or duration must be None"
if start is None or isinstance(start, dateordatetime):
self._start = start
@@ -151,6 +182,54 @@
if duration is not None:
self._duration = duration
+ def __repr__(self):
+ return "timerange(%r, %s)" % (self.start(), self.end())
+
+ def __eq__(self, other):
+ if not isinstance(other, timerange):
+ return NotImplemented
+ if self.start() != other.start():
+ return False
+ return self.end() == other.end()
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __lt__(self, other):
+ if not isinstance(other, timerange):
+ return NotImplemented
+ if self.start() == other.start():
+ return self.end() < other.end()
+ else:
+ return self.start() < other.start()
+
+ def __le__(self, other):
+ if not isinstance(other, timerange):
+ return NotImplemented
+ if self.start() == other.start():
+ return self.end() <= other.end()
+ else:
+ return self.start() <= other.start()
+
+ def __gt__(self, other):
+ if not isinstance(other, timerange):
+ return NotImplemented
+ if self.start() == other.start():
+ return self.end() > other.end()
+ else:
+ return self.start() > other.start()
+
+ def __ge__(self, other):
+ if not isinstance(other, timerange):
+ return NotImplemented
+ if self.start() == other.start():
+ return self.end() >= other.end()
+ else:
+ return self.start() >= other.start()
+
+ def __hash__(self, other):
+ return hash((self.start(), self.end()))
+
def start(self):
return self._start
@@ -193,3 +272,27 @@
return self.end() < other.end() and self.end() > other.start()
else:
return False
+
+
+##
+# Convenience functions
+##
+
+def asTimeZone(dateOrDatetime, tzinfo):
+ """
+ Convert a L{date} or L{datetime} to the given time zone.
+ """
+ return dateordatetime(dateOrDatetime).asTimeZone(tzinfo).dateOrDatetime()
+
+def asUTC(dateOrDatetime):
+ """
+ Convert a L{date} or L{datetime} to UTC.
+ """
+ return dateordatetime(dateOrDatetime).asUTC().dateOrDatetime()
+
+def iCalendarString(dateOrDatetime):
+ """
+ Convert a L{date} or L{datetime} to a string appropriate for use
+ in an iCalendar property.
+ """
+ return dateordatetime(dateOrDatetime).iCalendarString()
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/test/test_datetime.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/test/test_datetime.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twext/python/test/test_datetime.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -15,48 +15,132 @@
##
from datetime import date, datetime, timedelta
+from dateutil.tz import tzstr
+from twisted.internet.defer import DeferredList
+
from twext.python.datetime import dateordatetime, timerange, utc
-from twistedcaldav.test.util import TestCase, testUnimplemented
+from twistedcaldav.test.util import TestCase, featureUnimplemented, testUnimplemented
-class DateTimeTests(TestCase):
- def test_date_date(self):
- d = date.today()
- dodt = dateordatetime(d)
- self.assertEquals(dodt.date(), d)
+tzUSEastern = tzstr("EST5EDT")
- def test_date_date_tz(self):
+
+def timezones(f):
+ """
+ Decorator for a test to be called with multiple timezones.
+ """
+ return lambda self: DeferredList([
+ d for d in (
+ f(self, tz) for tz in (None, utc, tzUSEastern)
+ ) if d is not None
+ ])
+
+def timeSeries(n):
+ now = datetime.now()
+ for i in range(0, n):
+ dodt = dateordatetime(now + timedelta(days=i))
+ dodt.n = "t%d" %(i+1,)
+ yield dodt
+
+
+class DatetimeTests(TestCase):
+ @timezones
+ def test_date_date(self, tz):
d = date.today()
- dodt = dateordatetime(d, defaultTZ=utc)
+ dodt = dateordatetime(d, defaultTZ=tz)
self.assertEquals(dodt.date(), d)
- def test_date_datetime(self):
+ @timezones
+ def test_date_datetime(self, tz):
d = date.today()
- dodt = dateordatetime(d)
- self.assertEquals(dodt.datetime(), datetime(d.year, d.month, d.day))
+ dodt = dateordatetime(d, defaultTZ=tz)
+ self.assertEquals(dodt.datetime(), datetime(d.year, d.month, d.day, tzinfo=tz))
- def test_date_datetime_tz(self):
- d = date.today()
- dodt = dateordatetime(d, defaultTZ=utc)
- self.assertEquals(dodt.datetime(), datetime(d.year, d.month, d.day, tzinfo=utc))
-
def test_datetime_date(self):
dt = datetime.now()
dodt = dateordatetime(dt)
self.assertEquals(dodt.date(), dt.date())
- def test_datetime_datetime(self):
+ @timezones
+ def test_datetime_datetime(self, tz):
dt = datetime.now()
- dodt = dateordatetime(dt)
+ dodt = dateordatetime(dt, defaultTZ=tz)
self.assertEquals(dodt.datetime(), dt)
- def test_datetime_datetime_tz(self):
- dt = datetime.now()
- dodt = dateordatetime(dt, defaultTZ=utc)
- self.assertEquals(dodt.datetime(), dt)
+ def test_compare_date_date(self):
+ return self._test_compare(date, date.today())
+ @timezones
+ def test_compare_date_datetime(self, tz):
+ return self._test_compare(date, datetime.now(), tz=tz)
+
+ def test_compare_datetime_date(self):
+ return self._test_compare(datetime, date.today())
+
+ @timezones
+ def test_compare_datetime_datetime(self, tz):
+ return self._test_compare(datetime, datetime.now(), tz=tz)
+
+ def _test_compare(self, baseclass, now, tz=None):
+ first = dateordatetime(now + timedelta(days=0))
+ second = dateordatetime(now + timedelta(days=1))
+ third = dateordatetime(now + timedelta(days=2))
+
+ def base(dodt):
+ if tz:
+ return dodt.dateOrDatetime().replace(tzinfo=tz)
+ else:
+ return dodt.dateOrDatetime()
+
+ #
+ # date & datetime's comparators do not correctly return
+ # NotImplemented when they should, which breaks comparison
+ # operators if date/datetime is first. Boo. Seriously weak.
+ #
+
+ self.assertTrue (first == base(first) )
+ #self.assertTrue (base(first) == first ) # Bug in datetime
+ self.assertTrue (first == base(first) )
+ self.assertTrue (first != base(second))
+ self.assertTrue (base(first) != second )
+ self.assertTrue (first != second )
+ self.assertTrue (first < second )
+ self.assertTrue (second < third )
+ self.assertTrue (first < base(second))
+ #self.assertTrue (base(second) < third ) # Bug in datetime
+ self.assertTrue (first < second )
+ self.assertTrue (second < third )
+ #self.assertTrue (base(first) < second )
+ self.assertTrue (second < base(third) ) # Bug in datetime
+ self.assertTrue (first <= second )
+ self.assertTrue (second <= third )
+ self.assertTrue (first <= base(second))
+ #self.assertTrue (base(second) <= third ) # Bug in datetime
+ self.assertTrue (first <= base(second))
+ #self.assertTrue (base(second) <= third ) # Bug in datetime
+ self.assertTrue (first <= second )
+ self.assertTrue (second <= third )
+ #self.assertTrue (base(first) <= second ) # Bug in datetime
+ self.assertTrue (second <= base(third) )
+ self.assertFalse(first > second )
+ self.assertFalse(second > third )
+ self.assertFalse(first > base(second))
+ #self.assertFalse(base(second) > third ) # Bug in datetime
+ self.assertFalse(first > second )
+ self.assertFalse(second > third )
+ #self.assertFalse(base(first) > second ) # Bug in datetime
+ self.assertFalse(second > base(third) )
+ self.assertFalse(first >= second )
+ self.assertFalse(second >= third )
+ self.assertFalse(first >= base(second))
+ #self.assertFalse(base(second) >= third ) # Bug in datetime
+ self.assertFalse(first >= second )
+ self.assertFalse(second >= third )
+ #self.assertFalse(base(first) >= second ) # Bug in datetime
+ self.assertFalse(second >= base(third) )
+
def test_date_iCalendarString(self):
d = date(2010, 2, 22)
dodt = dateordatetime(d)
@@ -72,20 +156,29 @@
dodt = dateordatetime(dt)
self.assertEquals(dodt.iCalendarString(), "20100222T174442Z")
- @testUnimplemented
def test_datetime_iCalendarString_tz(self):
- # Need to test a non-UTC timezone also
- raise NotImplementedError()
+ dt = datetime(2010, 2, 22, 17, 44, 42, 98303, tzinfo=tzUSEastern)
+ dodt = dateordatetime(dt)
+ self.assertEquals(dodt.iCalendarString(), "20100222T174442")
- @testUnimplemented
def test_asTimeZone(self):
- raise NotImplementedError()
+ dt = datetime(2010, 2, 22, 17, 44, 42, 98303, tzinfo=utc)
+ asUTC = dateordatetime(dt)
+ asEast = asUTC.asTimeZone(tzUSEastern)
+ self.assertEquals(asEast.datetime().tzinfo, tzUSEastern) # tz is changed
+ self.assertEquals(asEast.datetime().hour, 12) # hour is changed
+ self.assertEquals(asUTC, asEast) # still equal
- @testUnimplemented
def test_asUTC(self):
- raise NotImplementedError()
+ dt = datetime(2010, 2, 22, 17, 44, 42, 98303, tzinfo=tzUSEastern)
+ asEast = dateordatetime(dt)
+ asUTC = asEast.asTimeZone(utc)
+ self.assertEquals(asUTC.datetime().tzinfo, utc) # tz is changed
+ self.assertEquals(asUTC.datetime().hour, 22) # hour is changed
+ self.assertEquals(asEast, asUTC) # still equal
-class TimeRangeTests(TestCase):
+
+class TimerangeTests(TestCase):
def test_start(self):
start = datetime.now()
tr = timerange(start=start)
@@ -137,10 +230,212 @@
self.assertEquals(tr.duration(), duration)
@testUnimplemented
+ def test_compare(self):
+ raise NotImplemented
+
+ @featureUnimplemented
def test_overlapsWith(self):
- # Need a few tests; combinations of:
- # - start/end are None
- # - overlapping and not
- # - dates and datetimes
- # - timezones
- raise NotImplementedError()
+ t1, t2, t3, t4 = timeSeries(4)
+
+ d1 = dateordatetime(t1.date()); d1.n = "d1"
+ d2 = dateordatetime(t2.date()); d2.n = "d2"
+ d3 = dateordatetime(t3.date()); d3.n = "d3"
+ d4 = dateordatetime(t4.date()); d4.n = "d4"
+
+ for start1, end1, start2, end2, overlaps in (
+ # T-T-T-T
+
+ (t1, t2, t1, t2, True ),
+ (t1, t2, t1, t3, True ),
+ (t1, t2, t2, t3, False),
+ (t1, t2, t3, t4, False),
+
+ (t1, t3, t1, t2, True ),
+ (t1, t3, t2, t3, True ),
+
+ (t2, t3, t1, t2, False),
+ (t2, t3, t1, t3, True ),
+ (t2, t3, t1, t4, True ),
+
+ (t2, t4, t1, t3, True ),
+
+ (t3, t4, t1, t2, False),
+
+ # D-T-T-T
+
+ (d1, t2, t1, t2, True ),
+ (d1, t2, t1, t3, True ),
+ (d1, t2, t2, t3, False),
+ (d1, t2, t3, t4, False),
+
+ (d1, t3, t1, t2, True ),
+ (d1, t3, t2, t3, True ),
+
+ (d2, t3, t1, t2, False),
+ (d2, t3, t1, t3, True ),
+ (d2, t3, t1, t4, True ),
+
+ (d2, t4, t1, t3, True ),
+
+ (d3, t4, t1, t2, False),
+
+ # T-D-T-T
+
+ (t1, d2, t1, t2, True ),
+ (t1, d2, t1, t3, True ),
+ (t1, d2, t2, t3, False),
+ (t1, d2, t3, t4, False),
+
+ (t1, d3, t1, t2, True ),
+ (t1, d3, t2, t3, True ),
+
+ (t2, d3, t1, t2, False),
+ (t2, d3, t1, t3, True ),
+ (t2, d3, t1, t4, True ),
+
+ (t2, d4, t1, t3, True ),
+
+ (t3, d4, t1, t2, False),
+
+ # T-T-D-T
+
+ (t1, t2, d1, t2, True ),
+ (t1, t2, d1, t3, True ),
+ (t1, t2, d2, t3, False),
+ (t1, t2, d3, t4, False),
+
+ (t1, t3, d1, t2, True ),
+ (t1, t3, d2, t3, True ),
+
+ (t2, t3, d1, t2, False),
+ (t2, t3, d1, t3, True ),
+ (t2, t3, d1, t4, True ),
+
+ (t2, t4, d1, t3, True ),
+
+ (t3, t4, d1, t2, False),
+
+ # T-T-T-D
+
+ (t1, t2, t1, d2, True ),
+ (t1, t2, t1, d3, True ),
+ (t1, t2, t2, d3, False),
+ (t1, t2, t3, d4, False),
+
+ (t1, t3, t1, d2, True ),
+ (t1, t3, t2, d3, True ),
+
+ (t2, t3, t1, d2, False),
+ (t2, t3, t1, d3, True ),
+ (t2, t3, t1, d4, True ),
+
+ (t2, t4, t1, d3, True ),
+
+ (t3, t4, t1, d2, False),
+
+ # D-D-T-T
+
+ (d1, d2, t1, t2, True ),
+ (d1, d2, t1, t3, True ),
+ (d1, d2, t2, t3, False),
+ (d1, d2, t3, t4, False),
+
+ (d1, d3, t1, t2, True ),
+ (d1, d3, t2, t3, True ),
+
+ (d2, d3, t1, t2, False),
+ (d2, d3, t1, t3, True ),
+ (d2, d3, t1, t4, True ),
+
+ (d2, d4, t1, t3, True ),
+
+ (d3, d4, t1, t2, False),
+
+ # T-D-D-T
+
+ (t1, d2, d1, t2, True ),
+ (t1, d2, d1, t3, True ),
+ (t1, d2, d2, t3, False),
+ (t1, d2, d3, t4, False),
+
+ (t1, d3, d1, t2, True ),
+ (t1, d3, d2, t3, True ),
+
+ (t2, d3, d1, t2, False),
+ (t2, d3, d1, t3, True ),
+ (t2, d3, d1, t4, True ),
+
+ (t2, d4, d1, t3, True ),
+
+ (t3, d4, d1, t2, False),
+
+ # D-T-D-T
+
+ (d1, t2, d1, t2, True ),
+ (d1, t2, d1, t3, True ),
+ (d1, t2, d2, t3, False),
+ (d1, t2, d3, t4, False),
+
+ (d1, t3, d1, t2, True ),
+ (d1, t3, d2, t3, True ),
+
+ (d2, t3, d1, t2, False),
+ (d2, t3, d1, t3, True ),
+ (d2, t3, d1, t4, True ),
+
+ (d2, t4, d1, t3, True ),
+
+ (d3, t4, d1, t2, False),
+
+ # T-T-D-D
+
+ (t1, t2, d1, d2, True ),
+ (t1, t2, d1, d3, True ),
+ (t1, t2, d2, d3, False),
+ (t1, t2, d3, d4, False),
+
+ (t1, t3, d1, d2, True ),
+ (t1, t3, d2, d3, True ),
+
+ (t2, t3, d1, d2, False),
+ (t2, t3, d1, d3, True ),
+ (t2, t3, d1, d4, True ),
+
+ (t2, t4, d1, d3, True ),
+
+ (t3, t4, d1, d2, False),
+
+ # D-D-D-D
+
+ (d1, d2, d1, d2, True ),
+ (d1, d2, d1, d3, True ),
+ (d1, d2, d2, d3, False),
+ (d1, d2, d3, d4, False),
+
+ (d1, d3, d1, d2, True ),
+ (d1, d3, d2, d3, True ),
+
+ (d2, d3, d1, d2, False),
+ (d2, d3, d1, d3, True ),
+ (d2, d3, d1, d4, True ),
+
+ (d2, d4, d1, d3, True ),
+
+ (d3, d4, d1, d2, False),
+ ):
+ #print start1.n, end1.n, start2.n, end2.n, overlaps
+
+ if overlaps:
+ test = self.assertTrue
+ error = "should overlap with"
+ else:
+ test = self.assertFalse
+ error = "should not overlap with"
+
+ tr1 = timerange(start1, end1)
+ tr2 = timerange(start2, end2)
+
+ test(
+ tr1.overlapsWith(tr2),
+ "%r (%s-%s) %s %r (%s-%s)" % (tr1, start1.n, end1.n, error, tr2, start2.n, end2.n)
+ )
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/dateops.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/dateops.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/dateops.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -19,13 +19,10 @@
"""
__all__ = [
- "normalizeStartEndDuration",
- "normalizeToUTC",
"normalizeForIndex",
"floatoffset",
"compareDateTime",
"differenceDateTime",
- "makeComparableDateTime",
"timeRangesOverlap",
"periodEnd",
"normalizePeriodList",
@@ -33,53 +30,10 @@
]
import datetime
-from vobject.icalendar import utc, dateTimeToString, dateToString
+from vobject.icalendar import utc
-def toString(dt):
- """
- Convert a L{datetime.date} or L{datetime.datetime} object to a string.
- @param dt: a L{datetime.date} or L{datetime.datetime} object to normalize
- @return: the converted date or datetime
- """
- if not isinstance(dt, datetime.date):
- raise TypeError("%r is not a datetime.date instance" % (dt,))
-
- return dateTimeToString(dt) if isinstance(dt, datetime.datetime) else dateToString(dt)
+from twext.python.datetime import dateordatetime
-def normalizeStartEndDuration(dtstart, dtend=None, duration=None):
- """
- Given a DTSTART and DTEND or DURATION (or neither), return a normalized tuple of
- DTSTART and DTEND.
- """
-
- assert dtend is None or duration is None, "Cannot specify both dtend and duration"
- if dtstart is not None:
- dtstart = normalizeToUTC(dtstart)
- if dtend is not None:
- dtend = normalizeToUTC(dtend)
- elif duration:
- dtend = dtstart + duration
-
- return (dtstart, dtend)
-
-def normalizeToUTC(dt):
- """
- Normalize a L{datetime.date} or L{datetime.datetime} object to UTC.
- If its a L{datetime.date}, just return it as-is.
- @param dt: a L{datetime.date} or L{datetime.datetime} object to normalize
- @return: the normalized date or datetime
- """
- if not isinstance(dt, datetime.date):
- raise TypeError("%r is not a datetime.date instance" % (dt,))
-
- if isinstance(dt, datetime.datetime):
- if dt.tzinfo is not None:
- return dt.astimezone(utc)
- else:
- return dt
- else:
- return dt
-
def normalizeForIndex(dt):
"""
Normalize a L{datetime.date} or L{datetime.datetime} object for use in the Index.
@@ -113,22 +67,9 @@
tzinfo = utc
return dt.astimezone(tzinfo).replace(tzinfo=utc)
-def compareDateTime(dt1, dt2, defaulttz = None):
- """
- Compare two L{datetime.date} or L{datetime.datetime} objects in
- a transparent manner that does not depend on the nature of the objects
- and whether timesones are set.
- @param dt1: a L{datetime.datetime} or L{datetime.date} specifying a date to test.
- @param dt2: a L{datetime.datetime} or L{datetime.date} specifying a date to test.
- @param defaulttz: a L{datetime.tzinfo} for the VTIMEZONE object to use if one of the
- datetime's is a date or floating.
- @return: 0 if dt1 == dt2,
- -1 if dt1 < dt2
- 1 if dt1 > dt2
- """
-
- dt1, dt2 = makeComparableDateTime(dt1, dt2, defaulttz)
-
+def compareDateTime(dt1, dt2, defaulttz=None):
+ dt1 = dateordatetime(dt1, defaultTZ=defaulttz)
+ dt2 = dateordatetime(dt2, defaultTZ=defaulttz)
if dt1 == dt2:
return 0
elif dt1 < dt2:
@@ -137,78 +78,21 @@
return 1
def differenceDateTime(start, end, defaulttz = None):
- """
- Determines the difference between start and end datetime's returning the duration.
- NB This handles the case where start and end are not of the same datetime type.
-
- @param start: a L{datetime.datetime} or L{datetime.date} specify the start time.
- @param end: a L{datetime.datetime} or L{datetime.date} specify the end time.
- @param defaulttz: a L{datetime.tzinfo} for the VTIMEZONE object to use if one of the
- datetime's is a date or floating.
- @return: the L{datetime.timedelta} for the difference between the two.
- """
+ return dateordatetime(end, defaultTZ=defaulttz) - dateordatetime(start)
- start, end = makeComparableDateTime(start, end, defaulttz)
- return end - start
+#def timeRangesOverlap(start1, end1, start2, end2, defaulttz = None):
+# def dodt(d):
+# if d is None:
+# return None
+# else:
+# return dateordatetime(d, defaulttz)
+#
+# dodt1 = timerange(dodt(start1), dodt(end1))
+# dodt2 = timerange(dodt(start2), dodt(end2))
+#
+# return dodt1.overlapsWith(dodt2)
-def makeComparableDateTime(dt1, dt2, defaulttz = None):
- """
- Ensure that the two datetime objects passed in are of a comparable type for arithmetic
- and comparison operations..
-
- @param start: a L{datetime.datetime} or L{datetime.date} specifying one time.
- @param end: a L{datetime.datetime} or L{datetime.date} specifying another time.
- @param defaulttz: a L{datetime.tzinfo} for the VTIMEZONE object to use if one of the
- datetime's is a date or floating.
- @return: a C{tuple} of two L{datetime.xx}'s for the comparable items.
- """
-
- for dt in (dt1, dt2):
- if not isinstance(dt, datetime.date):
- raise TypeError("%r is not a datetime.date instance" % (dt,))
-
- # Pick appropriate tzinfo
- tzi = [None]
- def getTzinfo(dtzi):
- if tzi[0] is None:
- if defaulttz is not None:
- tzi[0] = defaulttz
- else:
- return dtzi
- return tzi[0]
-
- # If any one argument is a datetime.date, convert that into a datetime.datetime
- # with the time set to midnight and the same timezone as the other argument
- if isinstance(dt1, datetime.datetime) and not isinstance(dt2, datetime.datetime):
- dt2 = datetime.datetime(dt2.year, dt2.month, dt2.day, 0, 0, 0, 0, getTzinfo(dt1.tzinfo))
- elif not isinstance(dt1, datetime.datetime) and isinstance(dt2, datetime.datetime):
- dt1 = datetime.datetime(dt1.year, dt1.month, dt1.day, 0, 0, 0, 0, getTzinfo(dt2.tzinfo))
- elif isinstance(dt1, datetime.datetime) and isinstance(dt2, datetime.datetime):
- # Ensure that they both have or have not a tzinfo
- if (dt1.tzinfo is not None and dt2.tzinfo is None):
- dt2 = dt2.replace(tzinfo=getTzinfo(dt1.tzinfo))
- elif (dt1.tzinfo is None and dt2.tzinfo is not None):
- dt1 = dt1.replace(tzinfo=getTzinfo(dt2.tzinfo))
-
- return (dt1, dt2)
-
def timeRangesOverlap(start1, end1, start2, end2, defaulttz = None):
- """
- Determines whether two time ranges overlap.
- @param start1: a L{datetime.datetime} or L{datetime.date} specifying the
- beginning of the first time span.
- @param end1: a L{datetime.datetime} or L{datetime.date} specifying the
- end of the first time span. C{end} may be None, indicating that
- there is no end date.
- @param start2: a L{datetime.datetime} or L{datetime.date} specifying the
- beginning of the second time span.
- @param end2: a L{datetime.datetime} or L{datetime.date} specifying the
- end of the second time span. C{end} may be None, indicating that
- there is no end date.
- @param defaulttz: a L{datetime.tzinfo} for the VTIMEZONE object to use if one of the
- datetime's is a date or floating.
- @return: True if the two given time spans overlap, False otherwise.
- """
# Can't compare datetime.date and datetime.datetime objects, so normalize
# to date if they are mixed.
if isinstance(start1, datetime.datetime) and (start2 is not None) and not isinstance(start2, datetime.datetime): start1 = start1.date()
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/addressbook.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/addressbook.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/addressbook.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -36,7 +36,7 @@
from twistedcaldav.config import config
from twistedcaldav.extensions import ReadOnlyResourceMixIn, DAVResource
-from twistedcaldav.resource import CalDAVResource, SearchAddressBookResource, SearchAllAddressBookResource
+from twistedcaldav.resource import CalDAVResource, SearchAddressBookResource, SearchAllAddressBookResource, CalDAVComplianceMixIn
from twistedcaldav.directory.idirectory import IDirectoryService
from twistedcaldav.directory.resource import AutoProvisioningResourceMixIn
@@ -51,6 +51,7 @@
class DirectoryAddressBookProvisioningResource (
AutoProvisioningResourceMixIn,
ReadOnlyResourceMixIn,
+ CalDAVComplianceMixIn,
DAVResource,
):
def defaultAccessControlList(self):
Copied: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/test/test_wiki.py (from rev 5224, CalendarServer/trunk/twistedcaldav/directory/test/test_wiki.py)
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/test/test_wiki.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/test/test_wiki.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -0,0 +1,34 @@
+##
+# Copyright (c) 2005-2010 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.
+##
+
+from twistedcaldav.test.util import TestCase
+from twistedcaldav.directory.wiki import WikiDirectoryService, WikiDirectoryRecord
+
+class WikiTestCase(TestCase):
+ """
+ Test the Wiki Directory Service
+ """
+
+ def test_enabled(self):
+ service = WikiDirectoryService()
+ service.realmName = "Test"
+ record = WikiDirectoryRecord(service,
+ WikiDirectoryService.recordType_wikis,
+ "test",
+ None
+ )
+ self.assertTrue(record.enabled)
+ self.assertTrue(record.enabledForCalendaring)
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/wiki.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/wiki.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/wiki.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -123,6 +123,8 @@
enabledForCalendaring=True,
uid="%s%s" % (WikiDirectoryService.UIDPrefix, shortName),
)
+ # Wiki enabling doesn't come from augments db, so enable here...
+ self.enabled = True
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/ical.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -42,13 +42,12 @@
from vobject.base import Component as vComponent, ContentLine as vContentLine, ParseError as vParseError
from vobject.icalendar import TimezoneComponent, dateTimeToString, deltaToOffset, getTransition, stringToDate, stringToDateTime, stringToDurations, utc
-from twext.web2.dav.util import allDataFromStream
+from twext.python.log import Logger
+from twext.python.datetime import dateordatetime, timerange, asUTC, iCalendarString
from twext.web2.stream import IStream
+from twext.web2.dav.util import allDataFromStream
-from twext.python.log import Logger
-
-from twistedcaldav.dateops import compareDateTime, normalizeToUTC, timeRangesOverlap,\
- normalizeStartEndDuration, toString, normalizeForIndex, differenceDateTime
+from twistedcaldav.dateops import timeRangesOverlap, normalizeForIndex, differenceDateTime
from twistedcaldav.instance import InstanceList
from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
@@ -485,7 +484,7 @@
if component.name() == "VTIMEZONE":
continue
rid = component.getRecurrenceIDUTC()
- if rid and recurrence_id and compareDateTime(rid, recurrence_id) == 0:
+ if rid and recurrence_id and dateordatetime(rid) == recurrence_id:
return component
elif rid is None and recurrence_id is None:
return component
@@ -623,7 +622,7 @@
"""
dtstart = self.propertyNativeValue("DTSTART")
if dtstart is not None:
- return normalizeToUTC(dtstart)
+ return asUTC(dtstart)
else:
return None
@@ -643,7 +642,7 @@
dtend = dtstart + duration
if dtend is not None:
- return normalizeToUTC(dtend)
+ return asUTC(dtend)
else:
return None
@@ -662,7 +661,7 @@
due = dtstart + duration
if due is not None:
- return normalizeToUTC(due)
+ return asUTC(due)
else:
return None
@@ -675,7 +674,7 @@
rid = self.propertyNativeValue("RECURRENCE-ID")
if rid is not None:
- return normalizeToUTC(rid)
+ return asUTC(rid)
else:
return None
@@ -1104,7 +1103,7 @@
# Check whether recurrence-id matches an RDATE - if so it is OK
rdates = set()
for rdate in master.properties("RDATE"):
- rdates.update([normalizeToUTC(item) for item in rdate.value()])
+ rdates.update([asUTC(item) for item in rdate.value()])
if rid not in rdates:
# Check whether we have a truncated RRULE
rrules = master.properties("RRULE")
@@ -1847,7 +1846,7 @@
remaining -= 1
continue
rid = component.getRecurrenceIDUTC()
- if (toString(rid) if rid else "") not in rids:
+ if (iCalendarString(rid) if rid else "") not in rids:
self.removeComponent(component)
remaining -= 1
@@ -2012,28 +2011,28 @@
dtend = self.getProperty("DTEND")
duration = self.getProperty("DURATION")
- newdtstart, newdtend = normalizeStartEndDuration(
- dtstart.value(),
- dtend.value() if dtend is not None else None,
- duration.value() if duration is not None else None,
+ timeRange = timerange(
+ start = dtstart.value(),
+ end = dtend.value() if dtend is not None else None,
+ duration = duration.value() if duration is not None else None,
)
-
- dtstart.setValue(newdtstart)
+
+ dtstart.setValue(timeRange.start().asUTC().dateOrDatetime())
if "X-VOBJ-ORIGINAL-TZID" in dtstart.params():
dtstart.params()["ORIGINAL-TZID"] = dtstart.params()["X-VOBJ-ORIGINAL-TZID"]
del dtstart.params()["X-VOBJ-ORIGINAL-TZID"]
if dtend is not None:
- dtend.setValue(newdtend)
+ dtend.setValue(timeRange.end().asUTC().dateOrDatetime())
if "X-VOBJ-ORIGINAL-TZID" in dtend.params():
dtend.params()["ORIGINAL-TZID"] = dtend.params()["X-VOBJ-ORIGINAL-TZID"]
del dtend.params()["X-VOBJ-ORIGINAL-TZID"]
elif duration is not None:
self.removeProperty(duration)
- self.addProperty(Property("DTEND", newdtend))
+ self.addProperty(Property("DTEND", timeRange.end().asUTC().dateOrDatetime()))
exdates = self.properties("EXDATE")
for exdate in exdates:
- exdate.setValue([normalizeToUTC(value) for value in exdate.value()])
+ exdate.setValue([asUTC(value) for value in exdate.value()])
try:
del exdate.params()["TZID"]
except KeyError:
@@ -2041,7 +2040,7 @@
rid = self.getProperty("RECURRENCE-ID")
if rid is not None:
- rid.setValue(normalizeToUTC(rid.value()))
+ rid.setValue(asUTC(rid.value()))
try:
del rid.params()["TZID"]
except KeyError:
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/instance.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/instance.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/instance.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -20,10 +20,12 @@
import datetime
-from twistedcaldav.dateops import normalizeForIndex, compareDateTime, differenceDateTime, periodEnd
-
from vobject.icalendar import utc
+from twext.python.datetime import dateordatetime
+
+from twistedcaldav.dateops import normalizeForIndex, differenceDateTime, periodEnd
+
# The maximum number of instances we will expand out to.
# Raise a TooManyInstancesError exception if we exceed this.
max_allowed_instances = 1000
@@ -279,7 +281,7 @@
def _addMasterComponent(self, component, limit, start, end, duration):
# Always add first instance if included in range.
- if compareDateTime(start, limit) < 0:
+ if dateordatetime(start) < limit:
# dateutils does not do all-day - so convert to datetime.datetime
start = normalizeForIndex(start)
end = normalizeForIndex(end)
@@ -299,7 +301,7 @@
recur = component.getRRuleSet(True)
if recur is not None:
for startDate in recur:
- if compareDateTime(startDate, limit) >= 0:
+ if dateordatetime(startDate) >= limit:
self.limit = limit
break
endDate = startDate + duration
@@ -319,12 +321,12 @@
rid = normalizeForIndex(rid)
# Make sure start is within the limit
- if compareDateTime(start, limit) > 0 and compareDateTime(rid, limit) > 0:
+ if dateordatetime(start) > limit and dateordatetime(rid) > limit:
return
# Make sure override RECURRENCE-ID is a valid instance of the master
if got_master:
- if str(rid) not in self.instances and compareDateTime(rid, limit) <= 0:
+ if str(rid) not in self.instances and dateordatetime(rid) <= limit:
if self.ignoreInvalidInstances:
return
else:
@@ -373,7 +375,7 @@
"""
start = component.getStartDateUTC()
- if start is not None and (compareDateTime(start, limit) >= 0):
+ if start is not None and dateordatetime(start) >= limit:
# If the free busy is beyond the end of the range we want, ignore it
return
@@ -387,7 +389,7 @@
assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
for period in fb.value():
# Ignore if period starts after limit
- if compareDateTime(period[0], limit) >= 0:
+ if dateordatetime(period[0]) >= limit:
continue
start = normalizeForIndex(period[0])
end = normalizeForIndex(periodEnd(period))
@@ -405,7 +407,7 @@
"""
start = component.getStartDateUTC()
- if start is not None and (compareDateTime(start, limit) >= 0):
+ if start is not None and dateordatetime(start) >= limit:
# If the free busy is beyond the end of the range we want, ignore it
return
if start is None:
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/report_addressbook_findshared.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/report_addressbook_findshared.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/report_addressbook_findshared.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -26,7 +26,7 @@
"getWritersGroupForSharedAddressBookGroup",
]
-from plistlib import readPlist
+from twext.python.plistlib import readPlist
#import traceback
import opendirectory
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/icaldiff.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/icaldiff.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -15,10 +15,9 @@
##
from twext.python.log import Logger
+from twext.python.datetime import timerange, asUTC, iCalendarString
from twistedcaldav.config import config
-from twistedcaldav.dateops import normalizeToUTC, toString,\
- normalizeStartEndDuration
from twistedcaldav.ical import Component, Property
from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
from twistedcaldav.scheduling.itip import iTipGenerator
@@ -281,7 +280,7 @@
# Get all EXDATEs in UTC
exdates = set()
for exdate in master.properties("EXDATE"):
- exdates.update([normalizeToUTC(value) for value in exdate.value()])
+ exdates.update([asUTC(value) for value in exdate.value()])
return exdates, map, master
@@ -330,7 +329,7 @@
# Mark Attendee as DECLINED in the server instance
if self._attendeeDecline(self.newCalendar.overriddenComponent(rid)):
changeCausesReply = True
- changedRids.append(toString(rid) if rid else "")
+ changedRids.append(iCalendarString(rid) if rid else "")
else:
# We used to generate a 403 here - but instead we now ignore this error and let the server data
# override the client
@@ -406,7 +405,7 @@
#return False, False, (), None
changeCausesReply |= reply
if reply:
- changedRids.append(toString(rid) if rid else "")
+ changedRids.append(iCalendarString(rid) if rid else "")
# We need to derive instances for any declined using an EXDATE
for decline in sorted(declines):
@@ -417,7 +416,7 @@
self.newCalendar.addComponent(overridden)
if self._attendeeDecline(overridden):
changeCausesReply = True
- changedRids.append(toString(decline) if decline else "")
+ changedRids.append(iCalendarString(decline) if decline else "")
else:
self._logDiffError("attendeeMerge: Unable to override an instance to mark as DECLINED: %s" % (decline,))
return False, False, (), None
@@ -539,7 +538,7 @@
# Bad if EXDATEs have been removed
missing = serverProps[-1] - clientProps[-1]
if missing:
- log.debug("EXDATEs missing: %s" % (", ".join([toString(exdate) for exdate in missing]),))
+ log.debug("EXDATEs missing: %s" % (", ".join([iCalendarString(exdate) for exdate in missing]),))
return False
declines.extend(clientProps[-1] - serverProps[-1])
return True
@@ -554,10 +553,10 @@
dtend = component.getProperty("DTEND")
duration = component.getProperty("DURATION")
- newdtstart, newdtend = normalizeStartEndDuration(
- dtstart.value() if dtstart is not None else None,
- dtend.value() if dtend is not None else None,
- duration.value() if duration is not None else None,
+ timeRange = timerange(
+ start = dtstart.value() if dtstart is not None else None,
+ end = dtend.value() if dtend is not None else None,
+ duration = duration.value() if duration is not None else None,
)
newdue = None
@@ -566,17 +565,16 @@
duration = component.getProperty("DURATION")
if dtstart or duration:
- newdtstart, newdtend = normalizeStartEndDuration(
- dtstart.value() if dtstart is not None else None,
- None,
- duration.value() if duration is not None else None,
+ timeRange = timerange(
+ start = dtstart.value() if dtstart is not None else None,
+ duration = duration.value() if duration is not None else None,
)
else:
- newdtstart = newdtend = None
+ timeRange = timerange()
newdue = component.getProperty("DUE")
if newdue is not None:
- newdue = normalizeToUTC(newdue.value())
+ newdue = asUTC(newdue.value())
# Recurrence rules - we need to normalize the order of the value parts
newrrules = set()
@@ -591,15 +589,15 @@
newrdates = set()
rdates = component.properties("RDATE")
for rdate in rdates:
- newrdates.update([normalizeToUTC(value) for value in rdate.value()])
+ newrdates.update([asUTC(value) for value in rdate.value()])
# EXDATEs
newexdates = set()
exdates = component.properties("EXDATE")
for exdate in exdates:
- newexdates.update([normalizeToUTC(value) for value in exdate.value()])
+ newexdates.update([asUTC(value) for value in exdate.value()])
- return newdtstart, newdtend, newdue, newrrules, newrdates, newexdates
+ return timeRange.start(), timeRange.end(), newdue, newrrules, newrdates, newexdates
def _transferProperty(self, propName, serverComponent, clientComponent):
@@ -738,7 +736,7 @@
if addedChanges:
rid = comp1.getRecurrenceIDUTC()
- rids[toString(rid) if rid is not None else ""] = propsChanged
+ rids[iCalendarString(rid) if rid is not None else ""] = propsChanged
def _logDiffError(self, title):
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/itip.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -36,9 +36,9 @@
from vobject.icalendar import dateTimeToString
from twext.python.log import Logger
+from twext.python.datetime import asUTC, iCalendarString
from twistedcaldav.config import config
-from twistedcaldav.dateops import normalizeToUTC, toString
from twistedcaldav.ical import Property, iCalendarProductID, Component
log = Logger()
@@ -321,7 +321,7 @@
if attendee:
attendees.add(attendee)
if rids is not None and (partstat or private_comment):
- rids.add((toString(rid), partstat, private_comment,))
+ rids.add((iCalendarString(rid), partstat, private_comment,))
# Check for an invalid instance by itself
len_attendees = len(attendees)
@@ -542,7 +542,7 @@
comp.addProperty(Property("SEQUENCE", seq))
comp.addProperty(instance.getOrganizerProperty())
if instance_rid:
- comp.addProperty(Property("RECURRENCE-ID", normalizeToUTC(instance_rid)))
+ comp.addProperty(Property("RECURRENCE-ID", asUTC(instance_rid)))
def addProperties(propname):
for property in instance.properties(propname):
Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/scheduler.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/scheduling/scheduler.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -14,38 +14,41 @@
# limitations under the License.
##
-from twext.python.log import Logger, LoggingMixIn
-from twext.web2.dav.http import ErrorResponse
+import itertools
+import re
+import socket
+import urlparse
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.python.failure import Failure
+
+from twext.python.log import Logger, LoggingMixIn
+from twext.python.datetime import iCalendarString
from twext.web2 import responsecode
+from twext.web2.http import HTTPError, Response, StatusResponse
+from twext.web2.http_headers import MimeType
from twext.web2.dav import davxml
from twext.web2.dav.http import errorForFailure, messageForFailure, statusForFailure
-from twext.web2.http import HTTPError, Response, StatusResponse
-from twext.web2.http_headers import MimeType
+from twext.web2.dav.http import ErrorResponse
-from twistedcaldav import caldavxml, dateops
+from twistedcaldav import caldavxml
+from twistedcaldav.caldavxml import caldav_namespace
+from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.accounting import accountingEnabled, emitAccounting
-from twistedcaldav.caldavxml import caldav_namespace, TimeRange
from twistedcaldav.config import config
-from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.ical import Component
from twistedcaldav.scheduling import addressmapping
from twistedcaldav.scheduling.caldav import ScheduleViaCalDAV
-from twistedcaldav.scheduling.cuaddress import InvalidCalendarUser,\
- LocalCalendarUser, RemoteCalendarUser, EmailCalendarUser,\
- PartitionedCalendarUser
+from twistedcaldav.scheduling.cuaddress import InvalidCalendarUser
+from twistedcaldav.scheduling.cuaddress import LocalCalendarUser
+from twistedcaldav.scheduling.cuaddress import RemoteCalendarUser
+from twistedcaldav.scheduling.cuaddress import EmailCalendarUser
+from twistedcaldav.scheduling.cuaddress import PartitionedCalendarUser
from twistedcaldav.scheduling.imip import ScheduleViaIMip
from twistedcaldav.scheduling.ischedule import ScheduleViaISchedule
from twistedcaldav.scheduling.ischeduleservers import IScheduleServers
from twistedcaldav.scheduling.itip import iTIPRequestStatus
-import itertools
-import re
-import socket
-import urlparse
-
"""
CalDAV/Server-to-Server scheduling behavior.
"""
@@ -324,7 +327,7 @@
log.err("VFREEBUSY start or end not UTC: %s" % (self.calendar,))
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"), description="VFREEBUSY start or end not UTC"))
- self.timeRange = TimeRange(start=dateops.toString(dtstart), end=dateops.toString(dtend))
+ self.timeRange = caldavxml.TimeRange(start=iCalendarString(dtstart), end=iCalendarString(dtend))
self.timeRange.start = dtstart
self.timeRange.end = dtend
Deleted: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_dateops.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_dateops.py 2010-02-27 04:51:40 UTC (rev 5224)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_dateops.py 2010-03-01 18:49:46 UTC (rev 5225)
@@ -1,155 +0,0 @@
-##
-# Copyright (c) 2008 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.
-##
-
-
-import twistedcaldav.test.util
-from vobject.icalendar import utc, getTzid
-from twistedcaldav.dateops import normalizeStartEndDuration
-from twistedcaldav.timezones import TimezoneCache
-import datetime
-
-#TODO: add tests for all the methods in dateops
-
-class Tests_normalizeStartEndDuration (twistedcaldav.test.util.TestCase):
- """
- Test abstract SQL DB class
- """
-
- def setUp(self):
- super(Tests_normalizeStartEndDuration, self).setUp()
-
- TimezoneCache.create()
- TimezoneCache.activeCache.loadTimezone("America/New_York")
-
- def test_invalid(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
- end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=utc)
- duration = end - start
-
- self.assertRaises(AssertionError, normalizeStartEndDuration, start, end, duration)
-
- def test_start_only_utc(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
-
- newstart, newend = normalizeStartEndDuration(start)
- self.assertEqual(newstart, start)
- self.assertTrue(newend is None)
-
- def test_start_only_float(self):
- start = datetime.datetime(2008, 1, 1, 0, 0, 0)
-
- newstart, newend = normalizeStartEndDuration(start)
- self.assertEqual(newstart, start)
- self.assertTrue(newend is None)
-
- def test_start_only_date(self):
- start = datetime.date(2008, 1, 1)
-
- newstart, newend = normalizeStartEndDuration(start)
- self.assertEqual(newstart, start)
- self.assertTrue(newend is None)
-
- def test_start_only_tzid(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=getTzid("America/New_York"))
- utcstart = datetime.datetime(2008, 1, 1, 5, 0, 0, tzinfo=utc)
-
- newstart, newend = normalizeStartEndDuration(start)
- self.assertEqual(newstart, utcstart)
- self.assertTrue(newend is None)
-
- def test_start_end_utc(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
- end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=utc)
-
- newstart, newend = normalizeStartEndDuration(start, dtend=end)
- self.assertEqual(newstart, start)
- self.assertEqual(newend, end)
-
- def test_start_end_float(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0)
- end = datetime.datetime(2008, 1, 1, 1, 0, 0)
-
- newstart, newend = normalizeStartEndDuration(start, dtend=end)
- self.assertEqual(newstart, start)
- self.assertEqual(newend, end)
-
- def test_start_end_date(self):
-
- start = datetime.date(2008, 1, 1)
- end = datetime.date(2008, 1, 2)
-
- newstart, newend = normalizeStartEndDuration(start, dtend=end)
- self.assertEqual(newstart, start)
- self.assertEqual(newend, end)
-
- def test_start_end_tzid(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=getTzid("America/New_York"))
- end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=getTzid("America/New_York"))
- utcstart = datetime.datetime(2008, 1, 1, 5, 0, 0, tzinfo=utc)
- utcend = datetime.datetime(2008, 1, 1, 6, 0, 0, tzinfo=utc)
-
- newstart, newend = normalizeStartEndDuration(start, dtend=end)
- self.assertEqual(newstart, utcstart)
- self.assertEqual(newend, utcend)
-
- def test_start_duration_utc(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
- end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=utc)
- duration = end - start
-
- newstart, newend = normalizeStartEndDuration(start, duration=duration)
- self.assertEqual(newstart, start)
- self.assertEqual(newend, end)
-
- def test_start_duration_float(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0)
- end = datetime.datetime(2008, 1, 1, 1, 0, 0)
- duration = end - start
-
- newstart, newend = normalizeStartEndDuration(start, duration=duration)
- self.assertEqual(newstart, start)
- self.assertEqual(newend, end)
-
- def test_start_duration_date(self):
-
- start = datetime.date(2008, 1, 1)
- end = datetime.date(2008, 1, 2)
- duration = end - start
-
- newstart, newend = normalizeStartEndDuration(start, duration=duration)
- self.assertEqual(newstart, start)
- self.assertEqual(newend, end)
-
- def test_start_duration_tzid(self):
-
- start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=getTzid("America/New_York"))
- end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=getTzid("America/New_York"))
- utcstart = datetime.datetime(2008, 1, 1, 5, 0, 0, tzinfo=utc)
- utcend = datetime.datetime(2008, 1, 1, 6, 0, 0, tzinfo=utc)
- duration = end - start
-
- newstart, newend = normalizeStartEndDuration(start, duration=duration)
- self.assertEqual(newstart, utcstart)
- self.assertEqual(newend, utcend)
-
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100301/177a9805/attachment-0001.html>
More information about the calendarserver-changes
mailing list