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