1.1 --- a/imiptools/handlers/scheduling/quota.py Fri May 13 22:41:17 2016 +0200
1.2 +++ b/imiptools/handlers/scheduling/quota.py Fri May 13 22:48:45 2016 +0200
1.3 @@ -21,6 +21,8 @@
1.4
1.5 from imiptools.dates import get_duration, to_utc_datetime
1.6 from imiptools.data import get_uri, uri_dict
1.7 +from imiptools.handlers.scheduling.common import get_scheduling_conflicts, \
1.8 + standard_responses
1.9 from imiptools.period import Endless
1.10 from datetime import timedelta
1.11
1.12 @@ -50,6 +52,15 @@
1.13 if not limit:
1.14 return "DECLINED", _("You have no quota allocation for the recipient.")
1.15
1.16 + # Decline endless events even for unlimited quotas.
1.17 + # NOTE: Such events could be supported in a similar way to those supported
1.18 + # NOTE: for each user.
1.19 +
1.20 + total = _get_duration(handler)
1.21 +
1.22 + if total == Endless():
1.23 + return "DECLINED", _("The event period exceeds your quota allocation for the recipient.")
1.24 +
1.25 # Where the quota is unlimited, accept the invitation.
1.26
1.27 if limit == "*":
1.28 @@ -57,11 +68,6 @@
1.29
1.30 # Decline events whose durations exceed the balance.
1.31
1.32 - total = _get_duration(handler)
1.33 -
1.34 - if total == Endless():
1.35 - return "DECLINED", _("The event period exceeds your quota allocation for the recipient.")
1.36 -
1.37 balance = get_duration(limit) - _get_usage(entries)
1.38
1.39 if total > balance:
1.40 @@ -81,7 +87,7 @@
1.41 total = _get_duration(handler)
1.42 expiry = _get_expiry_time(handler)
1.43
1.44 - # Reject indefinitely recurring events.
1.45 + # Ignore indefinitely recurring events.
1.46
1.47 if total == Endless() or not expiry:
1.48 return
1.49 @@ -103,11 +109,12 @@
1.50 quota, group = _get_quota_and_group(handler, args)
1.51
1.52 total = _get_duration(handler)
1.53 -
1.54 - # Allow indefinitely recurring events.
1.55 + expiry = _get_expiry_time(handler)
1.56
1.57 - if total == Endless():
1.58 - total = None
1.59 + # Ignore indefinitely recurring events.
1.60 +
1.61 + if total == Endless() or not expiry:
1.62 + return
1.63
1.64 # Update the journal entries.
1.65
1.66 @@ -196,8 +203,6 @@
1.67 nor are the quotas themselves.
1.68 """
1.69
1.70 - _ = handler.get_translator()
1.71 -
1.72 quota, organiser = _get_quota_and_identity(handler, args)
1.73
1.74 # Check the event periods against the quota's consolidated record of the
1.75 @@ -207,10 +212,7 @@
1.76 freebusy = handler.get_journal().get_freebusy(quota, organiser)
1.77 scheduled = handler.can_schedule(freebusy, periods)
1.78
1.79 - if scheduled:
1.80 - return "ACCEPTED", _("The recipient has scheduled the requested period.")
1.81 - else:
1.82 - return "DECLINED", _("The requested period cannot be scheduled.")
1.83 + return standard_responses(handler, scheduled and "ACCEPTED" or "DECLINED")
1.84
1.85 def add_to_quota_freebusy(handler, args):
1.86
1.87 @@ -263,11 +265,9 @@
1.88 """
1.89 Check the current object of the given 'handler' against the schedules
1.90 managed by the quota, delegating to a specific recipient according to the
1.91 - given policy.
1.92 + given policies.
1.93 """
1.94
1.95 - _ = handler.get_translator()
1.96 -
1.97 # First check the quota and decline any request that would exceed the quota.
1.98
1.99 scheduled = check_quota(handler, args)
1.100 @@ -279,7 +279,7 @@
1.101 # Obtain the quota and organiser group details to evaluate delegation.
1.102
1.103 quota, group = _get_quota_and_group(handler, args)
1.104 - policy = args and (args[1:] or ["arbitrary"])[0]
1.105 + policies = args and args[1:] or ["available"]
1.106
1.107 # Determine the status of the recipient.
1.108
1.109 @@ -305,26 +305,29 @@
1.110 # unavailable delegates.
1.111
1.112 entries = handler.get_journal().get_entries(quota, group)
1.113 - unavailable = set()
1.114 + conflicts = get_scheduling_conflicts(handler, entries, delegates, attendee=True)
1.115
1.116 - for period in handler.get_periods(handler.obj):
1.117 - overlapping = entries.get_overlapping(period)
1.118 + # Get the delegates in order of increasing unavailability (or decreasing
1.119 + # availability).
1.120
1.121 - # Where scheduling cannot occur, find the busy potential delegates.
1.122 + unavailability = conflicts.items()
1.123 +
1.124 + # Apply the policies to choose a suitable delegate.
1.125
1.126 - if overlapping:
1.127 - for p in overlapping:
1.128 - unavailable.add(p.attendee)
1.129 + if "most-available" in policies:
1.130 + unavailability.sort(key=lambda t: t[1])
1.131 + available = [delegate for (delegate, commitments) in unavailability]
1.132 + delegate = available and available[0]
1.133
1.134 - # Get the remaining, available delegates.
1.135 + # The default is to select completely available delegates.
1.136
1.137 - available = delegates.difference(unavailable)
1.138 + else:
1.139 + available = [delegate for (delegate, commitments) in unavailability if not commitments]
1.140 + delegate = available and (handler.user in available and handler.user or available[0])
1.141
1.142 - # Apply the policy to choose an available delegate.
1.143 - # NOTE: Currently an arbitrary delegate is chosen if not the recipient.
1.144 + # Only accept or delegate if a suitably available delegate is found.
1.145
1.146 - if available:
1.147 - delegate = handler.user in available and handler.user or list(available)[0]
1.148 + if delegate:
1.149
1.150 # Add attendee for delegate, obtaining the original attendee dictionary.
1.151 # Modify this user's status to refer to the delegate.
1.152 @@ -335,11 +338,13 @@
1.153 attendee_attr["DELEGATED-TO"] = [delegate]
1.154 handler.obj["ATTENDEE"] = attendee_map.items()
1.155
1.156 - return "DELEGATED", _("The recipient has delegated the requested period.")
1.157 + response = "DELEGATED"
1.158 else:
1.159 - return "ACCEPTED", _("The recipient has scheduled the requested period.")
1.160 + response = "ACCEPTED"
1.161 else:
1.162 - return "DECLINED", _("The requested period cannot be scheduled.")
1.163 + response = "DECLINED"
1.164 +
1.165 + return standard_responses(handler, response)
1.166
1.167 # Locking and unlocking.
1.168
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/tests/test_resource_invitation_constraints_quota_delegation_policy.sh Fri May 13 22:48:45 2016 +0200
3.3 @@ -0,0 +1,332 @@
3.4 +#!/bin/sh
3.5 +
3.6 +. "`dirname \"$0\"`/common.sh"
3.7 +
3.8 +USER1="mailto:resource-car-porsche911@example.com"
3.9 +USER2="mailto:resource-car-fiat500@example.com"
3.10 +SENDER1="mailto:paul.boddie@example.com"
3.11 +SENDER2="mailto:vincent.vole@example.com"
3.12 +USER1ADDRESS="resource-car-porsche911@example.com"
3.13 +USER2ADDRESS="resource-car-fiat500@example.com"
3.14 +SENDER1ADDRESS="paul.boddie@example.com"
3.15 +SENDER2ADDRESS="vincent.vole@example.com"
3.16 +QUOTA=cars
3.17 +OTHER_QUOTA=rooms
3.18 +
3.19 +mkdir -p "$PREFS/$USER1"
3.20 +echo 'Europe/Oslo' > "$PREFS/$USER1/TZID"
3.21 +echo 'share' > "$PREFS/$USER1/freebusy_sharing"
3.22 +cat > "$PREFS/$USER1/scheduling_function" <<EOF
3.23 +schedule_for_delegate $QUOTA most-available
3.24 +EOF
3.25 +
3.26 +mkdir -p "$PREFS/$USER2"
3.27 +echo 'Europe/Oslo' > "$PREFS/$USER2/TZID"
3.28 +echo 'share' > "$PREFS/$USER2/freebusy_sharing"
3.29 +cat > "$PREFS/$USER2/scheduling_function" <<EOF
3.30 +schedule_for_delegate $QUOTA most-available
3.31 +EOF
3.32 +
3.33 +# Have a common group for all organisers when scheduling.
3.34 +
3.35 +cat <<EOF | "$SET_QUOTA_GROUPS" "$QUOTA" $SET_QUOTA_GROUPS_ARGS
3.36 +* all
3.37 +EOF
3.38 +
3.39 +# Set a common quota.
3.40 +
3.41 +cat <<EOF | "$SET_QUOTA_LIMITS" "$QUOTA" $SET_QUOTA_LIMITS_ARGS
3.42 +all *
3.43 +EOF
3.44 +
3.45 +# Allow cars to delegate to each other.
3.46 +
3.47 +cat <<EOF | "$SET_DELEGATES" "$QUOTA" $SET_DELEGATES_ARGS
3.48 +mailto:resource-car-porsche911@example.com
3.49 +mailto:resource-car-fiat500@example.com
3.50 +EOF
3.51 +
3.52 + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/fb-request-car.txt" 2>> $ERROR \
3.53 +| "$SHOWMAIL" \
3.54 +> out0.tmp
3.55 +
3.56 + grep -q 'METHOD:REPLY' out0.tmp \
3.57 +&& ! grep -q '^FREEBUSY' out0.tmp \
3.58 +&& echo "Success" \
3.59 +|| echo "Failed"
3.60 +
3.61 +# Attempt to schedule an event.
3.62 +
3.63 +"$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car.txt" 2>> $ERROR
3.64 +
3.65 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER1" "freebusy" \
3.66 +| tee out0s.tmp \
3.67 +| grep -q "^20141126T150000Z${TAB}20141126T160000Z" \
3.68 +&& echo "Success" \
3.69 +|| echo "Failed"
3.70 +
3.71 +# Present the request to the resource.
3.72 +
3.73 + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/event-request-car.txt" 2>> $ERROR \
3.74 +| tee out1r.tmp \
3.75 +| "$SHOWMAIL" \
3.76 +> out1.tmp
3.77 +
3.78 + grep -q 'METHOD:REPLY' out1.tmp \
3.79 +&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' out1.tmp \
3.80 +&& echo "Success" \
3.81 +|| echo "Failed"
3.82 +
3.83 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
3.84 +| tee out1f.tmp \
3.85 +| grep -q "^20141126T150000Z${TAB}20141126T160000Z" \
3.86 +&& echo "Success" \
3.87 +|| echo "Failed"
3.88 +
3.89 +# Check the quota (event is confirmed).
3.90 +
3.91 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "all" \
3.92 +| tee out1e.tmp \
3.93 +| grep -q "event21@example.com" \
3.94 +&& echo "Success" \
3.95 +|| echo "Failed"
3.96 +
3.97 +# Attempt to schedule another event.
3.98 +
3.99 + sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=5/;' "$TEMPLATES/event-request-cars-recurring.txt" \
3.100 +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
3.101 +
3.102 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER1" "freebusy" \
3.103 +> out2s.tmp
3.104 +
3.105 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2s.tmp" \
3.106 +&& grep -q "event21@example.com" "out2s.tmp" \
3.107 +&& grep -q "event25@example.com" "out2s.tmp" \
3.108 +&& echo "Success" \
3.109 +|| echo "Failed"
3.110 +
3.111 +# Present the request to the recipients.
3.112 +
3.113 + sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=5/;' "$TEMPLATES/event-request-cars-recurring.txt" \
3.114 +| "$RESOURCE_SCRIPT" $ARGS 2>> $ERROR \
3.115 +> out2r.tmp
3.116 +
3.117 + "$SHOWMAIL" < "out2r.tmp" \
3.118 +> out2p0.tmp
3.119 +
3.120 + "$SHOWMAIL" 1 < "out2r.tmp" \
3.121 +> out2p1.tmp
3.122 +
3.123 + grep -q 'METHOD:REPLY' out2p0.tmp \
3.124 +&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' out2p0.tmp \
3.125 +&& echo "Success" \
3.126 +|| echo "Failed"
3.127 +
3.128 + grep -q 'METHOD:REPLY' out2p1.tmp \
3.129 +&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' out2p1.tmp \
3.130 +&& echo "Success" \
3.131 +|| echo "Failed"
3.132 +
3.133 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
3.134 +> out2f0.tmp
3.135 +
3.136 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f0.tmp" \
3.137 +&& grep -q "event21@example.com" "out2f0.tmp" \
3.138 +&& grep -q "event25@example.com" "out2f0.tmp" \
3.139 +&& echo "Success" \
3.140 +|| echo "Failed"
3.141 +
3.142 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
3.143 +> out2f1.tmp
3.144 +
3.145 + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f1.tmp" \
3.146 +&& grep -q "event25@example.com" "out2f1.tmp" \
3.147 +&& echo "Success" \
3.148 +|| echo "Failed"
3.149 +
3.150 +# Check the quota (event is confirmed).
3.151 +
3.152 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "all" \
3.153 +| tee out2e.tmp \
3.154 +| grep -q "event25@example.com" \
3.155 +&& echo "Success" \
3.156 +|| echo "Failed"
3.157 +
3.158 +# Attempt to schedule another event.
3.159 +
3.160 +"$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-delegating.txt" 2>> $ERROR
3.161 +
3.162 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER2" "freebusy" \
3.163 +| tee out3s.tmp \
3.164 +| grep -q "^20141126T153000Z${TAB}20141126T163000Z" \
3.165 +&& echo "Success" \
3.166 +|| echo "Failed"
3.167 +
3.168 +# Present the request to the resource.
3.169 +
3.170 + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-delegating.txt" 2>> $ERROR \
3.171 +> out3r.tmp
3.172 +
3.173 + "$SHOWMAIL" < out3r.tmp \
3.174 +> out3p0.tmp
3.175 +
3.176 + "$SHOWMAIL" 1 < out3r.tmp \
3.177 +> out3p1.tmp
3.178 +
3.179 +if grep -q "To: $SENDER2ADDRESS" out3p0.tmp ; then
3.180 + ORGFN=out3p0.tmp ; DELFN=out3p1.tmp
3.181 +else
3.182 + ORGFN=out3p1.tmp ; DELFN=out3p0.tmp
3.183 +fi
3.184 +
3.185 +# One of the responses will be a request sent to the delegate.
3.186 +
3.187 + grep -q "To: $USER2ADDRESS" "$DELFN" \
3.188 +&& grep -q 'METHOD:REQUEST' "$DELFN" \
3.189 +&& grep -q 'ATTENDEE.*;PARTSTAT=DELEGATED.*:'"$USER1" "$DELFN" \
3.190 +&& grep -q 'ATTENDEE.*:'"$USER2" "$DELFN" \
3.191 +&& echo "Success" \
3.192 +|| echo "Failed"
3.193 +
3.194 +# The other will be a reply to the organiser.
3.195 +
3.196 + grep -q "To: $SENDER2ADDRESS" "$ORGFN" \
3.197 +&& grep -q 'METHOD:REPLY' "$ORGFN" \
3.198 +&& grep -q 'ATTENDEE.*;PARTSTAT=DELEGATED.*:'"$USER1" "$ORGFN" \
3.199 +&& grep -q 'ATTENDEE.*:'"$USER2" "$ORGFN" \
3.200 +&& echo "Success" \
3.201 +|| echo "Failed"
3.202 +
3.203 +# Neither the delegator or the delegate will have changed their schedules.
3.204 +
3.205 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
3.206 +> out4f1.tmp
3.207 +
3.208 + ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" "out4f1.tmp" \
3.209 +&& echo "Success" \
3.210 +|| echo "Failed"
3.211 +
3.212 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
3.213 +> out4f2.tmp
3.214 +
3.215 + ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" "out4f2.tmp" \
3.216 +&& echo "Success" \
3.217 +|| echo "Failed"
3.218 +
3.219 +# Check the quota (event is not confirmed).
3.220 +
3.221 + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "all" \
3.222 +> out4e.tmp
3.223 +
3.224 + grep -q "event21@example.com" "out4e.tmp" \
3.225 +&& grep -q "event25@example.com" "out4e.tmp" \
3.226 +&& ! grep -q "event27@example.com" "out4e.tmp" \
3.227 +&& echo "Success" \
3.228 +|| echo "Failed"
3.229 +
3.230 +# Present the reply to the organiser.
3.231 +
3.232 + "$PERSON_SCRIPT" $ARGS < "$ORGFN" 2>> "$ERROR" \
3.233 +| tee out5r.tmp \
3.234 +| "$SHOWMAIL" \
3.235 +> out5.tmp
3.236 +
3.237 +# Check the free/busy status of the attendees at the organiser.
3.238 +# Currently, neither are attending.
3.239 +
3.240 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER2" "freebusy_other" "$USER1" \
3.241 +> out5s0.tmp \
3.242 +
3.243 + ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" out5s0.tmp \
3.244 +&& echo "Success" \
3.245 +|| echo "Failed"
3.246 +
3.247 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER2" "freebusy_other" "$USER2" \
3.248 +> out5s1.tmp \
3.249 +
3.250 + ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" out5s1.tmp \
3.251 +&& echo "Success" \
3.252 +|| echo "Failed"
3.253 +
3.254 +# Present the request to the delegate.
3.255 +
3.256 + "$RESOURCE_SCRIPT" $ARGS < "$DELFN" 2>> "$ERROR" \
3.257 +> out6r.tmp
3.258 +
3.259 + "$SHOWMAIL" < out6r.tmp \
3.260 +> out6p0.tmp
3.261 +
3.262 + "$SHOWMAIL" 1 < out6r.tmp \
3.263 +> out6p1.tmp
3.264 +
3.265 +if grep -q "To: $SENDER2ADDRESS" out6p0.tmp ; then
3.266 + ORGFN=out6p0.tmp ; DELFN=out6p1.tmp
3.267 +else
3.268 + ORGFN=out6p1.tmp ; DELFN=out6p0.tmp
3.269 +fi
3.270 +
3.271 +# One of the responses will be a reply sent to the organiser.
3.272 +
3.273 + grep -q "To: $SENDER2ADDRESS" "$ORGFN" \
3.274 +&& grep -q 'METHOD:REPLY' "$ORGFN" \
3.275 +&& grep -q 'ATTENDEE.*;PARTSTAT=DELEGATED.*:'"$USER1" "$ORGFN" \
3.276 +&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED.*:'"$USER2" "$ORGFN" \
3.277 +&& echo "Success" \
3.278 +|| echo "Failed"
3.279 +
3.280 +# The other will be a reply to the delegator.
3.281 +
3.282 + grep -q "To: $USER1ADDRESS" "$DELFN" \
3.283 +&& grep -q 'METHOD:REPLY' "$DELFN" \
3.284 +&& grep -q 'ATTENDEE.*;PARTSTAT=DELEGATED.*:'"$USER1" "$DELFN" \
3.285 +&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED.*:'"$USER2" "$DELFN" \
3.286 +&& echo "Success" \
3.287 +|| echo "Failed"
3.288 +
3.289 +# The delegate should now have a changed schedule.
3.290 +
3.291 + "$LIST_SCRIPT" $LIST_ARGS "$USER1" "freebusy" \
3.292 +> out7f0.tmp
3.293 +
3.294 + ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" "out7f0.tmp" \
3.295 +&& echo "Success" \
3.296 +|| echo "Failed"
3.297 +
3.298 + "$LIST_SCRIPT" $LIST_ARGS "$USER2" "freebusy" \
3.299 +> out7f1.tmp
3.300 +
3.301 + grep -q "^20141126T153000Z${TAB}20141126T163000Z" "out7f1.tmp" \
3.302 +&& grep -q "event27@example.com" "out7f1.tmp" \
3.303 +&& echo "Success" \
3.304 +|| echo "Failed"
3.305 +
3.306 +# Present the reply to the organiser.
3.307 +
3.308 + "$PERSON_SCRIPT" $ARGS < "$ORGFN" 2>> "$ERROR" \
3.309 +| tee out8r.tmp \
3.310 +| "$SHOWMAIL" \
3.311 +> out8.tmp
3.312 +
3.313 +# Check the free/busy status of the attendees at the organiser.
3.314 +# Now, the delegate is attending.
3.315 +
3.316 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER2" "freebusy_other" "$USER1" \
3.317 +> out8s0.tmp \
3.318 +
3.319 + ! grep -q "^20141126T153000Z${TAB}20141126T163000Z" out8s0.tmp \
3.320 +&& ! grep -q "event27@example.com" out8s0.tmp \
3.321 +&& echo "Success" \
3.322 +|| echo "Failed"
3.323 +
3.324 + "$LIST_SCRIPT" $LIST_ARGS "$SENDER2" "freebusy_other" "$USER2" \
3.325 +> out8s1.tmp \
3.326 +
3.327 + grep -q "^20141126T153000Z${TAB}20141126T163000Z" out8s1.tmp \
3.328 +&& grep -q "event27@example.com" out8s1.tmp \
3.329 +&& echo "Success" \
3.330 +|| echo "Failed"
3.331 +
3.332 +# Present the reply to the delegator.
3.333 +
3.334 + "$RESOURCE_SCRIPT" $ARGS < "$DELFN" 2>> "$ERROR" \
3.335 +> out9r.tmp