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