# HG changeset patch # User Paul Boddie # Date 1457570611 -3600 # Node ID 31d839d508f6cb303db441fc661b05798fc2f4ed # Parent 16b00c3bee0277d979c84409acac2feb05373688 Introduced a mechanism for selecting storage using a store type and the existing "directory" information. Made a specific PostgreSQL store type. Changed various tools and tests to support non-file store types, introducing a script to access stored data and to serialise it for inspection using file-based tools. diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/__init__.py --- a/imiptools/__init__.py Wed Mar 09 21:38:56 2016 +0100 +++ b/imiptools/__init__.py Thu Mar 10 01:43:31 2016 +0100 @@ -25,7 +25,7 @@ from imiptools.content import handle_itip_part from imiptools.data import get_address, get_addresses, get_uri from imiptools.mail import Messenger -import imiptools.stores.file +from imiptools.stores import get_store, get_publisher, get_journal import sys, os # Postfix exit codes. @@ -56,6 +56,7 @@ self.outgoing_only = outgoing_only self.messenger = None self.lmtp_socket = None + self.store_type = None self.store_dir = None self.publishing_dir = None self.journal_dir = None @@ -63,13 +64,13 @@ self.debug = False def get_store(self): - return imiptools.stores.file.FileStore(self.store_dir) + return get_store(self.store_type, self.store_dir) def get_publisher(self): - return self.publishing_dir and imiptools.stores.file.FilePublisher(self.publishing_dir) or None + return self.publishing_dir and get_publisher(self.publishing_dir) or None def get_journal(self): - return imiptools.stores.file.FileJournal(self.journal_dir) + return get_journal(self.store_type, self.journal_dir) def process(self, f, original_recipients): @@ -122,6 +123,7 @@ recipients = [] senders = [] lmtp = [] + store_type = [] store_dir = [] publishing_dir = [] preferences_dir = [] @@ -152,6 +154,11 @@ elif arg == "-L": local_smtp = True + # Switch to getting the store type. + + elif arg == "-T": + l = store_type + # Switch to getting the store directory. elif arg == "-S": @@ -179,11 +186,14 @@ else: l.append(arg) - self.messenger = Messenger(lmtp_socket=lmtp and lmtp[0] or None, local_smtp=local_smtp, sender=senders and senders[0] or None) - self.store_dir = store_dir and store_dir[0] or None - self.publishing_dir = publishing_dir and publishing_dir[0] or None - self.preferences_dir = preferences_dir and preferences_dir[0] or None - self.journal_dir = journal_dir and journal_dir[0] or None + getvalue = lambda value, default=None: value and value[0] or default + + self.messenger = Messenger(lmtp_socket=getvalue(lmtp), local_smtp=local_smtp, sender=getvalue(senders)) + self.store_type = getvalue(store_type, config.STORE_TYPE) + self.store_dir = getvalue(store_dir) + self.publishing_dir = getvalue(publishing_dir) + self.preferences_dir = getvalue(preferences_dir) + self.journal_dir = getvalue(journal_dir) self.process(stream, original_recipients) def __call__(self): @@ -198,6 +208,7 @@ if "--help" in args: print >>sys.stderr, """\ Usage: %s [ -o ... ] [-s ... ] [ -l | -L ] \\ + [ -T ] [ -P ] \\ [ -p ] [ -j ] [ -d ] @@ -225,6 +236,7 @@ -p Indicates the location of user preference directories -S Indicates the location of the calendar data store containing user storage directories +-T Indicates the store and journal type (the configured value if omitted) Output options: diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/client.py --- a/imiptools/client.py Wed Mar 09 21:38:56 2016 +0100 +++ b/imiptools/client.py Thu Mar 10 01:43:31 2016 +0100 @@ -28,7 +28,7 @@ get_duration, get_timestamp from imiptools.i18n import get_translator from imiptools.profile import Preferences -import imiptools.stores.file +from imiptools.stores import get_store, get_publisher, get_journal class Client: @@ -48,11 +48,11 @@ self.user = user self.messenger = messenger - self.store = store or imiptools.stores.file.FileStore() - self.journal = journal or imiptools.stores.file.FileJournal() + self.store = store or get_store(config.STORE_TYPE, config.STORE_DIR) + self.journal = journal or get_journal(config.STORE_TYPE, config.JOURNAL_DIR) try: - self.publisher = publisher or imiptools.stores.file.FilePublisher() + self.publisher = publisher or get_publisher(config.PUBLISH_DIR) except OSError: self.publisher = None diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/config.py --- a/imiptools/config.py Wed Mar 09 21:38:56 2016 +0100 +++ b/imiptools/config.py Thu Mar 10 01:43:31 2016 +0100 @@ -14,6 +14,10 @@ OUTGOING_PREFIX = "people-outgoing" +# The store (and journal) type. + +STORE_TYPE = "file" + # The location of the stored calendar information. STORE_DIR = "/var/lib/imip-agent/store" diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/period.py --- a/imiptools/period.py Wed Mar 09 21:38:56 2016 +0100 +++ b/imiptools/period.py Thu Mar 10 01:43:31 2016 +0100 @@ -957,7 +957,7 @@ self._check_mutable() - if recurrenceids is None: + if not recurrenceids: columns, values = ["object_uid", "object_recurrenceid is not null"], [uid] else: columns, values = ["object_uid", "object_recurrenceid not in ?", "object_recurrenceid is not null"], [uid, tuple(recurrenceids)] diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/stores/__init__.py --- a/imiptools/stores/__init__.py Wed Mar 09 21:38:56 2016 +0100 +++ b/imiptools/stores/__init__.py Thu Mar 10 01:43:31 2016 +0100 @@ -3,7 +3,7 @@ """ General support for calendar data storage. -Copyright (C) 2014, 2015, 2016 Paul Boddie +Copyright (C) 2016 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -19,601 +19,25 @@ this program. If not, see . """ -from imiptools.dates import format_datetime - -class StoreBase: - - "The core operations of a data store." - - def acquire_lock(self, user, timeout=None): - pass - - def release_lock(self, user): - pass - - # User discovery. - - def get_users(self): - - "Return a list of users." - - pass - - # Event and event metadata access. - - def get_events(self, user): - - "Return a list of event identifiers." - - pass - - def get_all_events(self, user): - - "Return a set of (uid, recurrenceid) tuples for all events." - - uids = self.get_events(user) - if not uids: - return set() - - all_events = set() - for uid in uids: - all_events.add((uid, None)) - all_events.update([(uid, recurrenceid) for recurrenceid in self.get_recurrences(user, uid)]) - - return all_events - - def get_event(self, user, uid, recurrenceid=None, dirname=None): - - """ - Get the event for the given 'user' with the given 'uid'. If - the optional 'recurrenceid' is specified, a specific instance or - occurrence of an event is returned. - """ - - pass - - def get_complete_event(self, user, uid): - - "Get the event for the given 'user' with the given 'uid'." - - pass - - def set_event(self, user, uid, recurrenceid, node): - - """ - Set an event for 'user' having the given 'uid' and 'recurrenceid' (which - if the latter is specified, a specific instance or occurrence of an - event is referenced), using the given 'node' description. - """ - - if recurrenceid: - return self.set_recurrence(user, uid, recurrenceid, node) - else: - return self.set_complete_event(user, uid, node) - - def set_complete_event(self, user, uid, node): +from imiptools.stores import file +from imiptools.stores.database import stores as database_stores - "Set an event for 'user' having the given 'uid' and 'node'." - - pass - - def remove_event(self, user, uid, recurrenceid=None): - - """ - Remove an event for 'user' having the given 'uid'. If the optional - 'recurrenceid' is specified, a specific instance or occurrence of an - event is removed. - """ - - if recurrenceid: - return self.remove_recurrence(user, uid, recurrenceid) - else: - for recurrenceid in self.get_recurrences(user, uid) or []: - self.remove_recurrence(user, uid, recurrenceid) - return self.remove_complete_event(user, uid) - - def remove_complete_event(self, user, uid): - - "Remove an event for 'user' having the given 'uid'." - - self.remove_recurrences(user, uid) - return self.remove_parent_event(user, uid) - - def remove_parent_event(self, user, uid): - - "Remove the parent event for 'user' having the given 'uid'." - - pass - - def get_recurrences(self, user, uid): - - """ - Get additional event instances for an event of the given 'user' with the - indicated 'uid'. Both active and cancelled recurrences are returned. - """ - - return self.get_active_recurrences(user, uid) + self.get_cancelled_recurrences(user, uid) - - def get_active_recurrences(self, user, uid): - - """ - Get additional event instances for an event of the given 'user' with the - indicated 'uid'. Cancelled recurrences are not returned. - """ - - pass - - def get_cancelled_recurrences(self, user, uid): - - """ - Get additional event instances for an event of the given 'user' with the - indicated 'uid'. Only cancelled recurrences are returned. - """ - - pass - - def get_recurrence(self, user, uid, recurrenceid): - - """ - For the event of the given 'user' with the given 'uid', return the - specific recurrence indicated by the 'recurrenceid'. - """ - - pass - - def set_recurrence(self, user, uid, recurrenceid, node): - - "Set an event for 'user' having the given 'uid' and 'node'." - - pass +# Build a catalogue of store types. - def remove_recurrence(self, user, uid, recurrenceid): - - """ - Remove a special recurrence from an event stored by 'user' having the - given 'uid' and 'recurrenceid'. - """ - - pass - - def remove_recurrences(self, user, uid): - - """ - Remove all recurrences for an event stored by 'user' having the given - 'uid'. - """ - - for recurrenceid in self.get_recurrences(user, uid): - self.remove_recurrence(user, uid, recurrenceid) - - return self.remove_recurrence_collection(user, uid) - - def remove_recurrence_collection(self, user, uid): - - """ - Remove the collection of recurrences stored by 'user' having the given - 'uid'. - """ - - pass - - # Free/busy period providers, upon extension of the free/busy records. - - def _get_freebusy_providers(self, user): - - """ - Return the free/busy providers for the given 'user'. - - This function returns any stored datetime and a list of providers as a - 2-tuple. Each provider is itself a (uid, recurrenceid) tuple. - """ - - pass - - def get_freebusy_providers(self, user, dt=None): - - """ - Return a set of uncancelled events of the form (uid, recurrenceid) - providing free/busy details beyond the given datetime 'dt'. - - If 'dt' is not specified, all events previously found to provide - details will be returned. Otherwise, if 'dt' is earlier than the - datetime recorded for the known providers, None is returned, indicating - that the list of providers must be recomputed. - - This function returns a list of (uid, recurrenceid) tuples upon success. - """ - - t = self._get_freebusy_providers(user) - if not t: - return None - - dt_string, t = t - - # If the requested datetime is earlier than the stated datetime, the - # providers will need to be recomputed. - - if dt: - providers_dt = get_datetime(dt_string) - if not providers_dt or providers_dt > dt: - return None - - # Otherwise, return the providers. - - return t[1:] - - def _set_freebusy_providers(self, user, dt_string, t): - - "Set the given provider timestamp 'dt_string' and table 't'." - - pass - - def set_freebusy_providers(self, user, dt, providers): - - """ - Define the uncancelled events providing free/busy details beyond the - given datetime 'dt'. - """ - - t = [] - - for obj in providers: - t.append((obj.get_uid(), obj.get_recurrenceid())) - - return self._set_freebusy_providers(user, format_datetime(dt), t) - - def append_freebusy_provider(self, user, provider): - - "For the given 'user', append the free/busy 'provider'." - - t = self._get_freebusy_providers(user) - if not t: - return False - - dt_string, t = t - t.append((provider.get_uid(), provider.get_recurrenceid())) - - return self._set_freebusy_providers(user, dt_string, t) - - def remove_freebusy_provider(self, user, provider): - - "For the given 'user', remove the free/busy 'provider'." - - t = self._get_freebusy_providers(user) - if not t: - return False - - dt_string, t = t - try: - t.remove((provider.get_uid(), provider.get_recurrenceid())) - except ValueError: - return False - - return self._set_freebusy_providers(user, dt_string, t) - - # Free/busy period access. - - def get_freebusy(self, user, name=None, mutable=False): - - "Get free/busy details for the given 'user'." - - pass - - def get_freebusy_for_other(self, user, other, mutable=False): - - "For the given 'user', get free/busy details for the 'other' user." - - pass - - def get_freebusy_for_update(self, user, name=None): - - "Get free/busy details for the given 'user'." - - return self.get_freebusy(user, name, True) - - def get_freebusy_for_other_for_update(self, user, other): - - "For the given 'user', get free/busy details for the 'other' user." - - return self.get_freebusy_for_other(user, other, True) +stores = { + "file" : file, + } +stores.update(database_stores) - def set_freebusy(self, user, freebusy, name=None): - - "For the given 'user', set 'freebusy' details." - - pass - - def set_freebusy_for_other(self, user, freebusy, other): - - "For the given 'user', set 'freebusy' details for the 'other' user." - - pass - - # Tentative free/busy periods related to countering. - - def get_freebusy_offers(self, user, mutable=False): - - "Get free/busy offers for the given 'user'." - - pass - - def get_freebusy_offers_for_update(self, user): - - "Get free/busy offers for the given 'user'." - - return self.get_freebusy_offers(user, True) - - def set_freebusy_offers(self, user, freebusy): - - "For the given 'user', set 'freebusy' offers." - - return self.set_freebusy(user, freebusy, "freebusy-offers") - - # Requests and counter-proposals. - - def get_requests(self, user): - - "Get requests for the given 'user'." - - pass - - def set_requests(self, user, requests): - - "For the given 'user', set the list of queued 'requests'." - - pass - - def set_request(self, user, uid, recurrenceid=None, type=None): - - """ - For the given 'user', set the queued 'uid' and 'recurrenceid', - indicating a request, along with any given 'type'. - """ - - pass - - def queue_request(self, user, uid, recurrenceid=None, type=None): - - """ - Queue a request for 'user' having the given 'uid'. If the optional - 'recurrenceid' is specified, the entry refers to a specific instance - or occurrence of an event. The 'type' parameter can be used to indicate - a specific type of request. - """ - - requests = self.get_requests(user) or [] - - if not self.have_request(requests, uid, recurrenceid): - return self.set_request(user, uid, recurrenceid, type) - - return False - - def dequeue_request(self, user, uid, recurrenceid=None): +# Access functions. - """ - Dequeue all requests for 'user' having the given 'uid'. If the optional - 'recurrenceid' is specified, all requests for that specific instance or - occurrence of an event are dequeued. - """ - - requests = self.get_requests(user) or [] - result = [] - - for request in requests: - if request[:2] != (uid, recurrenceid): - result.append(request) - - self.set_requests(user, result) - return True - - def has_request(self, user, uid, recurrenceid=None, type=None, strict=False): - return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict) - - def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False): - - """ - Return whether 'requests' contains a request with the given 'uid' and - any specified 'recurrenceid' and 'type'. If 'strict' is set to a true - value, the precise type of the request must match; otherwise, any type - of request for the identified object may be matched. - """ - - for request in requests: - if request[:2] == (uid, recurrenceid) and ( - not strict or - not request[2:] and not type or - request[2:] and request[2] == type): - - return True - - return False - - def get_counters(self, user, uid, recurrenceid=None): - - """ - For the given 'user', return a list of users from whom counter-proposals - have been received for the given 'uid' and optional 'recurrenceid'. - """ - - pass - - def get_counter(self, user, other, uid, recurrenceid=None): - - """ - For the given 'user', return the counter-proposal from 'other' for the - given 'uid' and optional 'recurrenceid'. - """ - - pass - - def set_counter(self, user, other, node, uid, recurrenceid=None): - - """ - For the given 'user', store a counter-proposal received from 'other' the - given 'node' representing that proposal for the given 'uid' and - 'recurrenceid'. - """ - - pass - - def remove_counters(self, user, uid, recurrenceid=None): - - """ - For the given 'user', remove all counter-proposals associated with the - given 'uid' and 'recurrenceid'. - """ - - pass +def get_store(store_type, store_dir): + return stores[store_type].Store(store_dir) - def remove_counter(self, user, other, uid, recurrenceid=None): - - """ - For the given 'user', remove any counter-proposal from 'other' - associated with the given 'uid' and 'recurrenceid'. - """ - - pass - - # Event cancellation. - - def cancel_event(self, user, uid, recurrenceid=None): - - """ - Cancel an event for 'user' having the given 'uid'. If the optional - 'recurrenceid' is specified, a specific instance or occurrence of an - event is cancelled. - """ - - pass - - def uncancel_event(self, user, uid, recurrenceid=None): - - """ - Uncancel an event for 'user' having the given 'uid'. If the optional - 'recurrenceid' is specified, a specific instance or occurrence of an - event is uncancelled. - """ - - pass - - def remove_cancellations(self, user, uid, recurrenceid=None): - - """ - Remove cancellations for 'user' for any event having the given 'uid'. If - the optional 'recurrenceid' is specified, a specific instance or - occurrence of an event is affected. - """ - - # Remove all recurrence cancellations if a general event is indicated. - - if not recurrenceid: - for _recurrenceid in self.get_cancelled_recurrences(user, uid): - self.remove_cancellation(user, uid, _recurrenceid) - - return self.remove_cancellation(user, uid, recurrenceid) - - def remove_cancellation(self, user, uid, recurrenceid=None): - - """ - Remove a cancellation for 'user' for the event having the given 'uid'. - If the optional 'recurrenceid' is specified, a specific instance or - occurrence of an event is affected. - """ - - pass - -class PublisherBase: - - "The core operations of a data publisher." - - def set_freebusy(self, user, freebusy): - - "For the given 'user', set 'freebusy' details." - - pass - -class JournalBase: - - "The core operations of a journal system supporting quotas." - - # Quota and user identity/group discovery. - - def get_quotas(self): +def get_publisher(publishing_dir): + return file.Publisher(publishing_dir) - "Return a list of quotas." - - pass - - def get_quota_users(self, quota): - - "Return a list of quota users." - - pass - - # Groups of users sharing quotas. - - def get_groups(self, quota): - - "Return the identity mappings for the given 'quota' as a dictionary." - - pass - - def get_limits(self, quota): - - """ - Return the limits for the 'quota' as a dictionary mapping identities or - groups to durations. - """ - - pass - - # Free/busy period access for users within quota groups. - - def get_freebusy(self, quota, user, mutable=False): - - "Get free/busy details for the given 'quota' and 'user'." - - pass - - def get_freebusy_for_update(self, quota, user): - - "Get free/busy details for the given 'quota' and 'user'." - - return self.get_freebusy(quota, user, True) - - def set_freebusy(self, quota, user, freebusy): - - "For the given 'quota' and 'user', set 'freebusy' details." - - pass - - # Journal entry methods. - - def get_entries(self, quota, group, mutable=False): - - """ - Return a list of journal entries for the given 'quota' for the indicated - 'group'. - """ - - pass - - def get_entries_for_update(self, quota, group): - - """ - Return a list of journal entries for the given 'quota' for the indicated - 'group'. - """ - - return self.get_entries(quota, group, True) - - def set_entries(self, quota, group, entries): - - """ - For the given 'quota' and indicated 'group', set the list of journal - 'entries'. - """ - - pass +def get_journal(store_type, journal_dir): + return stores[store_type].Journal(journal_dir) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/stores/common.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imiptools/stores/common.py Thu Mar 10 01:43:31 2016 +0100 @@ -0,0 +1,619 @@ +#!/usr/bin/env python + +""" +General support for calendar data storage. + +Copyright (C) 2014, 2015, 2016 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from imiptools.dates import format_datetime + +class StoreBase: + + "The core operations of a data store." + + def acquire_lock(self, user, timeout=None): + pass + + def release_lock(self, user): + pass + + # User discovery. + + def get_users(self): + + "Return a list of users." + + pass + + # Event and event metadata access. + + def get_events(self, user): + + "Return a list of event identifiers." + + pass + + def get_all_events(self, user): + + "Return a set of (uid, recurrenceid) tuples for all events." + + uids = self.get_events(user) + if not uids: + return set() + + all_events = set() + for uid in uids: + all_events.add((uid, None)) + all_events.update([(uid, recurrenceid) for recurrenceid in self.get_recurrences(user, uid)]) + + return all_events + + def get_event(self, user, uid, recurrenceid=None, dirname=None): + + """ + Get the event for the given 'user' with the given 'uid'. If + the optional 'recurrenceid' is specified, a specific instance or + occurrence of an event is returned. + """ + + pass + + def get_complete_event(self, user, uid): + + "Get the event for the given 'user' with the given 'uid'." + + pass + + def set_event(self, user, uid, recurrenceid, node): + + """ + Set an event for 'user' having the given 'uid' and 'recurrenceid' (which + if the latter is specified, a specific instance or occurrence of an + event is referenced), using the given 'node' description. + """ + + if recurrenceid: + return self.set_recurrence(user, uid, recurrenceid, node) + else: + return self.set_complete_event(user, uid, node) + + def set_complete_event(self, user, uid, node): + + "Set an event for 'user' having the given 'uid' and 'node'." + + pass + + def remove_event(self, user, uid, recurrenceid=None): + + """ + Remove an event for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, a specific instance or occurrence of an + event is removed. + """ + + if recurrenceid: + return self.remove_recurrence(user, uid, recurrenceid) + else: + for recurrenceid in self.get_recurrences(user, uid) or []: + self.remove_recurrence(user, uid, recurrenceid) + return self.remove_complete_event(user, uid) + + def remove_complete_event(self, user, uid): + + "Remove an event for 'user' having the given 'uid'." + + self.remove_recurrences(user, uid) + return self.remove_parent_event(user, uid) + + def remove_parent_event(self, user, uid): + + "Remove the parent event for 'user' having the given 'uid'." + + pass + + def get_recurrences(self, user, uid): + + """ + Get additional event instances for an event of the given 'user' with the + indicated 'uid'. Both active and cancelled recurrences are returned. + """ + + return self.get_active_recurrences(user, uid) + self.get_cancelled_recurrences(user, uid) + + def get_active_recurrences(self, user, uid): + + """ + Get additional event instances for an event of the given 'user' with the + indicated 'uid'. Cancelled recurrences are not returned. + """ + + pass + + def get_cancelled_recurrences(self, user, uid): + + """ + Get additional event instances for an event of the given 'user' with the + indicated 'uid'. Only cancelled recurrences are returned. + """ + + pass + + def get_recurrence(self, user, uid, recurrenceid): + + """ + For the event of the given 'user' with the given 'uid', return the + specific recurrence indicated by the 'recurrenceid'. + """ + + pass + + def set_recurrence(self, user, uid, recurrenceid, node): + + "Set an event for 'user' having the given 'uid' and 'node'." + + pass + + def remove_recurrence(self, user, uid, recurrenceid): + + """ + Remove a special recurrence from an event stored by 'user' having the + given 'uid' and 'recurrenceid'. + """ + + pass + + def remove_recurrences(self, user, uid): + + """ + Remove all recurrences for an event stored by 'user' having the given + 'uid'. + """ + + for recurrenceid in self.get_recurrences(user, uid): + self.remove_recurrence(user, uid, recurrenceid) + + return self.remove_recurrence_collection(user, uid) + + def remove_recurrence_collection(self, user, uid): + + """ + Remove the collection of recurrences stored by 'user' having the given + 'uid'. + """ + + pass + + # Free/busy period providers, upon extension of the free/busy records. + + def _get_freebusy_providers(self, user): + + """ + Return the free/busy providers for the given 'user'. + + This function returns any stored datetime and a list of providers as a + 2-tuple. Each provider is itself a (uid, recurrenceid) tuple. + """ + + pass + + def get_freebusy_providers(self, user, dt=None): + + """ + Return a set of uncancelled events of the form (uid, recurrenceid) + providing free/busy details beyond the given datetime 'dt'. + + If 'dt' is not specified, all events previously found to provide + details will be returned. Otherwise, if 'dt' is earlier than the + datetime recorded for the known providers, None is returned, indicating + that the list of providers must be recomputed. + + This function returns a list of (uid, recurrenceid) tuples upon success. + """ + + t = self._get_freebusy_providers(user) + if not t: + return None + + dt_string, t = t + + # If the requested datetime is earlier than the stated datetime, the + # providers will need to be recomputed. + + if dt: + providers_dt = get_datetime(dt_string) + if not providers_dt or providers_dt > dt: + return None + + # Otherwise, return the providers. + + return t[1:] + + def _set_freebusy_providers(self, user, dt_string, t): + + "Set the given provider timestamp 'dt_string' and table 't'." + + pass + + def set_freebusy_providers(self, user, dt, providers): + + """ + Define the uncancelled events providing free/busy details beyond the + given datetime 'dt'. + """ + + t = [] + + for obj in providers: + t.append((obj.get_uid(), obj.get_recurrenceid())) + + return self._set_freebusy_providers(user, format_datetime(dt), t) + + def append_freebusy_provider(self, user, provider): + + "For the given 'user', append the free/busy 'provider'." + + t = self._get_freebusy_providers(user) + if not t: + return False + + dt_string, t = t + t.append((provider.get_uid(), provider.get_recurrenceid())) + + return self._set_freebusy_providers(user, dt_string, t) + + def remove_freebusy_provider(self, user, provider): + + "For the given 'user', remove the free/busy 'provider'." + + t = self._get_freebusy_providers(user) + if not t: + return False + + dt_string, t = t + try: + t.remove((provider.get_uid(), provider.get_recurrenceid())) + except ValueError: + return False + + return self._set_freebusy_providers(user, dt_string, t) + + # Free/busy period access. + + def get_freebusy(self, user, name=None, mutable=False): + + "Get free/busy details for the given 'user'." + + pass + + def get_freebusy_for_other(self, user, other, mutable=False): + + "For the given 'user', get free/busy details for the 'other' user." + + pass + + def get_freebusy_for_update(self, user, name=None): + + "Get free/busy details for the given 'user'." + + return self.get_freebusy(user, name, True) + + def get_freebusy_for_other_for_update(self, user, other): + + "For the given 'user', get free/busy details for the 'other' user." + + return self.get_freebusy_for_other(user, other, True) + + def set_freebusy(self, user, freebusy, name=None): + + "For the given 'user', set 'freebusy' details." + + pass + + def set_freebusy_for_other(self, user, freebusy, other): + + "For the given 'user', set 'freebusy' details for the 'other' user." + + pass + + # Tentative free/busy periods related to countering. + + def get_freebusy_offers(self, user, mutable=False): + + "Get free/busy offers for the given 'user'." + + pass + + def get_freebusy_offers_for_update(self, user): + + "Get free/busy offers for the given 'user'." + + return self.get_freebusy_offers(user, True) + + def set_freebusy_offers(self, user, freebusy): + + "For the given 'user', set 'freebusy' offers." + + return self.set_freebusy(user, freebusy, "freebusy-offers") + + # Requests and counter-proposals. + + def get_requests(self, user): + + "Get requests for the given 'user'." + + pass + + def set_requests(self, user, requests): + + "For the given 'user', set the list of queued 'requests'." + + pass + + def set_request(self, user, uid, recurrenceid=None, type=None): + + """ + For the given 'user', set the queued 'uid' and 'recurrenceid', + indicating a request, along with any given 'type'. + """ + + pass + + def queue_request(self, user, uid, recurrenceid=None, type=None): + + """ + Queue a request for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, the entry refers to a specific instance + or occurrence of an event. The 'type' parameter can be used to indicate + a specific type of request. + """ + + requests = self.get_requests(user) or [] + + if not self.have_request(requests, uid, recurrenceid): + return self.set_request(user, uid, recurrenceid, type) + + return False + + def dequeue_request(self, user, uid, recurrenceid=None): + + """ + Dequeue all requests for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, all requests for that specific instance or + occurrence of an event are dequeued. + """ + + requests = self.get_requests(user) or [] + result = [] + + for request in requests: + if request[:2] != (uid, recurrenceid): + result.append(request) + + self.set_requests(user, result) + return True + + def has_request(self, user, uid, recurrenceid=None, type=None, strict=False): + return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict) + + def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False): + + """ + Return whether 'requests' contains a request with the given 'uid' and + any specified 'recurrenceid' and 'type'. If 'strict' is set to a true + value, the precise type of the request must match; otherwise, any type + of request for the identified object may be matched. + """ + + for request in requests: + if request[:2] == (uid, recurrenceid) and ( + not strict or + not request[2:] and not type or + request[2:] and request[2] == type): + + return True + + return False + + def get_counters(self, user, uid, recurrenceid=None): + + """ + For the given 'user', return a list of users from whom counter-proposals + have been received for the given 'uid' and optional 'recurrenceid'. + """ + + pass + + def get_counter(self, user, other, uid, recurrenceid=None): + + """ + For the given 'user', return the counter-proposal from 'other' for the + given 'uid' and optional 'recurrenceid'. + """ + + pass + + def set_counter(self, user, other, node, uid, recurrenceid=None): + + """ + For the given 'user', store a counter-proposal received from 'other' the + given 'node' representing that proposal for the given 'uid' and + 'recurrenceid'. + """ + + pass + + def remove_counters(self, user, uid, recurrenceid=None): + + """ + For the given 'user', remove all counter-proposals associated with the + given 'uid' and 'recurrenceid'. + """ + + pass + + def remove_counter(self, user, other, uid, recurrenceid=None): + + """ + For the given 'user', remove any counter-proposal from 'other' + associated with the given 'uid' and 'recurrenceid'. + """ + + pass + + # Event cancellation. + + def cancel_event(self, user, uid, recurrenceid=None): + + """ + Cancel an event for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, a specific instance or occurrence of an + event is cancelled. + """ + + pass + + def uncancel_event(self, user, uid, recurrenceid=None): + + """ + Uncancel an event for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, a specific instance or occurrence of an + event is uncancelled. + """ + + pass + + def remove_cancellations(self, user, uid, recurrenceid=None): + + """ + Remove cancellations for 'user' for any event having the given 'uid'. If + the optional 'recurrenceid' is specified, a specific instance or + occurrence of an event is affected. + """ + + # Remove all recurrence cancellations if a general event is indicated. + + if not recurrenceid: + for _recurrenceid in self.get_cancelled_recurrences(user, uid): + self.remove_cancellation(user, uid, _recurrenceid) + + return self.remove_cancellation(user, uid, recurrenceid) + + def remove_cancellation(self, user, uid, recurrenceid=None): + + """ + Remove a cancellation for 'user' for the event having the given 'uid'. + If the optional 'recurrenceid' is specified, a specific instance or + occurrence of an event is affected. + """ + + pass + +class PublisherBase: + + "The core operations of a data publisher." + + def set_freebusy(self, user, freebusy): + + "For the given 'user', set 'freebusy' details." + + pass + +class JournalBase: + + "The core operations of a journal system supporting quotas." + + # Quota and user identity/group discovery. + + def get_quotas(self): + + "Return a list of quotas." + + pass + + def get_quota_users(self, quota): + + "Return a list of quota users." + + pass + + # Groups of users sharing quotas. + + def get_groups(self, quota): + + "Return the identity mappings for the given 'quota' as a dictionary." + + pass + + def get_limits(self, quota): + + """ + Return the limits for the 'quota' as a dictionary mapping identities or + groups to durations. + """ + + pass + + # Free/busy period access for users within quota groups. + + def get_freebusy(self, quota, user, mutable=False): + + "Get free/busy details for the given 'quota' and 'user'." + + pass + + def get_freebusy_for_update(self, quota, user): + + "Get free/busy details for the given 'quota' and 'user'." + + return self.get_freebusy(quota, user, True) + + def set_freebusy(self, quota, user, freebusy): + + "For the given 'quota' and 'user', set 'freebusy' details." + + pass + + # Journal entry methods. + + def get_entries(self, quota, group, mutable=False): + + """ + Return a list of journal entries for the given 'quota' for the indicated + 'group'. + """ + + pass + + def get_entries_for_update(self, quota, group): + + """ + Return a list of journal entries for the given 'quota' for the indicated + 'group'. + """ + + return self.get_entries(quota, group, True) + + def set_entries(self, quota, group, entries): + + """ + For the given 'quota' and indicated 'group', set the list of journal + 'entries'. + """ + + pass + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/stores/database.py --- a/imiptools/stores/database.py Wed Mar 09 21:38:56 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,845 +0,0 @@ -#!/usr/bin/env python - -""" -A database store of calendar data. - -Copyright (C) 2014, 2015, 2016 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from imiptools.stores import StoreBase, JournalBase - -from datetime import datetime -from imiptools.data import parse_string, to_string -from imiptools.dates import format_datetime, get_datetime, to_timezone -from imiptools.period import FreeBusyPeriod, FreeBusyDatabaseCollection -from imiptools.sql import DatabaseOperations - -class DatabaseStoreBase: - - "A database store supporting user-specific locking." - - def __init__(self, connection, paramstyle=None): - DatabaseOperations.__init__(self, paramstyle=paramstyle) - self.connection = connection - self.cursor = connection.cursor() - - def acquire_lock(self, user, timeout=None): - pass - - def release_lock(self, user): - pass - -class DatabaseStore(DatabaseStoreBase, StoreBase, DatabaseOperations): - - "A database store of tabular free/busy data and objects." - - # User discovery. - - def get_users(self): - - "Return a list of users." - - query = "select distinct store_user from freebusy" - self.cursor.execute(query) - return [r[0] for r in self.cursor.fetchall()] - - # Event and event metadata access. - - def get_events(self, user): - - "Return a list of event identifiers." - - columns = ["store_user", "status"] - values = [user, "active"] - - query, values = self.get_query( - "select object_uid from objects :condition", - columns, values) - - self.cursor.execute(query, values) - return [r[0] for r in self.cursor.fetchall()] - - def get_all_events(self, user): - - "Return a set of (uid, recurrenceid) tuples for all events." - - query, values = self.get_query( - "select object_uid, null as object_recurrenceid from objects :condition " - "union all " - "select object_uid, object_recurrenceid from recurrences :condition", - ["store_user"], [user]) - - self.cursor.execute(query, values) - return self.cursor.fetchall() - - def get_event_table(self, recurrenceid=None, dirname=None): - - "Get the table providing events for any specified 'dirname'." - - if recurrenceid: - return self.get_recurrence_table(dirname) - else: - return self.get_complete_event_table(dirname) - - def get_event_table_filters(self, dirname=None): - - "Get filter details for any specified 'dirname'." - - if dirname == "cancellations": - return ["status"], ["cancelled"] - else: - return [], [] - - def get_event(self, user, uid, recurrenceid=None, dirname=None): - - """ - Get the event for the given 'user' with the given 'uid'. If - the optional 'recurrenceid' is specified, a specific instance or - occurrence of an event is returned. - """ - - table = self.get_event_table(recurrenceid, dirname) - columns, values = self.get_event_table_filters(dirname) - - if recurrenceid: - columns += ["store_user", "object_uid", "object_recurrenceid"] - values += [user, uid, recurrenceid] - else: - columns += ["store_user", "object_uid"] - values += [user, uid] - - query, values = self.get_query( - "select object_text from %(table)s :condition" % { - "table" : table - }, - columns, values) - - self.cursor.execute(query, values) - result = self.cursor.fetchone() - return result and parse_string(result[0], "utf-8") - - def get_complete_event_table(self, dirname=None): - - "Get the table providing events for any specified 'dirname'." - - if dirname == "counters": - return "countered_objects" - else: - return "objects" - - def get_complete_event(self, user, uid): - - "Get the event for the given 'user' with the given 'uid'." - - columns = ["store_user", "object_uid"] - values = [user, uid] - - query, values = self.get_query( - "select object_text from objects :condition", - columns, values) - - self.cursor.execute(query, values) - result = self.cursor.fetchone() - return result and parse_string(result[0], "utf-8") - - def set_complete_event(self, user, uid, node): - - "Set an event for 'user' having the given 'uid' and 'node'." - - columns = ["store_user", "object_uid"] - values = [user, uid] - setcolumns = ["object_text", "status"] - setvalues = [to_string(node, "utf-8"), "active"] - - query, values = self.get_query( - "update objects :set :condition", - columns, values, setcolumns, setvalues) - - self.cursor.execute(query, values) - - if self.cursor.rowcount > 0 or self.get_complete_event(user, uid): - return True - - columns = ["store_user", "object_uid", "object_text", "status"] - values = [user, uid, to_string(node, "utf-8"), "active"] - - query, values = self.get_query( - "insert into objects (:columns) values (:values)", - columns, values) - - self.cursor.execute(query, values) - return True - - def remove_parent_event(self, user, uid): - - "Remove the parent event for 'user' having the given 'uid'." - - columns = ["store_user", "object_uid"] - values = [user, uid] - - query, values = self.get_query( - "delete from objects :condition", - columns, values) - - self.cursor.execute(query, values) - return self.cursor.rowcount > 0 - - def get_active_recurrences(self, user, uid): - - """ - Get additional event instances for an event of the given 'user' with the - indicated 'uid'. Cancelled recurrences are not returned. - """ - - columns = ["store_user", "object_uid", "status"] - values = [user, uid, "active"] - - query, values = self.get_query( - "select object_recurrenceid from recurrences :condition", - columns, values) - - self.cursor.execute(query, values) - return [t[0] for t in self.cursor.fetchall() or []] - - def get_cancelled_recurrences(self, user, uid): - - """ - Get additional event instances for an event of the given 'user' with the - indicated 'uid'. Only cancelled recurrences are returned. - """ - - columns = ["store_user", "object_uid", "status"] - values = [user, uid, "cancelled"] - - query, values = self.get_query( - "select object_recurrenceid from recurrences :condition", - columns, values) - - self.cursor.execute(query, values) - return [t[0] for t in self.cursor.fetchall() or []] - - def get_recurrence_table(self, dirname=None): - - "Get the table providing recurrences for any specified 'dirname'." - - if dirname == "counters": - return "countered_recurrences" - else: - return "recurrences" - - def get_recurrence(self, user, uid, recurrenceid): - - """ - For the event of the given 'user' with the given 'uid', return the - specific recurrence indicated by the 'recurrenceid'. - """ - - columns = ["store_user", "object_uid", "object_recurrenceid"] - values = [user, uid, recurrenceid] - - query, values = self.get_query( - "select object_text from recurrences :condition", - columns, values) - - self.cursor.execute(query, values) - result = self.cursor.fetchone() - return result and parse_string(result[0], "utf-8") - - def set_recurrence(self, user, uid, recurrenceid, node): - - "Set an event for 'user' having the given 'uid' and 'node'." - - columns = ["store_user", "object_uid", "object_recurrenceid"] - values = [user, uid, recurrenceid] - setcolumns = ["object_text", "status"] - setvalues = [to_string(node, "utf-8"), "active"] - - query, values = self.get_query( - "update recurrences :set :condition", - columns, values, setcolumns, setvalues) - - self.cursor.execute(query, values) - - if self.cursor.rowcount > 0 or self.get_recurrence(user, uid, recurrenceid): - return True - - columns = ["store_user", "object_uid", "object_recurrenceid", "object_text", "status"] - values = [user, uid, recurrenceid, to_string(node, "utf-8"), "active"] - - query, values = self.get_query( - "insert into recurrences (:columns) values (:values)", - columns, values) - - self.cursor.execute(query, values) - return True - - def remove_recurrence(self, user, uid, recurrenceid): - - """ - Remove a special recurrence from an event stored by 'user' having the - given 'uid' and 'recurrenceid'. - """ - - columns = ["store_user", "object_uid", "object_recurrenceid"] - values = [user, uid, recurrenceid] - - query, values = self.get_query( - "delete from recurrences :condition", - columns, values) - - self.cursor.execute(query, values) - return True - - def remove_recurrences(self, user, uid): - - """ - Remove all recurrences for an event stored by 'user' having the given - 'uid'. - """ - - columns = ["store_user", "object_uid"] - values = [user, uid] - - query, values = self.get_query( - "delete from recurrences :condition", - columns, values) - - self.cursor.execute(query, values) - return True - - # Free/busy period providers, upon extension of the free/busy records. - - def _get_freebusy_providers(self, user): - - """ - Return the free/busy providers for the given 'user'. - - This function returns any stored datetime and a list of providers as a - 2-tuple. Each provider is itself a (uid, recurrenceid) tuple. - """ - - columns = ["store_user"] - values = [user] - - query, values = self.get_query( - "select object_uid, object_recurrenceid from freebusy_providers :condition", - columns, values) - - self.cursor.execute(query, values) - providers = self.cursor.fetchall() - - columns = ["store_user"] - values = [user] - - query, values = self.get_query( - "select start from freebusy_provider_datetimes :condition", - columns, values) - - self.cursor.execute(query, values) - result = self.cursor.fetchone() - dt_string = result and result[0] - - return dt_string, providers - - def _set_freebusy_providers(self, user, dt_string, t): - - "Set the given provider timestamp 'dt_string' and table 't'." - - # NOTE: Locking? - - columns = ["store_user"] - values = [user] - - query, values = self.get_query( - "delete from freebusy_providers :condition", - columns, values) - - self.cursor.execute(query, values) - - columns = ["store_user", "object_uid", "object_recurrenceid"] - - for uid, recurrenceid in t: - values = [user, uid, recurrenceid] - - query, values = self.get_query( - "insert into freebusy_providers (:columns) values (:values)", - columns, values) - - self.cursor.execute(query, values) - - columns = ["store_user"] - values = [user] - setcolumns = ["start"] - setvalues = [dt_string] - - query, values = self.get_query( - "update freebusy_provider_datetimes :set :condition", - columns, values, setcolumns, setvalues) - - self.cursor.execute(query, values) - - if self.cursor.rowcount > 0: - return True - - columns = ["store_user", "start"] - values = [user, dt_string] - - query, values = self.get_query( - "insert into freebusy_provider_datetimes (:columns) values (:values)", - columns, values) - - self.cursor.execute(query, values) - return True - - # Free/busy period access. - - def get_freebusy(self, user, name=None, mutable=False): - - "Get free/busy details for the given 'user'." - - table = name or "freebusy" - return FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], mutable, self.paramstyle) - - def get_freebusy_for_other(self, user, other, mutable=False): - - "For the given 'user', get free/busy details for the 'other' user." - - table = "freebusy" - return FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], mutable, self.paramstyle) - - def set_freebusy(self, user, freebusy, name=None): - - "For the given 'user', set 'freebusy' details." - - table = name or "freebusy" - - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table: - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], True, self.paramstyle) - fbc += freebusy - - return True - - def set_freebusy_for_other(self, user, freebusy, other): - - "For the given 'user', set 'freebusy' details for the 'other' user." - - table = "freebusy" - - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table: - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], True, self.paramstyle) - fbc += freebusy - - return True - - # Tentative free/busy periods related to countering. - - def get_freebusy_offers(self, user, mutable=False): - - "Get free/busy offers for the given 'user'." - - # Expire old offers and save the collection if modified. - - now = format_datetime(to_timezone(datetime.utcnow(), "UTC")) - columns = ["store_user", "expires"] - values = [user, now] - - query, values = self.get_query( - "delete from freebusy_offers :condition", - columns, values) - - self.cursor.execute(query, values) - - return self.get_freebusy(user, "freebusy_offers", mutable) - - # Requests and counter-proposals. - - def get_requests(self, user): - - "Get requests for the given 'user'." - - columns = ["store_user"] - values = [user] - - query, values = self.get_query( - "select object_uid, object_recurrenceid from requests :condition", - columns, values) - - self.cursor.execute(query, values) - return self.cursor.fetchall() - - def set_requests(self, user, requests): - - "For the given 'user', set the list of queued 'requests'." - - # NOTE: Locking? - - columns = ["store_user"] - values = [user] - - query, values = self.get_query( - "delete from requests :condition", - columns, values) - - self.cursor.execute(query, values) - - for uid, recurrenceid, type in requests: - columns = ["store_user", "object_uid", "object_recurrenceid", "request_type"] - values = [user, uid, recurrenceid, type] - - query, values = self.get_query( - "insert into requests (:columns) values (:values)", - columns, values) - - self.cursor.execute(query, values) - - return True - - def set_request(self, user, uid, recurrenceid=None, type=None): - - """ - For the given 'user', set the queued 'uid' and 'recurrenceid', - indicating a request, along with any given 'type'. - """ - - columns = ["store_user", "object_uid", "object_recurrenceid", "request_type"] - values = [user, uid, recurrenceid, type] - - query, values = self.get_query( - "insert into requests (:columns) values (:values)", - columns, values) - - self.cursor.execute(query, values) - return True - - def get_counters(self, user, uid, recurrenceid=None): - - """ - For the given 'user', return a list of users from whom counter-proposals - have been received for the given 'uid' and optional 'recurrenceid'. - """ - - table = self.get_event_table(recurrenceid, "counters") - - if recurrenceid: - columns = ["store_user", "object_uid", "object_recurrenceid"] - values = [user, uid, recurrenceid] - else: - columns = ["store_user", "object_uid"] - values = [user, uid] - - query, values = self.get_query( - "select other from %(table)s :condition" % { - "table" : table - }, - columns, values) - - self.cursor.execute(query, values) - return self.cursor.fetchall() - - def get_counter(self, user, other, uid, recurrenceid=None): - - """ - For the given 'user', return the counter-proposal from 'other' for the - given 'uid' and optional 'recurrenceid'. - """ - - table = self.get_event_table(recurrenceid, "counters") - - if recurrenceid: - columns = ["store_user", "other", "object_uid", "object_recurrenceid"] - values = [user, other, uid, recurrenceid] - else: - columns = ["store_user", "other", "object_uid"] - values = [user, other, uid] - - query, values = self.get_query( - "select object_text from %(table)s :condition" % { - "table" : table - }, - columns, values) - - self.cursor.execute(query, values) - result = self.cursor.fetchall() - return result and parse_string(result[0], "utf-8") - - def set_counter(self, user, other, node, uid, recurrenceid=None): - - """ - For the given 'user', store a counter-proposal received from 'other' the - given 'node' representing that proposal for the given 'uid' and - 'recurrenceid'. - """ - - table = self.get_event_table(recurrenceid, "counters") - - columns = ["store_user", "other", "object_uid", "object_recurrenceid", "object_text"] - values = [user, other, uid, recurrenceid, to_string(node, "utf-8")] - - query, values = self.get_query( - "insert into %(table)s (:columns) values (:values)" % { - "table" : table - }, - columns, values) - - self.cursor.execute(query, values) - return True - - def remove_counters(self, user, uid, recurrenceid=None): - - """ - For the given 'user', remove all counter-proposals associated with the - given 'uid' and 'recurrenceid'. - """ - - table = self.get_event_table(recurrenceid, "counters") - - if recurrenceid: - columns = ["store_user", "object_uid", "object_recurrenceid"] - values = [user, uid, recurrenceid] - else: - columns = ["store_user", "object_uid"] - values = [user, uid] - - query, values = self.get_query( - "delete from %(table)s :condition" % { - "table" : table - }, - columns, values) - - self.cursor.execute(query, values) - return True - - def remove_counter(self, user, other, uid, recurrenceid=None): - - """ - For the given 'user', remove any counter-proposal from 'other' - associated with the given 'uid' and 'recurrenceid'. - """ - - table = self.get_event_table(recurrenceid, "counters") - - if recurrenceid: - columns = ["store_user", "other", "object_uid", "object_recurrenceid"] - values = [user, other, uid, recurrenceid] - else: - columns = ["store_user", "other", "object_uid"] - values = [user, other, uid] - - query, values = self.get_query( - "delete from %(table)s :condition" % { - "table" : table - }, - columns, values) - - self.cursor.execute(query, values) - return True - - # Event cancellation. - - def cancel_event(self, user, uid, recurrenceid=None): - - """ - Cancel an event for 'user' having the given 'uid'. If the optional - 'recurrenceid' is specified, a specific instance or occurrence of an - event is cancelled. - """ - - table = self.get_event_table(recurrenceid) - - if recurrenceid: - columns = ["store_user", "object_uid", "object_recurrenceid"] - values = [user, uid, recurrenceid] - else: - columns = ["store_user", "object_uid"] - values = [user, uid] - - setcolumns = ["status"] - setvalues = ["cancelled"] - - query, values = self.get_query( - "update %(table)s :set :condition" % { - "table" : table - }, - columns, values, setcolumns, setvalues) - - self.cursor.execute(query, values) - return True - - def uncancel_event(self, user, uid, recurrenceid=None): - - """ - Uncancel an event for 'user' having the given 'uid'. If the optional - 'recurrenceid' is specified, a specific instance or occurrence of an - event is uncancelled. - """ - - table = self.get_event_table(recurrenceid) - - if recurrenceid: - columns = ["store_user", "object_uid", "object_recurrenceid"] - values = [user, uid, recurrenceid] - else: - columns = ["store_user", "object_uid"] - values = [user, uid] - - setcolumns = ["status"] - setvalues = ["active"] - - query, values = self.get_query( - "update %(table)s :set :condition" % { - "table" : table - }, - columns, values, setcolumns, setvalues) - - self.cursor.execute(query, values) - return True - - def remove_cancellation(self, user, uid, recurrenceid=None): - - """ - Remove a cancellation for 'user' for the event having the given 'uid'. - If the optional 'recurrenceid' is specified, a specific instance or - occurrence of an event is affected. - """ - - table = self.get_event_table(recurrenceid) - - if recurrenceid: - columns = ["store_user", "object_uid", "object_recurrenceid", "status"] - values = [user, uid, recurrenceid, "cancelled"] - else: - columns = ["store_user", "object_uid", "status"] - values = [user, uid, "cancelled"] - - query, values = self.get_query( - "delete from %(table)s :condition" % { - "table" : table - }, - columns, values) - - self.cursor.execute(query, values) - return True - -class DatabaseJournal(DatabaseStoreBase, JournalBase): - - "A journal system to support quotas." - - # Quota and user identity/group discovery. - - def get_quotas(self): - - "Return a list of quotas." - - query = "select distinct journal_quota from quota_freebusy" - self.cursor.execute(query) - return [r[0] for r in self.cursor.fetchall()] - - def get_quota_users(self, quota): - - "Return a list of quota users." - - columns = ["quota"] - values = [quota] - - query, values = self.get_query( - "select distinct user_group from quota_freebusy :condition", - columns, values) - - self.cursor.execute(query) - return [r[0] for r in self.cursor.fetchall()] - - # Groups of users sharing quotas. - - def get_groups(self, quota): - - "Return the identity mappings for the given 'quota' as a dictionary." - - columns = ["quota"] - values = [quota] - - query, values = self.get_query( - "select store_user, user_group from user_groups :condition", - columns, values) - - self.cursor.execute(query) - return dict(self.cursor.fetchall()) - - def get_limits(self, quota): - - """ - Return the limits for the 'quota' as a dictionary mapping identities or - groups to durations. - """ - - columns = ["quota"] - values = [quota] - - query, values = self.get_query( - "select user_group, quota_limit from quota_limits :condition", - columns, values) - - self.cursor.execute(query) - return dict(self.cursor.fetchall()) - - # Free/busy period access for users within quota groups. - - def get_freebusy(self, quota, user, mutable=False): - - "Get free/busy details for the given 'quota' and 'user'." - - table = "user_freebusy" - return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], mutable, self.paramstyle) - - def set_freebusy(self, quota, user, freebusy): - - "For the given 'quota' and 'user', set 'freebusy' details." - - table = "user_freebusy" - - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table: - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], True, self.paramstyle) - fbc += freebusy - - return True - - # Journal entry methods. - - def get_entries(self, quota, group, mutable=False): - - """ - Return a list of journal entries for the given 'quota' for the indicated - 'group'. - """ - - table = "quota_freebusy" - return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], mutable, self.paramstyle) - - def set_entries(self, quota, group, entries): - - """ - For the given 'quota' and indicated 'group', set the list of journal - 'entries'. - """ - - table = "quota_freebusy" - - if not isinstance(entries, FreeBusyDatabaseCollection) or entries.table_name != table: - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], True, self.paramstyle) - fbc += entries - - return True - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/stores/database/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imiptools/stores/database/__init__.py Thu Mar 10 01:43:31 2016 +0100 @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +General support for database stores. + +Copyright (C) 2016 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from imiptools.stores.database import postgresql + +stores = { + "postgresql" : postgresql, + } + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/stores/database/common.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imiptools/stores/database/common.py Thu Mar 10 01:43:31 2016 +0100 @@ -0,0 +1,880 @@ +#!/usr/bin/env python + +""" +A database store of calendar data. + +Copyright (C) 2014, 2015, 2016 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from imiptools.stores.common import StoreBase, JournalBase + +from datetime import datetime +from imiptools.data import parse_string, to_string +from imiptools.dates import format_datetime, get_datetime, to_timezone +from imiptools.period import FreeBusyDatabaseCollection +from imiptools.sql import DatabaseOperations + +class DatabaseStoreBase(DatabaseOperations): + + "A database store supporting user-specific locking." + + def __init__(self, connection, paramstyle=None): + DatabaseOperations.__init__(self, paramstyle=paramstyle) + self.connection = connection + self.cursor = connection.cursor() + + def acquire_lock(self, user, timeout=None): + pass + + def release_lock(self, user): + pass + +class DatabaseStore(DatabaseStoreBase, StoreBase): + + "A database store of tabular free/busy data and objects." + + # User discovery. + + def get_users(self): + + "Return a list of users." + + query = "select distinct store_user from freebusy" + self.cursor.execute(query) + return [r[0] for r in self.cursor.fetchall()] + + # Event and event metadata access. + + def get_events(self, user): + + "Return a list of event identifiers." + + columns = ["store_user", "status"] + values = [user, "active"] + + query, values = self.get_query( + "select object_uid from objects :condition", + columns, values) + + self.cursor.execute(query, values) + return [r[0] for r in self.cursor.fetchall()] + + def get_all_events(self, user): + + "Return a set of (uid, recurrenceid) tuples for all events." + + query, values = self.get_query( + "select object_uid, null as object_recurrenceid from objects :condition " + "union all " + "select object_uid, object_recurrenceid from recurrences :condition", + ["store_user"], [user]) + + self.cursor.execute(query, values) + return self.cursor.fetchall() + + def get_event_table(self, recurrenceid=None, dirname=None): + + "Get the table providing events for any specified 'dirname'." + + if recurrenceid: + return self.get_recurrence_table(dirname) + else: + return self.get_complete_event_table(dirname) + + def get_event_table_filters(self, dirname=None): + + "Get filter details for any specified 'dirname'." + + if dirname == "cancellations": + return ["status"], ["cancelled"] + else: + return ["status"], ["active"] + + def get_event(self, user, uid, recurrenceid=None, dirname=None): + + """ + Get the event for the given 'user' with the given 'uid'. If + the optional 'recurrenceid' is specified, a specific instance or + occurrence of an event is returned. + """ + + table = self.get_event_table(recurrenceid, dirname) + columns, values = self.get_event_table_filters(dirname) + + if recurrenceid: + columns += ["store_user", "object_uid", "object_recurrenceid"] + values += [user, uid, recurrenceid] + else: + columns += ["store_user", "object_uid"] + values += [user, uid] + + query, values = self.get_query( + "select object_text from %(table)s :condition" % { + "table" : table + }, + columns, values) + + self.cursor.execute(query, values) + result = self.cursor.fetchone() + return result and parse_string(result[0], "utf-8") + + def get_complete_event_table(self, dirname=None): + + "Get the table providing events for any specified 'dirname'." + + if dirname == "counters": + return "countered_objects" + else: + return "objects" + + def get_complete_event(self, user, uid): + + "Get the event for the given 'user' with the given 'uid'." + + columns = ["store_user", "object_uid"] + values = [user, uid] + + query, values = self.get_query( + "select object_text from objects :condition", + columns, values) + + self.cursor.execute(query, values) + result = self.cursor.fetchone() + return result and parse_string(result[0], "utf-8") + + def set_complete_event(self, user, uid, node): + + "Set an event for 'user' having the given 'uid' and 'node'." + + columns = ["store_user", "object_uid"] + values = [user, uid] + setcolumns = ["object_text", "status"] + setvalues = [to_string(node, "utf-8"), "active"] + + query, values = self.get_query( + "update objects :set :condition", + columns, values, setcolumns, setvalues) + + self.cursor.execute(query, values) + + if self.cursor.rowcount > 0 or self.get_complete_event(user, uid): + return True + + columns = ["store_user", "object_uid", "object_text", "status"] + values = [user, uid, to_string(node, "utf-8"), "active"] + + query, values = self.get_query( + "insert into objects (:columns) values (:values)", + columns, values) + + self.cursor.execute(query, values) + return True + + def remove_parent_event(self, user, uid): + + "Remove the parent event for 'user' having the given 'uid'." + + columns = ["store_user", "object_uid"] + values = [user, uid] + + query, values = self.get_query( + "delete from objects :condition", + columns, values) + + self.cursor.execute(query, values) + return self.cursor.rowcount > 0 + + def get_active_recurrences(self, user, uid): + + """ + Get additional event instances for an event of the given 'user' with the + indicated 'uid'. Cancelled recurrences are not returned. + """ + + columns = ["store_user", "object_uid", "status"] + values = [user, uid, "active"] + + query, values = self.get_query( + "select object_recurrenceid from recurrences :condition", + columns, values) + + self.cursor.execute(query, values) + return [t[0] for t in self.cursor.fetchall() or []] + + def get_cancelled_recurrences(self, user, uid): + + """ + Get additional event instances for an event of the given 'user' with the + indicated 'uid'. Only cancelled recurrences are returned. + """ + + columns = ["store_user", "object_uid", "status"] + values = [user, uid, "cancelled"] + + query, values = self.get_query( + "select object_recurrenceid from recurrences :condition", + columns, values) + + self.cursor.execute(query, values) + return [t[0] for t in self.cursor.fetchall() or []] + + def get_recurrence_table(self, dirname=None): + + "Get the table providing recurrences for any specified 'dirname'." + + if dirname == "counters": + return "countered_recurrences" + else: + return "recurrences" + + def get_recurrence(self, user, uid, recurrenceid): + + """ + For the event of the given 'user' with the given 'uid', return the + specific recurrence indicated by the 'recurrenceid'. + """ + + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + + query, values = self.get_query( + "select object_text from recurrences :condition", + columns, values) + + self.cursor.execute(query, values) + result = self.cursor.fetchone() + return result and parse_string(result[0], "utf-8") + + def set_recurrence(self, user, uid, recurrenceid, node): + + "Set an event for 'user' having the given 'uid' and 'node'." + + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + setcolumns = ["object_text", "status"] + setvalues = [to_string(node, "utf-8"), "active"] + + query, values = self.get_query( + "update recurrences :set :condition", + columns, values, setcolumns, setvalues) + + self.cursor.execute(query, values) + + if self.cursor.rowcount > 0 or self.get_recurrence(user, uid, recurrenceid): + return True + + columns = ["store_user", "object_uid", "object_recurrenceid", "object_text", "status"] + values = [user, uid, recurrenceid, to_string(node, "utf-8"), "active"] + + query, values = self.get_query( + "insert into recurrences (:columns) values (:values)", + columns, values) + + self.cursor.execute(query, values) + return True + + def remove_recurrence(self, user, uid, recurrenceid): + + """ + Remove a special recurrence from an event stored by 'user' having the + given 'uid' and 'recurrenceid'. + """ + + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + + query, values = self.get_query( + "delete from recurrences :condition", + columns, values) + + self.cursor.execute(query, values) + return True + + def remove_recurrences(self, user, uid): + + """ + Remove all recurrences for an event stored by 'user' having the given + 'uid'. + """ + + columns = ["store_user", "object_uid"] + values = [user, uid] + + query, values = self.get_query( + "delete from recurrences :condition", + columns, values) + + self.cursor.execute(query, values) + return True + + # Free/busy period providers, upon extension of the free/busy records. + + def _get_freebusy_providers(self, user): + + """ + Return the free/busy providers for the given 'user'. + + This function returns any stored datetime and a list of providers as a + 2-tuple. Each provider is itself a (uid, recurrenceid) tuple. + """ + + columns = ["store_user"] + values = [user] + + query, values = self.get_query( + "select object_uid, object_recurrenceid from freebusy_providers :condition", + columns, values) + + self.cursor.execute(query, values) + providers = self.cursor.fetchall() + + columns = ["store_user"] + values = [user] + + query, values = self.get_query( + "select start from freebusy_provider_datetimes :condition", + columns, values) + + self.cursor.execute(query, values) + result = self.cursor.fetchone() + dt_string = result and result[0] + + return dt_string, providers + + def _set_freebusy_providers(self, user, dt_string, t): + + "Set the given provider timestamp 'dt_string' and table 't'." + + # NOTE: Locking? + + columns = ["store_user"] + values = [user] + + query, values = self.get_query( + "delete from freebusy_providers :condition", + columns, values) + + self.cursor.execute(query, values) + + columns = ["store_user", "object_uid", "object_recurrenceid"] + + for uid, recurrenceid in t: + values = [user, uid, recurrenceid] + + query, values = self.get_query( + "insert into freebusy_providers (:columns) values (:values)", + columns, values) + + self.cursor.execute(query, values) + + columns = ["store_user"] + values = [user] + setcolumns = ["start"] + setvalues = [dt_string] + + query, values = self.get_query( + "update freebusy_provider_datetimes :set :condition", + columns, values, setcolumns, setvalues) + + self.cursor.execute(query, values) + + if self.cursor.rowcount > 0: + return True + + columns = ["store_user", "start"] + values = [user, dt_string] + + query, values = self.get_query( + "insert into freebusy_provider_datetimes (:columns) values (:values)", + columns, values) + + self.cursor.execute(query, values) + return True + + # Free/busy period access. + + def get_freebusy(self, user, name=None, mutable=False): + + "Get free/busy details for the given 'user'." + + table = name or "freebusy" + return FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], mutable, self.paramstyle) + + def get_freebusy_for_other(self, user, other, mutable=False): + + "For the given 'user', get free/busy details for the 'other' user." + + table = "freebusy_other" + return FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], mutable, self.paramstyle) + + def set_freebusy(self, user, freebusy, name=None): + + "For the given 'user', set 'freebusy' details." + + table = name or "freebusy" + + if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table: + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], True, self.paramstyle) + fbc += freebusy + + return True + + def set_freebusy_for_other(self, user, freebusy, other): + + "For the given 'user', set 'freebusy' details for the 'other' user." + + table = "freebusy_other" + + if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table: + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], True, self.paramstyle) + fbc += freebusy + + return True + + # Tentative free/busy periods related to countering. + + def get_freebusy_offers(self, user, mutable=False): + + "Get free/busy offers for the given 'user'." + + # Expire old offers and save the collection if modified. + + now = format_datetime(to_timezone(datetime.utcnow(), "UTC")) + columns = ["store_user", "expires"] + values = [user, now] + + query, values = self.get_query( + "delete from freebusy_offers :condition", + columns, values) + + self.cursor.execute(query, values) + + return self.get_freebusy(user, "freebusy_offers", mutable) + + def set_freebusy_offers(self, user, freebusy): + + "For the given 'user', set 'freebusy' offers." + + return self.set_freebusy(user, freebusy, "freebusy_offers") + + # Requests and counter-proposals. + + def get_requests(self, user): + + "Get requests for the given 'user'." + + columns = ["store_user"] + values = [user] + + query, values = self.get_query( + "select object_uid, object_recurrenceid, request_type from requests :condition", + columns, values) + + self.cursor.execute(query, values) + return self.cursor.fetchall() + + def set_request(self, user, uid, recurrenceid=None, type=None): + + """ + For the given 'user', set the queued 'uid' and 'recurrenceid', + indicating a request, along with any given 'type'. + """ + + columns = ["store_user", "object_uid", "object_recurrenceid", "request_type"] + values = [user, uid, recurrenceid, type] + + query, values = self.get_query( + "insert into requests (:columns) values (:values)", + columns, values) + + self.cursor.execute(query, values) + return True + + def queue_request(self, user, uid, recurrenceid=None, type=None): + + """ + Queue a request for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, the entry refers to a specific instance + or occurrence of an event. The 'type' parameter can be used to indicate + a specific type of request. + """ + + if recurrenceid: + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + else: + columns = ["store_user", "object_uid"] + values = [user, uid] + + setcolumns = ["request_type"] + setvalues = [type] + + query, values = self.get_query( + "update requests :set :condition", + columns, values, setcolumns, setvalues) + + self.cursor.execute(query, values) + + if self.cursor.rowcount > 0: + return + + self.set_request(user, uid, recurrenceid, type) + + def dequeue_request(self, user, uid, recurrenceid=None): + + """ + Dequeue all requests for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, all requests for that specific instance or + occurrence of an event are dequeued. + """ + + if recurrenceid: + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + else: + columns = ["store_user", "object_uid"] + values = [user, uid] + + query, values = self.get_query( + "delete from requests :condition", + columns, values) + + self.cursor.execute(query, values) + return True + + def get_counters(self, user, uid, recurrenceid=None): + + """ + For the given 'user', return a list of users from whom counter-proposals + have been received for the given 'uid' and optional 'recurrenceid'. + """ + + table = self.get_event_table(recurrenceid, "counters") + + if recurrenceid: + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + else: + columns = ["store_user", "object_uid"] + values = [user, uid] + + query, values = self.get_query( + "select other from %(table)s :condition" % { + "table" : table + }, + columns, values) + + self.cursor.execute(query, values) + return self.cursor.fetchall() + + def get_counter(self, user, other, uid, recurrenceid=None): + + """ + For the given 'user', return the counter-proposal from 'other' for the + given 'uid' and optional 'recurrenceid'. + """ + + table = self.get_event_table(recurrenceid, "counters") + + if recurrenceid: + columns = ["store_user", "other", "object_uid", "object_recurrenceid"] + values = [user, other, uid, recurrenceid] + else: + columns = ["store_user", "other", "object_uid"] + values = [user, other, uid] + + query, values = self.get_query( + "select object_text from %(table)s :condition" % { + "table" : table + }, + columns, values) + + self.cursor.execute(query, values) + result = self.cursor.fetchone() + return result and parse_string(result[0], "utf-8") + + def set_counter(self, user, other, node, uid, recurrenceid=None): + + """ + For the given 'user', store a counter-proposal received from 'other' the + given 'node' representing that proposal for the given 'uid' and + 'recurrenceid'. + """ + + table = self.get_event_table(recurrenceid, "counters") + + if recurrenceid: + columns = ["store_user", "other", "object_uid", "object_recurrenceid", "object_text"] + values = [user, other, uid, recurrenceid, to_string(node, "utf-8")] + else: + columns = ["store_user", "other", "object_uid", "object_text"] + values = [user, other, uid, to_string(node, "utf-8")] + + query, values = self.get_query( + "insert into %(table)s (:columns) values (:values)" % { + "table" : table + }, + columns, values) + + self.cursor.execute(query, values) + return True + + def remove_counters(self, user, uid, recurrenceid=None): + + """ + For the given 'user', remove all counter-proposals associated with the + given 'uid' and 'recurrenceid'. + """ + + table = self.get_event_table(recurrenceid, "counters") + + if recurrenceid: + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + else: + columns = ["store_user", "object_uid"] + values = [user, uid] + + query, values = self.get_query( + "delete from %(table)s :condition" % { + "table" : table + }, + columns, values) + + self.cursor.execute(query, values) + return True + + def remove_counter(self, user, other, uid, recurrenceid=None): + + """ + For the given 'user', remove any counter-proposal from 'other' + associated with the given 'uid' and 'recurrenceid'. + """ + + table = self.get_event_table(recurrenceid, "counters") + + if recurrenceid: + columns = ["store_user", "other", "object_uid", "object_recurrenceid"] + values = [user, other, uid, recurrenceid] + else: + columns = ["store_user", "other", "object_uid"] + values = [user, other, uid] + + query, values = self.get_query( + "delete from %(table)s :condition" % { + "table" : table + }, + columns, values) + + self.cursor.execute(query, values) + return True + + # Event cancellation. + + def cancel_event(self, user, uid, recurrenceid=None): + + """ + Cancel an event for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, a specific instance or occurrence of an + event is cancelled. + """ + + table = self.get_event_table(recurrenceid) + + if recurrenceid: + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + else: + columns = ["store_user", "object_uid"] + values = [user, uid] + + setcolumns = ["status"] + setvalues = ["cancelled"] + + query, values = self.get_query( + "update %(table)s :set :condition" % { + "table" : table + }, + columns, values, setcolumns, setvalues) + + self.cursor.execute(query, values) + return True + + def uncancel_event(self, user, uid, recurrenceid=None): + + """ + Uncancel an event for 'user' having the given 'uid'. If the optional + 'recurrenceid' is specified, a specific instance or occurrence of an + event is uncancelled. + """ + + table = self.get_event_table(recurrenceid) + + if recurrenceid: + columns = ["store_user", "object_uid", "object_recurrenceid"] + values = [user, uid, recurrenceid] + else: + columns = ["store_user", "object_uid"] + values = [user, uid] + + setcolumns = ["status"] + setvalues = ["active"] + + query, values = self.get_query( + "update %(table)s :set :condition" % { + "table" : table + }, + columns, values, setcolumns, setvalues) + + self.cursor.execute(query, values) + return True + + def remove_cancellation(self, user, uid, recurrenceid=None): + + """ + Remove a cancellation for 'user' for the event having the given 'uid'. + If the optional 'recurrenceid' is specified, a specific instance or + occurrence of an event is affected. + """ + + table = self.get_event_table(recurrenceid) + + if recurrenceid: + columns = ["store_user", "object_uid", "object_recurrenceid", "status"] + values = [user, uid, recurrenceid, "cancelled"] + else: + columns = ["store_user", "object_uid", "status"] + values = [user, uid, "cancelled"] + + query, values = self.get_query( + "delete from %(table)s :condition" % { + "table" : table + }, + columns, values) + + self.cursor.execute(query, values) + return True + +class DatabaseJournal(DatabaseStoreBase, JournalBase): + + "A journal system to support quotas." + + # Quota and user identity/group discovery. + + def get_quotas(self): + + "Return a list of quotas." + + query = "select distinct journal_quota from quota_freebusy" + self.cursor.execute(query) + return [r[0] for r in self.cursor.fetchall()] + + def get_quota_users(self, quota): + + "Return a list of quota users." + + columns = ["quota"] + values = [quota] + + query, values = self.get_query( + "select distinct user_group from quota_freebusy :condition", + columns, values) + + self.cursor.execute(query) + return [r[0] for r in self.cursor.fetchall()] + + # Groups of users sharing quotas. + + def get_groups(self, quota): + + "Return the identity mappings for the given 'quota' as a dictionary." + + columns = ["quota"] + values = [quota] + + query, values = self.get_query( + "select store_user, user_group from user_groups :condition", + columns, values) + + self.cursor.execute(query) + return dict(self.cursor.fetchall()) + + def get_limits(self, quota): + + """ + Return the limits for the 'quota' as a dictionary mapping identities or + groups to durations. + """ + + columns = ["quota"] + values = [quota] + + query, values = self.get_query( + "select user_group, quota_limit from quota_limits :condition", + columns, values) + + self.cursor.execute(query) + return dict(self.cursor.fetchall()) + + # Free/busy period access for users within quota groups. + + def get_freebusy(self, quota, user, mutable=False): + + "Get free/busy details for the given 'quota' and 'user'." + + table = "user_freebusy" + return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], mutable, self.paramstyle) + + def set_freebusy(self, quota, user, freebusy): + + "For the given 'quota' and 'user', set 'freebusy' details." + + table = "user_freebusy" + + if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table: + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], True, self.paramstyle) + fbc += freebusy + + return True + + # Journal entry methods. + + def get_entries(self, quota, group, mutable=False): + + """ + Return a list of journal entries for the given 'quota' for the indicated + 'group'. + """ + + table = "quota_freebusy" + return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], mutable, self.paramstyle) + + def set_entries(self, quota, group, entries): + + """ + For the given 'quota' and indicated 'group', set the list of journal + 'entries'. + """ + + table = "quota_freebusy" + + if not isinstance(entries, FreeBusyDatabaseCollection) or entries.table_name != table: + fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], True, self.paramstyle) + fbc += entries + + return True + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/stores/database/postgresql.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imiptools/stores/database/postgresql.py Thu Mar 10 01:43:31 2016 +0100 @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +""" +A PostgreSQL database store of calendar data. + +Copyright (C) 2016 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from imiptools.stores.database.common import DatabaseStore, DatabaseJournal +import psycopg2 + +class Store(DatabaseStore): + + "A PostgreSQL database store of calendar objects and free/busy data." + + def __init__(self, store_dir): + + "Interpret 'store_dir' as a connection string." + + connection = psycopg2.connect(store_dir) + connection.autocommit = True + DatabaseStore.__init__(self, connection, psycopg2.paramstyle) + +class Journal(DatabaseJournal): + + "A PostgreSQL journal system supporting quotas." + + def __init__(self, store_dir): + + "Interpret 'store_dir' as a connection string." + + connection = psycopg2.connect(store_dir) + connection.autocommit = True + DatabaseJournal.__init__(self, connection, psycopg2.paramstyle) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 16b00c3bee02 -r 31d839d508f6 imiptools/stores/file.py --- a/imiptools/stores/file.py Wed Mar 09 21:38:56 2016 +0100 +++ b/imiptools/stores/file.py Thu Mar 10 01:43:31 2016 +0100 @@ -19,7 +19,7 @@ this program. If not, see . """ -from imiptools.stores import StoreBase, PublisherBase, JournalBase +from imiptools.stores.common import StoreBase, PublisherBase, JournalBase from datetime import datetime from imiptools.config import STORE_DIR, PUBLISH_DIR, JOURNAL_DIR @@ -148,7 +148,7 @@ finally: self.release_lock(user) -class FileStore(FileStoreBase, StoreBase): +class Store(FileStoreBase, StoreBase): "A file store of tabular free/busy data and objects." @@ -724,7 +724,7 @@ return False -class FilePublisher(FileBase, PublisherBase): +class Publisher(FileBase, PublisherBase): "A publisher of objects." @@ -760,7 +760,7 @@ return True -class FileJournal(FileStoreBase, JournalBase): +class Journal(FileStoreBase, JournalBase): "A journal system to support quotas." diff -r 16b00c3bee02 -r 31d839d508f6 tests/common.sh --- a/tests/common.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/common.sh Thu Mar 10 01:43:31 2016 +0100 @@ -3,25 +3,37 @@ THIS_DIR=`dirname "$0"` BASE_DIR="$THIS_DIR/.." -STORE=/tmp/store +STORETYPE=file + +if [ "$STORETYPE" = "file" ]; then + STORE=/tmp/store + JOURNAL=/tmp/journal +elif [ "$STORETYPE" = "postgresql" ]; then + DBNAME='test' + STORE="dbname=$DBNAME" + JOURNAL="$STORE" +fi + STATIC=/tmp/static PREFS=/tmp/prefs -JOURNAL=/tmp/journal -ARGS="-S $STORE -P $STATIC -p $PREFS -j $JOURNAL -d" +ARGS="-T $STORETYPE -S $STORE -P $STATIC -p $PREFS -j $JOURNAL -d" ACCEPT_SCRIPT="$THIS_DIR/test_handle.py" -ACCEPT_ARGS="accept $STORE $JOURNAL $PREFS" +ACCEPT_ARGS="accept $STORETYPE $STORE $JOURNAL $PREFS" COUNTER_SCRIPT="$THIS_DIR/test_handle.py" -COUNTER_ARGS="counter $STORE $JOURNAL $PREFS" +COUNTER_ARGS="counter $STORETYPE $STORE $JOURNAL $PREFS" DECLINE_SCRIPT="$THIS_DIR/test_handle.py" -DECLINE_ARGS="decline $STORE $JOURNAL $PREFS" +DECLINE_ARGS="decline $STORETYPE $STORE $JOURNAL $PREFS" FREEBUSY_SCRIPT="$BASE_DIR/tools/make_freebusy.py" FREEBUSY_ARGS="-s -n" +LIST_SCRIPT="$THIS_DIR/list_table.py" +LIST_ARGS="$STORETYPE $STORE $JOURNAL" + OUTGOING_SCRIPT="$BASE_DIR/imip_person_outgoing.py" PERSON_SCRIPT="$BASE_DIR/imip_person.py" @@ -39,9 +51,16 @@ PYTHONPATH="$BASE_DIR" export PYTHONPATH -rm -rf "$STORE" +if [ "$STORETYPE" = "file" ]; then + rm -rf "$STORE" + rm -rf "$JOURNAL" +elif [ "$STORETYPE" = "postgresql" ]; then + dropdb "$DBNAME" + createdb "$DBNAME" + psql -f "$BASE_DIR/conf/postgresql/schema.sql" "$DBNAME" +fi + rm -rf "$STATIC" rm -rf "$PREFS" -rm -rf "$JOURNAL" rm -f "$ERROR" rm -f out*.tmp diff -r 16b00c3bee02 -r 31d839d508f6 tests/list_table.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/list_table.py Thu Mar 10 01:43:31 2016 +0100 @@ -0,0 +1,105 @@ +#!/usr/bin/env python + +""" +Show the contents of a table. + +Copyright (C) 2016 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from imiptools.data import Object +from imiptools.stores import get_store, get_journal +import sys + +def show_list(data): + for row in data: + print row or "" + +def show_object(data): + if data: + print Object(data).to_string() + +def show_periods(data): + for row in data: + print "\t".join(row.as_tuple(strings_only=True)) + +def show_tuples(data): + for row in data: + print "\t".join([(column or "") for column in row]) + +if __name__ == "__main__": + try: + store_type, store_dir, journal_dir, user, table = sys.argv[1:6] + args = sys.argv[6:] + except ValueError: + print >>sys.stderr, """\ +Need a store type, a store directory, a journal directory, a user URI, and a +table to show. Other arguments may be needed for certain tables. +""" + sys.exit(1) + + store = get_store(store_type, store_dir) + journal = get_journal(store_type, journal_dir) + + # Periods. + + if table == "entries": + data = journal.get_entries(user) + show_periods(data) + + elif table == "freebusy": + data = store.get_freebusy(user) + show_periods(data) + + elif table == "freebusy_offers": + data = store.get_freebusy_offers(user) + show_periods(data) + + elif table == "freebusy_other": + other = args[0] + data = store.get_freebusy_for_other(user, other) + show_periods(data) + + # Tuples. + + elif table == "requests": + data = store.get_requests(user) + show_tuples(data) + + # Objects. + + elif table == "countered_object": + uid = args[0] + other = args[1] + data = store.get_counter(user, other, uid) + show_object(data) + + elif table == "object": + uid = args[0] + data = store.get_event(user, uid) + show_object(data) + + elif table == "recurrence": + uid = args[0] + recurrenceid = args[1] + data = store.get_event(user, uid, recurrenceid) + show_object(data) + + elif table == "cancelled_recurrences": + uid = args[0] + data = store.get_cancelled_recurrences(user, uid) + show_list(data) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_freebusy_publishing.sh --- a/tests/test_freebusy_publishing.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_freebusy_publishing.sh Thu Mar 10 01:43:31 2016 +0100 @@ -4,8 +4,6 @@ USER="mailto:paul.boddie@example.com" SENDER="mailto:resource-room-confroom@example.com" -FBFILE="$STORE/$USER/freebusy" -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER" mkdir -p "$PREFS/$USER" echo 'Europe/Oslo' > "$PREFS/$USER/TZID" @@ -15,17 +13,23 @@ | "$SHOWMAIL" \ > out0.tmp - grep -q "^20140401T070000Z${TAB}20140401T080000Z" "$FBOTHERFILE" \ -&& grep -q "^20140401T080000Z${TAB}20140401T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out1.tmp + + grep -q "^20140401T070000Z${TAB}20140401T080000Z" "out1.tmp" \ +&& grep -q "^20140401T080000Z${TAB}20140401T100000Z" "out1.tmp" \ && echo "Success" \ || echo "Failed" "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/fb-publish-again.txt" 2>> $ERROR \ | "$SHOWMAIL" \ -> out0.tmp +> out2.tmp - grep -q "^20140401T070000Z${TAB}20140401T080000Z" "$FBOTHERFILE" \ -&& ! grep -q "^20140401T080000Z${TAB}20140401T100000Z" "$FBOTHERFILE" \ -&& grep -q "^20140401T083000Z${TAB}20140401T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out3.tmp + + grep -q "^20140401T070000Z${TAB}20140401T080000Z" "out3.tmp" \ +&& ! grep -q "^20140401T080000Z${TAB}20140401T100000Z" "out3.tmp" \ +&& grep -q "^20140401T083000Z${TAB}20140401T100000Z" "out3.tmp" \ && echo "Success" \ || echo "Failed" diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_handle.py --- a/tests/test_handle.py Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_handle.py Thu Mar 10 01:43:31 2016 +0100 @@ -24,7 +24,7 @@ from imiptools.dates import get_datetime, to_timezone from imiptools.mail import Messenger from imiptools.period import RecurringPeriod -import imiptools.stores.file +from imiptools.stores import get_store, get_journal import sys class TestClient(ClientForObject): @@ -94,19 +94,20 @@ if __name__ == "__main__": try: - action, store_dir, journal_dir, preferences_dir, user = sys.argv[1:6] + action, store_type, store_dir, journal_dir, preferences_dir, user = sys.argv[1:7] if action == "counter": - start, end = sys.argv[6:8] - i = 8 + start, end = sys.argv[7:9] + i = 9 else: start, end = None, None - i = 6 + i = 7 uid, recurrenceid = (sys.argv[i:i+2] + [None] * 2)[:2] except ValueError: print >>sys.stderr, """\ -Need 'accept', 'counter' or 'decline', a store directory, a preferences -directory, user URI, any counter-proposal datetimes (see below), plus the -appropriate event UID and RECURRENCE-ID (if a recurrence is involved). +Need 'accept', 'counter' or 'decline', a store type, a store directory, a +journal directory, a preferences directory, user URI, any counter-proposal +datetimes (see below), plus the appropriate event UID and RECURRENCE-ID (if a +recurrence is involved). The RECURRENCE-ID must be in exactly the form employed by the store, not a different but equivalent representation. @@ -120,8 +121,8 @@ """ sys.exit(1) - store = imiptools.stores.file.FileStore(store_dir) - journal = imiptools.stores.file.FileJournal(journal_dir) + store = get_store(store_type, store_dir) + journal = get_journal(store_type, journal_dir) if uid is not None: fragment = store.get_event(user, uid, recurrenceid) diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_outgoing_invitation.sh --- a/tests/test_outgoing_invitation.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_outgoing_invitation.sh Thu Mar 10 01:43:31 2016 +0100 @@ -3,70 +3,84 @@ . "`dirname \"$0\"`/common.sh" USER="mailto:paul.boddie@example.com" -FBFILE="$STORE/$USER/freebusy" mkdir -p "$PREFS/$USER" echo 'Europe/Oslo' > "$PREFS/$USER/TZID" echo 'share' > "$PREFS/$USER/freebusy_sharing" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request.txt" 2>> $ERROR -cp "$FBFILE" out1.tmp - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +| tee out1.tmp \ +| grep -q "^20141126T150000Z${TAB}20141126T160000Z" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel.txt" 2>> $ERROR echo "Cancel..." -cp "$FBFILE" out2.tmp - ! grep -q '^2' "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out2.tmp + + ! grep -q '^2' "out2.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring.txt" 2>> $ERROR -cp "$FBFILE" out3.tmp - [ `cat "$FBFILE" | wc -l` = '3' ] \ -&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out3.tmp + + [ `cat "out3.tmp" | wc -l` = '3' ] \ +&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring-instance.txt" 2>> $ERROR -cp "$FBFILE" out4.tmp - [ `cat "$FBFILE" | wc -l` = '2' ] \ -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out4.tmp + + [ `cat "out4.tmp" | wc -l` = '2' ] \ +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out4.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-reschedule-instance.txt" 2>> $ERROR -cp "$FBFILE" out5.tmp - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBFILE" \ -&& ! grep -q "^20141010T090000Z${TAB}20141010T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out5.tmp + + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5.tmp" \ +&& ! grep -q "^20141010T090000Z${TAB}20141010T100000Z" "out5.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring.txt" 2>> $ERROR -cp "$FBFILE" out6.tmp - ! grep -q '^2' "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out6.tmp + + ! grep -q '^2' "out6.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-day.txt" 2>> $ERROR -cp "$FBFILE" out7.tmp - [ `cat "$FBFILE" | wc -l` = '3' ] \ -&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out7.tmp + + [ `cat "out7.tmp" | wc -l` = '3' ] \ +&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "out7.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring-day.txt" 2>> $ERROR -cp "$FBFILE" out8.tmp - ! grep -q '^2' "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out8.tmp + + ! grep -q '^2' "out8.tmp" \ && echo "Success" \ || echo "Failed" @@ -75,31 +89,39 @@ echo 'Europe/Mariehamn' > "$PREFS/$USER/TZID" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring.txt" 2>> $ERROR -cp "$FBFILE" out9.tmp - [ `cat "$FBFILE" | wc -l` = '3' ] \ -&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out9.tmp + + [ `cat "out9.tmp" | wc -l` = '3' ] \ +&& grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out9.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring.txt" 2>> $ERROR -cp "$FBFILE" out10.tmp - ! grep -q '^2' "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out10.tmp + + ! grep -q '^2' "out10.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-recurring-day.txt" 2>> $ERROR -cp "$FBFILE" out11.tmp - [ `cat "$FBFILE" | wc -l` = '3' ] \ -&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out11.tmp + + [ `cat "out11.tmp" | wc -l` = '3' ] \ +&& grep -q "^20141211T230000Z${TAB}20141212T230000Z" "out11.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-recurring-day.txt" 2>> $ERROR -cp "$FBFILE" out12.tmp - ! grep -q '^2' "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out12.tmp + + ! grep -q '^2' "out12.tmp" \ && echo "Success" \ || echo "Failed" diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_person_invitation.sh --- a/tests/test_person_invitation.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_person_invitation.sh Thu Mar 10 01:43:31 2016 +0100 @@ -4,8 +4,6 @@ USER="mailto:vincent.vole@example.com" SENDER="mailto:paul.boddie@example.com" -FBFILE="$STORE/$USER/freebusy" -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER" mkdir -p "$PREFS/$USER" echo 'Europe/Oslo' > "$PREFS/$USER/TZID" @@ -37,12 +35,17 @@ && echo "Success" \ || echo "Failed" - ! [ -e "$FBFILE" ] \ -|| ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out2f.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out2fo.tmp + + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -50,7 +53,10 @@ | tee out3.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out3f.tmp + + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f.tmp" \ && echo "Success" \ || echo "Failed" @@ -62,11 +68,17 @@ && echo "Success" \ || echo "Failed" - ! grep -q "event7@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out4f.tmp + + ! grep -q "event7@example.com" "out4f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "event7@example.com" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out4fo.tmp + + grep -q "event7@example.com" "out4fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -74,8 +86,11 @@ | tee out5.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "event6@example.com" "$FBFILE" \ -&& ! grep -q "event7@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out5f.tmp + + grep -q "event6@example.com" "out5f.tmp" \ +&& ! grep -q "event7@example.com" "out5f.tmp" \ && echo "Success" \ || echo "Failed" @@ -87,11 +102,17 @@ && echo "Success" \ || echo "Failed" - ! grep -q "event6@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out6f.tmp + + ! grep -q "event6@example.com" "out6f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "event6@example.com" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out6fo.tmp + + grep -q "event6@example.com" "out6fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -103,11 +124,17 @@ && echo "Success" \ || echo "Failed" - ! grep -q "event6@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out7f.tmp + + ! grep -q "event6@example.com" "out7f.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q "event6@example.com" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out7fo.tmp + + ! grep -q "event6@example.com" "out7fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -119,10 +146,16 @@ && echo "Success" \ || echo "Failed" - ! grep -q "spoof2@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out8f.tmp + + ! grep -q "spoof2@example.com" "out8f.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q "spoof2@example.com" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out8fo.tmp + + ! grep -q "spoof2@example.com" "out8fo.tmp" \ && echo "Success" \ || echo "Failed" diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_person_invitation_add.sh --- a/tests/test_person_invitation_add.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_person_invitation_add.sh Thu Mar 10 01:43:31 2016 +0100 @@ -4,10 +4,6 @@ USER="mailto:vincent.vole@example.com" SENDER="mailto:paul.boddie@example.com" -FBFILE="$STORE/$USER/freebusy" -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER" -FBSENDERFILE="$STORE/$SENDER/freebusy" -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER" mkdir -p "$PREFS/$USER" echo 'Europe/Oslo' > "$PREFS/$USER/TZID" @@ -21,7 +17,10 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out1f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out1f.tmp" \ && echo "Success" \ || echo "Failed" @@ -35,12 +34,17 @@ && echo "Success" \ || echo "Failed" - ! [ -e "$FBFILE" ] \ -|| ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out2f.tmp + + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out2fo.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -54,7 +58,10 @@ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out3f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \ && echo "Success" \ || echo "Failed" @@ -64,7 +71,10 @@ | "$SHOWMAIL" \ > out4.tmp - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDEROTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \ +> out4fo.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out4fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -72,8 +82,11 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-add-person-recurring.txt" 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \ -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out4f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out4f.tmp" \ +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out4f.tmp" \ && echo "Success" \ || echo "Failed" @@ -87,13 +100,19 @@ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ -&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out5f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out5f.tmp" \ +&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out5f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \ -&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out5fo.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out5fo.tmp" \ +&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out5fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -118,13 +137,19 @@ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ -&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out7f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out7f.tmp" \ +&& ! grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out7f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \ -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out7fo.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out7fo.tmp" \ +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out7fo.tmp" \ && echo "Success" \ || echo "Failed" @@ -146,7 +171,10 @@ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ -&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out9f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out9f.tmp" \ +&& grep -q "^20150109T090000Z${TAB}20150109T100000Z" "out9f.tmp" \ && echo "Success" \ || echo "Failed" diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_person_invitation_counter.sh --- a/tests/test_person_invitation_counter.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_person_invitation_counter.sh Thu Mar 10 01:43:31 2016 +0100 @@ -4,11 +4,6 @@ USER="mailto:vincent.vole@example.com" SENDER="mailto:paul.boddie@example.com" -FBFILE="$STORE/$USER/freebusy" -FBOFFERFILE="$STORE/$USER/freebusy-offers" -FBSENDERFILE="$STORE/$SENDER/freebusy" -FBSENDEROTHERFILE="$STORE/$SENDER/freebusy-other/$USER" -FBSENDERREQUESTS="$STORE/$SENDER/requests" mkdir -p "$PREFS/$USER" echo 'Europe/Oslo' > "$PREFS/$USER/TZID" @@ -28,7 +23,10 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person.txt" 2>> $ERROR - grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out0f.tmp + + grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out0f.tmp" \ && echo "Success" \ || echo "Failed" @@ -42,8 +40,10 @@ && echo "Success" \ || echo "Failed" - ! [ -e "$FBFILE" ] \ -|| ! grep -q "event6@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out1f.tmp + + ! grep -q "event6@example.com" "out1f.tmp" \ && echo "Success" \ || echo "Failed" @@ -61,9 +61,11 @@ # Note that the invitation has only been prepared, not processed. - ! [ -e "$FBFILE" ] \ -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \ - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBFILE" ) \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out2f.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f.tmp" \ +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2f.tmp" \ && echo "Success" \ || echo "Failed" @@ -72,8 +74,10 @@ && echo "Success" \ || echo "Failed" - ! [ -e "$FBOFFERFILE" ] \ -|| ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBOFFERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \ +> out2o.tmp + + ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2o.tmp" \ && echo "Success" \ || echo "Failed" @@ -81,13 +85,18 @@ "$OUTGOING_SCRIPT" $ARGS < out2r.tmp 2>> $ERROR - ! [ -e "$FBFILE" ] \ -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \ - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBFILE" ) \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out2f2.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out2f2.tmp" \ +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2f2.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBOFFERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \ +> out2o2.tmp + + grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out2o2.tmp" \ && echo "Success" \ || echo "Failed" @@ -98,21 +107,32 @@ | "$SHOWMAIL" \ > out3.tmp - ! [ -e "$FBSENDEROTHERFILE" ] \ -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBSENDEROTHERFILE" \ - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBSENDEROTHERFILE" ) \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy_other" "$USER" \ +> out3f.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out3f.tmp" \ +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out3f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T160000' "$STORE/$SENDER/objects/event6@example.com" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event6@example.com" \ +> out3O.tmp + + grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T160000' "out3O.tmp" \ && echo "Success" \ || echo "Failed" - grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T170000' "$STORE/$SENDER/counters/objects/event6@example.com/$USER" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event6@example.com" "$USER" \ +> out3C.tmp + + grep -q 'DTSTART;TZID=Europe/Oslo.*:20141126T170000' "out3C.tmp" \ && echo "Success" \ || echo "Failed" - grep -q 'event6@example.com' "$FBSENDERREQUESTS" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "requests" \ +> out3R.tmp + + grep -q 'event6@example.com' "out3R.tmp" \ && echo "Success" \ || echo "Failed" @@ -137,11 +157,17 @@ "$OUTGOING_SCRIPT" $ARGS < out5.tmp 2>> $ERROR - ! [ -e "$STORE/$SENDER/counters/objects/event6@example.com/$USER" ] \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "countered_object" "event6@example.com" "$USER" \ +> out5C.tmp + + ! grep -q 'event6@example.com' "out5C.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q 'event6@example.com' "$FBSENDERREQUESTS" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "requests" \ +> out5R.tmp + + ! grep -q 'event6@example.com' "out5R.tmp" \ && echo "Success" \ || echo "Failed" @@ -149,13 +175,18 @@ | "$SHOWMAIL" \ > out6.tmp - ! [ -e "$FBFILE" ] \ -|| ( ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBFILE" \ - && ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBFILE" ) \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out6f.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out6f.tmp" \ +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out6f.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "$FBOFFERFILE" \ -&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "$FBOFFERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_offers" \ +> out6o.tmp + + ! grep -q "^20141126T150000Z${TAB}20141126T160000Z" "out6o.tmp" \ +&& ! grep -q "^20141126T160000Z${TAB}20141126T170000Z" "out6o.tmp" \ && echo "Success" \ || echo "Failed" diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_person_invitation_recurring.sh --- a/tests/test_person_invitation_recurring.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_person_invitation_recurring.sh Thu Mar 10 01:43:31 2016 +0100 @@ -4,9 +4,6 @@ USER="mailto:vincent.vole@example.com" SENDER="mailto:paul.boddie@example.com" -FBFILE="$STORE/$USER/freebusy" -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER" -FBSENDERFILE="$STORE/$SENDER/freebusy" mkdir -p "$PREFS/$USER" echo 'Europe/Oslo' > "$PREFS/$USER/TZID" @@ -39,7 +36,10 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out1f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out1f.tmp" \ && echo "Success" \ || echo "Failed" @@ -53,12 +53,17 @@ && echo "Success" \ || echo "Failed" - ! [ -e "$FBFILE" ] \ -|| ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out2f.tmp + + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out2o.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out2o.tmp" \ && echo "Success" \ || echo "Failed" @@ -68,7 +73,10 @@ | tee out3.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out3f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \ && echo "Success" \ || echo "Failed" @@ -76,8 +84,11 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring-instance.txt" 2>> $ERROR - [ `grep "event8@example.com" "$FBSENDERFILE" | wc -l` = '2' ] \ -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out3s.tmp + + [ `grep "event8@example.com" "out3s.tmp" | wc -l` = '2' ] \ +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out3s.tmp" \ && echo "Success" \ || echo "Failed" @@ -91,13 +102,19 @@ && echo "Success" \ || echo "Failed" - [ `grep "event8@example.com" "$FBFILE" | wc -l` = '2' ] \ -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out4f.tmp + + [ `grep "event8@example.com" "out4f.tmp" | wc -l` = '2' ] \ +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out4f.tmp" \ && echo "Success" \ || echo "Failed" - [ `grep "event8@example.com" "$FBOTHERFILE" | wc -l` = '2' ] \ -&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out4o.tmp + + [ `grep "event8@example.com" "out4o.tmp" | wc -l` = '2' ] \ +&& ! grep -q "^20141114T090000Z${TAB}20141114T100000Z" "out4o.tmp" \ && echo "Success" \ || echo "Failed" @@ -105,8 +122,11 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-reschedule-instance.txt" 2>> $ERROR - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \ -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out4s.tmp + + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out4s.tmp" \ +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out4s.tmp" \ && echo "Success" \ || echo "Failed" @@ -120,13 +140,19 @@ && echo "Success" \ || echo "Failed" - ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBFILE" \ -&& grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out5f.tmp + + ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5f.tmp" \ +&& grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out5f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBOTHERFILE" \ -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out5o.tmp + + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5o.tmp" \ +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out5o.tmp" \ && echo "Success" \ || echo "Failed" @@ -136,8 +162,11 @@ | tee out6.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBFILE" \ -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out6f.tmp + + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6f.tmp" \ +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out6f.tmp" \ && echo "Success" \ || echo "Failed" @@ -145,7 +174,10 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR - ! grep -q "event8@example.com" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out6s.tmp + + ! grep -q "event8@example.com" "out6s.tmp" \ && echo "Success" \ || echo "Failed" @@ -153,17 +185,23 @@ "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR \ | "$SHOWMAIL" \ -> out6.tmp +> out7.tmp - ! grep -q 'METHOD:REPLY' out6.tmp \ + ! grep -q 'METHOD:REPLY' out7.tmp \ && echo "Success" \ || echo "Failed" - ! grep -q "event8@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out7f.tmp + + ! grep -q "event8@example.com" "out7f.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q "event8@example.com" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out7o.tmp + + ! grep -q "event8@example.com" "out7o.tmp" \ && echo "Success" \ || echo "Failed" @@ -173,53 +211,74 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out7s.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out7s.tmp" \ && echo "Success" \ || echo "Failed" "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR \ | "$SHOWMAIL" \ -> out7.tmp +> out8.tmp - ! grep -q 'METHOD:REPLY' out7.tmp \ + ! grep -q 'METHOD:REPLY' out8.tmp \ && echo "Success" \ || echo "Failed" - ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out8f.tmp + + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out8f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out8o.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out8o.tmp" \ && echo "Success" \ || echo "Failed" "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER" "event8@example.com" 2>> $ERROR \ -| tee out8.tmp \ +| tee out9.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out9f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out9f.tmp" \ && echo "Success" \ || echo "Failed" "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR - ! grep -q "event8@example.com" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out9s.tmp + + ! grep -q "event8@example.com" "out9s.tmp" \ && echo "Success" \ || echo "Failed" "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring.txt" 2>> $ERROR \ | "$SHOWMAIL" \ -> out9.tmp +> out10.tmp - ! grep -q 'METHOD:REPLY' out9.tmp \ + ! grep -q 'METHOD:REPLY' out10.tmp \ && echo "Success" \ || echo "Failed" - ! grep -q "event8@example.com" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out10f.tmp + + ! grep -q "event8@example.com" "out10f.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q "event8@example.com" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out10o.tmp + + ! grep -q "event8@example.com" "out10o.tmp" \ && echo "Success" \ || echo "Failed" @@ -228,32 +287,44 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-day-floating.txt" 2>> $ERROR - grep -q "^20141211T230000Z${TAB}20141212T230000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out10s.tmp + + grep -q "^20141211T230000Z${TAB}20141212T230000Z" "out10s.tmp" \ && echo "Success" \ || echo "Failed" "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-day-floating.txt" 2>> $ERROR \ | "$SHOWMAIL" \ -> out10.tmp +> out11.tmp - ! grep -q 'METHOD:REPLY' out10.tmp \ + ! grep -q 'METHOD:REPLY' out11.tmp \ && echo "Success" \ || echo "Failed" - ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out11f.tmp + + ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "out11f.tmp" \ && echo "Success" \ || echo "Failed" # (The organiser is not attending.) - ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out11o.tmp + + ! grep -q "^20141211T220000Z${TAB}20141212T220000Z" "out11o.tmp" \ && echo "Success" \ || echo "Failed" "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER" "event12@example.com" 2>> $ERROR \ -| tee out11.tmp \ +| tee out12.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "^20141211T220000Z${TAB}20141212T220000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out12f.tmp + + grep -q "^20141211T220000Z${TAB}20141212T220000Z" "out12f.tmp" \ && echo "Success" \ || echo "Failed" diff -r 16b00c3bee02 -r 31d839d508f6 tests/test_person_invitation_refresh.sh --- a/tests/test_person_invitation_refresh.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tests/test_person_invitation_refresh.sh Thu Mar 10 01:43:31 2016 +0100 @@ -4,9 +4,6 @@ USER="mailto:vincent.vole@example.com" SENDER="mailto:paul.boddie@example.com" -FBFILE="$STORE/$USER/freebusy" -FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER" -FBSENDERFILE="$STORE/$SENDER/freebusy" mkdir -p "$PREFS/$USER" echo 'Europe/Oslo' > "$PREFS/$USER/TZID" @@ -21,7 +18,10 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out1f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out1f.tmp" \ && echo "Success" \ || echo "Failed" @@ -45,12 +45,17 @@ && echo "Success" \ || echo "Failed" - ! [ -e "$FBFILE" ] \ -|| ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out3f.tmp + + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out3f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out3f.tmp" \ && echo "Success" \ || echo "Failed" @@ -60,7 +65,10 @@ | tee out4.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out4f.tmp + + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "out4f.tmp" \ && echo "Success" \ || echo "Failed" @@ -78,8 +86,11 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring-reschedule-instance.txt" 2>> $ERROR - grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \ -&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out5f.tmp + + grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out5f.tmp" \ +&& ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out5f.tmp" \ && echo "Success" \ || echo "Failed" @@ -102,8 +113,14 @@ | "$SHOWMAIL" \ > out6a.tmp - [ -e "$STORE/$USER/objects/event8@example.com" ] \ -&& [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event8@example.com" \ +> out6O.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "recurrence" "event8@example.com" "20141010T080000Z" \ +> out6R.tmp + + grep -q 'event8@example.com' "out6O.tmp" \ +&& grep -q 'event8@example.com' "out6R.tmp" \ && echo "Success" \ || echo "Failed" @@ -112,14 +129,26 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-cancel-person-recurring-rescheduled-instance.txt" 2>> $ERROR - [ -e "$STORE/$SENDER/objects/event8@example.com" ] \ -&& ! [ -e "$STORE/$SENDER/recurrences/event8@example.com/20141010T080000Z" ] \ -&& [ -e "$STORE/$SENDER/cancellations/recurrences/event8@example.com/20141010T080000Z" ] \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "object" "event8@example.com" \ +> out6O2.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "recurrence" "event8@example.com" "20141010T080000Z" \ +> out6R2.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "cancelled_recurrences" "event8@example.com" \ +> out6C.tmp + + grep -q 'event8@example.com' "out6O2.tmp" \ +&& ! grep -q 'event8@example.com' "out6R2.tmp" \ +&& grep -q '20141010T080000Z' "out6C.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \ -&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out6f.tmp + + ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out6f.tmp" \ +&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out6f.tmp" \ && echo "Success" \ || echo "Failed" @@ -143,9 +172,18 @@ | "$SHOWMAIL" \ > out7a.tmp - [ -e "$STORE/$USER/objects/event8@example.com" ] \ -&& ! [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \ -&& [ -e "$STORE/$USER/cancellations/recurrences/event8@example.com/20141010T080000Z" ] \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "object" "event8@example.com" \ +> out7O.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "recurrence" "event8@example.com" "20141010T080000Z" \ +> out7R.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "cancelled_recurrences" "event8@example.com" \ +> out7C.tmp + + grep -q 'event8@example.com' "out7O.tmp" \ +&& ! grep -q 'event8@example.com' "out7R.tmp" \ +&& grep -q '20141010T080000Z' "out7C.tmp" \ && echo "Success" \ || echo "Failed" @@ -153,8 +191,11 @@ "$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-add-person-recurring-rescheduled-instance.txt" 2>> $ERROR - grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBSENDERFILE" \ -&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "$FBSENDERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$SENDER" "freebusy" \ +> out7f.tmp + + grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out7f.tmp" \ +&& ! grep -q "^20141011T080000Z${TAB}20141011T090000Z" "out7f.tmp" \ && echo "Success" \ || echo "Failed" @@ -162,17 +203,32 @@ | "$SHOWMAIL" \ > out8.tmp - [ -e "$STORE/$USER/objects/event8@example.com" ] \ -&& [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \ -&& ! [ -e "$STORE/$USER/cancellations/recurrences/event8@example.com/20141010T080000Z" ] \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "object" "event8@example.com" \ +> out8O.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "recurrence" "event8@example.com" "20141010T080000Z" \ +> out8R.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "cancelled_recurrences" "event8@example.com" \ +> out8C.tmp + + grep -q 'event8@example.com' "out8O.tmp" \ +&& grep -q 'event8@example.com' "out8R.tmp" \ +&& ! grep -q '20141010T080000Z' "out8C.tmp" \ && echo "Success" \ || echo "Failed" - ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out8f.tmp + + ! grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out8f.tmp" \ && echo "Success" \ || echo "Failed" - grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBOTHERFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy_other" "$SENDER" \ +> out8f.tmp + + grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out8f.tmp" \ && echo "Success" \ || echo "Failed" @@ -182,7 +238,10 @@ | tee out9.tmp \ | "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR - grep -q "^20141010T080000Z${TAB}20141010T090000Z" "$FBFILE" \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "freebusy" \ +> out9f.tmp + + grep -q "^20141010T080000Z${TAB}20141010T090000Z" "out9f.tmp" \ && echo "Success" \ || echo "Failed" @@ -205,7 +264,13 @@ | "$SHOWMAIL" \ > out11.tmp - [ -e "$STORE/$USER/objects/event8@example.com" ] \ -&& [ -e "$STORE/$USER/recurrences/event8@example.com/20141010T080000Z" ] \ + "$LIST_SCRIPT" $LIST_ARGS "$USER" "object" "event8@example.com" \ +> out11O.tmp + + "$LIST_SCRIPT" $LIST_ARGS "$USER" "recurrence" "event8@example.com" "20141010T080000Z" \ +> out11R.tmp + + grep -q 'event8@example.com' "out11O.tmp" \ +&& grep -q 'event8@example.com' "out11R.tmp" \ && echo "Success" \ || echo "Failed" diff -r 16b00c3bee02 -r 31d839d508f6 tools/install.sh --- a/tools/install.sh Wed Mar 09 21:38:56 2016 +0100 +++ b/tools/install.sh Thu Mar 10 01:43:31 2016 +0100 @@ -28,6 +28,7 @@ for DIR in "$INSTALL_DIR/imiptools" \ "$INSTALL_DIR/imiptools/stores" \ + "$INSTALL_DIR/imiptools/stores/database" \ "$INSTALL_DIR/imiptools/handlers" \ "$INSTALL_DIR/imiptools/handlers/scheduling" ; do if [ ! -e "$DIR" ]; then @@ -45,6 +46,7 @@ cp imiptools/*.py "$INSTALL_DIR/imiptools/" cp imiptools/stores/*.py "$INSTALL_DIR/imiptools/stores/" +cp imiptools/stores/database/*.py "$INSTALL_DIR/imiptools/stores/database/" cp imiptools/handlers/*.py "$INSTALL_DIR/imiptools/handlers/" cp imiptools/handlers/scheduling/*.py "$INSTALL_DIR/imiptools/handlers/scheduling/" diff -r 16b00c3bee02 -r 31d839d508f6 tools/make_freebusy.py --- a/tools/make_freebusy.py Wed Mar 09 21:38:56 2016 +0100 +++ b/tools/make_freebusy.py Thu Mar 10 01:43:31 2016 +0100 @@ -34,11 +34,12 @@ sys.path.append(parent) from codecs import getwriter +from imiptools import config from imiptools.client import Client from imiptools.data import get_window_end, Object from imiptools.dates import get_default_timezone, to_utc_datetime from imiptools.period import FreeBusyCollection -from imiptools.stores.file import FileStore, FilePublisher, FileJournal +from imiptools.stores import get_store, get_publisher, get_journal def make_freebusy(client, participant, store_and_publish, include_needs_action, reset_updated_list, verbose): @@ -148,6 +149,7 @@ participants = [] args = [] + store_type = [] store_dir = [] publishing_dir = [] journal_dir = [] @@ -163,6 +165,8 @@ if arg in ("-n", "-s", "-v", "-r"): args.append(arg) l = ignored + elif arg == "-T": + l = store_type elif arg == "-S": l = store_dir elif arg == "-P": @@ -188,10 +192,11 @@ General options: --j indicate the journal directory location --p indicate the preferences directory location --P indicate the publishing directory location --S indicate the store directory location +-j Indicates the journal directory location +-p Indicates the preferences directory location +-P Indicates the publishing directory location +-S Indicates the store directory location +-T Indicates the store type (the configured value if omitted) """ % split(sys.argv[0])[1] sys.exit(1) @@ -205,16 +210,19 @@ # Override defaults if indicated. - store_dir = store_dir and store_dir[0] or None - publishing_dir = publishing_dir and publishing_dir[0] or None - journal_dir = journal_dir and journal_dir[0] or None - preferences_dir = preferences_dir and preferences_dir[0] or None + getvalue = lambda value, default=None: value and value[0] or default + + store_type = getvalue(store_type, config.STORE_TYPE) + store_dir = getvalue(store_dir) + publishing_dir = getvalue(publishing_dir) + journal_dir = getvalue(journal_dir) + preferences_dir = getvalue(preferences_dir) # Obtain store-related objects. - store = FileStore(store_dir) - publisher = FilePublisher(publishing_dir) - journal = FileJournal(journal_dir) + store = get_store(store_type, store_dir) + publisher = get_publisher(publishing_dir) + journal = get_journal(store_type, journal_dir) # Obtain a list of users for processing. diff -r 16b00c3bee02 -r 31d839d508f6 tools/update_quotas.py --- a/tools/update_quotas.py Wed Mar 09 21:38:56 2016 +0100 +++ b/tools/update_quotas.py Thu Mar 10 01:43:31 2016 +0100 @@ -32,9 +32,10 @@ sys.path.append(parent) from codecs import getwriter +from imiptools import config from imiptools.dates import get_datetime, get_default_timezone, get_time, \ to_utc_datetime -from imiptools.stores.file import FileJournal +from imiptools.stores import get_journal def remove_expired_entries(entries, expiry): @@ -110,6 +111,7 @@ quotas = [] args = [] + store_type = [] journal_dir = [] expiry = [] ignored = [] @@ -123,6 +125,8 @@ if arg in ("-s", "-v"): args.append(arg) l = ignored + elif arg == "-T": + l = store_type elif arg == "-j": l = journal_dir elif arg == "-e": @@ -141,8 +145,9 @@ General options: --e indicate an expiry time for events (default is now) --j indicate the journal directory location +-e Indicates an expiry time for events (default is now) +-j Indicates the journal directory location +-T Indicates the store type (the configured value if omitted) """ % split(sys.argv[0])[1] sys.exit(1) @@ -153,8 +158,11 @@ # Override defaults if indicated. - journal_dir = journal_dir and journal_dir[0] or None - expiry = expiry and expiry[0] or None + getvalue = lambda value, default=None: value and value[0] or default + + store_type = getvalue(store_type, config.STORE_TYPE) + journal_dir = getvalue(journal_dir) + expiry = getvalue(expiry) if expiry: expiry = to_utc_datetime(get_datetime(expiry), get_default_timezone()) @@ -164,7 +172,7 @@ # Obtain store-related objects. - journal = FileJournal(journal_dir) + journal = get_journal(store_type, journal_dir) # Obtain a list of users for processing.