imip-agent

Annotated docs/wiki/Resources

1384:9baa0aae5b43
2017-10-31 Paul Boddie Provided a convenience function for instantiating handler objects. Added a mode that produces output instead of sending messages without also producing debugging information. client-editing-simplification
paul@1032 1
= Resources =
paul@1032 2
paul@1032 3
In imip-agent, resources are a special kind of user that act upon requests
paul@1032 4
to schedule events and that perform such scheduling autonomously, meaning
paul@1032 5
that no human intervention is necessary when such resources receive messages
paul@1032 6
containing invitations.
paul@1032 7
paul@1032 8
By default, the [[../AgentPrograms|agent program]] responsible for resources
paul@1032 9
merely attempts to fit a received event into the resource's schedule. However,
paul@1032 10
in some organisations and environments, it is likely to be the case that other
paul@1032 11
policies are needed to ensure that a resource is not misused, overused or made
paul@1032 12
unnecessarily unavailable.
paul@1032 13
paul@1032 14
The [[../Preferences|preferences]] provide a way of controlling the behaviour
paul@1032 15
of resources, just as with any other kind of user, but certain preferences
paul@1032 16
are central to the configuration of resources.
paul@1032 17
paul@1032 18
<<TableOfContents(2,4)>>
paul@1032 19
paul@1032 20
== Scheduling Functions ==
paul@1032 21
paul@1032 22
The [[../Preferences#scheduling_function|scheduling_function]] setting
paul@1032 23
indicates the behaviour of a resource when a valid request to schedule an
paul@1032 24
event has been received. By default, a value equivalent to the following is
paul@1032 25
employed:
paul@1032 26
paul@1037 27
{{{{#!table
paul@1037 28
'''Scheduling Functions''' || '''Decision Process'''
paul@1037 29
==
paul@1037 30
<style="vertical-align: top;">
paul@1037 31
paul@1032 32
{{{
paul@1032 33
schedule_in_freebusy
paul@1032 34
}}}
paul@1032 35
paul@1037 36
||
paul@1037 37
paul@1037 38
{{{#!graphviz
paul@1037 39
//format=svg
paul@1037 40
//transform=notugly
paul@1037 41
digraph scheduling_decisions {
paul@1037 42
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1037 43
  edge [tooltip="Scheduling decisions"];
paul@1037 44
paul@1037 45
  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];
paul@1037 46
paul@1037 47
  subgraph {
paul@1037 48
    rank=same;
paul@1037 49
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 50
    freebusy [label="Free/busy",shape=folder];
paul@1037 51
  }
paul@1037 52
paul@1037 53
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 54
paul@1039 55
  subgraph {
paul@1039 56
    rank=same;
paul@1039 57
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1039 58
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1039 59
  }
paul@1037 60
paul@1037 61
  mail -> schedule_in_freebusy -> schedule -> accept;
paul@1037 62
  schedule_in_freebusy -> decline [style=dashed];
paul@1037 63
  freebusy -> schedule_in_freebusy;
paul@1037 64
}
paul@1037 65
}}}
paul@1037 66
paul@1037 67
}}}}
paul@1037 68
paul@1032 69
As described above, this merely attempts to schedule an event in the free
paul@1032 70
periods of the resource's schedule. However, no attempt is made to reject the
paul@1032 71
booking of the resource according to the identity of the organiser.
paul@1032 72
paul@1183 73
=== Concurrent Reservations ===
paul@1183 74
paul@1183 75
The `schedule_in_freebusy` function causes a resource to attempt to schedule
paul@1183 76
an event, and by default it rejects requests that involve periods for which
paul@1183 77
the resource is otherwise committed. However, a resource can be allowed to
paul@1183 78
attend (or commit to) multiple concurrent events.
paul@1183 79
paul@1183 80
By indicating a value as an argument to the function, a kind of capacity or
paul@1183 81
commitment level can be assigned to a resource. For example:
paul@1183 82
paul@1183 83
{{{
paul@1183 84
schedule_in_freebusy 5
paul@1183 85
}}}
paul@1183 86
paul@1183 87
This example indicates that a resource can support five different events
paul@1183 88
occupying the same point in time. Applications of such concurrent reservations
paul@1183 89
include things like rooms or resources that can be shared and which have a
paul@1183 90
notion of a capacity that is not immediately exhausted as soon as one event
paul@1183 91
seeks to reserve such a room or resource.
paul@1183 92
paul@1032 93
=== Identity Controls ===
paul@1032 94
paul@1032 95
Although identity controls may be implemented in the e-mail system,
paul@1032 96
effectively preventing the messages from addresses other than those within
paul@1032 97
an organisation (for example) from being delivered to the resource, it is
paul@1032 98
possible to use scheduling functions to implement such controls instead.
paul@1032 99
paul@1032 100
==== Same Domain Membership ====
paul@1032 101
paul@1032 102
For instance, the following combines the default free/busy check with a
paul@1032 103
test that the organiser belongs to the same Internet mail domain (by using
paul@1032 104
the organiser's address):
paul@1032 105
paul@1037 106
{{{{#!table
paul@1037 107
'''Scheduling Functions''' || '''Decision Process'''
paul@1037 108
==
paul@1037 109
<style="vertical-align: top;">
paul@1037 110
paul@1032 111
{{{
paul@1032 112
schedule_in_freebusy
paul@1032 113
same_domain_only
paul@1032 114
}}}
paul@1032 115
paul@1037 116
||
paul@1037 117
paul@1037 118
{{{#!graphviz
paul@1037 119
//format=svg
paul@1037 120
//transform=notugly
paul@1037 121
digraph scheduling_decisions {
paul@1037 122
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1037 123
  edge [tooltip="Scheduling decisions"];
paul@1037 124
paul@1037 125
  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];
paul@1037 126
paul@1037 127
  subgraph {
paul@1037 128
    rank=same;
paul@1037 129
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 130
    freebusy [label="Free/busy",shape=folder];
paul@1037 131
  }
paul@1037 132
paul@1037 133
  same_domain_only [label="Organiser has resource domain?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 134
paul@1037 135
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 136
paul@1039 137
  subgraph {
paul@1039 138
    rank=same;
paul@1039 139
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1039 140
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1039 141
  }
paul@1037 142
paul@1037 143
  mail -> schedule_in_freebusy -> same_domain_only -> schedule -> accept;
paul@1037 144
  schedule_in_freebusy -> decline [style=dashed];
paul@1037 145
  same_domain_only -> decline [style=dashed];
paul@1037 146
  freebusy -> schedule_in_freebusy;
paul@1037 147
}
paul@1037 148
}}}
paul@1037 149
paul@1037 150
}}}}
paul@1037 151
paul@1032 152
Note that if the first function is omitted, no check against the resource's
paul@1032 153
schedule will occur, so it is necessary to mention any such function in the
paul@1032 154
list.
paul@1032 155
paul@1032 156
==== Access Control Lists ====
paul@1032 157
paul@1032 158
A simple domain-related test may not be sufficient to control access to a
paul@1032 159
resource. Thus, another function is provided to exercise a finer degree of
paul@1032 160
control over event participants. For example:
paul@1032 161
paul@1037 162
{{{{#!table
paul@1037 163
'''Scheduling Functions and Data''' || '''Decision Process'''
paul@1037 164
==
paul@1037 165
<style="vertical-align: top;">
paul@1037 166
paul@1032 167
{{{
paul@1032 168
schedule_in_freebusy
paul@1032 169
access_control_list
paul@1032 170
}}}
paul@1032 171
paul@1037 172
Access control list:
paul@1037 173
paul@1037 174
{{{
paul@1037 175
accept
paul@1037 176
}}}
paul@1037 177
paul@1037 178
||
paul@1037 179
paul@1037 180
{{{#!graphviz
paul@1037 181
//format=svg
paul@1037 182
//transform=notugly
paul@1037 183
digraph scheduling_decisions {
paul@1037 184
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1037 185
  edge [tooltip="Scheduling decisions"];
paul@1037 186
paul@1037 187
  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];
paul@1037 188
paul@1037 189
  subgraph {
paul@1037 190
    rank=same;
paul@1037 191
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 192
    freebusy [label="Free/busy",shape=folder];
paul@1037 193
  }
paul@1037 194
paul@1037 195
  subgraph {
paul@1037 196
    rank=same;
paul@1037 197
    access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 198
    acl [label="acl setting",shape=folder];
paul@1037 199
  }
paul@1037 200
paul@1037 201
  accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 202
  end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 203
paul@1037 204
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 205
paul@1039 206
  subgraph {
paul@1039 207
    rank=same;
paul@1039 208
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1039 209
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1039 210
  }
paul@1037 211
paul@1037 212
  mail -> schedule_in_freebusy -> access_control_list -> accept_default -> end_acl -> schedule -> accept;
paul@1037 213
  end_acl -> decline [style=dashed];
paul@1037 214
  schedule_in_freebusy -> decline [style=dashed];
paul@1037 215
  freebusy -> schedule_in_freebusy;
paul@1037 216
  acl -> access_control_list;
paul@1037 217
}
paul@1037 218
}}}
paul@1037 219
paul@1037 220
}}}}
paul@1037 221
paul@1037 222
To accompany the scheduling functions, the [[../Preferences#acl|acl]] setting
paul@1037 223
in the resource's preferences must be set, or if a separate file is more
paul@1037 224
appropriate, its full path may be given as an argument to `access_control_list`:
paul@1032 225
paul@1032 226
{{{
paul@1032 227
schedule_in_freebusy
paul@1032 228
access_control_list /etc/imip-agent/resources.acl
paul@1032 229
}}}
paul@1032 230
paul@1032 231
Within the file provided by the setting or separate file, a list of rules
paul@1037 232
must describe the handling procedure for an event. For example, the following
paul@1037 233
was given in the above example:
paul@1032 234
paul@1032 235
{{{
paul@1032 236
accept
paul@1032 237
}}}
paul@1032 238
paul@1032 239
This will merely accept all invitations, anyway. However, it may be
paul@1032 240
appropriate to prevent certain users from using resources. For example:
paul@1032 241
paul@1037 242
{{{{#!table
paul@1037 243
'''Scheduling Functions and Data''' || '''Decision Process'''
paul@1037 244
==
paul@1037 245
<style="vertical-align: top;">
paul@1037 246
paul@1037 247
{{{
paul@1037 248
schedule_in_freebusy
paul@1037 249
access_control_list
paul@1037 250
}}}
paul@1037 251
paul@1037 252
Access control list:
paul@1037 253
paul@1032 254
{{{
paul@1032 255
accept
paul@1032 256
decline attendee simon.skunk@example.com
paul@1032 257
}}}
paul@1032 258
paul@1037 259
||
paul@1037 260
paul@1037 261
{{{#!graphviz
paul@1037 262
//format=svg
paul@1037 263
//transform=notugly
paul@1037 264
digraph scheduling_decisions {
paul@1037 265
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1037 266
  edge [tooltip="Scheduling decisions"];
paul@1037 267
paul@1037 268
  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];
paul@1037 269
paul@1037 270
  subgraph {
paul@1037 271
    rank=same;
paul@1037 272
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 273
    freebusy [label="Free/busy",shape=folder];
paul@1037 274
  }
paul@1037 275
paul@1037 276
  subgraph {
paul@1037 277
    rank=same;
paul@1037 278
    access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 279
    acl [label="acl setting",shape=folder];
paul@1037 280
  }
paul@1037 281
paul@1037 282
  accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 283
  decline_attendee [label="Is attendee simon.skunk@example.com?",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 284
  end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 285
paul@1037 286
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 287
paul@1039 288
  subgraph {
paul@1039 289
    rank=same;
paul@1039 290
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1039 291
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1039 292
  }
paul@1037 293
paul@1037 294
  mail -> schedule_in_freebusy -> access_control_list -> accept_default -> decline_attendee -> end_acl -> schedule -> accept;
paul@1037 295
  end_acl -> decline [style=dashed];
paul@1037 296
  schedule_in_freebusy -> decline [style=dashed];
paul@1037 297
  freebusy -> schedule_in_freebusy;
paul@1037 298
  acl -> access_control_list;
paul@1037 299
}
paul@1037 300
}}}
paul@1037 301
paul@1037 302
}}}}
paul@1037 303
paul@1032 304
This example indicates that by default, invitations will be accepted, but if
paul@1032 305
one of the attendees of an event is `simon.skunk@example.com`, the invitation
paul@1032 306
will be declined. However, it may be the case that this rule should be
paul@1032 307
overridden under certain circumstances. For example:
paul@1032 308
paul@1037 309
{{{{#!table
paul@1037 310
'''Scheduling Functions and Data''' || '''Decision Process'''
paul@1037 311
==
paul@1037 312
<style="vertical-align: top;">
paul@1037 313
paul@1037 314
{{{
paul@1037 315
schedule_in_freebusy
paul@1037 316
access_control_list
paul@1037 317
}}}
paul@1037 318
paul@1037 319
Access control list:
paul@1037 320
paul@1032 321
{{{
paul@1032 322
accept
paul@1032 323
decline attendee simon.skunk@example.com
paul@1032 324
accept organiser paul.boddie@example.com
paul@1032 325
}}}
paul@1032 326
paul@1037 327
||
paul@1037 328
paul@1037 329
{{{#!graphviz
paul@1037 330
//format=svg
paul@1037 331
//transform=notugly
paul@1037 332
digraph scheduling_decisions {
paul@1037 333
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1037 334
  edge [tooltip="Scheduling decisions"];
paul@1037 335
paul@1037 336
  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];
paul@1037 337
paul@1037 338
  subgraph {
paul@1037 339
    rank=same;
paul@1037 340
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 341
    freebusy [label="Free/busy",shape=folder];
paul@1037 342
  }
paul@1037 343
paul@1037 344
  subgraph {
paul@1037 345
    rank=same;
paul@1037 346
    access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold];
paul@1037 347
    acl [label="acl setting",shape=folder];
paul@1037 348
  }
paul@1037 349
paul@1037 350
  accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 351
  decline_attendee [label="Is attendee simon.skunk@example.com?",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 352
  accept_organiser [label="Is organiser paul.boddie@example.com?",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 353
  end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1037 354
paul@1037 355
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 356
paul@1039 357
  subgraph {
paul@1039 358
    rank=same;
paul@1039 359
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1039 360
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1039 361
  }
paul@1037 362
paul@1037 363
  mail -> schedule_in_freebusy -> access_control_list -> accept_default -> decline_attendee -> accept_organiser -> end_acl -> schedule -> accept;
paul@1037 364
  end_acl -> decline [style=dashed];
paul@1037 365
  schedule_in_freebusy -> decline [style=dashed];
paul@1037 366
  freebusy -> schedule_in_freebusy;
paul@1037 367
  acl -> access_control_list;
paul@1037 368
}
paul@1037 369
}}}
paul@1037 370
paul@1037 371
}}}}
paul@1037 372
paul@1032 373
Here, the stated organiser may still arrange a booking of the resource where
paul@1032 374
the previously-mentioned attendee is involved.
paul@1039 375
paul@1039 376
=== Quota Controls ===
paul@1039 377
paul@1039 378
In contrast to each user's stored information which consolidates information
paul@1227 379
related to that user's own schedule in a section of the data store, the quota 
paul@1227 380
system consolidates information related to the schedules of one or more 
paul@1227 381
resources in a repository known as the journal, thus enabling observations to
paul@1227 382
be made about their collective usage.
paul@1039 383
paul@1039 384
First, consider a resource such as a car where an organiser of an event may be
paul@1039 385
booking the car for travel purposes. A quota prevents the organiser from
paul@1039 386
booking the resource too much and denying other users access to it.
paul@1039 387
paul@1227 388
{{{#!graphviz
paul@1227 389
//format=svg
paul@1227 390
//transform=notugly
paul@1227 391
digraph quota_users {
paul@1227 392
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Quota example"];
paul@1227 393
  edge [tooltip="Quota example"];
paul@1227 394
paul@1227 395
  subgraph {
paul@1227 396
    rank=same;
paul@1227 397
    reservation1 [label="Reservation from oliver.otter@example.com\nMonday at 10am",shape=folder,style=filled,fillcolor=cyan];
paul@1227 398
    reservation2 [label="Reservation from oliver.otter@example.com\nTuesday at 10am",shape=folder,style=filled,fillcolor=cyan];
paul@1227 399
    reservation3 [label="Reservation from oliver.otter@example.com\nWednesday at 10am",shape=folder,style=filled,fillcolor=cyan];
paul@1227 400
  }
paul@1227 401
paul@1227 402
  subgraph {
paul@1227 403
    rank=same;
paul@1227 404
    test1 [label="Within quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1227 405
    test2 [label="Within quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1227 406
    test3 [label="Within quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1227 407
  }
paul@1227 408
paul@1227 409
  subgraph {
paul@1227 410
    rank=max;
paul@1227 411
    quota [label="Quota for resource-car-pontiac@example.com",shape=folder];
paul@1227 412
  }
paul@1227 413
paul@1227 414
  reservation1 -> test1;
paul@1227 415
  reservation2 -> test2;
paul@1227 416
  reservation3 -> test3;
paul@1227 417
paul@1227 418
  quota -> test1;
paul@1227 419
  quota -> test2;
paul@1227 420
  quota -> test3;
paul@1227 421
}
paul@1227 422
}}}
paul@1227 423
paul@1039 424
Now consider a number of separate car resources. An organiser might attempt to
paul@1039 425
get around any individual resource quota by booking a number of different cars.
paul@1227 426
Since each car is only aware of its own usage, it would be unaware of the
paul@1227 427
undesirable cumulative usage of all cars by the organiser.
paul@1227 428
paul@1167 429
By grouping the resources together, the organiser will exhaust any quota set on
paul@1167 430
the group of resources as they try and make reservations for the different
paul@1227 431
members of the quota group. Some additional measures are introduced as follows:
paul@1227 432
paul@1227 433
{{{#!graphviz
paul@1227 434
//format=svg
paul@1227 435
//transform=notugly
paul@1227 436
digraph quota_users {
paul@1227 437
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Quota example"];
paul@1227 438
  edge [tooltip="Quota example"];
paul@1227 439
paul@1227 440
  subgraph {
paul@1227 441
    rank=same;
paul@1227 442
    reservation1 [label="Reservation from oliver.otter@example.com\nMonday at 10am",shape=folder,style=filled,fillcolor=cyan];
paul@1227 443
    reservation2 [label="Reservation from oliver.otter@example.com\nTuesday at 10am",shape=folder,style=filled,fillcolor=cyan];
paul@1227 444
    reservation3 [label="Reservation from oliver.otter@example.com\nWednesday at 10am",shape=folder,style=filled,fillcolor=cyan];
paul@1227 445
  }
paul@1227 446
paul@1227 447
  subgraph {
paul@1227 448
    rank=same;
paul@1227 449
    car1 [label="resource-car-pontiac@example.com",shape=folder];
paul@1227 450
    car2 [label="resource-car-cadillac@example.com",shape=folder];
paul@1227 451
    car3 [label="resource-car-lexus@example.com",shape=folder];
paul@1227 452
  }
paul@1227 453
paul@1227 454
  test [label="Within common quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1227 455
paul@1227 456
  subgraph {
paul@1227 457
    rank=max;
paul@1227 458
    journal [label="Quota for cars",shape=folder];
paul@1227 459
  }
paul@1227 460
paul@1227 461
  reservation1 -> car1 -> test;
paul@1227 462
  reservation2 -> car2 -> test;
paul@1227 463
  reservation3 -> car3 -> test;
paul@1227 464
paul@1227 465
  journal -> test;
paul@1227 466
}
paul@1227 467
}}}
paul@1039 468
paul@1039 469
==== Initialising Quotas ====
paul@1039 470
paul@1165 471
Within the journal storage area, a quota may be initialised with limits
paul@1165 472
indicating the amount of time that can be occupied by the cumulative total of
paul@1165 473
all events scheduled by an individual user or a group of which they are a
paul@1165 474
member.
paul@1165 475
paul@1165 476
Such limits may be set directly using the `limits` file in a quota directory
paul@1165 477
(described in the [[../FilesystemUsage|filesystem guide]]) or in the
paul@1165 478
`quota_limits` table (described in the [[../DatabaseStore|database guide]]),
paul@1165 479
but a tool is also provided to set such limits. For example:
paul@1039 480
paul@1039 481
{{{
paul@1178 482
cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com'
paul@1178 483
mailto:vincent.vole@example.com PT10H
paul@1178 484
EOF
paul@1039 485
}}}
paul@1039 486
paul@1178 487
{{{#!wiki tip
paul@1178 488
In the above example, shell syntax is used to indicate a
paul@1178 489
[[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04|here document]]
paul@1178 490
providing a kind of "inline" file that is terminated by the final `EOF`.
paul@1178 491
The contents of this file are piped to the tool with a single argument given
paul@1178 492
indicating the quota involved.
paul@1178 493
paul@1178 494
You could also just invoke the tool and then enter the limit descriptions,
paul@1178 495
ending the input with Ctrl-D or equivalent end-of-file keystroke, or save
paul@1178 496
the descriptions in a file and then use input redirection with the filename.
paul@1178 497
}}}
paul@1178 498
paul@1178 499
The above example indicates that the given user may only reserve 10 hours
paul@1178 500
of events or less time within the given quota (corresponding to a specific
paul@1178 501
resource in the above example). Attempts to schedule more time will be
paul@1178 502
declined.
paul@1039 503
paul@1039 504
To impose a general quota, the special `*` identity can be used:
paul@1039 505
paul@1039 506
{{{
paul@1178 507
cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com'
paul@1178 508
* PT10H
paul@1178 509
EOF
paul@1039 510
}}}
paul@1039 511
paul@1180 512
Note that this general quota applies to each individual identity and not
paul@1180 513
collectively to all unspecified identities. To impose such a collective
paul@1180 514
quota, a group may be defined for this purpose as described below.
paul@1180 515
paul@1039 516
When a user identity is not listed and no general quota is defined, that
paul@1039 517
particular user will be unable to reserve the resource unless defined as a
paul@1039 518
member of a group listed in the `limits` file, as described below.
paul@1039 519
paul@1190 520
It may be useful to define unlimited quotas for certain identities or
paul@1190 521
groups in order to effectively exclude them from limits. For example:
paul@1190 522
paul@1190 523
{{{
paul@1190 524
cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com'
paul@1190 525
mailto:vincent.vole@example.com *
paul@1190 526
EOF
paul@1190 527
}}}
paul@1190 528
paul@1039 529
==== Sharing Quotas Across Users ====
paul@1039 530
paul@1039 531
When the use of resources is to be shared between users in such a way that
paul@1180 532
groups of users will be sharing a single quota, a `groups` file in a quota
paul@1180 533
directory (or records in the `quota_groups` table) must be defined, mapping
paul@1180 534
each user identity to the group to which they will belong.
paul@1180 535
paul@1180 536
A tool is provided to define groups and is used as follows:
paul@1180 537
paul@1180 538
{{{
paul@1180 539
cat <<EOF | tools/set_quota_groups.py 'mailto:resource-car-cadillac@example.com'
paul@1180 540
mailto:vincent.vole@example.com developers
paul@1180 541
* others
paul@1180 542
EOF
paul@1180 543
}}}
paul@1180 544
paul@1180 545
Here, otherwise unrecognised organisers are mapped to the `others` group.
paul@1180 546
Thus, all scheduling performed by such organisers will be done in a common
paul@1180 547
journal with this label.
paul@1180 548
paul@1180 549
The process of determining where an organiser's usage of resources is
paul@1180 550
recorded is illustrated by the following example:
paul@1039 551
paul@1167 552
{{{{#!table
paul@1167 553
'''Scheduling Data''' || '''Decision Process'''
paul@1167 554
==
paul@1167 555
<style="vertical-align: top;">
paul@1167 556
The groups are defined as follows in the `groups` file:
paul@1167 557
paul@1039 558
{{{
paul@1039 559
mailto:vincent.vole@example.com developers
paul@1039 560
mailto:harvey.horse@example.com developers
paul@1039 561
mailto:paul.boddie@example.com developers
paul@1039 562
mailto:simon.skunk@example.com testers
paul@1039 563
}}}
paul@1039 564
paul@1039 565
The group identity can then be employed in the `limits` file:
paul@1039 566
paul@1039 567
{{{
paul@1039 568
developers PT10H
paul@1039 569
testers PT20H
paul@1039 570
}}}
paul@1039 571
paul@1167 572
||
paul@1167 573
paul@1167 574
{{{#!graphviz
paul@1167 575
//format=svg
paul@1167 576
//transform=notugly
paul@1167 577
digraph quota_users {
paul@1167 578
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Quota users"];
paul@1167 579
  edge [tooltip="Quota users"];
paul@1167 580
paul@1167 581
  subgraph {
paul@1167 582
    rank=same;
paul@1167 583
    user1 [label="User is vincent.vole@example.com"];
paul@1167 584
    user2 [label="User is oliver.otter@example.com"];
paul@1167 585
  }
paul@1167 586
paul@1167 587
  havegroup [label="Have group for quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1167 588
  haveuser [label="Have group for user in quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1167 589
paul@1167 590
  group1 [label="User is vincent.vole@example.com\nGroup is developers"];
paul@1167 591
  group2 [label="User is oliver.otter@example.com"];
paul@1167 592
paul@1167 593
  checkuser1 [label="Have limit for group?",shape=ellipse,style=filled,fillcolor=gold];
paul@1167 594
  checkgeneral1 [label="Have general limit?",shape=ellipse,style=filled,fillcolor=gold];
paul@1167 595
paul@1167 596
  checkuser2 [label="Have limit for user?",shape=ellipse,style=filled,fillcolor=gold];
paul@1167 597
  checkgeneral2 [label="Have general limit?",shape=ellipse,style=filled,fillcolor=gold];
paul@1167 598
paul@1167 599
  accept [label="Quota",shape=folder,style=filled,fillcolor=cyan];
paul@1167 600
  decline [label="No quota",shape=folder,style=filled,fillcolor=cyan];
paul@1167 601
paul@1167 602
  user1 -> havegroup -> haveuser -> group1 -> checkuser1 -> checkgeneral1 -> accept;
paul@1167 603
  user2 -> havegroup -> haveuser -> group2 -> checkuser2 -> checkgeneral2 -> decline [style=dashed];
paul@1167 604
}
paul@1167 605
}}}
paul@1167 606
}}}}
paul@1167 607
paul@1167 608
Where individuals are not assigned to groups, any individual limit will apply
paul@1167 609
to them; otherwise, the general quota applies. Where individuals are assigned
paul@1167 610
to groups, any group limit will apply; otherwise, the general quota applies.
paul@1039 611
paul@1039 612
==== Individual Resource Quotas ====
paul@1039 613
paul@1039 614
The trivial case of applying quotas is to give a resource its own quota. This
paul@1039 615
is achieved by not specifying any arguments to the `check_quota` scheduling
paul@1058 616
function or to the `add_to_quota` and `remove_from_quota` functions.
paul@1058 617
paul@1058 618
{{{{#!table
paul@1058 619
'''Scheduling Functions''' || '''Decision Process'''
paul@1058 620
==
paul@1058 621
<style="vertical-align: top;">
paul@1058 622
paul@1058 623
{{{
paul@1058 624
check_quota
paul@1058 625
}}}
paul@1058 626
paul@1058 627
||
paul@1058 628
paul@1058 629
{{{#!graphviz
paul@1058 630
//format=svg
paul@1058 631
//transform=notugly
paul@1058 632
digraph scheduling_decisions {
paul@1058 633
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1058 634
  edge [tooltip="Scheduling decisions"];
paul@1058 635
paul@1058 636
  subgraph {
paul@1058 637
    rank=same;
paul@1058 638
    mail [label="Incoming mail\nfrom vincent.vole@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1058 639
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
paul@1058 640
  }
paul@1058 641
paul@1058 642
  subgraph {
paul@1058 643
    rank=same;
paul@1058 644
    check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1058 645
    quota [label="Quota for resource",shape=folder];
paul@1058 646
    quota_for_vole [label="...applying to\nvincent.vole@example.com",shape=folder]; 
paul@1058 647
  }
paul@1058 648
paul@1058 649
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1058 650
paul@1058 651
  subgraph {
paul@1058 652
    rank=same;
paul@1058 653
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1058 654
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1058 655
  }
paul@1058 656
paul@1058 657
  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1058 658
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1058 659
paul@1058 660
  mail -> check_quota -> schedule -> accept;
paul@1058 661
  check_quota -> decline [style=dashed];
paul@1058 662
  schedule -> add_to_quota -> quota;
paul@1058 663
  quota -> quota_for_vole -> check_quota;
paul@1058 664
paul@1058 665
  cancel -> remove_from_quota -> quota;
paul@1058 666
}
paul@1058 667
}}}
paul@1058 668
paul@1058 669
}}}}
paul@1039 670
paul@1039 671
==== Common Resource Quotas ====
paul@1039 672
paul@1039 673
By indicating an argument to the different functions, a common quota can be
paul@1039 674
employed. In the following example, both resources would employ the given
paul@1039 675
function invocations to pool their knowledge about their schedules.
paul@1039 676
paul@1039 677
{{{{#!table
paul@1058 678
'''Scheduling Functions''' || '''Decision Process'''
paul@1039 679
==
paul@1039 680
<style="vertical-align: top;">
paul@1039 681
paul@1039 682
{{{
paul@1039 683
check_quota cars
paul@1039 684
}}}
paul@1039 685
paul@1039 686
||
paul@1039 687
paul@1039 688
{{{#!graphviz
paul@1039 689
//format=svg
paul@1039 690
//transform=notugly
paul@1039 691
digraph scheduling_decisions {
paul@1039 692
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1039 693
  edge [tooltip="Scheduling decisions"];
paul@1039 694
paul@1039 695
  subgraph {
paul@1039 696
    rank=same;
paul@1039 697
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1039 698
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1039 699
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
paul@1039 700
  }
paul@1039 701
paul@1039 702
  subgraph {
paul@1039 703
    rank=same;
paul@1039 704
    check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 705
    quota_cars [label="Quota for cars",shape=folder];
paul@1039 706
    quota_cars_vole [label="...applying to\nvincent.vole@example.com",shape=folder];
paul@1039 707
  }
paul@1039 708
paul@1039 709
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 710
paul@1039 711
  subgraph {
paul@1039 712
    rank=same;
paul@1039 713
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1039 714
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1039 715
  }
paul@1039 716
paul@1039 717
  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1039 718
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1039 719
paul@1039 720
  mail_cadillac -> check_quota;
paul@1039 721
  mail_pontiac -> check_quota -> schedule -> accept;
paul@1039 722
  check_quota -> decline [style=dashed];
paul@1039 723
  schedule -> add_to_quota -> quota_cars;
paul@1039 724
  quota_cars -> quota_cars_vole -> check_quota;
paul@1039 725
paul@1039 726
  cancel -> remove_from_quota -> quota_cars;
paul@1039 727
}
paul@1039 728
}}}
paul@1039 729
paul@1039 730
}}}}
paul@1039 731
paul@1039 732
==== Collective Scheduling ====
paul@1039 733
paul@1039 734
Consider two separate resources: both may be reserved at the same time by the
paul@1039 735
same organiser; neither resource would normally decline the reservation on the
paul@1039 736
basis of schedule availability, should the period concerned be free. However,
paul@1039 737
it may be undesirable for one organiser to occupy both resources at the same
paul@1039 738
time.
paul@1039 739
paul@1039 740
Consequently, a mechanism is required to pool the resource schedules in such a
paul@1039 741
way that any reservation performed for one resource at a given point in time
paul@1039 742
prohibits another reservation performed for a related resource at the same
paul@1039 743
point in time by the same user.
paul@1039 744
paul@1039 745
The free/busy records held for a given quota group permit such collective
paul@1039 746
scheduling decisions and are employed as follows:
paul@1039 747
paul@1039 748
{{{{#!table
paul@1058 749
'''Scheduling Functions''' || '''Decision Process'''
paul@1039 750
==
paul@1039 751
<style="vertical-align: top;">
paul@1039 752
paul@1039 753
{{{
paul@1164 754
schedule_across_quota cars
paul@1039 755
}}}
paul@1039 756
paul@1039 757
||
paul@1039 758
paul@1039 759
{{{#!graphviz
paul@1039 760
//format=svg
paul@1039 761
//transform=notugly
paul@1039 762
digraph scheduling_decisions {
paul@1039 763
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1039 764
  edge [tooltip="Scheduling decisions"];
paul@1039 765
paul@1039 766
  subgraph {
paul@1039 767
    rank=same;
paul@1039 768
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1039 769
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1039 770
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
paul@1039 771
  }
paul@1039 772
paul@1039 773
  subgraph {
paul@1039 774
    rank=same;
paul@1039 775
    schedule_across_quota [label="Can be scheduled within the quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 776
    quota_cars [label="Quota for cars",shape=folder];
paul@1039 777
    freebusy_cars_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder];
paul@1039 778
  }
paul@1039 779
paul@1039 780
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1039 781
paul@1039 782
  subgraph {
paul@1039 783
    rank=same;
paul@1039 784
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1039 785
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1039 786
  }
paul@1039 787
paul@1039 788
  add_to_quota_freebusy [label="Add to quota free/busy",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1039 789
  remove_from_quota_freebusy [label="Remove from quota free/busy",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1039 790
paul@1039 791
  mail_cadillac -> schedule_across_quota;
paul@1039 792
  mail_pontiac -> schedule_across_quota -> schedule -> accept;
paul@1039 793
  schedule_across_quota -> decline [style=dashed];
paul@1039 794
  schedule -> add_to_quota_freebusy -> quota_cars -> freebusy_cars_vole;
paul@1039 795
  freebusy_cars_vole -> schedule_across_quota;
paul@1039 796
paul@1190 797
  cancel -> remove_from_quota_freebusy -> quota_cars;
paul@1039 798
}
paul@1039 799
}}}
paul@1039 800
paul@1039 801
}}}}
paul@1180 802
paul@1180 803
==== Delegating Attendance ====
paul@1180 804
paul@1180 805
A number of resources may be regarded as interchangeable and can therefore
paul@1180 806
stand in for each other when they are unavailable. The iCalendar specification
paul@1180 807
supports the notion of delegation: the recipient of an event invitation may
paul@1180 808
delegate their attendance to other calendar user, informing that user and the
paul@1180 809
event organiser of this decision.
paul@1180 810
paul@1180 811
To define such delegation relationships, a quota is first selected as the
paul@1180 812
repository of common scheduling information for a group of resources. Then,
paul@1180 813
the members of the group are defined as delegates. This can be done using a
paul@1180 814
tool provided for this purpose. For example:
paul@1180 815
paul@1180 816
{{{
paul@1180 817
cat <<EOF | tools/set_delegates.py 'cars'
paul@1180 818
mailto:resource-car-cadillac@example.com
paul@1180 819
mailto:resource-car-pontiac@example.com
paul@1180 820
EOF
paul@1180 821
}}}
paul@1180 822
paul@1180 823
Here, two resources are defined that will delegate to each other if they
paul@1180 824
cannot attend an event according to their own schedule.
paul@1180 825
paul@1180 826
{{{{#!table
paul@1180 827
'''Scheduling Functions''' || '''Decision Process'''
paul@1180 828
==
paul@1180 829
<style="vertical-align: top;">
paul@1180 830
paul@1180 831
{{{
paul@1180 832
schedule_for_delegate cars
paul@1180 833
}}}
paul@1180 834
paul@1180 835
||
paul@1180 836
paul@1180 837
{{{#!graphviz
paul@1180 838
//format=svg
paul@1180 839
//transform=notugly
paul@1180 840
digraph scheduling_decisions {
paul@1180 841
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1180 842
  edge [tooltip="Scheduling decisions"];
paul@1180 843
paul@1180 844
  subgraph {
paul@1180 845
    rank=same;
paul@1180 846
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1180 847
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1180 848
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
paul@1180 849
  }
paul@1180 850
paul@1180 851
  subgraph {
paul@1180 852
    rank=same;
paul@1180 853
    schedule_for_delegate [label="Can be scheduled or delegated?",shape=ellipse,style=filled,fillcolor=gold];
paul@1180 854
    quota_cars [label="Quota for cars",shape=folder];
paul@1180 855
    freebusy_cars_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder];
paul@1180 856
  }
paul@1180 857
paul@1180 858
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1180 859
paul@1180 860
  subgraph {
paul@1180 861
    rank=same;
paul@1180 862
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1180 863
    delegate [label="Delegate",shape=folder,style=filled,fillcolor=cyan];
paul@1180 864
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1180 865
  }
paul@1180 866
paul@1180 867
  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1180 868
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1180 869
paul@1180 870
  mail_cadillac -> schedule_for_delegate;
paul@1180 871
  mail_pontiac -> schedule_for_delegate -> schedule -> accept;
paul@1180 872
  schedule_for_delegate -> delegate [style=dashed];
paul@1180 873
  schedule_for_delegate -> decline [style=dashed];
paul@1180 874
  schedule -> add_to_quota -> quota_cars -> freebusy_cars_vole;
paul@1180 875
  freebusy_cars_vole -> schedule_for_delegate;
paul@1180 876
paul@1190 877
  cancel -> remove_from_quota -> quota_cars;
paul@1180 878
}
paul@1180 879
}}}
paul@1180 880
paul@1180 881
}}}}
paul@1180 882
paul@1180 883
Note that it is generally more useful to have delegation decisions made on the
paul@1180 884
basis of many resources, which is what the journal entries for each quota
paul@1190 885
provides, but also considering organisers attempting to reserve those resources
paul@1190 886
as a single group whose reservations are consolidated and assessed collectively.
paul@1190 887
Thus, a single group of users would be defined:
paul@1190 888
paul@1190 889
{{{
paul@1190 890
cat <<EOF | tools/set_quota_groups.py 'cars'
paul@1190 891
* all
paul@1190 892
EOF
paul@1190 893
}}}
paul@1190 894
paul@1180 895
Although the journal for each quota may be divided up to administer quotas for
paul@1180 896
multiple groups of organisers, for the purposes of delegation - deciding
paul@1180 897
whether a resource is generally available, and deciding which other resource
paul@1180 898
would be available instead - a single group of all organisers is more desirable.
paul@1190 899
paul@1190 900
If limits need to be imposed alongside delegation, separate quotas may be used
paul@1190 901
to achieve this.
paul@1190 902
paul@1190 903
{{{{#!table
paul@1190 904
'''Scheduling Functions''' || '''Decision Process'''
paul@1190 905
==
paul@1190 906
<style="vertical-align: top;">
paul@1190 907
paul@1190 908
{{{
paul@1190 909
check_quota cars
paul@1190 910
schedule_for_delegate car_delegation
paul@1190 911
}}}
paul@1190 912
paul@1190 913
||
paul@1190 914
paul@1190 915
{{{#!graphviz
paul@1190 916
//format=svg
paul@1190 917
//transform=notugly
paul@1190 918
digraph scheduling_decisions {
paul@1190 919
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
paul@1190 920
  edge [tooltip="Scheduling decisions"];
paul@1190 921
paul@1190 922
  subgraph {
paul@1190 923
    rank=same;
paul@1190 924
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1190 925
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
paul@1190 926
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
paul@1190 927
  }
paul@1190 928
paul@1190 929
  subgraph {
paul@1190 930
    rank=same;
paul@1190 931
    check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold];
paul@1190 932
    quota_cars [label="Quota for cars",shape=folder];
paul@1190 933
    quota_cars_vole [label="...applying to\nvincent.vole@example.com",shape=folder];
paul@1190 934
  }
paul@1190 935
paul@1190 936
  subgraph {
paul@1190 937
    rank=same;
paul@1190 938
    schedule_for_delegate [label="Can be scheduled or delegated?",shape=ellipse,style=filled,fillcolor=gold];
paul@1190 939
    quota_cars_delegation [label="Quota for cars_delegation",shape=folder];
paul@1190 940
    freebusy_cars_delegation_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder];
paul@1190 941
  }
paul@1190 942
paul@1190 943
  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];
paul@1190 944
paul@1190 945
  subgraph {
paul@1190 946
    rank=same;
paul@1190 947
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
paul@1190 948
    delegate [label="Delegate",shape=folder,style=filled,fillcolor=cyan];
paul@1190 949
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
paul@1190 950
  }
paul@1190 951
paul@1190 952
  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1190 953
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];
paul@1190 954
paul@1190 955
  mail_cadillac -> check_quota;
paul@1190 956
  mail_pontiac -> check_quota;
paul@1190 957
  check_quota -> schedule_for_delegate;
paul@1190 958
  check_quota -> decline [style=dashed];
paul@1190 959
  schedule_for_delegate -> schedule -> accept;
paul@1190 960
  schedule_for_delegate -> delegate [style=dashed];
paul@1190 961
  schedule_for_delegate -> decline [style=dashed];
paul@1190 962
  schedule -> add_to_quota;
paul@1190 963
  add_to_quota -> quota_cars -> quota_cars_vole;
paul@1190 964
  add_to_quota -> quota_cars_delegation -> freebusy_cars_delegation_vole;
paul@1190 965
  quota_cars_vole -> check_quota;
paul@1190 966
  freebusy_cars_delegation_vole -> schedule_for_delegate;
paul@1190 967
paul@1190 968
  cancel -> remove_from_quota;
paul@1190 969
  remove_from_quota -> quota_cars;
paul@1190 970
  remove_from_quota -> quota_cars_delegation;
paul@1190 971
}
paul@1190 972
}}}
paul@1190 973
paul@1190 974
}}}}