[CalendarServer-changes] [13512] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon May 19 20:39:30 PDT 2014


Revision: 13512
          http://trac.calendarserver.org//changeset/13512
Author:   gaya at apple.com
Date:     2014-05-19 20:39:30 -0700 (Mon, 19 May 2014)
Log Message:
-----------
Remove GROUP_ATTENDDEE rows for old events

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/who/test/test_group_attendees.py

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2014-05-20 01:09:59 UTC (rev 13511)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2014-05-20 03:39:30 UTC (rev 13512)
@@ -1041,6 +1041,7 @@
     "GroupAttendees" : {
         "Enabled": False,
         "ReconciliationDelaySeconds" : 5,
+        "UpdateOldEventLimitSeconds" : 1 * 24 * 60 * 60,   # 1 day
     },
 
     "AutomaticPurging": {

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2014-05-20 01:09:59 UTC (rev 13511)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2014-05-20 03:39:30 UTC (rev 13512)
@@ -1964,8 +1964,7 @@
             changed = component.reconcileGroupAttendees(groupCUAToAttendeeMemberPropMap)
 
         # save for post processing when self._resourceID is non-zero
-        if inserting and groupCUAToAttendeeMemberPropMap:
-            self._groupCUAToAttendeeMemberPropMap = groupCUAToAttendeeMemberPropMap
+        self._groupCUAToAttendeeMemberPropMap = groupCUAToAttendeeMemberPropMap
 
         returnValue(changed)
 
@@ -2037,17 +2036,50 @@
 
 
     @inlineCallbacks
-    def deleteGROUP_ATTENDEE(self, groupCUAToAttendeeMemberPropMap=None):
-        """
-        delete groupp attendee rows for this resource
-        """
-        ga = schema.GROUP_ATTENDEE
-        rows = yield Delete(
-            From=ga,
-            Where=ga.RESOURCE_ID == self._resourceID,
-            Return=[ga.GROUP_ID]
-        ).on(self._txn)
-        returnValue(bool(rows))
+    def deleteOldGROUP_ATTENDEE(self, component, instances, inserting, txn):
+            
+        # If this event is old, break possible tie to group update
+        if hasattr(self, "_groupCUAToAttendeeMemberPropMap"):
+            
+            if (component.masterComponent() is None or not component.isRecurring()):
+                cutoffDate_datatime  = (
+                    datetime.datetime.utcnow() + 
+                    datetime.timedelta(seconds=config.GroupAttendees.UpdateOldEventLimitSeconds)
+                )
+                tr = schema.TIME_RANGE
+                rows = yield Select(
+                    [Count(tr.CALENDAR_OBJECT_RESOURCE_ID)],
+                    From=tr,
+                    Where=(
+                        tr.CALENDAR_OBJECT_RESOURCE_ID == self._resourceID).And(
+                        tr.END_DATE > cutoffDate_datatime
+                    ),
+                ).on(txn)
+                isOld = rows[0][0] == 0
+               
+            else:
+                if instances:
+                    cutoffDate_DateTime = (
+                        DateTime.getNowUTC() + 
+                        Duration(seconds=config.GroupAttendees.UpdateOldEventLimitSeconds)
+                    )
+                    maxInstanceKey = sorted(instance for instance in instances)[-1]
+                    isOld = cutoffDate_DateTime > instances[maxInstanceKey].end
+                else:
+                    isOld = True
+                
+            if isOld:
+                if inserting:
+                    # don't create GROUP_ATTENDEE rows in updateGROUP_ATTENDEE()
+                    del self._groupCUAToAttendeeMemberPropMap
+                else:
+                    # delete existing group rows
+                    ga = schema.GROUP_ATTENDEE
+                    rows = yield Delete(
+                        From=ga,
+                        Where=ga.RESOURCE_ID == self._resourceID,
+                        #Return=[ga.GROUP_ID]
+                    ).on(txn)
 
 
     def validCalendarDataCheck(self, component, inserting):
@@ -2742,7 +2774,8 @@
         yield self.updateDatabase(component, inserting=inserting)
 
         # update GROUP_ATTENDEE table rows
-        yield self.updateGROUP_ATTENDEE()
+        if inserting:
+            yield self.updateGROUP_ATTENDEE()
 
         # Post process managed attachments
         if internal_state in (
@@ -2806,6 +2839,7 @@
         # freebusy related properties have changed (e.g. an attendee reply and refresh). In those cases
         # the component will have a special attribute present to let us know to suppress the instance indexing.
         instanceIndexingRequired = not getattr(component, "noInstanceIndexing", False) or inserting or reCreate
+        instances = None
 
         if instanceIndexingRequired:
 
@@ -2879,7 +2913,7 @@
 
             # Now coerce indexing to off if needed
             if not doInstanceIndexing:
-                instances = None
+                #instances = None # used by deleteOldGROUP_ATTENDEE() call at end
                 recurrenceLowerLimit = None
                 recurrenceLimit = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
 
@@ -2988,6 +3022,8 @@
 
         if instanceIndexingRequired and doInstanceIndexing:
             yield self._addInstances(component, instances, truncateLowerLimit, isInboxItem, txn)
+            
+        yield self.deleteOldGROUP_ATTENDEE(component, instances, inserting, txn)
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/txdav/who/test/test_group_attendees.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_group_attendees.py	2014-05-20 01:09:59 UTC (rev 13511)
+++ CalendarServer/trunk/txdav/who/test/test_group_attendees.py	2014-05-20 03:39:30 UTC (rev 13512)
@@ -58,6 +58,7 @@
         super(GroupAttendeeReconciliation, self).configure()
         config.GroupAttendees.Enabled = True
         config.GroupAttendees.ReconciliationDelaySeconds = 0
+        config.GroupAttendees.UpdateOldEventLimitSeconds = 0
 
 
     @inlineCallbacks
@@ -670,6 +671,64 @@
 
 
     @inlineCallbacks
+    def test_groupPutOldEvent(self):
+        """
+        Test that old event with group attendee is expaned but not linked to group update
+        """
+
+        data_put_1 = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:20140101T100000
+DURATION:PT1H
+SUMMARY:event 1
+UID:event1 at ninevah.local
+ORGANIZER:MAILTO:user02 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:MAILTO:group01 at example.com
+END:VEVENT
+END:VCALENDAR"""
+
+        data_get_1 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VEVENT
+UID:event1 at ninevah.local
+DTSTART;TZID=US/Eastern:20140101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:x-uid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:x-uid:20000000-0000-0000-0000-000000000001
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;MEMBER="urn:x-uid:20000000-0000-0000-0000-000000000001";PARTSTAT=NEEDS-ACTION;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:x-uid:10000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:x-uid:10000000-0000-0000-0000-000000000002
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+        groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+
+        calendar = yield self.calendarUnderTest(name="calendar", home="10000000-0000-0000-0000-000000000002")
+        vcalendar = Component.fromString(data_put_1)
+        yield calendar.createCalendarObjectWithName("data1.ics", vcalendar)
+        yield self.commit()
+
+        wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+        if len(wps): # This is needed because the test currently fails and does actually create job items we have to wait for
+            yield self.commit()
+            yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+        self.assertEqual(len(wps), 0)
+
+        cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+        vcalendar = yield cobj.component()
+        self.assertEqual(normalize_iCalStr(vcalendar), normalize_iCalStr(data_get_1))
+
+
+    @inlineCallbacks
     def test_groupChangeOldEvent(self):
         """
         Test that group attendee changes not applied to old events
@@ -784,10 +843,139 @@
             yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
         self.assertEqual(len(wps), 0)
 
-    test_groupChangeOldEvent.todo = "Doesn't work yet"
 
+    @inlineCallbacks
+    def test_groupChangeOldNoMasterEvent(self):
+        """
+        Test that group attendee changes not applied to old events with no master event
+        """
+        yield None
 
+    test_groupChangeOldNoMasterEvent.todo = "Create test data"
+
+
     @inlineCallbacks
+    def test_groupChangeOldRecurringEvent(self):
+        """
+        Test that group attendee changes not applied to old recurring events
+        """
+
+        data_put_1 = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:20120101T100000
+DURATION:PT1H
+RRULE:FREQ=DAILY;UNTIL=20240101T100000
+SUMMARY:event 1
+UID:event1 at ninevah.local
+ORGANIZER:MAILTO:user02 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:MAILTO:group01 at example.com
+END:VEVENT
+END:VCALENDAR"""
+
+        data_put_2 = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:20120101T100000
+DURATION:PT1H
+RRULE:FREQ=DAILY;UNTIL=20140101T100000
+SUMMARY:event 1
+UID:event1 at ninevah.local
+ORGANIZER:MAILTO:user02 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:MAILTO:group01 at example.com
+END:VEVENT
+END:VCALENDAR"""
+
+        data_get_1 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VEVENT
+UID:event1 at ninevah.local
+DTSTART;TZID=US/Eastern:20120101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:x-uid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:x-uid:20000000-0000-0000-0000-000000000001
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;MEMBER="urn:x-uid:20000000-0000-0000-0000-000000000001";PARTSTAT=NEEDS-ACTION;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:x-uid:10000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:x-uid:10000000-0000-0000-0000-000000000002
+RRULE:FREQ=DAILY;UNTIL=20240101T100000
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+
+        data_get_2 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VEVENT
+UID:event1 at ninevah.local
+DTSTART;TZID=US/Eastern:20120101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:x-uid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:x-uid:20000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:x-uid:10000000-0000-0000-0000-000000000002
+RRULE:FREQ=DAILY;UNTIL=20140101T100000
+SEQUENCE:1
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+
+        @inlineCallbacks
+        def expandedMembers(self, records=None):
+            yield None
+            returnValue(set())
+
+        groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+
+        calendar = yield self.calendarUnderTest(name="calendar", home="10000000-0000-0000-0000-000000000002")
+        vcalendar = Component.fromString(data_put_1)
+        yield calendar.createCalendarObjectWithName("data1.ics", vcalendar)
+        yield self.commit()
+
+        wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+        yield self.commit()
+        self.assertEqual(len(wps), 1)
+        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+
+        cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+        vcalendar = yield cobj.component()
+        self.assertEqual(normalize_iCalStr(vcalendar), normalize_iCalStr(data_get_1))
+
+        yield self._verifyObjectResourceCount("10000000-0000-0000-0000-000000000001", 1)
+
+        vcalendar = Component.fromString(data_put_2)
+        yield cobj.setComponent(vcalendar)
+        yield self.commit()
+
+        self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers)
+
+        cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+        vcalendar = yield cobj.component()
+        self.assertEqual(normalize_iCalStr(vcalendar), normalize_iCalStr(data_get_2))
+
+        wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+        if len(wps): # This is needed because the test currently fails and does actually create job items we have to wait for
+            yield self.commit()
+            yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+        self.assertEqual(len(wps), 0)
+
+
+
+    @inlineCallbacks
     def test_groupRemovalFromDirectory(self):
         """
         Test that removing a group from the directory also removes the expanded attendees.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140519/95492704/attachment-0001.html>


More information about the calendarserver-changes mailing list