-- name: CreateAssignment :one INSERT INTO assignments ( classroom_id, teacher_id, title, instructions, status, due_at, published_at, pass_threshold ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8 ) RETURNING *; -- name: AssignStudentToAssignment :exec INSERT INTO assignment_assignees ( assignment_id, student_id ) VALUES ( $1, $2 ) ON CONFLICT (assignment_id, student_id) DO NOTHING; -- name: DeleteAssignmentAssignee :exec DELETE FROM assignment_assignees WHERE assignment_id = $1 AND student_id = $2; -- name: GetAssignmentAssignee :one SELECT * FROM assignment_assignees WHERE assignment_id = $1 AND student_id = $2; -- name: UpdateAssignmentAIReview :one UPDATE assignment_assignees SET ai_feedback = $3, next_step_outcome = NULLIF($4::text, '')::assignment_next_step_outcome WHERE assignment_id = $1 AND student_id = $2 RETURNING *; -- name: UpdateAssignmentRedoPlan :one UPDATE assignment_assignees SET redo_plan = NULLIF($3::text, ''), redo_plan_generated_at = CASE WHEN NULLIF($3::text, '') IS NULL THEN NULL ELSE NOW() END WHERE assignment_id = $1 AND student_id = $2 RETURNING *; -- name: GetAssignmentRedoPlan :one SELECT assignment_id, student_id, redo_plan, redo_plan_generated_at FROM assignment_assignees WHERE assignment_id = $1 AND student_id = $2; -- name: UpdateAssignmentTeacherFeedback :one WITH student_question_set AS ( SELECT asq.assignment_id, asq.question_id, asq.position FROM assignment_student_questions asq WHERE asq.assignment_id = $1 AND asq.student_id = $2 ), selected_questions AS ( SELECT assignment_id, question_id, position FROM student_question_set UNION ALL SELECT aq.assignment_id, aq.question_id, aq.position FROM assignment_questions aq WHERE aq.assignment_id = $1 AND NOT EXISTS (SELECT 1 FROM student_question_set) ), score_summary AS ( SELECT CASE WHEN COUNT(sa.id) = 0 THEN NULL ELSE ROUND((AVG( CASE WHEN sa.is_correct IS NULL THEN COALESCE(sa.review_understanding_score, 0)::NUMERIC ELSE ( ((CASE WHEN sa.is_correct THEN 1 ELSE 0 END)::NUMERIC) + COALESCE(sa.review_understanding_score, 0)::NUMERIC ) / 2 END ) * 10)::NUMERIC, 2) END AS overall_score FROM selected_questions aq LEFT JOIN student_answers sa ON sa.assignment_id = aq.assignment_id AND sa.question_id = aq.question_id AND sa.student_id = $2 WHERE aq.assignment_id = $1 ), updated AS ( UPDATE assignment_assignees aa SET teacher_feedback = $3, pass_status_override = NULLIF($4::text, '')::assignment_pass_status, next_step_outcome = NULLIF($5::text, '')::assignment_next_step_outcome, overall_score = (SELECT overall_score FROM score_summary), pass_status = COALESCE( NULLIF($4::text, '')::assignment_pass_status, CASE WHEN (SELECT overall_score FROM score_summary) IS NULL THEN 'pending'::assignment_pass_status WHEN (SELECT overall_score FROM score_summary) >= a.pass_threshold THEN 'pass'::assignment_pass_status ELSE 'no_pass'::assignment_pass_status END ) FROM assignments a WHERE aa.assignment_id = $1 AND aa.student_id = $2 AND a.id = aa.assignment_id RETURNING aa.* ) SELECT * FROM updated; -- name: AddQuestionToAssignment :exec INSERT INTO assignment_questions ( assignment_id, question_id, position ) VALUES ( $1, $2, $3 ) ON CONFLICT (assignment_id, question_id) DO UPDATE SET position = EXCLUDED.position; -- name: DeleteAssignmentStudentQuestions :exec DELETE FROM assignment_student_questions WHERE assignment_id = $1 AND student_id = $2; -- name: AddAssignmentStudentQuestion :one INSERT INTO assignment_student_questions ( assignment_id, student_id, question_id, position, source_bucket, source_topic, source_difficulty, generator_seed ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8 ) RETURNING *; -- name: ListAssignmentStudentQuestions :many SELECT * FROM assignment_student_questions WHERE assignment_id = $1 AND student_id = $2 ORDER BY position ASC, id ASC; -- name: ListGeneratedQuestionsForAssignmentStudent :many SELECT asq.id, asq.assignment_id, asq.student_id, asq.question_id, asq.position, asq.source_bucket, asq.source_topic, asq.source_difficulty, asq.generator_seed, asq.created_at, q.author_teacher_id, q.title, q.prompt, q.subject, q.source, q.status, q.created_at AS question_created_at, q.updated_at AS question_updated_at, q.correct_answer, q.topic, q.difficulty FROM assignment_student_questions asq JOIN questions q ON q.id = asq.question_id WHERE asq.assignment_id = $1 AND asq.student_id = $2 ORDER BY asq.position ASC, asq.id ASC; -- name: ListAssignmentsByTeacher :many SELECT * FROM assignments WHERE teacher_id = $1 ORDER BY created_at DESC; -- name: ListAssignmentsForStudent :many SELECT a.* FROM assignment_assignees aa JOIN assignments a ON a.id = aa.assignment_id WHERE aa.student_id = $1 ORDER BY a.created_at DESC; -- name: GetAssignmentByID :one SELECT * FROM assignments WHERE id = $1; -- name: UpdateAssignmentDraft :one UPDATE assignments SET classroom_id = $2, title = $3, instructions = $4, due_at = $5, pass_threshold = $6, updated_at = NOW() WHERE id = $1 RETURNING *; -- name: CloseAssignment :one UPDATE assignments SET status = 'closed'::assignment_status, updated_at = NOW() WHERE id = $1 RETURNING *; -- name: ListQuestionsForAssignment :many SELECT aq.assignment_id, aq.question_id, aq.position, q.author_teacher_id, q.title, q.prompt, q.subject, q.source, q.status, q.created_at, q.updated_at FROM assignment_questions aq JOIN questions q ON q.id = aq.question_id WHERE aq.assignment_id = $1 ORDER BY aq.position ASC, aq.question_id ASC; -- name: GetAssignmentReviewSummary :one WITH student_question_set AS ( SELECT asq.student_id, asq.question_id, asq.position FROM assignment_student_questions asq WHERE asq.assignment_id = $1 ), students_with_personalized AS ( SELECT DISTINCT student_id FROM student_question_set ), selected_questions AS ( SELECT student_id, question_id, position FROM student_question_set UNION ALL SELECT aa.student_id, aq.question_id, aq.position FROM assignment_assignees aa JOIN assignment_questions aq ON aq.assignment_id = aa.assignment_id WHERE aa.assignment_id = $1 AND NOT EXISTS ( SELECT 1 FROM students_with_personalized swp WHERE swp.student_id = aa.student_id ) ), student_states AS ( SELECT aa.student_id, COUNT(sq.question_id)::BIGINT AS total_questions, CASE WHEN COUNT(sa.id) = 0 THEN 'not_started'::answer_status WHEN COUNT(sq.question_id) > 0 AND COUNT(sa.id) FILTER (WHERE sa.status = 'reviewed') = COUNT(sq.question_id) THEN 'reviewed'::answer_status WHEN COUNT(sa.id) FILTER (WHERE sa.status = 'submitted') > 0 THEN 'submitted'::answer_status WHEN COUNT(sa.id) FILTER (WHERE sa.status = 'in_progress') > 0 THEN 'in_progress'::answer_status WHEN COUNT(sa.id) FILTER (WHERE sa.status = 'reviewed') > 0 THEN 'in_progress'::answer_status ELSE 'not_started'::answer_status END AS review_status FROM assignment_assignees aa LEFT JOIN selected_questions sq ON sq.student_id = aa.student_id LEFT JOIN student_answers sa ON sa.assignment_id = aa.assignment_id AND sa.question_id = sq.question_id AND sa.student_id = aa.student_id WHERE aa.assignment_id = $1 GROUP BY aa.student_id ) SELECT $1::BIGINT AS assignment_id, COALESCE(MAX(student_states.total_questions), 0)::BIGINT AS total_questions, COUNT(*)::BIGINT AS total_assigned, COUNT(*) FILTER (WHERE review_status = 'not_started')::BIGINT AS not_started, COUNT(*) FILTER (WHERE review_status = 'in_progress')::BIGINT AS in_progress, COUNT(*) FILTER (WHERE review_status = 'submitted')::BIGINT AS submitted, COUNT(*) FILTER (WHERE review_status = 'reviewed')::BIGINT AS reviewed FROM student_states; -- name: ListAssignmentReviewQueue :many WITH student_question_set AS ( SELECT asq.student_id, asq.question_id, asq.position FROM assignment_student_questions asq WHERE asq.assignment_id = $1 ), students_with_personalized AS ( SELECT DISTINCT student_id FROM student_question_set ), selected_questions AS ( SELECT student_id, question_id, position FROM student_question_set UNION ALL SELECT aa.student_id, aq.question_id, aq.position FROM assignment_assignees aa JOIN assignment_questions aq ON aq.assignment_id = aa.assignment_id WHERE aa.assignment_id = $1 AND NOT EXISTS ( SELECT 1 FROM students_with_personalized swp WHERE swp.student_id = aa.student_id ) ), student_states AS ( SELECT aa.assignment_id, aa.student_id, aa.next_step_outcome, u.full_name AS student_name, u.email AS student_email, COUNT(sq.question_id)::BIGINT AS total_questions, COUNT(sa.id)::BIGINT AS answered_questions, COUNT(sa.id) FILTER (WHERE sa.status = 'reviewed')::BIGINT AS reviewed_questions, COUNT(sa.id) FILTER (WHERE sa.status = 'submitted')::BIGINT AS submitted_questions, COUNT(sa.id) FILTER (WHERE sa.status = 'in_progress')::BIGINT AS in_progress_questions, MAX(sa.submitted_at)::timestamptz AS latest_submitted_at, MAX(sa.reviewed_at)::timestamptz AS latest_reviewed_at, CASE WHEN COUNT(sa.id) = 0 THEN 'not_started'::answer_status WHEN COUNT(sq.question_id) > 0 AND COUNT(sa.id) FILTER (WHERE sa.status = 'reviewed') = COUNT(sq.question_id) THEN 'reviewed'::answer_status WHEN COUNT(sa.id) FILTER (WHERE sa.status = 'submitted') > 0 THEN 'submitted'::answer_status WHEN COUNT(sa.id) FILTER (WHERE sa.status = 'in_progress') > 0 THEN 'in_progress'::answer_status WHEN COUNT(sa.id) FILTER (WHERE sa.status = 'reviewed') > 0 THEN 'in_progress'::answer_status ELSE 'not_started'::answer_status END AS review_status FROM assignment_assignees aa JOIN users u ON u.id = aa.student_id LEFT JOIN selected_questions sq ON sq.student_id = aa.student_id LEFT JOIN student_answers sa ON sa.assignment_id = aa.assignment_id AND sa.question_id = sq.question_id AND sa.student_id = aa.student_id WHERE aa.assignment_id = $1 GROUP BY aa.assignment_id, aa.student_id, aa.next_step_outcome, u.full_name, u.email ) SELECT student_states.assignment_id, student_states.student_id, student_states.next_step_outcome, student_states.student_name, student_states.student_email, student_states.total_questions, student_states.answered_questions, student_states.reviewed_questions, student_states.submitted_questions, student_states.in_progress_questions, student_states.review_status, student_states.latest_submitted_at, student_states.latest_reviewed_at FROM student_states WHERE ($2::text = '' OR review_status::text = $2::text) ORDER BY student_states.student_name ASC, student_states.student_id ASC;