shinkan-jinkendo/.claude/docs/technical/DATABASE_SCHEMA.md
Lars b6de1f15ea
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 25s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 23s
feat(media): implement centralized media archive and inline media linking
- Introduced a centralized media archive (`/media`) with lifecycle management, including soft delete and recovery options.
- Enhanced media upload functionality to support multiple files and automatic type inference.
- Updated documentation to reflect the new media architecture and inline media linking specifications.
- Version bump to 0.8.59 to accommodate changes in media handling and database schema.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-08 10:56:43 +02:00

18 KiB
Raw Blame History

Shinkan Jinkendo - Datenbank-Schema (Technisch)

Version: 0.5.3
Stand: 2026-05-07
Hinweis: Produktiver Deploy sollte mindestens bis Migration 037 (RahmenSlotBlueprints) und für Medien-Archiv bis 045+ (media_assets, …) geführt sein — Details siehe backend/version.py (DB_SCHEMA_VERSION) und MEDIA_ASSETS_AND_ARCHIVE_SPEC.md.


Übersicht

Dieses Dokument beschreibt die technische Datenbankstruktur von Shinkan Jinkendo.

Zuständig für fachliche Modellierung: siehe DOMAIN_MODEL.md


Migrations-Historie

Nr. Datum Beschreibung Status
001 2026-04-20 Initial Schema (Auth, Profiles) Deployed
002 2026-04-20 Clubs, Divisions, Groups Deployed
003 2026-04-20 Skills, Methods Deployed
004 2026-04-21 Training Types Deployed
005 2026-04-21 Exercises (Basis) Deployed
006 2026-04-21 Training Planning Deployed
007 2026-04-22 Exercise Catalogs (idempotent) Deployed
008 2026-04-23 M:N Exercise Relations + Hierarchical Catalogs Deployed
009 2026-04-23 Target Groups M:N Refactoring (BREAKING) Deployed
010 2026-04-23 Rename training_styles to style_directions Deployed
011 2026-04-23 Create Training Types Deployed
012 2026-04-23 Exercise Training Characters + Trainer Contexts Deployed
013 2026-04-23 Training Types Focus Area Deployed
014 2026-04-24 Variant Progression Search Legacy Deployed
016 2026-04-24 Saved Searches Deployed
017 2026-04-24 Exercise Blocks Deployed
018 2026-04-24 Wiki Import Tracking Deployed
019 2026-04-24 Exercises Optional Fields (goal/execution nullable) Deployed
020 2026-04-27 Exercise Skills UNIQUE Constraint Deployed
021 2026-04-27 Import Skills from Matrix (DEPRECATED) ⚠️ Faulty
022 2026-04-27 Skills Schema Complete (BREAKING) Deployed
023 2026-04-27 Skills Complete Import (69 Skills) Deployed
024031 versch. Reifegradmodelle, Medien, Planvorlagen/Sektionen u.a. — siehe backend/migrations/ je Umgebung
032 2026-04-30 Progressionsgraph Übung→Übung: exercise_progression_graphs, exercise_progression_edges
033 2026-04-30 exercise_progression_edges.notes
034 2026-04-30 Kanten-Endpunkte optional exercise_variants; UNIQUE/CHECK
035 2026-05-05 Rahmenprogramm: training_framework_programs (+ Ziele, Slots, früher training_framework_slot_exercises); training_plan_templates.visibility (Backfill club) — siehe TRAINING_FRAMEWORK_SPEC.md
036 2026-05-05 Rahmen nur Bibliothek: Kopf mit focus_area_id, style_direction_id, M:N Trainingsarten/Zielgruppen; Entfall plan_mode, group_id; Slottraining_unit_id geleert — siehe 036_framework_program_context_only_library.sql
037 2026-05-05 SlotBlueprint: training_units.framework_slot_id (+ CHECK Blueprint vs. Kalender), origin_framework_slot_id; Migration SlotÜbungen → training_unit_sections/training_unit_section_items; DROP training_framework_slot_exercises
040046 2026-05 Mitgliedschaft/Anträge, Übungs-Governance-Erweiterungen, media_assets, platform_media_storage, exercise_media.media_asset_id, Tags/GIN u.a. — exakte Nummern: backend/migrations/; fachliche Norm Medien: MEDIA_ASSETS_AND_ARCHIVE_SPEC.md je Umgebung

