feat: Quality-Filter für KI-Pipeline & History (#24)
Backend: - insights.py: KI-Pipeline filtert activity_log nach quality_label - Nur 'excellent', 'good', 'acceptable' (poor wird ausgeschlossen) - NULL-Werte erlaubt (für alte Einträge vor Migration 014) Frontend: - History.jsx: Toggle "Nur qualitativ hochwertige Aktivitäten" - Filter wirkt auf Activity-Statistiken, Charts, Listen - Anzeige: X von Y Activities (wenn gefiltert) Dokumentation: - CLAUDE.md: Feature-Roadmap aktualisiert (Phase 0-2) Closes #24 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9210d051a8
commit
9ec774e956
52
CLAUDE.md
52
CLAUDE.md
|
|
@ -190,19 +190,47 @@ frontend/src/
|
|||
|
||||
## Feature-Roadmap
|
||||
|
||||
> Vollständiges Backlog: `.claude/docs/BACKLOG.md`
|
||||
> Beim Implementieren: verlinkte Dok-Datei zuerst lesen!
|
||||
> 📋 **Detaillierte Roadmap:** `.claude/docs/ROADMAP.md` (Phasen 0-3, Timeline, Abhängigkeiten)
|
||||
> 📚 **Vollständiges Backlog:** `.claude/docs/BACKLOG.md`
|
||||
> 🎯 **Gitea Issues:** http://192.168.2.144:3000/Lars/mitai-jinkendo/issues
|
||||
>
|
||||
> **Beim Implementieren:** verlinkte Dok-Datei zuerst lesen!
|
||||
|
||||
| Version | Feature | Dokumentation |
|
||||
|---------|---------|---------------|
|
||||
| v9c | Membership (aktiv) | `technical/MEMBERSHIP_SYSTEM.md` ✅ |
|
||||
| v9d | Schlaf-Modul | `functional/SLEEP_MODULE.md` (ausstehend) |
|
||||
| v9d | Trainingstypen + HF | `functional/TRAINING_TYPES.md` ✅ |
|
||||
| v9e | Ziele + Vitalwerte | `functional/GOALS_VITALS.md` (ausstehend) |
|
||||
| v9f | KI-Prompt Flexibilisierung | `functional/AI_PROMPTS.md` ✅ |
|
||||
| v9g | Meditation + Selbstwahrnehmung | `functional/MEDITATION.md` (ausstehend) |
|
||||
| v9h | Connectoren + Stripe | ausstehend |
|
||||
| — | Responsive UI | `functional/RESPONSIVE_UI.md` ✅ |
|
||||
### Aktuelle Entwicklung (Phase 0-2, ~10-13 Wochen)
|
||||
|
||||
| Phase | Fokus | Dauer | Gitea Issues |
|
||||
|-------|-------|-------|--------------|
|
||||
| **Phase 0** | Infrastruktur (v9f) | 4-6 Wochen | #24, #28, #29, #30 |
|
||||
| **Phase 1** | Foundation (Charts, Goals) | 2-3 Wochen | #26, #25 |
|
||||
| **Phase 2** | Engagement (Korrelationen, KI) | 3-4 Wochen | #27, #25 |
|
||||
| **Phase 3** | Begleitung (Development Routes) | später | - |
|
||||
|
||||
**Phase 0 Issues:**
|
||||
- #24: Quality-Filter (3h, Quick Win) ← **Start hier**
|
||||
- #28: AI-Prompts Flexibilisierung (16-20h, kritisch)
|
||||
- #29: Abilities-Matrix UI (6-8h)
|
||||
- #30: Responsive UI (8-10h, parallel)
|
||||
|
||||
**Phase 1 Issues:**
|
||||
- #26: Charts erweitern (8-10h)
|
||||
- #25: Ziele-System Basis (10-12h)
|
||||
|
||||
**Phase 2 Issues:**
|
||||
- #27: Korrelationen (6-8h)
|
||||
- #25: Goals KI-Integration (4h)
|
||||
|
||||
### Versions-Übersicht
|
||||
|
||||
| Version | Feature | Dokumentation | Status |
|
||||
|---------|---------|---------------|--------|
|
||||
| v9c | Membership (aktiv) | `technical/MEMBERSHIP_SYSTEM.md` | ✅ Production |
|
||||
| v9d | Schlaf-Modul | `functional/SLEEP_MODULE.md` | ✅ Production |
|
||||
| v9d | Trainingstypen + HF | `functional/TRAINING_TYPES.md` | ✅ Production |
|
||||
| v9e | Ziele + Vitalwerte | `functional/GOALS_VITALS.md` | 🔲 Phase 1 |
|
||||
| v9f | KI-Prompt Flexibilisierung | `functional/AI_PROMPTS.md` | 🔲 Phase 0 |
|
||||
| v9g | Meditation + Selbstwahrnehmung | `functional/MEDITATION.md` | 🔲 Phase 3 |
|
||||
| v9h | Connectoren + Stripe | ausstehend | 🔲 Später |
|
||||
| — | Responsive UI | `functional/RESPONSIVE_UI.md` | 🔲 Phase 0 |
|
||||
|
||||
## Deployment
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,13 @@ def _get_profile_data(pid: str):
|
|||
caliper = [r2d(r) for r in cur.fetchall()]
|
||||
cur.execute("SELECT * FROM nutrition_log WHERE profile_id=%s ORDER BY date DESC LIMIT 90", (pid,))
|
||||
nutrition = [r2d(r) for r in cur.fetchall()]
|
||||
cur.execute("SELECT * FROM activity_log WHERE profile_id=%s ORDER BY date DESC LIMIT 90", (pid,))
|
||||
# Quality-Filter: nur hochwertige Aktivitäten für KI-Analyse (Issue #24)
|
||||
cur.execute("""
|
||||
SELECT * FROM activity_log
|
||||
WHERE profile_id=%s
|
||||
AND (quality_label IN ('excellent', 'good', 'acceptable') OR quality_label IS NULL)
|
||||
ORDER BY date DESC LIMIT 90
|
||||
""", (pid,))
|
||||
activity = [r2d(r) for r in cur.fetchall()]
|
||||
# v9d Phase 2: Sleep, Rest Days, Vitals
|
||||
cur.execute("SELECT * FROM sleep_log WHERE profile_id=%s ORDER BY date DESC LIMIT 30", (pid,))
|
||||
|
|
|
|||
|
|
@ -588,11 +588,16 @@ function NutritionSection({ nutrition, weights, profile, insights, onRequest, lo
|
|||
// ── Activity Section ──────────────────────────────────────────────────────────
|
||||
function ActivitySection({ activities, insights, onRequest, loadingSlug, filterActiveSlugs }) {
|
||||
const [period, setPeriod] = useState(30)
|
||||
const [qualityFilter, setQualityFilter] = useState(false) // Issue #24: Quality-Filter Toggle
|
||||
if (!activities?.length) return (
|
||||
<EmptySection text="Noch keine Aktivitätsdaten." to="/activity" toLabel="Aktivität erfassen"/>
|
||||
)
|
||||
const cutoff = dayjs().subtract(period,'day').format('YYYY-MM-DD')
|
||||
const filtA = activities.filter(d=>period===9999||d.date>=cutoff)
|
||||
// Issue #24: Filter nach Datum UND Quality-Label
|
||||
const filtA = activities.filter(d =>
|
||||
(period===9999 || d.date>=cutoff) &&
|
||||
(!qualityFilter || ['excellent', 'good', 'acceptable'].includes(d.quality_label))
|
||||
)
|
||||
|
||||
const byDate={}
|
||||
filtA.forEach(a=>{ byDate[a.date]=(byDate[a.date]||0)+(a.kcal_active||0) })
|
||||
|
|
@ -620,6 +625,24 @@ function ActivitySection({ activities, insights, onRequest, loadingSlug, filterA
|
|||
<div>
|
||||
<SectionHeader title="🏋️ Aktivität" to="/activity" toLabel="Alle Einträge" lastUpdated={activities[0]?.date}/>
|
||||
<PeriodSelector value={period} onChange={setPeriod}/>
|
||||
|
||||
{/* Issue #24: Quality-Filter Toggle */}
|
||||
<div style={{marginBottom:12,display:'flex',alignItems:'center',gap:8}}>
|
||||
<label style={{display:'flex',alignItems:'center',gap:6,fontSize:12,cursor:'pointer',
|
||||
padding:'6px 10px',borderRadius:8,background:qualityFilter?'var(--accent-light)':'var(--surface2)',
|
||||
border:`1px solid ${qualityFilter?'var(--accent)':'var(--border)'}`,
|
||||
color:qualityFilter?'var(--accent)':'var(--text2)',transition:'all 0.2s'}}>
|
||||
<input type="checkbox" checked={qualityFilter} onChange={e=>setQualityFilter(e.target.checked)}
|
||||
style={{width:14,height:14,cursor:'pointer',accentColor:'var(--accent)'}}/>
|
||||
<span style={{fontWeight:qualityFilter?600:400}}>Nur qualitativ hochwertige Aktivitäten</span>
|
||||
</label>
|
||||
{qualityFilter && (
|
||||
<span style={{fontSize:11,color:'var(--text3)'}}>
|
||||
({filtA.length} von {activities.filter(d=>period===9999||d.date>=cutoff).length})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{display:'flex',gap:6,marginBottom:12}}>
|
||||
{[['Trainings',filtA.length,'var(--text1)'],['Kcal',totalKcal,'#EF9F27'],
|
||||
['Stunden',Math.round(totalMin/60*10)/10,'#378ADD'],
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user