1.1 --- a/imiptools/handlers/scheduling/__init__.py Fri Jan 29 22:17:47 2016 +0100
1.2 +++ b/imiptools/handlers/scheduling/__init__.py Fri Jan 29 22:25:02 2016 +0100
1.3 @@ -19,11 +19,7 @@
1.4 this program. If not, see <http://www.gnu.org/licenses/>.
1.5 """
1.6
1.7 -from imiptools.data import uri_values
1.8 -from imiptools.dates import ValidityError, to_timezone
1.9 -from imiptools.period import coalesce_freebusy, invert_freebusy, \
1.10 - periods_from, remove_event_periods, \
1.11 - remove_periods
1.12 +from imiptools.handlers.scheduling.manifest import scheduling_functions
1.13
1.14 def apply_scheduling_functions(functions, handler):
1.15
1.16 @@ -60,196 +56,4 @@
1.17
1.18 return response
1.19
1.20 -def schedule_in_freebusy(handler, freebusy=None):
1.21 -
1.22 - """
1.23 - Attempt to schedule the current object of the given 'handler' in the
1.24 - free/busy schedule of a resource, returning an indication of the kind of
1.25 - response to be returned.
1.26 -
1.27 - If 'freebusy' is specified, the given collection of busy periods will be
1.28 - used to determine whether any conflicts occur. Otherwise, the current user's
1.29 - free/busy records will be used.
1.30 - """
1.31 -
1.32 - # If newer than any old version, discard old details from the
1.33 - # free/busy record and check for suitability.
1.34 -
1.35 - periods = handler.get_periods(handler.obj)
1.36 -
1.37 - freebusy = freebusy or handler.store.get_freebusy(handler.user)
1.38 - offers = handler.store.get_freebusy_offers(handler.user)
1.39 -
1.40 - # Check the periods against any scheduled events and against
1.41 - # any outstanding offers.
1.42 -
1.43 - scheduled = handler.can_schedule(freebusy, periods)
1.44 - scheduled = scheduled and handler.can_schedule(offers, periods)
1.45 -
1.46 - return scheduled and "ACCEPTED" or "DECLINED"
1.47 -
1.48 -def schedule_corrected_in_freebusy(handler):
1.49 -
1.50 - """
1.51 - Attempt to schedule the current object of the given 'handler', correcting
1.52 - specified datetimes according to the configuration of a resource,
1.53 - returning an indication of the kind of response to be returned.
1.54 - """
1.55 -
1.56 - obj = handler.obj.copy()
1.57 -
1.58 - # Check any constraints on the request.
1.59 -
1.60 - try:
1.61 - corrected = handler.correct_object()
1.62 -
1.63 - # Refuse to schedule obviously invalid requests.
1.64 -
1.65 - except ValidityError:
1.66 - return None
1.67 -
1.68 - # With a valid request, determine whether the event can be scheduled.
1.69 -
1.70 - scheduled = schedule_in_freebusy(handler)
1.71 -
1.72 - # Restore the original object if it was corrected but could not be
1.73 - # scheduled.
1.74 -
1.75 - if scheduled == "DECLINED" and corrected:
1.76 - handler.set_object(obj)
1.77 -
1.78 - # Where the corrected object can be scheduled, issue a counter
1.79 - # request.
1.80 -
1.81 - return scheduled == "ACCEPTED" and (corrected and "COUNTER" or "ACCEPTED") or "DECLINED"
1.82 -
1.83 -def schedule_next_available_in_freebusy(handler):
1.84 -
1.85 - """
1.86 - Attempt to schedule the current object of the given 'handler', correcting
1.87 - specified datetimes according to the configuration of a resource, then
1.88 - suggesting the next available period in the free/busy records if scheduling
1.89 - cannot occur for the requested period, returning an indication of the kind
1.90 - of response to be returned.
1.91 - """
1.92 -
1.93 - scheduled = schedule_corrected_in_freebusy(handler)
1.94 -
1.95 - if scheduled in ("ACCEPTED", "COUNTER"):
1.96 - return scheduled
1.97 -
1.98 - # There should already be free/busy information for the user.
1.99 -
1.100 - user_freebusy = handler.store.get_freebusy(handler.user)
1.101 - busy = user_freebusy
1.102 -
1.103 - # Subtract any periods from this event from the free/busy collections.
1.104 -
1.105 - event_periods = remove_event_periods(user_freebusy, handler.uid, handler.recurrenceid)
1.106 -
1.107 - # Find busy periods for the other attendees.
1.108 -
1.109 - for attendee in uri_values(handler.obj.get_values("ATTENDEE")):
1.110 - if attendee != handler.user:
1.111 - freebusy = handler.store.get_freebusy_for_other(handler.user, attendee)
1.112 - if freebusy:
1.113 - remove_periods(freebusy, event_periods)
1.114 - busy += freebusy
1.115 -
1.116 - # Obtain the combined busy periods.
1.117 -
1.118 - busy.sort()
1.119 - busy = coalesce_freebusy(busy)
1.120 -
1.121 - # Obtain free periods.
1.122 -
1.123 - free = invert_freebusy(busy)
1.124 - permitted_values = handler.get_permitted_values()
1.125 - periods = []
1.126 -
1.127 - # Do not attempt to redefine rule-based periods.
1.128 -
1.129 - last = None
1.130 -
1.131 - for period in handler.get_periods(handler.obj, explicit_only=True):
1.132 - duration = period.get_duration()
1.133 -
1.134 - # Try and schedule periods normally since some of them may be
1.135 - # compatible with the schedule.
1.136 -
1.137 - if permitted_values:
1.138 - period = period.get_corrected(permitted_values)
1.139 -
1.140 - scheduled = handler.can_schedule(freebusy, [period])
1.141 -
1.142 - if scheduled == "ACCEPTED":
1.143 - periods.append(period)
1.144 - last = period.get_end()
1.145 - continue
1.146 -
1.147 - # Get free periods from the time of each period.
1.148 -
1.149 - for found in periods_from(free, period):
1.150 -
1.151 - # Skip any periods before the last period.
1.152 -
1.153 - if last:
1.154 - if last > found.get_end():
1.155 - continue
1.156 -
1.157 - # Adjust the start of the free period to exclude the last period.
1.158 -
1.159 - found = found.make_corrected(max(found.get_start(), last), found.get_end())
1.160 -
1.161 - # Only test free periods long enough to hold the requested period.
1.162 -
1.163 - if found.get_duration() >= duration:
1.164 -
1.165 - # Obtain a possible period, starting at the found point and
1.166 - # with the requested duration. Then, correct the period if
1.167 - # necessary.
1.168 -
1.169 - start = to_timezone(found.get_start(), period.get_tzid())
1.170 - possible = period.make_corrected(start, start + period.get_duration())
1.171 - if permitted_values:
1.172 - possible = possible.get_corrected(permitted_values)
1.173 -
1.174 - # Only if the possible period is still within the free period
1.175 - # can it be used.
1.176 -
1.177 - if possible.within(found):
1.178 - periods.append(possible)
1.179 - break
1.180 -
1.181 - # Where no period can be found, decline the invitation.
1.182 -
1.183 - else:
1.184 - return "DECLINED"
1.185 -
1.186 - # Use the found period to set the start of the next window to search.
1.187 -
1.188 - last = periods[-1].get_end()
1.189 -
1.190 - # Replace the periods in the object.
1.191 -
1.192 - obj = handler.obj.copy()
1.193 - changed = handler.obj.set_periods(periods)
1.194 -
1.195 - # Check one last time, reverting the change if not scheduled.
1.196 -
1.197 - scheduled = schedule_in_freebusy(handler, busy)
1.198 -
1.199 - if scheduled == "DECLINED":
1.200 - handler.set_object(obj)
1.201 -
1.202 - return scheduled == "ACCEPTED" and (changed and "COUNTER" or "ACCEPTED") or "DECLINED"
1.203 -
1.204 -# Registry of scheduling functions.
1.205 -
1.206 -scheduling_functions = {
1.207 - "schedule_in_freebusy" : schedule_in_freebusy,
1.208 - "schedule_corrected_in_freebusy" : schedule_corrected_in_freebusy,
1.209 - "schedule_next_available_in_freebusy" : schedule_next_available_in_freebusy,
1.210 - }
1.211 -
1.212 # vim: tabstop=4 expandtab shiftwidth=4