mitai-jinkendo/.claude/docs/working/SHINKAN_PROJECT_SETUP.md
Lars 2453da0da1
All checks were successful
Deploy Development / deploy (push) Successful in 55s
Build Test / pytest-backend (push) Successful in 8s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
feat: add body_history_viz widget and enhance configuration handling
- Introduced the `body_history_viz` widget to the dashboard, allowing users to visualize body history data.
- Updated widget configuration to include `body_history_viz` in the allowed widgets and added validation for its configuration.
- Enhanced the widget catalog with details for the new `body_history_viz` entry.
- Added tests to ensure proper validation of the `body_history_viz` widget configuration.
- Updated application version to reflect the addition of the new widget.
2026-04-22 07:00:24 +02:00

32 KiB
Raw Blame History

Shinkan Jinkendo Technisches Setup

Trainer- und Vereinsplattform für Kampfsport-Trainingsplanung
Basis: Mitai Jinkendo Ökosystem
Domain: shinkan.jinkendo.de (真観 - "Wahre Beobachtung")


1. Fachliche Einordnung

Shinkan ist KEINE persönliche Tracking-App.

Shinkan ist eine trainer- und vereinszentrierte Planungs- und Inhaltsplattform mit Fokus auf:

  • Übungsverwaltung und -suche
  • Trainingsplanung für Gruppen
  • Standardisierung und Wiederverwendung
  • Freigabe und Governance von Inhalten
  • Import aus bestehendem MediaWiki

Primäre Nutzer in MVP: Trainer, Vereinsadmins, Redakteure
Nicht in MVP: Individuelle Sportler-Apps, persönliches Tracking, Gürtel-Tracking

Abgrenzung zu Mitai:

  • Mitai = persönliches Körper- und Trainings-Tracking
  • Shinkan = Trainer-Arbeit, Übungen, Trainingsplanung, Vereinsstandards

2. Übernommene Infrastruktur von Mitai

Komponente Status Verwendung
Auth-System 1:1 übernehmen Token + bcrypt
Membership-Basis Übernehmen Tiers, Features (vereinfacht)
User-Profiles Übernehmen Multi-Profile pro Account
Tech-Stack 1:1 React 18 + FastAPI + PostgreSQL 16
Docker/Compose 1:1 Dev + Prod Umgebungen
PWA 1:1 Mobile + Desktop
Design-System Basis CSS-Variablen, mobile-first
Versioning 1:1 version.py + Migrations
Gitea CI/CD 1:1 Auto-Deploy dev/prod

Ports:

  • Prod: 3003/8003 (Frontend/Backend)
  • Dev: 3098/8098 (Frontend/Backend)

3. Technische Basis (identisch zu Mitai)

Frontend:  React 18 + Vite + PWA (Node 20)
Backend:   FastAPI Python 3.12
Datenbank: PostgreSQL 16 Alpine
Container: Docker + Docker Compose
Auth:      Token-basiert + bcrypt
KI:        OpenRouter API (optional, nicht MVP-kritisch)

4. Verzeichnisstruktur (angepasst)

