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 set_freebusy(self, user, freebusy, name=None): 252 253 "For the given 'user', set 'freebusy' details." 254 255 pass 256 257 def set_freebusy_for_other(self, user, freebusy, other): 258 259 "For the given 'user', set 'freebusy' details for the 'other' user." 260 261 pass 262 263 # Tentative free/busy periods related to countering. 264 265 def get_freebusy_offers(self, user): 266 267 "Get free/busy offers for the given 'user'." 268 269 pass 270 271 def set_freebusy_offers(self, user, freebusy): 272 273 "For the given 'user', set 'freebusy' offers." 274 275 return self.set_freebusy(user, freebusy, "freebusy-offers") 276 277 # Requests and counter-proposals. 278 279 def get_requests(self, user): 280 281 "Get requests for the given 'user'." 282 283 pass 284 285 def set_requests(self, user, requests): 286 287 "For the given 'user', set the list of queued 'requests'." 288 289 pass 290 291 def set_request(self, user, uid, recurrenceid=None, type=None): 292 293 """ 294 For the given 'user', set the queued 'uid' and 'recurrenceid', 295 indicating a request, along with any given 'type'. 296 """ 297 298 pass 299 300 def queue_request(self, user, uid, recurrenceid=None, type=None): 301 302 """ 303 Queue a request for 'user' having the given 'uid'. If the optional 304 'recurrenceid' is specified, the entry refers to a specific instance 305 or occurrence of an event. The 'type' parameter can be used to indicate 306 a specific type of request. 307 """ 308 309 requests = self.get_requests(user) or [] 310 311 if not self.have_request(requests, uid, recurrenceid): 312 return self.set_request(user, uid, recurrenceid, type) 313 314 return False 315 316 def dequeue_request(self, user, uid, recurrenceid=None): 317 318 """ 319 Dequeue all requests for 'user' having the given 'uid'. If the optional 320 'recurrenceid' is specified, all requests for that specific instance or 321 occurrence of an event are dequeued. 322 """ 323 324 requests = self.get_requests(user) or [] 325 result = [] 326 327 for request in requests: 328 if request[:2] != (uid, recurrenceid): 329 result.append(request) 330 331 self.set_requests(user, result) 332 return True 333 334 def has_request(self, user, uid, recurrenceid=None, type=None, strict=False): 335 return self.have_request(self.get_requests(user) or [], uid, recurrenceid, type, strict) 336 337 def have_request(self, requests, uid, recurrenceid=None, type=None, strict=False): 338 339 """ 340 Return whether 'requests' contains a request with the given 'uid' and 341 any specified 'recurrenceid' and 'type'. If 'strict' is set to a true 342 value, the precise type of the request must match; otherwise, any type 343 of request for the identified object may be matched. 344 """ 345 346 for request in requests: 347 if request[:2] == (uid, recurrenceid) and ( 348 not strict or 349 not request[2:] and not type or 350 request[2:] and request[2] == type): 351 352 return True 353 354 return False 355 356 def get_counters(self, user, uid, recurrenceid=None): 357 358 """ 359 For the given 'user', return a list of users from whom counter-proposals 360 have been received for the given 'uid' and optional 'recurrenceid'. 361 """ 362 363 pass 364 365 def get_counter(self, user, other, uid, recurrenceid=None): 366 367 """ 368 For the given 'user', return the counter-proposal from 'other' for the 369 given 'uid' and optional 'recurrenceid'. 370 """ 371 372 pass 373 374 def set_counter(self, user, other, node, uid, recurrenceid=None): 375 376 """ 377 For the given 'user', store a counter-proposal received from 'other' the 378 given 'node' representing that proposal for the given 'uid' and 379 'recurrenceid'. 380 """ 381 382 pass 383 384 def remove_counters(self, user, uid, recurrenceid=None): 385 386 """ 387 For the given 'user', remove all counter-proposals associated with the 388 given 'uid' and 'recurrenceid'. 389 """ 390 391 pass 392 393 def remove_counter(self, user, other, uid, recurrenceid=None): 394 395 """ 396 For the given 'user', remove any counter-proposal from 'other' 397 associated with the given 'uid' and 'recurrenceid'. 398 """ 399 400 pass 401 402 # Event cancellation. 403 404 def cancel_event(self, user, uid, recurrenceid=None): 405 406 """ 407 Cancel an event for 'user' having the given 'uid'. If the optional 408 'recurrenceid' is specified, a specific instance or occurrence of an 409 event is cancelled. 410 """ 411 412 pass 413 414 def uncancel_event(self, user, uid, recurrenceid=None): 415 416 """ 417 Uncancel an event for 'user' having the given 'uid'. If the optional 418 'recurrenceid' is specified, a specific instance or occurrence of an 419 event is uncancelled. 420 """ 421 422 pass 423 424 def remove_cancellations(self, user, uid, recurrenceid=None): 425 426 """ 427 Remove cancellations for 'user' for any event having the given 'uid'. If 428 the optional 'recurrenceid' is specified, a specific instance or 429 occurrence of an event is affected. 430 """ 431 432 # Remove all recurrence cancellations if a general event is indicated. 433 434 if not recurrenceid: 435 for _recurrenceid in self.get_cancelled_recurrences(user, uid): 436 self.remove_cancellation(user, uid, _recurrenceid) 437 438 return self.remove_cancellation(user, uid, recurrenceid) 439 440 def remove_cancellation(self, user, uid, recurrenceid=None): 441 442 """ 443 Remove a cancellation for 'user' for the event having the given 'uid'. 444 If the optional 'recurrenceid' is specified, a specific instance or 445 occurrence of an event is affected. 446 """ 447 448 pass 449 450 class PublisherBase: 451 452 "The core operations of a data publisher." 453 454 def set_freebusy(self, user, freebusy): 455 456 "For the given 'user', set 'freebusy' details." 457 458 pass 459 460 class JournalBase: 461 462 "The core operations of a journal system supporting quotas." 463 464 # Quota and user identity/group discovery. 465 466 def get_quotas(self): 467 468 "Return a list of quotas." 469 470 pass 471 472 def get_quota_users(self, quota): 473 474 "Return a list of quota users." 475 476 pass 477 478 # Groups of users sharing quotas. 479 480 def get_groups(self, quota): 481 482 "Return the identity mappings for the given 'quota' as a dictionary." 483 484 pass 485 486 def get_limits(self, quota): 487 488 """ 489 Return the limits for the 'quota' as a dictionary mapping identities or 490 groups to durations. 491 """ 492 493 pass 494 495 # Free/busy period access for users within quota groups. 496 497 def get_freebusy(self, quota, user): 498 499 "Get free/busy details for the given 'quota' and 'user'." 500 501 pass 502 503 def set_freebusy(self, quota, user, freebusy): 504 505 "For the given 'quota' and 'user', set 'freebusy' details." 506 507 pass 508 509 # Journal entry methods. 510 511 def get_entries(self, quota, group): 512 513 """ 514 Return a list of journal entries for the given 'quota' for the indicated 515 'group'. 516 """ 517 518 pass 519 520 def set_entries(self, quota, group, entries): 521 522 """ 523 For the given 'quota' and indicated 'group', set the list of journal 524 'entries'. 525 """ 526 527 pass 528 529 # vim: tabstop=4 expandtab shiftwidth=4