imip-agent

Annotated imiptools/stores/common.py

1359:8cb064fcd9f1
2017-10-22 Paul Boddie Reworked various aspects of the recurrence computation implementation, removing explicit sort operations and changing day selection to produce results in order.
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@1305 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@1069 225
    # Free/busy period providers, upon extension of the free/busy records.
paul@1069 226
paul@1075 227
    def _get_freebusy_providers(self, user):
paul@1075 228
paul@1075 229
        """
paul@1075 230
        Return the free/busy providers for the given 'user'.
paul@1075 231
paul@1075 232
        This function returns any stored datetime and a list of providers as a
paul@1075 233
        2-tuple. Each provider is itself a (uid, recurrenceid) tuple.
paul@1075 234
        """
paul@1075 235
paul@1075 236
        pass
paul@1075 237
paul@1069 238
    def get_freebusy_providers(self, user, dt=None):
paul@1069 239
paul@1069 240
        """
paul@1069 241
        Return a set of uncancelled events of the form (uid, recurrenceid)
paul@1069 242
        providing free/busy details beyond the given datetime 'dt'.
paul@1069 243
paul@1069 244
        If 'dt' is not specified, all events previously found to provide
paul@1069 245
        details will be returned. Otherwise, if 'dt' is earlier than the
paul@1069 246
        datetime recorded for the known providers, None is returned, indicating
paul@1069 247
        that the list of providers must be recomputed.
paul@1069 248
paul@1069 249
        This function returns a list of (uid, recurrenceid) tuples upon success.
paul@1069 250
        """
paul@1069 251
paul@1075 252
        t = self._get_freebusy_providers(user)
paul@1075 253
        if not t:
paul@1075 254
            return None
paul@1075 255
paul@1075 256
        dt_string, t = t
paul@1075 257
paul@1075 258
        # If the requested datetime is earlier than the stated datetime, the
paul@1075 259
        # providers will need to be recomputed.
paul@1075 260
paul@1075 261
        if dt:
paul@1075 262
            providers_dt = get_datetime(dt_string)
paul@1075 263
            if not providers_dt or providers_dt > dt:
paul@1075 264
                return None
paul@1075 265
paul@1075 266
        # Otherwise, return the providers.
paul@1075 267
paul@1089 268
        return t
paul@1075 269
paul@1075 270
    def _set_freebusy_providers(self, user, dt_string, t):
paul@1075 271
paul@1075 272
        "Set the given provider timestamp 'dt_string' and table 't'."
paul@1075 273
paul@1069 274
        pass
paul@1069 275
paul@1069 276
    def set_freebusy_providers(self, user, dt, providers):
paul@1069 277
paul@1069 278
        """
paul@1069 279
        Define the uncancelled events providing free/busy details beyond the
paul@1069 280
        given datetime 'dt'.
paul@1069 281
        """
paul@1069 282
paul@1075 283
        t = []
paul@1075 284
paul@1075 285
        for obj in providers:
paul@1075 286
            t.append((obj.get_uid(), obj.get_recurrenceid()))
paul@1075 287
paul@1075 288
        return self._set_freebusy_providers(user, format_datetime(dt), t)
paul@1069 289
paul@1069 290
    def append_freebusy_provider(self, user, provider):
paul@1069 291
paul@1069 292
        "For the given 'user', append the free/busy 'provider'."
paul@1069 293
paul@1075 294
        t = self._get_freebusy_providers(user)
paul@1075 295
        if not t:
paul@1075 296
            return False
paul@1075 297
paul@1075 298
        dt_string, t = t
paul@1197 299
        details = (provider.get_uid(), provider.get_recurrenceid())
paul@1197 300
paul@1197 301
        if not details in t:
paul@1197 302
            t.append(details)
paul@1075 303
paul@1075 304
        return self._set_freebusy_providers(user, dt_string, t)
paul@1069 305
paul@1069 306
    def remove_freebusy_provider(self, user, provider):
paul@1069 307
paul@1069 308
        "For the given 'user', remove the free/busy 'provider'."
