Datový Model (Supabase / PostgreSQL)
Tento dokument popisuje schéma DB na vyšší úrovni. Kanonický zdroj pravdy je supabase/migrations/20260422000001_init.sql — pokud něco kolize, věřit SQL souboru.
Aktuální stav (2026-05-11): jediná migrace, 15 tabulek, 7 enum typů. RLS enabled na všech tabulkách, policy v rolích (is_staff(), is_admin()), nikdy ne hardcoded auth.uid() v business logice.
Disciplíny (must)
- Relační model, žádný JSONB pro hlavní entity.
- RLS přes role helpery (
is_staff,is_admin), ne přes uživatelské ID. - M:N přes join tabulky (např.
slot_topics↔studentspřesstudent_slot_topics). - Schema připravené na Fázi 2+ (
day_overrides,taught_by,arrived_at/left_at,afterschoolclass_type) — UI bude doplněno až bude potřeba.
1. Enums (7)
| Enum | Hodnoty | Použití |
|---|---|---|
user_role |
pending, director, teacher, office, parent |
profiles.role |
class_type |
school, afterschool |
classes.class_type (afterschool = družina) |
student_status |
active, inactive |
students.status |
staff_role |
lead_teacher, co_teacher, assistant |
class_staff.role |
guardian_relationship |
mother, father, grandparent, guardian, other |
student_guardians.relationship |
attendance_status |
present, absent, late, excused |
attendance_records.status |
day_override_type |
excursion, project_day, school_holiday, other |
day_overrides.type |
2. Tabulky — přehled (15)
Identity & Auth
| Tabulka | Účel | Klíčové sloupce |
|---|---|---|
profiles |
Uživatelské účty (1:1 s auth.users) |
id (FK → auth.users), email, role (default pending) |
profiles.idje FK naauth.users(id). Při registraci přes Supabase Auth se profil vytvoří automaticky triggeremhandle_new_usersrole = 'pending'(čeká na schválení).
Referenční data
| Tabulka | Účel | Unique |
|---|---|---|
school_years |
Školní roky ("2025/2026") | Jen 1 active naráz (partial unique index) |
classes |
Třídy (Kentaur, Phenix, afterschool) | name |
subjects |
Předměty (Matematika, ČJ, Kosmická) | name |
Žáci a zákonní zástupci
| Tabulka | Účel | Vazby |
|---|---|---|
students |
Žáci | grade (ročník), status |
students_classes |
Zápis žáka do třídy per rok | M:N students ↔ classes × school_years |
guardians |
Zákonní zástupci | nullable user_id (link na auth.users až po registraci rodiče) |
student_guardians |
Zástupce ↔ žák | M:N s typem vztahu + is_primary flag |
Personál (učitelé)
| Tabulka | Účel | Vazby |
|---|---|---|
class_staff |
Učitel přidělen do třídy v ročníku | M:N profiles ↔ classes × school_years s rolí (lead_teacher / co_teacher / assistant) |
Rozvrh a třídnice
| Tabulka | Účel | Klíčové |
|---|---|---|
schedule_slots |
Týdenní opakující se rozvrh per třída | day_of_week 1–5, slot_order 1–10, default_teacher_id (slot-level ownership) |
classbook_entries |
Skutečný záznam hodiny per slot per datum | taught_by (zastupování — Fáze 2), attendance_done_at (kompletnost docházky) |
slot_topics |
Témata probraná v hodině (M:N s žáky) | grade_hint (rychlý "přiřaď ročníku X" shortcut) |
student_slot_topics |
Který žák pracoval na kterém tématu | Unique per (student_id, slot_topic_id) |
attendance_records |
Docházka per slot | arrived_at / left_at nullable (rezerva pro Fázi 2 MŠMT export) |
Speciální dny
| Tabulka | Účel | Klíčové |
|---|---|---|
day_overrides |
Mimořádné dny (výlet, projektový den, prázdniny) | class_id NULL = celoškolní; typ z day_override_type |
day_overrides.class_idmůže být NULL pro celoškolní akce. Unique constrainty (partial indexes) řeší kolize: 1 override per (datum, třída) nebo 1 celoškolní override per datum.
3. ER diagram
erDiagram
auth_users ||--|| profiles : "1:1"
profiles ||--o{ class_staff : "učí v"
profiles ||--o{ schedule_slots : "default teacher"
profiles ||--o{ classbook_entries : "taught_by"
profiles ||--o{ guardians : "user_id (nullable)"
school_years ||--o{ students_classes : "zápisy roku"
school_years ||--o{ class_staff : "personál roku"
school_years ||--o{ schedule_slots : "rozvrh roku"
classes ||--o{ students_classes : ""
classes ||--o{ class_staff : ""
classes ||--o{ schedule_slots : ""
classes ||--o{ day_overrides : ""
students ||--o{ students_classes : ""
students ||--o{ student_guardians : ""
students ||--o{ student_slot_topics : ""
students ||--o{ attendance_records : ""
guardians ||--o{ student_guardians : ""
subjects ||--o{ schedule_slots : ""
schedule_slots ||--o{ classbook_entries : ""
classbook_entries ||--o{ slot_topics : ""
classbook_entries ||--o{ attendance_records : ""
slot_topics ||--o{ student_slot_topics : ""
4. Triggers
| Trigger | Tabulka | Co dělá |
|---|---|---|
*_updated_at |
profiles, students, guardians, classbook_entries, attendance_records |
Auto-update updated_at při každém UPDATE |
on_auth_user_created |
auth.users (insert) |
Vytvoří profil s role='pending' |
5. Helper funkce (RLS)
is_staff() → boolean -- role IN ('teacher', 'director', 'office')
is_admin() → boolean -- role IN ('director', 'office')
Obě jsou security definer, stable, set search_path = public. Volají se v using / with check policies.
6. RLS pattern
Všechny tabulky mají RLS enabled. Policies se řídí třemi vzory:
| Vzor | Použito na | Pravidlo |
|---|---|---|
| Authenticated read | school_years, classes, subjects |
Každý přihlášený uživatel může číst |
| Staff read + admin write | students, students_classes, guardians, student_guardians, class_staff |
is_staff() čte vše, is_admin() zapisuje |
| Staff full + parent read of own children | attendance_records, schedule_slots, students (parent path) |
Personál plný přístup, rodiče čtou jen data svých dětí přes student_guardians ↔ guardians.user_id |
Self-access: profiles_self_update umožňuje uživateli editovat vlastní profil. guardians_self_read umožňuje rodiči číst svůj záznam zástupce.
Classbook write: classbook_entries, slot_topics, student_slot_topics, attendance_records (write) má každý člen personálu — v MVP žádné per-class omezení (učitelé v Sofii učí napříč třídami).
Detailní policies viz init.sql řádky 320–537. Tento dokument popisuje vzory, ne každou jednotlivou policy.
7. Co schéma neobsahuje (záměrně)
- Feed / events / posts — Fáze 3+. Reálná data pro rodiče jdou přes Sofii jako orchestrátora, ne přes vlastní entity.
- Tasks / PM modul — Fáze 2+. Plánováno jako organická náhrada přes BookStack tagy + Sofie.
- Omluvenky, vyzvedávání — Fáze 2+. Schema bude doplněno, až přijde UI.
- Audit log — TBD před přechodem na produkční data (GDPR gate v AGENTS.md).
8. Migrace
Aktuálně 1 migrace (20260422000001_init.sql). Předchozí 2 fix-up migrace byly squashnuty do init při bootstrap cleanupu 2026-05-11 (default_teacher_id, attendance_done_at). Squash je legitimní strategie, dokud nemáme produkční data — viz DEV-LOG.
Pro úplnou strukturu (všechny indexy, FK constraints, RLS policies) viz init.sql. Tento dokument je rychlá orientace — udržuje se přes /curate-docs.