Boost Azure Demo
This commit is contained in:
1069
Backend/internal/sqlc/assignments.sql.go
Normal file
1069
Backend/internal/sqlc/assignments.sql.go
Normal file
File diff suppressed because it is too large
Load Diff
147
Backend/internal/sqlc/classrooms.sql.go
Normal file
147
Backend/internal/sqlc/classrooms.sql.go
Normal file
@@ -0,0 +1,147 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: classrooms.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const addStudentToClassroom = `-- name: AddStudentToClassroom :exec
|
||||
INSERT INTO classroom_students (
|
||||
classroom_id,
|
||||
student_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
)
|
||||
ON CONFLICT (classroom_id, student_id) DO NOTHING
|
||||
`
|
||||
|
||||
type AddStudentToClassroomParams struct {
|
||||
ClassroomID int64 `json:"classroom_id"`
|
||||
StudentID int64 `json:"student_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) AddStudentToClassroom(ctx context.Context, arg AddStudentToClassroomParams) error {
|
||||
_, err := q.db.Exec(ctx, addStudentToClassroom, arg.ClassroomID, arg.StudentID)
|
||||
return err
|
||||
}
|
||||
|
||||
const createClassroom = `-- name: CreateClassroom :one
|
||||
INSERT INTO classrooms (
|
||||
teacher_id,
|
||||
name,
|
||||
code,
|
||||
description
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4
|
||||
)
|
||||
RETURNING id, teacher_id, name, code, description, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateClassroomParams struct {
|
||||
TeacherID int64 `json:"teacher_id"`
|
||||
Name string `json:"name"`
|
||||
Code pgtype.Text `json:"code"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateClassroom(ctx context.Context, arg CreateClassroomParams) (Classroom, error) {
|
||||
row := q.db.QueryRow(ctx, createClassroom,
|
||||
arg.TeacherID,
|
||||
arg.Name,
|
||||
arg.Code,
|
||||
arg.Description,
|
||||
)
|
||||
var i Classroom
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.TeacherID,
|
||||
&i.Name,
|
||||
&i.Code,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listClassroomsByTeacher = `-- name: ListClassroomsByTeacher :many
|
||||
SELECT id, teacher_id, name, code, description, created_at, updated_at
|
||||
FROM classrooms
|
||||
WHERE teacher_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListClassroomsByTeacher(ctx context.Context, teacherID int64) ([]Classroom, error) {
|
||||
rows, err := q.db.Query(ctx, listClassroomsByTeacher, teacherID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []Classroom{}
|
||||
for rows.Next() {
|
||||
var i Classroom
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.TeacherID,
|
||||
&i.Name,
|
||||
&i.Code,
|
||||
&i.Description,
|
||||
&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 listStudentsForClassroom = `-- name: ListStudentsForClassroom :many
|
||||
SELECT u.id, u.email, u.password_hash, u.role, u.full_name, u.is_active, u.created_at, u.updated_at
|
||||
FROM classroom_students cs
|
||||
JOIN users u ON u.id = cs.student_id
|
||||
WHERE cs.classroom_id = $1
|
||||
ORDER BY u.full_name ASC
|
||||
`
|
||||
|
||||
func (q *Queries) ListStudentsForClassroom(ctx context.Context, classroomID int64) ([]User, error) {
|
||||
rows, err := q.db.Query(ctx, listStudentsForClassroom, classroomID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []User{}
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.Role,
|
||||
&i.FullName,
|
||||
&i.IsActive,
|
||||
&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
|
||||
}
|
||||
32
Backend/internal/sqlc/db.go
Normal file
32
Backend/internal/sqlc/db.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
742
Backend/internal/sqlc/messages.sql.go
Normal file
742
Backend/internal/sqlc/messages.sql.go
Normal file
@@ -0,0 +1,742 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: messages.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const addMessageThreadParticipant = `-- name: AddMessageThreadParticipant :exec
|
||||
INSERT INTO message_thread_participants (
|
||||
thread_id,
|
||||
user_id,
|
||||
last_read_at
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
)
|
||||
ON CONFLICT (thread_id, user_id) DO NOTHING
|
||||
`
|
||||
|
||||
type AddMessageThreadParticipantParams struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
LastReadAt pgtype.Timestamptz `json:"last_read_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) AddMessageThreadParticipant(ctx context.Context, arg AddMessageThreadParticipantParams) error {
|
||||
_, err := q.db.Exec(ctx, addMessageThreadParticipant, arg.ThreadID, arg.UserID, arg.LastReadAt)
|
||||
return err
|
||||
}
|
||||
|
||||
const createMessageThread = `-- name: CreateMessageThread :one
|
||||
INSERT INTO message_threads (
|
||||
created_by_user_id,
|
||||
subject
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
)
|
||||
RETURNING id, created_by_user_id, subject, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateMessageThreadParams struct {
|
||||
CreatedByUserID int64 `json:"created_by_user_id"`
|
||||
Subject string `json:"subject"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateMessageThread(ctx context.Context, arg CreateMessageThreadParams) (MessageThread, error) {
|
||||
row := q.db.QueryRow(ctx, createMessageThread, arg.CreatedByUserID, arg.Subject)
|
||||
var i MessageThread
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedByUserID,
|
||||
&i.Subject,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createThreadMessage = `-- name: CreateThreadMessage :one
|
||||
INSERT INTO messages (
|
||||
thread_id,
|
||||
sender_user_id,
|
||||
body
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
)
|
||||
RETURNING id, thread_id, sender_user_id, body, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateThreadMessageParams struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
SenderUserID int64 `json:"sender_user_id"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateThreadMessage(ctx context.Context, arg CreateThreadMessageParams) (Message, error) {
|
||||
row := q.db.QueryRow(ctx, createThreadMessage, arg.ThreadID, arg.SenderUserID, arg.Body)
|
||||
var i Message
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ThreadID,
|
||||
&i.SenderUserID,
|
||||
&i.Body,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteMessageThread = `-- name: DeleteMessageThread :one
|
||||
DELETE FROM message_threads
|
||||
WHERE id = $1
|
||||
RETURNING id, created_by_user_id, subject, created_at, updated_at
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteMessageThread(ctx context.Context, threadID int64) (MessageThread, error) {
|
||||
row := q.db.QueryRow(ctx, deleteMessageThread, threadID)
|
||||
var i MessageThread
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedByUserID,
|
||||
&i.Subject,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteThreadMessage = `-- name: DeleteThreadMessage :one
|
||||
DELETE FROM messages
|
||||
WHERE id = $1
|
||||
AND thread_id = $2
|
||||
AND sender_user_id = $3
|
||||
RETURNING id, thread_id, sender_user_id, body, created_at, updated_at
|
||||
`
|
||||
|
||||
type DeleteThreadMessageParams struct {
|
||||
MessageID int64 `json:"message_id"`
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteThreadMessage(ctx context.Context, arg DeleteThreadMessageParams) (Message, error) {
|
||||
row := q.db.QueryRow(ctx, deleteThreadMessage, arg.MessageID, arg.ThreadID, arg.UserID)
|
||||
var i Message
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ThreadID,
|
||||
&i.SenderUserID,
|
||||
&i.Body,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getMessageRecipientByIDForUser = `-- name: GetMessageRecipientByIDForUser :one
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline
|
||||
FROM users u
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE u.id = $2
|
||||
AND u.id <> $1
|
||||
AND u.is_active = TRUE
|
||||
AND (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM classrooms c
|
||||
JOIN classroom_students cs ON cs.classroom_id = c.id
|
||||
WHERE c.teacher_id = u.id
|
||||
AND cs.student_id = $1
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM classrooms c
|
||||
JOIN classroom_students cs ON cs.classroom_id = c.id
|
||||
WHERE c.teacher_id = $1
|
||||
AND cs.student_id = u.id
|
||||
)
|
||||
)
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetMessageRecipientByIDForUserParams struct {
|
||||
ID int64 `json:"id"`
|
||||
ID_2 int64 `json:"id_2"`
|
||||
}
|
||||
|
||||
type GetMessageRecipientByIDForUserRow struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetMessageRecipientByIDForUser(ctx context.Context, arg GetMessageRecipientByIDForUserParams) (GetMessageRecipientByIDForUserRow, error) {
|
||||
row := q.db.QueryRow(ctx, getMessageRecipientByIDForUser, arg.ID, arg.ID_2)
|
||||
var i GetMessageRecipientByIDForUserRow
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getMessageThreadForUser = `-- name: GetMessageThreadForUser :one
|
||||
SELECT
|
||||
t.id,
|
||||
t.subject,
|
||||
t.created_by_user_id,
|
||||
t.created_at,
|
||||
t.updated_at,
|
||||
participant.last_read_at,
|
||||
COALESCE((
|
||||
SELECT COUNT(*)::bigint
|
||||
FROM messages unread
|
||||
WHERE unread.thread_id = t.id
|
||||
AND unread.sender_user_id <> $2
|
||||
AND (participant.last_read_at IS NULL OR unread.created_at > participant.last_read_at)
|
||||
), 0)::bigint AS unread_count
|
||||
FROM message_threads t
|
||||
JOIN message_thread_participants participant ON participant.thread_id = t.id
|
||||
WHERE t.id = $1
|
||||
AND participant.user_id = $2
|
||||
AND participant.archived_at IS NULL
|
||||
`
|
||||
|
||||
type GetMessageThreadForUserParams struct {
|
||||
ID int64 `json:"id"`
|
||||
SenderUserID int64 `json:"sender_user_id"`
|
||||
}
|
||||
|
||||
type GetMessageThreadForUserRow struct {
|
||||
ID int64 `json:"id"`
|
||||
Subject string `json:"subject"`
|
||||
CreatedByUserID int64 `json:"created_by_user_id"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
LastReadAt pgtype.Timestamptz `json:"last_read_at"`
|
||||
UnreadCount int64 `json:"unread_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetMessageThreadForUser(ctx context.Context, arg GetMessageThreadForUserParams) (GetMessageThreadForUserRow, error) {
|
||||
row := q.db.QueryRow(ctx, getMessageThreadForUser, arg.ID, arg.SenderUserID)
|
||||
var i GetMessageThreadForUserRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Subject,
|
||||
&i.CreatedByUserID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastReadAt,
|
||||
&i.UnreadCount,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listMessageRecipientsForUser = `-- name: ListMessageRecipientsForUser :many
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline
|
||||
FROM users u
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE u.id <> $1
|
||||
AND u.is_active = TRUE
|
||||
AND (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM classrooms c
|
||||
JOIN classroom_students cs ON cs.classroom_id = c.id
|
||||
WHERE c.teacher_id = u.id
|
||||
AND cs.student_id = $1
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM classrooms c
|
||||
JOIN classroom_students cs ON cs.classroom_id = c.id
|
||||
WHERE c.teacher_id = $1
|
||||
AND cs.student_id = u.id
|
||||
)
|
||||
)
|
||||
ORDER BY COALESCE(NULLIF(p.preferred_name, ''), u.full_name) ASC, u.id ASC
|
||||
`
|
||||
|
||||
type ListMessageRecipientsForUserRow struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListMessageRecipientsForUser(ctx context.Context, id int64) ([]ListMessageRecipientsForUserRow, error) {
|
||||
rows, err := q.db.Query(ctx, listMessageRecipientsForUser, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListMessageRecipientsForUserRow{}
|
||||
for rows.Next() {
|
||||
var i ListMessageRecipientsForUserRow
|
||||
if err := rows.Scan(
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listMessageThreadParticipantsForUser = `-- name: ListMessageThreadParticipantsForUser :many
|
||||
SELECT
|
||||
mtp.thread_id,
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline,
|
||||
mtp.joined_at,
|
||||
mtp.last_read_at,
|
||||
mtp.archived_at
|
||||
FROM message_thread_participants mtp
|
||||
JOIN users u ON u.id = mtp.user_id
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE mtp.thread_id IN (
|
||||
SELECT participant.thread_id
|
||||
FROM message_thread_participants participant
|
||||
WHERE participant.user_id = $1
|
||||
AND participant.archived_at IS NULL
|
||||
)
|
||||
ORDER BY mtp.thread_id ASC, COALESCE(NULLIF(p.preferred_name, ''), u.full_name) ASC, u.id ASC
|
||||
`
|
||||
|
||||
type ListMessageThreadParticipantsForUserRow struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
JoinedAt pgtype.Timestamptz `json:"joined_at"`
|
||||
LastReadAt pgtype.Timestamptz `json:"last_read_at"`
|
||||
ArchivedAt pgtype.Timestamptz `json:"archived_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListMessageThreadParticipantsForUser(ctx context.Context, userID int64) ([]ListMessageThreadParticipantsForUserRow, error) {
|
||||
rows, err := q.db.Query(ctx, listMessageThreadParticipantsForUser, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListMessageThreadParticipantsForUserRow{}
|
||||
for rows.Next() {
|
||||
var i ListMessageThreadParticipantsForUserRow
|
||||
if err := rows.Scan(
|
||||
&i.ThreadID,
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
&i.JoinedAt,
|
||||
&i.LastReadAt,
|
||||
&i.ArchivedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listMessageThreadsForUser = `-- name: ListMessageThreadsForUser :many
|
||||
SELECT
|
||||
t.id AS thread_id,
|
||||
t.subject,
|
||||
t.created_by_user_id,
|
||||
t.created_at AS thread_created_at,
|
||||
t.updated_at AS thread_updated_at,
|
||||
COALESCE(last_message.id, 0)::bigint AS last_message_id,
|
||||
COALESCE(last_message.body, '') AS last_message_body,
|
||||
last_message.created_at AS last_message_created_at,
|
||||
COALESCE(last_message.sender_user_id, 0)::bigint AS last_message_sender_user_id,
|
||||
sender.full_name AS last_message_sender_full_name,
|
||||
sender_profile.preferred_name AS last_message_sender_preferred_name,
|
||||
sender_profile.profile_icon_url AS last_message_sender_profile_icon_url,
|
||||
COALESCE((
|
||||
SELECT COUNT(*)::bigint
|
||||
FROM messages unread
|
||||
WHERE unread.thread_id = t.id
|
||||
AND unread.sender_user_id <> $1
|
||||
AND (participant.last_read_at IS NULL OR unread.created_at > participant.last_read_at)
|
||||
), 0)::bigint AS unread_count
|
||||
FROM message_thread_participants participant
|
||||
JOIN message_threads t ON t.id = participant.thread_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT m.id, m.body, m.created_at, m.sender_user_id
|
||||
FROM messages m
|
||||
WHERE m.thread_id = t.id
|
||||
ORDER BY m.created_at DESC, m.id DESC
|
||||
LIMIT 1
|
||||
) AS last_message ON TRUE
|
||||
LEFT JOIN users sender ON sender.id = last_message.sender_user_id
|
||||
LEFT JOIN profiles sender_profile ON sender_profile.user_id = sender.id
|
||||
WHERE participant.user_id = $1
|
||||
AND participant.archived_at IS NULL
|
||||
ORDER BY COALESCE(last_message.created_at, t.updated_at) DESC, t.id DESC
|
||||
`
|
||||
|
||||
type ListMessageThreadsForUserRow struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
Subject string `json:"subject"`
|
||||
CreatedByUserID int64 `json:"created_by_user_id"`
|
||||
ThreadCreatedAt pgtype.Timestamptz `json:"thread_created_at"`
|
||||
ThreadUpdatedAt pgtype.Timestamptz `json:"thread_updated_at"`
|
||||
LastMessageID int64 `json:"last_message_id"`
|
||||
LastMessageBody string `json:"last_message_body"`
|
||||
LastMessageCreatedAt pgtype.Timestamptz `json:"last_message_created_at"`
|
||||
LastMessageSenderUserID int64 `json:"last_message_sender_user_id"`
|
||||
LastMessageSenderFullName pgtype.Text `json:"last_message_sender_full_name"`
|
||||
LastMessageSenderPreferredName pgtype.Text `json:"last_message_sender_preferred_name"`
|
||||
LastMessageSenderProfileIconUrl pgtype.Text `json:"last_message_sender_profile_icon_url"`
|
||||
UnreadCount int64 `json:"unread_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListMessageThreadsForUser(ctx context.Context, senderUserID int64) ([]ListMessageThreadsForUserRow, error) {
|
||||
rows, err := q.db.Query(ctx, listMessageThreadsForUser, senderUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListMessageThreadsForUserRow{}
|
||||
for rows.Next() {
|
||||
var i ListMessageThreadsForUserRow
|
||||
if err := rows.Scan(
|
||||
&i.ThreadID,
|
||||
&i.Subject,
|
||||
&i.CreatedByUserID,
|
||||
&i.ThreadCreatedAt,
|
||||
&i.ThreadUpdatedAt,
|
||||
&i.LastMessageID,
|
||||
&i.LastMessageBody,
|
||||
&i.LastMessageCreatedAt,
|
||||
&i.LastMessageSenderUserID,
|
||||
&i.LastMessageSenderFullName,
|
||||
&i.LastMessageSenderPreferredName,
|
||||
&i.LastMessageSenderProfileIconUrl,
|
||||
&i.UnreadCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listMessagesForThreadForUser = `-- name: ListMessagesForThreadForUser :many
|
||||
SELECT
|
||||
m.id,
|
||||
m.thread_id,
|
||||
m.sender_user_id,
|
||||
m.body,
|
||||
m.created_at,
|
||||
m.updated_at,
|
||||
sender.email AS sender_email,
|
||||
sender.role AS sender_role,
|
||||
sender.full_name AS sender_full_name,
|
||||
sender_profile.preferred_name AS sender_preferred_name,
|
||||
sender_profile.profile_icon_url AS sender_profile_icon_url,
|
||||
sender_profile.headline AS sender_headline
|
||||
FROM messages m
|
||||
JOIN message_thread_participants participant ON participant.thread_id = m.thread_id
|
||||
JOIN users sender ON sender.id = m.sender_user_id
|
||||
LEFT JOIN profiles sender_profile ON sender_profile.user_id = sender.id
|
||||
WHERE m.thread_id = $1
|
||||
AND participant.user_id = $2
|
||||
AND participant.archived_at IS NULL
|
||||
ORDER BY m.created_at ASC, m.id ASC
|
||||
`
|
||||
|
||||
type ListMessagesForThreadForUserParams struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
type ListMessagesForThreadForUserRow struct {
|
||||
ID int64 `json:"id"`
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
SenderUserID int64 `json:"sender_user_id"`
|
||||
Body string `json:"body"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
SenderEmail string `json:"sender_email"`
|
||||
SenderRole UserRole `json:"sender_role"`
|
||||
SenderFullName string `json:"sender_full_name"`
|
||||
SenderPreferredName pgtype.Text `json:"sender_preferred_name"`
|
||||
SenderProfileIconUrl pgtype.Text `json:"sender_profile_icon_url"`
|
||||
SenderHeadline pgtype.Text `json:"sender_headline"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListMessagesForThreadForUser(ctx context.Context, arg ListMessagesForThreadForUserParams) ([]ListMessagesForThreadForUserRow, error) {
|
||||
rows, err := q.db.Query(ctx, listMessagesForThreadForUser, arg.ThreadID, arg.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListMessagesForThreadForUserRow{}
|
||||
for rows.Next() {
|
||||
var i ListMessagesForThreadForUserRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ThreadID,
|
||||
&i.SenderUserID,
|
||||
&i.Body,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SenderEmail,
|
||||
&i.SenderRole,
|
||||
&i.SenderFullName,
|
||||
&i.SenderPreferredName,
|
||||
&i.SenderProfileIconUrl,
|
||||
&i.SenderHeadline,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listParticipantsForThreadForUser = `-- name: ListParticipantsForThreadForUser :many
|
||||
SELECT
|
||||
mtp.thread_id,
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline,
|
||||
mtp.joined_at,
|
||||
mtp.last_read_at,
|
||||
mtp.archived_at
|
||||
FROM message_thread_participants mtp
|
||||
JOIN users u ON u.id = mtp.user_id
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE mtp.thread_id = $1
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM message_thread_participants participant
|
||||
WHERE participant.thread_id = mtp.thread_id
|
||||
AND participant.user_id = $2
|
||||
AND participant.archived_at IS NULL
|
||||
)
|
||||
ORDER BY COALESCE(NULLIF(p.preferred_name, ''), u.full_name) ASC, u.id ASC
|
||||
`
|
||||
|
||||
type ListParticipantsForThreadForUserParams struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
type ListParticipantsForThreadForUserRow struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
JoinedAt pgtype.Timestamptz `json:"joined_at"`
|
||||
LastReadAt pgtype.Timestamptz `json:"last_read_at"`
|
||||
ArchivedAt pgtype.Timestamptz `json:"archived_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListParticipantsForThreadForUser(ctx context.Context, arg ListParticipantsForThreadForUserParams) ([]ListParticipantsForThreadForUserRow, error) {
|
||||
rows, err := q.db.Query(ctx, listParticipantsForThreadForUser, arg.ThreadID, arg.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListParticipantsForThreadForUserRow{}
|
||||
for rows.Next() {
|
||||
var i ListParticipantsForThreadForUserRow
|
||||
if err := rows.Scan(
|
||||
&i.ThreadID,
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
&i.JoinedAt,
|
||||
&i.LastReadAt,
|
||||
&i.ArchivedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const markMessageThreadRead = `-- name: MarkMessageThreadRead :one
|
||||
UPDATE message_thread_participants
|
||||
SET last_read_at = COALESCE((SELECT MAX(m.created_at) FROM messages m WHERE m.thread_id = $1), NOW())
|
||||
WHERE message_thread_participants.thread_id = $1
|
||||
AND message_thread_participants.user_id = $2
|
||||
RETURNING thread_id, user_id, joined_at, last_read_at, archived_at
|
||||
`
|
||||
|
||||
type MarkMessageThreadReadParams struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) MarkMessageThreadRead(ctx context.Context, arg MarkMessageThreadReadParams) (MessageThreadParticipant, error) {
|
||||
row := q.db.QueryRow(ctx, markMessageThreadRead, arg.ThreadID, arg.UserID)
|
||||
var i MessageThreadParticipant
|
||||
err := row.Scan(
|
||||
&i.ThreadID,
|
||||
&i.UserID,
|
||||
&i.JoinedAt,
|
||||
&i.LastReadAt,
|
||||
&i.ArchivedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const touchMessageThread = `-- name: TouchMessageThread :exec
|
||||
UPDATE message_threads
|
||||
SET updated_at = NOW()
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) TouchMessageThread(ctx context.Context, id int64) error {
|
||||
_, err := q.db.Exec(ctx, touchMessageThread, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateMessageThreadSubject = `-- name: UpdateMessageThreadSubject :one
|
||||
UPDATE message_threads
|
||||
SET subject = $1,
|
||||
updated_at = NOW()
|
||||
WHERE id = $2
|
||||
RETURNING id, created_by_user_id, subject, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateMessageThreadSubjectParams struct {
|
||||
Subject string `json:"subject"`
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateMessageThreadSubject(ctx context.Context, arg UpdateMessageThreadSubjectParams) (MessageThread, error) {
|
||||
row := q.db.QueryRow(ctx, updateMessageThreadSubject, arg.Subject, arg.ThreadID)
|
||||
var i MessageThread
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedByUserID,
|
||||
&i.Subject,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateThreadMessageBody = `-- name: UpdateThreadMessageBody :one
|
||||
UPDATE messages
|
||||
SET body = $1,
|
||||
updated_at = NOW()
|
||||
WHERE id = $2
|
||||
AND thread_id = $3
|
||||
AND sender_user_id = $4
|
||||
RETURNING id, thread_id, sender_user_id, body, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateThreadMessageBodyParams struct {
|
||||
Body string `json:"body"`
|
||||
MessageID int64 `json:"message_id"`
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateThreadMessageBody(ctx context.Context, arg UpdateThreadMessageBodyParams) (Message, error) {
|
||||
row := q.db.QueryRow(ctx, updateThreadMessageBody,
|
||||
arg.Body,
|
||||
arg.MessageID,
|
||||
arg.ThreadID,
|
||||
arg.UserID,
|
||||
)
|
||||
var i Message
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ThreadID,
|
||||
&i.SenderUserID,
|
||||
&i.Body,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
526
Backend/internal/sqlc/models.go
Normal file
526
Backend/internal/sqlc/models.go
Normal file
@@ -0,0 +1,526 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type AnswerStatus string
|
||||
|
||||
const (
|
||||
AnswerStatusNotStarted AnswerStatus = "not_started"
|
||||
AnswerStatusInProgress AnswerStatus = "in_progress"
|
||||
AnswerStatusSubmitted AnswerStatus = "submitted"
|
||||
AnswerStatusReviewed AnswerStatus = "reviewed"
|
||||
)
|
||||
|
||||
func (e *AnswerStatus) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = AnswerStatus(s)
|
||||
case string:
|
||||
*e = AnswerStatus(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for AnswerStatus: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullAnswerStatus struct {
|
||||
AnswerStatus AnswerStatus `json:"answer_status"`
|
||||
Valid bool `json:"valid"` // Valid is true if AnswerStatus is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullAnswerStatus) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.AnswerStatus, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.AnswerStatus.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullAnswerStatus) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.AnswerStatus), nil
|
||||
}
|
||||
|
||||
type AssignmentNextStepOutcome string
|
||||
|
||||
const (
|
||||
AssignmentNextStepOutcomeRedo AssignmentNextStepOutcome = "redo"
|
||||
AssignmentNextStepOutcomeAccept AssignmentNextStepOutcome = "accept"
|
||||
AssignmentNextStepOutcomeSupport AssignmentNextStepOutcome = "support"
|
||||
)
|
||||
|
||||
func (e *AssignmentNextStepOutcome) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = AssignmentNextStepOutcome(s)
|
||||
case string:
|
||||
*e = AssignmentNextStepOutcome(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for AssignmentNextStepOutcome: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullAssignmentNextStepOutcome struct {
|
||||
AssignmentNextStepOutcome AssignmentNextStepOutcome `json:"assignment_next_step_outcome"`
|
||||
Valid bool `json:"valid"` // Valid is true if AssignmentNextStepOutcome is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullAssignmentNextStepOutcome) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.AssignmentNextStepOutcome, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.AssignmentNextStepOutcome.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullAssignmentNextStepOutcome) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.AssignmentNextStepOutcome), nil
|
||||
}
|
||||
|
||||
type AssignmentPassStatus string
|
||||
|
||||
const (
|
||||
AssignmentPassStatusPending AssignmentPassStatus = "pending"
|
||||
AssignmentPassStatusPass AssignmentPassStatus = "pass"
|
||||
AssignmentPassStatusNoPass AssignmentPassStatus = "no_pass"
|
||||
)
|
||||
|
||||
func (e *AssignmentPassStatus) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = AssignmentPassStatus(s)
|
||||
case string:
|
||||
*e = AssignmentPassStatus(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for AssignmentPassStatus: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullAssignmentPassStatus struct {
|
||||
AssignmentPassStatus AssignmentPassStatus `json:"assignment_pass_status"`
|
||||
Valid bool `json:"valid"` // Valid is true if AssignmentPassStatus is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullAssignmentPassStatus) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.AssignmentPassStatus, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.AssignmentPassStatus.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullAssignmentPassStatus) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.AssignmentPassStatus), nil
|
||||
}
|
||||
|
||||
type AssignmentStatus string
|
||||
|
||||
const (
|
||||
AssignmentStatusDraft AssignmentStatus = "draft"
|
||||
AssignmentStatusAssigned AssignmentStatus = "assigned"
|
||||
AssignmentStatusClosed AssignmentStatus = "closed"
|
||||
)
|
||||
|
||||
func (e *AssignmentStatus) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = AssignmentStatus(s)
|
||||
case string:
|
||||
*e = AssignmentStatus(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for AssignmentStatus: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullAssignmentStatus struct {
|
||||
AssignmentStatus AssignmentStatus `json:"assignment_status"`
|
||||
Valid bool `json:"valid"` // Valid is true if AssignmentStatus is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullAssignmentStatus) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.AssignmentStatus, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.AssignmentStatus.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullAssignmentStatus) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.AssignmentStatus), nil
|
||||
}
|
||||
|
||||
type QuestionDifficulty string
|
||||
|
||||
const (
|
||||
QuestionDifficultyEasy QuestionDifficulty = "easy"
|
||||
QuestionDifficultyMedium QuestionDifficulty = "medium"
|
||||
QuestionDifficultyHard QuestionDifficulty = "hard"
|
||||
)
|
||||
|
||||
func (e *QuestionDifficulty) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = QuestionDifficulty(s)
|
||||
case string:
|
||||
*e = QuestionDifficulty(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for QuestionDifficulty: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullQuestionDifficulty struct {
|
||||
QuestionDifficulty QuestionDifficulty `json:"question_difficulty"`
|
||||
Valid bool `json:"valid"` // Valid is true if QuestionDifficulty is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullQuestionDifficulty) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.QuestionDifficulty, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.QuestionDifficulty.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullQuestionDifficulty) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.QuestionDifficulty), nil
|
||||
}
|
||||
|
||||
type QuestionStatus string
|
||||
|
||||
const (
|
||||
QuestionStatusDraft QuestionStatus = "draft"
|
||||
QuestionStatusPublished QuestionStatus = "published"
|
||||
QuestionStatusArchived QuestionStatus = "archived"
|
||||
)
|
||||
|
||||
func (e *QuestionStatus) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = QuestionStatus(s)
|
||||
case string:
|
||||
*e = QuestionStatus(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for QuestionStatus: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullQuestionStatus struct {
|
||||
QuestionStatus QuestionStatus `json:"question_status"`
|
||||
Valid bool `json:"valid"` // Valid is true if QuestionStatus is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullQuestionStatus) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.QuestionStatus, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.QuestionStatus.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullQuestionStatus) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.QuestionStatus), nil
|
||||
}
|
||||
|
||||
type QuestionTopic string
|
||||
|
||||
const (
|
||||
QuestionTopicPlaceValue QuestionTopic = "place_value"
|
||||
QuestionTopicArithmetic QuestionTopic = "arithmetic"
|
||||
QuestionTopicNegativeNumbers QuestionTopic = "negative_numbers"
|
||||
QuestionTopicBidmas QuestionTopic = "bidmas"
|
||||
QuestionTopicFractions QuestionTopic = "fractions"
|
||||
QuestionTopicAlgebra QuestionTopic = "algebra"
|
||||
QuestionTopicGeometry QuestionTopic = "geometry"
|
||||
QuestionTopicData QuestionTopic = "data"
|
||||
)
|
||||
|
||||
func (e *QuestionTopic) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = QuestionTopic(s)
|
||||
case string:
|
||||
*e = QuestionTopic(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for QuestionTopic: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullQuestionTopic struct {
|
||||
QuestionTopic QuestionTopic `json:"question_topic"`
|
||||
Valid bool `json:"valid"` // Valid is true if QuestionTopic is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullQuestionTopic) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.QuestionTopic, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.QuestionTopic.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullQuestionTopic) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.QuestionTopic), nil
|
||||
}
|
||||
|
||||
type UserRole string
|
||||
|
||||
const (
|
||||
UserRoleStudent UserRole = "student"
|
||||
UserRoleTeacher UserRole = "teacher"
|
||||
)
|
||||
|
||||
func (e *UserRole) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = UserRole(s)
|
||||
case string:
|
||||
*e = UserRole(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for UserRole: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullUserRole struct {
|
||||
UserRole UserRole `json:"user_role"`
|
||||
Valid bool `json:"valid"` // Valid is true if UserRole is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullUserRole) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.UserRole, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.UserRole.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullUserRole) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.UserRole), nil
|
||||
}
|
||||
|
||||
type Assignment struct {
|
||||
ID int64 `json:"id"`
|
||||
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"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
PassThreshold pgtype.Numeric `json:"pass_threshold"`
|
||||
}
|
||||
|
||||
type AssignmentAssignee 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"`
|
||||
}
|
||||
|
||||
type AssignmentQuestion struct {
|
||||
AssignmentID int64 `json:"assignment_id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
Position int32 `json:"position"`
|
||||
}
|
||||
|
||||
type AssignmentStudentQuestion 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"`
|
||||
}
|
||||
|
||||
type Classroom struct {
|
||||
ID int64 `json:"id"`
|
||||
TeacherID int64 `json:"teacher_id"`
|
||||
Name string `json:"name"`
|
||||
Code pgtype.Text `json:"code"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ClassroomStudent struct {
|
||||
ClassroomID int64 `json:"classroom_id"`
|
||||
StudentID int64 `json:"student_id"`
|
||||
JoinedAt pgtype.Timestamptz `json:"joined_at"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
ID int64 `json:"id"`
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
SenderUserID int64 `json:"sender_user_id"`
|
||||
Body string `json:"body"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type MessageThread struct {
|
||||
ID int64 `json:"id"`
|
||||
CreatedByUserID int64 `json:"created_by_user_id"`
|
||||
Subject string `json:"subject"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type MessageThreadParticipant struct {
|
||||
ThreadID int64 `json:"thread_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
JoinedAt pgtype.Timestamptz `json:"joined_at"`
|
||||
LastReadAt pgtype.Timestamptz `json:"last_read_at"`
|
||||
ArchivedAt pgtype.Timestamptz `json:"archived_at"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
Timezone pgtype.Text `json:"timezone"`
|
||||
Locale pgtype.Text `json:"locale"`
|
||||
GradeLevel pgtype.Text `json:"grade_level"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Question struct {
|
||||
ID int64 `json:"id"`
|
||||
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"`
|
||||
CorrectAnswer pgtype.Text `json:"correct_answer"`
|
||||
Topic NullQuestionTopic `json:"topic"`
|
||||
Difficulty NullQuestionDifficulty `json:"difficulty"`
|
||||
}
|
||||
|
||||
type QuestionTag struct {
|
||||
QuestionID int64 `json:"question_id"`
|
||||
TagID int64 `json:"tag_id"`
|
||||
}
|
||||
|
||||
type StudentAnswer struct {
|
||||
ID int64 `json:"id"`
|
||||
AssignmentID int64 `json:"assignment_id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
StudentID int64 `json:"student_id"`
|
||||
AnswerText pgtype.Text `json:"answer_text"`
|
||||
AiFeedback pgtype.Text `json:"ai_feedback"`
|
||||
TeacherFeedback pgtype.Text `json:"teacher_feedback"`
|
||||
Status AnswerStatus `json:"status"`
|
||||
SubmittedAt pgtype.Timestamptz `json:"submitted_at"`
|
||||
ReviewedAt pgtype.Timestamptz `json:"reviewed_at"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
SolveMode string `json:"solve_mode"`
|
||||
WorkingSteps pgtype.Text `json:"working_steps"`
|
||||
IsCorrect pgtype.Bool `json:"is_correct"`
|
||||
ReviewNeedsAttention bool `json:"review_needs_attention"`
|
||||
ReviewIssueReason pgtype.Text `json:"review_issue_reason"`
|
||||
ReviewCorrectnessScore pgtype.Numeric `json:"review_correctness_score"`
|
||||
ReviewUnderstandingScore pgtype.Numeric `json:"review_understanding_score"`
|
||||
ReviewQuestionScore pgtype.Numeric `json:"review_question_score"`
|
||||
ReviewConfidence pgtype.Numeric `json:"review_confidence"`
|
||||
ReviewTags []string `json:"review_tags"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
PasswordHash pgtype.Text `json:"password_hash"`
|
||||
Role UserRole `json:"role"`
|
||||
FullName string `json:"full_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
206
Backend/internal/sqlc/questions.sql.go
Normal file
206
Backend/internal/sqlc/questions.sql.go
Normal file
@@ -0,0 +1,206 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: questions.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const attachTagToQuestion = `-- name: AttachTagToQuestion :exec
|
||||
INSERT INTO question_tags (
|
||||
question_id,
|
||||
tag_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
)
|
||||
ON CONFLICT (question_id, tag_id) DO NOTHING
|
||||
`
|
||||
|
||||
type AttachTagToQuestionParams struct {
|
||||
QuestionID int64 `json:"question_id"`
|
||||
TagID int64 `json:"tag_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) AttachTagToQuestion(ctx context.Context, arg AttachTagToQuestionParams) error {
|
||||
_, err := q.db.Exec(ctx, attachTagToQuestion, arg.QuestionID, arg.TagID)
|
||||
return err
|
||||
}
|
||||
|
||||
const createQuestion = `-- name: CreateQuestion :one
|
||||
INSERT INTO questions (
|
||||
author_teacher_id,
|
||||
title,
|
||||
prompt,
|
||||
topic,
|
||||
subject,
|
||||
difficulty,
|
||||
source,
|
||||
status,
|
||||
correct_answer
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9
|
||||
)
|
||||
RETURNING id, author_teacher_id, title, prompt, subject, source, status, created_at, updated_at, correct_answer, topic, difficulty
|
||||
`
|
||||
|
||||
type CreateQuestionParams struct {
|
||||
AuthorTeacherID int64 `json:"author_teacher_id"`
|
||||
Title string `json:"title"`
|
||||
Prompt string `json:"prompt"`
|
||||
Topic NullQuestionTopic `json:"topic"`
|
||||
Subject pgtype.Text `json:"subject"`
|
||||
Difficulty NullQuestionDifficulty `json:"difficulty"`
|
||||
Source pgtype.Text `json:"source"`
|
||||
Status QuestionStatus `json:"status"`
|
||||
CorrectAnswer pgtype.Text `json:"correct_answer"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateQuestion(ctx context.Context, arg CreateQuestionParams) (Question, error) {
|
||||
row := q.db.QueryRow(ctx, createQuestion,
|
||||
arg.AuthorTeacherID,
|
||||
arg.Title,
|
||||
arg.Prompt,
|
||||
arg.Topic,
|
||||
arg.Subject,
|
||||
arg.Difficulty,
|
||||
arg.Source,
|
||||
arg.Status,
|
||||
arg.CorrectAnswer,
|
||||
)
|
||||
var i Question
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AuthorTeacherID,
|
||||
&i.Title,
|
||||
&i.Prompt,
|
||||
&i.Subject,
|
||||
&i.Source,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CorrectAnswer,
|
||||
&i.Topic,
|
||||
&i.Difficulty,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createTag = `-- name: CreateTag :one
|
||||
INSERT INTO tags (name)
|
||||
VALUES ($1)
|
||||
ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
|
||||
RETURNING id, name, created_at
|
||||
`
|
||||
|
||||
func (q *Queries) CreateTag(ctx context.Context, name string) (Tag, error) {
|
||||
row := q.db.QueryRow(ctx, createTag, name)
|
||||
var i Tag
|
||||
err := row.Scan(&i.ID, &i.Name, &i.CreatedAt)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getQuestionByID = `-- name: GetQuestionByID :one
|
||||
SELECT id, author_teacher_id, title, prompt, subject, source, status, created_at, updated_at, correct_answer, topic, difficulty
|
||||
FROM questions
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetQuestionByID(ctx context.Context, id int64) (Question, error) {
|
||||
row := q.db.QueryRow(ctx, getQuestionByID, id)
|
||||
var i Question
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AuthorTeacherID,
|
||||
&i.Title,
|
||||
&i.Prompt,
|
||||
&i.Subject,
|
||||
&i.Source,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CorrectAnswer,
|
||||
&i.Topic,
|
||||
&i.Difficulty,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listQuestionsByTeacher = `-- name: ListQuestionsByTeacher :many
|
||||
SELECT id, author_teacher_id, title, prompt, subject, source, status, created_at, updated_at, correct_answer, topic, difficulty
|
||||
FROM questions
|
||||
WHERE author_teacher_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListQuestionsByTeacher(ctx context.Context, authorTeacherID int64) ([]Question, error) {
|
||||
rows, err := q.db.Query(ctx, listQuestionsByTeacher, authorTeacherID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []Question{}
|
||||
for rows.Next() {
|
||||
var i Question
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.AuthorTeacherID,
|
||||
&i.Title,
|
||||
&i.Prompt,
|
||||
&i.Subject,
|
||||
&i.Source,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&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 listTags = `-- name: ListTags :many
|
||||
SELECT id, name, created_at
|
||||
FROM tags
|
||||
ORDER BY name ASC
|
||||
`
|
||||
|
||||
func (q *Queries) ListTags(ctx context.Context) ([]Tag, error) {
|
||||
rows, err := q.db.Query(ctx, listTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []Tag{}
|
||||
for rows.Next() {
|
||||
var i Tag
|
||||
if err := rows.Scan(&i.ID, &i.Name, &i.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
649
Backend/internal/sqlc/student_answers.sql.go
Normal file
649
Backend/internal/sqlc/student_answers.sql.go
Normal file
@@ -0,0 +1,649 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: student_answers.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const listAnswersForAssignment = `-- name: ListAnswersForAssignment :many
|
||||
SELECT id, assignment_id, question_id, student_id, answer_text, ai_feedback, teacher_feedback, status, submitted_at, reviewed_at, created_at, updated_at, solve_mode, working_steps, is_correct, review_needs_attention, review_issue_reason, review_correctness_score, review_understanding_score, review_question_score, review_confidence, review_tags
|
||||
FROM student_answers
|
||||
WHERE assignment_id = $1
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
|
||||
func (q *Queries) ListAnswersForAssignment(ctx context.Context, assignmentID int64) ([]StudentAnswer, error) {
|
||||
rows, err := q.db.Query(ctx, listAnswersForAssignment, assignmentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []StudentAnswer{}
|
||||
for rows.Next() {
|
||||
var i StudentAnswer
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.AssignmentID,
|
||||
&i.QuestionID,
|
||||
&i.StudentID,
|
||||
&i.AnswerText,
|
||||
&i.AiFeedback,
|
||||
&i.TeacherFeedback,
|
||||
&i.Status,
|
||||
&i.SubmittedAt,
|
||||
&i.ReviewedAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SolveMode,
|
||||
&i.WorkingSteps,
|
||||
&i.IsCorrect,
|
||||
&i.ReviewNeedsAttention,
|
||||
&i.ReviewIssueReason,
|
||||
&i.ReviewCorrectnessScore,
|
||||
&i.ReviewUnderstandingScore,
|
||||
&i.ReviewQuestionScore,
|
||||
&i.ReviewConfidence,
|
||||
&i.ReviewTags,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listAnswersForStudent = `-- name: ListAnswersForStudent :many
|
||||
SELECT id, assignment_id, question_id, student_id, answer_text, ai_feedback, teacher_feedback, status, submitted_at, reviewed_at, created_at, updated_at, solve_mode, working_steps, is_correct, review_needs_attention, review_issue_reason, review_correctness_score, review_understanding_score, review_question_score, review_confidence, review_tags
|
||||
FROM student_answers
|
||||
WHERE student_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListAnswersForStudent(ctx context.Context, studentID int64) ([]StudentAnswer, error) {
|
||||
rows, err := q.db.Query(ctx, listAnswersForStudent, studentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []StudentAnswer{}
|
||||
for rows.Next() {
|
||||
var i StudentAnswer
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.AssignmentID,
|
||||
&i.QuestionID,
|
||||
&i.StudentID,
|
||||
&i.AnswerText,
|
||||
&i.AiFeedback,
|
||||
&i.TeacherFeedback,
|
||||
&i.Status,
|
||||
&i.SubmittedAt,
|
||||
&i.ReviewedAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SolveMode,
|
||||
&i.WorkingSteps,
|
||||
&i.IsCorrect,
|
||||
&i.ReviewNeedsAttention,
|
||||
&i.ReviewIssueReason,
|
||||
&i.ReviewCorrectnessScore,
|
||||
&i.ReviewUnderstandingScore,
|
||||
&i.ReviewQuestionScore,
|
||||
&i.ReviewConfidence,
|
||||
&i.ReviewTags,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listQuestionDetailsForAssignmentStudent = `-- name: ListQuestionDetailsForAssignmentStudent :many
|
||||
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
|
||||
sq.assignment_id,
|
||||
sq.question_id,
|
||||
sq.position
|
||||
FROM student_question_set sq
|
||||
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)
|
||||
)
|
||||
SELECT
|
||||
aq.assignment_id,
|
||||
aq.question_id,
|
||||
aq.position,
|
||||
q.title,
|
||||
q.prompt,
|
||||
q.subject,
|
||||
q.source,
|
||||
COALESCE(
|
||||
ARRAY(
|
||||
SELECT t.name
|
||||
FROM question_tags qt
|
||||
JOIN tags t ON t.id = qt.tag_id
|
||||
WHERE qt.question_id = aq.question_id
|
||||
ORDER BY t.name ASC
|
||||
),
|
||||
ARRAY[]::TEXT[]
|
||||
)::TEXT[] AS question_tags,
|
||||
q.status AS question_status,
|
||||
q.correct_answer,
|
||||
aa.ai_feedback AS assignment_ai_feedback,
|
||||
aa.teacher_feedback AS assignment_teacher_feedback,
|
||||
review_summary.overall_score,
|
||||
a.pass_threshold,
|
||||
aa.next_step_outcome,
|
||||
aa.pass_status_override,
|
||||
COALESCE(
|
||||
aa.pass_status_override,
|
||||
CASE
|
||||
WHEN review_summary.overall_score IS NULL THEN 'pending'::assignment_pass_status
|
||||
WHEN review_summary.overall_score >= a.pass_threshold THEN 'pass'::assignment_pass_status
|
||||
ELSE 'no_pass'::assignment_pass_status
|
||||
END
|
||||
) AS pass_status,
|
||||
sa.id AS answer_id,
|
||||
sa.student_id,
|
||||
sa.answer_text,
|
||||
sa.solve_mode,
|
||||
sa.working_steps,
|
||||
sa.is_correct,
|
||||
sa.ai_feedback,
|
||||
sa.teacher_feedback,
|
||||
sa.status AS answer_status,
|
||||
sa.review_needs_attention,
|
||||
sa.review_issue_reason,
|
||||
sa.review_correctness_score,
|
||||
sa.review_understanding_score,
|
||||
sa.review_question_score,
|
||||
sa.review_confidence,
|
||||
sa.review_tags,
|
||||
sa.submitted_at,
|
||||
sa.reviewed_at,
|
||||
sa.created_at AS answer_created_at,
|
||||
sa.updated_at AS answer_updated_at
|
||||
FROM selected_questions aq
|
||||
JOIN assignments a ON a.id = aq.assignment_id
|
||||
JOIN questions q ON q.id = aq.question_id
|
||||
LEFT JOIN assignment_assignees aa
|
||||
ON aa.assignment_id = aq.assignment_id
|
||||
AND aa.student_id = $2
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT CASE
|
||||
WHEN COUNT(sa2.id) = 0 THEN NULL::NUMERIC(5,2)
|
||||
ELSE ROUND((AVG(
|
||||
CASE
|
||||
WHEN sa2.is_correct IS NULL THEN COALESCE(sa2.review_understanding_score, 0)::NUMERIC
|
||||
ELSE (
|
||||
((CASE WHEN sa2.is_correct THEN 1 ELSE 0 END)::NUMERIC) + COALESCE(sa2.review_understanding_score, 0)::NUMERIC
|
||||
) / 2
|
||||
END
|
||||
) * 10)::NUMERIC, 2)::NUMERIC(5,2)
|
||||
END AS overall_score
|
||||
FROM selected_questions aq2
|
||||
LEFT JOIN student_answers sa2
|
||||
ON sa2.assignment_id = aq2.assignment_id
|
||||
AND sa2.question_id = aq2.question_id
|
||||
AND sa2.student_id = $2
|
||||
WHERE aq2.assignment_id = aq.assignment_id
|
||||
) review_summary ON TRUE
|
||||
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
|
||||
ORDER BY aq.position ASC, aq.question_id ASC
|
||||
`
|
||||
|
||||
type ListQuestionDetailsForAssignmentStudentParams struct {
|
||||
AssignmentID int64 `json:"assignment_id"`
|
||||
StudentID int64 `json:"student_id"`
|
||||
}
|
||||
|
||||
type ListQuestionDetailsForAssignmentStudentRow struct {
|
||||
AssignmentID int64 `json:"assignment_id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
Position int32 `json:"position"`
|
||||
Title string `json:"title"`
|
||||
Prompt string `json:"prompt"`
|
||||
Subject pgtype.Text `json:"subject"`
|
||||
Source pgtype.Text `json:"source"`
|
||||
QuestionTags []string `json:"question_tags"`
|
||||
QuestionStatus QuestionStatus `json:"question_status"`
|
||||
CorrectAnswer pgtype.Text `json:"correct_answer"`
|
||||
AssignmentAiFeedback pgtype.Text `json:"assignment_ai_feedback"`
|
||||
AssignmentTeacherFeedback pgtype.Text `json:"assignment_teacher_feedback"`
|
||||
OverallScore pgtype.Numeric `json:"overall_score"`
|
||||
PassThreshold pgtype.Numeric `json:"pass_threshold"`
|
||||
NextStepOutcome NullAssignmentNextStepOutcome `json:"next_step_outcome"`
|
||||
PassStatusOverride NullAssignmentPassStatus `json:"pass_status_override"`
|
||||
PassStatus NullAssignmentPassStatus `json:"pass_status"`
|
||||
AnswerID pgtype.Int8 `json:"answer_id"`
|
||||
StudentID pgtype.Int8 `json:"student_id"`
|
||||
AnswerText pgtype.Text `json:"answer_text"`
|
||||
SolveMode pgtype.Text `json:"solve_mode"`
|
||||
WorkingSteps pgtype.Text `json:"working_steps"`
|
||||
IsCorrect pgtype.Bool `json:"is_correct"`
|
||||
AiFeedback pgtype.Text `json:"ai_feedback"`
|
||||
TeacherFeedback pgtype.Text `json:"teacher_feedback"`
|
||||
AnswerStatus NullAnswerStatus `json:"answer_status"`
|
||||
ReviewNeedsAttention pgtype.Bool `json:"review_needs_attention"`
|
||||
ReviewIssueReason pgtype.Text `json:"review_issue_reason"`
|
||||
ReviewCorrectnessScore pgtype.Numeric `json:"review_correctness_score"`
|
||||
ReviewUnderstandingScore pgtype.Numeric `json:"review_understanding_score"`
|
||||
ReviewQuestionScore pgtype.Numeric `json:"review_question_score"`
|
||||
ReviewConfidence pgtype.Numeric `json:"review_confidence"`
|
||||
ReviewTags []string `json:"review_tags"`
|
||||
SubmittedAt pgtype.Timestamptz `json:"submitted_at"`
|
||||
ReviewedAt pgtype.Timestamptz `json:"reviewed_at"`
|
||||
AnswerCreatedAt pgtype.Timestamptz `json:"answer_created_at"`
|
||||
AnswerUpdatedAt pgtype.Timestamptz `json:"answer_updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListQuestionDetailsForAssignmentStudent(ctx context.Context, arg ListQuestionDetailsForAssignmentStudentParams) ([]ListQuestionDetailsForAssignmentStudentRow, error) {
|
||||
rows, err := q.db.Query(ctx, listQuestionDetailsForAssignmentStudent, arg.AssignmentID, arg.StudentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListQuestionDetailsForAssignmentStudentRow{}
|
||||
for rows.Next() {
|
||||
var i ListQuestionDetailsForAssignmentStudentRow
|
||||
if err := rows.Scan(
|
||||
&i.AssignmentID,
|
||||
&i.QuestionID,
|
||||
&i.Position,
|
||||
&i.Title,
|
||||
&i.Prompt,
|
||||
&i.Subject,
|
||||
&i.Source,
|
||||
&i.QuestionTags,
|
||||
&i.QuestionStatus,
|
||||
&i.CorrectAnswer,
|
||||
&i.AssignmentAiFeedback,
|
||||
&i.AssignmentTeacherFeedback,
|
||||
&i.OverallScore,
|
||||
&i.PassThreshold,
|
||||
&i.NextStepOutcome,
|
||||
&i.PassStatusOverride,
|
||||
&i.PassStatus,
|
||||
&i.AnswerID,
|
||||
&i.StudentID,
|
||||
&i.AnswerText,
|
||||
&i.SolveMode,
|
||||
&i.WorkingSteps,
|
||||
&i.IsCorrect,
|
||||
&i.AiFeedback,
|
||||
&i.TeacherFeedback,
|
||||
&i.AnswerStatus,
|
||||
&i.ReviewNeedsAttention,
|
||||
&i.ReviewIssueReason,
|
||||
&i.ReviewCorrectnessScore,
|
||||
&i.ReviewUnderstandingScore,
|
||||
&i.ReviewQuestionScore,
|
||||
&i.ReviewConfidence,
|
||||
&i.ReviewTags,
|
||||
&i.SubmittedAt,
|
||||
&i.ReviewedAt,
|
||||
&i.AnswerCreatedAt,
|
||||
&i.AnswerUpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listStudentPlanningPerformance = `-- name: ListStudentPlanningPerformance :many
|
||||
SELECT
|
||||
sa.assignment_id,
|
||||
sa.question_id,
|
||||
q.topic,
|
||||
q.subject,
|
||||
q.difficulty,
|
||||
COALESCE(
|
||||
ARRAY(
|
||||
SELECT t.name
|
||||
FROM question_tags qt
|
||||
JOIN tags t ON t.id = qt.tag_id
|
||||
WHERE qt.question_id = sa.question_id
|
||||
ORDER BY t.name ASC
|
||||
),
|
||||
ARRAY[]::TEXT[]
|
||||
)::TEXT[] AS question_tags,
|
||||
sa.is_correct,
|
||||
sa.review_understanding_score,
|
||||
sa.review_needs_attention,
|
||||
sa.review_issue_reason,
|
||||
sa.status,
|
||||
sa.submitted_at,
|
||||
sa.reviewed_at,
|
||||
sa.updated_at
|
||||
FROM student_answers sa
|
||||
JOIN questions q ON q.id = sa.question_id
|
||||
WHERE sa.student_id = $1
|
||||
AND sa.status IN ('submitted'::answer_status, 'reviewed'::answer_status)
|
||||
ORDER BY COALESCE(sa.reviewed_at, sa.submitted_at, sa.updated_at) DESC, sa.id DESC
|
||||
`
|
||||
|
||||
type ListStudentPlanningPerformanceRow struct {
|
||||
AssignmentID int64 `json:"assignment_id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
Topic NullQuestionTopic `json:"topic"`
|
||||
Subject pgtype.Text `json:"subject"`
|
||||
Difficulty NullQuestionDifficulty `json:"difficulty"`
|
||||
QuestionTags []string `json:"question_tags"`
|
||||
IsCorrect pgtype.Bool `json:"is_correct"`
|
||||
ReviewUnderstandingScore pgtype.Numeric `json:"review_understanding_score"`
|
||||
ReviewNeedsAttention bool `json:"review_needs_attention"`
|
||||
ReviewIssueReason pgtype.Text `json:"review_issue_reason"`
|
||||
Status AnswerStatus `json:"status"`
|
||||
SubmittedAt pgtype.Timestamptz `json:"submitted_at"`
|
||||
ReviewedAt pgtype.Timestamptz `json:"reviewed_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListStudentPlanningPerformance(ctx context.Context, studentID int64) ([]ListStudentPlanningPerformanceRow, error) {
|
||||
rows, err := q.db.Query(ctx, listStudentPlanningPerformance, studentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListStudentPlanningPerformanceRow{}
|
||||
for rows.Next() {
|
||||
var i ListStudentPlanningPerformanceRow
|
||||
if err := rows.Scan(
|
||||
&i.AssignmentID,
|
||||
&i.QuestionID,
|
||||
&i.Topic,
|
||||
&i.Subject,
|
||||
&i.Difficulty,
|
||||
&i.QuestionTags,
|
||||
&i.IsCorrect,
|
||||
&i.ReviewUnderstandingScore,
|
||||
&i.ReviewNeedsAttention,
|
||||
&i.ReviewIssueReason,
|
||||
&i.Status,
|
||||
&i.SubmittedAt,
|
||||
&i.ReviewedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateAnswerAIReview = `-- name: UpdateAnswerAIReview :one
|
||||
UPDATE student_answers
|
||||
SET
|
||||
ai_feedback = $2,
|
||||
review_needs_attention = $3,
|
||||
review_issue_reason = $4,
|
||||
review_correctness_score = $5,
|
||||
review_understanding_score = $6,
|
||||
review_question_score = $7,
|
||||
review_confidence = $8,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, assignment_id, question_id, student_id, answer_text, ai_feedback, teacher_feedback, status, submitted_at, reviewed_at, created_at, updated_at, solve_mode, working_steps, is_correct, review_needs_attention, review_issue_reason, review_correctness_score, review_understanding_score, review_question_score, review_confidence, review_tags
|
||||
`
|
||||
|
||||
type UpdateAnswerAIReviewParams struct {
|
||||
ID int64 `json:"id"`
|
||||
AiFeedback pgtype.Text `json:"ai_feedback"`
|
||||
ReviewNeedsAttention bool `json:"review_needs_attention"`
|
||||
ReviewIssueReason pgtype.Text `json:"review_issue_reason"`
|
||||
ReviewCorrectnessScore pgtype.Numeric `json:"review_correctness_score"`
|
||||
ReviewUnderstandingScore pgtype.Numeric `json:"review_understanding_score"`
|
||||
ReviewQuestionScore pgtype.Numeric `json:"review_question_score"`
|
||||
ReviewConfidence pgtype.Numeric `json:"review_confidence"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateAnswerAIReview(ctx context.Context, arg UpdateAnswerAIReviewParams) (StudentAnswer, error) {
|
||||
row := q.db.QueryRow(ctx, updateAnswerAIReview,
|
||||
arg.ID,
|
||||
arg.AiFeedback,
|
||||
arg.ReviewNeedsAttention,
|
||||
arg.ReviewIssueReason,
|
||||
arg.ReviewCorrectnessScore,
|
||||
arg.ReviewUnderstandingScore,
|
||||
arg.ReviewQuestionScore,
|
||||
arg.ReviewConfidence,
|
||||
)
|
||||
var i StudentAnswer
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AssignmentID,
|
||||
&i.QuestionID,
|
||||
&i.StudentID,
|
||||
&i.AnswerText,
|
||||
&i.AiFeedback,
|
||||
&i.TeacherFeedback,
|
||||
&i.Status,
|
||||
&i.SubmittedAt,
|
||||
&i.ReviewedAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SolveMode,
|
||||
&i.WorkingSteps,
|
||||
&i.IsCorrect,
|
||||
&i.ReviewNeedsAttention,
|
||||
&i.ReviewIssueReason,
|
||||
&i.ReviewCorrectnessScore,
|
||||
&i.ReviewUnderstandingScore,
|
||||
&i.ReviewQuestionScore,
|
||||
&i.ReviewConfidence,
|
||||
&i.ReviewTags,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAnswerReview = `-- name: UpdateAnswerReview :one
|
||||
UPDATE student_answers
|
||||
SET
|
||||
status = $2,
|
||||
review_needs_attention = $3,
|
||||
review_issue_reason = $4,
|
||||
review_correctness_score = $5,
|
||||
review_understanding_score = $6,
|
||||
review_question_score = $7,
|
||||
review_confidence = $8,
|
||||
review_tags = $9,
|
||||
reviewed_at = CASE
|
||||
WHEN $2::answer_status = 'reviewed' THEN NOW()
|
||||
ELSE NULL
|
||||
END,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, assignment_id, question_id, student_id, answer_text, ai_feedback, teacher_feedback, status, submitted_at, reviewed_at, created_at, updated_at, solve_mode, working_steps, is_correct, review_needs_attention, review_issue_reason, review_correctness_score, review_understanding_score, review_question_score, review_confidence, review_tags
|
||||
`
|
||||
|
||||
type UpdateAnswerReviewParams struct {
|
||||
ID int64 `json:"id"`
|
||||
Status AnswerStatus `json:"status"`
|
||||
ReviewNeedsAttention bool `json:"review_needs_attention"`
|
||||
ReviewIssueReason pgtype.Text `json:"review_issue_reason"`
|
||||
ReviewCorrectnessScore pgtype.Numeric `json:"review_correctness_score"`
|
||||
ReviewUnderstandingScore pgtype.Numeric `json:"review_understanding_score"`
|
||||
ReviewQuestionScore pgtype.Numeric `json:"review_question_score"`
|
||||
ReviewConfidence pgtype.Numeric `json:"review_confidence"`
|
||||
ReviewTags []string `json:"review_tags"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateAnswerReview(ctx context.Context, arg UpdateAnswerReviewParams) (StudentAnswer, error) {
|
||||
row := q.db.QueryRow(ctx, updateAnswerReview,
|
||||
arg.ID,
|
||||
arg.Status,
|
||||
arg.ReviewNeedsAttention,
|
||||
arg.ReviewIssueReason,
|
||||
arg.ReviewCorrectnessScore,
|
||||
arg.ReviewUnderstandingScore,
|
||||
arg.ReviewQuestionScore,
|
||||
arg.ReviewConfidence,
|
||||
arg.ReviewTags,
|
||||
)
|
||||
var i StudentAnswer
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AssignmentID,
|
||||
&i.QuestionID,
|
||||
&i.StudentID,
|
||||
&i.AnswerText,
|
||||
&i.AiFeedback,
|
||||
&i.TeacherFeedback,
|
||||
&i.Status,
|
||||
&i.SubmittedAt,
|
||||
&i.ReviewedAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SolveMode,
|
||||
&i.WorkingSteps,
|
||||
&i.IsCorrect,
|
||||
&i.ReviewNeedsAttention,
|
||||
&i.ReviewIssueReason,
|
||||
&i.ReviewCorrectnessScore,
|
||||
&i.ReviewUnderstandingScore,
|
||||
&i.ReviewQuestionScore,
|
||||
&i.ReviewConfidence,
|
||||
&i.ReviewTags,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const upsertStudentAnswer = `-- name: UpsertStudentAnswer :one
|
||||
INSERT INTO student_answers (
|
||||
assignment_id,
|
||||
question_id,
|
||||
student_id,
|
||||
answer_text,
|
||||
solve_mode,
|
||||
working_steps,
|
||||
ai_feedback,
|
||||
teacher_feedback,
|
||||
status,
|
||||
submitted_at,
|
||||
reviewed_at,
|
||||
is_correct
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9,
|
||||
$10,
|
||||
$11,
|
||||
$12
|
||||
)
|
||||
ON CONFLICT (assignment_id, question_id, student_id) DO UPDATE
|
||||
SET
|
||||
answer_text = EXCLUDED.answer_text,
|
||||
solve_mode = EXCLUDED.solve_mode,
|
||||
working_steps = EXCLUDED.working_steps,
|
||||
ai_feedback = EXCLUDED.ai_feedback,
|
||||
teacher_feedback = EXCLUDED.teacher_feedback,
|
||||
status = EXCLUDED.status,
|
||||
submitted_at = EXCLUDED.submitted_at,
|
||||
reviewed_at = EXCLUDED.reviewed_at,
|
||||
is_correct = EXCLUDED.is_correct,
|
||||
updated_at = NOW()
|
||||
RETURNING id, assignment_id, question_id, student_id, answer_text, ai_feedback, teacher_feedback, status, submitted_at, reviewed_at, created_at, updated_at, solve_mode, working_steps, is_correct, review_needs_attention, review_issue_reason, review_correctness_score, review_understanding_score, review_question_score, review_confidence, review_tags
|
||||
`
|
||||
|
||||
type UpsertStudentAnswerParams struct {
|
||||
AssignmentID int64 `json:"assignment_id"`
|
||||
QuestionID int64 `json:"question_id"`
|
||||
StudentID int64 `json:"student_id"`
|
||||
AnswerText pgtype.Text `json:"answer_text"`
|
||||
SolveMode string `json:"solve_mode"`
|
||||
WorkingSteps pgtype.Text `json:"working_steps"`
|
||||
AiFeedback pgtype.Text `json:"ai_feedback"`
|
||||
TeacherFeedback pgtype.Text `json:"teacher_feedback"`
|
||||
Status AnswerStatus `json:"status"`
|
||||
SubmittedAt pgtype.Timestamptz `json:"submitted_at"`
|
||||
ReviewedAt pgtype.Timestamptz `json:"reviewed_at"`
|
||||
IsCorrect pgtype.Bool `json:"is_correct"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertStudentAnswer(ctx context.Context, arg UpsertStudentAnswerParams) (StudentAnswer, error) {
|
||||
row := q.db.QueryRow(ctx, upsertStudentAnswer,
|
||||
arg.AssignmentID,
|
||||
arg.QuestionID,
|
||||
arg.StudentID,
|
||||
arg.AnswerText,
|
||||
arg.SolveMode,
|
||||
arg.WorkingSteps,
|
||||
arg.AiFeedback,
|
||||
arg.TeacherFeedback,
|
||||
arg.Status,
|
||||
arg.SubmittedAt,
|
||||
arg.ReviewedAt,
|
||||
arg.IsCorrect,
|
||||
)
|
||||
var i StudentAnswer
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AssignmentID,
|
||||
&i.QuestionID,
|
||||
&i.StudentID,
|
||||
&i.AnswerText,
|
||||
&i.AiFeedback,
|
||||
&i.TeacherFeedback,
|
||||
&i.Status,
|
||||
&i.SubmittedAt,
|
||||
&i.ReviewedAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SolveMode,
|
||||
&i.WorkingSteps,
|
||||
&i.IsCorrect,
|
||||
&i.ReviewNeedsAttention,
|
||||
&i.ReviewIssueReason,
|
||||
&i.ReviewCorrectnessScore,
|
||||
&i.ReviewUnderstandingScore,
|
||||
&i.ReviewQuestionScore,
|
||||
&i.ReviewConfidence,
|
||||
&i.ReviewTags,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
577
Backend/internal/sqlc/users.sql.go
Normal file
577
Backend/internal/sqlc/users.sql.go
Normal file
@@ -0,0 +1,577 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: users.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createUser = `-- name: CreateUser :one
|
||||
INSERT INTO users (
|
||||
email,
|
||||
password_hash,
|
||||
role,
|
||||
full_name
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4
|
||||
)
|
||||
RETURNING id, email, password_hash, role, full_name, is_active, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
Email string `json:"email"`
|
||||
PasswordHash pgtype.Text `json:"password_hash"`
|
||||
Role UserRole `json:"role"`
|
||||
FullName string `json:"full_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, createUser,
|
||||
arg.Email,
|
||||
arg.PasswordHash,
|
||||
arg.Role,
|
||||
arg.FullName,
|
||||
)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.Role,
|
||||
&i.FullName,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getAuthUserByEmail = `-- name: GetAuthUserByEmail :one
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
u.is_active AS user_is_active,
|
||||
u.password_hash AS user_password_hash,
|
||||
u.created_at AS user_created_at,
|
||||
u.updated_at AS user_updated_at,
|
||||
p.user_id AS profile_user_id,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline,
|
||||
p.bio,
|
||||
p.timezone,
|
||||
p.locale,
|
||||
p.grade_level,
|
||||
p.learning_goal,
|
||||
p.created_at AS profile_created_at,
|
||||
p.updated_at AS profile_updated_at
|
||||
FROM users u
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE u.email = $1
|
||||
`
|
||||
|
||||
type GetAuthUserByEmailRow struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
UserIsActive bool `json:"user_is_active"`
|
||||
UserPasswordHash pgtype.Text `json:"user_password_hash"`
|
||||
UserCreatedAt pgtype.Timestamptz `json:"user_created_at"`
|
||||
UserUpdatedAt pgtype.Timestamptz `json:"user_updated_at"`
|
||||
ProfileUserID pgtype.Int8 `json:"profile_user_id"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
Timezone pgtype.Text `json:"timezone"`
|
||||
Locale pgtype.Text `json:"locale"`
|
||||
GradeLevel pgtype.Text `json:"grade_level"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
ProfileCreatedAt pgtype.Timestamptz `json:"profile_created_at"`
|
||||
ProfileUpdatedAt pgtype.Timestamptz `json:"profile_updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAuthUserByEmail(ctx context.Context, email string) (GetAuthUserByEmailRow, error) {
|
||||
row := q.db.QueryRow(ctx, getAuthUserByEmail, email)
|
||||
var i GetAuthUserByEmailRow
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.UserIsActive,
|
||||
&i.UserPasswordHash,
|
||||
&i.UserCreatedAt,
|
||||
&i.UserUpdatedAt,
|
||||
&i.ProfileUserID,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
&i.Bio,
|
||||
&i.Timezone,
|
||||
&i.Locale,
|
||||
&i.GradeLevel,
|
||||
&i.LearningGoal,
|
||||
&i.ProfileCreatedAt,
|
||||
&i.ProfileUpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getAuthUserByID = `-- name: GetAuthUserByID :one
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
u.is_active AS user_is_active,
|
||||
u.created_at AS user_created_at,
|
||||
u.updated_at AS user_updated_at,
|
||||
p.user_id AS profile_user_id,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline,
|
||||
p.bio,
|
||||
p.timezone,
|
||||
p.locale,
|
||||
p.grade_level,
|
||||
p.learning_goal,
|
||||
p.created_at AS profile_created_at,
|
||||
p.updated_at AS profile_updated_at
|
||||
FROM users u
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE u.id = $1
|
||||
`
|
||||
|
||||
type GetAuthUserByIDRow struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
UserIsActive bool `json:"user_is_active"`
|
||||
UserCreatedAt pgtype.Timestamptz `json:"user_created_at"`
|
||||
UserUpdatedAt pgtype.Timestamptz `json:"user_updated_at"`
|
||||
ProfileUserID pgtype.Int8 `json:"profile_user_id"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
Timezone pgtype.Text `json:"timezone"`
|
||||
Locale pgtype.Text `json:"locale"`
|
||||
GradeLevel pgtype.Text `json:"grade_level"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
ProfileCreatedAt pgtype.Timestamptz `json:"profile_created_at"`
|
||||
ProfileUpdatedAt pgtype.Timestamptz `json:"profile_updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAuthUserByID(ctx context.Context, id int64) (GetAuthUserByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, getAuthUserByID, id)
|
||||
var i GetAuthUserByIDRow
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.UserIsActive,
|
||||
&i.UserCreatedAt,
|
||||
&i.UserUpdatedAt,
|
||||
&i.ProfileUserID,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
&i.Bio,
|
||||
&i.Timezone,
|
||||
&i.Locale,
|
||||
&i.GradeLevel,
|
||||
&i.LearningGoal,
|
||||
&i.ProfileCreatedAt,
|
||||
&i.ProfileUpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT id, email, password_hash, role, full_name, is_active, created_at, updated_at
|
||||
FROM users
|
||||
WHERE email = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByEmail, email)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.Role,
|
||||
&i.FullName,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByID = `-- name: GetUserByID :one
|
||||
SELECT id, email, password_hash, role, full_name, is_active, created_at, updated_at
|
||||
FROM users
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByID, id)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.Role,
|
||||
&i.FullName,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserWithProfileByID = `-- name: GetUserWithProfileByID :one
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
u.is_active AS user_is_active,
|
||||
u.created_at AS user_created_at,
|
||||
u.updated_at AS user_updated_at,
|
||||
p.user_id AS profile_user_id,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline,
|
||||
p.bio,
|
||||
p.timezone,
|
||||
p.locale,
|
||||
p.grade_level,
|
||||
p.learning_goal,
|
||||
p.created_at AS profile_created_at,
|
||||
p.updated_at AS profile_updated_at
|
||||
FROM users u
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE u.id = $1
|
||||
`
|
||||
|
||||
type GetUserWithProfileByIDRow struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
UserIsActive bool `json:"user_is_active"`
|
||||
UserCreatedAt pgtype.Timestamptz `json:"user_created_at"`
|
||||
UserUpdatedAt pgtype.Timestamptz `json:"user_updated_at"`
|
||||
ProfileUserID pgtype.Int8 `json:"profile_user_id"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
Timezone pgtype.Text `json:"timezone"`
|
||||
Locale pgtype.Text `json:"locale"`
|
||||
GradeLevel pgtype.Text `json:"grade_level"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
ProfileCreatedAt pgtype.Timestamptz `json:"profile_created_at"`
|
||||
ProfileUpdatedAt pgtype.Timestamptz `json:"profile_updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserWithProfileByID(ctx context.Context, id int64) (GetUserWithProfileByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, getUserWithProfileByID, id)
|
||||
var i GetUserWithProfileByIDRow
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.UserIsActive,
|
||||
&i.UserCreatedAt,
|
||||
&i.UserUpdatedAt,
|
||||
&i.ProfileUserID,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
&i.Bio,
|
||||
&i.Timezone,
|
||||
&i.Locale,
|
||||
&i.GradeLevel,
|
||||
&i.LearningGoal,
|
||||
&i.ProfileCreatedAt,
|
||||
&i.ProfileUpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listUsersByRole = `-- name: ListUsersByRole :many
|
||||
SELECT id, email, password_hash, role, full_name, is_active, created_at, updated_at
|
||||
FROM users
|
||||
WHERE role = $1
|
||||
ORDER BY full_name ASC
|
||||
`
|
||||
|
||||
func (q *Queries) ListUsersByRole(ctx context.Context, role UserRole) ([]User, error) {
|
||||
rows, err := q.db.Query(ctx, listUsersByRole, role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []User{}
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.Role,
|
||||
&i.FullName,
|
||||
&i.IsActive,
|
||||
&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 listUsersWithProfileByRole = `-- name: ListUsersWithProfileByRole :many
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.email AS user_email,
|
||||
u.role AS user_role,
|
||||
u.full_name AS user_full_name,
|
||||
u.is_active AS user_is_active,
|
||||
u.created_at AS user_created_at,
|
||||
u.updated_at AS user_updated_at,
|
||||
p.user_id AS profile_user_id,
|
||||
p.preferred_name,
|
||||
p.profile_icon_url,
|
||||
p.headline,
|
||||
p.bio,
|
||||
p.timezone,
|
||||
p.locale,
|
||||
p.grade_level,
|
||||
p.learning_goal,
|
||||
p.created_at AS profile_created_at,
|
||||
p.updated_at AS profile_updated_at
|
||||
FROM users u
|
||||
LEFT JOIN profiles p ON p.user_id = u.id
|
||||
WHERE u.role = $1
|
||||
ORDER BY u.full_name ASC
|
||||
`
|
||||
|
||||
type ListUsersWithProfileByRoleRow struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserRole UserRole `json:"user_role"`
|
||||
UserFullName string `json:"user_full_name"`
|
||||
UserIsActive bool `json:"user_is_active"`
|
||||
UserCreatedAt pgtype.Timestamptz `json:"user_created_at"`
|
||||
UserUpdatedAt pgtype.Timestamptz `json:"user_updated_at"`
|
||||
ProfileUserID pgtype.Int8 `json:"profile_user_id"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
Timezone pgtype.Text `json:"timezone"`
|
||||
Locale pgtype.Text `json:"locale"`
|
||||
GradeLevel pgtype.Text `json:"grade_level"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
ProfileCreatedAt pgtype.Timestamptz `json:"profile_created_at"`
|
||||
ProfileUpdatedAt pgtype.Timestamptz `json:"profile_updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListUsersWithProfileByRole(ctx context.Context, role UserRole) ([]ListUsersWithProfileByRoleRow, error) {
|
||||
rows, err := q.db.Query(ctx, listUsersWithProfileByRole, role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListUsersWithProfileByRoleRow{}
|
||||
for rows.Next() {
|
||||
var i ListUsersWithProfileByRoleRow
|
||||
if err := rows.Scan(
|
||||
&i.UserID,
|
||||
&i.UserEmail,
|
||||
&i.UserRole,
|
||||
&i.UserFullName,
|
||||
&i.UserIsActive,
|
||||
&i.UserCreatedAt,
|
||||
&i.UserUpdatedAt,
|
||||
&i.ProfileUserID,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
&i.Bio,
|
||||
&i.Timezone,
|
||||
&i.Locale,
|
||||
&i.GradeLevel,
|
||||
&i.LearningGoal,
|
||||
&i.ProfileCreatedAt,
|
||||
&i.ProfileUpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateUserActiveStatus = `-- name: UpdateUserActiveStatus :one
|
||||
UPDATE users
|
||||
SET
|
||||
is_active = $2,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, email, password_hash, role, full_name, is_active, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateUserActiveStatusParams struct {
|
||||
ID int64 `json:"id"`
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserActiveStatus(ctx context.Context, arg UpdateUserActiveStatusParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, updateUserActiveStatus, arg.ID, arg.IsActive)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.Role,
|
||||
&i.FullName,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateUserFullName = `-- name: UpdateUserFullName :one
|
||||
UPDATE users
|
||||
SET
|
||||
full_name = $2,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, email, password_hash, role, full_name, is_active, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateUserFullNameParams struct {
|
||||
ID int64 `json:"id"`
|
||||
FullName string `json:"full_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserFullName(ctx context.Context, arg UpdateUserFullNameParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, updateUserFullName, arg.ID, arg.FullName)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.PasswordHash,
|
||||
&i.Role,
|
||||
&i.FullName,
|
||||
&i.IsActive,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const upsertUserProfile = `-- name: UpsertUserProfile :one
|
||||
INSERT INTO profiles (
|
||||
user_id,
|
||||
preferred_name,
|
||||
profile_icon_url,
|
||||
headline,
|
||||
bio,
|
||||
timezone,
|
||||
locale,
|
||||
grade_level,
|
||||
learning_goal
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9
|
||||
)
|
||||
ON CONFLICT (user_id) DO UPDATE
|
||||
SET
|
||||
preferred_name = EXCLUDED.preferred_name,
|
||||
profile_icon_url = EXCLUDED.profile_icon_url,
|
||||
headline = EXCLUDED.headline,
|
||||
bio = EXCLUDED.bio,
|
||||
timezone = EXCLUDED.timezone,
|
||||
locale = EXCLUDED.locale,
|
||||
grade_level = EXCLUDED.grade_level,
|
||||
learning_goal = EXCLUDED.learning_goal,
|
||||
updated_at = NOW()
|
||||
RETURNING user_id, preferred_name, profile_icon_url, headline, bio, timezone, locale, grade_level, learning_goal, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpsertUserProfileParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
PreferredName pgtype.Text `json:"preferred_name"`
|
||||
ProfileIconUrl pgtype.Text `json:"profile_icon_url"`
|
||||
Headline pgtype.Text `json:"headline"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
Timezone pgtype.Text `json:"timezone"`
|
||||
Locale pgtype.Text `json:"locale"`
|
||||
GradeLevel pgtype.Text `json:"grade_level"`
|
||||
LearningGoal pgtype.Text `json:"learning_goal"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertUserProfile(ctx context.Context, arg UpsertUserProfileParams) (Profile, error) {
|
||||
row := q.db.QueryRow(ctx, upsertUserProfile,
|
||||
arg.UserID,
|
||||
arg.PreferredName,
|
||||
arg.ProfileIconUrl,
|
||||
arg.Headline,
|
||||
arg.Bio,
|
||||
arg.Timezone,
|
||||
arg.Locale,
|
||||
arg.GradeLevel,
|
||||
arg.LearningGoal,
|
||||
)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.PreferredName,
|
||||
&i.ProfileIconUrl,
|
||||
&i.Headline,
|
||||
&i.Bio,
|
||||
&i.Timezone,
|
||||
&i.Locale,
|
||||
&i.GradeLevel,
|
||||
&i.LearningGoal,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
Reference in New Issue
Block a user