Migration 022: Skills Schema Complete (BREAKING CHANGE)

Problem: Skills hatten keine Kategorisierung (Haupt-/Unterkategorie, Fokusbereich).
Lösung: Vollständiges hierarchisches Schema für produktionsreifen Import.

Neue Tabellen:

-- Haupt-Kategorien (KARATE / ALLGEMEINE)
skill_main_categories (
  id SERIAL PRIMARY KEY,
  name VARCHAR(200) UNIQUE NOT NULL,  -- "KARATE Fähigkeiten" / "ALLGEMEINE sportliche Fähigkeiten"
  slug VARCHAR(50) UNIQUE NOT NULL,   -- "karate" / "allgemeine"
  description TEXT,
  sort_order INT,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
)

-- Level-Definitionen (1-5 Beschreibungen aus Fähigkeitsmatrix)
skill_level_definitions (
  id SERIAL PRIMARY KEY,
  skill_id INT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
  level INT NOT NULL CHECK (level BETWEEN 1 AND 5),
  description TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  UNIQUE (skill_id, level)
)

Erweiterte Tabellen:

-- skill_categories erweitert
ALTER TABLE skill_categories ADD COLUMN slug VARCHAR(50);
ALTER TABLE skill_categories ADD COLUMN main_category_id INT REFERENCES skill_main_categories(id);
ALTER TABLE skill_categories ADD CONSTRAINT skill_categories_slug_unique UNIQUE (slug);

-- skills erweitert
ALTER TABLE skills ADD COLUMN main_category_id INT REFERENCES skill_main_categories(id);
ALTER TABLE skills ADD COLUMN focus_areas JSONB DEFAULT '[]'::jsonb;

Indizes:

  • idx_skills_main_category_id
  • idx_skills_category_id
  • idx_skill_categories_main_category_id
  • idx_skill_level_definitions_skill_id

Struktur:

skill_main_categories (Hauptkategorie)
  └─ skill_categories (Unterkategorie)
      └─ skills (Fähigkeit)
          └─ skill_level_definitions (Level 1-5 Beschreibungen)

Migration 023: Skills Complete Import

