[CalendarServer-changes] [3579] CalendarServer/branches/users/cdaboo/location-partial-accept-3573/ twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Fri Jan 9 09:07:58 PST 2009


Revision: 3579
          http://trac.macosforge.org/projects/calendarserver/changeset/3579
Author:   cdaboo at apple.com
Date:     2009-01-09 09:07:58 -0800 (Fri, 09 Jan 2009)
Log Message:
-----------
Allow auto-accept to do mixed accept/decline for recurring instances.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/instance.py
    CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/scheduling/processing.py

Modified: CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/instance.py
===================================================================
--- CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/instance.py	2009-01-08 22:23:26 UTC (rev 3578)
+++ CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/instance.py	2009-01-09 17:07:58 UTC (rev 3579)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 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.
@@ -24,7 +24,7 @@
 
 from vobject.icalendar import utc
 
-# The maximum number of instances we will ezpand out to.
+# The maximum number of instances we will expand out to.
 # Raise a TooManyInstancesError exception if we exceed this.
 max_allowed_instances = 1000
 
@@ -300,7 +300,7 @@
         rid = component.getRecurrenceIDUTC()
         range = component.getRange()
         
-        # Now add this instance, effectively overridding the one with the matching R-ID
+        # Now add this instance, effectively overriding the one with the matching R-ID
         start = normalizeForIndex(start)
         end = normalizeForIndex(end)
         rid = normalizeForIndex(rid)
@@ -322,7 +322,7 @@
             for key in sorted(x for x in self.instances.keys() if x > str(rid)):
                 oldinstance = self.instances[key]
                 
-                # Do not override instance that is alreday overridden
+                # Do not override instance that is already overridden
                 if oldinstance.overridden:
                     continue
                 
@@ -372,7 +372,7 @@
         Add the specified master VAVAILABILITY Component to the instance list, expanding it
         within the supplied time range. VAVAILABILITY components are not recurring, they have an
         optional DTSTART and DTEND/DURATION defining a single time-range which may be bounded
-        depedning on the presence of the properties. If unbounded at one or both ends, we will
+        depending on the presence of the properties. If unbounded at one or both ends, we will
         set the time to 1/1/1900 in the past and 1/1/3000 in the future.
         @param component: the Component to expand
         @param limit: the end datetime.datetime for expansion

Modified: CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/scheduling/processing.py	2009-01-08 22:23:26 UTC (rev 3578)
+++ CalendarServer/branches/users/cdaboo/location-partial-accept-3573/twistedcaldav/scheduling/processing.py	2009-01-09 17:07:58 UTC (rev 3579)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2005-2008 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -449,54 +449,94 @@
         for calURL in calendars:
             testcal = (yield self.request.locateResource(calURL))
             
-            # First list is BUSY, second BUSY-TENTATIVE, third BUSY-UNAVAILABLE
-            fbinfo = ([], [], [])
-            
             # Now do search for overlapping time-range
             for instance in instances.instances.itervalues():
-                try:
-                    tr = caldavxml.TimeRange(start="20000101", end="20000101")
-                    tr.start = instance.start
-                    tr.end = instance.end
-                    yield report_common.generateFreeBusyInfo(self.request, testcal, fbinfo, tr, 0, uid)
-                    
-                    # If any fbinfo entries exist we have an overlap
-                    if len(fbinfo[0]) or len(fbinfo[1]) or len(fbinfo[2]):
+                if instance_states[instance]:
+                    try:
+                        # First list is BUSY, second BUSY-TENTATIVE, third BUSY-UNAVAILABLE
+                        fbinfo = ([], [], [])
+                        
+                        tr = caldavxml.TimeRange(start="20000101", end="20000101")
+                        tr.start = instance.start
+                        tr.end = instance.end
+                        yield report_common.generateFreeBusyInfo(self.request, testcal, fbinfo, tr, 0, uid)
+                        
+                        # If any fbinfo entries exist we have an overlap
+                        if len(fbinfo[0]) or len(fbinfo[1]) or len(fbinfo[2]):
+                            instance_states[instance] = False
+                    except NumberOfMatchesWithinLimits:
                         instance_states[instance] = False
-                except NumberOfMatchesWithinLimits:
-                    instance_states[instance] = False
-                    log.info("Exceeded number of matches whilst trying to find free-time.")
+                        log.info("Exceeded number of matches whilst trying to find free-time.")
             
             # If everything is declined we can exit now
-            if all([not state for state in instance_states.itervalues()]):
+            if not any(instance_states.itervalues()):
                 break
         
         # TODO: here we should do per-instance ACCEPT/DECLINE behavior
         # For now we will assume overall ACCEPT/DECLINE
 
         # Collect all the accepted and declined states
