[CalendarServer-changes] [10194] CalendarServer/branches/release/CalendarServer-4.3-dev
source_changes at macosforge.org
source_changes at macosforge.org
Thu Jan 3 10:29:57 PST 2013
Revision: 10194
http://trac.calendarserver.org//changeset/10194
Author: cdaboo at apple.com
Date: 2013-01-03 10:29:57 -0800 (Thu, 03 Jan 2013)
Log Message:
-----------
Fix for case where more than one attachment is in the same dropbox during a purge. Added more test cases to cover various multiple
attachment, multiple dropbox, multiple user scenarios.
Modified Paths:
--------------
CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/shell/cmd.py
CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/test/test_purge_old_events.py
CalendarServer/branches/release/CalendarServer-4.3-dev/txdav/common/datastore/sql.py
Modified: CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/shell/cmd.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/shell/cmd.py 2013-01-03 18:20:43 UTC (rev 10193)
+++ CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/shell/cmd.py 2013-01-03 18:29:57 UTC (rev 10194)
@@ -38,7 +38,7 @@
from calendarserver.version import version
from calendarserver.tap.util import getRootResource
from calendarserver.tools.tables import Table
-from calendarserver.tools.purge import purgeUID
+from calendarserver.tools.purge import PurgePrincipalService
from calendarserver.tools.shell.vfs import Folder, RootFolder
from calendarserver.tools.shell.directory import findRecords, summarizeRecords, recordInfo
@@ -658,8 +658,11 @@
total = 0
for record in records:
- count, assignments = (yield purgeUID(
- record.uid, directory, rootResource,
+ count, assignments = (yield PurgePrincipalService.purgeUIDs(
+ self.protocol.service.store,
+ directory,
+ rootResource,
+ (record.uid,),
verbose = False,
dryrun = dryRun,
completely = completely,
Modified: CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/test/test_purge_old_events.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/test/test_purge_old_events.py 2013-01-03 18:20:43 UTC (rev 10193)
+++ CalendarServer/branches/release/CalendarServer-4.3-dev/calendarserver/tools/test/test_purge_old_events.py 2013-01-03 18:29:57 UTC (rev 10194)
@@ -25,14 +25,13 @@
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.timezone import PyCalendarTimezone
-from twext.enterprise.dal.syntax import Update
+from twext.enterprise.dal.syntax import Update, Delete
from twext.web2.http_headers import MimeType
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.trial import unittest
from twistedcaldav.config import config
-from twistedcaldav.ical import Component
from twistedcaldav.vcard import Component as VCardComponent
from txdav.common.datastore.sql_tables import schema
@@ -91,7 +90,7 @@
END:VCALENDAR
""".replace("\n", "\r\n") % {"year": now - 5}
-OLD_ATTACHMENT_ICS = """BEGIN:VCALENDAR
+ATTACHMENT_ICS = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.1//EN
CALSCALE:GREGORIAN
@@ -128,119 +127,18 @@
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20100303T181216Z
-UID:57A5D1F6-9A57-4F74-9520-25C617F54B88
+UID:57A5D1F6-9A57-4F74-9520-25C617F54B88-%(uid)s
TRANSP:OPAQUE
SUMMARY:Ancient event with attachment
DTSTART;TZID=US/Pacific:%(year)s0308T111500
DTEND;TZID=US/Pacific:%(year)s0308T151500
DTSTAMP:20100303T181220Z
-X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/57A5D1F6-9A57-4F74-95
- 20-25C617F54B88.dropbox
+X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/%(dropboxid)s.dropbox
SEQUENCE:2
END:VEVENT
END:VCALENDAR
-""".replace("\n", "\r\n") % {"year": now - 5}
+""".replace("\n", "\r\n")
-OLD_ATTACHMENT2_ICS = """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.1//EN
-CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:US/Pacific
-BEGIN:STANDARD
-TZOFFSETFROM:-0700
-RRULE:FREQ=YEARLY;UNTIL=20061029T090000Z;BYMONTH=10;BYDAY=-1SU
-DTSTART:19621028T020000
-TZNAME:PST
-TZOFFSETTO:-0800
-END:STANDARD
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0800
-RRULE:FREQ=YEARLY;UNTIL=20060402T100000Z;BYMONTH=4;BYDAY=1SU
-DTSTART:19870405T020000
-TZNAME:PDT
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0800
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-DTSTART:20070311T020000
-TZNAME:PDT
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:-0700
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-DTSTART:20071104T020000
-TZNAME:PST
-TZOFFSETTO:-0800
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-CREATED:20100303T181216Z
-UID:57A5D1F6-9A57-4F74-9520-25C617F54B88-2
-TRANSP:OPAQUE
-SUMMARY:Ancient event with attachment #2
-DTSTART;TZID=US/Pacific:%(year)s0408T111500
-DTEND;TZID=US/Pacific:%(year)s0408T151500
-DTSTAMP:20100303T181220Z
-X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/57A5D1F6-9A57-4F74-95
- 20-25C617F54B88-2.dropbox
-SEQUENCE:2
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n") % {"year": now - 5}
-
-CURRENT_ATTACHMENT3_ICS = """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.1//EN
-CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:US/Pacific
-BEGIN:STANDARD
-TZOFFSETFROM:-0700
-RRULE:FREQ=YEARLY;UNTIL=20061029T090000Z;BYMONTH=10;BYDAY=-1SU
-DTSTART:19621028T020000
-TZNAME:PST
-TZOFFSETTO:-0800
-END:STANDARD
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0800
-RRULE:FREQ=YEARLY;UNTIL=20060402T100000Z;BYMONTH=4;BYDAY=1SU
-DTSTART:19870405T020000
-TZNAME:PDT
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0800
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-DTSTART:20070311T020000
-TZNAME:PDT
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:-0700
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-DTSTART:20071104T020000
-TZNAME:PST
-TZOFFSETTO:-0800
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-CREATED:20100303T181216Z
-UID:57A5D1F6-9A57-4F74-9520-25C617F54B88-3
-TRANSP:OPAQUE
-SUMMARY:Ancient event with attachment #3
-DTSTART;TZID=US/Pacific:%(year)s0408T111500
-DTEND;TZID=US/Pacific:%(year)s0408T151500
-DTSTAMP:20100303T181220Z
-X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/57A5D1F6-9A57-4F74-95
- 20-25C617F54B88-2.dropbox
-SEQUENCE:2
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n") % {"year": now + 1}
-
ENDLESS_ICS = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.1//EN
@@ -437,13 +335,19 @@
"calendar1" : {
"old.ics" : (OLD_ICS, metadata,),
"endless.ics" : (ENDLESS_ICS, metadata,),
- "oldattachment.ics" : (OLD_ATTACHMENT_ICS, metadata,),
+ "oldattachment1.ics" : (ATTACHMENT_ICS % {"year": now - 5, "uid": "1.1", "dropboxid": "1.1"}, metadata,),
+ "oldattachment2.ics" : (ATTACHMENT_ICS % {"year": now - 5, "uid": "1.2", "dropboxid": "1.2"}, metadata,),
+ "currentattachment3.ics" : (ATTACHMENT_ICS % {"year": now + 1, "uid": "1.3", "dropboxid": "1.3"}, metadata,),
}
},
"home2" : {
"calendar2" : {
"straddling.ics" : (STRADDLING_ICS, metadata,),
"recent.ics" : (RECENT_ICS, metadata,),
+ "oldattachment1.ics" : (ATTACHMENT_ICS % {"year": now - 5, "uid": "2.1", "dropboxid": "2.1"}, metadata,),
+ "oldattachment2.ics" : (ATTACHMENT_ICS % {"year": now + 1, "uid": "2.2", "dropboxid": "2.1"}, metadata,),
+ "oldattachment3.ics" : (ATTACHMENT_ICS % {"year": now - 5, "uid": "2.3", "dropboxid": "2.2"}, metadata,),
+ "oldattachment4.ics" : (ATTACHMENT_ICS % {"year": now - 5, "uid": "2.4", "dropboxid": "2.2"}, metadata,),
},
"calendar3" : {
"repeating_awhile.ics" : (REPEATING_AWHILE_ICS, metadata,),
@@ -479,7 +383,15 @@
yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
self.notifierFactory.reset()
+ txn = self._sqlCalendarStore.newTransaction()
+ Delete(
+ From=schema.ATTACHMENT,
+ Where=None
+ ).on(txn)
+ (yield txn.commit())
+
+
def storeUnderTest(self):
"""
Create and return a L{CalendarStore} for testing.
@@ -497,14 +409,18 @@
self.assertEquals(sorted(results),
sorted([
['home1', 'calendar1', 'old.ics', '1901-01-01 01:00:00'],
- ['home1', 'calendar1', 'oldattachment.ics', '1901-01-01 01:00:00'],
+ ['home1', 'calendar1', 'oldattachment1.ics', '1901-01-01 01:00:00'],
+ ['home1', 'calendar1', 'oldattachment2.ics', '1901-01-01 01:00:00'],
['home2', 'calendar3', 'repeating_awhile.ics', '1901-01-01 01:00:00'],
['home2', 'calendar2', 'recent.ics', '%s-03-04 22:15:00' % (now,)],
+ ['home2', 'calendar2', 'oldattachment1.ics', '1901-01-01 01:00:00'],
+ ['home2', 'calendar2', 'oldattachment3.ics', '1901-01-01 01:00:00'],
+ ['home2', 'calendar2', 'oldattachment4.ics', '1901-01-01 01:00:00'],
])
)
# Query for oldest event - actually with limited time caching, the oldest event
- # cannot be precisely know, all we get back is the first one in the sorted list
+ # cannot be precisely known, all we get back is the first one in the sorted list
# where each has the 1901 "dummy" time stamp to indicate a partial cache
results = (yield txn.eventsOlderThan(cutoff, batchSize=1))
self.assertEquals(len(results), 1)
@@ -520,11 +436,11 @@
count = (yield txn.removeOldEvents(cutoff, batchSize=1))
self.assertEquals(count, 1)
results = (yield txn.eventsOlderThan(cutoff))
- self.assertEquals(len(results), 3)
+ self.assertEquals(len(results), 7)
# Remove remaining oldest events
count = (yield txn.removeOldEvents(cutoff))
- self.assertEquals(count, 3)
+ self.assertEquals(count, 7)
results = (yield txn.eventsOlderThan(cutoff))
self.assertEquals(results, [])
@@ -534,67 +450,58 @@
@inlineCallbacks
- def _addAttachment(self, orphan=False, old2=False, current3=False):
+ def _addAttachment(self, home, calendar, event, name):
txn = self._sqlCalendarStore.newTransaction()
# Create an event with an attachment
- home = (yield txn.calendarHomeWithUID("home1"))
- calendar = (yield home.calendarWithName("calendar1"))
- event = (yield calendar.calendarObjectWithName("oldattachment.ics"))
- attachment = (yield event.createAttachmentWithName("oldattachment.txt"))
+ home = (yield txn.calendarHomeWithUID(home))
+ calendar = (yield home.calendarWithName(calendar))
+ event = (yield calendar.calendarObjectWithName(event))
+ attachment = (yield event.createAttachmentWithName(name))
t = attachment.store(MimeType("text", "x-fixture"))
- t.write("old attachment")
- t.write(" text")
+ t.write("%s/%s/%s/%s" % (home, calendar, event, name,))
+ t.write(" attachment")
(yield t.loseConnection())
- if orphan:
- # Reset dropbox id in calendar_object
- co = schema.CALENDAR_OBJECT
- Update(
- {co.DROPBOX_ID: None, },
- Where=co.RESOURCE_ID == event._resourceID,
- ).on(txn)
+ (yield txn.commit())
- if old2:
- event = (yield calendar.createCalendarObjectWithName(
- "oldattachment2.ics", Component.fromString(OLD_ATTACHMENT2_ICS)
- ))
- attachment = (yield event.createAttachmentWithName("oldattachment2.txt"))
- t = attachment.store(MimeType("text", "x-fixture"))
- t.write("old attachment #2")
- t.write(" text")
- (yield t.loseConnection())
+ returnValue(attachment)
- if current3:
- event = (yield calendar.createCalendarObjectWithName(
- "currentattachment3.ics", Component.fromString(CURRENT_ATTACHMENT3_ICS)
- ))
- attachment = (yield event.createAttachmentWithName("currentattachment3.txt"))
- t = attachment.store(MimeType("text", "x-fixture"))
- t.write("current attachment #3")
- t.write(" text")
- (yield t.loseConnection())
- (yield txn.commit())
+ @inlineCallbacks
+ def _orphanAttachment(self, home, calendar, event):
- returnValue(attachment)
+ txn = self._sqlCalendarStore.newTransaction()
+ # Reset dropbox id in calendar_object
+ home = (yield txn.calendarHomeWithUID(home))
+ calendar = (yield home.calendarWithName(calendar))
+ event = (yield calendar.calendarObjectWithName(event))
+ co = schema.CALENDAR_OBJECT
+ Update(
+ {co.DROPBOX_ID: None, },
+ Where=co.RESOURCE_ID == event._resourceID,
+ ).on(txn)
+ (yield txn.commit())
+
+
@inlineCallbacks
def test_removeOrphanedAttachments(self):
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota, 0)
- attachment = (yield self._addAttachment())
+ attachment = (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
attachmentPath = attachment._path.path
self.assertTrue(os.path.exists(attachmentPath))
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertNotEqual(quota, 0)
orphans = (yield self.transactionUnderTest().orphanedAttachments())
@@ -602,10 +509,10 @@
count = (yield self.transactionUnderTest().removeOrphanedAttachments(batchSize=100))
self.assertEquals(count, 0)
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertNotEqual(quota, 0)
# File still exists
@@ -614,10 +521,10 @@
# Delete all old events (including the event containing the attachment)
cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
count = (yield self.transactionUnderTest().removeOldEvents(cutoff))
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota, 0)
# Just look for orphaned attachments but don't delete
@@ -636,7 +543,7 @@
dryrun=True,
verbose=False
))
- self.assertEquals(total, 4)
+ self.assertEquals(total, 8)
# Actually remove
total = (yield PurgeOldEventsService.purgeOldEvents(
@@ -645,7 +552,7 @@
2,
verbose=False
))
- self.assertEquals(total, 4)
+ self.assertEquals(total, 8)
# There should be no more left
total = (yield PurgeOldEventsService.purgeOldEvents(
@@ -668,10 +575,10 @@
VCardComponent.fromString(VCARD_1)))
self.assertEquals(len((yield abColl.addressbookObjects())), 1)
- # Verify there are 3 events in calendar1
+ # Verify there are 5 events in calendar1
calHome = (yield txn.calendarHomeWithUID("home1"))
calColl = (yield calHome.calendarWithName("calendar1"))
- self.assertEquals(len((yield calColl.calendarObjects())), 3)
+ self.assertEquals(len((yield calColl.calendarObjects())), 5)
# Make the newly created objects available to the purgeUID transaction
(yield txn.commit())
@@ -681,8 +588,8 @@
self.rootResource, ("home1",), verbose=False, proxies=False,
when=PyCalendarDateTime(now, 4, 1, 12, 0, 0, 0, PyCalendarTimezone(utc=True))))
- # 2 items deleted: 1 event and 1 vcard
- self.assertEquals(total, 2)
+ # 4 items deleted: 2 events and 1 vcard
+ self.assertEquals(total, 3)
txn = self._sqlCalendarStore.newTransaction()
# adressbook home is deleted since it's now empty
@@ -691,7 +598,7 @@
calHome = (yield txn.calendarHomeWithUID("home1"))
calColl = (yield calHome.calendarWithName("calendar1"))
- self.assertEquals(len((yield calColl.calendarObjects())), 2)
+ self.assertEquals(len((yield calColl.calendarObjects())), 3)
@inlineCallbacks
@@ -705,10 +612,10 @@
VCardComponent.fromString(VCARD_1)))
self.assertEquals(len((yield abColl.addressbookObjects())), 1)
- # Verify there are 3 events in calendar1
+ # Verify there are 5 events in calendar1
calHome = (yield txn.calendarHomeWithUID("home1"))
calColl = (yield calHome.calendarWithName("calendar1"))
- self.assertEquals(len((yield calColl.calendarObjects())), 3)
+ self.assertEquals(len((yield calColl.calendarObjects())), 5)
# Make the newly created objects available to the purgeUID transaction
(yield txn.commit())
@@ -717,8 +624,8 @@
total, ignored = (yield PurgePrincipalService.purgeUIDs(self._sqlCalendarStore, self.directory,
self.rootResource, ("home1",), verbose=False, proxies=False, completely=True))
- # 4 items deleted: 3 events and 1 vcard
- self.assertEquals(total, 4)
+ # 4 items deleted: 5 events and 1 vcard
+ self.assertEquals(total, 6)
# Homes have been deleted as well
txn = self._sqlCalendarStore.newTransaction()
@@ -736,13 +643,22 @@
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota1 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota1, 0)
- (yield self._addAttachment(orphan=True, current3=True))
- (yield self.commit())
+ (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.2"))
+ (yield self._addAttachment("home1", "calendar1", "currentattachment3.ics", "att3"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment1.ics", "att4"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment2.ics", "att5"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment3.ics", "att6"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment4.ics", "att7"))
+ (yield self._orphanAttachment("home1", "calendar1", "oldattachment1.ics"))
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota2 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota2 > quota1)
# Remove old events first
@@ -752,28 +668,29 @@
2,
verbose=False
))
- self.assertEquals(total, 4)
+ self.assertEquals(total, 8)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota3 = (yield home.quotaUsedBytes())
- self.assertTrue(quota3 == quota2)
+ (yield self.commit())
+ self.assertTrue(quota3 < quota2)
# Dry run
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 0, 2, dryrun=True, verbose=False))
self.assertEquals(total, 1)
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota4 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota4 == quota3)
# Actually remove
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 0, 2, dryrun=False, verbose=False))
self.assertEquals(total, 1)
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota5 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota5 < quota4)
# There should be no more left
@@ -789,31 +706,42 @@
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota1 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota1, 0)
- (yield self._addAttachment(orphan=True, old2=True))
- (yield self.commit())
+ (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.2"))
+ (yield self._addAttachment("home1", "calendar1", "currentattachment3.ics", "att3"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment1.ics", "att4"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment2.ics", "att5"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment3.ics", "att6"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment4.ics", "att7"))
+ (yield self._orphanAttachment("home1", "calendar1", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment2.ics"))
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota2 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota2 > quota1)
# Dry run
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 0, 2, dryrun=True, verbose=False))
- self.assertEquals(total, 1)
- (yield self.commit())
+ self.assertEquals(total, 3)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota3 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota3 == quota2)
# Actually remove
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 0, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 1)
- (yield self.commit())
+ self.assertEquals(total, 3)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota4 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota4 < quota3)
# There should be no more left
@@ -829,31 +757,42 @@
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota1 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota1, 0)
- (yield self._addAttachment(orphan=True, old2=True))
- (yield self.commit())
+ (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.2"))
+ (yield self._addAttachment("home1", "calendar1", "currentattachment3.ics", "att3"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment1.ics", "att4"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment2.ics", "att5"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment3.ics", "att6"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment4.ics", "att7"))
+ (yield self._orphanAttachment("home1", "calendar1", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment2.ics"))
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota2 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota2 > quota1)
# Dry run
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home1", 0, 2, dryrun=True, verbose=False))
self.assertEquals(total, 1)
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota3 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota3 == quota2)
# Actually remove
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home1", 0, 2, dryrun=False, verbose=False))
self.assertEquals(total, 1)
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota4 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota4 < quota3)
# There should be no more left
@@ -869,31 +808,40 @@
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota1 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota1, 0)
- (yield self._addAttachment(orphan=True, old2=True))
- (yield self.commit())
+ (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.2"))
+ (yield self._addAttachment("home1", "calendar1", "currentattachment3.ics", "att3"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment1.ics", "att4"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment2.ics", "att5"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment3.ics", "att6"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment4.ics", "att7"))
+ (yield self._orphanAttachment("home1", "calendar1", "oldattachment1.ics"))
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota2 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota2 > quota1)
# Dry run
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home2", 0, 2, dryrun=True, verbose=False))
self.assertEquals(total, 0)
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota3 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota3 == quota2)
# Actually remove
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home2", 0, 2, dryrun=False, verbose=False))
self.assertEquals(total, 0)
- (yield self.commit())
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota4 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota4 == quota3)
# There should be no more left
@@ -902,38 +850,49 @@
@inlineCallbacks
- def test_purgeOrphanedAttachmentsWithCutoffOld(self):
+ def test_purgeOrphanedAttachmentsWithCutoff(self):
"""
L{PurgeAttachmentsService.purgeOrphanedAttachments} purges only orphaned attachments, not current ones.
"""
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota1 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota1, 0)
- (yield self._addAttachment(orphan=True, old2=True))
- (yield self.commit())
+ (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.2"))
+ (yield self._addAttachment("home1", "calendar1", "currentattachment3.ics", "att3"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment1.ics", "att4"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment2.ics", "att5"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment3.ics", "att6"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment4.ics", "att7"))
+ (yield self._orphanAttachment("home1", "calendar1", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment2.ics"))
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota2 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota2 > quota1)
# Dry run
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=True, verbose=False))
- self.assertEquals(total, 2)
- (yield self.commit())
+ self.assertEquals(total, 7)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota3 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota3 == quota2)
# Actually remove
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 2)
- (yield self.commit())
+ self.assertEquals(total, 7)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota4 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota4 < quota3)
# There should be no more left
@@ -942,38 +901,49 @@
@inlineCallbacks
- def test_purgeOrphanedAttachmentsWithCutoffOldWithMatchingUUID(self):
+ def test_purgeOrphanedAttachmentsWithCutoffWithMatchingUUID(self):
"""
L{PurgeAttachmentsService.purgeOrphanedAttachments} purges only orphaned attachments, not current ones.
"""
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota1 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota1, 0)
- (yield self._addAttachment(orphan=True, old2=True))
- (yield self.commit())
+ (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.2"))
+ (yield self._addAttachment("home1", "calendar1", "currentattachment3.ics", "att3"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment1.ics", "att4"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment2.ics", "att5"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment3.ics", "att6"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment4.ics", "att7"))
+ (yield self._orphanAttachment("home1", "calendar1", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment2.ics"))
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota2 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota2 > quota1)
# Dry run
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home1", 14, 2, dryrun=True, verbose=False))
- self.assertEquals(total, 2)
- (yield self.commit())
+ self.assertEquals(total, 3)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota3 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota3 == quota2)
# Actually remove
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home1", 14, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 2)
- (yield self.commit())
+ self.assertEquals(total, 3)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota4 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota4 < quota3)
# There should be no more left
@@ -982,120 +952,51 @@
@inlineCallbacks
- def test_purgeOrphanedAttachmentsWithCutoffOldWithoutMatchingUUID(self):
+ def test_purgeOrphanedAttachmentsWithCutoffWithoutMatchingUUID(self):
"""
L{PurgeAttachmentsService.purgeOrphanedAttachments} purges only orphaned attachments, not current ones.
"""
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota1 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertEqual(quota1, 0)
- (yield self._addAttachment(orphan=True, old2=True))
- (yield self.commit())
+ (yield self._addAttachment("home1", "calendar1", "oldattachment1.ics", "att1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.1"))
+ (yield self._addAttachment("home1", "calendar1", "oldattachment2.ics", "att2.2"))
+ (yield self._addAttachment("home1", "calendar1", "currentattachment3.ics", "att3"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment1.ics", "att4"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment2.ics", "att5"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment3.ics", "att6"))
+ (yield self._addAttachment("home2", "calendar2", "oldattachment4.ics", "att7"))
+ (yield self._orphanAttachment("home1", "calendar1", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment1.ics"))
+ (yield self._orphanAttachment("home2", "calendar2", "oldattachment2.ics"))
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota2 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota2 > quota1)
# Dry run
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home2", 14, 2, dryrun=True, verbose=False))
- self.assertEquals(total, 0)
- (yield self.commit())
+ self.assertEquals(total, 4)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota3 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota3 == quota2)
# Actually remove
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home2", 14, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 0)
- (yield self.commit())
+ self.assertEquals(total, 4)
home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
quota4 = (yield home.quotaUsedBytes())
+ (yield self.commit())
self.assertTrue(quota4 == quota3)
# There should be no more left
total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, "home2", 14, 2, dryrun=False, verbose=False))
self.assertEquals(total, 0)
-
-
- @inlineCallbacks
- def test_purgeOrphanedAttachmentsWithCutoffCurrent(self):
- """
- L{PurgeAttachmentsService.purgeOrphanedAttachments} purges only orphaned attachments, not current ones.
- """
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota1 = (yield home.quotaUsedBytes())
- self.assertEqual(quota1, 0)
-
- (yield self._addAttachment(orphan=True, current3=True))
- (yield self.commit())
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota2 = (yield home.quotaUsedBytes())
- self.assertTrue(quota2 > quota1)
-
- # Dry run
- total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=True, verbose=False))
- self.assertEquals(total, 1)
- (yield self.commit())
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota3 = (yield home.quotaUsedBytes())
- self.assertTrue(quota3 == quota2)
-
- # Actually remove
- total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 1)
- (yield self.commit())
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota4 = (yield home.quotaUsedBytes())
- self.assertTrue(quota4 < quota3)
-
- # There should be no more left
- total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 0)
-
-
- @inlineCallbacks
- def test_purgeOrphanedAttachmentsWithCutoffCurrentOld(self):
- """
- L{PurgeAttachmentsService.purgeOrphanedAttachments} purges only orphaned attachments, not current ones.
- """
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota1 = (yield home.quotaUsedBytes())
- self.assertEqual(quota1, 0)
-
- (yield self._addAttachment(orphan=True, old2=True, current3=True))
- (yield self.commit())
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota2 = (yield home.quotaUsedBytes())
- self.assertTrue(quota2 > quota1)
-
- # Dry run
- total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=True, verbose=False))
- self.assertEquals(total, 1)
- (yield self.commit())
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota3 = (yield home.quotaUsedBytes())
- self.assertTrue(quota3 == quota2)
-
- # Actually remove
- total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 1)
- (yield self.commit())
-
- home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
- quota4 = (yield home.quotaUsedBytes())
- self.assertTrue(quota4 < quota3)
-
- # There should be no more left
- total = (yield PurgeAttachmentsService.purgeOrphanedAttachments(self._sqlCalendarStore, None, 14, 2, dryrun=False, verbose=False))
- self.assertEquals(total, 0)
Modified: CalendarServer/branches/release/CalendarServer-4.3-dev/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-4.3-dev/txdav/common/datastore/sql.py 2013-01-03 18:20:43 UTC (rev 10193)
+++ CalendarServer/branches/release/CalendarServer-4.3-dev/txdav/common/datastore/sql.py 2013-01-03 18:29:57 UTC (rev 10194)
@@ -835,14 +835,14 @@
return self._sqlTxn.abort()
- def _oldEventsBase(limited): #@NoSelf
+ def _oldEventsBase(self, limit):
ch = schema.CALENDAR_HOME
co = schema.CALENDAR_OBJECT
cb = schema.CALENDAR_BIND
tr = schema.TIME_RANGE
kwds = {}
- if limited:
- kwds["Limit"] = Parameter("batchSize")
+ if limit:
+ kwds["Limit"] = limit
return Select(
[
ch.OWNER_UID,
@@ -867,11 +867,7 @@
**kwds
)
- _oldEventsLimited = _oldEventsBase(True)
- _oldEventsUnlimited = _oldEventsBase(False)
- del _oldEventsBase
-
def eventsOlderThan(self, cutoff, batchSize=None):
"""
Return up to the oldest batchSize events which exist completely earlier
@@ -889,12 +885,7 @@
raise ValueError("Cannot query events older than %s" % (truncateLowerLimit.getText(),))
kwds = {"CutOff": pyCalendarTodatetime(cutoff)}
- if batchSize is not None:
- kwds["batchSize"] = batchSize
- query = self._oldEventsLimited
- else:
- query = self._oldEventsUnlimited
- return query.on(self, **kwds)
+ return self._oldEventsBase(batchSize).on(self, **kwds)
@inlineCallbacks
@@ -921,16 +912,12 @@
returnValue(count)
- def _orphanedSummary(self, uuid, limited):
+ def _orphanedSummary(self, uuid):
at = schema.ATTACHMENT
co = schema.CALENDAR_OBJECT
ch = schema.CALENDAR_HOME
chm = schema.CALENDAR_HOME_METADATA
- kwds = {}
- if limited:
- kwds["Limit"] = Parameter('batchSize')
-
where = co.DROPBOX_ID == None
if uuid:
where = where.And(ch.OWNER_UID == Parameter('uuid'))
@@ -944,11 +931,10 @@
),
Where=where,
GroupBy=(ch.OWNER_UID, chm.QUOTA_USED_BYTES),
- **kwds
)
- def orphanedAttachments(self, uuid=None, batchSize=None):
+ def orphanedAttachments(self, uuid=None):
"""
Find attachments no longer referenced by any events.
@@ -957,19 +943,17 @@
kwds = {}
if uuid:
kwds["uuid"] = uuid
- if batchSize is not None:
- kwds["batchSize"] = batchSize
- return self._orphanedSummary(uuid, batchSize is not None).on(self, **kwds)
+ return self._orphanedSummary(uuid).on(self, **kwds)
- def _orphanedBase(self, uuid, limited):
+ def _orphanedBase(self, uuid, limit):
ch = schema.CALENDAR_HOME
at = schema.ATTACHMENT
co = schema.CALENDAR_OBJECT
kwds = {}
- if limited:
- kwds["Limit"] = Parameter('batchSize')
+ if limit:
+ kwds["Limit"] = limit
sfrom = at.join(co, at.DROPBOX_ID == co.DROPBOX_ID, "left outer")
where = co.DROPBOX_ID == None
@@ -997,9 +981,7 @@
kwds = {}
if uuid:
kwds["uuid"] = uuid
- if batchSize is not None:
- kwds["batchSize"] = batchSize
- results = (yield self._orphanedBase(uuid, batchSize is not None).on(self, **kwds))
+ results = (yield self._orphanedBase(uuid, batchSize).on(self, **kwds))
count = 0
for dropboxID, path in results:
@@ -1009,25 +991,21 @@
returnValue(count)
- def _oldAttachmentsSummaryBase(self, uuid, limited):
+ def _oldAttachmentsSummaryBase(self, uuid):
ch = schema.CALENDAR_HOME
chm = schema.CALENDAR_HOME_METADATA
co = schema.CALENDAR_OBJECT
tr = schema.TIME_RANGE
at = schema.ATTACHMENT
- kwds = {}
- if limited:
- kwds["Limit"] = Parameter('batchSize')
-
- where = co.DROPBOX_ID == Select(
+ where = at.DROPBOX_ID.In(Select(
[at.DROPBOX_ID],
From=at.join(co, at.DROPBOX_ID == co.DROPBOX_ID, "inner").join(
tr, co.RESOURCE_ID == tr.CALENDAR_OBJECT_RESOURCE_ID
),
GroupBy=(at.DROPBOX_ID,),
Having=Max(tr.END_DATE) < Parameter("CutOff"),
- )
+ ))
if uuid:
where = where.And(ch.OWNER_UID == Parameter('uuid'))
@@ -1035,17 +1013,15 @@
return Select(
[ch.OWNER_UID, chm.QUOTA_USED_BYTES, Sum(at.SIZE), Count(at.DROPBOX_ID)],
From=at.join(
- co, at.DROPBOX_ID == co.DROPBOX_ID, "left outer").join(
ch, at.CALENDAR_HOME_RESOURCE_ID == ch.RESOURCE_ID).join(
chm, ch.RESOURCE_ID == chm.RESOURCE_ID
),
Where=where,
GroupBy=(ch.OWNER_UID, chm.QUOTA_USED_BYTES),
- **kwds
)
- def oldAttachments(self, cutoff, uuid, batchSize=None):
+ def oldAttachments(self, cutoff, uuid):
"""
Find attachments attached to only events whose last instance is older than the specified cut-off.
@@ -1054,20 +1030,18 @@
kwds = {"CutOff": pyCalendarTodatetime(cutoff)}
if uuid:
kwds["uuid"] = uuid
- if batchSize is not None:
- kwds["batchSize"] = batchSize
- return self._oldAttachmentsSummaryBase(uuid, batchSize is not None).on(self, **kwds)
+ return self._oldAttachmentsSummaryBase(uuid).on(self, **kwds)
- def _oldAttachmentsBase(self, uuid, limited):
+ def _oldAttachmentsBase(self, uuid, limit):
ch = schema.CALENDAR_HOME
co = schema.CALENDAR_OBJECT
tr = schema.TIME_RANGE
at = schema.ATTACHMENT
kwds = {}
- if limited:
- kwds["Limit"] = Parameter('batchSize')
+ if limit:
+ kwds["Limit"] = limit
sfrom = at.join(
co, at.DROPBOX_ID == co.DROPBOX_ID, "inner").join(
@@ -1100,9 +1074,7 @@
kwds = {"CutOff": pyCalendarTodatetime(cutoff)}
if uuid:
kwds["uuid"] = uuid
- if batchSize is not None:
- kwds["batchSize"] = batchSize
- results = (yield self._oldAttachmentsBase(uuid, batchSize is not None).on(self, **kwds))
+ results = (yield self._oldAttachmentsBase(uuid, batchSize).on(self, **kwds))
count = 0
for dropboxID, path in results:
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130103/ff14c283/attachment-0001.html>
More information about the calendarserver-changes
mailing list