imip-agent

Annotated imiptools/stores/common.py

1448:be8ebc16d775
2018-01-21 Paul Boddie Support the loading of recurrences that need to be separated from a parent. client-editing-simplification
paul@1069 1
#!/usr/bin/env python
paul@1069 2
paul@1069 3
"""
paul@1069 4
General support for calendar data storage.
paul@1069 5
paul@1301 6
Copyright (C) 2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
paul@1069 7
paul@1069 8
This program is free software; you can redistribute it and/or modify it under
paul@1069 9
the terms of the GNU General Public License as published by the Free Software
paul@1069 10
Foundation; either version 3 of the License, or (at your option) any later
paul@1069 11
version.
paul@1069 12
paul@1069 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@1069 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@1069 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@1069 16
details.
paul@1069 17
paul@1069 18
You should have received a copy of the GNU General Public License along with
paul@1069 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@1069 20
"""
paul@1069 21
paul@1089 22
from imiptools.dates import format_datetime, get_datetime
paul@1075 23
paul@1340 24
class StoreInitialisationError(Exception):
paul@1340 25
paul@1340 26
    "An error occurring when the initialisation of a store fails."
paul@1340 27
paul@1340 28
    pass
paul@1340 29
paul@1069 30
class StoreBase:
paul@1069 31
paul@1069 32
    "The core operations of a data store."
paul@1069 33
paul@1069 34
    # User discovery.
paul@1069 35
paul@1069 36
    def get_users(self):
paul@1069 37
paul@1069 38
        "Return a list of users."
paul@1069 39
paul@1069 40
        pass
paul@1069 41
paul@1069 42
    # Event and event metadata access.
paul@1069 43
paul@1142 44
    def get_all_events(self, user, dirname=None):
paul@1142 45
paul@1142 46
        """
paul@1142 47
        Return a set of (uid, recurrenceid) tuples for all events. Unless
paul@1142 48
        'dirname' is specified, only active events are returned; otherwise,
paul@1142 49
        events from the given 'dirname' are returned.
paul@1142 50
        """
paul@1142 51
paul@1142 52
        cancelled = self.get_cancelled_events(user)
paul@1142 53
        active = self.get_events(user)
paul@1142 54
paul@1142 55
        if dirname == "cancellations":
paul@1142 56
            uids = cancelled
paul@1142 57
        else:
paul@1142 58
            uids = active
paul@1142 59
paul@1142 60
        if not uids:
paul@1142 61
            return set()
paul@1142 62
paul@1142 63
        all_events = set()
paul@1142 64
paul@1142 65
        # Add all qualifying parent events to the result set.
paul@1142 66
paul@1142 67
        for uid in uids:
paul@1142 68
            all_events.add((uid, None))
paul@1142 69
paul@1142 70
        # Process all parent events regardless of status to find those in the
paul@1142 71
        # category/directory of interest.
paul@1142 72
paul@1142 73
        for uid in active + cancelled:
paul@1142 74
paul@1142 75
            if dirname == "cancellations":
paul@1142 76
                recurrenceids = self.get_cancelled_recurrences(user, uid)
paul@1142 77
            else:
paul@1142 78
                recurrenceids = self.get_active_recurrences(user, uid)
paul@1142 79
paul@1142 80
            all_events.update([(uid, recurrenceid) for recurrenceid in recurrenceids])
paul@1142 81
paul@1142 82
        return all_events
paul@1142 83
paul@1069 84
    def get_events(self, user):
paul@1069 85
paul@1069 86
        "Return a list of event identifiers."
paul@1069 87
paul@1069 88
        pass
paul@1069 89
paul@1142 90
    def get_cancelled_events(self, user):
paul@1069 91
paul@1142 92
        "Return a list of event identifiers for cancelled events."
paul@1069 93
paul@1142 94
        pass
paul@1069 95
paul@1069 96
    def get_event(self, user, uid, recurrenceid=None, dirname=None):
paul@1069 97
paul@1069 98
        """
paul@1069 99
        Get the event for the given 'user' with the given 'uid'. If
paul@1069 100
        the optional 'recurrenceid' is specified, a specific instance or
paul@1069 101
        occurrence of an event is returned.
paul@1069 102
        """
paul@1069 103
paul@1069 104
        pass
paul@1069 105
paul@1069 106
    def set_event(self, user, uid, recurrenceid, node):
