DatabaseProgramming: procedures.sql

File procedures.sql, 7.9 KB (added by 231082, 4 days ago)

Procedures.

Line 
1CREATE OR REPLACE PROCEDURE sp_enroll_student_in_course(
2 p_student_id INTEGER,
3 p_course_code TEXT,
4 p_academic_year SMALLINT,
5 p_semester SMALLINT,
6 OUT success BOOLEAN,
7 OUT message TEXT
8)
9 LANGUAGE plpgsql AS $$
10DECLARE
11 v_pe_id INTEGER;
12 v_se_id INTEGER;
13 v_edition_id INTEGER;
14 v_current_credits INTEGER;
15 v_course_credits INTEGER;
16BEGIN
17
18 SELECT id INTO v_pe_id
19 FROM Program_Enrollment
20 WHERE student_id = p_student_id
21 AND date_disenrollment IS NULL
22 AND finished IS NOT TRUE;
23
24 IF v_pe_id IS NULL THEN
25 success := FALSE;
26 message := 'No active program enrollment found for this student.';
27 RETURN;
28 END IF;
29
30
31 SELECT id INTO v_se_id
32 FROM Semester_Enrollment
33 WHERE program_enrollment_id = v_pe_id
34 AND academic_year = p_academic_year
35 AND semester::int = p_semester::int;
36
37 IF v_se_id IS NULL THEN
38 success := FALSE;
39 message := format('Student is not enrolled in semester %s/%s.', p_academic_year, p_semester);
40 RETURN;
41 END IF;
42
43
44 SELECT id INTO v_edition_id
45 FROM Course_Edition
46 WHERE course_code = p_course_code
47 AND academic_year = p_academic_year
48 AND semester = p_semester;
49
50 IF v_edition_id IS NULL THEN
51 success := FALSE;
52 message := format('Course edition %s (%s/%s) does not exist.', p_course_code, p_academic_year, p_semester);
53 RETURN;
54 END IF;
55
56
57 IF EXISTS (
58 SELECT 1 FROM Course_Enrollment
59 WHERE semester_enrollment_id = v_se_id
60 AND course_edition_id = v_edition_id
61 ) THEN
62 success := FALSE;
63 message := 'Student is already enrolled in this course for this semester.';
64 RETURN;
65 END IF;
66
67
68
69 IF EXISTS (
70 SELECT 1 FROM v_student_registration_state
71 WHERE student_id = p_student_id
72 AND course_code = p_course_code
73 AND can_enroll = FALSE
74 ) THEN
75 SELECT enrollment_status INTO message
76 FROM v_student_registration_state
77 WHERE student_id = p_student_id
78 AND course_code = p_course_code;
79
80 success := FALSE;
81 message := 'Prerequisite or other restriction: ' || COALESCE(message, 'unknown');
82 RETURN;
83 END IF;
84
85
86 INSERT INTO Course_Enrollment (semester_enrollment_id, course_edition_id)
87 VALUES (v_se_id, v_edition_id);
88
89 success := TRUE;
90 message := 'Enrollment successful.';
91END;
92$$;
93
94
95
96CREATE OR REPLACE PROCEDURE sp_submit_exam_attempt(
97 p_exam_id INTEGER,
98 p_course_enrollment_id INTEGER,
99 p_answers JSONB,
100 OUT success BOOLEAN,
101 OUT message TEXT
102)
103 LANGUAGE plpgsql AS $$
104DECLARE
105 v_attempt_number SMALLINT;
106 v_total_points FLOAT := 0;
107 v_max_points FLOAT;
108 v_answer_record RECORD;
109 v_exam_scheduled TIMESTAMP;
110 v_exam_duration SMALLINT;
111 v_exam_end TIMESTAMP;
112 v_already_submitted BOOLEAN;
113BEGIN
114
115 SELECT scheduled_at, duration_minutes INTO v_exam_scheduled, v_exam_duration
116 FROM Exam WHERE id = p_exam_id;
117
118 IF v_exam_scheduled IS NULL THEN
119 success := FALSE;
120 message := 'Exam not found.';
121 RETURN;
122 END IF;
123
124 v_exam_end := v_exam_scheduled + (v_exam_duration * INTERVAL '1 minute');
125
126 IF CURRENT_TIMESTAMP < v_exam_scheduled THEN
127 success := FALSE;
128 message := 'Exam has not started yet.';
129 RETURN;
130 END IF;
131
132 IF CURRENT_TIMESTAMP > v_exam_end + INTERVAL '5 minutes' THEN
133 success := FALSE;
134 message := 'Exam submission deadline has passed.';
135 RETURN;
136 END IF;
137
138
139 SELECT COUNT(*) > 0 INTO v_already_submitted
140 FROM Exam_Attempt
141 WHERE exam_id = p_exam_id
142 AND course_enrollment_id = p_course_enrollment_id;
143
144 IF v_already_submitted THEN
145 success := FALSE;
146 message := 'You have already submitted an attempt for this exam.';
147 RETURN;
148 END IF;
149
150
151 SELECT SUM(points) INTO v_max_points
152 FROM Exam_Problem
153 WHERE exam_id = p_exam_id;
154
155
156 FOR v_answer_record IN SELECT * FROM jsonb_to_recordset(p_answers) AS x(problem_id INTEGER, answer TEXT, points_acquired FLOAT)
157 LOOP
158
159 IF NOT EXISTS (SELECT 1 FROM Exam_Problem WHERE exam_id = p_exam_id AND id = v_answer_record.problem_id) THEN
160 success := FALSE;
161 message := format('Problem id %s does not belong to exam %s.', v_answer_record.problem_id, p_exam_id);
162 RETURN;
163 END IF;
164 IF v_answer_record.points_acquired < 0 THEN
165 success := FALSE;
166 message := 'Points acquired cannot be negative.';
167 RETURN;
168 END IF;
169 v_total_points := v_total_points + v_answer_record.points_acquired;
170 END LOOP;
171
172
173 v_attempt_number := 1;
174
175
176 INSERT INTO Exam_Attempt (
177 exam_id, course_enrollment_id, attempt_number, submitted_at, total_points
178 )
179 VALUES (
180 p_exam_id, p_course_enrollment_id, v_attempt_number, CURRENT_TIMESTAMP, v_total_points
181 );
182
183
184 FOR v_answer_record IN SELECT * FROM jsonb_to_recordset(p_answers) AS x(problem_id INTEGER, answer TEXT, points_acquired FLOAT)
185 LOOP
186 INSERT INTO Student_Answer (
187 exam_id, exam_problem_id, exam_attempt_ceid, exam_attempt_attempt_number,
188 points_acquired, answer
189 )
190 VALUES (
191 p_exam_id, v_answer_record.problem_id, p_course_enrollment_id, v_attempt_number,
192 v_answer_record.points_acquired, v_answer_record.answer
193 );
194 END LOOP;
195
196 success := TRUE;
197 message := format('Exam submitted. Total points: %s / %s', v_total_points, v_max_points);
198END;
199$$;
200
201
202
203
204CREATE OR REPLACE PROCEDURE sp_export_aggregated_results(
205 p_teacher_id INTEGER,
206 p_course_edition_id INTEGER,
207 p_data JSONB,
208 OUT success BOOLEAN,
209 OUT new_result_id INTEGER,
210 OUT message TEXT
211)
212 LANGUAGE plpgsql AS $$
213BEGIN
214
215 IF NOT EXISTS (
216 SELECT 1 FROM Teaches
217 WHERE teacher_id = p_teacher_id AND course_edition_id = p_course_edition_id
218 ) THEN
219 success := FALSE;
220 message := 'Teacher does not teach this course edition.';
221 RETURN;
222 END IF;
223
224
225 IF NOT fn_validate_aggregated_data(p_data) THEN
226 success := FALSE;
227 message := 'Invalid aggregated data format. See fn_validate_aggregated_data for schema.';
228 RETURN;
229 END IF;
230
231
232 INSERT INTO Aggregated_Course_Edition_Results (
233 exported_at, teaches_teacher_id, teaches_course_edition_id, data
234 )
235 VALUES (
236 CURRENT_TIMESTAMP, p_teacher_id, p_course_edition_id, p_data
237 )
238 RETURNING id INTO new_result_id;
239
240
241
242
243 success := TRUE;
244 message := format('Aggregated results exported with id = %s.', new_result_id);
245END;
246$$;
247
248
249
250
251CREATE OR REPLACE PROCEDURE sp_graduate_student(
252 p_program_enrollment_id INTEGER,
253 OUT success BOOLEAN,
254 OUT message TEXT
255)
256 LANGUAGE plpgsql AS $$
257DECLARE
258 v_outstanding INTEGER;
259BEGIN
260
261 IF NOT EXISTS (
262 SELECT 1 FROM Program_Enrollment
263 WHERE id = p_program_enrollment_id
264 AND date_disenrollment IS NULL
265 AND finished IS NOT TRUE
266 ) THEN
267 success := FALSE;
268 message := 'Program enrollment not found or already finished.';
269 RETURN;
270 END IF;
271
272
273 SELECT outstanding_mandatory_courses INTO v_outstanding
274 FROM v_program_completion_status
275 WHERE program_enrollment_id = p_program_enrollment_id;
276
277 IF v_outstanding IS NULL THEN
278 success := FALSE;
279 message := 'Could not compute completion status.';
280 RETURN;
281 END IF;
282
283 IF v_outstanding > 0 THEN
284 success := FALSE;
285 message := format('Cannot graduate: %s mandatory course(s) still outstanding.', v_outstanding);
286 RETURN;
287 END IF;
288
289
290 UPDATE Program_Enrollment
291 SET finished = TRUE, date_disenrollment = CURRENT_DATE
292 WHERE id = p_program_enrollment_id;
293
294 success := TRUE;
295 message := 'Student has graduated. Program enrollment marked as finished.';
296END;
297$$;
298
299
300
301
302