-        accepted = all(instance_states.itervalues())
+        all_accepted = all(instance_states.itervalues())
+        all_declined = not any(instance_states.itervalues())
 
-        # Extract the ATTENDEE property matching current recipient from the calendar data
+        # Do the simple case of all accepted or decline separately
         cuas = self.recipient.principal.calendarUserAddresses()
-        attendeeProps = calendar.getAttendeeProperties(cuas)
-        if not attendeeProps:
-            returnValue((False, "",))
+        if all_accepted or all_declined:
+            # Extract the ATTENDEE property matching current recipient from the calendar data
+            attendeeProps = calendar.getAttendeeProperties(cuas)
+            if not attendeeProps:
+                returnValue((False, "",))
+        
+            if all_accepted:
+                partstat = "ACCEPTED"
+            else:
+                partstat = "DECLINED"
+            calendar.replacePropertyInAllComponents(Property("TRANSP", "OPAQUE" if all_accepted else "TRANSPARENT"))
     
-        if accepted:
-            partstat = "ACCEPTED"
+            made_changes = self.changeAttendeePartstat(attendeeProps, partstat)
+        
         else:
-            partstat = "DECLINED"
+            # Hard case: some accepted some declined
+            # What we will do is mark any master instance as accepted, then mark each existing
+            # overridden instance as accepted or declined, and generate new overridden instances for
+            # any other declines.
             
-            # Make sure declined events are TRANSPARENT on the calendar
-            calendar.replacePropertyInAllComponents(Property("TRANSP", "TRANSPARENT"))
+            made_changes = False
+            partstat = "MIXED RESPONSE"
 
-        made_changes = False
-        for attendeeProp in attendeeProps:
-            if attendeeProp.params().get("PARTSTAT", ("NEEDS-ACTION",))[0] != partstat:
-                attendeeProp.params()["PARTSTAT"] = [partstat]
-                made_changes = True
-        
+            # See if there is a master component first
+            master = calendar.masterComponent()
+            if master:
+                attendee = master.getAttendeeProperty(cuas)
+                if attendee:
+                    made_changes |= self.changeAttendeePartstat(attendee, "ACCEPTED")
+                    master.replaceProperty(Property("TRANSP", "OPAQUE"))
+
+            # Look at expanded instances and change partstat accordingly
+            for instance, accepted in instance_states.iteritems():
+                
+                overridden = calendar.overriddenComponent(instance.rid)
+                if not overridden and accepted:
+                    # Nothing to do as master is always ACCEPTED
+                    continue 
+                
+                if overridden:
+                    # Change ATTENDEE property to match new state
+                    attendee = overridden.getAttendeeProperty(cuas)
+                    if attendee:
+                        made_changes |= self.changeAttendeePartstat(attendee, "ACCEPTED" if accepted else "DECLINED")
+                        overridden.replaceProperty(Property("TRANSP", "OPAQUE" if accepted else "TRANSPARENT"))
+                else:
+                    # Derive a new overridden component and change partstat
+                    derived = calendar.deriveInstance(instance.rid)
+                    if derived:
+                        attendee = derived.getAttendeeProperty(cuas)
+                        if attendee:
+                            self.changeAttendeePartstat(attendee, "ACCEPTED" if accepted else "DECLINED")
+                            derived.replaceProperty(Property("TRANSP", "OPAQUE" if accepted else "TRANSPARENT"))
+                            calendar.addComponent(derived)
+                            made_changes = True
+            
         # Fake a SCHEDULE-STATUS on the ORGANIZER property
         if made_changes:
             calendar.setParameterToValueForPropertyWithValue("SCHEDULE-STATUS", iTIPRequestStatus.MESSAGE_DELIVERED, "ORGANIZER", None)
@@ -565,3 +605,26 @@
         
         # Change CTag on the parent calendar collection
         yield collection.updateCTag()
+
+    def changeAttendeePartstat(self, attendees, partstat):
+        """
+        Change the PARTSTAT on any ATTENDEE properties passed in.
+
+        @param attendees: a single ATTENDEE property or a list of them
+        @type attendees: L{Property}, C{list} or C{tuple}
+        @param partstat: new PARTSTAT to set
+        @type partstat: C{str}
+        
+        @return: C{True} if any change was made, C{False} otherwise
+        """
+
+        if isinstance(attendees, Property):
+            attendees = (attendees,)
+
+        madeChanges = False
+        for attendee in attendees:
+            if attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0] != partstat:
+                attendee.params()["PARTSTAT"] = [partstat]
+                madeChanges = True
+        
+        return madeChanges
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090109/9c163b7c/attachment-0001.html>


More information about the calendarserver-changes mailing list