paul@1069 107
paul@1069 108
        """
paul@1069 109
        Set an event for 'user' having the given 'uid' and 'recurrenceid' (which
paul@1069 110
        if the latter is specified, a specific instance or occurrence of an
paul@1069 111
        event is referenced), using the given 'node' description.
paul@1069 112
        """
paul@1069 113
paul@1069 114
        if recurrenceid:
paul@1069 115
            return self.set_recurrence(user, uid, recurrenceid, node)
paul@1069 116
        else:
paul@1324 117
            return self.set_parent_event(user, uid, node)
paul@1069 118
paul@1324 119
    def set_parent_event(self, user, uid, node):
paul@1069 120
paul@1069 121
        "Set an event for 'user' having the given 'uid' and 'node'."
paul@1069 122
paul@1069 123
        pass
paul@1069 124
paul@1069 125
    def remove_event(self, user, uid, recurrenceid=None):
paul@1069 126
paul@1069 127
        """
paul@1069 128
        Remove an event for 'user' having the given 'uid'. If the optional
paul@1069 129
        'recurrenceid' is specified, a specific instance or occurrence of an
paul@1069 130
        event is removed.
paul@1069 131
        """
paul@1069 132
paul@1069 133
        if recurrenceid:
paul@1069 134
            return self.remove_recurrence(user, uid, recurrenceid)
paul@1069 135
        else:
paul@1069 136
            for recurrenceid in self.get_recurrences(user, uid) or []:
paul@1069 137
                self.remove_recurrence(user, uid, recurrenceid)
paul@1069 138
            return self.remove_complete_event(user, uid)
paul@1069 139
paul@1069 140
    def remove_complete_event(self, user, uid):
paul@1069 141
paul@1069 142
        "Remove an event for 'user' having the given 'uid'."
paul@1069 143
paul@1069 144
        self.remove_recurrences(user, uid)
paul@1069 145
        return self.remove_parent_event(user, uid)
paul@1069 146
paul@1069 147
    def remove_parent_event(self, user, uid):
paul@1069 148
paul@1069 149
        "Remove the parent event for 'user' having the given 'uid'."
paul@1069 150
paul@1069 151
        pass
paul@1069 152
paul@1069 153
    def get_recurrences(self, user, uid):
paul@1069 154
paul@1069 155
        """
paul@1069 156
        Get additional event instances for an event of the given 'user' with the
paul@1069 157
        indicated 'uid'. Both active and cancelled recurrences are returned.
paul@1069 158
        """
paul@1069 159
paul@1069 160
        return self.get_active_recurrences(user, uid) + self.get_cancelled_recurrences(user, uid)
paul@1069 161
paul@1069 162
    def get_active_recurrences(self, user, uid):
paul@1069 163
paul@1069 164
        """
paul@1069 165
        Get additional event instances for an event of the given 'user' with the
paul@1069 166
        indicated 'uid'. Cancelled recurrences are not returned.
paul@1069 167
        """
paul@1069 168
paul@1069 169
        pass
paul@1069 170
paul@1069 171
    def get_cancelled_recurrences(self, user, uid):
paul@1069 172
paul@1069 173
        """
paul@1069 174
        Get additional event instances for an event of the given 'user' with the
paul@1069 175
        indicated 'uid'. Only cancelled recurrences are returned.
paul@1069 176
        """
paul@1069 177
paul@1069 178
        pass
paul@1069 179
paul@1069 180
    def get_recurrence(self, user, uid, recurrenceid):
paul@1069 181
paul@1069 182
        """
paul@1069 183
        For the event of the given 'user' with the given 'uid', return the
paul@1069 184
        specific recurrence indicated by the 'recurrenceid'.
paul@1069 185
        """
paul@1069 186
paul@1069 187
        pass
paul@1069 188
paul@1069 189
    def set_recurrence(self, user, uid, recurrenceid, node):
paul@1069 190
paul@1069 191
        "Set an event for 'user' having the given 'uid' and 'node'."
paul@1069 192
paul@1069 193
        pass
paul@1069 194
paul@1069 195
    def remove_recurrence(self, user, uid, recurrenceid):
paul@1069 196
paul@1069 197
        """
paul@1069 198
        Remove a special recurrence from an event stored by 'user' having the
paul@1069 199
        given 'uid' and 'recurrenceid'.
paul@1069 200
        """
paul@1069 201
paul@1069 202
        pass
