1.1 --- a/imiptools/stores/database.py Wed Mar 09 21:38:56 2016 +0100
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,845 +0,0 @@
1.4 -#!/usr/bin/env python
1.5 -
1.6 -"""
1.7 -A database store of calendar data.
1.8 -
1.9 -Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
1.10 -
1.11 -This program is free software; you can redistribute it and/or modify it under
1.12 -the terms of the GNU General Public License as published by the Free Software
1.13 -Foundation; either version 3 of the License, or (at your option) any later
1.14 -version.
1.15 -
1.16 -This program is distributed in the hope that it will be useful, but WITHOUT
1.17 -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.18 -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.19 -details.
1.20 -
1.21 -You should have received a copy of the GNU General Public License along with
1.22 -this program. If not, see <http://www.gnu.org/licenses/>.
1.23 -"""
1.24 -
1.25 -from imiptools.stores import StoreBase, JournalBase
1.26 -
1.27 -from datetime import datetime
1.28 -from imiptools.data import parse_string, to_string
1.29 -from imiptools.dates import format_datetime, get_datetime, to_timezone
1.30 -from imiptools.period import FreeBusyPeriod, FreeBusyDatabaseCollection
1.31 -from imiptools.sql import DatabaseOperations
1.32 -
1.33 -class DatabaseStoreBase:
1.34 -
1.35 - "A database store supporting user-specific locking."
1.36 -
1.37 - def __init__(self, connection, paramstyle=None):
1.38 - DatabaseOperations.__init__(self, paramstyle=paramstyle)
1.39 - self.connection = connection
1.40 - self.cursor = connection.cursor()
1.41 -
1.42 - def acquire_lock(self, user, timeout=None):
1.43 - pass
1.44 -
1.45 - def release_lock(self, user):
1.46 - pass
1.47 -
1.48 -class DatabaseStore(DatabaseStoreBase, StoreBase, DatabaseOperations):
1.49 -
1.50 - "A database store of tabular free/busy data and objects."
1.51 -
1.52 - # User discovery.
1.53 -
1.54 - def get_users(self):
1.55 -
1.56 - "Return a list of users."
1.57 -
1.58 - query = "select distinct store_user from freebusy"
1.59 - self.cursor.execute(query)
1.60 - return [r[0] for r in self.cursor.fetchall()]
1.61 -
1.62 - # Event and event metadata access.
1.63 -
1.64 - def get_events(self, user):
1.65 -
1.66 - "Return a list of event identifiers."
1.67 -
1.68 - columns = ["store_user", "status"]
1.69 - values = [user, "active"]
1.70 -
1.71 - query, values = self.get_query(
1.72 - "select object_uid from objects :condition",
1.73 - columns, values)
1.74 -
1.75 - self.cursor.execute(query, values)
1.76 - return [r[0] for r in self.cursor.fetchall()]
1.77 -
1.78 - def get_all_events(self, user):
1.79 -
1.80 - "Return a set of (uid, recurrenceid) tuples for all events."
1.81 -
1.82 - query, values = self.get_query(
1.83 - "select object_uid, null as object_recurrenceid from objects :condition "
1.84 - "union all "
1.85 - "select object_uid, object_recurrenceid from recurrences :condition",
1.86 - ["store_user"], [user])
1.87 -
1.88 - self.cursor.execute(query, values)
1.89 - return self.cursor.fetchall()
1.90 -
1.91 - def get_event_table(self, recurrenceid=None, dirname=None):
1.92 -
1.93 - "Get the table providing events for any specified 'dirname'."
1.94 -
1.95 - if recurrenceid:
1.96 - return self.get_recurrence_table(dirname)
1.97 - else:
1.98 - return self.get_complete_event_table(dirname)
1.99 -
1.100 - def get_event_table_filters(self, dirname=None):
1.101 -
1.102 - "Get filter details for any specified 'dirname'."
1.103 -
1.104 - if dirname == "cancellations":
1.105 - return ["status"], ["cancelled"]
1.106 - else:
1.107 - return [], []
1.108 -
1.109 - def get_event(self, user, uid, recurrenceid=None, dirname=None):
1.110 -
1.111 - """
1.112 - Get the event for the given 'user' with the given 'uid'. If
1.113 - the optional 'recurrenceid' is specified, a specific instance or
1.114 - occurrence of an event is returned.
1.115 - """
1.116 -
1.117 - table = self.get_event_table(recurrenceid, dirname)
1.118 - columns, values = self.get_event_table_filters(dirname)
1.119 -
1.120 - if recurrenceid:
1.121 - columns += ["store_user", "object_uid", "object_recurrenceid"]
1.122 - values += [user, uid, recurrenceid]
1.123 - else:
1.124 - columns += ["store_user", "object_uid"]
1.125 - values += [user, uid]
1.126 -
1.127 - query, values = self.get_query(
1.128 - "select object_text from %(table)s :condition" % {
1.129 - "table" : table
1.130 - },
1.131 - columns, values)
1.132 -
1.133 - self.cursor.execute(query, values)
1.134 - result = self.cursor.fetchone()
1.135 - return result and parse_string(result[0], "utf-8")
1.136 -
1.137 - def get_complete_event_table(self, dirname=None):
1.138 -
1.139 - "Get the table providing events for any specified 'dirname'."
1.140 -
1.141 - if dirname == "counters":
1.142 - return "countered_objects"
1.143 - else:
1.144 - return "objects"
1.145 -
1.146 - def get_complete_event(self, user, uid):
1.147 -
1.148 - "Get the event for the given 'user' with the given 'uid'."
1.149 -
1.150 - columns = ["store_user", "object_uid"]
1.151 - values = [user, uid]
1.152 -
1.153 - query, values = self.get_query(
1.154 - "select object_text from objects :condition",
1.155 - columns, values)
1.156 -
1.157 - self.cursor.execute(query, values)
1.158 - result = self.cursor.fetchone()
1.159 - return result and parse_string(result[0], "utf-8")
1.160 -
1.161 - def set_complete_event(self, user, uid, node):
1.162 -
1.163 - "Set an event for 'user' having the given 'uid' and 'node'."
1.164 -
1.165 - columns = ["store_user", "object_uid"]
1.166 - values = [user, uid]
1.167 - setcolumns = ["object_text", "status"]
1.168 - setvalues = [to_string(node, "utf-8"), "active"]
1.169 -
1.170 - query, values = self.get_query(
1.171 - "update objects :set :condition",
1.172 - columns, values, setcolumns, setvalues)
1.173 -
1.174 - self.cursor.execute(query, values)
1.175 -
1.176 - if self.cursor.rowcount > 0 or self.get_complete_event(user, uid):
1.177 - return True
1.178 -
1.179 - columns = ["store_user", "object_uid", "object_text", "status"]
1.180 - values = [user, uid, to_string(node, "utf-8"), "active"]
1.181 -
1.182 - query, values = self.get_query(
1.183 - "insert into objects (:columns) values (:values)",
1.184 - columns, values)
1.185 -
1.186 - self.cursor.execute(query, values)
1.187 - return True
1.188 -
1.189 - def remove_parent_event(self, user, uid):
1.190 -
1.191 - "Remove the parent event for 'user' having the given 'uid'."
1.192 -
1.193 - columns = ["store_user", "object_uid"]
1.194 - values = [user, uid]
1.195 -
1.196 - query, values = self.get_query(
1.197 - "delete from objects :condition",
1.198 - columns, values)
1.199 -
1.200 - self.cursor.execute(query, values)
1.201 - return self.cursor.rowcount > 0
1.202 -
1.203 - def get_active_recurrences(self, user, uid):
1.204 -
1.205 - """
1.206 - Get additional event instances for an event of the given 'user' with the
1.207 - indicated 'uid'. Cancelled recurrences are not returned.
1.208 - """
1.209 -
1.210 - columns = ["store_user", "object_uid", "status"]
1.211 - values = [user, uid, "active"]
1.212 -
1.213 - query, values = self.get_query(
1.214 - "select object_recurrenceid from recurrences :condition",
1.215 - columns, values)
1.216 -
1.217 - self.cursor.execute(query, values)
1.218 - return [t[0] for t in self.cursor.fetchall() or []]
1.219 -
1.220 - def get_cancelled_recurrences(self, user, uid):
1.221 -
1.222 - """
1.223 - Get additional event instances for an event of the given 'user' with the
1.224 - indicated 'uid'. Only cancelled recurrences are returned.
1.225 - """
1.226 -
1.227 - columns = ["store_user", "object_uid", "status"]
1.228 - values = [user, uid, "cancelled"]
1.229 -
1.230 - query, values = self.get_query(
1.231 - "select object_recurrenceid from recurrences :condition",
1.232 - columns, values)
1.233 -
1.234 - self.cursor.execute(query, values)
1.235 - return [t[0] for t in self.cursor.fetchall() or []]
1.236 -
1.237 - def get_recurrence_table(self, dirname=None):
1.238 -
1.239 - "Get the table providing recurrences for any specified 'dirname'."
1.240 -
1.241 - if dirname == "counters":
1.242 - return "countered_recurrences"
1.243 - else:
1.244 - return "recurrences"
1.245 -
1.246 - def get_recurrence(self, user, uid, recurrenceid):
1.247 -
1.248 - """
1.249 - For the event of the given 'user' with the given 'uid', return the
1.250 - specific recurrence indicated by the 'recurrenceid'.
1.251 - """
1.252 -
1.253 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.254 - values = [user, uid, recurrenceid]
1.255 -
1.256 - query, values = self.get_query(
1.257 - "select object_text from recurrences :condition",
1.258 - columns, values)
1.259 -
1.260 - self.cursor.execute(query, values)
1.261 - result = self.cursor.fetchone()
1.262 - return result and parse_string(result[0], "utf-8")
1.263 -
1.264 - def set_recurrence(self, user, uid, recurrenceid, node):
1.265 -
1.266 - "Set an event for 'user' having the given 'uid' and 'node'."
1.267 -
1.268 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.269 - values = [user, uid, recurrenceid]
1.270 - setcolumns = ["object_text", "status"]
1.271 - setvalues = [to_string(node, "utf-8"), "active"]
1.272 -
1.273 - query, values = self.get_query(
1.274 - "update recurrences :set :condition",
1.275 - columns, values, setcolumns, setvalues)
1.276 -
1.277 - self.cursor.execute(query, values)
1.278 -
1.279 - if self.cursor.rowcount > 0 or self.get_recurrence(user, uid, recurrenceid):
1.280 - return True
1.281 -
1.282 - columns = ["store_user", "object_uid", "object_recurrenceid", "object_text", "status"]
1.283 - values = [user, uid, recurrenceid, to_string(node, "utf-8"), "active"]
1.284 -
1.285 - query, values = self.get_query(
1.286 - "insert into recurrences (:columns) values (:values)",
1.287 - columns, values)
1.288 -
1.289 - self.cursor.execute(query, values)
1.290 - return True
1.291 -
1.292 - def remove_recurrence(self, user, uid, recurrenceid):
1.293 -
1.294 - """
1.295 - Remove a special recurrence from an event stored by 'user' having the
1.296 - given 'uid' and 'recurrenceid'.
1.297 - """
1.298 -
1.299 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.300 - values = [user, uid, recurrenceid]
1.301 -
1.302 - query, values = self.get_query(
1.303 - "delete from recurrences :condition",
1.304 - columns, values)
1.305 -
1.306 - self.cursor.execute(query, values)
1.307 - return True
1.308 -
1.309 - def remove_recurrences(self, user, uid):
1.310 -
1.311 - """
1.312 - Remove all recurrences for an event stored by 'user' having the given
1.313 - 'uid'.
1.314 - """
1.315 -
1.316 - columns = ["store_user", "object_uid"]
1.317 - values = [user, uid]
1.318 -
1.319 - query, values = self.get_query(
1.320 - "delete from recurrences :condition",
1.321 - columns, values)
1.322 -
1.323 - self.cursor.execute(query, values)
1.324 - return True
1.325 -
1.326 - # Free/busy period providers, upon extension of the free/busy records.
1.327 -
1.328 - def _get_freebusy_providers(self, user):
1.329 -
1.330 - """
1.331 - Return the free/busy providers for the given 'user'.
1.332 -
1.333 - This function returns any stored datetime and a list of providers as a
1.334 - 2-tuple. Each provider is itself a (uid, recurrenceid) tuple.
1.335 - """
1.336 -
1.337 - columns = ["store_user"]
1.338 - values = [user]
1.339 -
1.340 - query, values = self.get_query(
1.341 - "select object_uid, object_recurrenceid from freebusy_providers :condition",
1.342 - columns, values)
1.343 -
1.344 - self.cursor.execute(query, values)
1.345 - providers = self.cursor.fetchall()
1.346 -
1.347 - columns = ["store_user"]
1.348 - values = [user]
1.349 -
1.350 - query, values = self.get_query(
1.351 - "select start from freebusy_provider_datetimes :condition",
1.352 - columns, values)
1.353 -
1.354 - self.cursor.execute(query, values)
1.355 - result = self.cursor.fetchone()
1.356 - dt_string = result and result[0]
1.357 -
1.358 - return dt_string, providers
1.359 -
1.360 - def _set_freebusy_providers(self, user, dt_string, t):
1.361 -
1.362 - "Set the given provider timestamp 'dt_string' and table 't'."
1.363 -
1.364 - # NOTE: Locking?
1.365 -
1.366 - columns = ["store_user"]
1.367 - values = [user]
1.368 -
1.369 - query, values = self.get_query(
1.370 - "delete from freebusy_providers :condition",
1.371 - columns, values)
1.372 -
1.373 - self.cursor.execute(query, values)
1.374 -
1.375 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.376 -
1.377 - for uid, recurrenceid in t:
1.378 - values = [user, uid, recurrenceid]
1.379 -
1.380 - query, values = self.get_query(
1.381 - "insert into freebusy_providers (:columns) values (:values)",
1.382 - columns, values)
1.383 -
1.384 - self.cursor.execute(query, values)
1.385 -
1.386 - columns = ["store_user"]
1.387 - values = [user]
1.388 - setcolumns = ["start"]
1.389 - setvalues = [dt_string]
1.390 -
1.391 - query, values = self.get_query(
1.392 - "update freebusy_provider_datetimes :set :condition",
1.393 - columns, values, setcolumns, setvalues)
1.394 -
1.395 - self.cursor.execute(query, values)
1.396 -
1.397 - if self.cursor.rowcount > 0:
1.398 - return True
1.399 -
1.400 - columns = ["store_user", "start"]
1.401 - values = [user, dt_string]
1.402 -
1.403 - query, values = self.get_query(
1.404 - "insert into freebusy_provider_datetimes (:columns) values (:values)",
1.405 - columns, values)
1.406 -
1.407 - self.cursor.execute(query, values)
1.408 - return True
1.409 -
1.410 - # Free/busy period access.
1.411 -
1.412 - def get_freebusy(self, user, name=None, mutable=False):
1.413 -
1.414 - "Get free/busy details for the given 'user'."
1.415 -
1.416 - table = name or "freebusy"
1.417 - return FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], mutable, self.paramstyle)
1.418 -
1.419 - def get_freebusy_for_other(self, user, other, mutable=False):
1.420 -
1.421 - "For the given 'user', get free/busy details for the 'other' user."
1.422 -
1.423 - table = "freebusy"
1.424 - return FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], mutable, self.paramstyle)
1.425 -
1.426 - def set_freebusy(self, user, freebusy, name=None):
1.427 -
1.428 - "For the given 'user', set 'freebusy' details."
1.429 -
1.430 - table = name or "freebusy"
1.431 -
1.432 - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
1.433 - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user"], [user], True, self.paramstyle)
1.434 - fbc += freebusy
1.435 -
1.436 - return True
1.437 -
1.438 - def set_freebusy_for_other(self, user, freebusy, other):
1.439 -
1.440 - "For the given 'user', set 'freebusy' details for the 'other' user."
1.441 -
1.442 - table = "freebusy"
1.443 -
1.444 - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
1.445 - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["store_user", "other"], [user, other], True, self.paramstyle)
1.446 - fbc += freebusy
1.447 -
1.448 - return True
1.449 -
1.450 - # Tentative free/busy periods related to countering.
1.451 -
1.452 - def get_freebusy_offers(self, user, mutable=False):
1.453 -
1.454 - "Get free/busy offers for the given 'user'."
1.455 -
1.456 - # Expire old offers and save the collection if modified.
1.457 -
1.458 - now = format_datetime(to_timezone(datetime.utcnow(), "UTC"))
1.459 - columns = ["store_user", "expires"]
1.460 - values = [user, now]
1.461 -
1.462 - query, values = self.get_query(
1.463 - "delete from freebusy_offers :condition",
1.464 - columns, values)
1.465 -
1.466 - self.cursor.execute(query, values)
1.467 -
1.468 - return self.get_freebusy(user, "freebusy_offers", mutable)
1.469 -
1.470 - # Requests and counter-proposals.
1.471 -
1.472 - def get_requests(self, user):
1.473 -
1.474 - "Get requests for the given 'user'."
1.475 -
1.476 - columns = ["store_user"]
1.477 - values = [user]
1.478 -
1.479 - query, values = self.get_query(
1.480 - "select object_uid, object_recurrenceid from requests :condition",
1.481 - columns, values)
1.482 -
1.483 - self.cursor.execute(query, values)
1.484 - return self.cursor.fetchall()
1.485 -
1.486 - def set_requests(self, user, requests):
1.487 -
1.488 - "For the given 'user', set the list of queued 'requests'."
1.489 -
1.490 - # NOTE: Locking?
1.491 -
1.492 - columns = ["store_user"]
1.493 - values = [user]
1.494 -
1.495 - query, values = self.get_query(
1.496 - "delete from requests :condition",
1.497 - columns, values)
1.498 -
1.499 - self.cursor.execute(query, values)
1.500 -
1.501 - for uid, recurrenceid, type in requests:
1.502 - columns = ["store_user", "object_uid", "object_recurrenceid", "request_type"]
1.503 - values = [user, uid, recurrenceid, type]
1.504 -
1.505 - query, values = self.get_query(
1.506 - "insert into requests (:columns) values (:values)",
1.507 - columns, values)
1.508 -
1.509 - self.cursor.execute(query, values)
1.510 -
1.511 - return True
1.512 -
1.513 - def set_request(self, user, uid, recurrenceid=None, type=None):
1.514 -
1.515 - """
1.516 - For the given 'user', set the queued 'uid' and 'recurrenceid',
1.517 - indicating a request, along with any given 'type'.
1.518 - """
1.519 -
1.520 - columns = ["store_user", "object_uid", "object_recurrenceid", "request_type"]
1.521 - values = [user, uid, recurrenceid, type]
1.522 -
1.523 - query, values = self.get_query(
1.524 - "insert into requests (:columns) values (:values)",
1.525 - columns, values)
1.526 -
1.527 - self.cursor.execute(query, values)
1.528 - return True
1.529 -
1.530 - def get_counters(self, user, uid, recurrenceid=None):
1.531 -
1.532 - """
1.533 - For the given 'user', return a list of users from whom counter-proposals
1.534 - have been received for the given 'uid' and optional 'recurrenceid'.
1.535 - """
1.536 -
1.537 - table = self.get_event_table(recurrenceid, "counters")
1.538 -
1.539 - if recurrenceid:
1.540 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.541 - values = [user, uid, recurrenceid]
1.542 - else:
1.543 - columns = ["store_user", "object_uid"]
1.544 - values = [user, uid]
1.545 -
1.546 - query, values = self.get_query(
1.547 - "select other from %(table)s :condition" % {
1.548 - "table" : table
1.549 - },
1.550 - columns, values)
1.551 -
1.552 - self.cursor.execute(query, values)
1.553 - return self.cursor.fetchall()
1.554 -
1.555 - def get_counter(self, user, other, uid, recurrenceid=None):
1.556 -
1.557 - """
1.558 - For the given 'user', return the counter-proposal from 'other' for the
1.559 - given 'uid' and optional 'recurrenceid'.
1.560 - """
1.561 -
1.562 - table = self.get_event_table(recurrenceid, "counters")
1.563 -
1.564 - if recurrenceid:
1.565 - columns = ["store_user", "other", "object_uid", "object_recurrenceid"]
1.566 - values = [user, other, uid, recurrenceid]
1.567 - else:
1.568 - columns = ["store_user", "other", "object_uid"]
1.569 - values = [user, other, uid]
1.570 -
1.571 - query, values = self.get_query(
1.572 - "select object_text from %(table)s :condition" % {
1.573 - "table" : table
1.574 - },
1.575 - columns, values)
1.576 -
1.577 - self.cursor.execute(query, values)
1.578 - result = self.cursor.fetchall()
1.579 - return result and parse_string(result[0], "utf-8")
1.580 -
1.581 - def set_counter(self, user, other, node, uid, recurrenceid=None):
1.582 -
1.583 - """
1.584 - For the given 'user', store a counter-proposal received from 'other' the
1.585 - given 'node' representing that proposal for the given 'uid' and
1.586 - 'recurrenceid'.
1.587 - """
1.588 -
1.589 - table = self.get_event_table(recurrenceid, "counters")
1.590 -
1.591 - columns = ["store_user", "other", "object_uid", "object_recurrenceid", "object_text"]
1.592 - values = [user, other, uid, recurrenceid, to_string(node, "utf-8")]
1.593 -
1.594 - query, values = self.get_query(
1.595 - "insert into %(table)s (:columns) values (:values)" % {
1.596 - "table" : table
1.597 - },
1.598 - columns, values)
1.599 -
1.600 - self.cursor.execute(query, values)
1.601 - return True
1.602 -
1.603 - def remove_counters(self, user, uid, recurrenceid=None):
1.604 -
1.605 - """
1.606 - For the given 'user', remove all counter-proposals associated with the
1.607 - given 'uid' and 'recurrenceid'.
1.608 - """
1.609 -
1.610 - table = self.get_event_table(recurrenceid, "counters")
1.611 -
1.612 - if recurrenceid:
1.613 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.614 - values = [user, uid, recurrenceid]
1.615 - else:
1.616 - columns = ["store_user", "object_uid"]
1.617 - values = [user, uid]
1.618 -
1.619 - query, values = self.get_query(
1.620 - "delete from %(table)s :condition" % {
1.621 - "table" : table
1.622 - },
1.623 - columns, values)
1.624 -
1.625 - self.cursor.execute(query, values)
1.626 - return True
1.627 -
1.628 - def remove_counter(self, user, other, uid, recurrenceid=None):
1.629 -
1.630 - """
1.631 - For the given 'user', remove any counter-proposal from 'other'
1.632 - associated with the given 'uid' and 'recurrenceid'.
1.633 - """
1.634 -
1.635 - table = self.get_event_table(recurrenceid, "counters")
1.636 -
1.637 - if recurrenceid:
1.638 - columns = ["store_user", "other", "object_uid", "object_recurrenceid"]
1.639 - values = [user, other, uid, recurrenceid]
1.640 - else:
1.641 - columns = ["store_user", "other", "object_uid"]
1.642 - values = [user, other, uid]
1.643 -
1.644 - query, values = self.get_query(
1.645 - "delete from %(table)s :condition" % {
1.646 - "table" : table
1.647 - },
1.648 - columns, values)
1.649 -
1.650 - self.cursor.execute(query, values)
1.651 - return True
1.652 -
1.653 - # Event cancellation.
1.654 -
1.655 - def cancel_event(self, user, uid, recurrenceid=None):
1.656 -
1.657 - """
1.658 - Cancel an event for 'user' having the given 'uid'. If the optional
1.659 - 'recurrenceid' is specified, a specific instance or occurrence of an
1.660 - event is cancelled.
1.661 - """
1.662 -
1.663 - table = self.get_event_table(recurrenceid)
1.664 -
1.665 - if recurrenceid:
1.666 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.667 - values = [user, uid, recurrenceid]
1.668 - else:
1.669 - columns = ["store_user", "object_uid"]
1.670 - values = [user, uid]
1.671 -
1.672 - setcolumns = ["status"]
1.673 - setvalues = ["cancelled"]
1.674 -
1.675 - query, values = self.get_query(
1.676 - "update %(table)s :set :condition" % {
1.677 - "table" : table
1.678 - },
1.679 - columns, values, setcolumns, setvalues)
1.680 -
1.681 - self.cursor.execute(query, values)
1.682 - return True
1.683 -
1.684 - def uncancel_event(self, user, uid, recurrenceid=None):
1.685 -
1.686 - """
1.687 - Uncancel an event for 'user' having the given 'uid'. If the optional
1.688 - 'recurrenceid' is specified, a specific instance or occurrence of an
1.689 - event is uncancelled.
1.690 - """
1.691 -
1.692 - table = self.get_event_table(recurrenceid)
1.693 -
1.694 - if recurrenceid:
1.695 - columns = ["store_user", "object_uid", "object_recurrenceid"]
1.696 - values = [user, uid, recurrenceid]
1.697 - else:
1.698 - columns = ["store_user", "object_uid"]
1.699 - values = [user, uid]
1.700 -
1.701 - setcolumns = ["status"]
1.702 - setvalues = ["active"]
1.703 -
1.704 - query, values = self.get_query(
1.705 - "update %(table)s :set :condition" % {
1.706 - "table" : table
1.707 - },
1.708 - columns, values, setcolumns, setvalues)
1.709 -
1.710 - self.cursor.execute(query, values)
1.711 - return True
1.712 -
1.713 - def remove_cancellation(self, user, uid, recurrenceid=None):
1.714 -
1.715 - """
1.716 - Remove a cancellation for 'user' for the event having the given 'uid'.
1.717 - If the optional 'recurrenceid' is specified, a specific instance or
1.718 - occurrence of an event is affected.
1.719 - """
1.720 -
1.721 - table = self.get_event_table(recurrenceid)
1.722 -
1.723 - if recurrenceid:
1.724 - columns = ["store_user", "object_uid", "object_recurrenceid", "status"]
1.725 - values = [user, uid, recurrenceid, "cancelled"]
1.726 - else:
1.727 - columns = ["store_user", "object_uid", "status"]
1.728 - values = [user, uid, "cancelled"]
1.729 -
1.730 - query, values = self.get_query(
1.731 - "delete from %(table)s :condition" % {
1.732 - "table" : table
1.733 - },
1.734 - columns, values)
1.735 -
1.736 - self.cursor.execute(query, values)
1.737 - return True
1.738 -
1.739 -class DatabaseJournal(DatabaseStoreBase, JournalBase):
1.740 -
1.741 - "A journal system to support quotas."
1.742 -
1.743 - # Quota and user identity/group discovery.
1.744 -
1.745 - def get_quotas(self):
1.746 -
1.747 - "Return a list of quotas."
1.748 -
1.749 - query = "select distinct journal_quota from quota_freebusy"
1.750 - self.cursor.execute(query)
1.751 - return [r[0] for r in self.cursor.fetchall()]
1.752 -
1.753 - def get_quota_users(self, quota):
1.754 -
1.755 - "Return a list of quota users."
1.756 -
1.757 - columns = ["quota"]
1.758 - values = [quota]
1.759 -
1.760 - query, values = self.get_query(
1.761 - "select distinct user_group from quota_freebusy :condition",
1.762 - columns, values)
1.763 -
1.764 - self.cursor.execute(query)
1.765 - return [r[0] for r in self.cursor.fetchall()]
1.766 -
1.767 - # Groups of users sharing quotas.
1.768 -
1.769 - def get_groups(self, quota):
1.770 -
1.771 - "Return the identity mappings for the given 'quota' as a dictionary."
1.772 -
1.773 - columns = ["quota"]
1.774 - values = [quota]
1.775 -
1.776 - query, values = self.get_query(
1.777 - "select store_user, user_group from user_groups :condition",
1.778 - columns, values)
1.779 -
1.780 - self.cursor.execute(query)
1.781 - return dict(self.cursor.fetchall())
1.782 -
1.783 - def get_limits(self, quota):
1.784 -
1.785 - """
1.786 - Return the limits for the 'quota' as a dictionary mapping identities or
1.787 - groups to durations.
1.788 - """
1.789 -
1.790 - columns = ["quota"]
1.791 - values = [quota]
1.792 -
1.793 - query, values = self.get_query(
1.794 - "select user_group, quota_limit from quota_limits :condition",
1.795 - columns, values)
1.796 -
1.797 - self.cursor.execute(query)
1.798 - return dict(self.cursor.fetchall())
1.799 -
1.800 - # Free/busy period access for users within quota groups.
1.801 -
1.802 - def get_freebusy(self, quota, user, mutable=False):
1.803 -
1.804 - "Get free/busy details for the given 'quota' and 'user'."
1.805 -
1.806 - table = "user_freebusy"
1.807 - return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], mutable, self.paramstyle)
1.808 -
1.809 - def set_freebusy(self, quota, user, freebusy):
1.810 -
1.811 - "For the given 'quota' and 'user', set 'freebusy' details."
1.812 -
1.813 - table = "user_freebusy"
1.814 -
1.815 - if not isinstance(freebusy, FreeBusyDatabaseCollection) or freebusy.table_name != table:
1.816 - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "store_user"], [quota, user], True, self.paramstyle)
1.817 - fbc += freebusy
1.818 -
1.819 - return True
1.820 -
1.821 - # Journal entry methods.
1.822 -
1.823 - def get_entries(self, quota, group, mutable=False):
1.824 -
1.825 - """
1.826 - Return a list of journal entries for the given 'quota' for the indicated
1.827 - 'group'.
1.828 - """
1.829 -
1.830 - table = "quota_freebusy"
1.831 - return FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], mutable, self.paramstyle)
1.832 -
1.833 - def set_entries(self, quota, group, entries):
1.834 -
1.835 - """
1.836 - For the given 'quota' and indicated 'group', set the list of journal
1.837 - 'entries'.
1.838 - """
1.839 -
1.840 - table = "quota_freebusy"
1.841 -
1.842 - if not isinstance(entries, FreeBusyDatabaseCollection) or entries.table_name != table:
1.843 - fbc = FreeBusyDatabaseCollection(self.cursor, table, ["quota", "user_group"], [quota, group], True, self.paramstyle)
1.844 - fbc += entries
1.845 -
1.846 - return True
1.847 -
1.848 -# vim: tabstop=4 expandtab shiftwidth=4