3
CREATE OR REPLACE FUNCTION valid_url_name(name text) RETURNS boolean AS
6
RETURN name ~ E'^[a-z0-9][a-z0-9_\+\.\-]*$';
10
CREATE OR REPLACE FUNCTION valid_login_name(name text) RETURNS boolean AS
13
RETURN name ~ E'^[a-z0-9][a-z0-9@_\+\.\-]*$';
15
$$ LANGUAGE 'plpgsql';
17
CREATE SEQUENCE login_unixid_seq MINVALUE 1000 MAXVALUE 29999 START WITH 5000;
1
DROP TABLE users CASCADE;
3
login VARCHAR UNIQUE NOT NULL,
20
4
loginid SERIAL PRIMARY KEY NOT NULL,
21
login VARCHAR UNIQUE NOT NULL CHECK (valid_login_name(login)),
23
state VARCHAR NOT NULL CHECK (state in ('no_agreement', 'pending',
24
'enabled', 'disabled'))
25
DEFAULT 'no_agreement',
26
admin BOOLEAN NOT NULL DEFAULT false,
27
unixid INT UNIQUE DEFAULT nextval('login_unixid_seq') NOT NULL,
28
nick VARCHAR NOT NULL,
34
fullname VARCHAR NOT NULL,
35
studentid VARCHAR, -- may be null
40
CREATE TABLE subject (
41
subjectid SERIAL PRIMARY KEY NOT NULL,
42
subj_code VARCHAR UNIQUE NOT NULL,
43
subj_name VARCHAR NOT NULL,
44
subj_short_name VARCHAR UNIQUE NOT NULL CHECK (valid_url_name(subj_short_name))
47
CREATE TABLE semester (
48
semesterid SERIAL PRIMARY KEY NOT NULL,
49
year TEXT NOT NULL CHECK (valid_url_name(year)),
50
url_name TEXT NOT NULL CHECK (valid_url_name(url_name)),
52
display_name TEXT NOT NULL,
53
state TEXT NOT NULL CHECK (state IN ('disabled', 'past',
54
'current', 'future')) DEFAULT 'current',
55
UNIQUE (year, url_name),
59
CREATE TABLE offering (
7
studentid VARCHAR -- may be null
10
DROP TABLE offerings CASCADE;
11
CREATE TABLE offerings (
60
12
offeringid SERIAL PRIMARY KEY NOT NULL,
61
subject INT4 REFERENCES subject (subjectid) NOT NULL,
62
semesterid INTEGER REFERENCES semester (semesterid) NOT NULL,
65
show_worksheet_marks BOOLEAN NOT NULL DEFAULT false,
66
worksheet_cutoff TIMESTAMP,
67
groups_student_permissions VARCHAR NOT NULL DEFAULT 'none',
68
CHECK (groups_student_permissions in ('none', 'invite', 'create')),
69
UNIQUE (subject, semesterid)
73
CREATE TABLE project_set (
74
projectsetid SERIAL PRIMARY KEY NOT NULL,
75
offeringid INTEGER REFERENCES offering (offeringid) NOT NULL,
76
max_students_per_group INTEGER
79
CREATE TABLE project (
80
projectid SERIAL PRIMARY KEY NOT NULL,
81
short_name TEXT NOT NULL CHECK (valid_url_name(short_name)),
85
projectsetid INTEGER REFERENCES project_set (projectsetid) NOT NULL,
86
deadline TIMESTAMP NOT NULL
89
CREATE OR REPLACE FUNCTION check_project_namespacing_insertupdate()
94
IF TG_OP = ''UPDATE'' THEN
95
IF NEW.projectsetid = OLD.projectsetid AND NEW.short_name = OLD.short_name THEN
99
SELECT offeringid INTO oid FROM project_set WHERE project_set.projectsetid = NEW.projectsetid;
100
PERFORM 1 FROM project, project_set
101
WHERE project_set.offeringid = oid AND
102
project.projectsetid = project_set.projectsetid AND
103
project.short_name = NEW.short_name;
105
RAISE EXCEPTION ''a project named % already exists in offering ID %'', NEW.short_name, oid;
109
' LANGUAGE 'plpgsql';
111
CREATE TRIGGER check_project_namespacing
112
BEFORE INSERT OR UPDATE ON project
113
FOR EACH ROW EXECUTE PROCEDURE check_project_namespacing_insertupdate();
115
CREATE TABLE project_group (
116
groupnm VARCHAR NOT NULL CHECK (valid_url_name(groupnm)),
13
subj_name VARCHAR NOT NULL,
14
subj_code VARCHAR NOT NULL,
15
year CHAR(4) NOT NULL,
19
DROP TABLE groups CASCADE;
21
groupnm VARCHAR NOT NULL,
117
22
groupid SERIAL PRIMARY KEY NOT NULL,
118
projectsetid INTEGER REFERENCES project_set (projectsetid) NOT NULL,
23
offeringid INT4 REFERENCES offerings (offeringid),
120
createdby INT4 REFERENCES login (loginid) NOT NULL,
121
epoch TIMESTAMP NOT NULL,
122
UNIQUE (projectsetid, groupnm)
25
UNIQUE (offeringid, groupnm)
125
CREATE OR REPLACE FUNCTION check_group_namespacing_insertupdate()
130
SELECT offeringid INTO oid FROM project_set WHERE project_set.projectsetid = NEW.projectsetid;
131
PERFORM 1 FROM project_group, project_set WHERE project_set.offeringid = oid AND project_group.projectsetid = project_set.projectsetid AND project_group.groupnm = NEW.groupnm;
133
RAISE EXCEPTION ''a project group named % already exists in offering ID %'', NEW.groupnm, oid;
137
' LANGUAGE 'plpgsql';
139
CREATE TRIGGER check_group_namespacing
140
BEFORE INSERT OR UPDATE ON project_group
141
FOR EACH ROW EXECUTE PROCEDURE check_group_namespacing_insertupdate();
143
CREATE TABLE group_invitation (
144
loginid INT4 REFERENCES login (loginid) NOT NULL,
145
groupid INT4 REFERENCES project_group (groupid) NOT NULL,
146
inviter INT4 REFERENCES login (loginid) NOT NULL,
147
invited TIMESTAMP NOT NULL,
28
DROP TABLE group_invitations CASCADE;
29
CREATE TABLE group_invitations (
30
loginid INT4 REFERENCES users (loginid),
31
groupid INT4 REFERENCES groups (groupid),
149
32
UNIQUE (loginid,groupid)
152
CREATE TABLE group_member (
153
loginid INT4 REFERENCES login (loginid),
154
groupid INT4 REFERENCES project_group (groupid),
35
DROP TABLE group_members CASCADE;
36
CREATE TABLE group_members (
37
loginid INT4 REFERENCES users (loginid),
38
groupid INT4 REFERENCES groups (groupid),
39
projectid INT4 REFERENCES projects (projectid),
40
UNIQUE (loginid,projectid),
155
41
PRIMARY KEY (loginid,groupid)
44
DROP TABLE enrolment CASCADE;
158
45
CREATE TABLE enrolment (
159
loginid INT4 REFERENCES login (loginid),
160
offeringid INT4 REFERENCES offering (offeringid),
161
role TEXT NOT NULL CHECK (role IN ('student', 'tutor',
162
'lecturer')) DEFAULT 'student',
46
loginid INT4 REFERENCES users (loginid),
47
offeringid INT4 REFERENCES offerings (offeringid),
164
special_result VARCHAR,
166
special_supp_result VARCHAR,
168
active BOOL NOT NULL DEFAULT true,
169
51
PRIMARY KEY (loginid,offeringid)
172
CREATE TABLE assessed (
173
assessedid SERIAL PRIMARY KEY NOT NULL,
174
loginid INT4 REFERENCES login (loginid),
175
groupid INT4 REFERENCES project_group (groupid),
176
projectid INT4 REFERENCES project (projectid) NOT NULL,
177
-- exactly one of loginid and groupid must be non-null
178
CHECK ((loginid IS NOT NULL AND groupid IS NULL)
179
OR (loginid IS NULL AND groupid IS NOT NULL))
182
CREATE UNIQUE INDEX assessed_loginid_key ON assessed(loginid, projectid) WHERE loginid IS NOT NULL;
183
CREATE UNIQUE INDEX assessed_groupid_key ON assessed(groupid, projectid) WHERE groupid IS NOT NULL;
54
DROP TABLE roles CASCADE;
56
loginid INT4 PRIMARY KEY REFERENCES users (loginid),
60
DROP TABLE projects CASCADE;
61
CREATE TABLE projects (
62
projectid SERIAL PRIMARY KEY NOT NULL,
65
offeringid INT4 REFERENCES offerings (offeringid) NOT NULL,
69
DROP TABLE project_extension CASCADE;
185
70
CREATE TABLE project_extension (
186
extensionid SERIAL PRIMARY KEY,
187
assessedid INT4 REFERENCES assessed (assessedid) NOT NULL,
189
approver INT4 REFERENCES login (loginid) NOT NULL,
193
CREATE TABLE project_submission (
194
submissionid SERIAL PRIMARY KEY,
195
assessedid INT4 REFERENCES assessed (assessedid) NOT NULL,
196
path VARCHAR NOT NULL,
197
revision INT4 NOT NULL,
198
date_submitted TIMESTAMP NOT NULL,
199
submitter INT4 REFERENCES login (loginid) NOT NULL
71
loginid INT4 REFERENCES users (loginid),
72
groupid INT4 REFERENCES groups (groupid),
73
projectid INT4 REFERENCES projects (projectid) NOT NULL,
74
deadline TIMESTAMP NOT NULL,
75
approver INT4 REFERENCES users (loginid) NOT NULL,
77
-- exactly one of loginid and groupid must be non-null
78
CHECK ((loginid IS NOT NULL AND groupid IS NULL)
79
OR (loginid IS NULL AND groupid IS NOT NULL))
82
DROP TABLE project_mark CASCADE;
202
83
CREATE TABLE project_mark (
203
assessedid INT4 REFERENCES assessed (assessedid) NOT NULL,
84
loginid INT4 REFERENCES users (loginid),
85
groupid INT4 REFERENCES groups (groupid),
86
projectid INT4 REFERENCES projects (projectid) NOT NULL,
205
marker INT4 REFERENCES login (loginid) NOT NULL,
88
marker INT4 REFERENCES users (loginid) NOT NULL,
212
CREATE TABLE exercise (
213
identifier TEXT PRIMARY KEY CHECK (valid_url_name(identifier)),
216
description_xhtml_cache TEXT,
223
CREATE TABLE worksheet (
224
worksheetid SERIAL PRIMARY KEY,
225
offeringid INT4 REFERENCES offering (offeringid) NOT NULL,
226
identifier TEXT NOT NULL CHECK (valid_url_name(identifier)),
229
data_xhtml_cache TEXT,
230
assessable BOOLEAN NOT NULL,
231
published BOOLEAN NOT NULL DEFAULT true,
232
seq_no INT4 NOT NULL,
233
format TEXT NOT NUll,
234
UNIQUE (offeringid, identifier)
237
CREATE TABLE worksheet_exercise (
238
ws_ex_id SERIAL PRIMARY KEY,
239
worksheetid INT4 REFERENCES worksheet (worksheetid) NOT NULL,
240
exerciseid TEXT REFERENCES exercise (identifier) NOT NULL,
241
seq_no INT4 NOT NULL,
242
active BOOLEAN NOT NULL DEFAULT true,
243
optional BOOLEAN NOT NULL,
244
UNIQUE (worksheetid, exerciseid)
247
CREATE TABLE exercise_attempt (
248
loginid INT4 REFERENCES login (loginid) NOT NULL,
249
ws_ex_id INT4 REFERENCES worksheet_exercise (ws_ex_id) NOT NULL,
250
date TIMESTAMP NOT NULL,
251
attempt TEXT NOT NULL,
93
-- exactly one of loginid and groupid must be non-null
94
CHECK ((loginid IS NOT NULL AND groupid IS NULL)
95
OR (loginid IS NULL AND groupid IS NOT NULL))
98
DROP TABLE problem CASCADE;
99
CREATE TABLE problem (
100
problemid SERIAL PRIMARY KEY NOT NULL,
104
DROP TABLE problem_tags CASCADE;
105
CREATE TABLE problem_tags (
106
problemid INT4 REFERENCES tutorial_problem (problemid),
107
tag VARCHAR NOT NULL,
108
added_by INT4 REFERENCES users (loginid) NOT NULL,
109
date TIMESTAMP NOT NULL,
110
PRIMARY KEY (problemid,added_by,tag)
113
DROP TABLE problem_test_case CASCADE;
114
CREATE TABLE problem_test_case (
115
problemid INT4 REFERENCES problem (problemid) NOT NULL,
116
testcaseid SERIAL UNIQUE NOT NULL,
119
visibility VARCHAR CHECK (visibility in ('public', 'protected', 'private'))
122
DROP TABLE problem_test_case_tags CASCADE;
123
CREATE TABLE problem_test_case_tags (
124
testcaseid INT4 REFERENCES problem_test_case (testcaseid) NOT NULL,
125
tag VARCHAR NOT NULL,
127
added_by INT4 REFERENCES users (loginid) NOT NULL,
128
date TIMESTAMP NOT NULL,
129
PRIMARY KEY (testcaseid,added_by,tag)
132
DROP TABLE problem_attempt CASCADE;
133
CREATE TABLE problem_attempt (
134
problemid INT4 REFERENCES problem (problemid) NOT NULL,
135
loginid INT4 REFERENCES users (loginid) NOT NULL,
136
date TIMESTAMP NOT NULL,
137
attempt VARCHAR NOT NULL,
252
138
complete BOOLEAN NOT NULL,
253
active BOOLEAN NOT NULL DEFAULT true,
254
PRIMARY KEY (loginid, ws_ex_id, date)
139
PRIMARY KEY (problemid,loginid,date)
257
CREATE TABLE exercise_save (
258
loginid INT4 REFERENCES login (loginid) NOT NULL,
259
ws_ex_id INT4 REFERENCES worksheet_exercise (ws_ex_id) NOT NULL,
142
DROP INDEX problem_attempt_index;
143
CREATE INDEX problem_attempt_index ON problem_attempt (problemid, loginid);
145
DROP TABLE problem_attempt_breakdown CASCADE;
146
CREATE TABLE problem_attempt_breakdown (
147
problemid INT4 REFERENCES problem (problemid) NOT NULL,
148
testcaseid INT4 REFERENCES problem_test_case (testcaseid) NOT NULL,
149
loginid INT4 REFERENCES users (loginid) NOT NULL,
260
150
date TIMESTAMP NOT NULL,
262
PRIMARY KEY (loginid, ws_ex_id)
265
CREATE TABLE test_suite (
266
suiteid SERIAL PRIMARY KEY,
267
exerciseid TEXT REFERENCES exercise (identifier) NOT NULL,
274
CREATE TABLE test_case (
275
testid SERIAL PRIMARY KEY,
276
suiteid INT4 REFERENCES test_suite (suiteid) NOT NULL,
283
CREATE TABLE suite_variable (
284
varid SERIAL PRIMARY KEY,
285
suiteid INT4 REFERENCES test_suite (suiteid) NOT NULL,
288
var_type TEXT NOT NULL,
292
CREATE TABLE test_case_part (
293
partid SERIAL PRIMARY KEY,
294
testid INT4 REFERENCES test_case (testid) NOT NULL,
295
part_type TEXT NOT NULL,
154
DROP TABLE problem_prerequisites CASCADE;
155
CREATE TABLE problem_prerequisites (
156
parent INT4 REFERENCES problem (problemid) NOT NULL,
157
child INT4 REFERENCES problem (problemid) NOT NULL,
158
PRIMARY KEY (parent,child)