# HG changeset patch # User Paul Boddie # Date 1477257661 -7200 # Node ID 85897718c7ce45d3b4c373681c1e22a020a418f0 # Parent 7d1cbe361b3bbe08a9a870aab3d7124eb277d63b Changed scheduling function registries to map names to collections of functions. Introduced common event update/removal scheduling functions in the quota module. Introduced common free/busy modification functionality in the quota module for different scheduling functions to use. Added the common module to the reserved modules list in the update tool. Added tests of recurring events and indefinitely-recurring events requiring scheduling using the quota functionality. diff -r 7d1cbe361b3b -r 85897718c7ce imiptools/handlers/scheduling/__init__.py --- a/imiptools/handlers/scheduling/__init__.py Sat Oct 08 23:45:25 2016 +0200 +++ b/imiptools/handlers/scheduling/__init__.py Sun Oct 23 23:21:01 2016 +0200 @@ -179,7 +179,13 @@ for line in lines: parts = parse_line(line) - functions.append((registry.get(parts[0]), tuple(parts[1:]))) + + # A sequence of functions is provided for each name. + + l = registry.get(parts[0]) + if l: + for function in l: + functions.append((function, tuple(parts[1:]))) return functions diff -r 7d1cbe361b3b -r 85897718c7ce imiptools/handlers/scheduling/access.py --- a/imiptools/handlers/scheduling/access.py Sat Oct 08 23:45:25 2016 +0200 +++ b/imiptools/handlers/scheduling/access.py Sun Oct 23 23:21:01 2016 +0200 @@ -145,8 +145,8 @@ # Registry of scheduling functions. scheduling_functions = { - "access_control_list" : access_control_list, - "same_domain_only" : same_domain_only, + "access_control_list" : [access_control_list], + "same_domain_only" : [same_domain_only], } # Registries of locking and unlocking functions. diff -r 7d1cbe361b3b -r 85897718c7ce imiptools/handlers/scheduling/freebusy.py --- a/imiptools/handlers/scheduling/freebusy.py Sat Oct 08 23:45:25 2016 +0200 +++ b/imiptools/handlers/scheduling/freebusy.py Sun Oct 23 23:21:01 2016 +0200 @@ -247,9 +247,9 @@ # Registry of scheduling functions. scheduling_functions = { - "schedule_in_freebusy" : schedule_in_freebusy, - "schedule_corrected_in_freebusy" : schedule_corrected_in_freebusy, - "schedule_next_available_in_freebusy" : schedule_next_available_in_freebusy, + "schedule_in_freebusy" : [schedule_in_freebusy], + "schedule_corrected_in_freebusy" : [schedule_corrected_in_freebusy], + "schedule_next_available_in_freebusy" : [schedule_next_available_in_freebusy], } # Registries of locking and unlocking functions. diff -r 7d1cbe361b3b -r 85897718c7ce imiptools/handlers/scheduling/quota.py --- a/imiptools/handlers/scheduling/quota.py Sat Oct 08 23:45:25 2016 +0200 +++ b/imiptools/handlers/scheduling/quota.py Sun Oct 23 23:21:01 2016 +0200 @@ -81,17 +81,7 @@ """ quota, group = _get_quota_and_group(handler, args) - - # Update the journal entries. - - journal = handler.get_journal() - entries = journal.get_entries_for_update(quota, group) - handler.update_freebusy(entries, handler.user, False) - journal.set_entries(quota, group, entries) - - # Store/update the object so that recurring events can be maintained. - - _update_object(handler, args) + _add_to_quota(handler, quota, group, handler.user, False) def remove_from_quota(handler, args): @@ -101,26 +91,9 @@ """ quota, group = _get_quota_and_group(handler, args) - - # Update the journal entries. - - journal = handler.get_journal() - entries = journal.get_entries_for_update(quota, group) - - # Remove only the entries associated with this recipient. + _remove_from_quota(handler, quota, group, handler.user) - removed = handler.remove_from_freebusy(entries) - for p in removed: - if p.attendee != handler.user: - entries.insert_period(p) - - journal.set_entries(quota, group, entries) - - # Remove participation from the object to stop recurring event generation. - - _remove_object(handler, args) - -def _update_object(handler, args): +def update_event(handler, args): "Update a stored version of the current object of the given 'handler'." @@ -145,7 +118,7 @@ journal.set_event(quota, handler.uid, handler.recurrenceid, obj.to_node()) -def _remove_object(handler, args): +def remove_event(handler, args): "Remove a stored version of the current object of the given 'handler'." @@ -252,6 +225,53 @@ total += period.get_duration() return total +def _add_to_quota(handler, quota, user, participant, is_organiser): + + """ + Record details of the current object of the 'handler' in the applicable + free/busy resource. + """ + + journal = handler.get_journal() + freebusy = journal.get_entries_for_update(quota, user) + handler.update_freebusy(freebusy, participant, is_organiser) + + # Remove original recurrence details replaced by additional + # recurrences, as well as obsolete additional recurrences. + + handler.remove_freebusy_for_recurrences(freebusy, journal.get_recurrences(quota, handler.uid)) + + # Update free/busy provider information if the event may recur indefinitely. + + if handler.possibly_recurring_indefinitely(): + journal.append_freebusy_provider(quota, handler.obj) + + journal.set_entries(quota, user, freebusy) + +def _remove_from_quota(handler, quota, user, participant): + + """ + Remove details of the current object of the 'handler' from the applicable + free/busy resource. + """ + + journal = handler.get_journal() + freebusy = journal.get_entries_for_update(quota, user) + + # Remove only the entries associated with this recipient. + + removed = handler.remove_from_freebusy(freebusy) + for p in removed: + if p.attendee != participant: + freebusy.insert_period(p) + + # Update free/busy provider information if the event may recur indefinitely. + + if handler.possibly_recurring_indefinitely(): + journal.remove_freebusy_provider(quota, handler.obj) + + journal.set_entries(quota, user, freebusy) + # Collective free/busy maintenance. def schedule_across_quota(handler, args): @@ -281,11 +301,7 @@ """ quota, organiser = _get_quota_and_identity(handler, args) - - journal = handler.get_journal() - freebusy = journal.get_entries_for_update(quota, organiser) - handler.update_freebusy(freebusy, organiser, True) - journal.set_entries(quota, organiser, freebusy) + _add_to_quota(handler, quota, organiser, organiser, True) def remove_from_quota_freebusy(handler, args): @@ -295,11 +311,7 @@ """ quota, organiser = _get_quota_and_identity(handler, args) - - journal = handler.get_journal() - freebusy = journal.get_entries_for_update(quota, organiser) - handler.remove_from_freebusy(freebusy) - journal.set_entries(quota, organiser, freebusy) + _remove_from_quota(handler, quota, organiser, organiser) def _get_quota_and_identity(handler, args): @@ -428,37 +440,37 @@ # Registry of scheduling functions. scheduling_functions = { - "check_quota" : check_quota, - "schedule_across_quota" : schedule_across_quota, - "schedule_for_delegate" : schedule_for_delegate, + "check_quota" : [check_quota], + "schedule_across_quota" : [schedule_across_quota], + "schedule_for_delegate" : [schedule_for_delegate], } # Registries of locking and unlocking functions. locking_functions = { - "check_quota" : lock_journal, - "schedule_across_quota" : lock_journal, - "schedule_for_delegate" : lock_journal, + "check_quota" : [lock_journal], + "schedule_across_quota" : [lock_journal], + "schedule_for_delegate" : [lock_journal], } unlocking_functions = { - "check_quota" : unlock_journal, - "schedule_across_quota" : unlock_journal, - "schedule_for_delegate" : unlock_journal, + "check_quota" : [unlock_journal], + "schedule_across_quota" : [unlock_journal], + "schedule_for_delegate" : [unlock_journal], } # Registries of listener functions. confirmation_functions = { - "check_quota" : add_to_quota, - "schedule_across_quota" : add_to_quota_freebusy, - "schedule_for_delegate" : add_to_quota, + "check_quota" : [add_to_quota, update_event], + "schedule_across_quota" : [add_to_quota_freebusy, update_event], + "schedule_for_delegate" : [add_to_quota, update_event], } retraction_functions = { - "check_quota" : remove_from_quota, - "schedule_across_quota" : remove_from_quota_freebusy, - "schedule_for_delegate" : remove_from_quota, + "check_quota" : [remove_from_quota, remove_event], + "schedule_across_quota" : [remove_from_quota_freebusy, remove_event], + "schedule_for_delegate" : [remove_from_quota, remove_event], } # vim: tabstop=4 expandtab shiftwidth=4 diff -r 7d1cbe361b3b -r 85897718c7ce tests/test_resource_invitation_constraints_quota_recurring_group.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_resource_invitation_constraints_quota_recurring_group.sh Sun Oct 23 23:21:01 2016 +0200 @@ -0,0 +1,161 @@ +#!/bin/sh + +. "`dirname \"$0\"`/common.sh" + +USER="mailto:resource-car-porsche911@example.com" +SENDER="mailto:paul.boddie@example.com" +QUOTA=cars + +mkdir -p "$PREFS/$USER" +echo 'Europe/Oslo' > "$PREFS/$USER/TZID" +echo 'share' > "$PREFS/$USER/freebusy_sharing" +cat > "$PREFS/$USER/scheduling_function" <> $ERROR \ +| "$SHOWMAIL" \ +> out0.tmp + + grep -q 'METHOD:REPLY' out0.tmp \ +&& ! grep -q '^FREEBUSY' out0.tmp \ +&& echo "Success" \ +|| echo "Failed" + +# Attempt to schedule an event. + +"$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-recurring.txt" 2>> $ERROR + + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out0f.tmp + + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out0f.tmp" \ +&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out0f.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Present the request to the resource. + + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/event-request-car-recurring.txt" 2>> $ERROR \ +| tee out1r.tmp \ +| "$SHOWMAIL" \ +> out1.tmp + + grep -q 'METHOD:REPLY' out1.tmp \ +&& grep -q 'ATTENDEE.*;PARTSTAT=DECLINED' out1.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out1f.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1f.tmp" \ +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out1f.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Check the quota (event is not confirmed). + + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \ +> out1e.tmp + + ! grep -q "event24@example.com" "out1e.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Modify the event and attempt to schedule it again. + + sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=11/;' "$TEMPLATES/event-request-car-recurring.txt" \ +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR + + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out1s.tmp + + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out1s.tmp" \ +&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out1s.tmp" \ +&& grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out1s.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Present the request to the resource. + + sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=11/;' "$TEMPLATES/event-request-car-recurring.txt" \ +| "$RESOURCE_SCRIPT" $ARGS 2>> $ERROR \ +| tee out2r.tmp \ +| "$SHOWMAIL" \ +> out2.tmp + + grep -q 'METHOD:REPLY' out2.tmp \ +&& grep -q 'ATTENDEE.*;PARTSTAT=DECLINED' out2.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out2f.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \ +&& ! grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out2f.tmp" \ +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out2f.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Check the quota (event is confirmed). + + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \ +> out2e.tmp + + ! grep -q "event24@example.com" "out2e.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Modify the event and attempt to schedule it again. + + sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=10/;' "$TEMPLATES/event-request-car-recurring.txt" \ +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR + + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out2s.tmp + + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2s.tmp" \ +&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out2s.tmp" \ +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out2s.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Present the request to the resource. + + sed 's/FREQ=DAILY/FREQ=DAILY;COUNT=10/;' "$TEMPLATES/event-request-car-recurring.txt" \ +| "$RESOURCE_SCRIPT" $ARGS 2>> $ERROR \ +| tee out3r.tmp \ +| "$SHOWMAIL" \ +> out3.tmp + + grep -q 'METHOD:REPLY' out3.tmp \ +&& grep -q 'ATTENDEE.*;PARTSTAT=ACCEPTED' out3.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out3f.tmp + + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f.tmp" \ +&& grep -q "^20141205T150000Z${TAB}20141205T160000Z" "out3f.tmp" \ +&& ! grep -q "^20141206T150000Z${TAB}20141206T160000Z" "out3f.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Check the quota (event is confirmed). + + "$LIST_SCRIPT" $LIST_ARGS "$QUOTA" "entries" "$SENDER" \ +> out3e.tmp + + grep -q "event24@example.com" "out3e.tmp" \ +&& echo "Success" \ +|| echo "Failed" diff -r 7d1cbe361b3b -r 85897718c7ce tests/test_resource_invitation_recurring_indefinitely_group.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_resource_invitation_recurring_indefinitely_group.sh Sun Oct 23 23:21:01 2016 +0200 @@ -0,0 +1,108 @@ +#!/bin/sh + +. "`dirname \"$0\"`/common.sh" + +USER="mailto:resource-room-confroom@example.com" +QUOTA=rooms + +mkdir -p "$PREFS/$USER" +echo 'Europe/Oslo' > "$PREFS/$USER/TZID" +echo 'share' > "$PREFS/$USER/freebusy_sharing" + +# Add collective scheduling tests. + +cat > "$PREFS/$USER/scheduling_function" <> $ERROR \ +| "$SHOWMAIL" \ +> out0.tmp + + grep -q 'METHOD:REPLY' out0.tmp \ +&& ! grep -q '^FREEBUSY' out0.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-indefinitely.txt" 2>> $ERROR \ +| "$SHOWMAIL" \ +> out2.tmp + + grep -q 'METHOD:REPLY' out2.tmp \ +&& grep -q 'ATTENDEE;PARTSTAT=ACCEPTED' out2.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/fb-request-all.txt" 2>> $ERROR \ +| "$SHOWMAIL" \ +> out3.tmp + + grep -q 'METHOD:REPLY' out3.tmp \ +&& grep -q 'FREEBUSY;FBTYPE=BUSY:20141114T090000Z/20141114T100000Z' out3.tmp \ +&& grep -q 'FREEBUSY;FBTYPE=BUSY:20141212T090000Z/20141212T100000Z' out3.tmp \ +&& grep -q 'FREEBUSY;FBTYPE=BUSY:20150109T090000Z/20150109T100000Z' out3.tmp \ +&& echo "Success" \ +|| echo "Failed" + +"$FREEBUSY_SCRIPT" "$USER" $FREEBUSY_ARGS $ARGS 2>> $ERROR + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_providers" \ +| tee out3p.tmp \ +| grep -q 'event14@example.com' \ +&& echo "Success" \ +|| echo "Failed" + + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring-indefinitely.txt" 2>> $ERROR +echo "Cancel..." + + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/fb-request-all.txt" 2>> $ERROR \ +| "$SHOWMAIL" \ +> out4.tmp + + grep -q 'METHOD:REPLY' out4.tmp \ +&& ! grep -q 'FREEBUSY;FBTYPE=BUSY:20141114T090000Z/20141114T100000Z' out4.tmp \ +&& ! grep -q 'FREEBUSY;FBTYPE=BUSY:20141212T090000Z/20141212T100000Z' out4.tmp \ +&& ! grep -q 'FREEBUSY;FBTYPE=BUSY:20150109T090000Z/20150109T100000Z' out4.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_providers" \ +> out4p.tmp + + ! grep -q 'event14@example.com' "out4p.tmp" \ +&& echo "Success" \ +|| echo "Failed" + +# Re-add event to test scheduling and presence in the freebusy-providers file. + + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-indefinitely.txt" 2>> $ERROR \ +| "$SHOWMAIL" \ +> out5.tmp + + grep -q 'METHOD:REPLY' out5.tmp \ +&& grep -q 'ATTENDEE;PARTSTAT=ACCEPTED' out5.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$RESOURCE_SCRIPT" $ARGS < "$TEMPLATES/fb-request-all.txt" 2>> $ERROR \ +| "$SHOWMAIL" \ +> out6.tmp + + grep -q 'METHOD:REPLY' out6.tmp \ +&& grep -q 'FREEBUSY;FBTYPE=BUSY:20141114T090000Z/20141114T100000Z' out6.tmp \ +&& grep -q 'FREEBUSY;FBTYPE=BUSY:20141212T090000Z/20141212T100000Z' out6.tmp \ +&& grep -q 'FREEBUSY;FBTYPE=BUSY:20150109T090000Z/20150109T100000Z' out6.tmp \ +&& echo "Success" \ +|| echo "Failed" + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_providers" \ +| tee out6p.tmp \ +| grep -q 'event14@example.com' \ +&& echo "Success" \ +|| echo "Failed" diff -r 7d1cbe361b3b -r 85897718c7ce tools/update_scheduling_modules.py --- a/tools/update_scheduling_modules.py Sat Oct 08 23:45:25 2016 +0200 +++ b/tools/update_scheduling_modules.py Sun Oct 23 23:21:01 2016 +0200 @@ -23,7 +23,7 @@ from os.path import join, split, splitext import imiptools.handlers -reserved = ["__init__.py", "manifest.py"] +reserved = ["__init__.py", "common.py", "manifest.py"] # The main program generating a new version of the manifest module.