Docker & Deployment: - docker-compose.yml (Prod: Port 3003/8003) - docker-compose.dev-env.yml (Dev: Port 3098/8098) - Backend Dockerfile (Python 3.12-slim) - Frontend Dockerfile (Node 20 + Nginx) - Gitea Actions (deploy-dev.yml, deploy-prod.yml) Frontend: - React 18 + Vite setup - package.json, vite.config.js, index.html - App.jsx (minimal with version display) - api.js (complete API client) - app.css + AuthContext from Mitai - main.jsx entry point Backend Migrations: - 001_auth_membership.sql (Auth + Features + Tier Limits) - 002_organization.sql (Clubs, Divisions, Training Groups) - 003_catalogs.sql (Skills + Methods with sample data) Documentation: - .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.) - SHINKAN_PROJECT_SETUP.md (technical setup guide) Server: - Directories created on Pi: /home/lars/docker/shinkan[-dev] - Gitea Runner configured and running Ready for first deployment to dev.shinkan.jinkendo.de version: 0.1.0 date: 2026-04-21
32 KiB
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
- Fähigkeiten (skills) - Basis-Katalog
- Methoden (training_methods) - Methodenkatalog
- Ü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
approvedsetzen
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
- Repository erstellen auf Gitea:
shinkan-jinkendo - Basis-Dateien kopieren aus Mitai:
backend/auth.py,db.py,db_init.pyfrontend/src/context/,utils/api.js
- Migrationen schreiben (001-007)
- Version.py anpassen
- CLAUDE.md erstellen für Shinkan
- Erste Router implementieren:
skills.pymethods.pyexercises.py
Version: 2.0 (komplett überarbeitet)
Stand: 21.04.2026
Autor: Claude Code (basierend auf Anforderungsdokument)