| 1 |
|
|---|
| 2 |
|
|---|
| 3 | CREATE VIEW v_course_edition_overview AS
|
|---|
| 4 | SELECT
|
|---|
| 5 | ed.id AS edition_id,
|
|---|
| 6 | ed.academic_year,
|
|---|
| 7 | ed.semester::int AS semester,
|
|---|
| 8 | c.code AS course_code,
|
|---|
| 9 | c.name AS course_name,
|
|---|
| 10 | c.credits,
|
|---|
| 11 | ap.code AS program_code,
|
|---|
| 12 | ap.name AS program_name,
|
|---|
| 13 | cur.mandatory,
|
|---|
| 14 | cur.semester::int AS curriculum_semester,
|
|---|
| 15 | COUNT(DISTINCT ce.id) AS enrolled_students,
|
|---|
| 16 | COUNT(DISTINCT t.teacher_id) AS n_teachers,
|
|---|
| 17 | STRING_AGG(
|
|---|
| 18 | DISTINCT m.name || ' ' || m.surname, ', '
|
|---|
| 19 | ORDER BY m.name || ' ' || m.surname
|
|---|
| 20 | ) AS teacher_names,
|
|---|
| 21 | COUNT(DISTINCT lec.id) AS n_lectures,
|
|---|
| 22 | COUNT(DISTINCT ex.id) AS n_exercises,
|
|---|
| 23 | COUNT(DISTINCT exam.id) AS n_exams,
|
|---|
| 24 | COUNT(DISTINCT sv.id) AS n_surveys
|
|---|
| 25 | FROM Course_Edition ed
|
|---|
| 26 | JOIN Course c ON c.code = ed.course_code
|
|---|
| 27 | LEFT JOIN Curriculum cur ON cur.course_code = c.code
|
|---|
| 28 | LEFT JOIN Academic_Program ap ON ap.code = cur.academic_program_code
|
|---|
| 29 | LEFT JOIN Course_Enrollment ce ON ce.course_edition_id = ed.id
|
|---|
| 30 | LEFT JOIN Teaches t ON t.course_edition_id = ed.id
|
|---|
| 31 | LEFT JOIN Teacher tr ON tr.id = t.teacher_id
|
|---|
| 32 | LEFT JOIN Member m ON m.id = tr.id
|
|---|
| 33 | LEFT JOIN Lecture lec ON lec.course_edition_id = ed.id
|
|---|
| 34 | LEFT JOIN Exercise ex ON ex.course_edition_id = ed.id
|
|---|
| 35 | LEFT JOIN Exam exam ON exam.course_edition_id = ed.id
|
|---|
| 36 | LEFT JOIN Survey sv ON sv.course_edition_id = ed.id
|
|---|
| 37 | GROUP BY
|
|---|
| 38 | ed.id, ed.academic_year, ed.semester,
|
|---|
| 39 | c.code, c.name, c.credits,
|
|---|
| 40 | ap.code, ap.name, cur.mandatory, cur.semester;
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 | CREATE VIEW v_survey_status AS
|
|---|
| 48 | SELECT
|
|---|
| 49 | sv.id AS survey_id,
|
|---|
| 50 | sv.course_edition_id AS edition_id,
|
|---|
| 51 | c.name AS course_name,
|
|---|
| 52 | sv.title AS survey_title,
|
|---|
| 53 | sv.open_until,
|
|---|
| 54 | sv.open_until > CURRENT_TIMESTAMP
|
|---|
| 55 | AS is_open,
|
|---|
| 56 | so.option,
|
|---|
| 57 | so.capacity,
|
|---|
| 58 | COUNT(sr.course_enrollment_id) AS response_count,
|
|---|
| 59 | so.capacity - COUNT(sr.course_enrollment_id)
|
|---|
| 60 | AS spots_remaining
|
|---|
| 61 | FROM Survey sv
|
|---|
| 62 | JOIN Course_Edition ed ON ed.id = sv.course_edition_id
|
|---|
| 63 | JOIN Course c ON c.code = ed.course_code
|
|---|
| 64 | JOIN Survey_Option so ON so.survey_id = sv.id
|
|---|
| 65 | LEFT JOIN Survey_Response sr
|
|---|
| 66 | ON sr.survey_id = sv.id
|
|---|
| 67 | AND sr.option_text = so.option
|
|---|
| 68 | GROUP BY
|
|---|
| 69 | sv.id, sv.course_edition_id, c.name,
|
|---|
| 70 | sv.title, sv.open_until, so.option, so.capacity;
|
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 | CREATE VIEW v_teacher_dashboard AS
|
|---|
| 76 | SELECT
|
|---|
| 77 | tr.id AS teacher_id,
|
|---|
| 78 | m.name || ' ' || m.surname AS teacher_name,
|
|---|
| 79 | c.code AS course_code,
|
|---|
| 80 | c.name AS course_name,
|
|---|
| 81 | ed.id AS edition_id,
|
|---|
| 82 | ed.academic_year,
|
|---|
| 83 | ed.semester::int AS semester,
|
|---|
| 84 | COUNT(DISTINCT ce.id) AS enrolled_students,
|
|---|
| 85 |
|
|---|
| 86 | COUNT(DISTINCT esub.id)
|
|---|
| 87 | FILTER (WHERE esub.graded_at IS NULL)
|
|---|
| 88 | AS ungraded_submissions,
|
|---|
| 89 |
|
|---|
| 90 | COUNT(DISTINCT exam.id)
|
|---|
| 91 | FILTER (WHERE exam.scheduled_at > CURRENT_TIMESTAMP)
|
|---|
| 92 | AS upcoming_exams,
|
|---|
| 93 |
|
|---|
| 94 | COUNT(DISTINCT sv.id)
|
|---|
| 95 | FILTER (WHERE sv.open_until > CURRENT_TIMESTAMP)
|
|---|
| 96 | AS open_surveys
|
|---|
| 97 | FROM Teacher tr
|
|---|
| 98 | JOIN Member m ON m.id = tr.id
|
|---|
| 99 | JOIN Teaches t ON t.teacher_id = tr.id
|
|---|
| 100 | JOIN Course_Edition ed ON ed.id = t.course_edition_id
|
|---|
| 101 | AND ed.academic_year = 2025
|
|---|
| 102 | AND ed.semester::int = 1
|
|---|
| 103 | JOIN Course c ON c.code = ed.course_code
|
|---|
| 104 | LEFT JOIN Course_Enrollment ce ON ce.course_edition_id = ed.id
|
|---|
| 105 | LEFT JOIN Exercise ex ON ex.course_edition_id = ed.id
|
|---|
| 106 | LEFT JOIN Exercise_Submission esub
|
|---|
| 107 | ON esub.exercise_id = ex.id
|
|---|
| 108 | LEFT JOIN Exam exam ON exam.course_edition_id = ed.id
|
|---|
| 109 | LEFT JOIN Survey sv ON sv.course_edition_id = ed.id
|
|---|
| 110 | GROUP BY
|
|---|
| 111 | tr.id, m.name, m.surname,
|
|---|
| 112 | c.code, c.name, ed.id, ed.academic_year, ed.semester;
|
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
|
|---|
| 116 | CREATE OR REPLACE VIEW v_exam_results_summary AS
|
|---|
| 117 | SELECT
|
|---|
| 118 | exam.id AS exam_id,
|
|---|
| 119 | exam.course_edition_id AS edition_id,
|
|---|
| 120 | exam.type AS exam_type,
|
|---|
| 121 | c.name AS course_name,
|
|---|
| 122 | exam.scheduled_at,
|
|---|
| 123 | exam.duration_minutes,
|
|---|
| 124 | ep_totals.max_points,
|
|---|
| 125 | ep_totals.problem_count,
|
|---|
| 126 | COUNT(DISTINCT ea.course_enrollment_id)
|
|---|
| 127 | AS attempts,
|
|---|
| 128 | ROUND(AVG(ea.total_points)::numeric, 2)
|
|---|
| 129 | AS avg_score,
|
|---|
| 130 | ROUND(MIN(ea.total_points)::numeric, 2)
|
|---|
| 131 | AS min_score,
|
|---|
| 132 | ROUND(MAX(ea.total_points)::numeric, 2)
|
|---|
| 133 | AS max_score,
|
|---|
| 134 | COUNT(DISTINCT ea.course_enrollment_id)
|
|---|
| 135 | FILTER (WHERE ea.total_points >= 0.5 * ep_totals.max_points)
|
|---|
| 136 | AS passed_count,
|
|---|
| 137 | ROUND(
|
|---|
| 138 | 100.0 * COUNT(DISTINCT ea.course_enrollment_id)
|
|---|
| 139 | FILTER (WHERE ea.total_points >= 0.5 * ep_totals.max_points)
|
|---|
| 140 | / NULLIF(COUNT(DISTINCT ea.course_enrollment_id), 0)
|
|---|
| 141 | , 1) AS pass_rate_pct
|
|---|
| 142 | FROM Exam exam
|
|---|
| 143 | JOIN Course_Edition ed ON ed.id = exam.course_edition_id
|
|---|
| 144 | JOIN Course c ON c.code = ed.course_code
|
|---|
| 145 |
|
|---|
| 146 | JOIN LATERAL (
|
|---|
| 147 | SELECT
|
|---|
| 148 | SUM(points) AS max_points,
|
|---|
| 149 | COUNT(*) AS problem_count
|
|---|
| 150 | FROM Exam_Problem
|
|---|
| 151 | WHERE exam_id = exam.id
|
|---|
| 152 | ) ep_totals ON true
|
|---|
| 153 | LEFT JOIN Exam_Attempt ea ON ea.exam_id = exam.id
|
|---|
| 154 | GROUP BY exam.id, exam.course_edition_id, exam.type, c.name,
|
|---|
| 155 | exam.scheduled_at, exam.duration_minutes,
|
|---|
| 156 | ep_totals.max_points, ep_totals.problem_count;
|
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
|
|---|
| 160 |
|
|---|
| 161 |
|
|---|
| 162 |
|
|---|
| 163 |
|
|---|
| 164 | CREATE OR REPLACE VIEW v_member_inbox AS
|
|---|
| 165 | SELECT
|
|---|
| 166 | msg.member_id2 AS partner_id,
|
|---|
| 167 | mp.name || ' ' || mp.surname AS partner_name,
|
|---|
| 168 | m.id AS member_id,
|
|---|
| 169 | COUNT(*) AS total_messages,
|
|---|
| 170 | MAX(msg.sent_at) AS last_message_at,
|
|---|
| 171 | SUBSTRING((ARRAY_AGG(msg.message ORDER BY msg.sent_at DESC))[1] FOR 120) AS last_message_preview,
|
|---|
| 172 | COUNT(*) FILTER (WHERE msg.sent_at > CURRENT_TIMESTAMP - INTERVAL '7 days') AS recent_messages
|
|---|
| 173 | FROM Member m
|
|---|
| 174 | JOIN Member_Message msg ON msg.member_id1 = m.id
|
|---|
| 175 | JOIN Member mp ON mp.id = msg.member_id2
|
|---|
| 176 | GROUP BY m.id, partner_id, mp.name, mp.surname
|
|---|
| 177 |
|
|---|
| 178 | UNION ALL
|
|---|
| 179 |
|
|---|
| 180 | SELECT
|
|---|
| 181 | msg.member_id1 AS partner_id,
|
|---|
| 182 | mp.name || ' ' || mp.surname AS partner_name,
|
|---|
| 183 | m.id AS member_id,
|
|---|
| 184 | COUNT(*) AS total_messages,
|
|---|
| 185 | MAX(msg.sent_at) AS last_message_at,
|
|---|
| 186 | SUBSTRING((ARRAY_AGG(msg.message ORDER BY msg.sent_at DESC))[1] FOR 120) AS last_message_preview,
|
|---|
| 187 | COUNT(*) FILTER (WHERE msg.sent_at > CURRENT_TIMESTAMP - INTERVAL '7 days') AS recent_messages
|
|---|
| 188 | FROM Member m
|
|---|
| 189 | JOIN Member_Message msg ON msg.member_id2 = m.id
|
|---|
| 190 | JOIN Member mp ON mp.id = msg.member_id1
|
|---|
| 191 | GROUP BY m.id, partner_id, mp.name, mp.surname;
|
|---|
| 192 |
|
|---|
| 193 |
|
|---|
| 194 |
|
|---|
| 195 |
|
|---|
| 196 |
|
|---|
| 197 | CREATE VIEW v_program_catalog AS
|
|---|
| 198 | SELECT
|
|---|
| 199 | ap.code,
|
|---|
| 200 | ap.name,
|
|---|
| 201 | ap.duration_years::int,
|
|---|
| 202 | ap.numeric_id,
|
|---|
| 203 | COUNT(DISTINCT pe.student_id)
|
|---|
| 204 | FILTER (WHERE pe.finished IS NOT TRUE
|
|---|
| 205 | AND pe.date_disenrollment IS NULL)
|
|---|
| 206 | AS active_students,
|
|---|
| 207 | COUNT(DISTINCT cur.course_code) AS total_courses,
|
|---|
| 208 | COUNT(DISTINCT cur.course_code)
|
|---|
| 209 | FILTER (WHERE cur.mandatory)
|
|---|
| 210 | AS mandatory_courses,
|
|---|
| 211 | SUM(DISTINCT c.credits)
|
|---|
| 212 | FILTER (WHERE cur.mandatory)
|
|---|
| 213 | AS mandatory_credits
|
|---|
| 214 | FROM Academic_Program ap
|
|---|
| 215 | LEFT JOIN Program_Enrollment pe ON pe.academic_program_code = ap.code
|
|---|
| 216 | LEFT JOIN Curriculum cur ON cur.academic_program_code = ap.code
|
|---|
| 217 | LEFT JOIN Course c ON c.code = cur.course_code
|
|---|
| 218 | GROUP BY ap.code, ap.name, ap.duration_years, ap.numeric_id;
|
|---|
| 219 |
|
|---|
| 220 |
|
|---|
| 221 |
|
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 | CREATE VIEW v_course_catalog AS
|
|---|
| 225 | SELECT
|
|---|
| 226 | c.code,
|
|---|
| 227 | c.name,
|
|---|
| 228 | c.credits,
|
|---|
| 229 |
|
|---|
| 230 | STRING_AGG(DISTINCT cpre.name, ', ' ORDER BY cpre.name)
|
|---|
| 231 | AS prerequisites,
|
|---|
| 232 |
|
|---|
| 233 | STRING_AGG(DISTINCT ap.name, '; ' ORDER BY ap.name)
|
|---|
| 234 | AS programs,
|
|---|
| 235 | COUNT(DISTINCT ed.id) AS editions_count,
|
|---|
| 236 | MAX(ed.academic_year) AS last_offered_year
|
|---|
| 237 | FROM Course c
|
|---|
| 238 | LEFT JOIN Course_Prerequisite pr ON pr.successor_course_code = c.code
|
|---|
| 239 | LEFT JOIN Course cpre ON cpre.code = pr.predecessor_course_code
|
|---|
| 240 | LEFT JOIN Curriculum cur ON cur.course_code = c.code
|
|---|
| 241 | LEFT JOIN Academic_Program ap ON ap.code = cur.academic_program_code
|
|---|
| 242 | LEFT JOIN Course_Edition ed ON ed.course_code = c.code
|
|---|
| 243 | GROUP BY c.code, c.name, c.credits;
|
|---|
| 244 |
|
|---|
| 245 |
|
|---|
| 246 |
|
|---|
| 247 |
|
|---|
| 248 |
|
|---|
| 249 |
|
|---|
| 250 |
|
|---|
| 251 |
|
|---|
| 252 |
|
|---|
| 253 |
|
|---|
| 254 |
|
|---|
| 255 |
|
|---|
| 256 |
|
|---|
| 257 |
|
|---|
| 258 |
|
|---|
| 259 | CREATE OR REPLACE VIEW v_exam_attempt_detail AS
|
|---|
| 260 |
|
|---|
| 261 |
|
|---|
| 262 | WITH exam_max AS (
|
|---|
| 263 | SELECT
|
|---|
| 264 | exam_id,
|
|---|
| 265 | SUM(points) AS max_points,
|
|---|
| 266 | COUNT(*) AS problem_count
|
|---|
| 267 | FROM Exam_Problem
|
|---|
| 268 | GROUP BY exam_id
|
|---|
| 269 | ),
|
|---|
| 270 |
|
|---|
| 271 |
|
|---|
| 272 | attempt_ranks AS (
|
|---|
| 273 | SELECT
|
|---|
| 274 | exam_id,
|
|---|
| 275 | course_enrollment_id,
|
|---|
| 276 | attempt_number,
|
|---|
| 277 | total_points,
|
|---|
| 278 | RANK() OVER (
|
|---|
| 279 | PARTITION BY exam_id
|
|---|
| 280 | ORDER BY total_points DESC
|
|---|
| 281 | ) AS rank_in_exam,
|
|---|
| 282 | COUNT(*) OVER (
|
|---|
| 283 | PARTITION BY exam_id
|
|---|
| 284 | ) AS total_attempts,
|
|---|
| 285 | ROUND(
|
|---|
| 286 | 100.0 * PERCENT_RANK() OVER (
|
|---|
| 287 | PARTITION BY exam_id
|
|---|
| 288 | ORDER BY total_points
|
|---|
| 289 | )
|
|---|
| 290 | ) AS percentile
|
|---|
| 291 | FROM Exam_Attempt
|
|---|
| 292 | )
|
|---|
| 293 |
|
|---|
| 294 | SELECT
|
|---|
| 295 |
|
|---|
| 296 | ea.exam_id,
|
|---|
| 297 | exam.scheduled_at,
|
|---|
| 298 | exam.duration_minutes,
|
|---|
| 299 | ed.course_code,
|
|---|
| 300 | c.name AS course_name,
|
|---|
| 301 | ed.academic_year,
|
|---|
| 302 | ed.semester::int AS edition_semester,
|
|---|
| 303 |
|
|---|
| 304 |
|
|---|
| 305 | ea.course_enrollment_id,
|
|---|
| 306 | ea.attempt_number,
|
|---|
| 307 | ea.submitted_at,
|
|---|
| 308 |
|
|---|
| 309 |
|
|---|
| 310 | pe.student_id,
|
|---|
| 311 | s.index AS student_index,
|
|---|
| 312 | m.name || ' ' || m.surname AS student_name,
|
|---|
| 313 |
|
|---|
| 314 |
|
|---|
| 315 |
|
|---|
| 316 | JSON_AGG(
|
|---|
| 317 | JSON_BUILD_OBJECT(
|
|---|
| 318 | 'problem_id', sa.exam_problem_id,
|
|---|
| 319 | 'points_available', ep.points,
|
|---|
| 320 | 'points_acquired', sa.points_acquired,
|
|---|
| 321 | 'answer_excerpt', LEFT(sa.answer, 200)
|
|---|
| 322 | )
|
|---|
| 323 | ORDER BY sa.exam_problem_id
|
|---|
| 324 | ) AS problem_breakdown,
|
|---|
| 325 |
|
|---|
| 326 |
|
|---|
| 327 | ea.total_points,
|
|---|
| 328 | em.max_points,
|
|---|
| 329 | em.problem_count,
|
|---|
| 330 | ROUND(
|
|---|
| 331 | (100.0 * ea.total_points / NULLIF(em.max_points, 0))::numeric, 1
|
|---|
| 332 | ) AS percentage,
|
|---|
| 333 |
|
|---|
| 334 |
|
|---|
| 335 | CASE
|
|---|
| 336 | WHEN ea.total_points >= 0.5 * em.max_points THEN true
|
|---|
| 337 | ELSE false
|
|---|
| 338 | END AS passed,
|
|---|
| 339 |
|
|---|
| 340 |
|
|---|
| 341 | ar.rank_in_exam,
|
|---|
| 342 | ar.total_attempts,
|
|---|
| 343 | ar.percentile
|
|---|
| 344 |
|
|---|
| 345 | FROM Exam_Attempt ea
|
|---|
| 346 | JOIN Exam exam ON exam.id = ea.exam_id
|
|---|
| 347 | JOIN Course_Edition ed ON ed.id = exam.course_edition_id
|
|---|
| 348 | JOIN Course c ON c.code = ed.course_code
|
|---|
| 349 | JOIN exam_max em ON em.exam_id = ea.exam_id
|
|---|
| 350 | JOIN attempt_ranks ar
|
|---|
| 351 | ON ar.exam_id = ea.exam_id
|
|---|
| 352 | AND ar.course_enrollment_id = ea.course_enrollment_id
|
|---|
| 353 | AND ar.attempt_number = ea.attempt_number
|
|---|
| 354 |
|
|---|
| 355 | JOIN Course_Enrollment ce ON ce.id = ea.course_enrollment_id
|
|---|
| 356 | JOIN Semester_Enrollment se ON se.id = ce.semester_enrollment_id
|
|---|
| 357 | JOIN Program_Enrollment pe ON pe.id = se.program_enrollment_id
|
|---|
| 358 | JOIN Student s ON s.id = pe.student_id
|
|---|
| 359 | JOIN Member m ON m.id = s.id
|
|---|
| 360 |
|
|---|
| 361 | LEFT JOIN Student_Answer sa
|
|---|
| 362 | ON sa.exam_id = ea.exam_id
|
|---|
| 363 | AND sa.exam_attempt_ceid = ea.course_enrollment_id
|
|---|
| 364 | AND sa.exam_attempt_attempt_number = ea.attempt_number
|
|---|
| 365 | LEFT JOIN Exam_Problem ep
|
|---|
| 366 | ON ep.exam_id = ea.exam_id
|
|---|
| 367 | AND ep.id = sa.exam_problem_id
|
|---|
| 368 | GROUP BY
|
|---|
| 369 | ea.exam_id, exam.scheduled_at, exam.duration_minutes,
|
|---|
| 370 | ed.course_code, c.name, ed.academic_year, ed.semester,
|
|---|
| 371 | ea.course_enrollment_id, ea.attempt_number, ea.submitted_at,
|
|---|
| 372 | pe.student_id, s.index, m.name, m.surname,
|
|---|
| 373 | ea.total_points, em.max_points, em.problem_count,
|
|---|
| 374 | ar.rank_in_exam, ar.total_attempts, ar.percentile;
|
|---|
| 375 |
|
|---|
| 376 |
|
|---|
| 377 |
|
|---|
| 378 |
|
|---|
| 379 |
|
|---|
| 380 |
|
|---|
| 381 |
|
|---|
| 382 |
|
|---|
| 383 |
|
|---|
| 384 |
|
|---|
| 385 |
|
|---|
| 386 |
|
|---|
| 387 |
|
|---|
| 388 |
|
|---|
| 389 |
|
|---|
| 390 |
|
|---|
| 391 |
|
|---|
| 392 |
|
|---|
| 393 | CREATE OR REPLACE VIEW v_program_completion_status AS
|
|---|
| 394 |
|
|---|
| 395 |
|
|---|
| 396 | WITH passed_base AS (
|
|---|
| 397 | SELECT
|
|---|
| 398 | pe.student_id,
|
|---|
| 399 | pe.id AS pe_id,
|
|---|
| 400 | ed.course_code,
|
|---|
| 401 | c.credits
|
|---|
| 402 | FROM Program_Enrollment pe
|
|---|
| 403 | JOIN Semester_Enrollment se ON se.program_enrollment_id = pe.id
|
|---|
| 404 | JOIN Course_Enrollment ce ON ce.semester_enrollment_id = se.id
|
|---|
| 405 | JOIN Course_Edition ed ON ed.id = ce.course_edition_id
|
|---|
| 406 | JOIN Course c ON c.code = ed.course_code
|
|---|
| 407 | JOIN Student_Grade sg ON sg.course_enrollment_id = ce.id
|
|---|
| 408 | ),
|
|---|
| 409 |
|
|---|
| 410 | passed_expanded AS (
|
|---|
| 411 |
|
|---|
| 412 | SELECT student_id, pe_id, course_code, credits FROM passed_base
|
|---|
| 413 | UNION
|
|---|
| 414 |
|
|---|
| 415 | SELECT pb.student_id, pb.pe_id, eq.course2_code, c2.credits
|
|---|
| 416 | FROM passed_base pb
|
|---|
| 417 | JOIN Course_Equivalence eq ON eq.course1_code = pb.course_code
|
|---|
| 418 | JOIN Course c2 ON c2.code = eq.course2_code
|
|---|
| 419 | UNION
|
|---|
| 420 |
|
|---|
| 421 | SELECT pb.student_id, pb.pe_id, eq.course1_code, c1.credits
|
|---|
| 422 | FROM passed_base pb
|
|---|
| 423 | JOIN Course_Equivalence eq ON eq.course2_code = pb.course_code
|
|---|
| 424 | JOIN Course c1 ON c1.code = eq.course1_code
|
|---|
| 425 | ),
|
|---|
| 426 |
|
|---|
| 427 |
|
|---|
| 428 | outstanding_mandatory AS (
|
|---|
| 429 | SELECT
|
|---|
| 430 | pe.id AS pe_id,
|
|---|
| 431 | pe.student_id,
|
|---|
| 432 | cur.course_code,
|
|---|
| 433 | c.credits,
|
|---|
| 434 | cur.semester::int AS curriculum_semester
|
|---|
| 435 | FROM Program_Enrollment pe
|
|---|
| 436 | JOIN Curriculum cur
|
|---|
| 437 | ON cur.academic_program_code = pe.academic_program_code
|
|---|
| 438 | AND cur.mandatory = true
|
|---|
| 439 | JOIN Course c
|
|---|
| 440 | ON c.code = cur.course_code
|
|---|
| 441 |
|
|---|
| 442 | WHERE NOT EXISTS (
|
|---|
| 443 | SELECT 1 FROM passed_expanded pex
|
|---|
| 444 | WHERE pex.student_id = pe.student_id
|
|---|
| 445 | AND pex.course_code = cur.course_code
|
|---|
| 446 | )
|
|---|
| 447 | ),
|
|---|
| 448 |
|
|---|
| 449 |
|
|---|
| 450 | earned AS (
|
|---|
| 451 | SELECT
|
|---|
| 452 | pe_id,
|
|---|
| 453 | student_id,
|
|---|
| 454 |
|
|---|
| 455 | SUM(DISTINCT credits) AS credits_earned,
|
|---|
| 456 | COUNT(DISTINCT course_code) AS courses_passed
|
|---|
| 457 | FROM passed_expanded
|
|---|
| 458 | GROUP BY pe_id, student_id
|
|---|
| 459 | ),
|
|---|
| 460 |
|
|---|
| 461 |
|
|---|
| 462 | program_totals AS (
|
|---|
| 463 | SELECT
|
|---|
| 464 | ap.code AS program_code,
|
|---|
| 465 | ap.duration_years::int * 2 AS total_semesters,
|
|---|
| 466 | SUM(c.credits) AS total_credits,
|
|---|
| 467 | SUM(c.credits) FILTER (WHERE cur.mandatory)
|
|---|
| 468 | AS mandatory_credits,
|
|---|
| 469 | COUNT(cur.course_code) AS total_courses,
|
|---|
| 470 | COUNT(cur.course_code) FILTER (WHERE cur.mandatory)
|
|---|
| 471 | AS mandatory_courses
|
|---|
| 472 | FROM Academic_Program ap
|
|---|
| 473 | JOIN Curriculum cur ON cur.academic_program_code = ap.code
|
|---|
| 474 | JOIN Course c ON c.code = cur.course_code
|
|---|
| 475 | GROUP BY ap.code, ap.duration_years
|
|---|
| 476 | ),
|
|---|
| 477 |
|
|---|
| 478 |
|
|---|
| 479 | outstanding_summary AS (
|
|---|
| 480 | SELECT
|
|---|
| 481 | pe_id,
|
|---|
| 482 | student_id,
|
|---|
| 483 | COUNT(*) AS outstanding_mandatory_courses,
|
|---|
| 484 | SUM(credits) AS outstanding_mandatory_credits,
|
|---|
| 485 |
|
|---|
| 486 | MIN(curriculum_semester) AS earliest_outstanding_semester
|
|---|
| 487 | FROM outstanding_mandatory
|
|---|
| 488 | GROUP BY pe_id, student_id
|
|---|
| 489 | ),
|
|---|
| 490 |
|
|---|
| 491 |
|
|---|
| 492 | current_sem AS (
|
|---|
| 493 | SELECT
|
|---|
| 494 | se.program_enrollment_id AS pe_id,
|
|---|
| 495 | MAX(se.academic_year * 2 + se.semester::int - 1)
|
|---|
| 496 | AS current_sem_offset
|
|---|
| 497 | FROM Semester_Enrollment se
|
|---|
| 498 | GROUP BY se.program_enrollment_id
|
|---|
| 499 | )
|
|---|
| 500 |
|
|---|
| 501 | SELECT
|
|---|
| 502 | pe.id AS program_enrollment_id,
|
|---|
| 503 | pe.student_id,
|
|---|
| 504 | s.index AS student_index,
|
|---|
| 505 | m.name || ' ' || m.surname AS student_name,
|
|---|
| 506 | pe.academic_program_code,
|
|---|
| 507 | ap.name AS program_name,
|
|---|
| 508 | ap.duration_years::int,
|
|---|
| 509 | pe.date_enrollment,
|
|---|
| 510 |
|
|---|
| 511 |
|
|---|
| 512 | COALESCE(e.credits_earned, 0) AS credits_earned,
|
|---|
| 513 | pt.total_credits AS credits_required,
|
|---|
| 514 | pt.mandatory_credits AS mandatory_credits_required,
|
|---|
| 515 | GREATEST(0,
|
|---|
| 516 | pt.mandatory_credits - COALESCE(e.credits_earned, 0)
|
|---|
| 517 | ) AS credits_remaining,
|
|---|
| 518 |
|
|---|
| 519 |
|
|---|
| 520 | COALESCE(e.courses_passed, 0) AS courses_passed,
|
|---|
| 521 | pt.total_courses AS courses_required,
|
|---|
| 522 | COALESCE(os.outstanding_mandatory_courses, 0)
|
|---|
| 523 | AS outstanding_mandatory_courses,
|
|---|
| 524 | COALESCE(os.outstanding_mandatory_credits, 0)
|
|---|
| 525 | AS outstanding_mandatory_credits,
|
|---|
| 526 |
|
|---|
| 527 |
|
|---|
| 528 | LEAST(100, ROUND(
|
|---|
| 529 | 100.0 * COALESCE(e.credits_earned, 0)
|
|---|
| 530 | / NULLIF(pt.mandatory_credits, 0)
|
|---|
| 531 | , 1)) AS completion_pct,
|
|---|
| 532 |
|
|---|
| 533 |
|
|---|
| 534 |
|
|---|
| 535 | CASE
|
|---|
| 536 | WHEN cs.current_sem_offset IS NULL THEN NULL
|
|---|
| 537 | WHEN COALESCE(e.credits_earned, 0) >=
|
|---|
| 538 | ROUND(
|
|---|
| 539 | pt.mandatory_credits::numeric
|
|---|
| 540 | * cs.current_sem_offset
|
|---|
| 541 | / NULLIF(pt.total_semesters, 0)
|
|---|
| 542 | )
|
|---|
| 543 | THEN true
|
|---|
| 544 | ELSE false
|
|---|
| 545 | END AS on_track,
|
|---|
| 546 |
|
|---|
| 547 |
|
|---|
| 548 |
|
|---|
| 549 |
|
|---|
| 550 | CASE
|
|---|
| 551 | WHEN COALESCE(os.outstanding_mandatory_credits, 0) = 0
|
|---|
| 552 | THEN 'eligible_to_graduate'
|
|---|
| 553 | ELSE (
|
|---|
| 554 | 2025
|
|---|
| 555 | + CEIL(
|
|---|
| 556 | CEIL(
|
|---|
| 557 | COALESCE(os.outstanding_mandatory_credits, 0)::numeric / 20
|
|---|
| 558 | ) / 2.0
|
|---|
| 559 | )
|
|---|
| 560 | )::text || ' (approx.)'
|
|---|
| 561 | END AS projected_graduation_year,
|
|---|
| 562 |
|
|---|
| 563 |
|
|---|
| 564 |
|
|---|
| 565 | COALESCE(os.earliest_outstanding_semester, pt.total_semesters + 1)
|
|---|
| 566 | AS effective_study_semester,
|
|---|
| 567 |
|
|---|
| 568 |
|
|---|
| 569 | ARRAY(
|
|---|
| 570 | SELECT om.course_code
|
|---|
| 571 | FROM outstanding_mandatory om
|
|---|
| 572 | WHERE om.pe_id = pe.id
|
|---|
| 573 | ORDER BY om.curriculum_semester, om.course_code
|
|---|
| 574 | ) AS outstanding_mandatory_course_codes
|
|---|
| 575 |
|
|---|
| 576 | FROM Program_Enrollment pe
|
|---|
| 577 | JOIN Student s ON s.id = pe.student_id
|
|---|
| 578 | JOIN Member m ON m.id = s.id
|
|---|
| 579 | JOIN Academic_Program ap ON ap.code = pe.academic_program_code
|
|---|
| 580 | JOIN program_totals pt ON pt.program_code = pe.academic_program_code
|
|---|
| 581 | LEFT JOIN earned e ON e.pe_id = pe.id
|
|---|
| 582 | LEFT JOIN outstanding_summary os ON os.pe_id = pe.id
|
|---|
| 583 | LEFT JOIN current_sem cs ON cs.pe_id = pe.id
|
|---|
| 584 | WHERE pe.date_disenrollment IS NULL
|
|---|
| 585 | AND pe.finished IS NOT TRUE;
|
|---|