paul@1069 203
paul@1069 204
    def remove_recurrences(self, user, uid):
paul@1069 205
paul@1069 206
        """
paul@1069 207
        Remove all recurrences for an event stored by 'user' having the given
paul@1069 208
        'uid'.
paul@1069 209
        """
paul@1069 210
paul@1069 211
        for recurrenceid in self.get_recurrences(user, uid):
paul@1069 212
            self.remove_recurrence(user, uid, recurrenceid)
paul@1069 213
paul@1069 214
        return self.remove_recurrence_collection(user, uid)
paul@1069 215
paul@1069 216
    def remove_recurrence_collection(self, user, uid):
paul@1069 217
paul@1069 218
        """
paul@1069 219
        Remove the collection of recurrences stored by 'user' having the given
paul@1069 220
        'uid'.
paul@1069 221
        """
paul@1069 222
paul@1069 223
        pass
paul@1069 224
paul@1330 225
    def update_event_from_recurrences(self, user, obj):
paul@1330 226
paul@1330 227
        """
paul@1330 228
        Apply separate recurrence information to the event stored by 'user'
paul@1330 229
        represented by 'obj'. Recurrences cannot be updated in this way and are
paul@1330 230
        not modified by this method.
paul@1330 231
        """
paul@1330 232
paul@1330 233
        if obj.get_recurrenceid():
paul@1330 234
            return
paul@1330 235
paul@1330 236
        uid = obj.get_uid()
paul@1330 237
paul@1330 238
        if not obj.modifying:
paul@1347 239
            objects = []
paul@1347 240
            for recurrenceid in self.get_active_recurrences(user, uid):
paul@1347 241
                objects.append(self.get_event(user, uid, recurrenceid))
paul@1347 242
            obj.set_modifying(objects)
paul@1347 243
paul@1330 244
        if not obj.cancelling:
paul@1347 245
            objects = []
paul@1347 246
            for recurrenceid in self.get_cancelled_recurrences(user, uid):
paul@1347 247
                objects.append(self.get_event(user, uid, recurrenceid, "cancellations"))
paul@1347 248
            obj.set_cancelling(objects)
paul@1330 249
paul@1069 250
    # Free/busy period providers, upon extension of the free/busy records.
paul@1069 251
paul@1075 252
    def _get_freebusy_providers(self, user):
paul@1075 253
paul@1075 254
        """
paul@1075 255
        Return the free/busy providers for the given 'user'.
paul@1075 256
paul@1075 257
        This function returns any stored datetime and a list of providers as a
paul@1075 258
        2-tuple. Each provider is itself a (uid, recurrenceid) tuple.
paul@1075 259
        """
paul@1075 260
paul@1075 261
        pass
paul@1075 262
paul@1069 263
    def get_freebusy_providers(self, user, dt=None):
paul@1069 264
paul@1069 265
        """
paul@1069 266
        Return a set of uncancelled events of the form (uid, recurrenceid)
paul@1069 267
        providing free/busy details beyond the given datetime 'dt'.
paul@1069 268
paul@1069 269
        If 'dt' is not specified, all events previously found to provide
paul@1069 270
        details will be returned. Otherwise, if 'dt' is earlier than the
paul@1069 271
        datetime recorded for the known providers, None is returned, indicating
paul@1069 272
        that the list of providers must be recomputed.
paul@1069 273
paul@1069 274
        This function returns a list of (uid, recurrenceid) tuples upon success.
paul@1069 275
        """
paul@1069 276
paul@1075 277
        t = self._get_freebusy_providers(user)
paul@1075 278
        if not t:
paul@1075 279
            return None
paul@1075 280
paul@1075 281
        dt_string, t = t
paul@1075 282
paul@1075 283
        # If the requested datetime is earlier than the stated datetime, the
paul@1075 284
        # providers will need to be recomputed.
paul@1075 285
paul@1075 286
        if dt:
paul@1075 287
            providers_dt = get_datetime(dt_string)
paul@1075 288
            if not providers_dt or providers_dt > dt:
paul@1075 289
                return None
paul@1075 290
paul@1075 291
        # Otherwise, return the providers.
paul@1075 292
paul@1089 293
        return t
paul@1075 294
paul@1075 295
    def _set_freebusy_providers(self, user, dt_string, t):
paul@1075 296
paul@1075 297
        "Set the given provider timestamp 'dt_string' and table 't'."
