- 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.
1203 lines
32 KiB
Markdown
1203 lines
32 KiB
Markdown
# 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
|
||
|
||
```sql
|
||
-- 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)
|
||
|
||
```sql
|
||
-- 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)
|
||
|
||
```sql
|
||
-- Ü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
|
||
|
||
```sql
|
||
-- 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
|
||
|
||
```sql
|
||
-- Ä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
|
||
|
||
```sql
|
||
-- 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
|
||
|
||
```sql
|
||
-- Von Mitai übernehmen:
|
||
-- profiles, sessions, features, tier_limits, subscriptions
|
||
-- (siehe Mitai 001_*.sql + 002_*.sql)
|
||
```
|
||
|
||
### 7.2 002_organization.sql
|
||
|
||
```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
|
||
|
||
```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
|
||
|
||
```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
|
||
|
||
```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
|
||
|
||
```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
|
||
|
||
```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
|
||
|
||
```python
|
||
# 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)
|
||
|
||
```yaml
|
||
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)
|
||
|
||
```yaml
|
||
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
|
||
|
||
```python
|
||
# 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)
|