1/*	$Id$ */
2/*
3 * Copyright (c) 2014--2016 Kristaps Dzonsons <kristaps@kcons.eu>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18PRAGMA journal_mode=WAL;
19
20-- The winner table consists of rows corresponding to a player and her
21-- winner status. This table does not exist until the @experiment.state
22-- field is set to 3. This table is deleted when the database is wiped.
23
24CREATE TABLE winner (
25	-- Participant.
26	playerid INTEGER REFERENCES player(id) NOT NULL,
27	-- Boolean value as to whether the player is a winner. If this
28	-- is false, then the @winner.winrank and @winner.rnum columns
29	-- are undefined.
30	winner BOOLEAN NOT NULL,
31	-- If the player is a winner, the rank (first, second, third
32	-- draw...) of their winning.
33	winrank INTEGER NOT NULL,
34	-- The random number modulo the total number of tickts used for
35	-- this winning draw. In other words, this is the winning
36	-- lottery ticket.
37	rnum INTEGER NOT NULL,
38	-- Unique identifier.
39	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
40	unique (playerid)
41);
42
43-- The central player table consists of all participants. We use the
44-- term ``participant'' instead of player in the literature to
45-- disambiguate a player role and an experiment subject.  The
46-- @player.hash variable is redacted when the experiment is wiped.
47
48CREATE TABLE player (
49	-- Player e-mail address or identifier, in the event of a
50	-- captive participant. This is unique in the set of all
51	-- participants.
52	email TEXT NOT NULL,
53	-- If the participant joined from Mechanical Turk, the HIT
54	-- identifier. Otherwise set to the empty string.
55	hitid TEXT NOT NULL,
56	-- If the participant joined from Mechanical Turk, the
57	-- assignment identifier. Otherwise set to the empty string.
58	assignmentid TEXT NOT NULL,
59	-- Whether the Mechanical Turk participant, if applicable, has
60	-- indicated that they've finished the experiment. Note that
61	-- this is unprotected and can be set at any time: it is only
62	-- used to display a message to report to the Mechanical Turk
63	-- server.
64	mturkdone INTEGER NOT NULL DEFAULT(0),
65	-- The state of a participant can be 0, meaning the participant
66	-- is newly-added and has no password; 1 when the participant
67	-- has been mailed her password; 2, the participant has logged
68	-- in; and 3, an error occured when the password e-mail was
69	-- attempted.
70	state INTEGER NOT NULL DEFAULT(0),
71	-- Whether a participant is allowed to login during an experiment.
72	enabled INTEGER NOT NULL DEFAULT(1),
73	-- A cryptographically random number >0 given to the participant
74	-- when created. This is used for many purposes, one of which
75	-- being the bonus identifier for Mechanical Turk participants.
76	rseed INTEGER NOT NULL,
77	-- The player role (0 for row player, 1 for column player) set
78	-- when @experiment.state is >0 or when the participant joins.
79	role INTEGER NOT NULL DEFAULT(0),
80	-- Player was auto-added (i.e., auto-added herself). These
81	-- participants are never e-mailed.
82	autoadd INTEGER NOT NULL DEFAULT(0),
83	-- If the questionnaire facility has been enabled (via
84	-- @experiment.questionnaire), this is non-zero if the
85	-- questionnaire has been answered.
86	answer INTEGER NOT NULL DEFAULT(0),
87	-- Whether the participant should be shown instructions when she
88	-- logs in (versus being taken directly to the game-play tab).
89	instr INTEGER NOT NULL DEFAULT(1),
90	-- The round when the participant joined (i.e., started playing)
91	-- the experiment.  This is set to zero when participants join
92	-- at the outset.  If -1, the participant exists, but has not
93	-- yet started participating in the experiment.
94	joined INTEGER NOT NULL DEFAULT(-1),
95	-- The minimum of the slot of the participant's tickets among
96	-- all participants' tickets. For example, given 100
97	-- participants with roughly 10 tickets each, this might be 543
98	-- to indicate that slot 543 to 543 plus @player.finalscore are
99	-- this participant's slots in the lottery.
100	-- Mechanical Turk players are not included in this computation,
101	-- so this value is not meaningful for them.
102	finalrank INTEGER NOT NULL DEFAULT(0),
103	-- Set to the accumulated payoffs from @lottery.aggrpayoff
104	-- rounded up to the nearest integer.
105	-- Mechanical Turk players are not included in this computation,
106	-- so this value is not meaningful for them.
107	finalscore INTEGER NOT NULL DEFAULT(0),
108	-- A number from zero that indicates the number of time the
109	-- @player columns have been updated. This is used to make the
110	-- object cachable.
111	version INTEGER NOT NULL DEFAULT(0),
112	-- The participant's password set when the experiment is started
113	-- (i.e., when @experiment.state is >0) or when reset, or when a
114	-- captive participant registeres. Note: this is stored in the
115	-- clear: it is not a hash!
116	hash TEXT,
117	-- Unique identifier.
118	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
119	UNIQUE (email)
120);
121
122-- A lottery is created for an individual @player when the corresponding
123-- participant has been granted @payoff for all @game rows in a round.
124-- This doesn't really have anything to do with the ``lottery'' concept
125-- of reward: the name is just an historic holdover.
126
127CREATE TABLE lottery (
128	-- The round number (starting at zero) for which this lottery
129	-- was computed.
130	round INTEGER NOT NULL,
131	-- The participant owning the lottery.
132	playerid INTEGER REFERENCES player(id) NOT NULL,
133	-- The player's aggregate payoff (as a rational number)
134	-- computing by adding the previous round's aggregate payoff to
135	-- the current @"lottery.curpayoff".
136	aggrpayoff TEXT NOT NULL,
137	-- The value of @lottery.aggrpayoff represented as a natural
138	-- number of tickets.
139	aggrtickets INTEGER NOT NULL DEFAULT(0),
140	-- The participant's current payoff (as a rational number)
141	-- computing by accumulating her @payoff.payoff for all games in
142	-- the experiment. This is set to 0/1 if the participant has not
143	-- played all games.
144	curpayoff TEXT NOT NULL,
145	-- Unique identifier.
146	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
147	UNIQUE (round, playerid)
148);
149
150-- During a given round, this records a @"player"'s status in terms of
151-- number of @choice rows (plays) made.
152
153CREATE TABLE gameplay (
154	-- The round number (starting at zero).
155	round INTEGER NOT NULL,
156	-- The number of games this participant has played, i.e., the
157	-- count of @choice rows.
158	choices INTEGER NOT NULL DEFAULT(0),
159	-- Participant identifier.
160	playerid INTEGER REFERENCES player(id) NOT NULL,
161	-- Unique identifier.
162	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
163	UNIQUE (round, playerid)
164);
165
166-- When the given round has completed, this consists of the payoff of
167-- the participant's @choice strategy mix for a given @game when played
168-- against the average strategy of the opposing player role.
169
170CREATE TABLE payoff (
171	-- The round number (starting at zero).
172	round INTEGER NOT NULL,
173	-- Participant identifier.
174	playerid INTEGER REFERENCES player(id) NOT NULL,
175	-- Game identifier.
176	gameid INTEGER REFERENCES game(id) NOT NULL,
177	-- A rational number of the payoff.
178	payoff TEXT NOT NULL,
179	-- Unique identifier.
180	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
181	UNIQUE (round, playerid, gameid)
182);
183
184-- A strategy mixture created when a @player plays a round in a @"game".
185
186CREATE TABLE choice (
187	-- The round number (starting at zero).
188	round INTEGER NOT NULL,
189	-- A text (space-separated) list of rational numbers of the
190	-- strategy mixture ordered from the top if a row-playing role
191	-- (i.e., @player.role) or left if a column-player.
192	strats TEXT NOT NULL,
193	-- Number of entries in @"choice.strats". This obviously equals
194	-- the number of strategies available to the participant in that
195	-- @game, given the player role.
196	stratsz INTEGER NOT NULL,
197	-- The participant.
198	playerid INTEGER REFERENCES player(id) NOT NULL,
199	-- The game.
200	gameid INTEGER REFERENCES game(id) NOT NULL,
201	-- The session.
202	sessid INTEGER REFERENCES sess(id) NOT NULL,
203	-- When this was created (epoch).
204	created INTEGER NOT NULL,
205	-- Unique identifier.
206	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
207	UNIQUE (round, playerid, gameid)
208);
209
210-- The questionnaire is an optional set of questions given to each
211-- participant.  These participants must submit a set number of correct
212-- answers before joining the game.  Only a participant's first answer
213-- counts.
214
215CREATE TABLE questionnaire (
216	-- Participant.
217	playerid INTEGER REFERENCES player(id) NOT NULL,
218	-- The number of tries, correct or not. This is zero only in the
219	-- race after initially creating the row and noting an attempt.
220	-- It should not be used and will be removed.
221	tries INTEGER NOT NULL DEFAULT(0),
222	-- The question number starting at zero.
223	rank INTEGER NOT NULL,
224	-- The epoch time when the row is created.
225	first INTEGER NOT NULL,
226	-- Unique identifier.
227	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
228	UNIQUE (playerid, rank)
229);
230
231-- This describes an experiment configured by the experimenter (see
232-- @admin). There is always one (and only one) row in this table.
233
234CREATE TABLE experiment (
235	-- The running state of the experiment, being either 0, for new
236	-- (still in the configuration stage); 1, for started
237	-- (participants can log in, though the experiment itself may
238	-- not be accepting plays yet); 2, where the experiment has
239	-- expired, but the winner has not been chosen by the
240	-- experimenter; or 3, where the experiment has expired and the
241	-- winner has been chosen.
242	state INTEGER NOT NULL DEFAULT(0),
243	-- Epoch when the experiment when the first round of the
244	-- experiment begins.
245	start INTEGER DEFAULT(0),
246	-- The number of rounds that will be played. Minimum of one.
247	rounds INTEGER DEFAULT(0),
248	-- The maximum number of participants per player role at any
249	-- given time. If zero, the number of participants is unbound.
250	playermax INTEGER DEFAULT(0),
251	-- If specified when configuring the experiment, a daemon will
252	-- be started that periodically checks to see if the round has
253	-- advanced. Its process identifier is stored in this field.
254	-- Otherwise, this is zero.
255	roundpid INTEGER DEFAULT(0),
256	-- The number of rounds playable by each participant. If zero,
257	-- participants will play until the end of the experiment. If
258	-- >0, participants will not be allowed to play more than the
259	-- given number of rounds within the experiment.
260	prounds INTEGER DEFAULT(0),
261	-- The current round of the experiment, or -1 if the rounds have
262	-- not begun incrementing. This will be set to
263	-- @experiment.rounds when the experiment concludes.
264	round INTEGER DEFAULT(-1),
265	-- If round is non-negative, then the epoch when the round was
266	-- incremented. Otherwise, this is zero.
267	roundbegan INTEGER DEFAULT(0),
268	-- If >0, this represents the fraction of participants per
269	-- player role who play all games and determine that the round
270	-- automatically advances. In other words, if set to 0.5 (50%),
271	-- then from both player roles, if 50% or more participants have
272	-- played all games, the round advances in advance of the set
273	-- round termination time.
274	roundpct REAL DEFAULT(0),
275	-- If roundpct is non-zero, this is the ``grace time'' (in
276	-- minutes) before which the round will automatically advance
277	-- giving the percentage.
278	roundmin INTEGER DEFAULT(0),
279	-- The number of minutes per @"experiment.round". Minimum of
280	-- one.
281	minutes INTEGER DEFAULT(0),
282	-- The experiment is currently accepting (or did accept)
283	-- auto-added participants.
284	autoadd INTEGER NOT NULL DEFAULT(0),
285	-- HIT (Mechanical Turk) identifier given by the Mechanical Turk
286	-- server for an mturk experiment.  Otherwise, an empty string.
287	hitid TEXT NOT NULL DEFAULT(''),
288	-- Preserve the @experiment.autoadd state during and after the
289	-- experiment start. This is useful for rolling experiments so
290	-- as not to have a break between starting the experiment and
291	-- re-enabling captive mode.
292	autoaddpreserve INTEGER NOT NULL DEFAULT(0),
293	-- A JSON file that provides a ``fake'' history prepended to the
294	-- actual experiment history.
295	-- This is only shown to users through the browser interface.
296	history TEXT NOT NULL DEFAULT(''),
297	-- Human-readable amount awarded in lottery, e.g., 10 000 SEK.
298	-- If set to the empty string, there's no lottery.
299	lottery TEXT NOT NULL DEFAULT(''),
300	-- If non-zero, new participants are not directly assigned a
301	-- join round and must use the lobby facility. There, they will
302	-- be asked questions and cannot proceed until all questions
303	-- have been answered.  See @"questionnaire".
304	questionnaire INTEGER NOT NULL DEFAULT(0),
305	-- Set when the experiment begins (i.e., when @experiment.state
306	-- >0), this is the URL given to players in their initial e-mail
307	-- for when they log in. This is suffixed by
308	-- ``?ident=EMAIL&password=PASSWORD''.
309	loginuri TEXT DEFAULT(''),
310	-- The instructions shown to participants. This must contain
311	-- valid HTML5. It may contain \@\@gamelab-admin-email\@\@, which is
312	-- filled in with the experimenter's configured e-mail address;
313	-- \@\@gamelab-games\@\@ for the number of games;
314	-- \@\@gamelab-rounds\@\@ for the number of rounds; and
315	-- \@\@gamelab-round-time\@\@, a decimal number of the number of
316	-- hours per round.
317	instr TEXT DEFAULT(''),
318	-- When the experiment finishes (i.e., when @experiment.state is
319	-- set to 2), this is filled in with the total number of lottery
320	-- tickets (the @player.finalscore) awarded to all participants.
321	total INTEGER DEFAULT(0),
322	-- Bit-field. Contains 0x01 if the history is not to be
323	-- transmitted to participants, 0x02 if games and rows shouldn't
324	-- be randomised prior to display, and 0x04 to show participants
325	-- the relative count of rounds---relative to when they started
326	-- to play.
327	flags INTEGER DEFAULT(0),
328	-- If not an empty string, a Mechanical Turk access key.  This
329	-- and @experiment.awssecretkey must be set in order to enable a
330	-- Mechanical Turk experiment.
331	awsaccesskey TEXT NOT NULL DEFAULT(''),
332	-- If not an empty string, a Mechanical Turk secret key.  This
333	-- and @experiment.awsaccesskey must be set in order to enable a
334	-- Mechanical Turk experiment.
335	awssecretkey TEXT NOT NULL DEFAULT(''),
336	-- If not an empty string, set to an error from a preceeding
337	-- Mechanical Turk ``start new HIT'' operation that failed.
338	awserror TEXT NOT NULL DEFAULT(''),
339	-- How many Mechanical Turk workers to request, if applicable.
340	awsworkers INTEGER NOT NULL DEFAULT(2),
341	-- What to call this Mechanical Turk experiment, if applicable.
342	awsname TEXT NOT NULL DEFAULT(''),
343	-- Description of this Mechanical Turk experiment, if
344	-- applicable.
345	awsdesc TEXT NOT NULL DEFAULT(''),
346	-- Keywords (comma-separated) of this Mechanical Turk
347	-- experiment, if applicable.
348	awskeys TEXT NOT NULL DEFAULT(''),
349	-- Whether to use the Mechanical Turk sandbox in all enquiries,
350	-- if applicable.
351	awssandbox INTEGER NOT NULL DEFAULT(1),
352	-- How to convert a Mechanical Turk participant's final score
353	-- (tickets) into real currency, if applicable.
354	awsconvert REAL NOT NULL DEFAULT(1),
355	-- The base reward to give to Mechanical Turk participants, if
356	-- applicable.
357	awsreward REAL NOT NULL DEFAULT(0.01),
358	-- If non-empty and applicable, the locale to request (country
359	-- code) for Mechanical Turk participants.
360	awslocale TEXT NOT NULL DEFAULT(''),
361	-- If >=0 and applicable, the number of HITs for each Mechanical
362	-- Turk participant.
363	awswhitappr INTEGER NOT NULL DEFAULT(-1),
364	-- If >=0 and applicable, the percent (out of 100) of approved
365	-- HITs for each Mechanical Turk participant.
366	awswpctappr INTEGER NOT NULL DEFAULT(-1),
367	-- Unique identifier.
368	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
369);
370
371-- A game is a payoff bimatrix configured by the @"admin". There may be any
372-- non-zero number of games in a running @"experiment".
373
374CREATE TABLE game (
375	-- Number of strategies for row player.
376	p1 INTEGER NOT NULL,
377	-- Number of strategies for column player.
378	p2 INTEGER NOT NULL,
379	-- A list of space-separated payoffs from the top-left to the
380	-- bottom-right of the payoff matrix, ordered row-player payoff,
381	-- column-player payoff.
382	payoffs TEXT NOT NULL,
383	-- The name of the experiment. This is shown only to the
384	-- @"admin".
385	name TEXT NOT NULL,
386	-- Unique identifier.
387	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
388);
389
390-- A browser (or otherwise www) session. Sessions are the usual browser
391-- session used when participants (or the experimenter) are interacting
392-- with the system.
393
394CREATE TABLE sess (
395	-- Epoch when session was created.
396	created INTEGER NOT NULL,
397	-- Magic random cookie for each session.
398	cookie INTEGER NOT NULL,
399	-- Participant identifier, if not NULL.  Otherwise, this is the
400	-- @admin logged in.
401	playerid INTEGER REFERENCES player(id) DEFAULT NULL,
402	-- Browser-reported user agent.
403	useragent TEXT NOT NULL,
404	-- Unique identifier.
405	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
406);
407
408-- Experimenter (a.k.a. administrator) credentials. There is always one
409-- (and only one) row that exists, initialised to default values.
410
411CREATE TABLE admin (
412	-- The experimenter e-mail address. This is used as a predefined
413	-- template value for the instructions. It is also used as the
414	-- destination for backups of the database. Initialises to
415	-- foo\@example.com.
416	email TEXT NOT NULL,
417	-- The experimenter password. Note: this is stored as cleartext,
418	-- so it is not really a hash. Initialises to xyzzy.
419	hash TEXT NOT NULL,
420	-- Bit-field on whether the email and hash have been set by the
421	-- user. Contains 0x01 if the e-mail has been set, 0x02 for the
422	-- password.
423	isset INTEGER NOT NULL DEFAULT(0),
424	-- Unique identifier.
425	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
426);
427
428-- Records the average strategy mixture of a given player role for a
429-- given round and game when a round has concluded.
430
431CREATE TABLE past (
432	-- The round starting at zero.
433	round INTEGER NOT NULL,
434	-- Game being referenced.
435	gameid INTEGER REFERENCES game(id) NOT NULL,
436	-- Unique identifier.
437	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
438	-- For the row player, the average strategy mixture of the
439	-- current round. Strategy mixtures are only considered for
440	-- @choice rows where the @player played all games for the
441	-- round, so this will be a set of zeroes if no participant
442	-- completed all games. This is recorded as a space-separated
443	-- sequence of rational numbers, one per strategy.
444	currentsp1 TEXT NOT NULL,
445	-- Like @past.currentsp1 but for the column player role.
446	currentsp2 TEXT NOT NULL,
447	-- If zero row or column-player participants played all games,
448	-- this is set to 1, else it is 0 (sufficient players played).
449	skip INTEGER NOT NULL,
450	-- The total number of plays that were submitted (over all
451	-- games) in this round if and only if the participant submitted
452	-- for all games in that round.
453	plays INTEGER NOT NULL,
454	-- The accumulated count of rounds not skipping, i.e., the count
455	-- of zero-valued skip rounds.
456	roundcount INTEGER NOT NULL,
457	unique (round, gameid)
458);
459
460-- This consists of the SMTP server information used in sending e-mails.
461-- There is always only one row set, which defaults to empty values (see
462-- @smtp.isset).
463
464CREATE TABLE smtp (
465	-- The SMTP server username (used for logging in).
466	user TEXT NOT NULL DEFAULT(''),
467	-- The e-mail address used as the ``From'' address in all
468	-- communication from the server to participants in the
469	-- experimenter. It is usually the same as the experimenter
470	-- email set in @"admin", but may be set as a standard
471	-- ``No-Reply''. (This is discouraged, as your participants
472	-- should be able to reply to you if things go wrong.)
473	email TEXT NOT NULL DEFAULT(''),
474	-- The SMTP username's password (used for logging in). Note that
475	-- this is stored in cleartext, so make sure that your password
476	-- isn't used elsewhere.
477	pass TEXT NOT NULL DEFAULT(''),
478	-- The SMTP server in smtp://server:port format.
479	server TEXT NOT NULL DEFAULT(''),
480	-- Whether these entries have been set.
481	isset INTEGER NOT NULL DEFAULT(0),
482	-- Unique identifier.
483	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
484);
485
486INSERT INTO experiment DEFAULT VALUES;
487INSERT INTO smtp DEFAULT VALUES;
488
489INSERT INTO admin (email, hash) VALUES ('foo@example.com', 'xyzzy');
490