paul@1075 298
paul@1069 299
        pass
paul@1069 300
paul@1069 301
    def set_freebusy_providers(self, user, dt, providers):
paul@1069 302
paul@1069 303
        """
paul@1069 304
        Define the uncancelled events providing free/busy details beyond the
paul@1069 305
        given datetime 'dt'.
paul@1069 306
        """
paul@1069 307
paul@1075 308
        t = []
paul@1075 309
paul@1075 310
        for obj in providers:
paul@1075 311
            t.append((obj.get_uid(), obj.get_recurrenceid()))
paul@1075 312
paul@1075 313
        return self._set_freebusy_providers(user, format_datetime(dt), t)
paul@1069 314
paul@1069 315
    def append_freebusy_provider(self, user, provider):
paul@1069 316
paul@1069 317
        "For the given 'user', append the free/busy 'provider'."
paul@1069 318
paul@1075 319
        t = self._get_freebusy_providers(user)
paul@1075 320
        if not t:
paul@1075 321
            return False
paul@1075 322
paul@1075 323
        dt_string, t = t
paul@1197 324
        details = (provider.get_uid(), provider.get_recurrenceid())
paul@1197 325
paul@1197 326
        if not details in t:
paul@1197 327
            t.append(details)
paul@1075 328
paul@1075 329
        return self._set_freebusy_providers(user, dt_string, t)
paul@1069 330
paul@1069 331
    def remove_freebusy_provider(self, user, provider):
paul@1069 332
paul@1069 333
        "For the given 'user', remove the free/busy 'provider'."
paul@1069 334
paul@1075 335
        t = self._get_freebusy_providers(user)
paul@1075 336
        if not t:
paul@1075 337
            return False
paul@1075 338
paul@1075 339
        dt_string, t = t
paul@1075 340
        try:
paul@1075 341
            t.remove((provider.get_uid(), provider.get_recurrenceid()))
paul@1075 342
        except ValueError:
paul@1075 343
            return False
paul@1075 344
paul@1075 345
        return self._set_freebusy_providers(user, dt_string, t)
paul@1069 346
paul@1069 347
    # Free/busy period access.
paul@1069 348
paul@1077 349
    def get_freebusy(self, user, name=None, mutable=False):
paul@1069 350
paul@1069 351
        "Get free/busy details for the given 'user'."
paul@1069 352
paul@1069 353
        pass
paul@1069 354
paul@1077 355
    def get_freebusy_for_other(self, user, other, mutable=False):
paul@1069 356
paul@1069 357
        "For the given 'user', get free/busy details for the 'other' user."
paul@1069 358
paul@1069 359
        pass
paul@1069 360
paul@1071 361
    def get_freebusy_for_update(self, user, name=None):
paul@1071 362
paul@1142 363
        """
paul@1142 364
        Get free/busy details for the given 'user' that may be updated,
paul@1142 365
        potentially affecting the stored information directly.
paul@1142 366
        """
paul@1071 367
paul@1077 368
        return self.get_freebusy(user, name, True)
paul@1071 369
paul@1071 370
    def get_freebusy_for_other_for_update(self, user, other):
paul@1071 371
paul@1142 372
        """
paul@1142 373
        For the given 'user', get free/busy details for the 'other' user
paul@1142 374
        that may be updated, potentially affecting the stored information
paul@1142 375
        directly.
paul@1142 376
        """
paul@1071 377
paul@1077 378
        return self.get_freebusy_for_other(user, other, True)
paul@1071 379
paul@1069 380
    def set_freebusy(self, user, freebusy, name=None):
paul@1069 381
paul@1069 382
        "For the given 'user', set 'freebusy' details."
paul@1069 383
paul@1069 384
        pass
paul@1069 385
paul@1069 386
    def set_freebusy_for_other(self, user, freebusy, other):
paul@1069 387
paul@1069 388
        "For the given 'user', set 'freebusy' details for the 'other' user."
paul@1069 389
paul@1069 390
        pass
paul@1069 391
paul@1142 392
    def get_freebusy_others(self, user):
paul@1142 393
paul@1142 394
        """
paul@1142 395
        For the given 'user', return a list of other users for whom free/busy
paul@1142 396
        information is retained.
paul@1142 397
        """
paul@1142 398
paul@1142 399
        pass
paul@1142 400
paul@1069 401
    # Tentative free/busy periods related to countering.
