club3 is a Common Lisp program that schedule a series of meetings of the Toastmaster™ type fairly.
Get club3 from GitHub:
git clone https://github.com/ds26gte/club3
This produces
a directory club3
,
which contains a shell script called club3
. Put a copy of or soft link to
club3
in your PATH
.
The subdirectory lisp
contains several Lisp files, the main file
being club3.lisp
. If you move club3.lisp
to a different
location, you must ensure that all the other .lisp
files in the
lisp
subdirectory move with it, as code in club3.lisp
assumes
that they are in the same directory as itself.
The script club3
sets and uses an environment variable CLUB3DIR
to
identify the directory containing club3.lisp
. Change it to suit you.
The script club3
also sets and uses an environment variable LISP
to identify
your Common Lisp system. Change to suit you.
Optional: You may compile club3.lisp
(using the Common Lisp function
compile‑file
). Only club3.lisp
need be explicitly compiled — it will
take care of compiling the other .lisp
files.
In a directory that contains all the data relevant to your club, call
club3
with the argument schedules
or profiles
.
The data in the current directory serves as input to both calls, and
influences the result. This input data consists of an init file
club3rc.lisp
and a subdirectory past‑schedules
which contains the
scheduling history of the club. For an example, see
the directory examples/articulators
in the distribution.
% club3 schedules
generates schedules. (I.e., type club3 schedules
at your Unix
prompt %
.)
(For convenience, the argument schedules
can be dropped:
calling club3
is equivalent to calling club3 schedules
.)
The schedules consist of a .txt
file for each date
in the calendar (see below), and an HTML file new‑schedules.html
that lists the schedules as an easy-to-read and -print table.
For a saved example, see
examples/articulators/current-schedules.html in the club3 distro.
The generated .txt
files can be inserted into past‑schedules
(see
below) to influence the course of future schedules (created by future
calls to club3 schedules
). Before putting a .txt
file into
past‑schedules
, it is a good idea to edit this file by hand to ensure
that it accurately records the members who actually performed the roles
(since club members typically negotiate substitutions up to the very
last minute because of unforeseen circumstances).
% club3 profiles
generates member profiles in a subdirectory called profiles
, browsable
via an index.html
file.
Example: examples/articulators/profiles/index.html
in the distro.
The init file club3rc.lisp
is essential. At the very least, it must
use the operators members
, lineups
, and calendar
to respectively list
the members in the club; the type of meeting lineups used; and the
calendar dates for which schedules are desired.
Other operators such as availability
, expertise
, eagerness
,
role‑difficulty
, and number‑of‑trials
are optional: However it is useful
to invoke some or all of them so that the generated schedules are both
equitable and feasible.
The operators full‑names
, role‑names
, and club‑name
help make the output
schedules more readable.
Example: examples/articulators/club3rc.lisp
in the distro.
The operator members
lists the members in the club, a symbol for each
member. E.g.,
(members andrew brad charlie dan eileen fran george hilary ian judy kim lori marcia nelson oliver prasad rick stephanie theresa ulrich vanessa willy xavier yul zoe)
The operator lineups
introduces the various prescribed lineups for the
club’s meetings. Each lineup is a list whose first element is a keyword
describing the type of meeting, and whose second element is a list of
roles.
(lineups (:default (tm word joke poet speaker speaker speaker ge eval eval eval topics)))
Roles can repeat: the above lineup allows for three speaker
s and three
eval
s.
The first lineup is considered the default lineup (even if it wasn’t
explicitly named :default
). lineups
can take more arguments if different
lineups are possible (for different meetings).
(lineups (:default ...) (:topics-tuesday (tm word joke poet speaker speaker ge eval eval topics)))
This specifies a meeting of type :topics‑tuesday
, which differs from the
:default
meeting in that its lineup has only two speaker
s and two eval
s.
(The purpose of this meeting is plausibly to allow more time for topics
than is possible in a :default
meeting.)
A type of meeting called :superset
may be optionally added. This
specifies the list of roles that will be specified in the HTML table
generated by club3 schedules
for a sequence of meetings, which may be
of different type. The idea is to list all the roles possible in all
the meetings, and have the column for each meeting fill only the row
that is relevant to it.
The operator calendar
contains a series of dates for which meetings need
to be scheduled. Each date is given as a dotted integer YYYY.MM.DD; thus,
2009.02.24 is 2009 Feb 24.
(calendar 2009.03.03 2009.03.10)
If the type of meeting to be held on a particular date differs from the
:default
type of meeting, then the date is enclosed as the first element
in a list whose second element is the type of meeting. E.g.,
(calendar 2009.03.03 (2009.03.10 :contest) 2009.03.17 2009.03.24 (2009.03.31 :skills-night) (2009.04.07 :topics-tuesday))
The type of meeting is typically one of the keywords introduced by the
lineups
operator (see above). However, it can be anything. If it isn’t one
of the keywords introduced by lineups
, then no schedule is presumed
for that date. (This is useful to mark certain dates for special
activities that do not require scheduling, or have been scheduled by
other means.)
The availability
operator contains exceptional information about the
temporal availability of the members. By default, members are assumed
to be always available. To override this for any member, that member’s
symbol is associated with an expression denoting
when that member is available.
Availability expressions are given in prefix notation using operators
such as after
, always
, before
, between
, from
,
on
, and until
whose arguments
are dates or the results of other operations.
and
, not
, and or
are also allowed, but can only operate on the results of
other operations (i.e., not directly on dates). [Use on
if you find
yourself wanting to use and
/not
/or
directly on dates. E.g.,
(not (on 2009.03.17))
.]
Dates are either in YYYY.MM.DD format or the keyword
:further‑notice
(= ∞).
Examples:
(availabilty (kim (not (between 2009.03.10 2009.03.21))))
states that kim
will not be available from March 3 to 21, 2009
(inclusive).
(availability (kim ...) (theresa (not (until :further-notice))))
says that theresa
will not be available until further notice.
(availability (kim ...) (theresa ...) (kumaran (and (not (on 2009.02.24 2009.04.28 2009.05.26 2009.06.30)) (not (between 2009.03.10 2009.03.31)))))
states that kumaran
will not be available on Feb 24, April 28, May 26, and
June 30 of 2009; and also between March 10 and 31 of 2009.
Using the role‑difficulty
operator, you can identify specific roles to
be either :easy
, :intermediate
, or :hard
. By default, all roles are
considered to be :easy
. Example entries:
(role-difficulty (speaker :intermediate) (eval :hard))
By default, every member is considered to be capable of doing all and
only the :easy
roles To modify this, use the expertise
operator. Each
argument is a list whose first element is the member name and whose
subsequent elements are the additional roles they can tackle beyond the
:easy
roles. E.g.,
(expertise (rick topics eval))
If you entered :intermediate
or :hard
instead of a role name, all roles
that are intermediate or hard, respectively, are intended.
(expertise (rick ...) (connie :intermediate))
The keyword :all
stands for all roles. Thus to encode the fact that
bash
can do anything:
(expertise (rick ...) (connie ...) (bash :all))
You may also enter a list such as (‑ speaker topics)
instead of a role.
This requests that the speaker
and topics
roles be removed from that
member’s expertise.
(expertise (rick ...) (connie ...) (bash ...) (judyk (- speaker topics)))
By default, all members are supposed to exhibit the same moderate level
of eagerness to be scheduled. However, using the eagerness
operator,
you can add an eagerness level to a member. Each argument consists of
the member name followed by a number between 0 and 3 inclusive: 0 for
inactive; 1 for taking it slow; 2 for normal (the default); and 3 for
fast track. E.g.,
(eagerness (joseph 3) (judyk 1) (theresa 0))
states that theresa
is inactive, judyk
wants to take it slow, and joseph
wants the fast track.
club3
uses a system that ascribes demerits to each member for a role on
any day (based on history, availability, etc). Since a lineup is filled
in a certain order, earlier roles may beat out later roles in getting
their best-suited members, leading to overall higher demerits. club3
tries to counter this by picking among eligible members using an
geometrically decaying distribution among them, so a member with higher
demerits could still snag a role. club3
then generates more, different
lineups, to see if another lineup with fewer overall demerits
materializes.
By default, club3
generates 400 trial lineups for each meeting date, and
picks the best among them. club3
output will also indicate the ordinal
n
of the eventually picked lineup, so you get an idea if the number of
trials is reasonable. (If n
is too close to 400, and the best lineup
was updated many times, perhaps more trials are needed.)
To change the number of trials, use the number‑of‑trials
operator, e.g.,
(number-of-trials 200)
The symbol used for each member is typically capitalized when printing
out schedules or profiles; thus, charlie
becomes “Charlie” and marcia
becomes “Marcia”. However, in certain cases, naive capitalization may
be inaccurate or unsightly. For instance, if there are several members
with the same (first) name, the members
operator may have used an
initial to disambiguate, e.g., judyk
and judyp
for two different Judys.
To properly capitalize them in the output schedule (so the Judys don’t
judge you), use the full‑names
operator:
(full-names (judyk "Judy K") (judyp "Judy P"))
You may add more complicated names:
(full-names (judyk ...) (judyp ...) (kelvin "William Thomson, First Baron Kelvin"))
For routine scheduling, however, a first name, sometimes enhanced with a disambiguating initial, is usually enough.
lineups
uses abbreviated symbols for each role. When printing out the
schedule as an HTML file, the roles are capitalized, and if there are
multiple instances of a role in a meeting, each role instance gets a number suffix
(e.g., “Speaker #2”). If you want the printed name for a role to be
different than the mere capitalization of the role symbol, you may use
the role‑names
operator, e.g.,
(role-names (tm "Toastmaster") (eval "Evaluator"))
The name of the club is set using the operator club‑name
. This name is
used to title the schedules.
(club-name "The Articulators")
The subdirectory past‑schedules
contains the schedules (the .txt
files)
generated for previous calendars — i.e., by previous calls to club3 schedules
. While optional, past‑schedules
is useful to maintain, as
it provides the historical information necessary to ensure that no
member is over- or underscheduled.
club3
runs on any Common Lisp in a Unix-like environment, e.g.,
ABCL,
CLISP,
Clozure CL,
CMUCL,
MKCL,
ECL, and
SBCL.
(On Windows, you can get a Unix
environment via Cygwin, which also
provides CLISP.)