paul@1069 309
paul@1075 310
        t = self._get_freebusy_providers(user)
paul@1075 311
        if not t:
paul@1075 312
            return False
paul@1075 313
paul@1075 314
        dt_string, t = t
paul@1075 315
        try:
paul@1075 316
            t.remove((provider.get_uid(), provider.get_recurrenceid()))
paul@1075 317
        except ValueError:
paul@1075 318
            return False
paul@1075 319
paul@1075 320
        return self._set_freebusy_providers(user, dt_string, t)
paul@1069 321
paul@1069 322
    # Free/busy period access.
paul@1069 323
paul@1077 324
    def get_freebusy(self, user, name=None, mutable=False):
paul@1069 325
paul@1069 326
        "Get free/busy details for the given 'user'."
paul@1069 327
paul@1069 328
        pass
paul@1069 329
paul@1077 330
    def get_freebusy_for_other(self, user, other, mutable=False):
paul@1069 331
paul@1069 332
        "For the given 'user', get free/busy details for the 'other' user."
paul@1069 333
paul@1069 334
        pass
paul@1069 335
paul@1071 336
    def get_freebusy_for_update(self, user, name=None):
paul@1071 337
paul@1142 338
        """
paul@1142 339
        Get free/busy details for the given 'user' that may be updated,
paul@1142 340
        potentially affecting the stored information directly.
paul@1142 341
        """
paul@1071 342
paul@1077 343
        return self.get_freebusy(user, name, True)
paul@1071 344
paul@1071 345
    def get_freebusy_for_other_for_update(self, user, other):
paul@1071 346
paul@1142 347
        """
paul@1142 348
        For the given 'user', get free/busy details for the 'other' user
paul@1142 349
        that may be updated, potentially affecting the stored information
paul@1142 350
        directly.
paul@1142 351
        """
paul@1071 352
paul@1077 353
        return self.get_freebusy_for_other(user, other, True)
paul@1071 354
paul@1069 355
    def set_freebusy(self, user, freebusy, name=None):
paul@1069 356
paul@1069 357
        "For the given 'user', set 'freebusy' details."
paul@1069 358
paul@1069 359
        pass
paul@1069 360
paul@1069 361
    def set_freebusy_for_other(self, user, freebusy, other):
paul@1069 362
paul@1069 363
        "For the given 'user', set 'freebusy' details for the 'other' user."
paul@1069 364
paul@1069 365
        pass
paul@1069 366
paul@1142 367
    def get_freebusy_others(self, user):
paul@1142 368
paul@1142 369
        """
paul@1142 370
        For the given 'user', return a list of other users for whom free/busy
paul@1142 371
        information is retained.
paul@1142 372
        """
paul@1142 373
paul@1142 374
        pass
paul@1142 375
paul@1069 376
    # Tentative free/busy periods related to countering.
paul@1069 377
paul@1077 378
    def get_freebusy_offers(self, user, mutable=False):
paul@1069 379
paul@1069 380
        "Get free/busy offers for the given 'user'."
paul@1069 381
paul@1069 382
        pass
paul@1069 383
paul@1071 384
    def get_freebusy_offers_for_update(self, user):
paul@1071 385
paul@1142 386
        """
paul@1142 387
        Get free/busy offers for the given 'user' that may be updated,
paul@1142 388
        potentially affecting the stored information directly.
paul@1142 389
        """
paul@1071 390
paul@1077 391
        return self.get_freebusy_offers(user, True)
paul@1071 392
paul@1069 393
    def set_freebusy_offers(self, user, freebusy):
paul@1069 394
paul@1069 395
        "For the given 'user', set 'freebusy' offers."
paul@1069 396
paul@1069 397
        return self.set_freebusy(user, freebusy, "freebusy-offers")
paul@1069 398
paul@1069 399
    # Requests and counter-proposals.
paul@1069 400
paul@1069 401
    def get_requests(self, user):
paul@1069 402
paul@1069 403
        "Get requests for the given 'user'."