paul@1069 402
paul@1077 403
    def get_freebusy_offers(self, user, mutable=False):
paul@1069 404
paul@1069 405
        "Get free/busy offers for the given 'user'."
paul@1069 406
paul@1069 407
        pass
paul@1069 408
paul@1071 409
    def get_freebusy_offers_for_update(self, user):
paul@1071 410
paul@1142 411
        """
paul@1142 412
        Get free/busy offers for the given 'user' that may be updated,
paul@1142 413
        potentially affecting the stored information directly.
paul@1142 414
        """
paul@1071 415
paul@1077 416
        return self.get_freebusy_offers(user, True)
paul@1071 417
paul@1069 418
    def set_freebusy_offers(self, user, freebusy):
paul@1069 419
paul@1069 420
        "For the given 'user', set 'freebusy' offers."
paul@1069 421
paul@1069 422
        return self.set_freebusy(user, freebusy, "freebusy-offers")
paul@1069 423
paul@1069 424
    # Requests and counter-proposals.
paul@1069 425
paul@1069 426
    def get_requests(self, user):
paul@1069 427
paul@1069 428
        "Get requests for the given 'user'."
paul@1069 429
paul@1069 430
        pass
paul@1069 431
paul@1069 432
    def set_requests(self, user, requests):
paul@1069 433
paul@1069 434
        "For the given 'user', set the list of queued 'requests'."
paul@1069 435
paul@1069 436
        pass
paul@1069 437
paul@1069 438
    def set_request(self, user, uid, recurrenceid=None, type=None):
paul@1069 439
paul@1069 440
        """
paul@1069 441
        For the given 'user', set the queued 'uid' and 'recurrenceid',
paul@1069 442
        indicating a request, along with any given 'type'.
paul@1069 443
        """
paul@1069 444
paul@1069 445
        pass
paul@1069 446
paul@1069 447
    def queue_request(self, user, uid, recurrenceid=None, type=None):
paul@1069 448
paul@1069 449
        """
paul@1069 450
        Queue a request for 'user' having the given 'uid'. If the optional
paul@1069 451
        'recurrenceid' is specified, the entry refers to a specific instance
paul@1069 452
        or occurrence of an event. The 'type' parameter can be used to indicate
paul@1069 453
        a specific type of request.
paul@1069 454
        """
paul@1069 455
paul@1069 456
        requests = self.get_requests(user) or []
paul@1069 457
paul@1069 458
        if not self.have_request(requests, uid, recurrenceid):
paul@1069 459
            return self.set_request(user, uid, recurrenceid, type)
paul@1069 460
paul@1069 461
        return False
paul@1069 462
paul@1069 463
    def dequeue_request(self, user, uid, recurrenceid=None):
paul@1069 464
paul@1069 465
        """
paul@1069 466
        Dequeue all requests for 'user' having the given 'uid'. If the optional
paul@1069 467
        'recurrenceid' is specified, all requests for that specific instance or
paul@1069 468
        occurrence of an event are dequeued.
paul@1069 469
        """
paul@1069 470
paul@1069 471
        requests = self.get_requests(user) or []
paul@1069 472
        result = []
paul@1069 473
paul@1069 474
        for request in requests:
paul@1069 475
            if request[:2] != (uid, recurrenceid):
paul@1069 476
                result.append(request)
paul@1069 477
paul@1069 478
        self.set_requests(user, result)
paul@1069 479
        return True
paul@1069 480
paul@1069 481
    def has_request(self, user, uid, recurrenceid=None, type=None, strict=False):
paul@1069 482
        return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict)
paul@1069 483
paul@1069 484
    def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False):
paul@1069 485
paul@1069 486
        """
paul@1069 487
        Return whether 'requests' contains a request with the given 'uid' and
paul@1069 488
        any specified 'recurrenceid' and 'type'. If 'strict' is set to a true
paul@1069 489
        value, the precise type of the request must match; otherwise, any type
paul@1069 490
        of request for the identified object may be matched.
paul@1069 491
        """
paul@1069 492
paul@1069 493
        for request in requests:
paul@1069 494
            if request[:2] == (uid, recurrenceid) and (
paul@1069 495
                not strict or
paul@1069 496
                not request[2:] and not type or
paul@1069 497
                request[2:] and request[2] == type):
paul@1069 498
paul@1069 499
                return True
paul@1069 500
paul@1069 501
        return False
