fix(cron): resolve Garmin sync payload mismatch, harden cronjobs, and update wiki submodule
This commit is contained in:
@@ -35,13 +35,7 @@ Führe den nächtlichen Garmin Connect Sync durch.
|
|||||||
Vorgehen:
|
Vorgehen:
|
||||||
1. Führe den Garmin-Sync-Befehl aus:
|
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
|
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:
|
2. Finale Antwort:
|
||||||
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:
|
|
||||||
- Bei Erfolg: Gib exakt 'NO_REPLY' aus (intentional silence, kein Telegram-Spam in der Nacht).
|
- 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.
|
- Nur bei Authentifizierungsfehlern: Informiere kurz via Telegram (Thema #Sport), dass ein Garmin-Re-Auth nötig ist, oder starte den Re-Auth-Flow.
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
+1
-1
Submodule knowledge-wiki updated: 720c5cf74b...71ee240511
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field, model_validator
|
||||||
|
|
||||||
class QueryRequest(BaseModel):
|
class QueryRequest(BaseModel):
|
||||||
query: str = Field(..., description="Read-only SQL query to execute in DuckDB")
|
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")
|
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")
|
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")
|
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):
|
class StatusResponse(BaseModel):
|
||||||
status: str
|
status: str
|
||||||
|
|||||||
@@ -177,3 +177,29 @@ def test_garmin_sync_and_duplicate_guard(client: TestClient):
|
|||||||
assert r_sync.status_code == 200
|
assert r_sync.status_code == 200
|
||||||
assert r_sync.json()["status"] == "success"
|
assert r_sync.json()["status"] == "success"
|
||||||
assert r_sync.json()["synced_daily"] == 1
|
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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user