paul@1069 404
paul@1069 405
        pass
paul@1069 406
paul@1069 407
    def set_requests(self, user, requests):
paul@1069 408
paul@1069 409
        "For the given 'user', set the list of queued 'requests'."
paul@1069 410
paul@1069 411
        pass
paul@1069 412
paul@1069 413
    def set_request(self, user, uid, recurrenceid=None, type=None):
paul@1069 414
paul@1069 415
        """
paul@1069 416
        For the given 'user', set the queued 'uid' and 'recurrenceid',
paul@1069 417
        indicating a request, along with any given 'type'.
paul@1069 418
        """
paul@1069 419
paul@1069 420
        pass
paul@1069 421
paul@1069 422
    def queue_request(self, user, uid, recurrenceid=None, type=None):
paul@1069 423
paul@1069 424
        """
paul@1069 425
        Queue a request for 'user' having the given 'uid'. If the optional
paul@1069 426
        'recurrenceid' is specified, the entry refers to a specific instance
paul@1069 427
        or occurrence of an event. The 'type' parameter can be used to indicate
paul@1069 428
        a specific type of request.
paul@1069 429
        """
paul@1069 430
paul@1069 431
        requests = self.get_requests(user) or []
paul@1069 432
paul@1069 433
        if not self.have_request(requests, uid, recurrenceid):
paul@1069 434
            return self.set_request(user, uid, recurrenceid, type)
paul@1069 435
paul@1069 436
        return False
paul@1069 437
paul@1069 438
    def dequeue_request(self, user, uid, recurrenceid=None):
paul@1069 439
paul@1069 440
        """
paul@1069 441
        Dequeue all requests for 'user' having the given 'uid'. If the optional
paul@1069 442
        'recurrenceid' is specified, all requests for that specific instance or
paul@1069 443
        occurrence of an event are dequeued.
paul@1069 444
        """
paul@1069 445
paul@1069 446
        requests = self.get_requests(user) or []
paul@1069 447
        result = []
paul@1069 448
paul@1069 449
        for request in requests:
paul@1069 450
            if request[:2] != (uid, recurrenceid):
paul@1069 451
                result.append(request)
paul@1069 452
paul@1069 453
        self.set_requests(user, result)
paul@1069 454
        return True
paul@1069 455
paul@1069 456
    def has_request(self, user, uid, recurrenceid=None, type=None, strict=False):
paul@1069 457
        return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict)
paul@1069 458
paul@1069 459
    def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False):
paul@1069 460
paul@1069 461
        """
paul@1069 462
        Return whether 'requests' contains a request with the given 'uid' and
paul@1069 463
        any specified 'recurrenceid' and 'type'. If 'strict' is set to a true
paul@1069 464
        value, the precise type of the request must match; otherwise, any type
paul@1069 465
        of request for the identified object may be matched.
paul@1069 466
        """
paul@1069 467
paul@1069 468
        for request in requests:
paul@1069 469
            if request[:2] == (uid, recurrenceid) and (
paul@1069 470
                not strict or
paul@1069 471
                not request[2:] and not type or
paul@1069 472
                request[2:] and request[2] == type):
paul@1069 473
paul@1069 474
                return True
paul@1069 475
paul@1069 476
        return False
paul@1069 477
paul@1069 478
    def get_counters(self, user, uid, recurrenceid=None):
paul@1069 479
paul@1069 480
        """
paul@1069 481
        For the given 'user', return a list of users from whom counter-proposals
paul@1069 482
        have been received for the given 'uid' and optional 'recurrenceid'.
paul@1069 483
        """
paul@1069 484
paul@1069 485
        pass
paul@1069 486
paul@1305 487
    def get_counter_recurrences(self, user, uid):
paul@1305 488
paul@1305 489
        """
paul@1305 490
        For the given 'user', return a list of recurrence identifiers describing
paul@1305 491
        counter-proposals for the parent event with the given 'uid'.
paul@1305 492
        """
paul@1305 493
paul@1305 494
        pass