Quelle: Fähigkeitsmatrix (https://karatetrainer.net/index.php?title=Fähigkeitsmatrix)

Importiert:

  • 69 Skills mit vollständiger Kategorisierung
  • 2 Haupt-Kategorien: KARATE Fähigkeiten, ALLGEMEINE sportliche Fähigkeiten
  • 9 Unterkategorien: Kihon, Kumite, Kata, Selbstverteidigung, Koordination, Kondition, Kognition, Soziale Fähigkeiten, Psychische Fähigkeiten

Skills-Verteilung:

KARATE Fähigkeiten (32 Skills, focus_areas: ["karate"]):
├─ Kata (8): Technik Kombination, Kata Ablauf, Bunkai, Oyo, Henka, Kakushi, Kata Atmung, Kata Rhythmus
├─ Kihon (10): Dachi Waza, Uke Waza, Zuki Waza, Uchi Waza, Geri Waza, Nage Waza, Nukite Waza, Ken Waza, Hüfteinsatz, Kime
├─ Kumite (10): Beinarbeit, Distanzkontrolle, Angriff, Abwehr Konter, Präzision, Antizipation, Timing, Taktik, Fokus, Mentale Stärke
└─ Selbstverteidigung (4): Gefahrenbewustsein, Selbstbehauptung, Selbstschutz, Gefahrenabwehr

ALLGEMEINE sportliche Fähigkeiten (37 Skills, focus_areas: ["universal"]):
├─ Kognition (5): Aufmerksamkeit, Wahrnehmung, Urteilsvermögen, Merkfähigkeit, Lernfähigkeit
├─ Kondition (15): Maximalkraft, Schnellkraft, Reaktivkraft, Kraftausdauer, Muskelaufbau, 
│                   Reaktionsschnelligkeit, Bewegungsschnelligkeit, Handlungsschnelligkeit,
│                   Schnelligkeitsausdauer, Grundlagenausdauer, Aerobe Ausdauer, Anaerobe Ausdauer,
│                   Regenerationsfähigkeit, Ermüdungswiderstandsfähigkeit, Flexibilität
├─ Koordination (7): Orientierung, Differenzierung, Kopplung, Gleichgewicht, Rhythmisierung, Reaktion, Umstellung
├─ Psychische Fähigkeiten (6): Selbstvertrauen, Konzentration, Emotionale Kontrolle, Motivation, Stressresistenz, Stressregulation
└─ Soziale Fähigkeiten (4): Deeskalation, Selbstdisziplin, Toleranz, Fairness

Duplikat-Handling: Einige Skills kommen in der Matrix doppelt vor (Kumite + Kondition/Koordination):

  • Behalten in Kondition: Anaerobe Ausdauer, Bewegungsschnelligkeit, Flexibilität, Reaktionsschnelligkeit, Schnelligkeitsausdauer (konditionelle Primärfähigkeiten)
  • Behalten in Kumite: Antizipation, Timing (kampfspezifisch)

Cleanup:

  • DELETE FROM exercise_skills (M:N Beziehungen)
  • DELETE FROM skills (alte Daten)
  • DELETE FROM skill_categories
  • DELETE FROM skill_main_categories

Verifikation:

-- Sollte 69 ergeben
SELECT COUNT(*) FROM skills;

-- Zeigt Verteilung
SELECT 
    mc.name AS hauptkategorie,
    sc.name AS unterkategorie,
    COUNT(s.id) AS anzahl_skills
FROM skills s
JOIN skill_categories sc ON s.category_id = sc.id
JOIN skill_main_categories mc ON s.main_category_id = mc.id
GROUP BY mc.name, sc.name, mc.sort_order, sc.sort_order
ORDER BY mc.sort_order, sc.sort_order;

Kern-Entitäten

Auth & Profile

profiles (id, name, email, password_hash, role, created_at)
sessions (id, profile_id, token, created_at, expires_at)

Organisation

clubs (id, name, abbreviation, description, logo_url, ...)
divisions (id, club_id, name, description, ...)
training_groups (id, division_id, name, description, focus_area, level, ...)

Kataloge

Fokusbereiche & Stile (M:N mit Zielgruppen)

focus_areas (id, name, abbreviation, description, color, icon, ...)
style_directions (id, focus_area_id, name, abbreviation, description, parent_style_id, ...)
target_groups (id, name, description, min_age, max_age, ...)  -- Global, NICHT gebunden an Stil
training_style_target_groups (style_direction_id, target_group_id, is_primary)  -- M:N Junction

Fähigkeiten (Hierarchisches Schema ab Migration 022)

-- Haupt-Kategorien (2: KARATE, ALLGEMEINE)
skill_main_categories (
  id, name, slug, description, sort_order, created_at, updated_at
)

-- Unterkategorien (9: Kihon, Kumite, Kata, Selbstverteidigung, Koordination, Kondition, ...)
skill_categories (
  id, name, slug, description, sort_order,
  main_category_id REFERENCES skill_main_categories(id),
  parent_category_id,  -- Optional: Hierarchie
  created_at, updated_at
)

-- Fähigkeiten (69 Skills)
skills (
  id, name, description,
  category_id REFERENCES skill_categories(id),
  main_category_id REFERENCES skill_main_categories(id),
  focus_areas JSONB,  -- ["karate"] oder ["universal"]
  created_at, updated_at
)

-- Level-Definitionen (optional, noch nicht gefüllt)
skill_level_definitions (
  id, skill_id, level, description,
  created_at, updated_at,
  UNIQUE (skill_id, level)
)

Focus Areas Bedeutung:

  • ["karate"] - Skill ist spezifisch für Karate (z.B. Kata Ablauf, Kihon)
  • ["universal"] - Skill ist universell einsetzbar (z.B. Maximalkraft, Konzentration, Deeskalation)
  • Später möglich: ["karate", "selbstverteidigung"] - Mehrfachzuordnung

Trainingscharakter

training_characters (id, name, description, ...)
trainer_contexts (id, name, description, ...)  -- Neue Dimension (Migration 012)

Übungen (M:N Beziehungen ab Migration 008)

exercises (
  id, title, summary, goal, execution, preparation, trainer_notes,
  duration_min, duration_max,
  group_size_min, group_size_max,
  equipment JSONB,
  visibility, status, created_by, club_id,
  import_source, import_id, wiki_page_id,  -- MediaWiki Import (Migration 018)
  created_at, updated_at
)

-- M:N Beziehungen
exercise_focus_areas (exercise_id, focus_area_id, is_primary)
exercise_style_directions (exercise_id, style_direction_id, is_primary)
exercise_target_groups (exercise_id, target_group_id, is_primary)
exercise_age_groups (exercise_id, age_group)  -- Enum: Minis, Kinder, Schüler, Teenager, Erwachsene

-- Fähigkeiten-Zuordnung (mit Level)
exercise_skills (
  exercise_id, skill_id,
  is_primary, intensity,
  required_level INT,  -- Voraussetzung (1-5)
  target_level INT,    -- Ziel (1-5)
  UNIQUE (exercise_id, skill_id)  -- Migration 020
)

-- Varianten & Medien (028+ Embed/Datei; 045+ optional media_asset_id → media_assets)
exercise_variants (id, exercise_id, name, description, ...)
exercise_media (id, exercise_id, media_asset_id NULL FK, embed_url, file_path, )
media_assets (id, sha256, visibility, club_id, lifecycle_state, copyright_notice, storage_key, )
platform_media_storage (id, local_relative_root, )

Trainingsrahmenprogramm Bibliothek (Migrationen 035036)

Kopf ohne Gruppenbindung (training_framework_programs), Ziele, Slots. Slotspezifischer Ablauf liegt nach 037 nicht mehr in eigener Übungstabelle, sondern in training_units mit framework_slot_id — siehe nächster Abschnitt.

training_framework_programs ( focus_area_id, style_direction_id, visibility, club_id, created_by )
training_framework_goals (framework_program_id, sort_order, title, notes)
training_framework_slots (framework_program_id, sort_order, title, notes, training_unit_id -- ungenutzt)
training_framework_program_training_types (framework_program_id, training_type_id)
training_framework_program_target_groups (framework_program_id, target_group_id)

Training Planning & RahmenBlueprint (Migrationen 006, 031, 037)

Geplante Einheit und RahmenSlotBlueprint teilen sich training_units und den strukturierten Ablauf über Sektionen (031). BlueprintZeilen haben framework_slot_id gesetzt (genau eine Zeile pro Slot); KalenderZeilen haben framework_slot_id IS NULL und group_id / planned_date gesetzt. Kopien aus dem Rahmen können origin_framework_slot_id setzen.

training_units (
  id,
  group_id INT NULL REFERENCES training_groups(id),  -- Pflicht für KalenderZeilen (CHECK)
  planned_date DATE NULL,                            -- Pflicht für KalenderZeilen (CHECK)
  planned_time_start, planned_time_end, planned_focus,
  actual_date, actual_time_start, actual_time_end, attendance_count,
  status, notes, trainer_notes,
  created_by, plan_template_id REFERENCES training_plan_templates(id),
  framework_slot_id INT NULL REFERENCES training_framework_slots(id) ON DELETE CASCADE,
  origin_framework_slot_id INT NULL REFERENCES training_framework_slots(id) ON DELETE SET NULL,
  
)
training_unit_sections (
  training_unit_id, order_index, title, guidance_notes,
  source_template_section_id REFERENCES training_plan_template_sections(id)
)
training_unit_section_items (
  section_id, order_index, item_type CHECK ('exercise'|'note'),
  exercise_id, exercise_variant_id, planned_duration_min, actual_duration_min,
  notes, modifications, note_body
)

Legacy (Migration 006, für ältere Codepfade noch referenzierbar): training_unit_exercises; produktiver Standardablauf liegt in Sections/Items.

Trainingsvorlagen (031): training_plan_templates, training_plan_template_sections.

exercise_blocks (id, name, description, created_by, club_id, ...)  -- Migration 017

Progressionsgraph Übung → Übung (Migrationen 032034)

Separater gerichteter Graph zwischen Übungen (nicht zu verwechseln mit Varianten-Reihen innerhalb einer Übung, Migration 014). Detail-DDL und REST siehe technical/TRAINING_FRAMEWORK_SPEC.md §3.

exercise_progression_graphs (
  id, name, description, visibility, club_id, created_by,
  created_at, updated_at
)
exercise_progression_edges (
  id, graph_id,
  from_exercise_id, to_exercise_id,
  from_exercise_variant_id,  -- nullable (Migration 034)
  to_exercise_variant_id,
  edge_type,                 -- z. B. next_exercise, sibling
  notes,                     -- Migration 033
  created_at
)

MediaWiki Import (Migration 018)

wiki_import_log (
  id, category, import_type, import_status,
  items_total, items_imported, items_skipped, items_failed,
  error_log JSONB,
  started_at, finished_at,
  imported_by
)

wiki_import_references (
  id, wiki_page_id, entity_type, local_id,
  created_at, updated_at
)

Import-Typen:

  • exercise - Übungen aus Kategorie:Übungen
  • skill - Fähigkeiten aus Kategorie:Fähigkeitsbeschreibung (veraltet, nutze Migration 023)
  • method - Trainingsmethoden aus Kategorie:Methodenbeschreibung

Indizes

Performance-Indizes

-- Hierarchie-Lookups
CREATE INDEX idx_style_directions_focus_area ON style_directions(focus_area_id);
CREATE INDEX idx_skill_categories_main_category ON skill_categories(main_category_id);
CREATE INDEX idx_skills_category ON skills(category_id);
CREATE INDEX idx_skills_main_category ON skills(main_category_id);

-- M:N Joins
CREATE INDEX idx_exercise_focus_areas_exercise ON exercise_focus_areas(exercise_id);
CREATE INDEX idx_exercise_focus_areas_focus ON exercise_focus_areas(focus_area_id);
CREATE INDEX idx_exercise_style_directions_exercise ON exercise_style_directions(exercise_id);
CREATE INDEX idx_exercise_style_directions_style ON exercise_style_directions(style_direction_id);
CREATE INDEX idx_exercise_target_groups_exercise ON exercise_target_groups(exercise_id);
CREATE INDEX idx_exercise_target_groups_target ON exercise_target_groups(target_group_id);
CREATE INDEX idx_exercise_skills_exercise ON exercise_skills(exercise_id);
CREATE INDEX idx_exercise_skills_skill ON exercise_skills(skill_id);

-- Import-Tracking
CREATE INDEX idx_wiki_import_references_wiki_page ON wiki_import_references(wiki_page_id);
CREATE INDEX idx_wiki_import_references_entity ON wiki_import_references(entity_type, local_id);

Kaskadier-Regeln

Fokusbereich löschen

Verhalten: RESTRICT (Löschen verweigern wenn verwendet)

Prüfung:

SELECT COUNT(*) FROM style_directions WHERE focus_area_id = :id
SELECT COUNT(*) FROM exercise_focus_areas WHERE focus_area_id = :id

Alternativen:

  1. Umrouten: Alle abhängigen Stile auf neuen Fokusbereich setzen
  2. Archivieren: Status auf 'archived' setzen statt löschen

Stil löschen

Verhalten: RESTRICT wenn Zielgruppen oder Übungen zugeordnet

Umrouten-Workflow:

  1. Admin wählt Ziel-Stil
  2. System aktualisiert:
    • training_style_target_groups.style_direction_id
    • exercise_style_directions.style_direction_id
  3. Löschen erlaubt

Zielgruppe löschen

Verhalten: SET NULL oder CASCADE in exercise_target_groups

Prüfung:

SELECT COUNT(*) FROM training_style_target_groups WHERE target_group_id = :id
SELECT COUNT(*) FROM exercise_target_groups WHERE target_group_id = :id

Skill löschen

Verhalten: CASCADE zu skill_level_definitions, RESTRICT wenn in exercise_skills

Prüfung:

SELECT COUNT(*) FROM exercise_skills WHERE skill_id = :id

Nächste Schritte

  • Level-Definitionen aus Fähigkeitsmatrix extrahieren (optional)
  • Skills-Import testen mit Übungs-Import
  • Migration 024: Skills-Beschreibungen aus Wiki importieren
  • Admin-UI für Skill-Kategorien (CRUD)
  • Skill-Filter in Übungssuche integrieren

Letzte Aktualisierung: 2026-04-27
Verantwortlich: Claude Code
Review: Pending