shinkan-jinkendo/
├── backend/
│   ├── main.py                 # App-Setup + Router-Registration
│   ├── db.py                   # PostgreSQL Connection Pool
│   ├── db_init.py              # DB-Init + Migrations
│   ├── auth.py                 # Hash, Verify, Sessions, Feature-Access
│   ├── models.py               # Pydantic Models
│   ├── version.py              # Versionskontrolle
│   ├── migrations/             # SQL-Migrationen (001_*.sql)
│   └── routers/
│       ├── auth.py             # Login, Register, Sessions
│       ├── profiles.py         # User-Profiles
│       ├── clubs.py            # Vereine / Sparten
│       ├── groups.py           # Trainingsgruppen
│       ├── skills.py           # Fähigkeiten (Admin + CRUD)
│       ├── methods.py          # Trainingsmethoden (Admin + CRUD)
│       ├── exercises.py        # Übungen (Kern-Modul)
│       ├── training_units.py   # Trainingseinheiten
│       ├── training_programs.py # Trainingsprogramme
│       ├── planning.py         # Planungsansichten
│       ├── import_wiki.py      # MediaWiki-Import
│       ├── admin.py            # Admin-Panel
│       └── membership.py       # Subscription (vereinfacht)
│
├── frontend/
│   ├── src/
│   │   ├── App.jsx             # Root, Auth-Gates, Navigation
│   │   ├── app.css             # CSS-Variablen + Styles
│   │   ├── version.js          # Frontend-Versionierung
│   │   ├── config/
│   │   │   ├── appNav.js       # Hauptnavigation
│   │   │   └── adminNav.js     # Admin-Navigation
│   │   ├── context/
│   │   │   ├── AuthContext.jsx
│   │   │   └── ProfileContext.jsx
│   │   ├── pages/
│   │   │   ├── Dashboard.jsx
│   │   │   ├── ExercisesPage.jsx      # Übungssuche + CRUD
│   │   │   ├── PlanningPage.jsx       # Trainingsplanung
│   │   │   ├── GroupsPage.jsx         # Gruppenverwaltung
│   │   │   ├── SkillsPage.jsx         # Fähigkeiten (Admin)
│   │   │   ├── MethodsPage.jsx        # Methoden (Admin)
│   │   │   ├── ImportPage.jsx         # MediaWiki-Import
│   │   │   └── SettingsPage.jsx
│   │   └── utils/
│   │       ├── api.js          # ALLE API-Calls
│   │       └── planning.js     # Planungshelfer
│   └── public/
│       ├── manifest.json       # PWA-Manifest
│       └── icons/
│
├── .claude/
│   ├── commands/               # Slash-Commands
│   ├── docs/
│   │   ├── functional/
│   │   │   └── SHINKAN_REQUIREMENTS.md  # Anforderungsdokument
│   │   ├── technical/
│   │   │   └── SHINKAN_DOMAIN_MODEL.md  # Domänenmodell
│   │   └── rules/              # Architektur-Regeln (wie Mitai)
│   └── library/
│
├── docker-compose.yml          # Production
├── docker-compose.dev-env.yml  # Development
├── .env
├── .gitignore
└── CLAUDE.md                   # Agent-Einstieg

5. Domänenmodell (MVP Core)

5.1 Organisationsstruktur

-- Vereine
clubs (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(50),
    description TEXT,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)