paul@1305 495
paul@1069 496
    def get_counter(self, user, other, uid, recurrenceid=None):
paul@1069 497
paul@1069 498
        """
paul@1069 499
        For the given 'user', return the counter-proposal from 'other' for the
paul@1069 500
        given 'uid' and optional 'recurrenceid'.
paul@1069 501
        """
paul@1069 502
paul@1069 503
        pass
paul@1069 504
paul@1069 505
    def set_counter(self, user, other, node, uid, recurrenceid=None):
paul@1069 506
paul@1069 507
        """
paul@1069 508
        For the given 'user', store a counter-proposal received from 'other' the
paul@1069 509
        given 'node' representing that proposal for the given 'uid' and
paul@1069 510
        'recurrenceid'.
paul@1069 511
        """
paul@1069 512
paul@1069 513
        pass
paul@1069 514
paul@1306 515
    def remove_counters(self, user, uid, recurrenceid=None, attendee=None):
paul@1069 516
paul@1069 517
        """
paul@1069 518
        For the given 'user', remove all counter-proposals associated with the
paul@1306 519
        given 'uid' and 'recurrenceid'. If 'attendee' is specified, only objects
paul@1306 520
        provided by this attendee will be removed.
paul@1069 521
        """
paul@1069 522
paul@1069 523
        pass
paul@1069 524
paul@1069 525
    def remove_counter(self, user, other, uid, recurrenceid=None):
paul@1069 526
paul@1069 527
        """
paul@1069 528
        For the given 'user', remove any counter-proposal from 'other'
paul@1069 529
        associated with the given 'uid' and 'recurrenceid'.
paul@1069 530
        """
paul@1069 531
paul@1069 532
        pass
paul@1069 533
paul@1069 534
    # Event cancellation.
paul@1069 535
paul@1069 536
    def cancel_event(self, user, uid, recurrenceid=None):
paul@1069 537
paul@1069 538
        """
paul@1069 539
        Cancel an event for 'user' having the given 'uid'. If the optional
paul@1069 540
        'recurrenceid' is specified, a specific instance or occurrence of an
paul@1069 541
        event is cancelled.
paul@1069 542
        """
paul@1069 543
paul@1069 544
        pass
paul@1069 545
paul@1069 546
    def uncancel_event(self, user, uid, recurrenceid=None):
paul@1069 547
paul@1069 548
        """
paul@1069 549
        Uncancel an event for 'user' having the given 'uid'. If the optional
paul@1069 550
        'recurrenceid' is specified, a specific instance or occurrence of an
paul@1069 551
        event is uncancelled.
paul@1069 552
        """
paul@1069 553
paul@1069 554
        pass
paul@1069 555
paul@1069 556
    def remove_cancellations(self, user, uid, recurrenceid=None):
paul@1069 557
paul@1069 558
        """
paul@1069 559
        Remove cancellations for 'user' for any event having the given 'uid'. If
paul@1069 560
        the optional 'recurrenceid' is specified, a specific instance or
paul@1069 561
        occurrence of an event is affected.
paul@1069 562
        """
paul@1069 563
paul@1069 564
        # Remove all recurrence cancellations if a general event is indicated.
paul@1069 565
paul@1069 566
        if not recurrenceid:
paul@1069 567
            for _recurrenceid in self.get_cancelled_recurrences(user, uid):
paul@1069 568
                self.remove_cancellation(user, uid, _recurrenceid)
paul@1069 569
paul@1069 570
        return self.remove_cancellation(user, uid, recurrenceid)
paul@1069 571
paul@1069 572
    def remove_cancellation(self, user, uid, recurrenceid=None):
paul@1069 573
paul@1069 574
        """
paul@1069 575
        Remove a cancellation for 'user' for the event having the given 'uid'.
paul@1069 576
        If the optional 'recurrenceid' is specified, a specific instance or
paul@1069 577
        occurrence of an event is affected.
paul@1069 578
        """
paul@1069 579
paul@1069 580
        pass
