Compare commits

..

65 Commits

Author SHA1 Message Date
c1f45d3ba1 Merge pull request 'BLS Stoffgruppen, Lebensmittel verwalten, etc' (#108) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m10s
Build Test / pytest-backend (push) Successful in 5s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 21s
Reviewed-on: #108
2026-09-14 13:15:10 +02:00
037e8bab47 Merge pull request 'Food Enhancesment - Rezepte. Listen, BLS Support' (#107) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m6s
Build Test / pytest-backend (push) Successful in 9s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 22s
Reviewed-on: #107
2026-09-13 15:25:21 +02:00
67b508f09b Merge pull request 'BugFix manuelle Eingabe TRainings' (#105) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m9s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 21s
Reviewed-on: #105
2026-07-22 11:14:33 +02:00
fc2905f9da Merge pull request 'feat: add DashboardConfigurePage to the frontend' (#104) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m6s
Build Test / pytest-backend (push) Successful in 9s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 19s
Reviewed-on: #104
2026-04-29 22:04:34 +02:00
0638a7d76b Merge pull request 'pdf generator' (#103) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m0s
Build Test / pytest-backend (push) Successful in 5s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 21s
Reviewed-on: #103
2026-04-29 21:55:58 +02:00
2dbfd95cca Merge pull request 'Widget Update' (#102) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m2s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
Reviewed-on: #102
2026-04-23 09:12:58 +02:00
1cf3d5997d Merge pull request 'C1-C4 Diagramme' (#101) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 53s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #101
2026-04-21 08:14:07 +02:00
5cac1ededd Merge pull request 'Refaktor Level 2b' (#100) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 56s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
Reviewed-on: #100
2026-04-20 17:31:02 +02:00
41d809c68c Merge pull request 'Überartbeitung History - neue Gesamtübersicht' (#99) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m4s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #99
2026-04-20 14:39:54 +02:00
d66eadf88f Merge pull request 'feat: enhance recovery metrics and dashboard with sleep debt calculations and improved visualizations' (#98) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 59s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
Reviewed-on: #98
2026-04-20 12:52:20 +02:00
a1b458d228 Merge pull request 'Neue Aufbereitung Fitness Verlauf' (#97) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 55s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #97
2026-04-20 11:47:52 +02:00
6743814904 Merge pull request 'Fitness History + recovery' (#96) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 54s
Build Test / pytest-backend (push) Successful in 5s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #96
2026-04-20 08:46:25 +02:00
6ef07a77a5 Merge pull request 'Fitness historie' (#95) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 57s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #95
2026-04-20 08:26:46 +02:00
aa6644f44b Merge pull request 'Ernährungsasuwertungen' (#94) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 58s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 18s
Reviewed-on: #94
2026-04-19 21:02:28 +02:00
319a9cfafd Merge pull request 'Verlauf - Körper' (#93) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 59s
Build Test / pytest-backend (push) Successful in 9s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #93
2026-04-19 16:32:32 +02:00
0ab91416d0 Merge pull request 'feat: add reference values snapshot endpoints and data layer functions' (#92) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m1s
Build Test / pytest-backend (push) Successful in 12s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #92
2026-04-19 15:34:20 +02:00
5fbaa7cc8b Merge pull request 'feat: add relaxed arm circumference measurement and update related features' (#91) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 56s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
Reviewed-on: #91
2026-04-19 10:44:18 +02:00
9b53a4ba68 Merge pull request 'feat: enhance photo upload and management features' (#90) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m0s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #90
2026-04-19 10:20:04 +02:00
9720cb57ec Merge pull request 'feat: implement photo management features in the application' (#89) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m1s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #89
2026-04-19 09:24:08 +02:00
1e1605f878 Merge pull request 'Optimierung Platzhalter Umfang' (#88) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m0s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #88
2026-04-18 10:54:36 +02:00
4575bb23ee Merge pull request 'Bug-Fixing Analyse Fehler' (#87) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 55s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #87
2026-04-18 09:54:12 +02:00
a62c952097 Merge pull request 'Erste Version Platzhalter EAV' (#86) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 52s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #86
2026-04-17 21:52:13 +02:00
a7859f0050 Merge pull request 'Erste Version - Universal CSV Importer für EAV und activity_log' (#85) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m1s
Build Test / pytest-backend (push) Successful in 8s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #85
2026-04-15 11:46:30 +02:00
19a88e4f97 Merge pull request 'feat: Enhance goal progress tracking and display' (#84) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 53s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #84
2026-04-14 11:39:58 +02:00
424270f0e8 Merge pull request 'bug fix und Debug Workflow' (#83) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m2s
Build Test / pytest-backend (push) Successful in 8s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #83
2026-04-13 18:15:06 +02:00
f34e46b04f Merge pull request 'Async Workflow' (#82) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m3s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #82
2026-04-13 11:58:01 +02:00
46b703e5a2 Merge pull request 'Bug Fixes. Workflow Engine' (#81) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m0s
Build Test / pytest-backend (push) Successful in 8s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #81
2026-04-12 14:08:43 +02:00
25c4ecfd48 Merge pull request 'Verbesserung für Tests von Prompts' (#80) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m2s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #80
2026-04-12 11:58:07 +02:00
72eb94d186 Merge pull request 'feat: Improve float parsing logic for enhanced accuracy in numeric conversions' (#79) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 55s
Build Test / pytest-backend (push) Successful in 8s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
Reviewed-on: #79
2026-04-12 07:30:44 +02:00
4d81ea2cf3 Merge pull request 'feat: Update Gitea issues index and enhance data layer metrics' (#78) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m4s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #78
2026-04-11 22:17:18 +02:00
caeed3fbaa Merge pull request 'Platzhalter finalisiert - Option |d und Option |x implementiert' (#77) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m3s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #77
2026-04-11 22:10:10 +02:00
b5f745fc3e Merge pull request 'Workflow 1.1 bug fixes' (#74) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m5s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #74
2026-04-11 15:39:23 +02:00
2443b5ac3a Merge pull request 'Workflow Analysen V1.1' (#73) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 51s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #73
2026-04-11 12:38:08 +02:00
8b731313df Merge pull request 'Workflow V1' (#72) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m1s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #72
2026-04-11 10:59:39 +02:00
12893cba78 Merge pull request 'Universal CSV Importer' (#70) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 56s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #70
2026-04-11 07:06:47 +02:00
09439ad1a5 Merge pull request 'Indvidual Dashboard V0.9' (#67) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m0s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #67
2026-04-08 10:56:02 +02:00
75736dadec Merge pull request 'feat: Integrate caliper data enrichment and weight loading in API responses' (#64) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 57s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Reviewed-on: #64
2026-04-06 06:10:39 +02:00
8d56c352fc Merge pull request 'Bug Fixes' (#63) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #63
2026-04-05 17:54:00 +02:00
ebea9946a7 Merge pull request 'feat: Add admin access denial alert to Dashboard with timeout handling' (#62) from develop into main
All checks were successful
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Deploy Production / deploy (push) Successful in 55s
Reviewed-on: #62
2026-04-05 11:29:03 +02:00
9fb04af7df Merge pull request 'Responsive Gui - partially Workflow' (#61) from develop into main
Some checks failed
Deploy Production / deploy (push) Failing after 1s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #61
2026-04-05 11:27:43 +02:00
3f5b9b8b58 Merge pull request 'Platzhaltersystem für Ernährung, Körper und Aktivity' (#60) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #60
2026-04-03 09:28:13 +02:00
5bbea036c2 Merge pull request 'develop' (#59) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 53s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Reviewed-on: #59
2026-04-03 08:23:48 +02:00
c762495b6f Merge pull request 'Goals -System refactored - Platzhaltersystem enhanced (als draft)' (#53) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Reviewed-on: #53
2026-03-31 11:46:47 +02:00
979e734bd9 Merge pull request 'Bug Fix' (#52) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Reviewed-on: #52
2026-03-27 22:16:18 +01:00
79cb3e0100 Merge pull request 'V 0.9h dynamic focus area system' (#51) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #51
2026-03-27 21:14:40 +01:00
9d22e7e8af Merge pull request 'Goalsystem V1' (#50) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Reviewed-on: #50
2026-03-27 17:40:50 +01:00
f46c367c27 Merge pull request 'Flexibles KI Prompt System' (#48) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Reviewed-on: #48
2026-03-26 14:49:47 +01:00
ac4c6760d7 Merge pull request 'globaler Filter für Qualitätsgates von Trainings' (#41) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #41
2026-03-24 08:44:22 +01:00
5a6a140dfd Merge pull request 'Bugfixes: Vitals Import (German columns + decimal values)' (#23) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 56s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
2026-03-23 16:52:27 +01:00
931012c16b Merge pull request 'v9d Phase 2d: Vitals Module Refactoring (Baseline + Blood Pressure)' (#22) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
2026-03-23 16:27:03 +01:00
03f4b871a9 Merge pull request 'Production Release: RestDays Widget + Trainingstyp Fix' (#16) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Merge pull request #16: Production Release
2026-03-23 09:24:17 +01:00
601fc80178 Merge pull request 'WP 9c Phase 1' (#12) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #12
2026-03-22 14:14:34 +01:00
3ff2a1bf45 Merge pull request 'Abschluss 9c' (#11) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #11
2026-03-21 21:20:10 +01:00
51aa57f304 Merge pull request 'Final Feature 9c' (#10) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Reviewed-on: #10
2026-03-21 12:41:41 +01:00
ca4411f30f Merge pull request 'fix: update version string to v9c' (#9) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #9
2026-03-21 09:00:55 +01:00
b551365fb5 Merge pull request 'Membership-System und Bug Fixing (inkl. Nutrition)' (#8) from develop into main
Some checks failed
Deploy Production / deploy (push) Failing after 1s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
Reviewed-on: #8
2026-03-21 08:48:56 +01:00
272c123952 Merge pull request '9c Phase 2' (#6) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
Reviewed-on: #6
2026-03-19 14:59:25 +01:00
9387670a7b Merge pull request '9c datatables' (#5) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #5
2026-03-19 13:00:31 +01:00
d4a8401a6a Merge pull request 'Refactored Main.py' (#4) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #4
2026-03-19 11:44:32 +01:00
aaf88a6f12 Merge pull request 'fix: Migration-Fehler - meas_id Spalte in ai_insights' (#3) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #3
2026-03-19 08:41:13 +01:00
b789c1bd44 Merge pull request 'bug Fix Login' (#2) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
Reviewed-on: #2
2026-03-19 08:29:48 +01:00
5062aa8068 docker-compose.yml aktualisiert
All checks were successful
Deploy Production / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
2026-03-19 08:21:12 +01:00
8a042589e7 docker-compose.yml aktualisiert
Some checks failed
Deploy Production / deploy (push) Failing after 1s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 12s
2026-03-19 08:19:19 +01:00
3898b5ad45 docker-compose.yml aktualisiert
Some checks failed
Deploy Production / deploy (push) Failing after 38s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
2026-03-19 08:06:02 +01:00
9d15336144 Merge pull request 'Version 9b' (#1) from develop into main
Some checks failed
Deploy Production / deploy (push) Failing after 42s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
Reviewed-on: #1
2026-03-19 08:04:01 +01:00
10 changed files with 28 additions and 221 deletions

View File

@ -48,8 +48,6 @@ Gelernte Zuordnungen und Listen lassen sich als **JSON sichern** und auf einer a
Eigene Einträge des Nutzers sind **persönlich** (`manual_user`). Der Admin legt **zentrale** Einträge für alle an (`manual_admin`). Beide speichern **dieselben Stoffwerte** wie BLS-Lebensmittel (`food_attribute_values`, immer pro 100 g). Beim Anlegen: Makros plus Suche nach weiteren Stoffen (EPA, DHA, Omega-3). Etikett „pro 8 ml“ → Portionsgröße in g angeben, Umrechnung auf 100 g erfolgt serverseitig. Einheit des Stoffs beachten (oft **g**, Etikett in mg: 1100 mg = 1,1 g). Fehlt ein Stoff im Katalog: Nutzer kann ihn selbst anlegen oder Admin → Stoffe & Attribute (Bezeichnung änderbar, Schlüssel fest). In die Makro-Haupttabelle (`nutrition_log`) fließen nur ENERCC→kcal, PROT625→protein_g, FAT→fat_g, CHO→carbs_g (Wert je 100 g × gegessene g / 100). Andere Stoffe gehen nach `nutrition_daily_nutrients`. Eigene Einträge des Nutzers sind **persönlich** (`manual_user`). Der Admin legt **zentrale** Einträge für alle an (`manual_admin`). Beide speichern **dieselben Stoffwerte** wie BLS-Lebensmittel (`food_attribute_values`, immer pro 100 g). Beim Anlegen: Makros plus Suche nach weiteren Stoffen (EPA, DHA, Omega-3). Etikett „pro 8 ml“ → Portionsgröße in g angeben, Umrechnung auf 100 g erfolgt serverseitig. Einheit des Stoffs beachten (oft **g**, Etikett in mg: 1100 mg = 1,1 g). Fehlt ein Stoff im Katalog: Nutzer kann ihn selbst anlegen oder Admin → Stoffe & Attribute (Bezeichnung änderbar, Schlüssel fest). In die Makro-Haupttabelle (`nutrition_log`) fließen nur ENERCC→kcal, PROT625→protein_g, FAT→fat_g, CHO→carbs_g (Wert je 100 g × gegessene g / 100). Andere Stoffe gehen nach `nutrition_daily_nutrients`.
**Listen am Tag:** Eine Mitai-Liste (z. B. Quark-Skyr-Blaubeeren) ist zugeordnet, sobald die Tagebuchzeile `recipe_id` hat — nicht erst bei einem einzelnen `food_id`. In der Tagesübersicht erscheint „Liste: …“. Sind alle Zutaten gemappt, lassen sich die berechneten Katalog-Makros (Summe der Zutaten, skaliert auf die gegessene Menge) per Knopf in `nutrition_log` übernehmen.
**Nachträglich ändern:** Unter Ernährung → Tab **Lebensmittel** (persönliche Liste + zentrale Admin-Einträge, Suche). Persönliche Einträge legt und ändert der Owner. Zentrale Einträge liest jeder; **ändert nur ein Admin** (gleiche Liste, Zeile oder Bearbeiten). Beim Zuordnen gilt die Reihenfolge persönlich → zentral → BLS. Globale Namens-Mappings (Admin) gelten für alle, dürfen aber nicht auf ein privates Nutzer-Lebensmittel zeigen. Offizielle BLS-Einträge bleiben fest. Beim Speichern gelten die Felder als Werte je 100 g, außer die Portionsgröße wird bewusst auf die Etikett-Portion gesetzt. Betroffene Tagebuch-Tage werden im Hintergrund neu berechnet. Admin → Lebensmittelkatalog → Reiter **Manuell** zeigt **Zentral** und **Persönlich** getrennt; beide sind dort änderbar. **Nachträglich ändern:** Unter Ernährung → Tab **Lebensmittel** (persönliche Liste + zentrale Admin-Einträge, Suche). Persönliche Einträge legt und ändert der Owner. Zentrale Einträge liest jeder; **ändert nur ein Admin** (gleiche Liste, Zeile oder Bearbeiten). Beim Zuordnen gilt die Reihenfolge persönlich → zentral → BLS. Globale Namens-Mappings (Admin) gelten für alle, dürfen aber nicht auf ein privates Nutzer-Lebensmittel zeigen. Offizielle BLS-Einträge bleiben fest. Beim Speichern gelten die Felder als Werte je 100 g, außer die Portionsgröße wird bewusst auf die Etikett-Portion gesetzt. Betroffene Tagebuch-Tage werden im Hintergrund neu berechnet. Admin → Lebensmittelkatalog → Reiter **Manuell** zeigt **Zentral** und **Persönlich** getrennt; beide sind dort änderbar.
Weitere Quellen (USDA, Schweizer Nährwertdatenbank) kommen später als zusätzliche Katalogherkunft, nicht als zweite Wertetabelle. Weitere Quellen (USDA, Schweizer Nährwertdatenbank) kommen später als zusätzliche Katalogherkunft, nicht als zweite Wertetabelle.

View File

@ -48,5 +48,4 @@ FDDB: Items persistieren; `nutrition_log` nur bei leerem Tag oder laut Policy /
- `GET /nutrition/ingredient-names` — Distinct-Zutatennamen aller Listen inkl. Mapping-Status. Listendialog mappt manuell (`upsertMyFoodMapping`) an der Zeile. - `GET /nutrition/ingredient-names` — Distinct-Zutatennamen aller Listen inkl. Mapping-Status. Listendialog mappt manuell (`upsertMyFoodMapping`) an der Zeile.
- Mapping-Schreiben und Nährwert-Rebuild sind getrennte Transaktionen; Rebuild läuft nach der API-Antwort im Hintergrund (UI bleibt bedienbar) - Mapping-Schreiben und Nährwert-Rebuild sind getrennte Transaktionen; Rebuild läuft nach der API-Antwort im Hintergrund (UI bleibt bedienbar)
- `food_name_mappings.grams_per_unit` / `source_unit` (Migration **064**) - `food_name_mappings.grams_per_unit` / `source_unit` (Migration **064**)
- `GET /nutrition/items` liefert `recipe_name_de` + `recipe_complete`. `GET /nutrition/days/{date}/macros` vergleicht bestehende vs. Katalog-Tagesmakros; `POST …/apply-catalog-macros` schreibt die berechnete Summe nach `nutrition_log`. Listen-Save verknüpft passende Tagebuchzeilen (`items_linked`).
- `GET/POST /api/nutrition/food-knowledge` — portable JSON (`mitai-food-knowledge` v1): manuelle Foods, Mappings (über `bls_code` / Name, keine UUIDs), Listen. Import löst Katalog auf dem Zielsystem auf (BLS muss dort importiert sein). - `GET/POST /api/nutrition/food-knowledge` — portable JSON (`mitai-food-knowledge` v1): manuelle Foods, Mappings (über `bls_code` / Name, keine UUIDs), Listen. Import löst Katalog auf dem Zielsystem auf (BLS muss dort importiert sein).

View File

@ -10,10 +10,6 @@ LEADING_QTY_RE = re.compile(
r"^\s*\d+(?:[.,]\d+)?\s*(?:g|kg|ml|l|stück|stk|st\.?|portion(?:en)?)\b[\s,.:\-–]*", r"^\s*\d+(?:[.,]\d+)?\s*(?:g|kg|ml|l|stück|stk|st\.?|portion(?:en)?)\b[\s,.:\-–]*",
re.IGNORECASE, re.IGNORECASE,
) )
TRAILING_QTY_RE = re.compile(
r"[\s,.:\-–]+\d+(?:[.,]\d+)?\s*(?:g|kg|ml|l)\s*$",
re.IGNORECASE,
)
MULTISPACE_RE = re.compile(r"\s+") MULTISPACE_RE = re.compile(r"\s+")
DECIMAL_IN_NAME_RE = re.compile(r"(\d),(\d)") DECIMAL_IN_NAME_RE = re.compile(r"(\d),(\d)")
@ -31,10 +27,8 @@ def normalize_food_name(raw: str | None) -> str:
s = strip_leading_quantity(raw) s = strip_leading_quantity(raw)
if not s: if not s:
return "" return ""
s = TRAILING_QTY_RE.sub("", s)
s = s.replace("-", " ")
s = DECIMAL_IN_NAME_RE.sub(r"\1.\2", s) s = DECIMAL_IN_NAME_RE.sub(r"\1.\2", s)
return MULTISPACE_RE.sub(" ", s).strip().lower() return s.lower()
def item_name_keys(raw: str | None, stored_norm: str | None = None) -> set[str]: def item_name_keys(raw: str | None, stored_norm: str | None = None) -> set[str]:

View File

@ -326,12 +326,10 @@ def save_recipe(cur, profile_id: str, rec: dict[str, Any], recipe_id: str | None
qty["quantity_raw"], qty["quantity_g"], qty["quantity_amount"], qty["source_unit"], i, qty["quantity_raw"], qty["quantity_g"], qty["quantity_amount"], qty["source_unit"], i,
), ),
) )
linked, dates = link_recipes_to_items(cur, profile_id) link_recipes_to_items(cur, profile_id)
saved = get_recipe(cur, profile_id, rid) saved = get_recipe(cur, profile_id, rid)
if not saved: if not saved:
raise ValueError("Rezept konnte nicht gelesen werden") raise ValueError("Rezept konnte nicht gelesen werden")
saved["items_linked"] = linked
saved["dates_linked"] = dates
return saved return saved

View File

@ -446,65 +446,6 @@ def _touch_item_counts(cur, profile_id: str, day: str, sums: dict) -> None:
) )
def day_macro_preview(cur, profile_id: str, day: date | str) -> dict[str, Any]:
sums = compute_day_macro_sums(cur, profile_id, day)
catalog = sums.get("catalog")
existing = sums.get("existing")
mapped = int(sums.get("mapped_item_count") or 0)
return {
"date": str(day)[:10],
"existing": existing,
"catalog": catalog,
"fddb": sums.get("fddb"),
"mapped_item_count": mapped,
"unmapped_item_count": int(sums.get("unmapped_item_count") or 0),
"catalog_origin": sums.get("catalog_origin"),
"can_apply": bool(catalog) and mapped > 0,
"differs": bool(existing and catalog and macros_differ(existing, catalog)),
}
def apply_day_catalog_macros(cur, profile_id: str, day: date | str) -> dict[str, Any]:
preview = day_macro_preview(cur, profile_id, day)
if not preview["can_apply"]:
raise ValueError("Keine aus Zuordnungen berechenbaren Tagesmakros")
cur.execute(
"SELECT source FROM nutrition_log WHERE profile_id = %s AND date = %s",
(profile_id, day),
)
row = cur.fetchone()
source = (row.get("source") if row else None) or "csv"
apply_nutrition_day_macros(
cur,
profile_id,
day,
preview["catalog"],
macro_origin=preview.get("catalog_origin") or "bls",
source=source,
confirm=True,
)
rebuild_daily_nutrients(cur, profile_id, day)
preview = day_macro_preview(cur, profile_id, day)
preview["applied"] = True
return preview
def annotate_nutrition_item(cur, profile_id: str, row: dict[str, Any]) -> dict[str, Any]:
item = dict(row)
item["id"] = str(item["id"])
if item.get("food_id"):
item["food_id"] = str(item["food_id"])
if item.get("recipe_id"):
item["recipe_id"] = str(item["recipe_id"])
from data_layer.food_recipes import catalog_macros_for_recipe
item["recipe_complete"] = catalog_macros_for_recipe(
cur, profile_id, item["recipe_id"], item.get("quantity_g")
) is not None
else:
item["recipe_complete"] = False
return item
def resolve_choice_macros(sums: dict[str, Any], choice: str) -> tuple[dict[str, float], str]: def resolve_choice_macros(sums: dict[str, Any], choice: str) -> tuple[dict[str, float], str]:
if choice == "existing": if choice == "existing":
return sums["existing"], "user_confirmed" return sums["existing"], "user_confirmed"

View File

@ -339,11 +339,9 @@ def list_nutrition_items(
if date: if date:
cur.execute( cur.execute(
""" """
SELECT i.*, f.name_de AS food_name_de, f.bls_code, f.catalog_kind, SELECT i.*, f.name_de AS food_name_de, f.bls_code, f.catalog_kind
r.name_raw AS recipe_name_de
FROM nutrition_items i FROM nutrition_items i
LEFT JOIN food_catalog f ON f.id = i.food_id LEFT JOIN food_catalog f ON f.id = i.food_id
LEFT JOIN food_recipes r ON r.id = i.recipe_id
WHERE i.profile_id=%s AND i.date=%s WHERE i.profile_id=%s AND i.date=%s
ORDER BY i.logged_at NULLS LAST, i.source_name_raw ORDER BY i.logged_at NULLS LAST, i.source_name_raw
""", """,
@ -352,19 +350,16 @@ def list_nutrition_items(
else: else:
cur.execute( cur.execute(
""" """
SELECT i.*, f.name_de AS food_name_de, f.bls_code, f.catalog_kind, SELECT i.*, f.name_de AS food_name_de, f.bls_code, f.catalog_kind
r.name_raw AS recipe_name_de
FROM nutrition_items i FROM nutrition_items i
LEFT JOIN food_catalog f ON f.id = i.food_id LEFT JOIN food_catalog f ON f.id = i.food_id
LEFT JOIN food_recipes r ON r.id = i.recipe_id
WHERE i.profile_id=%s WHERE i.profile_id=%s
ORDER BY i.date DESC, i.logged_at NULLS LAST ORDER BY i.date DESC, i.logged_at NULLS LAST
LIMIT %s LIMIT %s
""", """,
(pid, min(limit, 500)), (pid, min(limit, 500)),
) )
from data_layer.nutrition_items import annotate_nutrition_item return [r2d(r) for r in cur.fetchall()]
return [annotate_nutrition_item(cur, pid, r2d(r)) for r in cur.fetchall()]
@router.get("/unmapped") @router.get("/unmapped")
@ -427,13 +422,10 @@ def create_food_recipe(
session: dict = Depends(require_auth), session: dict = Depends(require_auth),
): ):
from data_layer.food_recipes import save_recipe from data_layer.food_recipes import save_recipe
from data_layer.nutrition_items import schedule_daily_nutrient_rebuild
pid = get_pid(x_profile_id) pid = get_pid(x_profile_id)
try: try:
with get_db() as conn: with get_db() as conn:
saved = save_recipe(get_cursor(conn), pid, body.model_dump()) return save_recipe(get_cursor(conn), pid, body.model_dump())
schedule_daily_nutrient_rebuild(pid, saved.get("dates_linked") or [], "Liste angelegt")
return saved
except ValueError as e: except ValueError as e:
raise HTTPException(400, str(e)) from e raise HTTPException(400, str(e)) from e
@ -446,13 +438,10 @@ def update_food_recipe(
session: dict = Depends(require_auth), session: dict = Depends(require_auth),
): ):
from data_layer.food_recipes import save_recipe from data_layer.food_recipes import save_recipe
from data_layer.nutrition_items import schedule_daily_nutrient_rebuild
pid = get_pid(x_profile_id) pid = get_pid(x_profile_id)
try: try:
with get_db() as conn: with get_db() as conn:
saved = save_recipe(get_cursor(conn), pid, body.model_dump(), recipe_id) return save_recipe(get_cursor(conn), pid, body.model_dump(), recipe_id)
schedule_daily_nutrient_rebuild(pid, saved.get("dates_linked") or [], "Liste geändert")
return saved
except KeyError: except KeyError:
raise HTTPException(404, "Rezept nicht gefunden") from None raise HTTPException(404, "Rezept nicht gefunden") from None
except ValueError as e: except ValueError as e:
@ -623,35 +612,6 @@ def resolve_import_conflicts(
return {"applied": applied} return {"applied": applied}
@router.get("/days/{day}/macros")
def get_day_macros(
day: str,
x_profile_id: Optional[str] = Header(default=None),
session: dict = Depends(require_auth),
):
from data_layer.nutrition_items import day_macro_preview
pid = get_pid(x_profile_id)
with get_db() as conn:
return day_macro_preview(get_cursor(conn), pid, day)
@router.post("/days/{day}/apply-catalog-macros")
def apply_catalog_macros_for_day(
day: str,
x_profile_id: Optional[str] = Header(default=None),
session: dict = Depends(require_auth),
):
from data_layer.nutrition_items import apply_day_catalog_macros
pid = get_pid(x_profile_id)
try:
with get_db() as conn:
return apply_day_catalog_macros(get_cursor(conn), pid, day)
except ValueError as e:
raise HTTPException(400, str(e)) from e
@router.put("/days/{day}/mark") @router.put("/days/{day}/mark")
def upsert_day_mark( def upsert_day_mark(
day: str, day: str,

View File

@ -137,11 +137,6 @@ def test_list_name_matches_despite_quantity_prefix():
assert not names_match_list("Haferflocken", "haferflocken", "müsli mix") assert not names_match_list("Haferflocken", "haferflocken", "müsli mix")
def test_list_name_matches_hyphen_and_trailing_qty():
assert names_match_list("Quark-Skyr Blaubeeren", "quark-skyr blaubeeren", "quark skyr blaubeeren")
assert names_match_list("Quark skyr Blaubeeren 250 g", None, "Quark skyr Blaubeeren")
def test_unmapped_meta_includes_recent_ingredients(): def test_unmapped_meta_includes_recent_ingredients():
batches = [ batches = [
[], [],

View File

@ -20,7 +20,7 @@ MODULE_VERSIONS = {
"circumference": "1.0.1", "circumference": "1.0.1",
"caliper": "1.0.1", "caliper": "1.0.1",
"activity": "1.2.1", # Legacy CSV import: activity_entries feature enforcement "activity": "1.2.1", # Legacy CSV import: activity_entries feature enforcement
"nutrition": "1.3.8", # Listen in Tagesübersicht; Katalog-Makros auf den Tag übernehmen "nutrition": "1.3.7", # Sichtbarkeit persönlich vs. zentral im Mapping
"tandoor": "1.0.1", # Router-Import ohne future-annotations (Backend startet) "tandoor": "1.0.1", # Router-Import ohne future-annotations (Backend startet)
"bls": "1.0.13", # Stoffe gruppiert und sortiert (BLS-Nährstoffgruppen) "bls": "1.0.13", # Stoffe gruppiert und sortiert (BLS-Nährstoffgruppen)

View File

@ -197,63 +197,25 @@ function EntryForm({ onSaved }) {
} }
// ── Data Tab (Editable Entry List) ─────────────────────────────────────────── // ── Data Tab (Editable Entry List) ───────────────────────────────────────────
function itemAssignment(it) { function DayItems({ date }) {
if (it.food_name_de) return { ok: true, text: `→ ${it.food_name_de}` }
if (it.recipe_name_de || it.recipe_id) {
const name = it.recipe_name_de || 'Liste'
if (it.recipe_complete) return { ok: true, text: `→ Liste: ${name}` }
return { ok: false, text: `→ Liste: ${name} (Zutaten noch offen)` }
}
return { ok: false, text: '· noch nicht zugeordnet' }
}
function formatMacros(m) {
if (!m) return '—'
return `${Math.round(m.kcal || 0)} kcal · P ${Math.round(m.protein_g || 0)}g · F ${Math.round(m.fat_g || 0)}g · KH ${Math.round(m.carbs_g || 0)}g`
}
function DayItems({ date, onChanged }) {
const [items, setItems] = useState(null) const [items, setItems] = useState(null)
const [macros, setMacros] = useState(null)
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [error, setError] = useState(null) const [error, setError] = useState(null)
const [applying, setApplying] = useState(false)
const load = async () => {
const [nextItems, nextMacros] = await Promise.all([
nutritionApi.listNutritionItems(date),
nutritionApi.getNutritionDayMacros(date).catch(() => null),
])
setItems(nextItems)
setMacros(nextMacros)
}
const toggle = async () => { const toggle = async () => {
if (open) { if (open) {
setOpen(false) setOpen(false)
return return
} }
if (!items) {
try { try {
setError(null) setItems(await nutritionApi.listNutritionItems(date))
await load() } catch (e) {
setError(e.message)
return
}
}
setOpen(true) setOpen(true)
} catch (e) {
setError(e.message)
}
}
const applyCatalog = async () => {
setApplying(true)
setError(null)
try {
const next = await nutritionApi.applyNutritionDayCatalogMacros(date)
setMacros(next)
onChanged?.()
} catch (e) {
setError(e.message)
} finally {
setApplying(false)
}
} }
return ( return (
@ -263,54 +225,16 @@ function DayItems({ date, onChanged }) {
</button> </button>
{error && <p style={{ color: 'var(--danger)', fontSize: 12, margin: '6px 0 0' }}>{error}</p>} {error && <p style={{ color: 'var(--danger)', fontSize: 12, margin: '6px 0 0' }}>{error}</p>}
{open && Array.isArray(items) && ( {open && Array.isArray(items) && (
<>
<ul style={{ margin: '8px 0 0', paddingLeft: 18, fontSize: 13, color: 'var(--text1)' }}> <ul style={{ margin: '8px 0 0', paddingLeft: 18, fontSize: 13, color: 'var(--text1)' }}>
{items.length === 0 && <li style={{ color: 'var(--text3)' }}>Keine Zeilen für diesen Tag</li>} {items.length === 0 && <li style={{ color: 'var(--text3)' }}>Keine Zeilen für diesen Tag</li>}
{items.map((it) => { {items.map((it) => (
const asg = itemAssignment(it)
return (
<li key={it.id}> <li key={it.id}>
{it.source_name_raw} {it.source_name_raw}
{it.quantity_raw ? ` · ${it.quantity_raw}` : ''} {it.quantity_raw ? ` · ${it.quantity_raw}` : ''}
{' '} {it.food_name_de ? ` → ${it.food_name_de}` : ' · noch nicht zugeordnet'}
<span style={{ color: asg.ok ? 'var(--accent-dark)' : 'var(--text3)' }}>{asg.text}</span>
</li> </li>
) ))}
})}
</ul> </ul>
{macros && (
<div style={{ marginTop: 10, padding: '8px 10px', background: 'var(--surface2)', borderRadius: 8, fontSize: 12 }}>
<div>Aktuell in der Tagesübersicht: {formatMacros(macros.existing)}</div>
<div style={{ marginTop: 4 }}>
Aus Zuordnung berechnet
{macros.catalog_origin ? ` (${macros.catalog_origin})` : ''}:
{' '}{formatMacros(macros.catalog)}
{macros.unmapped_item_count
? ` · ${macros.unmapped_item_count} Zeile${macros.unmapped_item_count === 1 ? '' : 'n'} noch ohne vollständige Zuordnung (dort FDDB)`
: ''}
</div>
{macros.can_apply && macros.differs && (
<button
type="button"
className="btn btn-primary"
style={{ marginTop: 8, fontSize: 12 }}
disabled={applying}
onClick={applyCatalog}
>
{applying ? 'Übernehme…' : 'Berechnete Makros in den Tag übernehmen'}
</button>
)}
{macros.can_apply && !macros.differs && (
<div style={{ marginTop: 6, color: 'var(--accent-dark)' }}>Tagesmakros entsprechen bereits der Zuordnung.</div>
)}
{!macros.can_apply && (
<div style={{ marginTop: 6, color: 'var(--text3)' }}>
Noch keine berechenbaren Katalog-Makros. Liste verknüpfen und alle Zutaten zuordnen.
</div>
)}
</div>
)}
</>
)} )}
</div> </div>
) )
@ -452,7 +376,7 @@ function DataTab({ entries, onUpdate }) {
</div> </div>
)} )}
{e.item_count > 0 ? ( {e.item_count > 0 ? (
<DayItems date={e.date} onChanged={onUpdate} /> <DayItems date={e.date} />
) : ( ) : (
<div style={{fontSize:12, color:'var(--text3)', marginTop:6}}>Keine einzelnen Lebensmittelzeilen</div> <div style={{fontSize:12, color:'var(--text3)', marginTop:6}}>Keine einzelnen Lebensmittelzeilen</div>
)} )}

View File

@ -281,8 +281,6 @@ export const api = {
return readJsonResponse(r) return readJsonResponse(r)
}, },
listNutritionMarks: () => req('/nutrition/marks'), listNutritionMarks: () => req('/nutrition/marks'),
getNutritionDayMacros: (date) => req(`/nutrition/days/${encodeURIComponent(date)}/macros`),
applyNutritionDayCatalogMacros: (date) => req(`/nutrition/days/${encodeURIComponent(date)}/apply-catalog-macros`, { method: 'POST' }),
putNutritionDayMark: (date, d) => req(`/nutrition/days/${date}/mark`, jput(d)), putNutritionDayMark: (date, d) => req(`/nutrition/days/${date}/mark`, jput(d)),
deleteNutritionDayMark: (date) => req(`/nutrition/days/${date}/mark`, {method:'DELETE'}), deleteNutritionDayMark: (date) => req(`/nutrition/days/${date}/mark`, {method:'DELETE'}),
resolveNutritionConflicts: (decisions) => req('/nutrition/import-conflicts/resolve', json({decisions})), resolveNutritionConflicts: (decisions) => req('/nutrition/import-conflicts/resolve', json({decisions})),