File: Resources

package info (click to toggle)
imip-agent 0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 2,056 kB
  • sloc: python: 9,888; sh: 4,480; sql: 144; makefile: 8
file content (898 lines) | stat: -rw-r--r-- 30,341 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
= Resources =

In imip-agent, resources are a special kind of user that act upon requests
to schedule events and that perform such scheduling autonomously, meaning
that no human intervention is necessary when such resources receive messages
containing invitations.

By default, the [[../AgentPrograms|agent program]] responsible for resources
merely attempts to fit a received event into the resource's schedule. However,
in some organisations and environments, it is likely to be the case that other
policies are needed to ensure that a resource is not misused, overused or made
unnecessarily unavailable.

The [[../Preferences|preferences]] provide a way of controlling the behaviour
of resources, just as with any other kind of user, but certain preferences
are central to the configuration of resources.

<<TableOfContents(2,4)>>

== Scheduling Functions ==

The [[../Preferences#scheduling_function|scheduling_function]] setting
indicates the behaviour of a resource when a valid request to schedule an
event has been received. By default, a value equivalent to the following is
employed:

{{{{#!table
'''Scheduling Functions''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
schedule_in_freebusy
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];

  subgraph {
    rank=same;
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
    freebusy [label="Free/busy",shape=folder];
  }

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  mail -> schedule_in_freebusy -> schedule -> accept;
  schedule_in_freebusy -> decline [style=dashed];
  freebusy -> schedule_in_freebusy;
}
}}}

}}}}

As described above, this merely attempts to schedule an event in the free
periods of the resource's schedule. However, no attempt is made to reject the
booking of the resource according to the identity of the organiser.

=== Concurrent Reservations ===

The `schedule_in_freebusy` function causes a resource to attempt to schedule
an event, and by default it rejects requests that involve periods for which
the resource is otherwise committed. However, a resource can be allowed to
attend (or commit to) multiple concurrent events.

By indicating a value as an argument to the function, a kind of capacity or
commitment level can be assigned to a resource. For example:

{{{
schedule_in_freebusy 5
}}}

This example indicates that a resource can support five different events
occupying the same point in time. Applications of such concurrent reservations
include things like rooms or resources that can be shared and which have a
notion of a capacity that is not immediately exhausted as soon as one event
seeks to reserve such a room or resource.

=== Identity Controls ===

Although identity controls may be implemented in the e-mail system,
effectively preventing the messages from addresses other than those within
an organisation (for example) from being delivered to the resource, it is
possible to use scheduling functions to implement such controls instead.

==== Same Domain Membership ====

For instance, the following combines the default free/busy check with a
test that the organiser belongs to the same Internet mail domain (by using
the organiser's address):

{{{{#!table
'''Scheduling Functions''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
schedule_in_freebusy
same_domain_only
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];

  subgraph {
    rank=same;
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
    freebusy [label="Free/busy",shape=folder];
  }

  same_domain_only [label="Organiser has resource domain?",shape=ellipse,style=filled,fillcolor=gold];

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  mail -> schedule_in_freebusy -> same_domain_only -> schedule -> accept;
  schedule_in_freebusy -> decline [style=dashed];
  same_domain_only -> decline [style=dashed];
  freebusy -> schedule_in_freebusy;
}
}}}

}}}}

Note that if the first function is omitted, no check against the resource's
schedule will occur, so it is necessary to mention any such function in the
list.

==== Access Control Lists ====

A simple domain-related test may not be sufficient to control access to a
resource. Thus, another function is provided to exercise a finer degree of
control over event participants. For example:

{{{{#!table
'''Scheduling Functions and Data''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
schedule_in_freebusy
access_control_list
}}}

Access control list:

{{{
accept
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];

  subgraph {
    rank=same;
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
    freebusy [label="Free/busy",shape=folder];
  }

  subgraph {
    rank=same;
    access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold];
    acl [label="acl setting",shape=folder];
  }

  accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange];
  end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange];

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  mail -> schedule_in_freebusy -> access_control_list -> accept_default -> end_acl -> schedule -> accept;
  end_acl -> decline [style=dashed];
  schedule_in_freebusy -> decline [style=dashed];
  freebusy -> schedule_in_freebusy;
  acl -> access_control_list;
}
}}}

}}}}

To accompany the scheduling functions, the [[../Preferences#acl|acl]] setting
in the resource's preferences must be set, or if a separate file is more
appropriate, its full path may be given as an argument to `access_control_list`:

{{{
schedule_in_freebusy
access_control_list /etc/imip-agent/resources.acl
}}}

Within the file provided by the setting or separate file, a list of rules
must describe the handling procedure for an event. For example, the following
was given in the above example:

{{{
accept
}}}

This will merely accept all invitations, anyway. However, it may be
appropriate to prevent certain users from using resources. For example:

{{{{#!table
'''Scheduling Functions and Data''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
schedule_in_freebusy
access_control_list
}}}

Access control list:

{{{
accept
decline attendee simon.skunk@example.com
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];

  subgraph {
    rank=same;
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
    freebusy [label="Free/busy",shape=folder];
  }

  subgraph {
    rank=same;
    access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold];
    acl [label="acl setting",shape=folder];
  }

  accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange];
  decline_attendee [label="Is attendee simon.skunk@example.com?",shape=ellipse,style=filled,fillcolor=darkorange];
  end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange];

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  mail -> schedule_in_freebusy -> access_control_list -> accept_default -> decline_attendee -> end_acl -> schedule -> accept;
  end_acl -> decline [style=dashed];
  schedule_in_freebusy -> decline [style=dashed];
  freebusy -> schedule_in_freebusy;
  acl -> access_control_list;
}
}}}

}}}}

This example indicates that by default, invitations will be accepted, but if
one of the attendees of an event is `simon.skunk@example.com`, the invitation
will be declined. However, it may be the case that this rule should be
overridden under certain circumstances. For example:

{{{{#!table
'''Scheduling Functions and Data''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
schedule_in_freebusy
access_control_list
}}}

Access control list:

{{{
accept
decline attendee simon.skunk@example.com
accept organiser paul.boddie@example.com
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  mail [label="Incoming mail",shape=folder,style=filled,fillcolor=cyan];

  subgraph {
    rank=same;
    schedule_in_freebusy [label="Can schedule in free/busy?",shape=ellipse,style=filled,fillcolor=gold];
    freebusy [label="Free/busy",shape=folder];
  }

  subgraph {
    rank=same;
    access_control_list [label="Access control list permits booking?",shape=ellipse,style=filled,fillcolor=gold];
    acl [label="acl setting",shape=folder];
  }

  accept_default [label="Accept invitation by default",shape=ellipse,style=filled,fillcolor=darkorange];
  decline_attendee [label="Is attendee simon.skunk@example.com?",shape=ellipse,style=filled,fillcolor=darkorange];
  accept_organiser [label="Is organiser paul.boddie@example.com?",shape=ellipse,style=filled,fillcolor=darkorange];
  end_acl [label="end",shape=ellipse,style=filled,fillcolor=darkorange];

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  mail -> schedule_in_freebusy -> access_control_list -> accept_default -> decline_attendee -> accept_organiser -> end_acl -> schedule -> accept;
  end_acl -> decline [style=dashed];
  schedule_in_freebusy -> decline [style=dashed];
  freebusy -> schedule_in_freebusy;
  acl -> access_control_list;
}
}}}

}}}}

Here, the stated organiser may still arrange a booking of the resource where
the previously-mentioned attendee is involved.

=== Quota Controls ===

In contrast to each user's stored information which consolidates information
related to that user's own schedule, the quota system consolidates information
related to the schedules of one or more resources, thus enabling observations
to be made about their collective usage.

First, consider a resource such as a car where an organiser of an event may be
booking the car for travel purposes. A quota prevents the organiser from
booking the resource too much and denying other users access to it.

Now consider a number of separate car resources. An organiser might attempt to
get around any individual resource quota by booking a number of different cars.
By grouping the resources together, the organiser will exhaust any quota set on
the group of resources as they try and make reservations for the different
members of the quota group.

==== Initialising Quotas ====

Within the journal storage area, a quota may be initialised with limits
indicating the amount of time that can be occupied by the cumulative total of
all events scheduled by an individual user or a group of which they are a
member.

Such limits may be set directly using the `limits` file in a quota directory
(described in the [[../FilesystemUsage|filesystem guide]]) or in the
`quota_limits` table (described in the [[../DatabaseStore|database guide]]),
but a tool is also provided to set such limits. For example:

{{{
cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com'
mailto:vincent.vole@example.com PT10H
EOF
}}}

{{{#!wiki tip
In the above example, shell syntax is used to indicate a
[[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04|here document]]
providing a kind of "inline" file that is terminated by the final `EOF`.
The contents of this file are piped to the tool with a single argument given
indicating the quota involved.

You could also just invoke the tool and then enter the limit descriptions,
ending the input with Ctrl-D or equivalent end-of-file keystroke, or save
the descriptions in a file and then use input redirection with the filename.
}}}

The above example indicates that the given user may only reserve 10 hours
of events or less time within the given quota (corresponding to a specific
resource in the above example). Attempts to schedule more time will be
declined.

To impose a general quota, the special `*` identity can be used:

{{{
cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com'
* PT10H
EOF
}}}

Note that this general quota applies to each individual identity and not
collectively to all unspecified identities. To impose such a collective
quota, a group may be defined for this purpose as described below.

When a user identity is not listed and no general quota is defined, that
particular user will be unable to reserve the resource unless defined as a
member of a group listed in the `limits` file, as described below.

It may be useful to define unlimited quotas for certain identities or
groups in order to effectively exclude them from limits. For example:

{{{
cat <<EOF | set_quota_limits.py 'mailto:resource-car-cadillac@example.com'
mailto:vincent.vole@example.com *
EOF
}}}

==== Sharing Quotas Across Users ====

When the use of resources is to be shared between users in such a way that
groups of users will be sharing a single quota, a `groups` file in a quota
directory (or records in the `quota_groups` table) must be defined, mapping
each user identity to the group to which they will belong.

A tool is provided to define groups and is used as follows:

{{{
cat <<EOF | tools/set_quota_groups.py 'mailto:resource-car-cadillac@example.com'
mailto:vincent.vole@example.com developers
* others
EOF
}}}

Here, otherwise unrecognised organisers are mapped to the `others` group.
Thus, all scheduling performed by such organisers will be done in a common
journal with this label.

The process of determining where an organiser's usage of resources is
recorded is illustrated by the following example:

{{{{#!table
'''Scheduling Data''' || '''Decision Process'''
==
<style="vertical-align: top;">
The groups are defined as follows in the `groups` file:

{{{
mailto:vincent.vole@example.com developers
mailto:harvey.horse@example.com developers
mailto:paul.boddie@example.com developers
mailto:simon.skunk@example.com testers
}}}

The group identity can then be employed in the `limits` file:

{{{
developers PT10H
testers PT20H
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph quota_users {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Quota users"];
  edge [tooltip="Quota users"];

  subgraph {
    rank=same;
    user1 [label="User is vincent.vole@example.com"];
    user2 [label="User is oliver.otter@example.com"];
  }

  havegroup [label="Have group for quota?",shape=ellipse,style=filled,fillcolor=gold];
  haveuser [label="Have group for user in quota?",shape=ellipse,style=filled,fillcolor=gold];

  group1 [label="User is vincent.vole@example.com\nGroup is developers"];
  group2 [label="User is oliver.otter@example.com"];

  checkuser1 [label="Have limit for group?",shape=ellipse,style=filled,fillcolor=gold];
  checkgeneral1 [label="Have general limit?",shape=ellipse,style=filled,fillcolor=gold];

  checkuser2 [label="Have limit for user?",shape=ellipse,style=filled,fillcolor=gold];
  checkgeneral2 [label="Have general limit?",shape=ellipse,style=filled,fillcolor=gold];

  accept [label="Quota",shape=folder,style=filled,fillcolor=cyan];
  decline [label="No quota",shape=folder,style=filled,fillcolor=cyan];

  user1 -> havegroup -> haveuser -> group1 -> checkuser1 -> checkgeneral1 -> accept;
  user2 -> havegroup -> haveuser -> group2 -> checkuser2 -> checkgeneral2 -> decline [style=dashed];
}
}}}
}}}}

Where individuals are not assigned to groups, any individual limit will apply
to them; otherwise, the general quota applies. Where individuals are assigned
to groups, any group limit will apply; otherwise, the general quota applies.

==== Individual Resource Quotas ====

The trivial case of applying quotas is to give a resource its own quota. This
is achieved by not specifying any arguments to the `check_quota` scheduling
function or to the `add_to_quota` and `remove_from_quota` functions.

{{{{#!table
'''Scheduling Functions''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
check_quota
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  subgraph {
    rank=same;
    mail [label="Incoming mail\nfrom vincent.vole@example.com",shape=folder,style=filled,fillcolor=cyan];
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
  }

  subgraph {
    rank=same;
    check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold];
    quota [label="Quota for resource",shape=folder];
    quota_for_vole [label="...applying to\nvincent.vole@example.com",shape=folder]; 
  }

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];

  mail -> check_quota -> schedule -> accept;
  check_quota -> decline [style=dashed];
  schedule -> add_to_quota -> quota;
  quota -> quota_for_vole -> check_quota;

  cancel -> remove_from_quota -> quota;
}
}}}

}}}}

