Changes between Version 5 and Version 6 of DatabaseProgramming-AdvDb


Ignore:
Timestamp:
06/07/26 16:24:09 (9 days ago)
Author:
231175
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DatabaseProgramming-AdvDb

    v5 v6  
    66
    77{{{
    8 create or replace function write_review(
    9     user_id bigint,
    10     course_version_id bigint,
    11     new_rating int,
    12     new_comment text
     8create or replace function complete_quiz_attempt(
     9    p_enrollment_id int,
     10    p_quiz_id int,
     11    p_answers jsonb -- [{question_id, answer_option_id}]
    1312)
    14     returns review
    15 as
     13    returns bigint
     14    language plpgsql as
    1615$$
    17     declare
    18         r review;
    19         e_id bigint;
    20     begin
     16declare
     17    v_attempt_id    bigint;
     18    v_attempt_num   int;
     19    v_total_points  int;
     20    v_earned_points int;
     21    v_score         int;
     22    v_passed        boolean;
     23    v_passing_score numeric;
     24begin
     25    -- check answer count matches question count
     26    if (select count(*) from jsonb_array_elements(p_answers)) !=
     27       (select count(*) from quiz_question where quiz_id = p_quiz_id)
     28    then
     29        raise exception 'Answer count does not match question count for quiz %', p_quiz_id;
     30    end if;
    2131
    22         select e.id into e_id
    23         from enrollment e
    24         where e.user_id = write_review.user_id
    25           and e.course_version_id = write_review.course_version_id
    26         limit 1;
     32    -- check all questions belong to the quiz
     33    if exists (select 1
     34               from jsonb_array_elements(p_answers) a
     35               where not exists (select 1
     36                                 from quiz_question
     37                                 where id = (a ->> 'question_id')::bigint
     38                                   and quiz_id = p_quiz_id)) then
     39        raise exception 'One or more questions do not belong to quiz %', p_quiz_id;
     40    end if;
    2741
    28         -- Validate enrollment exists
    29         if e_id is null then
    30             raise exception 'No enrollment found for user % on course version %',
    31                 user_id, course_version_id
    32                 using errcode = 'NO_DATA_FOUND';
    33         end if;
     42    -- check all answer options belong to their respective questions
     43    if exists (select 1
     44               from jsonb_array_elements(p_answers) a
     45               where not exists (select 1
     46                                 from quiz_answer_option
     47                                 where id = (a ->> 'answer_option_id')::bigint
     48                                   and quiz_question_id = (a ->> 'question_id')::bigint)) then
     49        raise exception 'One or more answer options do not belong to their question';
     50    end if;
    3451
    35         insert into review (rating, comment, date, enrollment_id)
    36         values (new_rating, new_comment, now(), e_id)
    37         returning * into r;
     52    select coalesce(max(attempt_number), 0) + 1
     53    into v_attempt_num
     54    from quiz_attempt
     55    where enrollment_id = p_enrollment_id
     56      and quiz_id = p_quiz_id;
    3857
    39         return r;
    40     end;
    41 $$ language plpgsql;
     58    select coalesce(sum(points), 0)
     59    into v_total_points
     60    from quiz_question
     61    where quiz_id = p_quiz_id;
     62
     63    select coalesce(sum(qq.points), 0)
     64    into v_earned_points
     65    from jsonb_array_elements(p_answers) a
     66             join quiz_question qq on qq.id = (a ->> 'question_id')::bigint
     67             join quiz_answer_option qao on qao.id = (a ->> 'answer_option_id')::bigint
     68    where qao.correct = true;
     69
     70    select passing_score
     71    into v_passing_score
     72    from quiz
     73    where id = p_quiz_id;
     74
     75    v_score := case
     76                   when v_total_points > 0
     77                       then ((v_earned_points::numeric / v_total_points) * 100)::int
     78                   else 0 end;
     79   
     80    v_passed := v_score >= v_passing_score;
     81
     82    insert into quiz_attempt (attempt_number, completed_at, status, score, total_points, earned_points, passed, quiz_id,
     83                              enrollment_id)
     84    values (v_attempt_num, now(),
     85            case when v_passed then 'PASSED' else 'FAILED' end,
     86            v_score, v_total_points, v_earned_points, v_passed,
     87            p_quiz_id, p_enrollment_id)
     88    returning id into v_attempt_id;
     89
     90    insert into quiz_attempt_answer (correct, quiz_question_id, quiz_attempt_id)
     91    select qao.correct,
     92           (a ->> 'question_id')::bigint,
     93           v_attempt_id
     94    from jsonb_array_elements(p_answers) a
     95             join quiz_answer_option qao on qao.id = (a ->> 'answer_option_id')::bigint;
     96
     97    insert into quiz_attempt_answer_selected_options (quiz_attempt_answer_id, quiz_answer_option_id)
     98    select qaa.id, (a ->> 'answer_option_id')::bigint
     99    from jsonb_array_elements(p_answers) a
     100             join quiz_attempt_answer qaa
     101                  on qaa.quiz_attempt_id = v_attempt_id
     102                      and qaa.quiz_question_id = (a ->> 'question_id')::bigint;
     103
     104    return v_attempt_id;
     105end;
     106$$;
    42107}}}
    43108