161 lines
5.9 KiB
PL/PgSQL
161 lines
5.9 KiB
PL/PgSQL
-- +goose Up
|
|
CREATE TYPE user_role AS ENUM ('student', 'teacher');
|
|
CREATE TYPE question_status AS ENUM ('draft', 'published', 'archived');
|
|
CREATE TYPE assignment_status AS ENUM ('draft', 'assigned', 'closed');
|
|
CREATE TYPE answer_status AS ENUM ('not_started', 'in_progress', 'submitted', 'reviewed');
|
|
|
|
CREATE TABLE users (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password_hash TEXT,
|
|
role user_role NOT NULL,
|
|
full_name VARCHAR(255) NOT NULL,
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE classrooms (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
teacher_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL,
|
|
code VARCHAR(50) UNIQUE,
|
|
description TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE classroom_students (
|
|
classroom_id BIGINT NOT NULL REFERENCES classrooms(id) ON DELETE CASCADE,
|
|
student_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (classroom_id, student_id)
|
|
);
|
|
|
|
CREATE TABLE questions (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
author_teacher_id BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
|
title VARCHAR(255) NOT NULL,
|
|
prompt TEXT NOT NULL,
|
|
subject VARCHAR(100),
|
|
source TEXT,
|
|
status question_status NOT NULL DEFAULT 'draft',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE tags (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE question_tags (
|
|
question_id BIGINT NOT NULL REFERENCES questions(id) ON DELETE CASCADE,
|
|
tag_id BIGINT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (question_id, tag_id)
|
|
);
|
|
|
|
CREATE TABLE assignments (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
classroom_id BIGINT NOT NULL REFERENCES classrooms(id) ON DELETE CASCADE,
|
|
teacher_id BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
|
title VARCHAR(255) NOT NULL,
|
|
instructions TEXT,
|
|
status assignment_status NOT NULL DEFAULT 'draft',
|
|
due_at TIMESTAMPTZ,
|
|
published_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE assignment_assignees (
|
|
assignment_id BIGINT NOT NULL REFERENCES assignments(id) ON DELETE CASCADE,
|
|
student_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (assignment_id, student_id)
|
|
);
|
|
|
|
CREATE TABLE assignment_questions (
|
|
assignment_id BIGINT NOT NULL REFERENCES assignments(id) ON DELETE CASCADE,
|
|
question_id BIGINT NOT NULL REFERENCES questions(id) ON DELETE RESTRICT,
|
|
position INTEGER NOT NULL,
|
|
PRIMARY KEY (assignment_id, question_id),
|
|
CONSTRAINT assignment_questions_assignment_position_key UNIQUE (assignment_id, position)
|
|
);
|
|
|
|
CREATE TABLE student_answers (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
assignment_id BIGINT NOT NULL REFERENCES assignments(id) ON DELETE CASCADE,
|
|
question_id BIGINT NOT NULL REFERENCES questions(id) ON DELETE RESTRICT,
|
|
student_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
answer_text TEXT,
|
|
ai_feedback TEXT,
|
|
teacher_feedback TEXT,
|
|
status answer_status NOT NULL DEFAULT 'not_started',
|
|
submitted_at TIMESTAMPTZ,
|
|
reviewed_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
CONSTRAINT student_answers_assignment_question_student_key UNIQUE (assignment_id, question_id, student_id)
|
|
);
|
|
|
|
CREATE INDEX idx_users_role ON users(role);
|
|
CREATE INDEX idx_classrooms_teacher_id ON classrooms(teacher_id);
|
|
CREATE INDEX idx_questions_author_teacher_id ON questions(author_teacher_id);
|
|
CREATE INDEX idx_questions_status ON questions(status);
|
|
CREATE INDEX idx_assignments_classroom_id ON assignments(classroom_id);
|
|
CREATE INDEX idx_assignments_teacher_id ON assignments(teacher_id);
|
|
CREATE INDEX idx_assignment_assignees_student_id ON assignment_assignees(student_id);
|
|
CREATE INDEX idx_student_answers_student_id ON student_answers(student_id);
|
|
CREATE INDEX idx_student_answers_assignment_id ON student_answers(assignment_id);
|
|
|
|
-- +goose StatementBegin
|
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
-- +goose StatementEnd
|
|
|
|
CREATE TRIGGER users_updated_at BEFORE UPDATE ON users
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
CREATE TRIGGER classrooms_updated_at BEFORE UPDATE ON classrooms
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
CREATE TRIGGER questions_updated_at BEFORE UPDATE ON questions
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
CREATE TRIGGER assignments_updated_at BEFORE UPDATE ON assignments
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
CREATE TRIGGER student_answers_updated_at BEFORE UPDATE ON student_answers
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
-- +goose Down
|
|
DROP TRIGGER IF EXISTS student_answers_updated_at ON student_answers;
|
|
DROP TRIGGER IF EXISTS assignments_updated_at ON assignments;
|
|
DROP TRIGGER IF EXISTS questions_updated_at ON questions;
|
|
DROP TRIGGER IF EXISTS classrooms_updated_at ON classrooms;
|
|
DROP TRIGGER IF EXISTS users_updated_at ON users;
|
|
DROP FUNCTION IF EXISTS update_updated_at();
|
|
|
|
DROP TABLE IF EXISTS student_answers;
|
|
DROP TABLE IF EXISTS assignment_questions;
|
|
DROP TABLE IF EXISTS assignment_assignees;
|
|
DROP TABLE IF EXISTS assignments;
|
|
DROP TABLE IF EXISTS question_tags;
|
|
DROP TABLE IF EXISTS tags;
|
|
DROP TABLE IF EXISTS questions;
|
|
DROP TABLE IF EXISTS classroom_students;
|
|
DROP TABLE IF EXISTS classrooms;
|
|
DROP TABLE IF EXISTS users;
|
|
|
|
DROP TYPE IF EXISTS answer_status;
|
|
DROP TYPE IF EXISTS assignment_status;
|
|
DROP TYPE IF EXISTS question_status;
|
|
DROP TYPE IF EXISTS user_role;
|