Přeskočit obsah

Datový Model (Návrh pro Supabase / Postgres)

Tento dokument popisuje databázové schéma, které bude implementováno v Supabase.

1. Uživatelé a Role (Tabulka public.profiles)

Supabase používá tabulku auth.users pro přihlašování. My vytvoříme tabulku public.profiles, která bude propojena přes id.

  • profiles (rozšiřuje auth.users)

    • id: UUID (Primary Key, FK -> auth.users.id)
    • email: Text
    • full_name: Text
    • avatar_url: Text
    • roles: Array[Enum] ('admin', 'teacher', 'parent', 'student') - Uživatel může mít více rolí.
    • preferred_language: Enum ('cs', 'en') (default: 'cs') — jazyk UI a AI odpovědí.
    • summary_style: Enum ('narrative', 'bullets') (default: 'narrative') — styl AI souhrnu.
    • hubspot_contact_id: Text (pro synchronizaci s CRM)
    • is_identity_verified: Boolean (Fyzicky ověřeno školou)
    • identity_verified_at: Timestamp
    • identity_verified_by: UUID (FK -> profiles.id - kdo ověřil)
    • created_at: Timestamp
  • student_records (Citlivá data žáků - RLS: Pouze Admin/Ředitel)

    • id: UUID (PK, FK -> profiles.id)
    • birth_number: Text (Rodné číslo - Šifrováno?)
    • variable_symbol: Integer (Unikátní identifikátor pro platby/evidenci. Formát: YYNNN (např. 25011 pro 11. žáka v roce 2025).)
    • status: Enum ('active', 'graduated', 'transferred', 'did_not_start', 'interrupted')
    • enrollment_date: Date
    • end_date: Date
    • notes: Text (Interní poznámky)

2. Struktura Školy

  • school_years (Školní roky)

    • id: UUID
    • name: Text (např. "2025/2026")
    • is_active: Boolean
  • class_groups (Třídy/Triády)

    • id: UUID
    • name: Text (např. "Hvězdáři")
    • grade_level: Text (např. "1-3")
    • lead_teacher_id: UUID (FK -> profiles.id)
  • students_classes (Vazba Žák <-> Třída)

    • student_id: UUID (FK -> profiles.id)
    • class_id: UUID (FK -> class_groups.id)
    • school_year_id: UUID
  • student_guardians (Vazba Žák <-> Zákonný zástupce)

    • student_id: UUID (FK -> profiles.id)
    • guardian_id: UUID (FK -> profiles.id)
    • relationship_type: Enum ('mother', 'father', 'grandparent', 'other')
    • can_pickup: Boolean (Oprávněn vyzvedávat ze školy)
    • is_legal_guardian: Boolean (Zákonný zástupce - dostává oficiální zprávy)

(Tabulka pickup_persons byla odstraněna — v modelu "AI Podatelna" rodič sdělí vyzvedávající osobu v chatu, AI extrahuje údaje a uloží je přímo do attendance_records.pickup_person_name. Historická data jsou dohledatelná z communication_threads.)

3. Docházka a Omluvenky

  • attendance_records (Denní docházka)

    • id: UUID
    • student_id: UUID
    • date: Date
    • status: Enum ('present', 'absent', 'excused', 'late')
    • arrival_time: Time
    • departure_time: Time
    • pickup_person_name: Text (Kdo vyzvedl, pokud to nebyl defaultní rodič)
  • attendance_plans (Plán odchodů)

    • id: UUID
    • student_id: UUID
    • day_of_week: Int (0-6)
    • regular_departure_time: Time
    • type: Enum ('regular', 'exception')
    • valid_from: Date
    • valid_to: Date
  • excuse_notes (Omluvenky)

    • id: UUID
    • student_id: UUID
    • author_id: UUID (Rodič)
    • content: Text
    • from_date: Date
    • to_date: Date
    • reason: Text
    • status: Enum ('pending', 'approved', 'rejected')
  • activities (Kroužky a Akce)

    • id: UUID
    • name: Text (např. "Šachy", "Keramika")
    • description: Text
    • teacher_id: UUID (FK -> profiles.id - vedoucí kroužku)
    • schedule: Text (např. "Úterý 14:00-15:00")
    • capacity: Int
    • price: Decimal
    • is_active: Boolean
  • activity_enrollments (Přihlášky na kroužky)

    • id: UUID
    • activity_id: UUID (FK -> activities.id)
    • student_id: UUID (FK -> profiles.id)
    • status: Enum ('enrolled', 'waitlist', 'cancelled')
    • created_at: Timestamp
  • consents (Definice Souhlasů - GDPR, Výlety)

    • id: UUID
    • title: Text (např. "Souhlas s focením", "Souhlas s výletem na Šumavu")
    • content: Text (Markdown - právní text)
    • valid_until: Date (kdy vyprší)
    • is_mandatory: Boolean
    • category: Enum ('gdpr', 'trip', 'photo', 'health')
  • student_consents (Podpisy souhlasů)

    • id: UUID
    • consent_id: UUID (FK -> consents.id)
    • student_id: UUID (FK -> profiles.id)
    • signed_by: UUID (FK -> profiles.id - rodič)
    • signed_at: Timestamp
    • status: Enum ('granted', 'revoked', 'pending')
    • ip_address: Text (Audit)