paul@1069 502
paul@1069 503
    def get_counters(self, user, uid, recurrenceid=None):
paul@1069 504
paul@1069 505
        """
paul@1069 506
        For the given 'user', return a list of users from whom counter-proposals
paul@1069 507
        have been received for the given 'uid' and optional 'recurrenceid'.
paul@1069 508
        """
paul@1069 509
paul@1069 510
        pass
paul@1069 511
paul@1301 512
    def get_counter_recurrences(self, user, uid):
paul@1301 513
paul@1301 514
        """
paul@1301 515
        For the given 'user', return a list of recurrence identifiers describing
paul@1301 516
        counter-proposals for the parent event with the given 'uid'.
paul@1301 517
        """
paul@1301 518
paul@1301 519
        pass
paul@1301 520
paul@1069 521
    def get_counter(self, user, other, uid, recurrenceid=None):
paul@1069 522
paul@1069 523
        """
paul@1069 524
        For the given 'user', return the counter-proposal from 'other' for the
paul@1069 525
        given 'uid' and optional 'recurrenceid'.
paul@1069 526
        """
paul@1069 527
paul@1069 528
        pass
paul@1069 529
paul@1069 530
    def set_counter(self, user, other, node, uid, recurrenceid=None):
paul@1069 531
paul@1069 532
        """
paul@1069 533
        For the given 'user', store a counter-proposal received from 'other' the
paul@1069 534
        given 'node' representing that proposal for the given 'uid' and
paul@1069 535
        'recurrenceid'.
paul@1069 536
        """
paul@1069 537
paul@1069 538
        pass
paul@1069 539
paul@1306 540
    def remove_counters(self, user, uid, recurrenceid=None, attendee=None):
paul@1069 541
paul@1069 542
        """
paul@1069 543
        For the given 'user', remove all counter-proposals associated with the
paul@1306 544
        given 'uid' and 'recurrenceid'. If 'attendee' is specified, only objects
paul@1306 545
        provided by this attendee will be removed.
paul@1069 546
        """
paul@1069 547
paul@1069 548
        pass
paul@1069 549
paul@1069 550
    def remove_counter(self, user, other, uid, recurrenceid=None):
paul@1069 551
paul@1069 552
        """
paul@1069 553
        For the given 'user', remove any counter-proposal from 'other'
paul@1069 554
        associated with the given 'uid' and 'recurrenceid'.
paul@1069 555
        """
paul@1069 556
paul@1069 557
        pass
paul@1069 558
paul@1069 559
    # Event cancellation.
paul@1069 560
paul@1069 561
    def cancel_event(self, user, uid, recurrenceid=None):
paul@1069 562
paul@1069 563
        """
paul@1069 564
        Cancel an event for 'user' having the given 'uid'. If the optional
paul@1069 565
        'recurrenceid' is specified, a specific instance or occurrence of an
paul@1069 566
        event is cancelled.
paul@1069 567
        """
paul@1069 568
paul@1069 569
        pass
paul@1069 570
paul@1069 571
    def uncancel_event(self, user, uid, recurrenceid=None):
paul@1069 572
paul@1069 573
        """
paul@1069 574
        Uncancel an event for 'user' having the given 'uid'. If the optional
paul@1069 575
        'recurrenceid' is specified, a specific instance or occurrence of an
paul@1069 576
        event is uncancelled.
paul@1069 577
        """
paul@1069 578
paul@1069 579
        pass
paul@1069 580
paul@1069 581
    def remove_cancellations(self, user, uid, recurrenceid=None):
paul@1069 582
paul@1069 583
        """
paul@1069 584
        Remove cancellations for 'user' for any event having the given 'uid'. If
paul@1069 585
        the optional 'recurrenceid' is specified, a specific instance or
paul@1069 586
        occurrence of an event is affected.
paul@1069 587
        """
paul@1069 588
paul@1069 589
        # Remove all recurrence cancellations if a general event is indicated.
paul@1069 590
paul@1069 591
        if not recurrenceid:
paul@1069 592
            for _recurrenceid in self.get_cancelled_recurrences(user, uid):
paul@1069 593
                self.remove_cancellation(user, uid, _recurrenceid)
paul@1069 594
paul@1069 595
        return self.remove_cancellation(user, uid, recurrenceid)
paul@1069 596
paul@1069 597
    def remove_cancellation(self, user, uid, recurrenceid=None):
