first commit
This commit is contained in:
129
postgres/init.sql
Normal file
129
postgres/init.sql
Normal file
@@ -0,0 +1,129 @@
|
||||
-- ================================================
|
||||
-- LEERDOELEN TRACKER - DATABASE SCHEMA
|
||||
-- ================================================
|
||||
|
||||
-- Scholengroep scholen
|
||||
CREATE TABLE schools (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
slug VARCHAR(100) NOT NULL UNIQUE,
|
||||
-- Eén of meerdere e-maildomeinen gekoppeld aan deze school
|
||||
-- bv. '{"dekrekel.be", "sintjan.gent.be"}'
|
||||
email_domains TEXT[] NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Gebruikers
|
||||
-- Rollen:
|
||||
-- superadmin → ontwikkelaar/beheerder van het platform
|
||||
-- scholengroep_ict → maakt scholen aan, wijst directeurs en school_ict toe
|
||||
-- school_ict → beheert klassen en leerkrachten van één school
|
||||
-- director → leest overzicht van zijn school, geen beheer
|
||||
-- teacher → vult leerdoelen in
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255), -- enkel voor superadmin fallback
|
||||
first_name VARCHAR(100),
|
||||
last_name VARCHAR(100),
|
||||
role VARCHAR(20) NOT NULL DEFAULT 'teacher'
|
||||
CHECK (role IN ('superadmin', 'scholengroep_ict', 'school_ict', 'director', 'teacher')),
|
||||
school_id INTEGER REFERENCES schools(id) ON DELETE SET NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
last_login TIMESTAMP,
|
||||
-- Entra / OAuth2
|
||||
oauth_provider VARCHAR(20), -- 'microsoft' | NULL
|
||||
oauth_id VARCHAR(255), -- Entra object ID (oid claim)
|
||||
entra_tenant_id VARCHAR(255) -- tenant van de gebruiker
|
||||
);
|
||||
|
||||
CREATE INDEX idx_users_school ON users(school_id);
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
|
||||
-- School jaar (om data per jaar bij te houden)
|
||||
CREATE TABLE school_years (
|
||||
id SERIAL PRIMARY KEY,
|
||||
school_id INTEGER NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
|
||||
label VARCHAR(20) NOT NULL, -- bv. "2024-2025"
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
UNIQUE(school_id, label)
|
||||
);
|
||||
|
||||
-- Klassen per school per jaar
|
||||
CREATE TABLE classes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
school_id INTEGER NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
|
||||
school_year_id INTEGER NOT NULL REFERENCES school_years(id) ON DELETE CASCADE,
|
||||
name VARCHAR(50) NOT NULL, -- bv. "3A", "4B"
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
UNIQUE(school_id, school_year_id, name)
|
||||
);
|
||||
|
||||
-- Koppeling leerkracht aan klas (een leerkracht kan meerdere klassen hebben)
|
||||
CREATE TABLE teacher_classes (
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
class_id INTEGER NOT NULL REFERENCES classes(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (user_id, class_id)
|
||||
);
|
||||
|
||||
-- Beoordelingen van leerdoelen
|
||||
CREATE TABLE assessments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
school_id INTEGER NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
|
||||
school_year_id INTEGER NOT NULL REFERENCES school_years(id) ON DELETE CASCADE,
|
||||
vak_id VARCHAR(50) NOT NULL, -- bv. "wiskunde", "nederlands"
|
||||
goal_id VARCHAR(50) NOT NULL, -- GO! nummer, bv. "WIS.L4.01"
|
||||
status VARCHAR(10) NOT NULL
|
||||
CHECK (status IN ('groen', 'oranje', 'roze')),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
UNIQUE(user_id, school_year_id, vak_id, goal_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_assessments_school_year ON assessments(school_id, school_year_id);
|
||||
CREATE INDEX idx_assessments_user ON assessments(user_id);
|
||||
CREATE INDEX idx_assessments_vak ON assessments(vak_id);
|
||||
|
||||
-- Audit log (wie heeft wat gewijzigd)
|
||||
CREATE TABLE audit_log (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
details JSONB,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ================================================
|
||||
-- SEED DATA - standaard superadmin
|
||||
-- Wachtwoord wordt gezet via de setup route
|
||||
-- ================================================
|
||||
INSERT INTO schools (name, slug) VALUES ('Demo School', 'demo-school');
|
||||
|
||||
INSERT INTO users (email, role, first_name, last_name, school_id)
|
||||
VALUES ('admin@leerdoelen.local', 'superadmin', 'Super', 'Admin', 1);
|
||||
|
||||
INSERT INTO school_years (school_id, label, is_active)
|
||||
VALUES (1, '2024-2025', TRUE);
|
||||
|
||||
|
||||
-- ── Migratie: globale schooljaren (uitvoeren op bestaande installaties) ───────
|
||||
-- Dit blok is idempotent (IF NOT EXISTS / DO UPDATE) dus veilig om opnieuw te draaien.
|
||||
|
||||
-- 1. school_id nullable maken (was NOT NULL)
|
||||
ALTER TABLE school_years ALTER COLUMN school_id DROP NOT NULL;
|
||||
|
||||
-- 2. Unieke constraint op label zodat elk jaar maar één keer bestaat
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'school_years_label_key'
|
||||
) THEN
|
||||
ALTER TABLE school_years ADD CONSTRAINT school_years_label_key UNIQUE (label);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 3. Bestaande jaren zonder school_id=NULL (indien ze al bestaan) behouden
|
||||
-- Bestaande per-school jaren omzetten naar globale jaren:
|
||||
UPDATE school_years SET school_id = NULL WHERE school_id IS NOT NULL;
|
||||
34
postgres/migrate_v3_global_years.sql
Normal file
34
postgres/migrate_v3_global_years.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- ══════════════════════════════════════════════════════════════════════════════
|
||||
-- MIGRATIE v3: Globale schooljaren
|
||||
-- Uitvoeren op bestaande installaties die al draaien.
|
||||
-- Commando: docker exec -i leerdoelen_db psql -U leerdoelen leerdoelen < migrate_v3_global_years.sql
|
||||
-- ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. school_id nullable maken (was NOT NULL)
|
||||
ALTER TABLE school_years ALTER COLUMN school_id DROP NOT NULL;
|
||||
|
||||
-- 2. Unieke constraint op label (elk schooljaar bestaat maar één keer)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'school_years_label_key'
|
||||
) THEN
|
||||
ALTER TABLE school_years ADD CONSTRAINT school_years_label_key UNIQUE (label);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 3. Bestaande per-school jaren omzetten naar globale jaren
|
||||
-- (dubbele labels samenvoegen: bewaar het actieve, verwijder de rest)
|
||||
DELETE FROM school_years sy1
|
||||
USING school_years sy2
|
||||
WHERE sy1.label = sy2.label
|
||||
AND sy1.id > sy2.id;
|
||||
|
||||
UPDATE school_years SET school_id = NULL;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- Controleer resultaat:
|
||||
SELECT id, label, is_active, school_id FROM school_years ORDER BY label DESC;
|
||||
58
postgres/migrate_v4_classes_auditlog.sql
Normal file
58
postgres/migrate_v4_classes_auditlog.sql
Normal file
@@ -0,0 +1,58 @@
|
||||
-- ══════════════════════════════════════════════════════════════════════════════
|
||||
-- MIGRATIE v4: Klassen zonder school_year_id + Auditlog tabel
|
||||
-- Uitvoeren op bestaande installaties:
|
||||
-- docker exec -i leerdoelen_db psql -U leerdoelen leerdoelen < postgres/migrate_v4_classes_auditlog.sql
|
||||
-- ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. Verwijder school_year_id van classes (klassen zijn nu schooljaar-onafhankelijk)
|
||||
DO $$ BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name='classes' AND column_name='school_year_id'
|
||||
) THEN
|
||||
ALTER TABLE classes DROP CONSTRAINT IF EXISTS classes_school_year_id_fkey;
|
||||
ALTER TABLE classes DROP COLUMN school_year_id;
|
||||
RAISE NOTICE 'school_year_id verwijderd van classes';
|
||||
ELSE
|
||||
RAISE NOTICE 'school_year_id bestond al niet — niets te doen';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 2. Unieke constraint op (school_id, name) voor klassen
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'uq_class_school_name'
|
||||
) THEN
|
||||
ALTER TABLE classes ADD CONSTRAINT uq_class_school_name UNIQUE (school_id, name);
|
||||
RAISE NOTICE 'Unique constraint uq_class_school_name toegevoegd';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 3. Auditlog tabel aanmaken
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
timestamp TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
school_id INTEGER REFERENCES schools(id) ON DELETE SET NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
category VARCHAR(20) NOT NULL,
|
||||
target_type VARCHAR(50),
|
||||
target_id VARCHAR(100),
|
||||
detail TEXT,
|
||||
ip_address VARCHAR(45)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_audit_logs_timestamp ON audit_logs(timestamp);
|
||||
CREATE INDEX IF NOT EXISTS ix_audit_logs_action ON audit_logs(action);
|
||||
CREATE INDEX IF NOT EXISTS ix_audit_logs_category ON audit_logs(category);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- Controleer resultaat
|
||||
SELECT 'classes kolommen:' AS info;
|
||||
SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'classes' ORDER BY ordinal_position;
|
||||
|
||||
SELECT 'audit_logs tabel:' AS info;
|
||||
SELECT COUNT(*) AS entries FROM audit_logs;
|
||||
Reference in New Issue
Block a user