==== Common Resource Quotas ====

By indicating an argument to the different functions, a common quota can be
employed. In the following example, both resources would employ the given
function invocations to pool their knowledge about their schedules.

{{{{#!table
'''Scheduling Functions''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
check_quota cars
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  subgraph {
    rank=same;
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
  }

  subgraph {
    rank=same;
    check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold];
    quota_cars [label="Quota for cars",shape=folder];
    quota_cars_vole [label="...applying to\nvincent.vole@example.com",shape=folder];
  }

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];

  mail_cadillac -> check_quota;
  mail_pontiac -> check_quota -> schedule -> accept;
  check_quota -> decline [style=dashed];
  schedule -> add_to_quota -> quota_cars;
  quota_cars -> quota_cars_vole -> check_quota;

  cancel -> remove_from_quota -> quota_cars;
}
}}}

}}}}

==== Collective Scheduling ====

Consider two separate resources: both may be reserved at the same time by the
same organiser; neither resource would normally decline the reservation on the
basis of schedule availability, should the period concerned be free. However,
it may be undesirable for one organiser to occupy both resources at the same
time.

Consequently, a mechanism is required to pool the resource schedules in such a
way that any reservation performed for one resource at a given point in time
prohibits another reservation performed for a related resource at the same
point in time by the same user.

The free/busy records held for a given quota group permit such collective
scheduling decisions and are employed as follows:

{{{{#!table
'''Scheduling Functions''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
schedule_across_quota cars
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  subgraph {
    rank=same;
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
  }

  subgraph {
    rank=same;
    schedule_across_quota [label="Can be scheduled within the quota?",shape=ellipse,style=filled,fillcolor=gold];
    quota_cars [label="Quota for cars",shape=folder];
    freebusy_cars_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder];
  }

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  add_to_quota_freebusy [label="Add to quota free/busy",shape=ellipse,style=filled,fillcolor=darkorange];
  remove_from_quota_freebusy [label="Remove from quota free/busy",shape=ellipse,style=filled,fillcolor=darkorange];

  mail_cadillac -> schedule_across_quota;
  mail_pontiac -> schedule_across_quota -> schedule -> accept;
  schedule_across_quota -> decline [style=dashed];
  schedule -> add_to_quota_freebusy -> quota_cars -> freebusy_cars_vole;
  freebusy_cars_vole -> schedule_across_quota;

  cancel -> remove_from_quota_freebusy -> quota_cars;
}
}}}

}}}}

==== Delegating Attendance ====

A number of resources may be regarded as interchangeable and can therefore
stand in for each other when they are unavailable. The iCalendar specification
supports the notion of delegation: the recipient of an event invitation may
delegate their attendance to other calendar user, informing that user and the
event organiser of this decision.

To define such delegation relationships, a quota is first selected as the
repository of common scheduling information for a group of resources. Then,
the members of the group are defined as delegates. This can be done using a
tool provided for this purpose. For example:

{{{
cat <<EOF | tools/set_delegates.py 'cars'
mailto:resource-car-cadillac@example.com
mailto:resource-car-pontiac@example.com
EOF
}}}

Here, two resources are defined that will delegate to each other if they
cannot attend an event according to their own schedule.