paul@1069 598
paul@1069 599
        """
paul@1069 600
        Remove a cancellation for 'user' for the event having the given 'uid'.
paul@1069 601
        If the optional 'recurrenceid' is specified, a specific instance or
paul@1069 602
        occurrence of an event is affected.
paul@1069 603
        """
paul@1069 604
paul@1069 605
        pass
paul@1069 606
paul@1069 607
class PublisherBase:
paul@1069 608
paul@1069 609
    "The core operations of a data publisher."
paul@1069 610
paul@1069 611
    def set_freebusy(self, user, freebusy):
paul@1069 612
paul@1069 613
        "For the given 'user', set 'freebusy' details."
paul@1069 614
paul@1069 615
        pass
paul@1069 616
paul@1069 617
class JournalBase:
paul@1069 618
paul@1069 619
    "The core operations of a journal system supporting quotas."
paul@1069 620
paul@1069 621
    # Quota and user identity/group discovery.
paul@1069 622
paul@1069 623
    def get_quotas(self):
paul@1069 624
paul@1069 625
        "Return a list of quotas."
paul@1069 626
paul@1069 627
        pass
paul@1069 628
paul@1069 629
    def get_quota_users(self, quota):
paul@1069 630
paul@1069 631
        "Return a list of quota users."
paul@1069 632
paul@1069 633
        pass
paul@1069 634
paul@1195 635
    # Delegate information for the quota.
paul@1195 636
paul@1195 637
    def get_delegates(self, quota):
paul@1195 638
paul@1195 639
        "Return a list of delegates for 'quota'."
paul@1195 640
paul@1195 641
        pass
paul@1195 642
paul@1195 643
    def set_delegates(self, quota, delegates):
paul@1195 644
paul@1195 645
        "For the given 'quota', set the list of 'delegates'."
paul@1195 646
paul@1195 647
        pass
paul@1195 648
paul@1069 649
    # Groups of users sharing quotas.
paul@1069 650
paul@1069 651
    def get_groups(self, quota):
paul@1069 652
paul@1069 653
        "Return the identity mappings for the given 'quota' as a dictionary."
paul@1069 654
paul@1069 655
        pass
paul@1069 656
paul@1142 657
    def set_group(self, quota, store_user, user_group):
paul@1142 658
paul@1142 659
        """
paul@1142 660
        For the given 'quota', set a mapping from 'store_user' to 'user_group'.
paul@1142 661
        """
paul@1142 662
paul@1142 663
        pass
paul@1142 664
paul@1069 665
    def get_limits(self, quota):
paul@1069 666
paul@1069 667
        """
paul@1069 668
        Return the limits for the 'quota' as a dictionary mapping identities or
paul@1069 669
        groups to durations.
paul@1069 670
        """
paul@1069 671
paul@1069 672
        pass
paul@1069 673
paul@1089 674
    def set_limit(self, quota, group, limit):
paul@1089 675
paul@1089 676
        """
paul@1089 677
        For the given 'quota', set for a user 'group' the given 'limit' on
paul@1089 678
        resource usage.
paul@1089 679
        """
paul@1089 680
paul@1089 681
        pass
paul@1089 682
paul@1069 683
    # Journal entry methods.
paul@1069 684
paul@1077 685
    def get_entries(self, quota, group, mutable=False):
paul@1069 686
paul@1069 687
        """
paul@1069 688
        Return a list of journal entries for the given 'quota' for the indicated
paul@1069 689
        'group'.
paul@1069 690
        """
paul@1069 691
paul@1069 692
        pass
paul@1069 693
paul@1071 694
    def get_entries_for_update(self, quota, group):
paul@1071 695
paul@1071 696
        """
paul@1071 697
        Return a list of journal entries for the given 'quota' for the indicated
paul@1142 698
        'group' that may be updated, potentially affecting the stored
paul@1142 699
        information directly.
paul@1071 700
        """
paul@1071 701
paul@1077 702
        return self.get_entries(quota, group, True)
paul@1071 703
paul@1069 704
    def set_entries(self, quota, group, entries):
paul@1069 705
paul@1069 706
        """
paul@1069 707
        For the given 'quota' and indicated 'group', set the list of journal
paul@1069 708
        'entries'.
paul@1069 709
        """
paul@1069 710
paul@1069 711
        pass
paul@1069 712
paul@1069 713
# vim: tabstop=4 expandtab shiftwidth=4