Adding latest changes

This commit is contained in:
Moritz Graf
2026-01-02 17:58:42 +01:00
parent 408cfa87d1
commit 3b836d945e
12 changed files with 245 additions and 91 deletions
+38 -1
View File
@@ -53,6 +53,26 @@ class GarminSync:
pass
return activities
def get_last_sync_date(self) -> Any:
"""Get the date of the latest stored activity."""
activities = self.load_local_activities()
if not activities:
return None
latest_date = None
for act in activities:
start_str = act.get("startTimeLocal")
if start_str:
try:
# Parse "YYYY-MM-DD HH:MM:SS" -> date
d = datetime.strptime(start_str.split(" ")[0], "%Y-%m-%d").date()
if latest_date is None or d > latest_date:
latest_date = d
except ValueError:
continue
return latest_date
def sync_smart(self) -> int:
"""Sync only new activities since the last local one."""
try:
@@ -271,6 +291,22 @@ class GarminSync:
label = k.replace("_", " ").title()
breakdown_list.append({"label": label, "count": v})
# VO2 Max Logic
sorted_acts = sorted(
activities,
key=lambda x: x.get("startTimeLocal", ""),
reverse=True
)
vo2_max = None
for act in sorted_acts:
if "vo2MaxValue" in act:
vo2_max = act["vo2MaxValue"]
break
if "vo2MaxCyclingValue" in act:
vo2_max = act["vo2MaxCyclingValue"]
break
return {
"summary": {
"total_hours": round(current_period["hours"], 1),
@@ -278,5 +314,6 @@ class GarminSync:
"period_label": "Last 7 Days"
},
"breakdown": breakdown_list,
"strength_sessions": strength_count
"strength_sessions": strength_count,
"vo2_max": vo2_max
}
+7 -1
View File
@@ -107,10 +107,16 @@ async def get_settings_status():
env.load_service_env("withings")
env.load_service_env("gemini")
# Get last sync date
sync = GarminSync(None, storage_dir=get_storage_path("garmin"))
last_date = sync.get_last_sync_date()
return {
"garmin": {
"configured": bool(os.getenv("GARMIN_EMAIL") and os.getenv("GARMIN_PASSWORD")),
"email": os.getenv("GARMIN_EMAIL")
"email": os.getenv("GARMIN_EMAIL"),
"last_synced_workout": last_date.strftime("%d.%m.%Y") if last_date else None
},
"withings": {
"configured": bool(os.getenv("WITHINGS_CLIENT_ID") and os.getenv("WITHINGS_CLIENT_SECRET"))
-102
View File
@@ -1,102 +0,0 @@
import json
import os
import sys
from typing import Dict, Any
# Ensure we can import from src
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from common.env_manager import EnvManager
from garmin.workout_manager import WorkoutManager
def load_sample_workout() -> Dict[str, Any]:
"""Create a minimal valid workout for testing."""
return {
"workoutName": "Test Workout",
"description": "Base for AI test",
"sportType": {
"sportTypeId": 1,
"sportTypeKey": "running"
},
"workoutSegments": [
{
"segmentOrder": 1,
"sportType": {
"sportTypeId": 1,
"sportTypeKey": "running"
},
"workoutSteps": [
{
"type": "ExecutableStepDTO",
"stepOrder": 1,
"stepTypeId": 1,
"childStepId": None,
"endConditionId": 2,
"endConditionValue": 300,
"targetTypeId": 1,
"targetValueOne": 10.0,
"targetValueTwo": 12.0
}
]
}
]
}
def test_ai_modification():
print("\n--- Testing AI Workout Modifier ---")
# 1. Setup Environment
root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
env = EnvManager(root_dir)
env.load_service_env("gemini")
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
print("SKIP: GEMINI_API_KEY not found in environment.")
return
# 2. Prepare Data
manager = WorkoutManager()
original_workout = load_sample_workout()
# 3. specific instruction
prompt = "Add a 10 minute cooldown at the end."
print(f"Prompt: {prompt}")
# 4. Execute AI Modification
try:
modified_workout = manager.generate_workout_json(prompt, existing_workout=original_workout)
# 5. Verify Structure (Schema Validation)
errors = manager.validate_workout_json(modified_workout)
if errors:
print(f"FAIL: Schema Validation Errors: {json.dumps(errors, indent=2)}")
print(f"Invalid Workout JSON: {json.dumps(modified_workout, indent=2)}")
return
# 6. Verify Content Logic
segments = modified_workout.get("workoutSegments", [])
if not segments:
print("FAIL: No segments found in modified workout.")
return
steps = segments[0].get("workoutSteps", [])
last_step = steps[-1] if steps else None
# Check if last step looks like a cooldown
# stepTypeId 4 = Cooldown (usually, or we check description/type)
is_cooldown = last_step and (last_step.get("stepTypeId") == 4 or "cool" in str(last_step).lower())
if is_cooldown:
print("PASS: Cooldown added successfully.")
print(f"Modified Steps Count: {len(steps)}")
else:
print("WARN: Could not strictly verify cooldown type, but schema is valid.")
print(f"Last Step: {json.dumps(last_step, indent=2)}")
except Exception as e:
print(f"FAIL: Exception during AI processing: {e}")
if __name__ == "__main__":
test_ai_modification()