277 lines
7.6 KiB
Markdown
277 lines
7.6 KiB
Markdown
|
|
# Phase 0c – Architecture Cleanup & Refactoring
|
|||
|
|
|
|||
|
|
**Datum:** 2026-03-28
|
|||
|
|
**Voraussetzung:** Phase 0b abgeschlossen
|
|||
|
|
**Geschätzter Aufwand:** 12-16 Stunden
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Ziele
|
|||
|
|
|
|||
|
|
Phase 0c fokussiert auf **Architektur-Verbesserungen** und **Structured Data**, ohne neue Features hinzuzufügen.
|
|||
|
|
|
|||
|
|
**Kernprinzip:** Von Formatted Strings zu Structured Data → Basis für Phase 0c Refactoring (Charts/Diagramme).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 1: Dynamic Aggregation Methods (3-4h)
|
|||
|
|
|
|||
|
|
**Problem:** Aggregationsmethoden sind im Frontend hardcoded.
|
|||
|
|
|
|||
|
|
**Lösung:**
|
|||
|
|
|
|||
|
|
### Backend
|
|||
|
|
```python
|
|||
|
|
# backend/routers/goal_types.py
|
|||
|
|
@router.get("/aggregation-methods")
|
|||
|
|
def get_aggregation_methods():
|
|||
|
|
"""Return all available aggregation methods with metadata"""
|
|||
|
|
return [
|
|||
|
|
{
|
|||
|
|
"value": "latest",
|
|||
|
|
"label_de": "Letzter Wert",
|
|||
|
|
"label_en": "Latest",
|
|||
|
|
"description": "Aktuellster Wert aus Tabelle",
|
|||
|
|
"compatible_types": ["DECIMAL", "INTEGER", "FLOAT"],
|
|||
|
|
"requires_numeric": False
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"value": "avg_7d",
|
|||
|
|
"label_de": "Durchschnitt 7 Tage",
|
|||
|
|
"label_en": "7-day average",
|
|||
|
|
"description": "Mittelwert über 7 Tage",
|
|||
|
|
"compatible_types": ["DECIMAL", "INTEGER", "FLOAT"],
|
|||
|
|
"requires_numeric": True
|
|||
|
|
},
|
|||
|
|
# ... alle Methoden aus goal_utils.py
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Implementierung:**
|
|||
|
|
1. Endpoint erstellen (goal_types.py)
|
|||
|
|
2. Auto-Discovery aus goal_utils.py Docstrings oder Introspection
|
|||
|
|
3. Frontend anpassen (AdminGoalTypesPage.jsx)
|
|||
|
|
4. Migration für Backfill (falls nötig)
|
|||
|
|
|
|||
|
|
**Dokumentation:** `.claude/docs/technical/AGGREGATION_METHODS.md` (exists)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 2: Dynamic Placeholder Catalog (4-5h)
|
|||
|
|
|
|||
|
|
**Problem:** Platzhalter sind im Frontend nicht sichtbar/suchbar.
|
|||
|
|
|
|||
|
|
**Lösung:**
|
|||
|
|
|
|||
|
|
### Backend
|
|||
|
|
```python
|
|||
|
|
# backend/routers/prompts.py
|
|||
|
|
@router.get("/placeholders/catalog")
|
|||
|
|
def get_placeholder_catalog(session: dict = Depends(require_auth)):
|
|||
|
|
"""
|
|||
|
|
Return all registered placeholders with metadata.
|
|||
|
|
Enables auto-complete and placeholder selector in frontend.
|
|||
|
|
"""
|
|||
|
|
return {
|
|||
|
|
"categories": [
|
|||
|
|
{
|
|||
|
|
"name": "Profil",
|
|||
|
|
"placeholders": [
|
|||
|
|
{
|
|||
|
|
"key": "name",
|
|||
|
|
"placeholder": "{{name}}",
|
|||
|
|
"description": "Name des Nutzers",
|
|||
|
|
"example": "Lars",
|
|||
|
|
"data_type": "string"
|
|||
|
|
},
|
|||
|
|
# ...
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
# ... weitere Kategorien
|
|||
|
|
],
|
|||
|
|
"total_count": 111
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Frontend
|
|||
|
|
```javascript
|
|||
|
|
// PlaceholderSelector Component
|
|||
|
|
<PlaceholderSelector
|
|||
|
|
onSelect={(placeholder) => insertAtCursor(placeholder)}
|
|||
|
|
categories={fetchedCatalog.categories}
|
|||
|
|
searchable={true}
|
|||
|
|
showExamples={true}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Implementierung:**
|
|||
|
|
1. Endpoint erstellen (nutzt PLACEHOLDER_CATALOG aus placeholder_resolver.py)
|
|||
|
|
2. React Component: PlaceholderSelector
|
|||
|
|
3. Integration in UnifiedPromptModal (Prompt-Editor)
|
|||
|
|
4. Keyboard-Shortcuts (Strg+Space für Auto-Complete)
|
|||
|
|
|
|||
|
|
**Dokumentation:** `.claude/docs/technical/PLACEHOLDER_SYSTEM.md` (neu erstellen)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 3: Calculation Functions → Structured Return (5-7h)
|
|||
|
|
|
|||
|
|
**Problem:** Calculation Functions returnen formatted strings statt strukturierte Daten.
|
|||
|
|
|
|||
|
|
**Lösung:** Refactoring aller Calculation Functions zu structured return.
|
|||
|
|
|
|||
|
|
### Beispiel-Refactoring
|
|||
|
|
|
|||
|
|
**Vorher (Phase 0b):**
|
|||
|
|
```python
|
|||
|
|
def calculate_weight_trend(profile_id: str) -> str:
|
|||
|
|
# ... calculation ...
|
|||
|
|
return f"sinkend (-0.9 kg in 28 Tagen)"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Nachher (Phase 0c):**
|
|||
|
|
```python
|
|||
|
|
def calculate_weight_trend(profile_id: str) -> Dict:
|
|||
|
|
# ... calculation ...
|
|||
|
|
return {
|
|||
|
|
"raw_value": -0.9,
|
|||
|
|
"unit": "kg",
|
|||
|
|
"period_days": 28,
|
|||
|
|
"direction": "decreasing", # "increasing", "stable"
|
|||
|
|
"confidence": "high", # "high", "medium", "low"
|
|||
|
|
"data_points": 15,
|
|||
|
|
"slope_per_day": -0.032
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Formatting Layer
|
|||
|
|
```python
|
|||
|
|
# placeholder_resolver.py
|
|||
|
|
def format_weight_trend(data: Dict) -> str:
|
|||
|
|
"""Format structured weight trend for AI prompts"""
|
|||
|
|
direction_de = {"decreasing": "sinkend", "increasing": "steigend", "stable": "stabil"}
|
|||
|
|
return f"{direction_de[data['direction']]} ({data['raw_value']} {data['unit']} in {data['period_days']} Tagen)"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Betroffene Module
|
|||
|
|
- `calculations/body_metrics.py` (~15 Funktionen)
|
|||
|
|
- `calculations/nutrition_metrics.py` (~12 Funktionen)
|
|||
|
|
- `calculations/activity_metrics.py` (~18 Funktionen)
|
|||
|
|
- `calculations/recovery_metrics.py` (~10 Funktionen)
|
|||
|
|
- `calculations/scores.py` (~8 Funktionen)
|
|||
|
|
|
|||
|
|
**Gesamt:** ~63 Funktionen zu refactoren
|
|||
|
|
|
|||
|
|
### Strategie
|
|||
|
|
1. **Phase 0c/1:** Body Metrics (Proof of Concept)
|
|||
|
|
2. **Phase 0c/2:** Nutrition + Activity
|
|||
|
|
3. **Phase 0c/3:** Recovery + Scores
|
|||
|
|
4. **Phase 0c/4:** Placeholder Resolver anpassen (Formatter)
|
|||
|
|
|
|||
|
|
**Dokumentation:**
|
|||
|
|
- `.claude/docs/technical/CALCULATION_FUNCTIONS.md` (neu)
|
|||
|
|
- `.claude/docs/functional/mitai_jinkendo_konzept_diagramme_auswertungen_v2.md` (exists, Kontext für Chart-System)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Task 4: Stats/Chart Endpoints (Optional, 3-4h)
|
|||
|
|
|
|||
|
|
**Voraussetzung:** Task 3 abgeschlossen
|
|||
|
|
|
|||
|
|
**Lösung:** Separate Endpoints für Chart-Daten (nutzen structured calculation returns)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# backend/routers/stats.py (new)
|
|||
|
|
@router.get("/stats/body/weight-trend")
|
|||
|
|
def get_weight_trend_stats(
|
|||
|
|
days: int = 30,
|
|||
|
|
session: dict = Depends(require_auth)
|
|||
|
|
):
|
|||
|
|
"""Return structured weight trend data for charts"""
|
|||
|
|
trend_data = calculate_weight_trend(session['profile_id'])
|
|||
|
|
return {
|
|||
|
|
"trend": trend_data,
|
|||
|
|
"timeseries": [...], # Daily data points
|
|||
|
|
"projections": {...} # Linear projection
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Implementierung:**
|
|||
|
|
1. Neuer Router: `stats.py`
|
|||
|
|
2. Endpoints für alle Chart-relevanten Metriken
|
|||
|
|
3. OpenAPI Schema (für Frontend Type-Generation)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Abhängigkeiten
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Task 1 (Aggregation Methods) ─┐
|
|||
|
|
├─> Unabhängig, parallel möglich
|
|||
|
|
Task 2 (Placeholder Catalog) ─┘
|
|||
|
|
|
|||
|
|
Task 3 (Structured Returns) ──> Voraussetzung für Task 4
|
|||
|
|
Task 4 (Stats Endpoints) ──> Optional, nutzt Task 3 Results
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Akzeptanzkriterien Phase 0c
|
|||
|
|
|
|||
|
|
**Muss (Must-Have):**
|
|||
|
|
- ✅ Aggregation Methods API-gesteuert
|
|||
|
|
- ✅ Placeholder Catalog API verfügbar
|
|||
|
|
- ✅ Mindestens 1 Modul (Body Metrics) zu structured return refactored
|
|||
|
|
- ✅ Dokumentation für alle 3 Tasks
|
|||
|
|
|
|||
|
|
**Sollte (Should-Have):**
|
|||
|
|
- ✅ Alle Calculation Functions refactored
|
|||
|
|
- ✅ Frontend Placeholder Selector implementiert
|
|||
|
|
- ✅ Stats Endpoints für Body + Nutrition
|
|||
|
|
|
|||
|
|
**Kann (Nice-to-Have):**
|
|||
|
|
- ⚪ Stats Endpoints für alle Module
|
|||
|
|
- ⚪ Chart-Preview in Admin-Panel
|
|||
|
|
- ⚪ TypeScript Type-Generation aus OpenAPI
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Rollback-Plan
|
|||
|
|
|
|||
|
|
Falls Task 3 (Structured Returns) Probleme verursacht:
|
|||
|
|
1. Calculation Functions returnen **beide** Formate:
|
|||
|
|
```python
|
|||
|
|
return {
|
|||
|
|
"formatted": "sinkend (-0.9 kg)", # Backward-compat
|
|||
|
|
"structured": {...} # New format
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
2. Placeholder Resolver nutzt `formatted` weiterhin
|
|||
|
|
3. Stats Endpoints nutzen `structured`
|
|||
|
|
4. Schrittweise Migration ohne Breaking Changes
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Timeline (geschätzt)
|
|||
|
|
|
|||
|
|
**Option A (Serial):** 12-16 Stunden über 2-3 Tage
|
|||
|
|
**Option B (Parallel):** 8-10 Stunden mit 2 Personen
|
|||
|
|
|
|||
|
|
**Empfehlung:** Option A, da Tasks eng verzahnt sind.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Nach Phase 0c
|
|||
|
|
|
|||
|
|
**Enabler für:**
|
|||
|
|
- ✅ Chart/Diagram System (Phase 1)
|
|||
|
|
- ✅ Goal Progress Visualizations
|
|||
|
|
- ✅ Custom Dashboards
|
|||
|
|
- ✅ PDF/Excel Export mit strukturierten Daten
|
|||
|
|
- ✅ API für externe Tools (z.B. Home Assistant)
|
|||
|
|
|
|||
|
|
**Nächste Phase (Phase 1):**
|
|||
|
|
- Chart Library Integration (Recharts/Victory)
|
|||
|
|
- Interactive Dashboards
|
|||
|
|
- Goal Progress Tracking UI
|