feat: Exercises-Router M:N Zuordnungen
Backend API erweitert um M:N Katalog-Zuordnungen:
- GET /exercises/{id}: Liefert focus_areas[], training_styles[], target_groups[], age_groups_catalog[]
- POST /exercises: Akzeptiert focus_areas_multi[], training_styles_multi[], target_groups_multi[], age_groups_catalog[]
- PUT /exercises/{id}: DELETE+INSERT Pattern für M:N Updates (konsistent mit skills)
Rückwärtskompatibilität:
- Legacy FK-Felder (focus_area_id, training_style_id, training_character_id) bleiben erhalten
- Alte Aufrufe funktionieren unverändert
- Neue M:N Felder sind optional
version: 0.3.1
modules: exercises 0.4.0
This commit is contained in:
parent
c7444ecaec
commit
d67f659e97
|
|
@ -143,6 +143,45 @@ def get_exercise(exercise_id: int, session=Depends(require_auth)):
|
|||
""", (exercise_id,))
|
||||
exercise['media'] = [r2d(r) for r in cur.fetchall()]
|
||||
|
||||
# Get M:N catalog assignments
|
||||
# Focus Areas
|
||||
cur.execute("""
|
||||
SELECT efa.*, fa.name, fa.abbreviation, fa.color
|
||||
FROM exercise_focus_areas efa
|
||||
JOIN focus_areas fa ON efa.focus_area_id = fa.id
|
||||
WHERE efa.exercise_id = %s
|
||||
ORDER BY efa.is_primary DESC, fa.name
|
||||
""", (exercise_id,))
|
||||
exercise['focus_areas'] = [r2d(r) for r in cur.fetchall()]
|
||||
|
||||
# Training Styles
|
||||
cur.execute("""
|
||||
SELECT es.*, ts.name, ts.abbreviation
|
||||
FROM exercise_styles es
|
||||
JOIN training_styles ts ON es.training_style_id = ts.id
|
||||
WHERE es.exercise_id = %s
|
||||
ORDER BY es.is_primary DESC, ts.name
|
||||
""", (exercise_id,))
|
||||
exercise['training_styles'] = [r2d(r) for r in cur.fetchall()]
|
||||
|
||||
# Target Groups
|
||||
cur.execute("""
|
||||
SELECT etg.*, tg.name, tg.description
|
||||
FROM exercise_target_groups etg
|
||||
JOIN target_groups tg ON etg.target_group_id = tg.id
|
||||
WHERE etg.exercise_id = %s
|
||||
ORDER BY etg.is_primary DESC, tg.name
|
||||
""", (exercise_id,))
|
||||
exercise['target_groups'] = [r2d(r) for r in cur.fetchall()]
|
||||
|
||||
# Age Groups
|
||||
cur.execute("""
|
||||
SELECT age_group FROM exercise_age_groups
|
||||
WHERE exercise_id = %s
|
||||
ORDER BY age_group
|
||||
""", (exercise_id,))
|
||||
exercise['age_groups_catalog'] = [r['age_group'] for r in cur.fetchall()]
|
||||
|
||||
return exercise
|
||||
|
||||
|
||||
|
|
@ -227,6 +266,39 @@ def create_exercise(data: dict, session=Depends(require_auth)):
|
|||
skill.get('target_level')
|
||||
))
|
||||
|
||||
# Add M:N catalog assignments if provided
|
||||
# Focus Areas
|
||||
if data.get('focus_areas_multi'):
|
||||
for fa in data['focus_areas_multi']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_focus_areas (exercise_id, focus_area_id, is_primary)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (exercise_id, fa['focus_area_id'], fa.get('is_primary', False)))
|
||||
|
||||
# Training Styles
|
||||
if data.get('training_styles_multi'):
|
||||
for ts in data['training_styles_multi']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_styles (exercise_id, training_style_id, is_primary)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (exercise_id, ts['training_style_id'], ts.get('is_primary', False)))
|
||||
|
||||
# Target Groups
|
||||
if data.get('target_groups_multi'):
|
||||
for tg in data['target_groups_multi']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_target_groups (exercise_id, target_group_id, is_primary)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (exercise_id, tg['target_group_id'], tg.get('is_primary', False)))
|
||||
|
||||
# Age Groups
|
||||
if data.get('age_groups_catalog'):
|
||||
for age_group in data['age_groups_catalog']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_age_groups (exercise_id, age_group)
|
||||
VALUES (%s, %s)
|
||||
""", (exercise_id, age_group))
|
||||
|
||||
conn.commit()
|
||||
|
||||
return get_exercise(exercise_id, session)
|
||||
|
|
@ -313,6 +385,43 @@ def update_exercise(exercise_id: int, data: dict, session=Depends(require_auth))
|
|||
skill.get('target_level')
|
||||
))
|
||||
|
||||
# Update M:N catalog assignments if provided
|
||||
# Focus Areas
|
||||
if 'focus_areas_multi' in data:
|
||||
cur.execute("DELETE FROM exercise_focus_areas WHERE exercise_id = %s", (exercise_id,))
|
||||
for fa in data['focus_areas_multi']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_focus_areas (exercise_id, focus_area_id, is_primary)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (exercise_id, fa['focus_area_id'], fa.get('is_primary', False)))
|
||||
|
||||
# Training Styles
|
||||
if 'training_styles_multi' in data:
|
||||
cur.execute("DELETE FROM exercise_styles WHERE exercise_id = %s", (exercise_id,))
|
||||
for ts in data['training_styles_multi']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_styles (exercise_id, training_style_id, is_primary)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (exercise_id, ts['training_style_id'], ts.get('is_primary', False)))
|
||||
|
||||
# Target Groups
|
||||
if 'target_groups_multi' in data:
|
||||
cur.execute("DELETE FROM exercise_target_groups WHERE exercise_id = %s", (exercise_id,))
|
||||
for tg in data['target_groups_multi']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_target_groups (exercise_id, target_group_id, is_primary)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (exercise_id, tg['target_group_id'], tg.get('is_primary', False)))
|
||||
|
||||
# Age Groups
|
||||
if 'age_groups_catalog' in data:
|
||||
cur.execute("DELETE FROM exercise_age_groups WHERE exercise_id = %s", (exercise_id,))
|
||||
for age_group in data['age_groups_catalog']:
|
||||
cur.execute("""
|
||||
INSERT INTO exercise_age_groups (exercise_id, age_group)
|
||||
VALUES (%s, %s)
|
||||
""", (exercise_id, age_group))
|
||||
|
||||
conn.commit()
|
||||
|
||||
return get_exercise(exercise_id, session)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Shinkan Jinkendo Version Information
|
||||
|
||||
APP_VERSION = "0.3.0"
|
||||
APP_VERSION = "0.3.1"
|
||||
BUILD_DATE = "2026-04-23"
|
||||
DB_SCHEMA_VERSION = "20260423"
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ MODULE_VERSIONS = {
|
|||
"groups": "0.1.0",
|
||||
"skills": "0.1.0",
|
||||
"methods": "0.1.0",
|
||||
"exercises": "0.3.0", # Updated: M:N Beziehungen
|
||||
"exercises": "0.4.0", # Updated: M:N API-Integration
|
||||
"training_units": "0.1.0",
|
||||
"training_programs": "0.1.0",
|
||||
"planning": "0.1.0",
|
||||
|
|
@ -22,6 +22,17 @@ MODULE_VERSIONS = {
|
|||
}
|
||||
|
||||
CHANGELOG = [
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"date": "2026-04-23",
|
||||
"changes": [
|
||||
"Feature: Exercises-Router unterstützt M:N Zuordnungen",
|
||||
"API: GET /exercises/{id} liefert focus_areas[], training_styles[], target_groups[], age_groups_catalog[]",
|
||||
"API: POST/PUT /exercises akzeptiert focus_areas_multi[], training_styles_multi[], target_groups_multi[], age_groups_catalog[]",
|
||||
"Pattern: DELETE+INSERT für M:N Updates (konsistent mit skills)",
|
||||
"Backward-Compatible: Legacy FK-Felder (focus_area_id, training_style_id) bleiben erhalten",
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.3.0",
|
||||
"date": "2026-04-23",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Shinkan Jinkendo Frontend Version
|
||||
|
||||
export const APP_VERSION = "0.3.0"
|
||||
export const APP_VERSION = "0.3.1"
|
||||
export const BUILD_DATE = "2026-04-23"
|
||||
|
||||
export const PAGE_VERSIONS = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user