mitai-jinkendo/backend/profile_templates.py
Lars d7145874cf
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
feat: Training Type Profiles Phase 2.1 - Backend Profile Management (#15)
Admin endpoints for profile configuration:
- Extended TrainingTypeCreate/Update models with profile field
- Added profile column to all SELECT queries
- Profile templates for Running, Meditation, Strength Training
- Template endpoints: list, get, apply
- Profile stats endpoint (configured/unconfigured count)

New file: profile_templates.py
- TEMPLATE_RUNNING: Endurance-focused with HR zones
- TEMPLATE_MEDITATION: Mental-focused (low HR ≤ instead of ≥)
- TEMPLATE_STRENGTH: Strength-focused

API Endpoints:
- GET /api/admin/training-types/profiles/templates
- GET /api/admin/training-types/profiles/templates/{key}
- POST /api/admin/training-types/{id}/profile/apply-template
- GET /api/admin/training-types/profiles/stats

Next: Frontend Admin-UI (ProfileEditor component)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:50:40 +01:00

451 lines
15 KiB
Python

"""
Training Type Profile Templates
Pre-configured profiles for common training types.
Issue: #15
Date: 2026-03-23
"""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# TEMPLATE: LAUFEN (Running) - Ausdauer-fokussiert
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TEMPLATE_RUNNING = {
"version": "1.0",
"name": "Laufen (Standard)",
"description": "Ausdauerlauf mit Herzfrequenz-Zonen",
"rule_sets": {
"minimum_requirements": {
"enabled": True,
"pass_strategy": "weighted_score",
"pass_threshold": 0.6,
"rules": [
{
"parameter": "duration_min",
"operator": "gte",
"value": 15,
"weight": 5,
"optional": False,
"reason": "Mindestens 15 Minuten für Trainingseffekt"
},
{
"parameter": "avg_hr",
"operator": "gte",
"value": 100,
"weight": 3,
"optional": False,
"reason": "Puls muss für Ausdauerreiz erhöht sein"
},
{
"parameter": "distance_km",
"operator": "gte",
"value": 1.0,
"weight": 2,
"optional": False,
"reason": "Mindestens 1 km Distanz"
}
]
},
"intensity_zones": {
"enabled": True,
"zones": [
{
"id": "regeneration",
"name": "Regeneration",
"color": "#4CAF50",
"effect": "Aktive Erholung",
"target_duration_min": 30,
"rules": [
{
"parameter": "avg_hr_percent",
"operator": "between",
"value": [50, 60]
}
]
},
{
"id": "grundlagenausdauer",
"name": "Grundlagenausdauer",
"color": "#2196F3",
"effect": "Fettverbrennung, aerobe Basis",
"target_duration_min": 45,
"rules": [
{
"parameter": "avg_hr_percent",
"operator": "between",
"value": [60, 70]
}
]
},
{
"id": "entwicklungsbereich",
"name": "Entwicklungsbereich",
"color": "#FF9800",
"effect": "VO2max-Training, Laktattoleranz",
"target_duration_min": 30,
"rules": [
{
"parameter": "avg_hr_percent",
"operator": "between",
"value": [70, 80]
}
]
},
{
"id": "schwellentraining",
"name": "Schwellentraining",
"color": "#F44336",
"effect": "Anaerobe Schwelle, Wettkampftempo",
"target_duration_min": 20,
"rules": [
{
"parameter": "avg_hr_percent",
"operator": "between",
"value": [80, 90]
}
]
}
]
},
"training_effects": {
"enabled": True,
"default_effects": {
"primary_abilities": [
{
"category": "konditionell",
"ability": "ausdauer",
"intensity": 5
}
],
"secondary_abilities": [
{
"category": "konditionell",
"ability": "schnelligkeit",
"intensity": 2
},
{
"category": "koordinativ",
"ability": "rhythmus",
"intensity": 3
},
{
"category": "psychisch",
"ability": "willenskraft",
"intensity": 4
}
]
},
"metabolic_focus": ["aerobic", "fat_oxidation"],
"muscle_groups": ["legs", "core", "cardiovascular"]
},
"periodization": {
"enabled": True,
"frequency": {
"per_week_optimal": 3,
"per_week_max": 5
},
"recovery": {
"min_hours_between": 24
}
},
"performance_indicators": {
"enabled": False
},
"safety": {
"enabled": True,
"warnings": [
{
"parameter": "avg_hr_percent",
"operator": "gt",
"value": 95,
"severity": "high",
"message": "Herzfrequenz zu hoch - Überbelastungsrisiko"
},
{
"parameter": "duration_min",
"operator": "gt",
"value": 180,
"severity": "medium",
"message": "Sehr lange Einheit - achte auf Regeneration"
}
]
}
}
}
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# TEMPLATE: MEDITATION - Mental-fokussiert (≤ statt ≥ bei HR!)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TEMPLATE_MEDITATION = {
"version": "1.0",
"name": "Meditation (Standard)",
"description": "Mentales Training mit niedrigem Puls",
"rule_sets": {
"minimum_requirements": {
"enabled": True,
"pass_strategy": "weighted_score",
"pass_threshold": 0.6,
"rules": [
{
"parameter": "duration_min",
"operator": "gte",
"value": 5,
"weight": 5,
"optional": False,
"reason": "Mindestens 5 Minuten für Entspannungseffekt"
},
{
"parameter": "avg_hr",
"operator": "lte",
"value": 80,
"weight": 4,
"optional": False,
"reason": "Niedriger Puls zeigt Entspannung an"
}
]
},
"intensity_zones": {
"enabled": True,
"zones": [
{
"id": "deep_relaxation",
"name": "Tiefenentspannung",
"color": "#4CAF50",
"effect": "Parasympathikus-Aktivierung",
"target_duration_min": 10,
"rules": [
{
"parameter": "avg_hr_percent",
"operator": "between",
"value": [35, 45]
}
]
},
{
"id": "light_meditation",
"name": "Leichte Meditation",
"color": "#2196F3",
"effect": "Achtsamkeit, Fokus",
"target_duration_min": 15,
"rules": [
{
"parameter": "avg_hr_percent",
"operator": "between",
"value": [45, 55]
}
]
}
]
},
"training_effects": {
"enabled": True,
"default_effects": {
"primary_abilities": [
{
"category": "kognitiv",
"ability": "konzentration",
"intensity": 5
},
{
"category": "psychisch",
"ability": "stressresistenz",
"intensity": 5
}
],
"secondary_abilities": [
{
"category": "kognitiv",
"ability": "wahrnehmung",
"intensity": 4
},
{
"category": "psychisch",
"ability": "selbstvertrauen",
"intensity": 3
}
]
},
"metabolic_focus": ["parasympathetic_activation"],
"muscle_groups": []
},
"periodization": {
"enabled": True,
"frequency": {
"per_week_optimal": 5,
"per_week_max": 7
},
"recovery": {
"min_hours_between": 0
}
},
"performance_indicators": {
"enabled": False
},
"safety": {
"enabled": True,
"warnings": [
{
"parameter": "avg_hr",
"operator": "gt",
"value": 100,
"severity": "medium",
"message": "Herzfrequenz zu hoch für Meditation - bist du wirklich entspannt?"
}
]
}
}
}
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# TEMPLATE: KRAFTTRAINING - Kraft-fokussiert
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TEMPLATE_STRENGTH = {
"version": "1.0",
"name": "Krafttraining (Standard)",
"description": "Krafttraining mit moderater Herzfrequenz",
"rule_sets": {
"minimum_requirements": {
"enabled": True,
"pass_strategy": "weighted_score",
"pass_threshold": 0.5,
"rules": [
{
"parameter": "duration_min",
"operator": "gte",
"value": 20,
"weight": 5,
"optional": False,
"reason": "Mindestens 20 Minuten für Muskelreiz"
},
{
"parameter": "kcal_active",
"operator": "gte",
"value": 100,
"weight": 2,
"optional": True,
"reason": "Mindest-Kalorienverbrauch"
}
]
},
"intensity_zones": {
"enabled": False
},
"training_effects": {
"enabled": True,
"default_effects": {
"primary_abilities": [
{
"category": "konditionell",
"ability": "kraft",
"intensity": 5
}
],
"secondary_abilities": [
{
"category": "koordinativ",
"ability": "differenzierung",
"intensity": 3
},
{
"category": "psychisch",
"ability": "willenskraft",
"intensity": 4
}
]
},
"metabolic_focus": ["anaerobic", "muscle_growth"],
"muscle_groups": ["full_body"]
},
"periodization": {
"enabled": True,
"frequency": {
"per_week_optimal": 3,
"per_week_max": 5
},
"recovery": {
"min_hours_between": 48
}
},
"performance_indicators": {
"enabled": False
},
"safety": {
"enabled": True,
"warnings": []
}
}
}
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# TEMPLATE REGISTRY
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TEMPLATES = {
"running": {
"name_de": "Laufen",
"name_en": "Running",
"icon": "🏃",
"categories": ["cardio", "running"],
"template": TEMPLATE_RUNNING
},
"meditation": {
"name_de": "Meditation",
"name_en": "Meditation",
"icon": "🧘",
"categories": ["geist", "meditation"],
"template": TEMPLATE_MEDITATION
},
"strength": {
"name_de": "Krafttraining",
"name_en": "Strength Training",
"icon": "💪",
"categories": ["kraft", "krafttraining"],
"template": TEMPLATE_STRENGTH
}
}
def get_template(template_key: str) -> dict:
"""Get profile template by key."""
template_info = TEMPLATES.get(template_key)
if not template_info:
return None
return template_info["template"]
def list_templates() -> list:
"""List all available templates."""
return [
{
"key": key,
"name_de": info["name_de"],
"name_en": info["name_en"],
"icon": info["icon"],
"categories": info["categories"]
}
for key, info in TEMPLATES.items()
]