4. Evidence Práce

  • subjects (Předměty)

    • id: UUID
    • name: Text
    • color: Text
  • competencies (Kompetence)

    • id: UUID
    • subject_id: UUID (FK -> subjects.id)
    • name: Text
    • description: Text
  • work_items (Práce/Důkazy)

    • id: UUID
    • student_id: UUID (FK -> profiles.id)
    • created_by: UUID (FK -> profiles.id or NULL for AI agent)
    • title: Text
    • description: Text (Markdown support)
    • content: JSONB (Strukturovaný obsah: textové bloky, seznam URL na fotky/soubory, audio poznámky)
    • source: Enum ('upload', 'email', 'api_agent', 'manual')
    • external_reference_id: Text (např. Message-ID emailu pro párování)
    • status: Enum ('draft', 'pending_approval', 'published', 'archived')
    • ai_metadata: JSONB (Analýza od Gemini: navržené kompetence, sentiment, klíčová slova)
    • teacher_feedback: Text
    • created_at: Timestamp
    • published_at: Timestamp
    • visibility: Enum ('private', 'teacher', 'parent', 'public')
  • work_item_tags (Štítky na pracích - Kompetence)

    • work_item_id: UUID
    • competence_id: UUID
    • confidence_score: Float (0.0 - 1.0, pokud navrženo AI)
    • is_approved: Boolean (potvrzeno učitelem)

5. Komunikace (AI Concierge)

  • communication_threads (Konverzace / Tickety)

    • id: UUID
    • student_id: UUID (FK -> profiles.id, nullable — obecné dotazy nemají žáka)
    • category: Enum ('obecne', 'omluvenka', 'zdravi', 'vyuka', 'incident', 'survey')
    • status: Enum ('open', 'ai_resolved', 'teacher_review', 'closed')
    • assigned_to: UUID (FK -> profiles.id, nullable — pokud řeší AI)
    • created_at: Timestamp
    • updated_at: Timestamp
  • messages (Zprávy ve vlákně)

    • id: UUID
    • thread_id: UUID (FK -> communication_threads.id)
    • sender_id: UUID (FK -> profiles.id, nebo NULL pro AI bota)
    • content: Text (Markdown)
    • is_internal: Boolean (pro poznámky učitelů, rodič nevidí)
    • ai_metadata: JSONB (analýza sentimentu, navržené odpovědi, survey response apod.)
    • created_at: Timestamp

6. Ankety a Sběr Dat

  • surveys (Ankety)

    • id: UUID
    • title: Text
    • description: Text
    • author_id: UUID (FK -> profiles.id)
    • is_active: Boolean
    • deadline: Timestamp
    • target_audience: Enum ('all_parents', 'class_group', 'specific_parents')
  • survey_questions (Otázky)

    • id: UUID
    • survey_id: UUID (FK -> surveys.id)
    • question_text: Text
    • question_type: Enum ('yes_no', 'single_choice', 'multiple_choice', 'text')
    • order: Int
  • survey_options (Možnosti odpovědí)

    • id: UUID
    • question_id: UUID (FK -> survey_questions.id)
    • option_text: Text
    • order: Int
  • survey_responses (Odpovědi rodičů)

    • id: UUID
    • survey_id: UUID
    • question_id: UUID
    • option_id: UUID (nullable)
    • respondent_id: UUID (FK -> profiles.id)
    • text_answer: Text (nullable)
    • created_at: Timestamp

7. Audit Log

  • audit_logs (Auditní stopa — právní pojistka)
    • id: UUID
    • user_id: UUID (FK -> profiles.id)
    • action: Text (např. 'pickup_confirmed', 'excuse_created', 'consent_granted')
    • entity_type: Text (např. 'attendance_records', 'excuse_notes')
    • entity_id: UUID
    • details: JSONB (snapshot dat v okamžiku akce)
    • ip_address: Text
    • user_agent: Text
    • created_at: Timestamp

8. Nastavení Notifikací

  • notification_preferences (Preference uživatele)
    • id: UUID
    • user_id: UUID (FK -> profiles.id)
    • channel: Enum ('push', 'email')
    • category: Text (např. 'attendance', 'evidence', 'surveys', 'daily_digest')
    • enabled: Boolean (default: true)
    • summary_frequency: Enum ('daily', 'weekly', 'monthly') (default: 'daily') — jak často posílat souhrn.

9. Knowledge Base & FAQ

  • knowledge_documents (Indexované dokumenty z Google Drive)

    • id: UUID
    • drive_file_id: Text (Google Drive File ID)
    • title: Text
    • trust_level: Enum ('verified', 'current', 'draft')
    • content_hash: Text (pro detekci změn při syncu)
    • last_synced_at: Timestamp
    • created_at: Timestamp
  • faq_entries (Ověřené FAQ — samoučící se z dotazů rodičů)

    • id: UUID
    • question: Text
    • answer: Text
    • source_thread_id: UUID (FK -> communication_threads.id — dotaz, který FAQ vytvořil)
    • approved_by: UUID (FK -> profiles.id — ředitel/zástupce)
    • approved_at: Timestamp
    • is_published: Boolean
    • category: Text (např. 'stravování', 'výlety', 'pravidla')
    • usage_count: Integer (kolikrát Sofie tuto FAQ citovala)
    • created_at: Timestamp

10. Bezpečnost (RLS Policies)

Row Level Security (RLS) policies zajistí, že data vidí jen oprávnění uživatelé.

  • profiles: Každý může číst své (nebo veřejné profily učitelů). Admin může vše.
  • student_records: Pouze admin/ředitel.
  • work_items: Učitel vidí svou třídu, rodič vidí své dítě.
  • communication_threads: Rodič vidí jen své konverzace. Učitel vidí přiřazené tickety.
  • attendance_records: Rodič vidí své dítě. Učitel vidí svou třídu.
  • faq_entries: Publikované (is_published: true) vidí všichni.

[!NOTE] Detailní RLS policies budou implementovány v migraci 0000_init_schema.sql.