1 #!/usr/bin/env python 2 3 """ 4 General support for calendar data storage. 5 6 Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 class StoreBase: 23 24 "The core operations of a data store." 25 26 def acquire_lock(self, user, timeout=None): 27 pass 28 29 def release_lock(self, user): 30 pass 31 32 # User discovery. 33 34 def get_users(self): 35 36 "Return a list of users." 37 38 pass 39 40 # Event and event metadata access. 41 42 def get_events(self, user): 43 44 "Return a list of event identifiers." 45 46 pass 47 48 def get_all_events(self, user): 49 50 "Return a set of (uid, recurrenceid) tuples for all events." 51 52 uids = self.get_events(user) 53 if not uids: 54 return set() 55 56 all_events = set() 57 for uid in uids: 58 all_events.add((uid, None)) 59 all_events.update([(uid, recurrenceid) for recurrenceid in self.get_recurrences(user, uid)]) 60 61 return all_events 62 63 def get_event(self, user, uid, recurrenceid=None, dirname=None): 64 65 """ 66 Get the event for the given 'user' with the given 'uid'. If 67 the optional 'recurrenceid' is specified, a specific instance or 68 occurrence of an event is returned. 69 """ 70 71 pass 72 73 def get_complete_event(self, user, uid): 74 75 "Get the event for the given 'user' with the given 'uid'." 76 77 pass 78 79 def set_event(self, user, uid, recurrenceid, node): 80 81 """ 82 Set an event for 'user' having the given 'uid' and 'recurrenceid' (which 83 if the latter is specified, a specific instance or occurrence of an 84 event is referenced), using the given 'node' description. 85 """ 86 87 if recurrenceid: 88 return self.set_recurrence(user, uid, recurrenceid, node) 89 else: 90 return self.set_complete_event(user, uid, node) 91 92 def set_complete_event(self, user, uid, node): 93 94 "Set an event for 'user' having the given 'uid' and 'node'." 95 96 pass 97 98 def remove_event(self, user, uid, recurrenceid=None): 99 100 """ 101 Remove an event for 'user' having the given 'uid'. If the optional 102 'recurrenceid' is specified, a specific instance or occurrence of an 103 event is removed. 104 """ 105 106 if recurrenceid: 107 return self.remove_recurrence(user, uid, recurrenceid) 108 else: 109 for recurrenceid in self.get_recurrences(user, uid) or []: 110 self.remove_recurrence(user, uid, recurrenceid) 111 return self.remove_complete_event(user, uid) 112 113 def remove_complete_event(self, user, uid): 114 115 "Remove an event for 'user' having the given 'uid'." 116 117 self.remove_recurrences(user, uid) 118 return self.remove_parent_event(user, uid) 119 120 def remove_parent_event(self, user, uid): 121 122 "Remove the parent event for 'user' having the given 'uid'." 123 124 pass 125 126 def get_recurrences(self, user, uid): 127 128 """ 129 Get additional event instances for an event of the given 'user' with the 130 indicated 'uid'. Both active and cancelled recurrences are returned. 131 """ 132 133 return self.get_active_recurrences(user, uid) + self.get_cancelled_recurrences(user, uid) 134 135 def get_active_recurrences(self, user, uid): 136 137 """ 138 Get additional event instances for an event of the given 'user' with the 139 indicated 'uid'. Cancelled recurrences are not returned. 140 """ 141 142 pass 143 144 def get_cancelled_recurrences(self, user, uid): 145 146 """ 147 Get additional event instances for an event of the given 'user' with the 148 indicated 'uid'. Only cancelled recurrences are returned. 149 """ 150 151 pass 152 153 def get_recurrence(self, user, uid, recurrenceid): 154 155 """ 156 For the event of the given 'user' with the given 'uid', return the 157 specific recurrence indicated by the 'recurrenceid'. 158 """ 159 160 pass 161 162 def set_recurrence(self, user, uid, recurrenceid, node): 163 164 "Set an event for 'user' having the given 'uid' and 'node'." 165 166 pass 167 168 def remove_recurrence(self, user, uid, recurrenceid): 169 170 """ 171 Remove a special recurrence from an event stored by 'user' having the 172 given 'uid' and 'recurrenceid'. 173 """ 174 175 pass 176 177 def remove_recurrences(self, user, uid): 178 179 """ 180 Remove all recurrences for an event stored by 'user' having the given 181 'uid'. 182 """ 183 184 for recurrenceid in self.get_recurrences(user, uid): 185 self.remove_recurrence(user, uid, recurrenceid) 186 187 return self.remove_recurrence_collection(user, uid) 188 189 def remove_recurrence_collection(self, user, uid): 190 191 """ 192 Remove the collection of recurrences stored by 'user' having the given 193 'uid'. 194 """ 195 196 pass 197 198 # Free/busy period providers, upon extension of the free/busy records. 199 200 def get_freebusy_providers(self, user, dt=None): 201 202 """ 203 Return a set of uncancelled events of the form (uid, recurrenceid) 204 providing free/busy details beyond the given datetime 'dt'. 205 206 If 'dt' is not specified, all events previously found to provide 207 details will be returned. Otherwise, if 'dt' is earlier than the 208 datetime recorded for the known providers, None is returned, indicating 209 that the list of providers must be recomputed. 210 211 This function returns a list of (uid, recurrenceid) tuples upon success. 212 """ 213 214 pass 215 216 def set_freebusy_providers(self, user, dt, providers): 217 218 """ 219 Define the uncancelled events providing free/busy details beyond the 220 given datetime 'dt'. 221 """ 222 223 pass 224 225 def append_freebusy_provider(self, user, provider): 226 227 "For the given 'user', append the free/busy 'provider'." 228 229 pass 230 231 def remove_freebusy_provider(self, user, provider): 232 233 "For the given 'user', remove the free/busy 'provider'." 234 235 pass 236 237 # Free/busy period access. 238 239 def get_freebusy(self, user, name=None): 240 241 "Get free/busy details for the given 'user'." 242 243 pass 244 245 def get_freebusy_for_other(self, user, other): 246 247 "For the given 'user', get free/busy details for the 'other' user." 248 249 pass 250 251 def get_freebusy_for_update(self, user, name=None): 252 253 "Get free/busy details for the given 'user'." 254 255 pass 256 257 def get_freebusy_for_other_for_update(self, user, other): 258 259 "For the given 'user', get free/busy details for the 'other' user." 260 261 pass 262 263 def set_freebusy(self, user, freebusy, name=None): 264 265 "For the given 'user', set 'freebusy' details." 266 267 pass 268 269 def set_freebusy_for_other(self, user, freebusy, other): 270 271 "For the given 'user', set 'freebusy' details for the 'other' user." 272 273 pass 274 275 # Tentative free/busy periods related to countering. 276 277 def get_freebusy_offers(self, user): 278 279 "Get free/busy offers for the given 'user'." 280 281 pass 282 283 def get_freebusy_offers_for_update(self, user): 284 285 "Get free/busy offers for the given 'user'." 286 287 pass 288 289 def set_freebusy_offers(self, user, freebusy): 290 291 "For the given 'user', set 'freebusy' offers." 292 293 return self.set_freebusy(user, freebusy, "freebusy-offers") 294 295 # Requests and counter-proposals. 296 297 def get_requests(self, user): 298 299 "Get requests for the given 'user'." 300 301 pass 302 303 def set_requests(self, user, requests): 304 305 "For the given 'user', set the list of queued 'requests'." 306 307 pass 308 309 def set_request(self, user, uid, recurrenceid=None, type=None): 310 311 """ 312 For the given 'user', set the queued 'uid' and 'recurrenceid', 313 indicating a request, along with any given 'type'. 314 """ 315 316 pass 317 318 def queue_request(self, user, uid, recurrenceid=None, type=None): 319 320 """ 321 Queue a request for 'user' having the given 'uid'. If the optional 322 'recurrenceid' is specified, the entry refers to a specific instance 323 or occurrence of an event. The 'type' parameter can be used to indicate 324 a specific type of request. 325 """ 326 327 requests = self.get_requests(user) or [] 328 329 if not self.have_request(requests, uid, recurrenceid): 330 return self.set_request(user, uid, recurrenceid, type) 331 332 return False 333 334 def dequeue_request(self, user, uid, recurrenceid=None): 335 336 """ 337 Dequeue all requests for 'user' having the given 'uid'. If the optional 338 'recurrenceid' is specified, all requests for that specific instance or 339 occurrence of an event are dequeued. 340 """ 341 342 requests = self.get_requests(user) or [] 343 result = [] 344 345 for request in requests: 346 if request[:2] != (uid, recurrenceid): 347 result.append(request) 348 349 self.set_requests(user, result) 350 return True 351 352 def has_request(self, user, uid, recurrenceid=None, type=None, strict=False): 353 return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict) 354 355 def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False): 356 357 """ 358 Return whether 'requests' contains a request with the given 'uid' and 359 any specified 'recurrenceid' and 'type'. If 'strict' is set to a true 360 value, the precise type of the request must match; otherwise, any type 361 of request for the identified object may be matched. 362 """ 363 364 for request in requests: 365 if request[:2] == (uid, recurrenceid) and ( 366 not strict or 367 not request[2:] and not type or 368 request[2:] and request[2] == type): 369 370 return True 371 372 return False 373 374 def get_counters(self, user, uid, recurrenceid=None): 375 376 """ 377 For the given 'user', return a list of users from whom counter-proposals 378 have been received for the given 'uid' and optional 'recurrenceid'. 379 """ 380 381 pass 382 383 def get_counter(self, user, other, uid, recurrenceid=None): 384 385 """ 386 For the given 'user', return the counter-proposal from 'other' for the 387 given 'uid' and optional 'recurrenceid'. 388 """ 389 390 pass 391 392 def set_counter(self, user, other, node, uid, recurrenceid=None): 393 394 """ 395 For the given 'user', store a counter-proposal received from 'other' the 396 given 'node' representing that proposal for the given 'uid' and 397 'recurrenceid'. 398 """ 399 400 pass 401 402 def remove_counters(self, user, uid, recurrenceid=None): 403 404 """ 405 For the given 'user', remove all counter-proposals associated with the 406 given 'uid' and 'recurrenceid'. 407 """ 408 409 pass 410 411 def remove_counter(self, user, other, uid, recurrenceid=None): 412 413 """ 414 For the given 'user', remove any counter-proposal from 'other' 415 associated with the given 'uid' and 'recurrenceid'. 416 """ 417 418 pass 419 420 # Event cancellation. 421 422 def cancel_event(self, user, uid, recurrenceid=None): 423 424 """ 425 Cancel an event for 'user' having the given 'uid'. If the optional 426 'recurrenceid' is specified, a specific instance or occurrence of an 427 event is cancelled. 428 """ 429 430 pass 431 432 def uncancel_event(self, user, uid, recurrenceid=None): 433 434 """ 435 Uncancel an event for 'user' having the given 'uid'. If the optional 436 'recurrenceid' is specified, a specific instance or occurrence of an 437 event is uncancelled. 438 """ 439 440 pass 441 442 def remove_cancellations(self, user, uid, recurrenceid=None): 443 444 """ 445 Remove cancellations for 'user' for any event having the given 'uid'. If 446 the optional 'recurrenceid' is specified, a specific instance or 447 occurrence of an event is affected. 448 """ 449 450 # Remove all recurrence cancellations if a general event is indicated. 451 452 if not recurrenceid: 453 for _recurrenceid in self.get_cancelled_recurrences(user, uid): 454 self.remove_cancellation(user, uid, _recurrenceid) 455 456 return self.remove_cancellation(user, uid, recurrenceid) 457 458 def remove_cancellation(self, user, uid, recurrenceid=None): 459 460 """ 461 Remove a cancellation for 'user' for the event having the given 'uid'. 462 If the optional 'recurrenceid' is specified, a specific instance or 463 occurrence of an event is affected. 464 """ 465 466 pass 467 468 class PublisherBase: 469 470 "The core operations of a data publisher." 471 472 def set_freebusy(self, user, freebusy): 473 474 "For the given 'user', set 'freebusy' details." 475 476 pass 477 478 class JournalBase: 479 480 "The core operations of a journal system supporting quotas." 481 482 # Quota and user identity/group discovery. 483 484 def get_quotas(self): 485 486 "Return a list of quotas." 487 488 pass 489 490 def get_quota_users(self, quota): 491 492 "Return a list of quota users." 493 494 pass 495 496 # Groups of users sharing quotas. 497 498 def get_groups(self, quota): 499 500 "Return the identity mappings for the given 'quota' as a dictionary." 501 502 pass 503 504 def get_limits(self, quota): 505 506 """ 507 Return the limits for the 'quota' as a dictionary mapping identities or 508 groups to durations. 509 """ 510 511 pass 512 513 # Free/busy period access for users within quota groups. 514 515 def get_freebusy(self, quota, user): 516 517 "Get free/busy details for the given 'quota' and 'user'." 518 519 pass 520 521 def get_freebusy_for_update(self, quota, user): 522 523 "Get free/busy details for the given 'quota' and 'user'." 524 525 pass 526 527 def set_freebusy(self, quota, user, freebusy): 528 529 "For the given 'quota' and 'user', set 'freebusy' details." 530 531 pass 532 533 # Journal entry methods. 534 535 def get_entries(self, quota, group): 536 537 """ 538 Return a list of journal entries for the given 'quota' for the indicated 539 'group'. 540 """ 541 542 pass 543 544 def get_entries_for_update(self, quota, group): 545 546 """ 547 Return a list of journal entries for the given 'quota' for the indicated 548 'group'. 549 """ 550 551 pass 552 553 def set_entries(self, quota, group, entries): 554 555 """ 556 For the given 'quota' and indicated 'group', set the list of journal 557 'entries'. 558 """ 559 560 pass 561 562 # vim: tabstop=4 expandtab shiftwidth=4