-- Sparten (optional, kann auch später)
divisions (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    name VARCHAR(200) NOT NULL,
    focus_area VARCHAR(100),  -- karate, selbstverteidigung, gewaltschutz
    created_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsgruppen
training_groups (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    name VARCHAR(200) NOT NULL,
    focus VARCHAR(100),
    level VARCHAR(50),
    age_group VARCHAR(50),
    weekday VARCHAR(20),
    time_start TIME,
    time_end TIME,
    location VARCHAR(200),
    trainer_id INT REFERENCES profiles(id),
    co_trainer_ids JSONB,  -- [1, 2, 3]
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)

5.2 Fähigkeiten und Methoden (Kataloge)

-- Fähigkeiten (global)
skills (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    category VARCHAR(100),  -- kihon, kumite, kata, selbstverteidigung, fitness
    description TEXT,
    importance INT,  -- 1-5
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsmethoden (global)
training_methods (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(20),
    category VARCHAR(100),  -- intervall, rollenspiel, zirkel, koordination, etc.
    description TEXT,
    typical_duration INT,  -- in Minuten
    typical_group_size VARCHAR(50),
    related_skills JSONB,  -- [skill_id, skill_id, ...]
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)

5.3 Übungen (Kernobjekt)

-- Übungen
exercises (
    id SERIAL PRIMARY KEY,
    title VARCHAR(300) NOT NULL,
    summary TEXT,  -- Kurzbeschreibung
    goal TEXT NOT NULL,  -- Ziel der Übung
    execution TEXT NOT NULL,  -- Durchführung
    preparation TEXT,  -- Vorbereitung / Aufbau
    trainer_notes TEXT,  -- Hinweise für Trainer
    equipment JSONB,  -- ["Matten", "Pratzen", "Bälle"]
    
    -- Metadaten
    duration_min INT,
    duration_max INT,
    group_size_min INT,
    group_size_max INT,
    age_groups JSONB,  -- ["minis", "kinder", "erwachsene"]
    
    -- Fachliche Zuordnung
    focus_area VARCHAR(100),  -- karate, selbstverteidigung, gewaltschutz
    secondary_areas JSONB,
    training_character VARCHAR(50),  -- grundlage, aufbau, vertiefung, festigung, diagnose
    
    -- Methode
    primary_method_id INT REFERENCES training_methods(id),
    secondary_method_ids JSONB,
    
    -- Freigabe
    visibility VARCHAR(50) DEFAULT 'private',  -- private, club, official
    status VARCHAR(50) DEFAULT 'draft',  -- draft, in_review, approved, archived
    created_by INT REFERENCES profiles(id),
    club_id INT REFERENCES clubs(id),
    
    -- Import
    import_source VARCHAR(50),  -- mediawiki, manual
    import_id VARCHAR(200),
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
)

-- Übungs-Fähigkeiten (M:N)
exercise_skills (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    skill_id INT REFERENCES skills(id),
    is_primary BOOLEAN DEFAULT false,
    intensity INT,  -- 1-5
    development_contribution VARCHAR(50),  -- low, medium, high
    required_level INT,  -- Eingangs-Niveau
    target_level INT,  -- Ziel-Niveau
    created_at TIMESTAMP DEFAULT NOW()
)

-- Übungsvarianten
exercise_variants (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    variant_name VARCHAR(200) NOT NULL,
    description TEXT,
    execution_changes TEXT,
    duration_min INT,
    duration_max INT,
    equipment_changes JSONB,
    difficulty_adjustment VARCHAR(50),  -- easier, harder, adapted
    age_group_override JSONB,
    skill_focus_override JSONB,
    created_at TIMESTAMP DEFAULT NOW()
)

-- Medien (1:N)
exercise_media (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    media_type VARCHAR(50),  -- image, video, document, sketch
    file_path TEXT,
    title VARCHAR(200),
    description TEXT,
    sort_order INT,
    is_primary BOOLEAN DEFAULT false,
    context VARCHAR(100),  -- ablauf, detail, trainer_hint
    created_at TIMESTAMP DEFAULT NOW()
)

5.4 Trainingsplanung

-- Training Templates / Standards (vereinbar)
training_templates (
    id SERIAL PRIMARY KEY,
    name VARCHAR(300) NOT NULL,
    type VARCHAR(50),  -- template, standard
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    
    goal TEXT,
    focus_areas JSONB,
    duration_total INT,
    
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    version INT DEFAULT 1,
    created_by INT REFERENCES profiles(id),
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsabschnitte (für Templates + Units)
training_sections (
    id SERIAL PRIMARY KEY,
    parent_type VARCHAR(50),  -- template, unit
    parent_id INT,
    
    title VARCHAR(200) NOT NULL,
    section_type VARCHAR(100),  -- aufwaermen, kihon, kumite, kata, abschluss, etc.
    sort_order INT,
    duration_planned INT,
    goal TEXT,
    notes TEXT,
    
    is_combination BOOLEAN DEFAULT false,  -- Übungen als Komplex
    combination_method_id INT REFERENCES training_methods(id),
    
    created_at TIMESTAMP DEFAULT NOW()
)

-- Übungen in Abschnitten
section_exercises (
    id SERIAL PRIMARY KEY,
    section_id INT REFERENCES training_sections(id) ON DELETE CASCADE,
    exercise_id INT REFERENCES exercises(id),
    variant_id INT REFERENCES exercise_variants(id),
    sort_order INT,
    duration_planned INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
)

-- Konkrete Trainingseinheiten
training_units (
    id SERIAL PRIMARY KEY,
    group_id INT REFERENCES training_groups(id),
    date DATE NOT NULL,
    time_start TIME,
    time_end TIME,
    
    derived_from_template_id INT REFERENCES training_templates(id),
    derived_from_unit_id INT REFERENCES training_units(id),
    
    title VARCHAR(300),
    goal TEXT,
    focus_areas JSONB,
    
    -- Durchführung
    actual_time_start TIME,
    actual_time_end TIME,
    completion_status VARCHAR(50),  -- planned, in_progress, completed
    
    -- Reflexion
    reflection_text TEXT,
    what_worked_well TEXT,
    what_to_improve TEXT,
    
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsprogramme
training_programs (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    
    name VARCHAR(300) NOT NULL,
    description TEXT,
    goal TEXT,
    duration_weeks INT,
    focus_areas JSONB,
    
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    created_by INT REFERENCES profiles(id),
    
    created_at TIMESTAMP DEFAULT NOW()
)

-- Programm-Einheiten (Reihenfolge)
program_units (
    id SERIAL PRIMARY KEY,
    program_id INT REFERENCES training_programs(id) ON DELETE CASCADE,
    unit_order INT,
    template_id INT REFERENCES training_templates(id),
    week_number INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
)

5.5 Governance

-- Änderungsanfragen (für geschützte Inhalte)
content_change_requests (
    id SERIAL PRIMARY KEY,
    content_type VARCHAR(50),  -- exercise, template, skill, method
    content_id INT,
    requested_by INT REFERENCES profiles(id),
    change_description TEXT,
    change_details JSONB,
    status VARCHAR(50) DEFAULT 'pending',  -- pending, approved, rejected
    reviewed_by INT REFERENCES profiles(id),
    review_notes TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    reviewed_at TIMESTAMP
)

5.6 MediaWiki-Import

-- Import-Tracking
wiki_import_log (
    id SERIAL PRIMARY KEY,
    import_type VARCHAR(50),  -- skill, method, exercise
    import_status VARCHAR(50),  -- success, partial, failed
    items_total INT,
    items_imported INT,
    items_failed INT,
    error_log JSONB,
    imported_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW()
)

-- Import-Referenzen (für Re-Import-Schutz)
wiki_import_references (
    id SERIAL PRIMARY KEY,
    wiki_page_title VARCHAR(300),
    wiki_page_id VARCHAR(100),
    content_type VARCHAR(50),  -- skill, method, exercise
    local_id INT,
    last_imported TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(wiki_page_title, content_type)
)

6. MVP Core-Features (Release 1)

6.1 Pflicht in MVP

Organisationsstruktur:

  • Verein anlegen/verwalten
  • Trainingsgruppen anlegen/verwalten
  • Trainer zuordnen

Kataloge:

  • Fähigkeiten-Katalog (Admin CRUD)
  • Methoden-Katalog (Admin CRUD)

Übungen:

  • Übung anlegen (minimal: Titel, Ziel, Durchführung)
  • Übung vollständig beschreiben (alle Felder)
  • Übungsvarianten anlegen
  • Fähigkeiten zuordnen (Haupt/Neben, Intensität)
  • Methoden zuordnen (Haupt/Neben)
  • Medien hochladen (Bilder, Videos)
  • Übung suchen und filtern
    • Schnellsuche (Titel, Schlagworte)
    • Filter (Bereich, Zielgruppe, Fähigkeit, Dauer, Gruppengröße)
  • Übung drucken
  • Freigabelogik (privat / Verein / offiziell)

Trainingsplanung:

  • Trainingseinheit für Gruppe + Termin planen
  • Training aus Vorlage ableiten
  • Training aus altem Training kopieren
  • Trainingsabschnitte verwalten
  • Übungen zu Abschnitten zuordnen
  • Kombinations-Flag für Abschnitte
  • Kalenderansicht (Gruppe, Datum)
  • Druckansicht
  • Mobile Durchführungssicht
  • Notizen zu Training + Übungen
  • Reflexion nach Training

Import:

  • Einseitiger MediaWiki-Import
    • Fähigkeiten
    • Methoden
    • Übungen
  • Import-Tracking (Erfolg/Fehler)
  • Duplikat-Erkennung (Wiki-Referenz)

6.2 Bewusst NICHT in MVP

Kern-Features, die warten:

  • KI-Trainingsplanung
  • KI-Suche mit externen Calls
  • Sportler-Tracking (individuelle Entwicklung)
  • Gürtel/Graduierungs-System
  • Technik-Mastery-Tracking
  • Reifegradmodell (vollständig)
  • Turnier-/Wettkampfbegleitung
  • Community/Marktplatz
  • Komplexe Membership-Matrix
  • Vollhistorisierung jeder Änderung
  • Bidirektionale Wiki-Sync
  • Offline-Sync (vollständig)

Rollen/Governance (vereinfacht):

  • Feingranulare Rechte-Matrix
  • Workflow-basierte Freigabe
  • Multi-Tenant-Administration

7. Initiale Migrationen (MVP)

7.1 001_auth_membership.sql

-- Von Mitai übernehmen:
-- profiles, sessions, features, tier_limits, subscriptions
-- (siehe Mitai 001_*.sql + 002_*.sql)

7.2 002_organization.sql

-- Vereine
CREATE TABLE clubs (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(50),
    description TEXT,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Sparten (optional, kann später)
CREATE TABLE divisions (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id) ON DELETE CASCADE,
    name VARCHAR(200) NOT NULL,
    focus_area VARCHAR(100),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Trainingsgruppen
CREATE TABLE training_groups (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id) ON DELETE CASCADE,
    division_id INT REFERENCES divisions(id),
    name VARCHAR(200) NOT NULL,
    focus VARCHAR(100),
    level VARCHAR(50),
    age_group VARCHAR(50),
    weekday VARCHAR(20),
    time_start TIME,
    time_end TIME,
    location VARCHAR(200),
    trainer_id INT REFERENCES profiles(id),
    co_trainer_ids JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_groups_club ON training_groups(club_id);
CREATE INDEX idx_groups_trainer ON training_groups(trainer_id);

7.3 003_catalogs.sql

-- Fähigkeiten
CREATE TABLE skills (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    category VARCHAR(100),
    description TEXT,
    importance INT CHECK (importance BETWEEN 1 AND 5),
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Trainingsmethoden
CREATE TABLE training_methods (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(20),
    category VARCHAR(100),
    description TEXT,
    typical_duration INT,
    typical_group_size VARCHAR(50),
    related_skills JSONB,
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_skills_category ON skills(category);
CREATE INDEX idx_methods_category ON training_methods(category);

7.4 004_exercises.sql

-- Übungen
CREATE TABLE exercises (
    id SERIAL PRIMARY KEY,
    title VARCHAR(300) NOT NULL,
    summary TEXT,
    goal TEXT NOT NULL,
    execution TEXT NOT NULL,
    preparation TEXT,
    trainer_notes TEXT,
    equipment JSONB,
    
    duration_min INT,
    duration_max INT,
    group_size_min INT,
    group_size_max INT,
    age_groups JSONB,
    
    focus_area VARCHAR(100),
    secondary_areas JSONB,
    training_character VARCHAR(50),
    
    primary_method_id INT REFERENCES training_methods(id),
    secondary_method_ids JSONB,
    
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'draft',
    created_by INT REFERENCES profiles(id),
    club_id INT REFERENCES clubs(id),
    
    import_source VARCHAR(50),
    import_id VARCHAR(200),
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Übungs-Fähigkeiten
CREATE TABLE exercise_skills (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    skill_id INT REFERENCES skills(id),
    is_primary BOOLEAN DEFAULT false,
    intensity INT CHECK (intensity BETWEEN 1 AND 5),
    development_contribution VARCHAR(50),
    required_level INT,
    target_level INT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Varianten
CREATE TABLE exercise_variants (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    variant_name VARCHAR(200) NOT NULL,
    description TEXT,
    execution_changes TEXT,
    duration_min INT,
    duration_max INT,
    equipment_changes JSONB,
    difficulty_adjustment VARCHAR(50),
    age_group_override JSONB,
    skill_focus_override JSONB,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Medien
CREATE TABLE exercise_media (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    media_type VARCHAR(50),
    file_path TEXT,
    title VARCHAR(200),
    description TEXT,
    sort_order INT,
    is_primary BOOLEAN DEFAULT false,
    context VARCHAR(100),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_exercises_created_by ON exercises(created_by);
CREATE INDEX idx_exercises_club ON exercises(club_id);
CREATE INDEX idx_exercises_visibility ON exercises(visibility);
CREATE INDEX idx_exercise_skills_skill ON exercise_skills(skill_id);

7.5 005_training_planning.sql

-- Templates / Standards
CREATE TABLE training_templates (
    id SERIAL PRIMARY KEY,
    name VARCHAR(300) NOT NULL,
    type VARCHAR(50),
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    goal TEXT,
    focus_areas JSONB,
    duration_total INT,
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    version INT DEFAULT 1,
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Abschnitte
CREATE TABLE training_sections (
    id SERIAL PRIMARY KEY,
    parent_type VARCHAR(50),
    parent_id INT,
    title VARCHAR(200) NOT NULL,
    section_type VARCHAR(100),
    sort_order INT,
    duration_planned INT,
    goal TEXT,
    notes TEXT,
    is_combination BOOLEAN DEFAULT false,
    combination_method_id INT REFERENCES training_methods(id),
    created_at TIMESTAMP DEFAULT NOW()
);

-- Übungen in Abschnitten
CREATE TABLE section_exercises (
    id SERIAL PRIMARY KEY,
    section_id INT REFERENCES training_sections(id) ON DELETE CASCADE,
    exercise_id INT REFERENCES exercises(id),
    variant_id INT REFERENCES exercise_variants(id),
    sort_order INT,
    duration_planned INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Trainingseinheiten
CREATE TABLE training_units (
    id SERIAL PRIMARY KEY,
    group_id INT REFERENCES training_groups(id) ON DELETE CASCADE,
    date DATE NOT NULL,
    time_start TIME,
    time_end TIME,
    derived_from_template_id INT REFERENCES training_templates(id),
    derived_from_unit_id INT REFERENCES training_units(id),
    title VARCHAR(300),
    goal TEXT,
    focus_areas JSONB,
    actual_time_start TIME,
    actual_time_end TIME,
    completion_status VARCHAR(50),
    reflection_text TEXT,
    what_worked_well TEXT,
    what_to_improve TEXT,
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Programme
CREATE TABLE training_programs (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    name VARCHAR(300) NOT NULL,
    description TEXT,
    goal TEXT,
    duration_weeks INT,
    focus_areas JSONB,
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE program_units (
    id SERIAL PRIMARY KEY,
    program_id INT REFERENCES training_programs(id) ON DELETE CASCADE,
    unit_order INT,
    template_id INT REFERENCES training_templates(id),
    week_number INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_units_group_date ON training_units(group_id, date);
CREATE INDEX idx_sections_parent ON training_sections(parent_type, parent_id);

7.6 006_governance.sql

-- Änderungsanfragen
CREATE TABLE content_change_requests (
    id SERIAL PRIMARY KEY,
    content_type VARCHAR(50),
    content_id INT,
    requested_by INT REFERENCES profiles(id),
    change_description TEXT,
    change_details JSONB,
    status VARCHAR(50) DEFAULT 'pending',
    reviewed_by INT REFERENCES profiles(id),
    review_notes TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    reviewed_at TIMESTAMP
);

CREATE INDEX idx_change_requests_status ON content_change_requests(status);

7.7 007_wiki_import.sql

-- Import-Log
CREATE TABLE wiki_import_log (
    id SERIAL PRIMARY KEY,
    import_type VARCHAR(50),
    import_status VARCHAR(50),
    items_total INT,
    items_imported INT,
    items_failed INT,
    error_log JSONB,
    imported_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW()
);

-- Import-Referenzen
CREATE TABLE wiki_import_references (
    id SERIAL PRIMARY KEY,
    wiki_page_title VARCHAR(300),
    wiki_page_id VARCHAR(100),
    content_type VARCHAR(50),
    local_id INT,
    last_imported TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(wiki_page_title, content_type)
);

CREATE INDEX idx_wiki_refs_page ON wiki_import_references(wiki_page_title);

8. MediaWiki-Import-Strategie

8.1 Grundprinzip

Einseitig, einmalig, dann Shinkan-nativ weiter

  • Import aus MediaWiki nach Shinkan
  • KEINE bidirektionale Synchronisation
  • KEINE Live-Kopplung
  • KEINE kontinuierliche Aktualisierung

8.2 Import-Reihenfolge

  1. Fähigkeiten (skills) - Basis-Katalog
  2. Methoden (training_methods) - Methodenkatalog
  3. Übungen (exercises) - Hauptinhalt

Programme/Trainings: Nur wenn Datenqualität ausreicht

8.3 Import-Logik

# Pseudo-Code Import-Workflow
for wiki_page in wiki_pages:
    # Duplikat-Check
    existing = check_wiki_reference(wiki_page.title, content_type)
    if existing:
        log_skip(wiki_page)
        continue
    
    # Parse + Transform
    data = parse_wiki_page(wiki_page)
    
    # Validate
    if not validate_minimal_fields(data):
        log_error(wiki_page, "missing required fields")
        continue
    
    # Import
    try:
        local_id = create_content(data)
        create_wiki_reference(wiki_page, local_id)
        log_success(wiki_page, local_id)
    except Exception as e:
        log_error(wiki_page, str(e))

8.4 Import-Metadaten

Jeder importierte Inhalt erhält:

  • import_source = 'mediawiki'
  • import_id = wiki_page_id
  • Referenz in wiki_import_references
  • Status = draft (muss nachbearbeitet werden)

8.5 Nach-Import-Workflow

Nach Import müssen Trainer/Admins:

  • Importierte Inhalte prüfen
  • Unvollständige Felder ergänzen
  • Fähigkeiten/Methoden zuordnen
  • Status auf approved setzen

9. Docker-Setup (wie Mitai)

9.1 docker-compose.yml (Production)

version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    container_name: shinkan-db-prod
    environment:
      POSTGRES_DB: shinkan
      POSTGRES_USER: shinkan_user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - shinkan-db-data:/var/lib/postgresql/data
    ports:
      - "5434:5432"
    restart: unless-stopped

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: shinkan-api
    environment:
      DB_HOST: postgres
      DB_PORT: 5432
      DB_NAME: shinkan
      DB_USER: shinkan_user
      DB_PASSWORD: ${DB_PASSWORD}
      APP_URL: https://shinkan.jinkendo.de
      ALLOWED_ORIGINS: https://shinkan.jinkendo.de
    volumes:
      - shinkan-media:/app/media
    ports:
      - "8003:8000"
    depends_on:
      - postgres
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      args:
        VITE_API_URL: https://shinkan.jinkendo.de
    container_name: shinkan-ui
    ports:
      - "3003:80"
    restart: unless-stopped

volumes:
  shinkan-db-data:
  shinkan-media:

9.2 docker-compose.dev-env.yml (Development)

version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    container_name: dev-shinkan-postgres
    environment:
      POSTGRES_DB: shinkan_dev
      POSTGRES_USER: shinkan_dev
      POSTGRES_PASSWORD: dev_password
    volumes:
      - dev-shinkan-db-data:/var/lib/postgresql/data
    ports:
      - "5435:5432"
    restart: unless-stopped

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: dev-shinkan-api
    environment:
      DB_HOST: postgres
      DB_PORT: 5432
      DB_NAME: shinkan_dev
      DB_USER: shinkan_dev
      DB_PASSWORD: dev_password
      APP_URL: https://dev.shinkan.jinkendo.de
      ALLOWED_ORIGINS: https://dev.shinkan.jinkendo.de
    volumes:
      - dev-shinkan-media:/app/media
    ports:
      - "8098:8000"
    depends_on:
      - postgres
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      args:
        VITE_API_URL: https://dev.shinkan.jinkendo.de
    container_name: dev-shinkan-ui
    ports:
      - "3098:80"
    restart: unless-stopped

volumes:
  dev-shinkan-db-data:
  dev-shinkan-media:

10. Initiale Version

# backend/version.py

APP_VERSION = "0.1.0"
BUILD_DATE = "2026-04-21"
DB_SCHEMA_VERSION = "20260421"

MODULE_VERSIONS = {
    "auth": "1.0.0",
    "profiles": "1.0.0",
    "clubs": "0.1.0",
    "groups": "0.1.0",
    "skills": "0.1.0",
    "methods": "0.1.0",
    "exercises": "0.1.0",
    "training_units": "0.1.0",
    "training_programs": "0.1.0",
    "planning": "0.1.0",
    "import_wiki": "0.1.0",
    "admin": "1.0.0",
    "membership": "1.0.0",
}

CHANGELOG = [
    {
        "version": "0.1.0",
        "date": "2026-04-21",
        "changes": [
            "Initial MVP Setup",
            "Feature: Übungsverwaltung (Kern-Modul)",
            "Feature: Fähigkeiten- und Methodenkataloge",
            "Feature: Trainingsplanung für Gruppen",
            "Feature: Trainingsabschnitte mit Kombinations-Flag",
            "Feature: MediaWiki-Import (einseitig)",
            "Feature: Freigabelogik (privat/Verein/offiziell)",
        ]
    }
]

11. Umsetzungs-Reihenfolge (Empfehlung)

Phase 1: Basis (2-3 Tage)

  • Repository + Docker-Setup
  • Auth + Membership von Mitai kopieren
  • DB-Migrationen 001-003
  • Version.py anpassen

Phase 2: Kataloge (2 Tage)

  • Skills CRUD (Admin)
  • Methods CRUD (Admin)
  • Frontend Admin-Pages

Phase 3: Übungen (5-7 Tage)

  • Exercises CRUD
  • Exercise-Skills M:N
  • Exercise-Variants
  • Exercise-Media
  • Suche + Filter
  • Druckansicht

Phase 4: Trainingsplanung (5-7 Tage)

  • Training-Groups CRUD
  • Training-Templates/Standards
  • Training-Sections
  • Training-Units
  • Kalenderansicht
  • Mobile Durchführungssicht

Phase 5: Import (3-4 Tage)

  • MediaWiki-Parser
  • Import-Workflow (Skills, Methods, Exercises)
  • Import-Log + Duplikat-Erkennung

Phase 6: Governance (2-3 Tage)

  • Freigabelogik
  • Change-Requests (Basis)
  • Eigene vs. Vereins-Inhalte

Geschätzte Gesamtdauer MVP: 4-5 Wochen


12. Bewusst nur vorbereitet, nicht umgesetzt (in MVP)

Diese Elemente sollen durch das Datenmodell nicht verbaut werden, gehören aber nicht in MVP:

Reifegradmodelle:

  • Skill-Levels pro Modell
  • Maturity-Stages
  • Target-Descriptions pro Level

Individuelle Sportler-Entwicklung:

  • Athlete-Profiles
  • Skill-Progress-Tracking
  • Assessment-History

Erweiterte Governance:

  • Workflow-basierte Freigabe
  • Review-Zyklen
  • Versionierung mit Branches

Community:

  • Shared Content-Pool
  • Bewertungen
  • Kommentare

KI:

  • Trainingsplanung
  • Übungssuche mit Semantik
  • Automatische Programm-Generierung

13. Offene Punkte / Entscheidungsbedarf

🤔 MediaWiki-Parser: Welches Format nutzt das aktuelle Wiki? (Semantic MediaWiki)
🤔 Medien-Upload: Lokal oder S3/Cloud?
🤔 Suche: PostgreSQL Full-Text oder ElasticSearch später?
🤔 Offline: Service Worker + IndexedDB oder nur gute Mobile-UX?
🤔 Druckformat: PDF-Export oder nur HTML-Print?
🤔 Sparten: Schon in MVP oder erst später?

14. Nächste konkrete Schritte

  1. Repository erstellen auf Gitea: shinkan-jinkendo
  2. Basis-Dateien kopieren aus Mitai:
    • backend/auth.py, db.py, db_init.py
    • frontend/src/context/, utils/api.js
  3. Migrationen schreiben (001-007)
  4. Version.py anpassen
  5. CLAUDE.md erstellen für Shinkan
  6. Erste Router implementieren:
    • skills.py
    • methods.py
    • exercises.py

Version: 2.0 (komplett überarbeitet)
Stand: 21.04.2026
Autor: Claude Code (basierend auf Anforderungsdokument)