{{{{#!table
'''Scheduling Functions''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
schedule_for_delegate cars
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  subgraph {
    rank=same;
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
  }

  subgraph {
    rank=same;
    schedule_for_delegate [label="Can be scheduled or delegated?",shape=ellipse,style=filled,fillcolor=gold];
    quota_cars [label="Quota for cars",shape=folder];
    freebusy_cars_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder];
  }

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    delegate [label="Delegate",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];

  mail_cadillac -> schedule_for_delegate;
  mail_pontiac -> schedule_for_delegate -> schedule -> accept;
  schedule_for_delegate -> delegate [style=dashed];
  schedule_for_delegate -> decline [style=dashed];
  schedule -> add_to_quota -> quota_cars -> freebusy_cars_vole;
  freebusy_cars_vole -> schedule_for_delegate;

  cancel -> remove_from_quota -> quota_cars;
}
}}}

}}}}

Note that it is generally more useful to have delegation decisions made on the
basis of many resources, which is what the journal entries for each quota
provides, but also considering organisers attempting to reserve those resources
as a single group whose reservations are consolidated and assessed collectively.
Thus, a single group of users would be defined:

{{{
cat <<EOF | tools/set_quota_groups.py 'cars'
* all
EOF
}}}

Although the journal for each quota may be divided up to administer quotas for
multiple groups of organisers, for the purposes of delegation - deciding
whether a resource is generally available, and deciding which other resource
would be available instead - a single group of all organisers is more desirable.

If limits need to be imposed alongside delegation, separate quotas may be used
to achieve this.

{{{{#!table
'''Scheduling Functions''' || '''Decision Process'''
==
<style="vertical-align: top;">

{{{
check_quota cars
schedule_for_delegate car_delegation
}}}

||

{{{#!graphviz
//format=svg
//transform=notugly
digraph scheduling_decisions {
  node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Scheduling decisions"];
  edge [tooltip="Scheduling decisions"];

  subgraph {
    rank=same;
    mail_cadillac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-cadillac@example.com",shape=folder,style=filled,fillcolor=cyan];
    mail_pontiac [label="Incoming mail\nfrom vincent.vole@example.com\nto resource-car-pontiac@example.com",shape=folder,style=filled,fillcolor=cyan];
    cancel [label="Incoming cancellation",shape=folder,style=filled,fillcolor=cyan];
  }

  subgraph {
    rank=same;
    check_quota [label="Is allowed by quota?",shape=ellipse,style=filled,fillcolor=gold];
    quota_cars [label="Quota for cars",shape=folder];
    quota_cars_vole [label="...applying to\nvincent.vole@example.com",shape=folder];
  }

  subgraph {
    rank=same;
    schedule_for_delegate [label="Can be scheduled or delegated?",shape=ellipse,style=filled,fillcolor=gold];
    quota_cars_delegation [label="Quota for cars_delegation",shape=folder];
    freebusy_cars_delegation_vole [label="...recording schedule for\nvincent.vole@example.com",shape=folder];
  }

  schedule [label="Schedule event for resource",shape=ellipse,style=filled,fillcolor=gold];

  subgraph {
    rank=same;
    accept [label="Accept",shape=folder,style=filled,fillcolor=cyan];
    delegate [label="Delegate",shape=folder,style=filled,fillcolor=cyan];
    decline [label="Decline",shape=folder,style=filled,fillcolor=cyan];
  }

  add_to_quota [label="Add to quota",shape=ellipse,style=filled,fillcolor=darkorange];
  remove_from_quota [label="Remove from quota",shape=ellipse,style=filled,fillcolor=darkorange];

  mail_cadillac -> check_quota;
  mail_pontiac -> check_quota;
  check_quota -> schedule_for_delegate;
  check_quota -> decline [style=dashed];
  schedule_for_delegate -> schedule -> accept;
  schedule_for_delegate -> delegate [style=dashed];
  schedule_for_delegate -> decline [style=dashed];
  schedule -> add_to_quota;
  add_to_quota -> quota_cars -> quota_cars_vole;
  add_to_quota -> quota_cars_delegation -> freebusy_cars_delegation_vole;
  quota_cars_vole -> check_quota;
  freebusy_cars_delegation_vole -> schedule_for_delegate;

  cancel -> remove_from_quota;
  remove_from_quota -> quota_cars;
  remove_from_quota -> quota_cars_delegation;
}
}}}

}}}}