imip-agent

imiptools/handlers/scheduling/quota.py

1119:97b5330fa753
2016-04-18 Paul Boddie Merged changes from the default branch. freebusy-collections
     1 #!/usr/bin/env python     2      3 """     4 Quota-related scheduling functionality.     5      6 Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from imiptools.dates import get_duration, to_utc_datetime    23 from imiptools.data import get_uri    24 from imiptools.period import Endless    25 from datetime import timedelta    26     27 # Quota maintenance.    28     29 def check_quota(handler, args):    30     31     """    32     Check the current object of the given 'handler' against the applicable    33     quota.    34     """    35     36     quota, group = _get_quota_and_group(handler, args)    37     38     # Obtain the journal entries and check the balance.    39     40     journal = handler.get_journal()    41     entries = journal.get_entries(quota, group)    42     limits = journal.get_limits(quota)    43     44     # Obtain a limit for the group or any general limit.    45     # Decline invitations if no limit has been set.    46     47     limit = limits.get(group) or limits.get("*")    48     if not limit:    49         return "DECLINED"    50     51     # Decline events whose durations exceed the balance.    52     53     total = _get_duration(handler)    54     55     if total == Endless():    56         return "DECLINED"    57     58     balance = get_duration(limit) - _get_usage(entries)    59     60     if total > balance:    61         return "DECLINED"    62     else:    63         return "ACCEPTED"    64     65 def add_to_quota(handler, args):    66     67     """    68     Record details of the current object of the given 'handler' in the    69     applicable quota.    70     """    71     72     quota, group = _get_quota_and_group(handler, args)    73     74     total = _get_duration(handler)    75     expiry = _get_expiry_time(handler)    76     77     # Reject indefinitely recurring events.    78     79     if total == Endless() or not expiry:    80         return    81     82     # Update the journal entries.    83     84     journal = handler.get_journal()    85     entries = journal.get_entries_for_update(quota, group)    86     handler.update_freebusy(entries, group, False)    87     journal.set_entries(quota, group, entries)    88     89 def remove_from_quota(handler, args):    90     91     """    92     Remove details of the current object of the given 'handler' from the    93     applicable quota.    94     """    95     96     quota, group = _get_quota_and_group(handler, args)    97     98     total = _get_duration(handler)    99    100     # Allow indefinitely recurring events.   101    102     if total == Endless():   103         total = None   104    105     # Update the journal entries.   106    107     journal = handler.get_journal()   108     entries = journal.get_entries_for_update(quota, group)   109     handler.remove_from_freebusy(entries)   110     journal.set_entries(quota, group, entries)   111    112 def _get_quota_and_group(handler, args):   113    114     """   115     Combine information about the current object from the 'handler' with the   116     given 'args' to return a tuple containing the quota group and the user   117     identity or group involved.   118     """   119    120     quota = args and args[0] or handler.user   121    122     # Obtain the identity to whom the quota will apply.   123    124     organiser = get_uri(handler.obj.get_value("ORGANIZER"))   125    126     # Obtain any user group to which the quota will apply instead.   127    128     journal = handler.get_journal()   129     groups = journal.get_groups(quota)   130    131     return quota, groups.get(organiser) or organiser   132    133 def _get_duration(handler):   134    135     "Return the duration of the current object provided by the 'handler'."   136    137     # Reject indefinitely recurring events.   138    139     if handler.obj.possibly_recurring_indefinitely():   140         return Endless()   141    142     # Otherwise, return a sum of the period durations.   143    144     total = timedelta(0)   145    146     for period in handler.get_periods(handler.obj):   147         duration = period.get_duration()   148    149         # Decline events whose period durations are endless.   150    151         if duration == Endless():   152             return duration   153         else:   154             total += duration   155    156     return total   157    158 def _get_expiry_time(handler):   159    160     """   161     Return the expiry time for quota purposes of the current object provided by   162     the 'handler'.   163     """   164    165     # Reject indefinitely recurring events.   166    167     if handler.obj.possibly_recurring_indefinitely():   168         return None   169    170     periods = handler.get_periods(handler.obj)   171     return periods and to_utc_datetime(periods[-1].get_end_point()) or None   172    173 def _get_usage(entries):   174    175     "Return the usage total according to the given 'entries'."   176    177     total = timedelta(0)   178     for period in entries:   179         total += period.get_duration()   180     return total   181    182 # Collective free/busy maintenance.   183    184 def schedule_across_quota(handler, args):   185    186     """   187     Check the current object of the given 'handler' against the schedules   188     managed by the quota.   189     """   190    191     quota, organiser = _get_quota_and_identity(handler, args)   192    193     # If newer than any old version, discard old details from the   194     # free/busy record and check for suitability.   195    196     periods = handler.get_periods(handler.obj)   197     freebusy = handler.get_journal().get_freebusy(quota, organiser)   198     scheduled = handler.can_schedule(freebusy, periods)   199    200     return scheduled and "ACCEPTED" or "DECLINED"   201    202 def add_to_quota_freebusy(handler, args):   203    204     """   205     Record details of the current object of the 'handler' in the applicable   206     free/busy resource.   207     """   208    209     quota, organiser = _get_quota_and_identity(handler, args)   210    211     journal = handler.get_journal()   212     freebusy = journal.get_freebusy_for_update(quota, organiser)   213     handler.update_freebusy(freebusy, organiser, True)   214     journal.set_freebusy(quota, organiser, freebusy)   215    216 def remove_from_quota_freebusy(handler, args):   217    218     """   219     Remove details of the current object of the 'handler' from the applicable   220     free/busy resource.   221     """   222    223     quota, organiser = _get_quota_and_identity(handler, args)   224    225     journal = handler.get_journal()   226     freebusy = journal.get_freebusy_for_update(quota, organiser)   227     handler.remove_from_freebusy(freebusy)   228     journal.set_freebusy(quota, organiser, freebusy)   229    230 def _get_quota_and_identity(handler, args):   231    232     """   233     Combine information about the current object from the 'handler' with the   234     given 'args' to return a tuple containing the quota group and the user   235     identity involved.   236     """   237    238     quota = args and args[0] or handler.user   239    240     # Obtain the identity for whom the scheduling will apply.   241    242     organiser = get_uri(handler.obj.get_value("ORGANIZER"))   243    244     return quota, organiser   245    246 # Locking and unlocking.   247    248 def lock_journal(handler, args):   249    250     "Using the 'handler' and 'args', lock the journal for the quota."   251    252     handler.get_journal().acquire_lock(_get_quota(handler, args))   253    254 def unlock_journal(handler, args):   255    256     "Using the 'handler' and 'args', unlock the journal for the quota."   257    258     handler.get_journal().release_lock(_get_quota(handler, args))   259    260 def _get_quota(handler, args):   261    262     "Return the quota using the 'handler' and 'args'."   263    264     return args and args[0] or handler.user   265    266 # Registry of scheduling functions.   267    268 scheduling_functions = {   269     "check_quota" : check_quota,   270     "schedule_across_quota" : schedule_across_quota,   271     }   272    273 # Registries of locking and unlocking functions.   274    275 locking_functions = {   276     "check_quota" : lock_journal,   277     "schedule_across_quota" : lock_journal,   278     }   279    280 unlocking_functions = {   281     "check_quota" : unlock_journal,   282     "schedule_across_quota" : unlock_journal,   283     }   284    285 # Registries of listener functions.   286    287 confirmation_functions = {   288     "check_quota" : add_to_quota,   289     "schedule_across_quota" : add_to_quota_freebusy,   290     }   291    292 retraction_functions = {   293     "check_quota" : remove_from_quota,   294     "schedule_across_quota" : remove_from_quota_freebusy,   295     }   296    297 # vim: tabstop=4 expandtab shiftwidth=4