paul@1069 581
paul@1069 582
class PublisherBase:
paul@1069 583
paul@1069 584
    "The core operations of a data publisher."
paul@1069 585
paul@1069 586
    def set_freebusy(self, user, freebusy):
paul@1069 587
paul@1069 588
        "For the given 'user', set 'freebusy' details."
paul@1069 589
paul@1069 590
        pass
paul@1069 591
paul@1069 592
class JournalBase:
paul@1069 593
paul@1069 594
    "The core operations of a journal system supporting quotas."
paul@1069 595
paul@1069 596
    # Quota and user identity/group discovery.
paul@1069 597
paul@1069 598
    def get_quotas(self):
paul@1069 599
paul@1069 600
        "Return a list of quotas."
paul@1069 601
paul@1069 602
        pass
paul@1069 603
paul@1069 604
    def get_quota_users(self, quota):
paul@1069 605
paul@1069 606
        "Return a list of quota users."
paul@1069 607
paul@1069 608
        pass
paul@1069 609
paul@1195 610
    # Delegate information for the quota.
paul@1195 611
paul@1195 612
    def get_delegates(self, quota):
paul@1195 613
paul@1195 614
        "Return a list of delegates for 'quota'."
paul@1195 615
paul@1195 616
        pass
paul@1195 617
paul@1195 618
    def set_delegates(self, quota, delegates):
paul@1195 619
paul@1195 620
        "For the given 'quota', set the list of 'delegates'."
paul@1195 621
paul@1195 622
        pass
paul@1195 623
paul@1069 624
    # Groups of users sharing quotas.
paul@1069 625
paul@1069 626
    def get_groups(self, quota):
paul@1069 627
paul@1069 628
        "Return the identity mappings for the given 'quota' as a dictionary."
paul@1069 629
paul@1069 630
        pass
paul@1069 631
paul@1142 632
    def set_group(self, quota, store_user, user_group):
paul@1142 633
paul@1142 634
        """
paul@1142 635
        For the given 'quota', set a mapping from 'store_user' to 'user_group'.
paul@1142 636
        """
paul@1142 637
paul@1142 638
        pass
paul@1142 639
paul@1069 640
    def get_limits(self, quota):
paul@1069 641
paul@1069 642
        """
paul@1069 643
        Return the limits for the 'quota' as a dictionary mapping identities or
paul@1069 644
        groups to durations.
paul@1069 645
        """
paul@1069 646
paul@1069 647
        pass
paul@1069 648
paul@1089 649
    def set_limit(self, quota, group, limit):
paul@1089 650
paul@1089 651
        """
paul@1089 652
        For the given 'quota', set for a user 'group' the given 'limit' on
paul@1089 653
        resource usage.
paul@1089 654
        """
paul@1089 655
paul@1089 656
        pass
paul@1089 657
paul@1069 658
    # Journal entry methods.
paul@1069 659
paul@1077 660
    def get_entries(self, quota, group, mutable=False):
paul@1069 661
paul@1069 662
        """
paul@1069 663
        Return a list of journal entries for the given 'quota' for the indicated
paul@1069 664
        'group'.
paul@1069 665
        """
paul@1069 666
paul@1069 667
        pass
paul@1069 668
paul@1071 669
    def get_entries_for_update(self, quota, group):
paul@1071 670
paul@1071 671
        """
paul@1071 672
        Return a list of journal entries for the given 'quota' for the indicated
paul@1142 673
        'group' that may be updated, potentially affecting the stored
paul@1142 674
        information directly.
paul@1071 675
        """
paul@1071 676
paul@1077 677
        return self.get_entries(quota, group, True)
paul@1071 678
paul@1069 679
    def set_entries(self, quota, group, entries):
paul@1069 680
paul@1069 681
        """
paul@1069 682
        For the given 'quota' and indicated 'group', set the list of journal
paul@1069 683
        'entries'.
paul@1069 684
        """
paul@1069 685
paul@1069 686
        pass
paul@1069 687
paul@1069 688
# vim: tabstop=4 expandtab shiftwidth=4