1070 lines
33 KiB
Go
1070 lines
33 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.30.0
|
|
// source: assignments.sql
|
|
|
|
package sqlc
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const addAssignmentStudentQuestion = `-- 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 id, assignment_id, student_id, question_id, position, source_bucket, source_topic, source_difficulty, generator_seed, created_at
|
|
`
|
|
|
|
type AddAssignmentStudentQuestionParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
QuestionID int64 `json:"question_id"`
|
|
Position int32 `json:"position"`
|
|
SourceBucket string `json:"source_bucket"`
|
|
SourceTopic NullQuestionTopic `json:"source_topic"`
|
|
SourceDifficulty NullQuestionDifficulty `json:"source_difficulty"`
|
|
GeneratorSeed pgtype.Int8 `json:"generator_seed"`
|
|
}
|
|
|
|
func (q *Queries) AddAssignmentStudentQuestion(ctx context.Context, arg AddAssignmentStudentQuestionParams) (AssignmentStudentQuestion, error) {
|
|
row := q.db.QueryRow(ctx, addAssignmentStudentQuestion,
|
|
arg.AssignmentID,
|
|
arg.StudentID,
|
|
arg.QuestionID,
|
|
arg.Position,
|
|
arg.SourceBucket,
|
|
arg.SourceTopic,
|
|
arg.SourceDifficulty,
|
|
arg.GeneratorSeed,
|
|
)
|
|
var i AssignmentStudentQuestion
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.QuestionID,
|
|
&i.Position,
|
|
&i.SourceBucket,
|
|
&i.SourceTopic,
|
|
&i.SourceDifficulty,
|
|
&i.GeneratorSeed,
|
|
&i.CreatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const addQuestionToAssignment = `-- 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
|
|
`
|
|
|
|
type AddQuestionToAssignmentParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
QuestionID int64 `json:"question_id"`
|
|
Position int32 `json:"position"`
|
|
}
|
|
|
|
func (q *Queries) AddQuestionToAssignment(ctx context.Context, arg AddQuestionToAssignmentParams) error {
|
|
_, err := q.db.Exec(ctx, addQuestionToAssignment, arg.AssignmentID, arg.QuestionID, arg.Position)
|
|
return err
|
|
}
|
|
|
|
const assignStudentToAssignment = `-- name: AssignStudentToAssignment :exec
|
|
INSERT INTO assignment_assignees (
|
|
assignment_id,
|
|
student_id
|
|
) VALUES (
|
|
$1,
|
|
$2
|
|
)
|
|
ON CONFLICT (assignment_id, student_id) DO NOTHING
|
|
`
|
|
|
|
type AssignStudentToAssignmentParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
}
|
|
|
|
func (q *Queries) AssignStudentToAssignment(ctx context.Context, arg AssignStudentToAssignmentParams) error {
|
|
_, err := q.db.Exec(ctx, assignStudentToAssignment, arg.AssignmentID, arg.StudentID)
|
|
return err
|
|
}
|
|
|
|
const closeAssignment = `-- name: CloseAssignment :one
|
|
UPDATE assignments
|
|
SET status = 'closed'::assignment_status,
|
|
updated_at = NOW()
|
|
WHERE id = $1
|
|
RETURNING id, classroom_id, teacher_id, title, instructions, status, due_at, published_at, created_at, updated_at, pass_threshold
|
|
`
|
|
|
|
func (q *Queries) CloseAssignment(ctx context.Context, id int64) (Assignment, error) {
|
|
row := q.db.QueryRow(ctx, closeAssignment, id)
|
|
var i Assignment
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ClassroomID,
|
|
&i.TeacherID,
|
|
&i.Title,
|
|
&i.Instructions,
|
|
&i.Status,
|
|
&i.DueAt,
|
|
&i.PublishedAt,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.PassThreshold,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const createAssignment = `-- 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 id, classroom_id, teacher_id, title, instructions, status, due_at, published_at, created_at, updated_at, pass_threshold
|
|
`
|
|
|
|
type CreateAssignmentParams struct {
|
|
ClassroomID int64 `json:"classroom_id"`
|
|
TeacherID int64 `json:"teacher_id"`
|
|
Title string `json:"title"`
|
|
Instructions pgtype.Text `json:"instructions"`
|
|
Status AssignmentStatus `json:"status"`
|
|
DueAt pgtype.Timestamptz `json:"due_at"`
|
|
PublishedAt pgtype.Timestamptz `json:"published_at"`
|
|
PassThreshold pgtype.Numeric `json:"pass_threshold"`
|
|
}
|
|
|
|
func (q *Queries) CreateAssignment(ctx context.Context, arg CreateAssignmentParams) (Assignment, error) {
|
|
row := q.db.QueryRow(ctx, createAssignment,
|
|
arg.ClassroomID,
|
|
arg.TeacherID,
|
|
arg.Title,
|
|
arg.Instructions,
|
|
arg.Status,
|
|
arg.DueAt,
|
|
arg.PublishedAt,
|
|
arg.PassThreshold,
|
|
)
|
|
var i Assignment
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ClassroomID,
|
|
&i.TeacherID,
|
|
&i.Title,
|
|
&i.Instructions,
|
|
&i.Status,
|
|
&i.DueAt,
|
|
&i.PublishedAt,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.PassThreshold,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const deleteAssignmentAssignee = `-- name: DeleteAssignmentAssignee :exec
|
|
DELETE FROM assignment_assignees
|
|
WHERE assignment_id = $1
|
|
AND student_id = $2
|
|
`
|
|
|
|
type DeleteAssignmentAssigneeParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
}
|
|
|
|
func (q *Queries) DeleteAssignmentAssignee(ctx context.Context, arg DeleteAssignmentAssigneeParams) error {
|
|
_, err := q.db.Exec(ctx, deleteAssignmentAssignee, arg.AssignmentID, arg.StudentID)
|
|
return err
|
|
}
|
|
|
|
const deleteAssignmentStudentQuestions = `-- name: DeleteAssignmentStudentQuestions :exec
|
|
DELETE FROM assignment_student_questions
|
|
WHERE assignment_id = $1
|
|
AND student_id = $2
|
|
`
|
|
|
|
type DeleteAssignmentStudentQuestionsParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
}
|
|
|
|
func (q *Queries) DeleteAssignmentStudentQuestions(ctx context.Context, arg DeleteAssignmentStudentQuestionsParams) error {
|
|
_, err := q.db.Exec(ctx, deleteAssignmentStudentQuestions, arg.AssignmentID, arg.StudentID)
|
|
return err
|
|
}
|
|
|
|
const getAssignmentAssignee = `-- name: GetAssignmentAssignee :one
|
|
SELECT assignment_id, student_id, assigned_at, ai_feedback, teacher_feedback, overall_score, pass_threshold, pass_status, pass_status_override, next_step_outcome, redo_plan, redo_plan_generated_at
|
|
FROM assignment_assignees
|
|
WHERE assignment_id = $1
|
|
AND student_id = $2
|
|
`
|
|
|
|
type GetAssignmentAssigneeParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
}
|
|
|
|
func (q *Queries) GetAssignmentAssignee(ctx context.Context, arg GetAssignmentAssigneeParams) (AssignmentAssignee, error) {
|
|
row := q.db.QueryRow(ctx, getAssignmentAssignee, arg.AssignmentID, arg.StudentID)
|
|
var i AssignmentAssignee
|
|
err := row.Scan(
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.AssignedAt,
|
|
&i.AiFeedback,
|
|
&i.TeacherFeedback,
|
|
&i.OverallScore,
|
|
&i.PassThreshold,
|
|
&i.PassStatus,
|
|
&i.PassStatusOverride,
|
|
&i.NextStepOutcome,
|
|
&i.RedoPlan,
|
|
&i.RedoPlanGeneratedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAssignmentByID = `-- name: GetAssignmentByID :one
|
|
SELECT id, classroom_id, teacher_id, title, instructions, status, due_at, published_at, created_at, updated_at, pass_threshold
|
|
FROM assignments
|
|
WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) GetAssignmentByID(ctx context.Context, id int64) (Assignment, error) {
|
|
row := q.db.QueryRow(ctx, getAssignmentByID, id)
|
|
var i Assignment
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ClassroomID,
|
|
&i.TeacherID,
|
|
&i.Title,
|
|
&i.Instructions,
|
|
&i.Status,
|
|
&i.DueAt,
|
|
&i.PublishedAt,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.PassThreshold,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAssignmentRedoPlan = `-- 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
|
|
`
|
|
|
|
type GetAssignmentRedoPlanParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
}
|
|
|
|
type GetAssignmentRedoPlanRow struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
RedoPlan pgtype.Text `json:"redo_plan"`
|
|
RedoPlanGeneratedAt pgtype.Timestamptz `json:"redo_plan_generated_at"`
|
|
}
|
|
|
|
func (q *Queries) GetAssignmentRedoPlan(ctx context.Context, arg GetAssignmentRedoPlanParams) (GetAssignmentRedoPlanRow, error) {
|
|
row := q.db.QueryRow(ctx, getAssignmentRedoPlan, arg.AssignmentID, arg.StudentID)
|
|
var i GetAssignmentRedoPlanRow
|
|
err := row.Scan(
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.RedoPlan,
|
|
&i.RedoPlanGeneratedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAssignmentReviewSummary = `-- 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
|
|
`
|
|
|
|
type GetAssignmentReviewSummaryRow struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
TotalQuestions int64 `json:"total_questions"`
|
|
TotalAssigned int64 `json:"total_assigned"`
|
|
NotStarted int64 `json:"not_started"`
|
|
InProgress int64 `json:"in_progress"`
|
|
Submitted int64 `json:"submitted"`
|
|
Reviewed int64 `json:"reviewed"`
|
|
}
|
|
|
|
func (q *Queries) GetAssignmentReviewSummary(ctx context.Context, dollar_1 int64) (GetAssignmentReviewSummaryRow, error) {
|
|
row := q.db.QueryRow(ctx, getAssignmentReviewSummary, dollar_1)
|
|
var i GetAssignmentReviewSummaryRow
|
|
err := row.Scan(
|
|
&i.AssignmentID,
|
|
&i.TotalQuestions,
|
|
&i.TotalAssigned,
|
|
&i.NotStarted,
|
|
&i.InProgress,
|
|
&i.Submitted,
|
|
&i.Reviewed,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listAssignmentReviewQueue = `-- 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
|
|
`
|
|
|
|
type ListAssignmentReviewQueueParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
Column2 string `json:"column_2"`
|
|
}
|
|
|
|
type ListAssignmentReviewQueueRow struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
NextStepOutcome NullAssignmentNextStepOutcome `json:"next_step_outcome"`
|
|
StudentName string `json:"student_name"`
|
|
StudentEmail string `json:"student_email"`
|
|
TotalQuestions int64 `json:"total_questions"`
|
|
AnsweredQuestions int64 `json:"answered_questions"`
|
|
ReviewedQuestions int64 `json:"reviewed_questions"`
|
|
SubmittedQuestions int64 `json:"submitted_questions"`
|
|
InProgressQuestions int64 `json:"in_progress_questions"`
|
|
ReviewStatus AnswerStatus `json:"review_status"`
|
|
LatestSubmittedAt pgtype.Timestamptz `json:"latest_submitted_at"`
|
|
LatestReviewedAt pgtype.Timestamptz `json:"latest_reviewed_at"`
|
|
}
|
|
|
|
func (q *Queries) ListAssignmentReviewQueue(ctx context.Context, arg ListAssignmentReviewQueueParams) ([]ListAssignmentReviewQueueRow, error) {
|
|
rows, err := q.db.Query(ctx, listAssignmentReviewQueue, arg.AssignmentID, arg.Column2)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListAssignmentReviewQueueRow{}
|
|
for rows.Next() {
|
|
var i ListAssignmentReviewQueueRow
|
|
if err := rows.Scan(
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.NextStepOutcome,
|
|
&i.StudentName,
|
|
&i.StudentEmail,
|
|
&i.TotalQuestions,
|
|
&i.AnsweredQuestions,
|
|
&i.ReviewedQuestions,
|
|
&i.SubmittedQuestions,
|
|
&i.InProgressQuestions,
|
|
&i.ReviewStatus,
|
|
&i.LatestSubmittedAt,
|
|
&i.LatestReviewedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAssignmentStudentQuestions = `-- name: ListAssignmentStudentQuestions :many
|
|
SELECT id, assignment_id, student_id, question_id, position, source_bucket, source_topic, source_difficulty, generator_seed, created_at
|
|
FROM assignment_student_questions
|
|
WHERE assignment_id = $1
|
|
AND student_id = $2
|
|
ORDER BY position ASC, id ASC
|
|
`
|
|
|
|
type ListAssignmentStudentQuestionsParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
}
|
|
|
|
func (q *Queries) ListAssignmentStudentQuestions(ctx context.Context, arg ListAssignmentStudentQuestionsParams) ([]AssignmentStudentQuestion, error) {
|
|
rows, err := q.db.Query(ctx, listAssignmentStudentQuestions, arg.AssignmentID, arg.StudentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []AssignmentStudentQuestion{}
|
|
for rows.Next() {
|
|
var i AssignmentStudentQuestion
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.QuestionID,
|
|
&i.Position,
|
|
&i.SourceBucket,
|
|
&i.SourceTopic,
|
|
&i.SourceDifficulty,
|
|
&i.GeneratorSeed,
|
|
&i.CreatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAssignmentsByTeacher = `-- name: ListAssignmentsByTeacher :many
|
|
SELECT id, classroom_id, teacher_id, title, instructions, status, due_at, published_at, created_at, updated_at, pass_threshold
|
|
FROM assignments
|
|
WHERE teacher_id = $1
|
|
ORDER BY created_at DESC
|
|
`
|
|
|
|
func (q *Queries) ListAssignmentsByTeacher(ctx context.Context, teacherID int64) ([]Assignment, error) {
|
|
rows, err := q.db.Query(ctx, listAssignmentsByTeacher, teacherID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []Assignment{}
|
|
for rows.Next() {
|
|
var i Assignment
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.ClassroomID,
|
|
&i.TeacherID,
|
|
&i.Title,
|
|
&i.Instructions,
|
|
&i.Status,
|
|
&i.DueAt,
|
|
&i.PublishedAt,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.PassThreshold,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAssignmentsForStudent = `-- name: ListAssignmentsForStudent :many
|
|
SELECT a.id, a.classroom_id, a.teacher_id, a.title, a.instructions, a.status, a.due_at, a.published_at, a.created_at, a.updated_at, a.pass_threshold
|
|
FROM assignment_assignees aa
|
|
JOIN assignments a ON a.id = aa.assignment_id
|
|
WHERE aa.student_id = $1
|
|
ORDER BY a.created_at DESC
|
|
`
|
|
|
|
func (q *Queries) ListAssignmentsForStudent(ctx context.Context, studentID int64) ([]Assignment, error) {
|
|
rows, err := q.db.Query(ctx, listAssignmentsForStudent, studentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []Assignment{}
|
|
for rows.Next() {
|
|
var i Assignment
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.ClassroomID,
|
|
&i.TeacherID,
|
|
&i.Title,
|
|
&i.Instructions,
|
|
&i.Status,
|
|
&i.DueAt,
|
|
&i.PublishedAt,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.PassThreshold,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listGeneratedQuestionsForAssignmentStudent = `-- 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
|
|
`
|
|
|
|
type ListGeneratedQuestionsForAssignmentStudentParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
}
|
|
|
|
type ListGeneratedQuestionsForAssignmentStudentRow struct {
|
|
ID int64 `json:"id"`
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
QuestionID int64 `json:"question_id"`
|
|
Position int32 `json:"position"`
|
|
SourceBucket string `json:"source_bucket"`
|
|
SourceTopic NullQuestionTopic `json:"source_topic"`
|
|
SourceDifficulty NullQuestionDifficulty `json:"source_difficulty"`
|
|
GeneratorSeed pgtype.Int8 `json:"generator_seed"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
AuthorTeacherID int64 `json:"author_teacher_id"`
|
|
Title string `json:"title"`
|
|
Prompt string `json:"prompt"`
|
|
Subject pgtype.Text `json:"subject"`
|
|
Source pgtype.Text `json:"source"`
|
|
Status QuestionStatus `json:"status"`
|
|
QuestionCreatedAt pgtype.Timestamptz `json:"question_created_at"`
|
|
QuestionUpdatedAt pgtype.Timestamptz `json:"question_updated_at"`
|
|
CorrectAnswer pgtype.Text `json:"correct_answer"`
|
|
Topic NullQuestionTopic `json:"topic"`
|
|
Difficulty NullQuestionDifficulty `json:"difficulty"`
|
|
}
|
|
|
|
func (q *Queries) ListGeneratedQuestionsForAssignmentStudent(ctx context.Context, arg ListGeneratedQuestionsForAssignmentStudentParams) ([]ListGeneratedQuestionsForAssignmentStudentRow, error) {
|
|
rows, err := q.db.Query(ctx, listGeneratedQuestionsForAssignmentStudent, arg.AssignmentID, arg.StudentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListGeneratedQuestionsForAssignmentStudentRow{}
|
|
for rows.Next() {
|
|
var i ListGeneratedQuestionsForAssignmentStudentRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.QuestionID,
|
|
&i.Position,
|
|
&i.SourceBucket,
|
|
&i.SourceTopic,
|
|
&i.SourceDifficulty,
|
|
&i.GeneratorSeed,
|
|
&i.CreatedAt,
|
|
&i.AuthorTeacherID,
|
|
&i.Title,
|
|
&i.Prompt,
|
|
&i.Subject,
|
|
&i.Source,
|
|
&i.Status,
|
|
&i.QuestionCreatedAt,
|
|
&i.QuestionUpdatedAt,
|
|
&i.CorrectAnswer,
|
|
&i.Topic,
|
|
&i.Difficulty,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listQuestionsForAssignment = `-- 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
|
|
`
|
|
|
|
type ListQuestionsForAssignmentRow struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
QuestionID int64 `json:"question_id"`
|
|
Position int32 `json:"position"`
|
|
AuthorTeacherID int64 `json:"author_teacher_id"`
|
|
Title string `json:"title"`
|
|
Prompt string `json:"prompt"`
|
|
Subject pgtype.Text `json:"subject"`
|
|
Source pgtype.Text `json:"source"`
|
|
Status QuestionStatus `json:"status"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
|
}
|
|
|
|
func (q *Queries) ListQuestionsForAssignment(ctx context.Context, assignmentID int64) ([]ListQuestionsForAssignmentRow, error) {
|
|
rows, err := q.db.Query(ctx, listQuestionsForAssignment, assignmentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListQuestionsForAssignmentRow{}
|
|
for rows.Next() {
|
|
var i ListQuestionsForAssignmentRow
|
|
if err := rows.Scan(
|
|
&i.AssignmentID,
|
|
&i.QuestionID,
|
|
&i.Position,
|
|
&i.AuthorTeacherID,
|
|
&i.Title,
|
|
&i.Prompt,
|
|
&i.Subject,
|
|
&i.Source,
|
|
&i.Status,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const updateAssignmentAIReview = `-- 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 assignment_id, student_id, assigned_at, ai_feedback, teacher_feedback, overall_score, pass_threshold, pass_status, pass_status_override, next_step_outcome, redo_plan, redo_plan_generated_at
|
|
`
|
|
|
|
type UpdateAssignmentAIReviewParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
AiFeedback pgtype.Text `json:"ai_feedback"`
|
|
Column4 string `json:"column_4"`
|
|
}
|
|
|
|
func (q *Queries) UpdateAssignmentAIReview(ctx context.Context, arg UpdateAssignmentAIReviewParams) (AssignmentAssignee, error) {
|
|
row := q.db.QueryRow(ctx, updateAssignmentAIReview,
|
|
arg.AssignmentID,
|
|
arg.StudentID,
|
|
arg.AiFeedback,
|
|
arg.Column4,
|
|
)
|
|
var i AssignmentAssignee
|
|
err := row.Scan(
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.AssignedAt,
|
|
&i.AiFeedback,
|
|
&i.TeacherFeedback,
|
|
&i.OverallScore,
|
|
&i.PassThreshold,
|
|
&i.PassStatus,
|
|
&i.PassStatusOverride,
|
|
&i.NextStepOutcome,
|
|
&i.RedoPlan,
|
|
&i.RedoPlanGeneratedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateAssignmentDraft = `-- 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 id, classroom_id, teacher_id, title, instructions, status, due_at, published_at, created_at, updated_at, pass_threshold
|
|
`
|
|
|
|
type UpdateAssignmentDraftParams struct {
|
|
ID int64 `json:"id"`
|
|
ClassroomID int64 `json:"classroom_id"`
|
|
Title string `json:"title"`
|
|
Instructions pgtype.Text `json:"instructions"`
|
|
DueAt pgtype.Timestamptz `json:"due_at"`
|
|
PassThreshold pgtype.Numeric `json:"pass_threshold"`
|
|
}
|
|
|
|
func (q *Queries) UpdateAssignmentDraft(ctx context.Context, arg UpdateAssignmentDraftParams) (Assignment, error) {
|
|
row := q.db.QueryRow(ctx, updateAssignmentDraft,
|
|
arg.ID,
|
|
arg.ClassroomID,
|
|
arg.Title,
|
|
arg.Instructions,
|
|
arg.DueAt,
|
|
arg.PassThreshold,
|
|
)
|
|
var i Assignment
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ClassroomID,
|
|
&i.TeacherID,
|
|
&i.Title,
|
|
&i.Instructions,
|
|
&i.Status,
|
|
&i.DueAt,
|
|
&i.PublishedAt,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.PassThreshold,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateAssignmentRedoPlan = `-- 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 assignment_id, student_id, assigned_at, ai_feedback, teacher_feedback, overall_score, pass_threshold, pass_status, pass_status_override, next_step_outcome, redo_plan, redo_plan_generated_at
|
|
`
|
|
|
|
type UpdateAssignmentRedoPlanParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
Column3 string `json:"column_3"`
|
|
}
|
|
|
|
func (q *Queries) UpdateAssignmentRedoPlan(ctx context.Context, arg UpdateAssignmentRedoPlanParams) (AssignmentAssignee, error) {
|
|
row := q.db.QueryRow(ctx, updateAssignmentRedoPlan, arg.AssignmentID, arg.StudentID, arg.Column3)
|
|
var i AssignmentAssignee
|
|
err := row.Scan(
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.AssignedAt,
|
|
&i.AiFeedback,
|
|
&i.TeacherFeedback,
|
|
&i.OverallScore,
|
|
&i.PassThreshold,
|
|
&i.PassStatus,
|
|
&i.PassStatusOverride,
|
|
&i.NextStepOutcome,
|
|
&i.RedoPlan,
|
|
&i.RedoPlanGeneratedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateAssignmentTeacherFeedback = `-- 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.assignment_id, aa.student_id, aa.assigned_at, aa.ai_feedback, aa.teacher_feedback, aa.overall_score, aa.pass_threshold, aa.pass_status, aa.pass_status_override, aa.next_step_outcome, aa.redo_plan, aa.redo_plan_generated_at
|
|
)
|
|
SELECT assignment_id, student_id, assigned_at, ai_feedback, teacher_feedback, overall_score, pass_threshold, pass_status, pass_status_override, next_step_outcome, redo_plan, redo_plan_generated_at
|
|
FROM updated
|
|
`
|
|
|
|
type UpdateAssignmentTeacherFeedbackParams struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
TeacherFeedback pgtype.Text `json:"teacher_feedback"`
|
|
Column4 string `json:"column_4"`
|
|
Column5 string `json:"column_5"`
|
|
}
|
|
|
|
type UpdateAssignmentTeacherFeedbackRow struct {
|
|
AssignmentID int64 `json:"assignment_id"`
|
|
StudentID int64 `json:"student_id"`
|
|
AssignedAt pgtype.Timestamptz `json:"assigned_at"`
|
|
AiFeedback pgtype.Text `json:"ai_feedback"`
|
|
TeacherFeedback pgtype.Text `json:"teacher_feedback"`
|
|
OverallScore pgtype.Numeric `json:"overall_score"`
|
|
PassThreshold pgtype.Numeric `json:"pass_threshold"`
|
|
PassStatus AssignmentPassStatus `json:"pass_status"`
|
|
PassStatusOverride NullAssignmentPassStatus `json:"pass_status_override"`
|
|
NextStepOutcome NullAssignmentNextStepOutcome `json:"next_step_outcome"`
|
|
RedoPlan pgtype.Text `json:"redo_plan"`
|
|
RedoPlanGeneratedAt pgtype.Timestamptz `json:"redo_plan_generated_at"`
|
|
}
|
|
|
|
func (q *Queries) UpdateAssignmentTeacherFeedback(ctx context.Context, arg UpdateAssignmentTeacherFeedbackParams) (UpdateAssignmentTeacherFeedbackRow, error) {
|
|
row := q.db.QueryRow(ctx, updateAssignmentTeacherFeedback,
|
|
arg.AssignmentID,
|
|
arg.StudentID,
|
|
arg.TeacherFeedback,
|
|
arg.Column4,
|
|
arg.Column5,
|
|
)
|
|
var i UpdateAssignmentTeacherFeedbackRow
|
|
err := row.Scan(
|
|
&i.AssignmentID,
|
|
&i.StudentID,
|
|
&i.AssignedAt,
|
|
&i.AiFeedback,
|
|
&i.TeacherFeedback,
|
|
&i.OverallScore,
|
|
&i.PassThreshold,
|
|
&i.PassStatus,
|
|
&i.PassStatusOverride,
|
|
&i.NextStepOutcome,
|
|
&i.RedoPlan,
|
|
&i.RedoPlanGeneratedAt,
|
|
)
|
|
return i, err
|
|
}
|