| | 1 | = DatabaseProgramming = |
| | 2 | |
| | 3 | == Overview == |
| | 4 | |
| | 5 | The database application logic is implemented in PostgreSQL using functions, triggers, and procedures. This approach keeps business logic close to the data, ensuring consistency and reducing application complexity. |
| | 6 | |
| | 7 | == Meta Functions (Schema Validation) == |
| | 8 | |
| | 9 | === `fn_validate_exam_data` === |
| | 10 | |
| | 11 | Validates the JSON structure of `Exam_Results.data`. Ensures the data is an array of objects with required fields (`index`, `points`) and proper types. |
| | 12 | |
| | 13 | === `fn_validate_aggregated_data` === |
| | 14 | |
| | 15 | Validates the JSON structure of `Aggregated_Course_Edition_Results.data`. Checks for required fields (`index`, `total_pts`, `max_pts`, `percentage`, `passed`) and validates grade ranges. |
| | 16 | |
| | 17 | These functions are used in `CHECK` constraints on the respective tables: |
| | 18 | |
| | 19 | {{{ |
| | 20 | #!sql |
| | 21 | CONSTRAINT ck_aggregated_results_data CHECK (fn_validate_aggregated_data(data)) |
| | 22 | }}} |
| | 23 | |
| | 24 | === `fn_percentage_to_grade` === |
| | 25 | |
| | 26 | Converts a percentage score to the 6‑10 grading scale: |
| | 27 | |
| | 28 | - 91‑100% → 10 |
| | 29 | - 81‑90% → 9 |
| | 30 | - 71‑80% → 8 |
| | 31 | - 61‑70% → 7 |
| | 32 | - 51‑60% → 6 |
| | 33 | - <51% → NULL (fail) |
| | 34 | |
| | 35 | == Triggers == |
| | 36 | |
| | 37 | === Data Integrity Triggers === |
| | 38 | Trigger Purpose |
| | 39 | `trg_academic_program_defaults` Manages updated_at timestamps; prevents backward updates |
| | 40 | `trg_prevent_prereq_cycle` Prevents circular prerequisite dependencies using a recursive CTE |
| | 41 | `trg_prevent_equiv_duplicate` Prevents duplicate symmetric course equivalences |
| | 42 | `trg_validate_submission_grade` Ensures submission grade does not exceed exercise max_grade and is not negative |
| | 43 | `trg_validate_survey_response_option` Ensures survey responses reference valid options |
| | 44 | `trg_validate_survey_response_timing` Ensures responses are submitted before the survey closes |
| | 45 | `trg_validate_exam_attempt_timing` Validates exam attempt is not before start and not more than 5 min after end |
| | 46 | `trg_validate_course_edition_year` Prevents future editions and ensures edition starts after course creation |
| | 47 | |
| | 48 | The `trg_prevent_prereq_cycle` trigger uses a recursive query to detect cycles: |
| | 49 | |
| | 50 | {{{ |
| | 51 | #!sql |
| | 52 | WITH RECURSIVE reachable AS ( |
| | 53 | SELECT successor_course_code AS node |
| | 54 | FROM Course_Prerequisite |
| | 55 | WHERE predecessor_course_code = NEW.successor_course_code |
| | 56 | UNION |
| | 57 | SELECT cp.successor_course_code |
| | 58 | FROM Course_Prerequisite cp |
| | 59 | JOIN reachable r ON r.node = cp.predecessor_course_code |
| | 60 | ) |
| | 61 | SELECT EXISTS ( |
| | 62 | SELECT 1 FROM reachable WHERE node = NEW.predecessor_course_code |
| | 63 | ) INTO v_cycle_exists; |
| | 64 | }}} |
| | 65 | |
| | 66 | === Business Logic Trigger === |
| | 67 | |
| | 68 | ==== `trg_sync_student_grades` ==== |
| | 69 | |
| | 70 | Automatically updates student grades when aggregated results are inserted. This trigger: |
| | 71 | |
| | 72 | 1. Iterates over the JSON array in data |
| | 73 | 2. For each student that passed and has a grade, finds the correct `Course_Enrollment` |
| | 74 | 3. Inserts or updates the `Student_Grade` record (only if the new grade is higher) |
| | 75 | |
| | 76 | This ensures that final grades are propagated from the aggregated results to the individual student grade records. |
| | 77 | |
| | 78 | == Functions (Business Logic) == |
| | 79 | |
| | 80 | === `fn_export_exam_results` === |
| | 81 | |
| | 82 | Exports exam results for a teacher. Performs the following validations: |
| | 83 | |
| | 84 | - Exam exists |
| | 85 | - Teacher teaches the corresponding course edition |
| | 86 | - Exam has finished |
| | 87 | - No re‑export within the last 60 seconds (cooldown) |
| | 88 | |
| | 89 | Then builds a JSON array of student indices and total points (or 0 if no attempt) and inserts into `Exam_Results`. Returns the new ID. |
| | 90 | |
| | 91 | === `fn_export_aggregated_results` === |
| | 92 | |
| | 93 | Exports aggregated course results including partial exams, session exams, labs, and projects. It: |
| | 94 | |
| | 95 | - Validates that the teacher teaches the edition |
| | 96 | - Checks that partial_2 has finished (prerequisite for aggregation) |
| | 97 | - Calculates semester boundaries and mid‑point |
| | 98 | - Uses multiple CTEs to compute scores for each student: |
| | 99 | - `partial1_pts` – from `Exam_Attempt` on exams of type `partial_1` |
| | 100 | - `partial2_pts` – from `Exam_Attempt` on exams of type `partial_2` |
| | 101 | - `session_pts` – best of session exams (jan, jun, sep) |
| | 102 | - `lab_pts` – sum of exercise submissions before semester mid‑point |
| | 103 | - `project_pts` – sum of exercise submissions from mid‑point onward |
| | 104 | |
| | 105 | - Computes total points, percentage, pass/fail, and grade via `fn_percentage_to_grade` |
| | 106 | - Inserts the aggregated data into `Aggregated_Course_Edition_Results` |
| | 107 | |
| | 108 | == Procedures (Transactional Operations) == |
| | 109 | |
| | 110 | === `sp_enroll_student_in_course` === |
| | 111 | |
| | 112 | Enrolls a student in a course with comprehensive validation: |
| | 113 | |
| | 114 | - Checks for an active program enrollment |
| | 115 | - Checks that the student is enrolled in the specified semester |
| | 116 | - Verifies that the course edition exists |
| | 117 | - Prevents duplicate enrollment |
| | 118 | - Uses `fn_student_registration_state` to check prerequisites, credit cap, and other restrictions |
| | 119 | - Inserts the `Course_Enrollment` record |
| | 120 | |
| | 121 | Parameters: `p_student_id`, `p_course_code`, `p_academic_year`, `p_semester`; returns `success` and `message`. |
| | 122 | |
| | 123 | === `sp_submit_exam_attempt` === |
| | 124 | |
| | 125 | Allows a student to submit an exam attempt with answer validation: |
| | 126 | |
| | 127 | - Validates exam timing (not started, not past deadline + 5 min grace) |
| | 128 | - Prevents duplicate submissions |
| | 129 | - Validates each answer: problem belongs to the exam, points non‑negative |
| | 130 | - Computes total points |
| | 131 | - Inserts `Exam_Attempt` and each `Student_Answer` in a transaction |
| | 132 | |
| | 133 | Parameters: `p_exam_id`, `p_course_enrollment_id`, `p_answers` (JSON), returns `success` and `message`. |
| | 134 | |
| | 135 | === `sp_export_aggregated_results` === |
| | 136 | |
| | 137 | A wrapper procedure for `fn_export_aggregated_results` that additionally validates the input JSON using `fn_validate_aggregated_data`. Returns `success`, `new_result_id`, and `message`. |
| | 138 | |
| | 139 | === `sp_graduate_student` === |
| | 140 | |
| | 141 | Graduates a student by marking their program enrollment as finished: |
| | 142 | |
| | 143 | - Validates that the enrollment is active |
| | 144 | - Queries `v_program_completion_status` to count outstanding mandatory courses |
| | 145 | - If zero, updates `Program_Enrollment` to set `finished = TRUE` and `date_disenrollment = CURRENT_DATE` |
| | 146 | |
| | 147 | Parameters: `p_program_enrollment_id`, returns `success` and `message`. |
| | 148 | |
| | 149 | == Summary == |
| | 150 | |
| | 151 | || '''Type''' || '''Count''' || '''Purpose''' || |
| | 152 | || Meta Functions || 3 || JSON validation, grade conversion || |
| | 153 | || View Replacement Functions || 2 || Performance optimization for complex views || |
| | 154 | || Business Functions || 2 || Exam and aggregated results export || |
| | 155 | || Data Integrity Triggers || 8 || Enforce business rules at the database level || |
| | 156 | || Business Logic Trigger || 1 || Auto‑sync grades from aggregated results || |
| | 157 | || Procedures || 4 || Transactional operations with business logic || |
| | 158 | |
| | 159 | The trigger‑based approach ensures data integrity is enforced consistently, while functions and procedures encapsulate reusable business logic that can be called from the application layer. |