1.1 --- a/imiptools/handlers/resource.py Mon Feb 08 00:14:53 2016 +0100
1.2 +++ b/imiptools/handlers/resource.py Mon Feb 08 00:47:00 2016 +0100
1.3 @@ -23,7 +23,9 @@
1.4 from imiptools.handlers import Handler
1.5 from imiptools.handlers.common import CommonFreebusy, CommonEvent
1.6 from imiptools.handlers.scheduling import apply_scheduling_functions, \
1.7 - confirm_scheduling, retract_scheduling
1.8 + confirm_scheduling, \
1.9 + finish_scheduling, \
1.10 + retract_scheduling
1.11
1.12 class ResourceHandler(CommonEvent, Handler):
1.13
1.14 @@ -87,49 +89,56 @@
1.15 "Attempt to schedule the current object for the current user."
1.16
1.17 attendee_attr = uri_dict(self.obj.get_value_map("ATTENDEE"))[self.user]
1.18 +
1.19 + # Attempt to schedule the event.
1.20 +
1.21 scheduled = self.schedule()
1.22
1.23 - # Update the participation of the resource in the object.
1.24 - # Update free/busy information.
1.25 + try:
1.26 + # Update the participation of the resource in the object.
1.27 + # Update free/busy information.
1.28
1.29 - if scheduled in ("ACCEPTED", "DECLINED"):
1.30 - method = "REPLY"
1.31 - attendee_attr = self.update_participation(scheduled)
1.32 + if scheduled in ("ACCEPTED", "DECLINED"):
1.33 + method = "REPLY"
1.34 + attendee_attr = self.update_participation(scheduled)
1.35
1.36 - self.update_event_in_freebusy(for_organiser=False)
1.37 - self.remove_event_from_freebusy_offers()
1.38 + self.update_event_in_freebusy(for_organiser=False)
1.39 + self.remove_event_from_freebusy_offers()
1.40
1.41 - # Set the complete event or an additional occurrence.
1.42 + # Set the complete event or an additional occurrence.
1.43
1.44 - event = self.obj.to_node()
1.45 - self.store.set_event(self.user, self.uid, self.recurrenceid, event)
1.46 + event = self.obj.to_node()
1.47 + self.store.set_event(self.user, self.uid, self.recurrenceid, event)
1.48
1.49 - # Remove additional recurrences if handling a complete event.
1.50 - # Also remove any previous cancellations involving this event.
1.51 + # Remove additional recurrences if handling a complete event.
1.52 + # Also remove any previous cancellations involving this event.
1.53
1.54 - if not self.recurrenceid:
1.55 - self.store.remove_recurrences(self.user, self.uid)
1.56 - self.store.remove_cancellations(self.user, self.uid)
1.57 - else:
1.58 - self.store.remove_cancellation(self.user, self.uid, self.recurrenceid)
1.59 + if not self.recurrenceid:
1.60 + self.store.remove_recurrences(self.user, self.uid)
1.61 + self.store.remove_cancellations(self.user, self.uid)
1.62 + else:
1.63 + self.store.remove_cancellation(self.user, self.uid, self.recurrenceid)
1.64
1.65 - # Confirm any scheduling.
1.66 + if scheduled == "ACCEPTED":
1.67 + self.confirm_scheduling()
1.68
1.69 - if scheduled == "ACCEPTED":
1.70 - self.confirm_scheduling()
1.71 + # For countered proposals, record the offer in the resource's
1.72 + # free/busy collection.
1.73
1.74 - # For countered proposals, record the offer in the resource's
1.75 - # free/busy collection.
1.76 + elif scheduled == "COUNTER":
1.77 + method = "COUNTER"
1.78 + self.update_event_in_freebusy_offers()
1.79 +
1.80 + # For inappropriate periods, reply declining participation.
1.81
1.82 - elif scheduled == "COUNTER":
1.83 - method = "COUNTER"
1.84 - self.update_event_in_freebusy_offers()
1.85 + else:
1.86 + method = "REPLY"
1.87 + attendee_attr = self.update_participation("DECLINED")
1.88
1.89 - # For inappropriate periods, reply declining participation.
1.90 + # Confirm any scheduling.
1.91
1.92 - else:
1.93 - method = "REPLY"
1.94 - attendee_attr = self.update_participation("DECLINED")
1.95 + finally:
1.96 + self.finish_scheduling()
1.97
1.98 # Make a version of the object with just this attendee, update the
1.99 # DTSTAMP in the response, and return the object for sending.
1.100 @@ -192,6 +201,15 @@
1.101 if functions:
1.102 confirm_scheduling(functions.split("\n"), self)
1.103
1.104 + def finish_scheduling(self):
1.105 +
1.106 + "Finish the scheduling, unlocking resources where appropriate."
1.107 +
1.108 + functions = self.get_preferences().get("scheduling_function",
1.109 + "schedule_in_freebusy").split("\n")
1.110 +
1.111 + finish_scheduling(functions, self)
1.112 +
1.113 def retract_scheduling(self):
1.114
1.115 "Retract this event from scheduling records."
2.1 --- a/imiptools/handlers/scheduling/__init__.py Mon Feb 08 00:14:53 2016 +0100
2.2 +++ b/imiptools/handlers/scheduling/__init__.py Mon Feb 08 00:47:00 2016 +0100
2.3 @@ -21,8 +21,10 @@
2.4
2.5 from imiptools.text import parse_line
2.6 from imiptools.handlers.scheduling.manifest import confirmation_functions, \
2.7 + locking_functions, \
2.8 retraction_functions, \
2.9 - scheduling_functions
2.10 + scheduling_functions, \
2.11 + unlocking_functions
2.12
2.13 # Function application/invocation.
2.14
2.15 @@ -34,12 +36,25 @@
2.16 """
2.17
2.18 # Obtain the actual scheduling functions with arguments.
2.19 + # Also obtain functions to lock resources.
2.20
2.21 - functions = get_function_calls(functions, scheduling_functions)
2.22 + schedulers = get_function_calls(functions, scheduling_functions)
2.23 + locks = get_function_calls(functions, locking_functions)
2.24 +
2.25 + # First, lock the resources to be used.
2.26 +
2.27 + for fn, args in locks:
2.28 +
2.29 + # Not all scheduling functions require compound locking.
2.30 +
2.31 + if fn:
2.32 + fn(handler, args)
2.33 +
2.34 + # Then, invoke the scheduling functions.
2.35
2.36 response = "ACCEPTED"
2.37
2.38 - for fn, args in functions:
2.39 + for fn, args in schedulers:
2.40
2.41 # NOTE: Should signal an error for incorrectly configured resources.
2.42
2.43 @@ -77,6 +92,26 @@
2.44 functions = get_function_calls(functions, confirmation_functions)
2.45 apply_functions(functions, handler)
2.46
2.47 +def finish_scheduling(functions, handler):
2.48 +
2.49 + """
2.50 + Finish scheduling using the given scheduling 'functions' for the current
2.51 + object of the given 'handler'.
2.52 + """
2.53 +
2.54 + # Obtain functions to unlock resources.
2.55 +
2.56 + locks = get_function_calls(functions, unlocking_functions)
2.57 +
2.58 + # Unlock the resources that were used.
2.59 +
2.60 + for fn, args in locks:
2.61 +
2.62 + # Not all scheduling functions require compound locking.
2.63 +
2.64 + if fn:
2.65 + fn(handler, args)
2.66 +
2.67 def retract_scheduling(functions, handler):
2.68
2.69 """
3.1 --- a/imiptools/handlers/scheduling/access.py Mon Feb 08 00:14:53 2016 +0100
3.2 +++ b/imiptools/handlers/scheduling/access.py Mon Feb 08 00:47:00 2016 +0100
3.3 @@ -132,6 +132,11 @@
3.4 "same_domain_only" : same_domain_only,
3.5 }
3.6
3.7 +# Registries of locking and unlocking functions.
3.8 +
3.9 +locking_functions = {}
3.10 +unlocking_functions = {}
3.11 +
3.12 # Registries of listener functions.
3.13
3.14 confirmation_functions = {}
4.1 --- a/imiptools/handlers/scheduling/freebusy.py Mon Feb 08 00:14:53 2016 +0100
4.2 +++ b/imiptools/handlers/scheduling/freebusy.py Mon Feb 08 00:47:00 2016 +0100
4.3 @@ -217,6 +217,11 @@
4.4 "schedule_next_available_in_freebusy" : schedule_next_available_in_freebusy,
4.5 }
4.6
4.7 +# Registries of locking and unlocking functions.
4.8 +
4.9 +locking_functions = {}
4.10 +unlocking_functions = {}
4.11 +
4.12 # Registries of listener functions.
4.13
4.14 confirmation_functions = {}
5.1 --- a/imiptools/handlers/scheduling/manifest.py Mon Feb 08 00:14:53 2016 +0100
5.2 +++ b/imiptools/handlers/scheduling/manifest.py Mon Feb 08 00:47:00 2016 +0100
5.3 @@ -1,31 +1,45 @@
5.4 confirmation_functions = {}
5.5 +locking_functions = {}
5.6 retraction_functions = {}
5.7 scheduling_functions = {}
5.8 +unlocking_functions = {}
5.9
5.10 from imiptools.handlers.scheduling.quota import (
5.11 confirmation_functions as c,
5.12 + locking_functions as l,
5.13 retraction_functions as r,
5.14 - scheduling_functions as s)
5.15 + scheduling_functions as s,
5.16 + unlocking_functions as u)
5.17
5.18 confirmation_functions.update(c)
5.19 +locking_functions.update(l)
5.20 retraction_functions.update(r)
5.21 scheduling_functions.update(s)
5.22 +unlocking_functions.update(u)
5.23
5.24 from imiptools.handlers.scheduling.freebusy import (
5.25 confirmation_functions as c,
5.26 + locking_functions as l,
5.27 retraction_functions as r,
5.28 - scheduling_functions as s)
5.29 + scheduling_functions as s,
5.30 + unlocking_functions as u)
5.31
5.32 confirmation_functions.update(c)
5.33 +locking_functions.update(l)
5.34 retraction_functions.update(r)
5.35 scheduling_functions.update(s)
5.36 +unlocking_functions.update(u)
5.37
5.38 from imiptools.handlers.scheduling.access import (
5.39 confirmation_functions as c,
5.40 + locking_functions as l,
5.41 retraction_functions as r,
5.42 - scheduling_functions as s)
5.43 + scheduling_functions as s,
5.44 + unlocking_functions as u)
5.45
5.46 confirmation_functions.update(c)
5.47 +locking_functions.update(l)
5.48 retraction_functions.update(r)
5.49 scheduling_functions.update(s)
5.50 +unlocking_functions.update(u)
5.51
6.1 --- a/imiptools/handlers/scheduling/quota.py Mon Feb 08 00:14:53 2016 +0100
6.2 +++ b/imiptools/handlers/scheduling/quota.py Mon Feb 08 00:47:00 2016 +0100
6.3 @@ -76,15 +76,10 @@
6.4 # Obtain the journal entries and limits.
6.5
6.6 journal = handler.get_journal()
6.7 - journal.acquire_lock(quota)
6.8 + entries = journal.get_entries(quota, group)
6.9
6.10 - try:
6.11 - entries = journal.get_entries(quota, group)
6.12 - if _add_to_entries(entries, handler.obj.get_uid(), handler.obj.get_recurrenceid(), format_duration(total)):
6.13 - journal.set_entries(quota, group, entries)
6.14 -
6.15 - finally:
6.16 - journal.release_lock(quota)
6.17 + if _add_to_entries(entries, handler.obj.get_uid(), handler.obj.get_recurrenceid(), format_duration(total)):
6.18 + journal.set_entries(quota, group, entries)
6.19
6.20 def remove_from_quota(handler, args):
6.21
6.22 @@ -100,15 +95,10 @@
6.23 # Obtain the journal entries and limits.
6.24
6.25 journal = handler.get_journal()
6.26 - journal.acquire_lock(quota)
6.27 + entries = journal.get_entries(quota, group)
6.28
6.29 - try:
6.30 - entries = journal.get_entries(quota, group)
6.31 - if _remove_from_entries(entries, handler.obj.get_uid(), handler.obj.get_recurrenceid(), format_duration(total)):
6.32 - journal.set_entries(quota, group, entries)
6.33 -
6.34 - finally:
6.35 - journal.release_lock(quota)
6.36 + if _remove_from_entries(entries, handler.obj.get_uid(), handler.obj.get_recurrenceid(), format_duration(total)):
6.37 + journal.set_entries(quota, group, entries)
6.38
6.39 def _get_quota_and_group(handler, args):
6.40
6.41 @@ -254,15 +244,9 @@
6.42 quota, organiser = _get_quota_and_identity(handler, args)
6.43
6.44 journal = handler.get_journal()
6.45 - journal.acquire_lock(quota)
6.46 -
6.47 - try:
6.48 - freebusy = journal.get_freebusy(quota, organiser)
6.49 - handler.update_freebusy(freebusy, organiser, True)
6.50 - journal.set_freebusy(quota, organiser, freebusy)
6.51 -
6.52 - finally:
6.53 - journal.release_lock(quota)
6.54 + freebusy = journal.get_freebusy(quota, organiser)
6.55 + handler.update_freebusy(freebusy, organiser, True)
6.56 + journal.set_freebusy(quota, organiser, freebusy)
6.57
6.58 def remove_from_quota_freebusy(handler, args):
6.59
6.60 @@ -274,15 +258,9 @@
6.61 quota, organiser = _get_quota_and_identity(handler, args)
6.62
6.63 journal = handler.get_journal()
6.64 - journal.acquire_lock(quota)
6.65 -
6.66 - try:
6.67 - freebusy = journal.get_freebusy(quota, organiser)
6.68 - handler.remove_from_freebusy(freebusy)
6.69 - journal.set_freebusy(quota, organiser, freebusy)
6.70 -
6.71 - finally:
6.72 - journal.release_lock(quota)
6.73 + freebusy = journal.get_freebusy(quota, organiser)
6.74 + handler.remove_from_freebusy(freebusy)
6.75 + journal.set_freebusy(quota, organiser, freebusy)
6.76
6.77 def _get_quota_and_identity(handler, args):
6.78
6.79 @@ -300,6 +278,26 @@
6.80
6.81 return quota, organiser
6.82
6.83 +# Locking and unlocking.
6.84 +
6.85 +def lock_journal(handler, args):
6.86 +
6.87 + "Using the 'handler' and 'args', lock the journal for the quota."
6.88 +
6.89 + handler.get_journal().acquire_lock(_get_quota(handler, args))
6.90 +
6.91 +def unlock_journal(handler, args):
6.92 +
6.93 + "Using the 'handler' and 'args', unlock the journal for the quota."
6.94 +
6.95 + handler.get_journal().release_lock(_get_quota(handler, args))
6.96 +
6.97 +def _get_quota(handler, args):
6.98 +
6.99 + "Return the quota using the 'handler' and 'args'."
6.100 +
6.101 + return args and args[0] or handler.user
6.102 +
6.103 # Registry of scheduling functions.
6.104
6.105 scheduling_functions = {
6.106 @@ -307,6 +305,18 @@
6.107 "schedule_across_quota" : schedule_across_quota,
6.108 }
6.109
6.110 +# Registries of locking and unlocking functions.
6.111 +
6.112 +locking_functions = {
6.113 + "check_quota" : lock_journal,
6.114 + "schedule_across_quota" : lock_journal,
6.115 + }
6.116 +
6.117 +unlocking_functions = {
6.118 + "check_quota" : unlock_journal,
6.119 + "schedule_across_quota" : unlock_journal,
6.120 + }
6.121 +
6.122 # Registries of listener functions.
6.123
6.124 confirmation_functions = {
7.1 --- a/tools/update_scheduling_modules.py Mon Feb 08 00:14:53 2016 +0100
7.2 +++ b/tools/update_scheduling_modules.py Mon Feb 08 00:47:00 2016 +0100
7.3 @@ -46,8 +46,10 @@
7.4 try:
7.5 print >>f, """\
7.6 confirmation_functions = {}
7.7 +locking_functions = {}
7.8 retraction_functions = {}
7.9 scheduling_functions = {}
7.10 +unlocking_functions = {}
7.11 """
7.12
7.13 for filename in filenames:
7.14 @@ -56,12 +58,16 @@
7.15 print >>f, """\
7.16 from imiptools.handlers.scheduling.%s import (
7.17 confirmation_functions as c,
7.18 + locking_functions as l,
7.19 retraction_functions as r,
7.20 - scheduling_functions as s)
7.21 + scheduling_functions as s,
7.22 + unlocking_functions as u)
7.23
7.24 confirmation_functions.update(c)
7.25 +locking_functions.update(l)
7.26 retraction_functions.update(r)
7.27 scheduling_functions.update(s)
7.28 +unlocking_functions.update(u)
7.29 """ % module
7.30
7.31 finally: