fix: replace example migration with full working schema sync SQL (correct enums, all tables, all columns, idempotent)
This commit is contained in:
@@ -1,7 +0,0 @@
|
|||||||
-- Example Migration File
|
|
||||||
-- You can write raw SQL here to fix your schema mismatches
|
|
||||||
-- For example, if you added a new column in models.py, you would add it here:
|
|
||||||
|
|
||||||
-- ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "trial_end" TIMESTAMP WITHOUT TIME ZONE;
|
|
||||||
|
|
||||||
-- Then run: ./migrate.sh migrations/001_example.sql
|
|
||||||
294
migrations/001_full_schema_sync.sql
Normal file
294
migrations/001_full_schema_sync.sql
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- Pragativeer Full Schema Sync Migration
|
||||||
|
-- File: migrations/001_full_schema_sync.sql
|
||||||
|
--
|
||||||
|
-- HOW TO RUN:
|
||||||
|
-- Windows: .\migrate.bat migrations\001_full_schema_sync.sql
|
||||||
|
-- Linux/Mac (server): ./migrate.sh migrations/001_full_schema_sync.sql
|
||||||
|
--
|
||||||
|
-- This script is 100% idempotent (safe to run multiple times).
|
||||||
|
-- Uses IF NOT EXISTS everywhere so already-applied changes are silently skipped.
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- ── ENUM TYPES ─────────────────────────────────────────────────────────────────
|
||||||
|
-- Note: All enum values in DB are UPPERCASE (e.g. FREE, PRO, AI_COACH, JEE_MAIN)
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE classenum AS ENUM ('ELEVENTH', 'TWELFTH', 'DROPPER');
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE examenum AS ENUM ('JEE_MAIN', 'JEE_ADVANCED', 'NEET', 'JEE_NEET');
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TYPE examenum ADD VALUE IF NOT EXISTS 'JEE_NEET';
|
||||||
|
EXCEPTION WHEN others THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE mediumenum AS ENUM ('ENGLISH', 'HINDI', 'BILINGUAL');
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE tierenum AS ENUM ('FREE', 'PRO', 'AI_COACH');
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE questiontypeenum AS ENUM ('MCQ_SINGLE', 'MCQ_MULTIPLE', 'NUMERICAL');
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE sourcetypeenum AS ENUM ('DPS', 'TEST', 'MICROTEST');
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE errortypeenum AS ENUM ('CONCEPTUAL', 'CALCULATION', 'READING', 'TIME_PRESSURE');
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: user ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "user" (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
email TEXT UNIQUE,
|
||||||
|
phone TEXT UNIQUE,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
class classenum NOT NULL,
|
||||||
|
target_exam examenum NOT NULL,
|
||||||
|
medium mediumenum NOT NULL,
|
||||||
|
school TEXT,
|
||||||
|
city TEXT NOT NULL,
|
||||||
|
state TEXT NOT NULL,
|
||||||
|
tier tierenum NOT NULL DEFAULT 'FREE',
|
||||||
|
trial_end TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
subscription_end TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
profile_image_url TEXT,
|
||||||
|
streak_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
longest_streak INTEGER NOT NULL DEFAULT 0,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
|
||||||
|
last_active TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
|
||||||
|
active_session_token TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "user"
|
||||||
|
ADD COLUMN IF NOT EXISTS trial_end TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
ADD COLUMN IF NOT EXISTS subscription_end TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
ADD COLUMN IF NOT EXISTS profile_image_url TEXT,
|
||||||
|
ADD COLUMN IF NOT EXISTS streak_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN IF NOT EXISTS longest_streak INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN IF NOT EXISTS active_session_token TEXT,
|
||||||
|
ADD COLUMN IF NOT EXISTS school TEXT;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: parent ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS parent (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
email TEXT NOT NULL,
|
||||||
|
phone TEXT NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
language TEXT NOT NULL DEFAULT 'English',
|
||||||
|
linked_students JSONB NOT NULL DEFAULT '[]',
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE parent
|
||||||
|
ADD COLUMN IF NOT EXISTS language TEXT NOT NULL DEFAULT 'English',
|
||||||
|
ADD COLUMN IF NOT EXISTS linked_students JSONB NOT NULL DEFAULT '[]';
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: question ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS question (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
subject TEXT NOT NULL,
|
||||||
|
topic TEXT NOT NULL,
|
||||||
|
subtopic TEXT NOT NULL,
|
||||||
|
difficulty TEXT NOT NULL,
|
||||||
|
type questiontypeenum NOT NULL,
|
||||||
|
question_text TEXT NOT NULL,
|
||||||
|
question_text_hi TEXT,
|
||||||
|
options JSONB,
|
||||||
|
options_hi JSONB,
|
||||||
|
correct_answer JSONB NOT NULL,
|
||||||
|
solution_text TEXT NOT NULL,
|
||||||
|
solution_text_hi TEXT,
|
||||||
|
language mediumenum NOT NULL,
|
||||||
|
year INTEGER,
|
||||||
|
exam examenum,
|
||||||
|
tags TEXT,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_by UUID NOT NULL,
|
||||||
|
verified_by UUID
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE question
|
||||||
|
ADD COLUMN IF NOT EXISTS question_text_hi TEXT,
|
||||||
|
ADD COLUMN IF NOT EXISTS options_hi JSONB,
|
||||||
|
ADD COLUMN IF NOT EXISTS solution_text_hi TEXT,
|
||||||
|
ADD COLUMN IF NOT EXISTS tags TEXT,
|
||||||
|
ADD COLUMN IF NOT EXISTS verified_by UUID;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: dps ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dps (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
date DATE NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
questions JSONB NOT NULL DEFAULT '[]',
|
||||||
|
difficulty TEXT NOT NULL,
|
||||||
|
time_limit INTEGER NOT NULL,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_by UUID NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: test ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS test (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
exam_pattern examenum NOT NULL,
|
||||||
|
sections JSONB NOT NULL,
|
||||||
|
total_time INTEGER NOT NULL,
|
||||||
|
difficulty TEXT NOT NULL,
|
||||||
|
scheduled_date TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
end_date TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_by UUID NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE test
|
||||||
|
ADD COLUMN IF NOT EXISTS end_date TIMESTAMP WITHOUT TIME ZONE;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: transaction ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS transaction (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES "user"(id),
|
||||||
|
amount DOUBLE PRECISION NOT NULL,
|
||||||
|
tier TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: coupon ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS coupon (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
code TEXT UNIQUE NOT NULL,
|
||||||
|
discount_percent DOUBLE PRECISION NOT NULL,
|
||||||
|
max_discount_amount DOUBLE PRECISION NOT NULL,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
expires_at TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: attempt ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS attempt (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES "user"(id),
|
||||||
|
source_type sourcetypeenum NOT NULL,
|
||||||
|
source_id UUID NOT NULL,
|
||||||
|
started_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||||
|
submitted_at TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
time_taken INTEGER,
|
||||||
|
answers JSONB,
|
||||||
|
score INTEGER NOT NULL DEFAULT 0,
|
||||||
|
total_questions INTEGER NOT NULL,
|
||||||
|
accuracy DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
||||||
|
status TEXT NOT NULL DEFAULT 'In_Progress',
|
||||||
|
is_retake BOOLEAN NOT NULL DEFAULT FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE attempt
|
||||||
|
ADD COLUMN IF NOT EXISTS is_retake BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: mistake ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS mistake (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES "user"(id),
|
||||||
|
question_id UUID NOT NULL REFERENCES question(id),
|
||||||
|
attempt_id UUID NOT NULL REFERENCES attempt(id),
|
||||||
|
error_type errortypeenum NOT NULL,
|
||||||
|
topic TEXT NOT NULL,
|
||||||
|
subtopic TEXT NOT NULL,
|
||||||
|
next_review TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||||
|
review_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
consecutive_correct INTEGER NOT NULL DEFAULT 0,
|
||||||
|
is_mastered BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE mistake
|
||||||
|
ADD COLUMN IF NOT EXISTS consecutive_correct INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN IF NOT EXISTS is_mastered BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: studyplan ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS studyplan (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES "user"(id),
|
||||||
|
date DATE NOT NULL,
|
||||||
|
tasks JSONB NOT NULL,
|
||||||
|
is_completed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
generated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
|
||||||
|
feedback TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE studyplan
|
||||||
|
ADD COLUMN IF NOT EXISTS feedback TEXT;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: subscription ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS subscription (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES "user"(id),
|
||||||
|
tier tierenum NOT NULL,
|
||||||
|
billing_cycle TEXT NOT NULL,
|
||||||
|
start_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||||
|
end_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||||
|
amount INTEGER NOT NULL,
|
||||||
|
payment_id TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
auto_renew BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
coupon_code TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE subscription
|
||||||
|
ADD COLUMN IF NOT EXISTS coupon_code TEXT,
|
||||||
|
ADD COLUMN IF NOT EXISTS auto_renew BOOLEAN NOT NULL DEFAULT TRUE;
|
||||||
|
|
||||||
|
|
||||||
|
-- ── TABLE: systemsettings ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS systemsettings (
|
||||||
|
id TEXT PRIMARY KEY DEFAULT 'global',
|
||||||
|
maintenance_mode BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
allow_new_registrations BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
enable_free_trial BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
trial_duration_days INTEGER NOT NULL DEFAULT 15,
|
||||||
|
trial_tier tierenum NOT NULL DEFAULT 'AI_COACH',
|
||||||
|
updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE systemsettings
|
||||||
|
ADD COLUMN IF NOT EXISTS enable_free_trial BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
ADD COLUMN IF NOT EXISTS trial_duration_days INTEGER NOT NULL DEFAULT 15,
|
||||||
|
ADD COLUMN IF NOT EXISTS trial_tier tierenum NOT NULL DEFAULT 'AI_COACH';
|
||||||
|
|
||||||
|
INSERT INTO systemsettings (id, maintenance_mode, allow_new_registrations, enable_free_trial, trial_duration_days, trial_tier, updated_at)
|
||||||
|
VALUES ('global', FALSE, TRUE, TRUE, 15, 'AI_COACH', now())
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
Reference in New Issue
Block a user