From a34fa7ee08cb89e6a7e0408def122e2f26ff62a2 Mon Sep 17 00:00:00 2001 From: Moritz Graf Date: Sun, 18 Oct 2026 20:33:26 +0200 Subject: [PATCH] fix(cron): resolve Garmin sync payload mismatch, harden cronjobs, and update wiki submodule --- k8s/openclaw/setup-garmin-cron.sh | 8 +----- knowledge-wiki | 2 +- projects/duckdb-backend/app/models.py | 16 +++++++++++- .../duckdb-backend/tests/test_api_curated.py | 26 +++++++++++++++++++ 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/k8s/openclaw/setup-garmin-cron.sh b/k8s/openclaw/setup-garmin-cron.sh index cc984b2..02ddfd5 100755 --- a/k8s/openclaw/setup-garmin-cron.sh +++ b/k8s/openclaw/setup-garmin-cron.sh @@ -35,13 +35,7 @@ Führe den nächtlichen Garmin Connect Sync durch. Vorgehen: 1. Führe den Garmin-Sync-Befehl aus: uv run /home/node/mop-skills/mop-skills/skills/datastore/scripts/garmin_sync.py --days 2 --data-dir /home/node/.openclaw/wiki/main/data -2. Aktualisiere die gerenderten Markdown-Ansichten: - PYTHONPATH=/home/node/mop-skills/mop-skills/skills/datastore/scripts uv run /home/node/mop-skills/mop-skills/skills/datastore/scripts/render_views.py --data-dir /home/node/.openclaw/wiki/main/data --wiki-dir /home/node/.openclaw/wiki/main -3. Aktualisiere das persönliche Health & Nutrition Dashboard (moritz.html): - uv run /home/node/mop-skills/mop-skills/skills/datastore/scripts/update_moritz_website.py --data-dir /home/node/.openclaw/wiki/main/data -4. Git Commit & Push im Knowledge Wiki: - cd /home/node/.openclaw/wiki/main && git add data/garmin/ entities/nutrition-log/ public/ && git diff --staged --quiet || (git commit -m "chore(garmin): automated nightly mirror [skip ci]" && git push origin main) -5. Finale Antwort: +2. Finale Antwort: - Bei Erfolg: Gib exakt 'NO_REPLY' aus (intentional silence, kein Telegram-Spam in der Nacht). - Nur bei Authentifizierungsfehlern: Informiere kurz via Telegram (Thema #Sport), dass ein Garmin-Re-Auth nötig ist, oder starte den Re-Auth-Flow. EOF diff --git a/knowledge-wiki b/knowledge-wiki index 720c5cf..71ee240 160000 --- a/knowledge-wiki +++ b/knowledge-wiki @@ -1 +1 @@ -Subproject commit 720c5cf74bf9625c88411649ff9ca2724c18119a +Subproject commit 71ee240511cf37dff3193c32facb4395a0e9a26d diff --git a/projects/duckdb-backend/app/models.py b/projects/duckdb-backend/app/models.py index 1c930f3..5695217 100644 --- a/projects/duckdb-backend/app/models.py +++ b/projects/duckdb-backend/app/models.py @@ -1,7 +1,7 @@ from __future__ import annotations from typing import Any, Dict, List, Optional -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, model_validator class QueryRequest(BaseModel): query: str = Field(..., description="Read-only SQL query to execute in DuckDB") @@ -44,6 +44,20 @@ class GarminSyncRequest(BaseModel): daily: List[Dict[str, Any]] = Field(default_factory=list, description="Garmin daily summary records") sleep: List[Dict[str, Any]] = Field(default_factory=list, description="Garmin sleep records") activities: List[Dict[str, Any]] = Field(default_factory=list, description="Garmin activity records") + daily_summary: Optional[Dict[str, Any]] = None + sleep_data: Optional[Dict[str, Any]] = None + + @model_validator(mode="before") + @classmethod + def normalize_aliases(cls, data: Any) -> Any: + if isinstance(data, dict): + # Normalize daily_summary dict to daily list if daily is missing/empty + if "daily_summary" in data and data["daily_summary"] and not data.get("daily"): + data["daily"] = [data["daily_summary"]] + # Normalize sleep_data dict to sleep list if sleep is missing/empty + if "sleep_data" in data and data["sleep_data"] and not data.get("sleep"): + data["sleep"] = [data["sleep_data"]] + return data class StatusResponse(BaseModel): status: str diff --git a/projects/duckdb-backend/tests/test_api_curated.py b/projects/duckdb-backend/tests/test_api_curated.py index bb9f38b..467b4f5 100644 --- a/projects/duckdb-backend/tests/test_api_curated.py +++ b/projects/duckdb-backend/tests/test_api_curated.py @@ -177,3 +177,29 @@ def test_garmin_sync_and_duplicate_guard(client: TestClient): assert r_sync.status_code == 200 assert r_sync.json()["status"] == "success" assert r_sync.json()["synced_daily"] == 1 + + # Test alias support (daily_summary and sleep_data dicts) + garmin_alias_payload = { + "date": "2026-10-16", + "daily_summary": { + "calendarDate": "2026-10-16", + "totalKilocalories": 2550, + "activeKilocalories": 650, + "bmrKilocalories": 1900, + "totalSteps": 12000, + "restingHeartRate": 50, + }, + "sleep_data": { + "dailySleepDTO": { + "calendarDate": "2026-10-16", + "sleepTimeSeconds": 28800, + } + }, + "activities": [], + } + r_alias = client.post("/duckdb-api/v1/garmin/sync", json=garmin_alias_payload) + assert r_alias.status_code == 200 + assert r_alias.json()["status"] == "success" + assert r_alias.json()["synced_daily"] == 1 + assert r_alias.json()["synced_sleep"] == 1 +