DatabaseProgramming: meta-functions.sql

File meta-functions.sql, 12.0 KB (added by 231082, 4 days ago)

Helper (meta) functions.

Line 
1
2-- Validation function for Exam_Results.data
3CREATE OR REPLACE FUNCTION fn_validate_exam_data(p_data jsonb)
4 RETURNS boolean
5 LANGUAGE plpgsql IMMUTABLE AS $$
6BEGIN
7 RETURN (
8 jsonb_typeof(p_data) = 'array'
9 AND jsonb_array_length(p_data) > 0
10 AND NOT EXISTS (
11 SELECT 1
12 FROM jsonb_array_elements(p_data) AS elem
13 WHERE
14 elem->>'index' IS NULL
15 OR elem->>'index' !~ '^\d{10}$'
16 OR (elem->>'points') IS NULL
17 OR (elem->>'points')::numeric < 0
18 )
19 );
20END;
21$$;
22
23
24-- Validation function for Aggregated_Course_Edition_Results.data
25CREATE OR REPLACE FUNCTION fn_validate_aggregated_data(p_data jsonb)
26 RETURNS boolean
27 LANGUAGE plpgsql IMMUTABLE AS $$
28BEGIN
29 RETURN (
30 jsonb_typeof(p_data) = 'array'
31 AND jsonb_array_length(p_data) > 0
32 AND NOT EXISTS (
33 SELECT 1
34 FROM jsonb_array_elements(p_data) AS elem
35 WHERE
36 elem->>'index' IS NULL
37 OR elem->>'index' !~ '^\d{10}$'
38
39 OR (elem->>'total_pts') IS NULL
40 OR (elem->>'total_pts')::numeric < 0
41
42 OR (elem->>'max_pts') IS NULL
43 OR (elem->>'max_pts')::numeric <= 0
44
45 OR (elem->>'percentage') IS NULL
46 OR (elem->>'percentage')::numeric < 0
47 OR (elem->>'percentage')::numeric > 100
48
49 OR (elem->'passed') IS NULL
50 OR jsonb_typeof(elem->'passed') <> 'boolean'
51
52 OR (
53 elem->'grade' IS NOT NULL
54 AND jsonb_typeof(elem->'grade') <> 'null'
55 AND (
56 jsonb_typeof(elem->'grade') <> 'number'
57 OR (elem->>'grade')::int NOT BETWEEN 6 AND 10
58 )
59 )
60 )
61 );
62END;
63$$;
64
65
66
67CREATE OR REPLACE FUNCTION fn_percentage_to_grade(p_pct numeric)
68 RETURNS integer
69 LANGUAGE plpgsql IMMUTABLE AS $$
70BEGIN
71 RETURN CASE
72 WHEN p_pct >= 91 THEN 10
73 WHEN p_pct >= 81 THEN 9
74 WHEN p_pct >= 71 THEN 8
75 WHEN p_pct >= 61 THEN 7
76 WHEN p_pct >= 51 THEN 6
77 ELSE NULL
78 END;
79END;
80$$;
81
82
83
84
85
86-- FOR v_student_registration_state VIEW
87CREATE OR REPLACE FUNCTION fn_student_registration_state(p_student_id INTEGER)
88 RETURNS TABLE (
89 student_id INTEGER,
90 academic_program_code TEXT,
91 course_code TEXT,
92 course_name TEXT,
93 credits INTEGER,
94 mandatory BOOLEAN,
95 curriculum_semester SMALLINT,
96 already_passed BOOLEAN,
97 currently_enrolled BOOLEAN,
98 prerequisites_met BOOLEAN,
99 missing_prereq_count BIGINT,
100 credit_headroom BIGINT,
101 would_exceed_cap BOOLEAN,
102 can_enroll BOOLEAN,
103 enrollment_status TEXT
104 )
105 LANGUAGE plpgsql STABLE
106AS $$
107BEGIN
108 RETURN QUERY
109 WITH
110 active_pe AS (
111 SELECT pe.id AS pe_id, pe.academic_program_code
112 FROM Program_Enrollment pe
113 WHERE pe.student_id = p_student_id
114 AND pe.date_disenrollment IS NULL
115 AND pe.finished IS NOT TRUE
116 ),
117 passed_courses AS (
118 SELECT DISTINCT ed.course_code AS passed_code
119 FROM active_pe ape
120 JOIN Semester_Enrollment se ON se.program_enrollment_id = ape.pe_id
121 JOIN Course_Enrollment ce ON ce.semester_enrollment_id = se.id
122 JOIN Course_Edition ed ON ed.id = ce.course_edition_id
123 JOIN Student_Grade sg ON sg.course_enrollment_id = ce.id
124 ),
125 passed_with_equiv AS (
126 SELECT passed_code FROM passed_courses
127 UNION
128 SELECT eq.course2_code
129 FROM passed_courses pc
130 JOIN Course_Equivalence eq ON eq.course1_code = pc.passed_code
131 UNION
132 SELECT eq.course1_code
133 FROM passed_courses pc
134 JOIN Course_Equivalence eq ON eq.course2_code = pc.passed_code
135 ),
136 current_enrollments AS (
137 SELECT ed.course_code AS curr_code
138 FROM active_pe ape
139 JOIN Semester_Enrollment se ON se.program_enrollment_id = ape.pe_id
140 AND se.academic_year = 2025 AND (se.semester::int) = 1 -- cast to int, compare to 1
141 JOIN Course_Enrollment ce ON ce.semester_enrollment_id = se.id
142 JOIN Course_Edition ed ON ed.id = ce.course_edition_id
143 ),
144 current_credits AS (
145 SELECT COALESCE(SUM(c.credits), 0) AS credits_consumed
146 FROM active_pe ape
147 LEFT JOIN Semester_Enrollment se ON se.program_enrollment_id = ape.pe_id
148 AND se.academic_year = 2025 AND (se.semester::int) = 1
149 LEFT JOIN Course_Enrollment ce ON ce.semester_enrollment_id = se.id
150 LEFT JOIN Course_Edition ed ON ed.id = ce.course_edition_id
151 LEFT JOIN Course c ON c.code = ed.course_code
152 ),
153 prereq_check AS (
154 SELECT
155 cur.course_code AS prereq_course,
156 COUNT(cp.predecessor_course_code) AS total_prereqs,
157 COUNT(pwe.passed_code) AS met_prereqs
158 FROM active_pe ape
159 CROSS JOIN Curriculum cur
160 LEFT JOIN Course_Prerequisite cp ON cp.successor_course_code = cur.course_code
161 LEFT JOIN passed_with_equiv pwe ON pwe.passed_code = cp.predecessor_course_code
162 WHERE cur.academic_program_code = ape.academic_program_code
163 GROUP BY cur.course_code
164 )
165 SELECT
166 p_student_id,
167 ape.academic_program_code,
168 cur.course_code,
169 c.name,
170 c.credits,
171 cur.mandatory,
172 cur.semester,
173 (pwe.passed_code IS NOT NULL) AS already_passed,
174 (ce_now.curr_code IS NOT NULL) AS currently_enrolled,
175 COALESCE(pc.total_prereqs IS NULL OR pc.met_prereqs = pc.total_prereqs, TRUE) AS prerequisites_met,
176 COALESCE(pc.total_prereqs - pc.met_prereqs, 0) AS missing_prereq_count,
177 40 - cc.credits_consumed AS credit_headroom,
178 (40 - cc.credits_consumed) < c.credits AS would_exceed_cap,
179 CASE
180 WHEN pwe.passed_code IS NOT NULL THEN FALSE
181 WHEN ce_now.curr_code IS NOT NULL THEN FALSE
182 WHEN pc.total_prereqs IS NOT NULL AND pc.met_prereqs < pc.total_prereqs THEN FALSE
183 WHEN (40 - cc.credits_consumed) < c.credits THEN FALSE
184 ELSE TRUE
185 END AS can_enroll,
186 CASE
187 WHEN pwe.passed_code IS NOT NULL THEN 'already_passed'
188 WHEN ce_now.curr_code IS NOT NULL THEN 'currently_enrolled'
189 WHEN pc.total_prereqs IS NOT NULL AND pc.met_prereqs < pc.total_prereqs THEN 'prerequisites_not_met'
190 WHEN (40 - cc.credits_consumed) < c.credits THEN 'credit_cap_exceeded'
191 ELSE 'eligible'
192 END AS enrollment_status
193 FROM active_pe ape
194 CROSS JOIN Curriculum cur
195 JOIN Course c ON c.code = cur.course_code
196 LEFT JOIN passed_with_equiv pwe ON pwe.passed_code = cur.course_code
197 LEFT JOIN current_enrollments ce_now ON ce_now.curr_code = cur.course_code
198 LEFT JOIN prereq_check pc ON pc.prereq_course = cur.course_code
199 CROSS JOIN current_credits cc
200 WHERE cur.academic_program_code = ape.academic_program_code;
201END;
202$$;
203
204
205
206
207
208-- FOR v_teaching_assistant_eligibility VIEW
209CREATE OR REPLACE FUNCTION fn_teaching_assistant_eligibility(p_edition_id INTEGER)
210 RETURNS TABLE (
211 student_id INTEGER,
212 student_index TEXT,
213 student_name TEXT,
214 edition_id INTEGER,
215 academic_year SMALLINT,
216 semester SMALLINT,
217 course_code TEXT,
218 course_name TEXT,
219 eligible_supervisors TEXT,
220 times_enrolled BIGINT
221 )
222 LANGUAGE plpgsql STABLE
223AS $$
224DECLARE
225 var_course_code TEXT;
226 var_academic_year SMALLINT;
227 var_semester SMALLINT;
228BEGIN
229 SELECT ed.course_code, ed.academic_year, ed.semester
230 INTO var_course_code, var_academic_year, var_semester
231 FROM Course_Edition ed
232 WHERE ed.id = p_edition_id;
233
234 IF NOT FOUND THEN
235 RETURN;
236 END IF;
237
238 RETURN QUERY
239 WITH
240 course_passers AS (
241 SELECT DISTINCT pe.student_id
242 FROM Program_Enrollment pe
243 JOIN Semester_Enrollment se ON se.program_enrollment_id = pe.id
244 JOIN Course_Enrollment ce ON ce.semester_enrollment_id = se.id
245 JOIN Course_Edition ed ON ed.id = ce.course_edition_id
246 JOIN Student_Grade sg ON sg.course_enrollment_id = ce.id
247 WHERE ed.course_code = var_course_code
248 ),
249 currently_enrolled AS (
250 SELECT DISTINCT pe.student_id
251 FROM Program_Enrollment pe
252 JOIN Semester_Enrollment se ON se.program_enrollment_id = pe.id
253 AND se.academic_year = var_academic_year
254 AND (se.semester::int) = var_semester -- compare to SMALLINT variable, cast semester to int
255 JOIN Course_Enrollment ce ON ce.semester_enrollment_id = se.id
256 WHERE ce.course_edition_id = p_edition_id
257 ),
258 existing_tas AS (
259 SELECT ta.student_id
260 FROM Teaching_Assistant ta
261 JOIN Teaches tt ON tt.teacher_id = ta.teacher_id
262 WHERE tt.course_edition_id = p_edition_id
263 ),
264 supervisors AS (
265 SELECT STRING_AGG(m.name || ' ' || m.surname, ', ' ORDER BY m.surname) AS names
266 FROM Teaches t
267 JOIN Member m ON m.id = t.teacher_id
268 WHERE t.course_edition_id = p_edition_id
269 ),
270 times_taken AS (
271 SELECT
272 pe.student_id,
273 COUNT(DISTINCT ce.id) AS cnt
274 FROM Program_Enrollment pe
275 JOIN Semester_Enrollment se ON se.program_enrollment_id = pe.id
276 JOIN Course_Enrollment ce ON ce.semester_enrollment_id = se.id
277 JOIN Course_Edition ed ON ed.id = ce.course_edition_id
278 WHERE ed.course_code = var_course_code
279 GROUP BY pe.student_id
280 )
281 SELECT
282 s.id,
283 s.index,
284 m.name || ' ' || m.surname,
285 p_edition_id,
286 var_academic_year,
287 var_semester,
288 c.code,
289 c.name,
290 supervisors.names,
291 COALESCE(times_taken.cnt, 0)::BIGINT
292 FROM course_passers cp
293 JOIN Student s ON s.id = cp.student_id
294 JOIN Member m ON m.id = s.id
295 CROSS JOIN Course c
296 CROSS JOIN supervisors
297 LEFT JOIN currently_enrolled ce ON ce.student_id = cp.student_id
298 LEFT JOIN existing_tas et ON et.student_id = cp.student_id
299 LEFT JOIN times_taken ON times_taken.student_id = cp.student_id
300 WHERE ce.student_id IS NULL
301 AND et.student_id IS NULL
302 AND c.code = var_course_code;
303END;
304$$;