imip-agent

Change of imiptools/stores/__init__.py

1088:31d839d508f6
imiptools/stores/__init__.py freebusy-collections
     1.1 --- a/imiptools/stores/__init__.py	Wed Mar 09 21:38:56 2016 +0100
     1.2 +++ b/imiptools/stores/__init__.py	Thu Mar 10 01:43:31 2016 +0100
     1.3 @@ -3,7 +3,7 @@
     1.4  """
     1.5  General support for calendar data storage.
     1.6  
     1.7 -Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
     1.8 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
     1.9  
    1.10  This program is free software; you can redistribute it and/or modify it under
    1.11  the terms of the GNU General Public License as published by the Free Software
    1.12 @@ -19,601 +19,25 @@
    1.13  this program.  If not, see <http://www.gnu.org/licenses/>.
    1.14  """
    1.15  
    1.16 -from imiptools.dates import format_datetime
    1.17 -
    1.18 -class StoreBase:
    1.19 -
    1.20 -    "The core operations of a data store."
    1.21 -
    1.22 -    def acquire_lock(self, user, timeout=None):
    1.23 -        pass
    1.24 -
    1.25 -    def release_lock(self, user):
    1.26 -        pass
    1.27 -
    1.28 -    # User discovery.
    1.29 -
    1.30 -    def get_users(self):
    1.31 -
    1.32 -        "Return a list of users."
    1.33 -
    1.34 -        pass
    1.35 -
    1.36 -    # Event and event metadata access.
    1.37 -
    1.38 -    def get_events(self, user):
    1.39 -
    1.40 -        "Return a list of event identifiers."
    1.41 -
    1.42 -        pass
    1.43 -
    1.44 -    def get_all_events(self, user):
    1.45 -
    1.46 -        "Return a set of (uid, recurrenceid) tuples for all events."
    1.47 -
    1.48 -        uids = self.get_events(user)
    1.49 -        if not uids:
    1.50 -            return set()
    1.51 -
    1.52 -        all_events = set()
    1.53 -        for uid in uids:
    1.54 -            all_events.add((uid, None))
    1.55 -            all_events.update([(uid, recurrenceid) for recurrenceid in self.get_recurrences(user, uid)])
    1.56 -
    1.57 -        return all_events
    1.58 -
    1.59 -    def get_event(self, user, uid, recurrenceid=None, dirname=None):
    1.60 -
    1.61 -        """
    1.62 -        Get the event for the given 'user' with the given 'uid'. If
    1.63 -        the optional 'recurrenceid' is specified, a specific instance or
    1.64 -        occurrence of an event is returned.
    1.65 -        """
    1.66 -
    1.67 -        pass
    1.68 -
    1.69 -    def get_complete_event(self, user, uid):
    1.70 -
    1.71 -        "Get the event for the given 'user' with the given 'uid'."
    1.72 -
    1.73 -        pass
    1.74 -
    1.75 -    def set_event(self, user, uid, recurrenceid, node):
    1.76 -
    1.77 -        """
    1.78 -        Set an event for 'user' having the given 'uid' and 'recurrenceid' (which
    1.79 -        if the latter is specified, a specific instance or occurrence of an
    1.80 -        event is referenced), using the given 'node' description.
    1.81 -        """
    1.82 -
    1.83 -        if recurrenceid:
    1.84 -            return self.set_recurrence(user, uid, recurrenceid, node)
    1.85 -        else:
    1.86 -            return self.set_complete_event(user, uid, node)
    1.87 -
    1.88 -    def set_complete_event(self, user, uid, node):
    1.89 +from imiptools.stores import file
    1.90 +from imiptools.stores.database import stores as database_stores
    1.91  
    1.92 -        "Set an event for 'user' having the given 'uid' and 'node'."
    1.93 -
    1.94 -        pass
    1.95 -
    1.96 -    def remove_event(self, user, uid, recurrenceid=None):
    1.97 -
    1.98 -        """
    1.99 -        Remove an event for 'user' having the given 'uid'. If the optional
   1.100 -        'recurrenceid' is specified, a specific instance or occurrence of an
   1.101 -        event is removed.
   1.102 -        """
   1.103 -
   1.104 -        if recurrenceid:
   1.105 -            return self.remove_recurrence(user, uid, recurrenceid)
   1.106 -        else:
   1.107 -            for recurrenceid in self.get_recurrences(user, uid) or []:
   1.108 -                self.remove_recurrence(user, uid, recurrenceid)
   1.109 -            return self.remove_complete_event(user, uid)
   1.110 -
   1.111 -    def remove_complete_event(self, user, uid):
   1.112 -
   1.113 -        "Remove an event for 'user' having the given 'uid'."
   1.114 -
   1.115 -        self.remove_recurrences(user, uid)
   1.116 -        return self.remove_parent_event(user, uid)
   1.117 -
   1.118 -    def remove_parent_event(self, user, uid):
   1.119 -
   1.120 -        "Remove the parent event for 'user' having the given 'uid'."
   1.121 -
   1.122 -        pass
   1.123 -
   1.124 -    def get_recurrences(self, user, uid):
   1.125 -
   1.126 -        """
   1.127 -        Get additional event instances for an event of the given 'user' with the
   1.128 -        indicated 'uid'. Both active and cancelled recurrences are returned.
   1.129 -        """
   1.130 -
   1.131 -        return self.get_active_recurrences(user, uid) + self.get_cancelled_recurrences(user, uid)
   1.132 -
   1.133 -    def get_active_recurrences(self, user, uid):
   1.134 -
   1.135 -        """
   1.136 -        Get additional event instances for an event of the given 'user' with the
   1.137 -        indicated 'uid'. Cancelled recurrences are not returned.
   1.138 -        """
   1.139 -
   1.140 -        pass
   1.141 -
   1.142 -    def get_cancelled_recurrences(self, user, uid):
   1.143 -
   1.144 -        """
   1.145 -        Get additional event instances for an event of the given 'user' with the
   1.146 -        indicated 'uid'. Only cancelled recurrences are returned.
   1.147 -        """
   1.148 -
   1.149 -        pass
   1.150 -
   1.151 -    def get_recurrence(self, user, uid, recurrenceid):
   1.152 -
   1.153 -        """
   1.154 -        For the event of the given 'user' with the given 'uid', return the
   1.155 -        specific recurrence indicated by the 'recurrenceid'.
   1.156 -        """
   1.157 -
   1.158 -        pass
   1.159 -
   1.160 -    def set_recurrence(self, user, uid, recurrenceid, node):
   1.161 -
   1.162 -        "Set an event for 'user' having the given 'uid' and 'node'."
   1.163 -
   1.164 -        pass
   1.165 +# Build a catalogue of store types.
   1.166  
   1.167 -    def remove_recurrence(self, user, uid, recurrenceid):
   1.168 -
   1.169 -        """
   1.170 -        Remove a special recurrence from an event stored by 'user' having the
   1.171 -        given 'uid' and 'recurrenceid'.
   1.172 -        """
   1.173 -
   1.174 -        pass
   1.175 -
   1.176 -    def remove_recurrences(self, user, uid):
   1.177 -
   1.178 -        """
   1.179 -        Remove all recurrences for an event stored by 'user' having the given
   1.180 -        'uid'.
   1.181 -        """
   1.182 -
   1.183 -        for recurrenceid in self.get_recurrences(user, uid):
   1.184 -            self.remove_recurrence(user, uid, recurrenceid)
   1.185 -
   1.186 -        return self.remove_recurrence_collection(user, uid)
   1.187 -
   1.188 -    def remove_recurrence_collection(self, user, uid):
   1.189 -
   1.190 -        """
   1.191 -        Remove the collection of recurrences stored by 'user' having the given
   1.192 -        'uid'.
   1.193 -        """
   1.194 -
   1.195 -        pass
   1.196 -
   1.197 -    # Free/busy period providers, upon extension of the free/busy records.
   1.198 -
   1.199 -    def _get_freebusy_providers(self, user):
   1.200 -
   1.201 -        """
   1.202 -        Return the free/busy providers for the given 'user'.
   1.203 -
   1.204 -        This function returns any stored datetime and a list of providers as a
   1.205 -        2-tuple. Each provider is itself a (uid, recurrenceid) tuple.
   1.206 -        """
   1.207 -
   1.208 -        pass
   1.209 -
   1.210 -    def get_freebusy_providers(self, user, dt=None):
   1.211 -
   1.212 -        """
   1.213 -        Return a set of uncancelled events of the form (uid, recurrenceid)
   1.214 -        providing free/busy details beyond the given datetime 'dt'.
   1.215 -
   1.216 -        If 'dt' is not specified, all events previously found to provide
   1.217 -        details will be returned. Otherwise, if 'dt' is earlier than the
   1.218 -        datetime recorded for the known providers, None is returned, indicating
   1.219 -        that the list of providers must be recomputed.
   1.220 -
   1.221 -        This function returns a list of (uid, recurrenceid) tuples upon success.
   1.222 -        """
   1.223 -
   1.224 -        t = self._get_freebusy_providers(user)
   1.225 -        if not t:
   1.226 -            return None
   1.227 -
   1.228 -        dt_string, t = t
   1.229 -
   1.230 -        # If the requested datetime is earlier than the stated datetime, the
   1.231 -        # providers will need to be recomputed.
   1.232 -
   1.233 -        if dt:
   1.234 -            providers_dt = get_datetime(dt_string)
   1.235 -            if not providers_dt or providers_dt > dt:
   1.236 -                return None
   1.237 -
   1.238 -        # Otherwise, return the providers.
   1.239 -
   1.240 -        return t[1:]
   1.241 -
   1.242 -    def _set_freebusy_providers(self, user, dt_string, t):
   1.243 -
   1.244 -        "Set the given provider timestamp 'dt_string' and table 't'."
   1.245 -
   1.246 -        pass
   1.247 -
   1.248 -    def set_freebusy_providers(self, user, dt, providers):
   1.249 -
   1.250 -        """
   1.251 -        Define the uncancelled events providing free/busy details beyond the
   1.252 -        given datetime 'dt'.
   1.253 -        """
   1.254 -
   1.255 -        t = []
   1.256 -
   1.257 -        for obj in providers:
   1.258 -            t.append((obj.get_uid(), obj.get_recurrenceid()))
   1.259 -
   1.260 -        return self._set_freebusy_providers(user, format_datetime(dt), t)
   1.261 -
   1.262 -    def append_freebusy_provider(self, user, provider):
   1.263 -
   1.264 -        "For the given 'user', append the free/busy 'provider'."
   1.265 -
   1.266 -        t = self._get_freebusy_providers(user)
   1.267 -        if not t:
   1.268 -            return False
   1.269 -
   1.270 -        dt_string, t = t
   1.271 -        t.append((provider.get_uid(), provider.get_recurrenceid()))
   1.272 -
   1.273 -        return self._set_freebusy_providers(user, dt_string, t)
   1.274 -
   1.275 -    def remove_freebusy_provider(self, user, provider):
   1.276 -
   1.277 -        "For the given 'user', remove the free/busy 'provider'."
   1.278 -
   1.279 -        t = self._get_freebusy_providers(user)
   1.280 -        if not t:
   1.281 -            return False
   1.282 -
   1.283 -        dt_string, t = t
   1.284 -        try:
   1.285 -            t.remove((provider.get_uid(), provider.get_recurrenceid()))
   1.286 -        except ValueError:
   1.287 -            return False
   1.288 -
   1.289 -        return self._set_freebusy_providers(user, dt_string, t)
   1.290 -
   1.291 -    # Free/busy period access.
   1.292 -
   1.293 -    def get_freebusy(self, user, name=None, mutable=False):
   1.294 -
   1.295 -        "Get free/busy details for the given 'user'."
   1.296 -
   1.297 -        pass
   1.298 -
   1.299 -    def get_freebusy_for_other(self, user, other, mutable=False):
   1.300 -
   1.301 -        "For the given 'user', get free/busy details for the 'other' user."
   1.302 -
   1.303 -        pass
   1.304 -
   1.305 -    def get_freebusy_for_update(self, user, name=None):
   1.306 -
   1.307 -        "Get free/busy details for the given 'user'."
   1.308 -
   1.309 -        return self.get_freebusy(user, name, True)
   1.310 -
   1.311 -    def get_freebusy_for_other_for_update(self, user, other):
   1.312 -
   1.313 -        "For the given 'user', get free/busy details for the 'other' user."
   1.314 -
   1.315 -        return self.get_freebusy_for_other(user, other, True)
   1.316 +stores = {
   1.317 +    "file" : file,
   1.318 +    }
   1.319 +stores.update(database_stores)
   1.320  
   1.321 -    def set_freebusy(self, user, freebusy, name=None):
   1.322 -
   1.323 -        "For the given 'user', set 'freebusy' details."
   1.324 -
   1.325 -        pass
   1.326 -
   1.327 -    def set_freebusy_for_other(self, user, freebusy, other):
   1.328 -
   1.329 -        "For the given 'user', set 'freebusy' details for the 'other' user."
   1.330 -
   1.331 -        pass
   1.332 -
   1.333 -    # Tentative free/busy periods related to countering.
   1.334 -
   1.335 -    def get_freebusy_offers(self, user, mutable=False):
   1.336 -
   1.337 -        "Get free/busy offers for the given 'user'."
   1.338 -
   1.339 -        pass
   1.340 -
   1.341 -    def get_freebusy_offers_for_update(self, user):
   1.342 -
   1.343 -        "Get free/busy offers for the given 'user'."
   1.344 -
   1.345 -        return self.get_freebusy_offers(user, True)
   1.346 -
   1.347 -    def set_freebusy_offers(self, user, freebusy):
   1.348 -
   1.349 -        "For the given 'user', set 'freebusy' offers."
   1.350 -
   1.351 -        return self.set_freebusy(user, freebusy, "freebusy-offers")
   1.352 -
   1.353 -    # Requests and counter-proposals.
   1.354 -
   1.355 -    def get_requests(self, user):
   1.356 -
   1.357 -        "Get requests for the given 'user'."
   1.358 -
   1.359 -        pass
   1.360 -
   1.361 -    def set_requests(self, user, requests):
   1.362 -
   1.363 -        "For the given 'user', set the list of queued 'requests'."
   1.364 -
   1.365 -        pass
   1.366 -
   1.367 -    def set_request(self, user, uid, recurrenceid=None, type=None):
   1.368 -
   1.369 -        """
   1.370 -        For the given 'user', set the queued 'uid' and 'recurrenceid',
   1.371 -        indicating a request, along with any given 'type'.
   1.372 -        """
   1.373 -
   1.374 -        pass
   1.375 -
   1.376 -    def queue_request(self, user, uid, recurrenceid=None, type=None):
   1.377 -
   1.378 -        """
   1.379 -        Queue a request for 'user' having the given 'uid'. If the optional
   1.380 -        'recurrenceid' is specified, the entry refers to a specific instance
   1.381 -        or occurrence of an event. The 'type' parameter can be used to indicate
   1.382 -        a specific type of request.
   1.383 -        """
   1.384 -
   1.385 -        requests = self.get_requests(user) or []
   1.386 -
   1.387 -        if not self.have_request(requests, uid, recurrenceid):
   1.388 -            return self.set_request(user, uid, recurrenceid, type)
   1.389 -
   1.390 -        return False
   1.391 -
   1.392 -    def dequeue_request(self, user, uid, recurrenceid=None):
   1.393 +# Access functions.
   1.394  
   1.395 -        """
   1.396 -        Dequeue all requests for 'user' having the given 'uid'. If the optional
   1.397 -        'recurrenceid' is specified, all requests for that specific instance or
   1.398 -        occurrence of an event are dequeued.
   1.399 -        """
   1.400 -
   1.401 -        requests = self.get_requests(user) or []
   1.402 -        result = []
   1.403 -
   1.404 -        for request in requests:
   1.405 -            if request[:2] != (uid, recurrenceid):
   1.406 -                result.append(request)
   1.407 -
   1.408 -        self.set_requests(user, result)
   1.409 -        return True
   1.410 -
   1.411 -    def has_request(self, user, uid, recurrenceid=None, type=None, strict=False):
   1.412 -        return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict)
   1.413 -
   1.414 -    def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False):
   1.415 -
   1.416 -        """
   1.417 -        Return whether 'requests' contains a request with the given 'uid' and
   1.418 -        any specified 'recurrenceid' and 'type'. If 'strict' is set to a true
   1.419 -        value, the precise type of the request must match; otherwise, any type
   1.420 -        of request for the identified object may be matched.
   1.421 -        """
   1.422 -
   1.423 -        for request in requests:
   1.424 -            if request[:2] == (uid, recurrenceid) and (
   1.425 -                not strict or
   1.426 -                not request[2:] and not type or
   1.427 -                request[2:] and request[2] == type):
   1.428 -
   1.429 -                return True
   1.430 -
   1.431 -        return False
   1.432 -
   1.433 -    def get_counters(self, user, uid, recurrenceid=None):
   1.434 -
   1.435 -        """
   1.436 -        For the given 'user', return a list of users from whom counter-proposals
   1.437 -        have been received for the given 'uid' and optional 'recurrenceid'.
   1.438 -        """
   1.439 -
   1.440 -        pass
   1.441 -
   1.442 -    def get_counter(self, user, other, uid, recurrenceid=None):
   1.443 -
   1.444 -        """
   1.445 -        For the given 'user', return the counter-proposal from 'other' for the
   1.446 -        given 'uid' and optional 'recurrenceid'.
   1.447 -        """
   1.448 -
   1.449 -        pass
   1.450 -
   1.451 -    def set_counter(self, user, other, node, uid, recurrenceid=None):
   1.452 -
   1.453 -        """
   1.454 -        For the given 'user', store a counter-proposal received from 'other' the
   1.455 -        given 'node' representing that proposal for the given 'uid' and
   1.456 -        'recurrenceid'.
   1.457 -        """
   1.458 -
   1.459 -        pass
   1.460 -
   1.461 -    def remove_counters(self, user, uid, recurrenceid=None):
   1.462 -
   1.463 -        """
   1.464 -        For the given 'user', remove all counter-proposals associated with the
   1.465 -        given 'uid' and 'recurrenceid'.
   1.466 -        """
   1.467 -
   1.468 -        pass
   1.469 +def get_store(store_type, store_dir):
   1.470 +    return stores[store_type].Store(store_dir)
   1.471  
   1.472 -    def remove_counter(self, user, other, uid, recurrenceid=None):
   1.473 -
   1.474 -        """
   1.475 -        For the given 'user', remove any counter-proposal from 'other'
   1.476 -        associated with the given 'uid' and 'recurrenceid'.
   1.477 -        """
   1.478 -
   1.479 -        pass
   1.480 -
   1.481 -    # Event cancellation.
   1.482 -
   1.483 -    def cancel_event(self, user, uid, recurrenceid=None):
   1.484 -
   1.485 -        """
   1.486 -        Cancel an event for 'user' having the given 'uid'. If the optional
   1.487 -        'recurrenceid' is specified, a specific instance or occurrence of an
   1.488 -        event is cancelled.
   1.489 -        """
   1.490 -
   1.491 -        pass
   1.492 -
   1.493 -    def uncancel_event(self, user, uid, recurrenceid=None):
   1.494 -
   1.495 -        """
   1.496 -        Uncancel an event for 'user' having the given 'uid'. If the optional
   1.497 -        'recurrenceid' is specified, a specific instance or occurrence of an
   1.498 -        event is uncancelled.
   1.499 -        """
   1.500 -
   1.501 -        pass
   1.502 -
   1.503 -    def remove_cancellations(self, user, uid, recurrenceid=None):
   1.504 -
   1.505 -        """
   1.506 -        Remove cancellations for 'user' for any event having the given 'uid'. If
   1.507 -        the optional 'recurrenceid' is specified, a specific instance or
   1.508 -        occurrence of an event is affected.
   1.509 -        """
   1.510 -
   1.511 -        # Remove all recurrence cancellations if a general event is indicated.
   1.512 -
   1.513 -        if not recurrenceid:
   1.514 -            for _recurrenceid in self.get_cancelled_recurrences(user, uid):
   1.515 -                self.remove_cancellation(user, uid, _recurrenceid)
   1.516 -
   1.517 -        return self.remove_cancellation(user, uid, recurrenceid)
   1.518 -
   1.519 -    def remove_cancellation(self, user, uid, recurrenceid=None):
   1.520 -
   1.521 -        """
   1.522 -        Remove a cancellation for 'user' for the event having the given 'uid'.
   1.523 -        If the optional 'recurrenceid' is specified, a specific instance or
   1.524 -        occurrence of an event is affected.
   1.525 -        """
   1.526 -
   1.527 -        pass
   1.528 -
   1.529 -class PublisherBase:
   1.530 -
   1.531 -    "The core operations of a data publisher."
   1.532 -
   1.533 -    def set_freebusy(self, user, freebusy):
   1.534 -
   1.535 -        "For the given 'user', set 'freebusy' details."
   1.536 -
   1.537 -        pass
   1.538 -
   1.539 -class JournalBase:
   1.540 -
   1.541 -    "The core operations of a journal system supporting quotas."
   1.542 -
   1.543 -    # Quota and user identity/group discovery.
   1.544 -
   1.545 -    def get_quotas(self):
   1.546 +def get_publisher(publishing_dir):
   1.547 +    return file.Publisher(publishing_dir)
   1.548  
   1.549 -        "Return a list of quotas."
   1.550 -
   1.551 -        pass
   1.552 -
   1.553 -    def get_quota_users(self, quota):
   1.554 -
   1.555 -        "Return a list of quota users."
   1.556 -
   1.557 -        pass
   1.558 -
   1.559 -    # Groups of users sharing quotas.
   1.560 -
   1.561 -    def get_groups(self, quota):
   1.562 -
   1.563 -        "Return the identity mappings for the given 'quota' as a dictionary."
   1.564 -
   1.565 -        pass
   1.566 -
   1.567 -    def get_limits(self, quota):
   1.568 -
   1.569 -        """
   1.570 -        Return the limits for the 'quota' as a dictionary mapping identities or
   1.571 -        groups to durations.
   1.572 -        """
   1.573 -
   1.574 -        pass
   1.575 -
   1.576 -    # Free/busy period access for users within quota groups.
   1.577 -
   1.578 -    def get_freebusy(self, quota, user, mutable=False):
   1.579 -
   1.580 -        "Get free/busy details for the given 'quota' and 'user'."
   1.581 -
   1.582 -        pass
   1.583 -
   1.584 -    def get_freebusy_for_update(self, quota, user):
   1.585 -
   1.586 -        "Get free/busy details for the given 'quota' and 'user'."
   1.587 -
   1.588 -        return self.get_freebusy(quota, user, True)
   1.589 -
   1.590 -    def set_freebusy(self, quota, user, freebusy):
   1.591 -
   1.592 -        "For the given 'quota' and 'user', set 'freebusy' details."
   1.593 -
   1.594 -        pass
   1.595 -
   1.596 -    # Journal entry methods.
   1.597 -
   1.598 -    def get_entries(self, quota, group, mutable=False):
   1.599 -
   1.600 -        """
   1.601 -        Return a list of journal entries for the given 'quota' for the indicated
   1.602 -        'group'.
   1.603 -        """
   1.604 -
   1.605 -        pass
   1.606 -
   1.607 -    def get_entries_for_update(self, quota, group):
   1.608 -
   1.609 -        """
   1.610 -        Return a list of journal entries for the given 'quota' for the indicated
   1.611 -        'group'.
   1.612 -        """
   1.613 -
   1.614 -        return self.get_entries(quota, group, True)
   1.615 -
   1.616 -    def set_entries(self, quota, group, entries):
   1.617 -
   1.618 -        """
   1.619 -        For the given 'quota' and indicated 'group', set the list of journal
   1.620 -        'entries'.
   1.621 -        """
   1.622 -
   1.623 -        pass
   1.624 +def get_journal(store_type, journal_dir):
   1.625 +    return stores[store_type].Journal(journal_dir)
   1.626  
   1.627  # vim: tabstop=4 expandtab shiftwidth=4