feat: improve date formatting using local start times and add sport-specific emoji icons to activity summaries
This commit is contained in:
@@ -7,6 +7,7 @@ from strava_mcp_server.utils import (
|
||||
format_date_iso,
|
||||
format_date_human,
|
||||
format_duration_markdown,
|
||||
get_sport_emoji,
|
||||
)
|
||||
|
||||
|
||||
@@ -59,13 +60,14 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
||||
essential_data = []
|
||||
for a in activities:
|
||||
start_date_raw = a.get("start_date")
|
||||
start_date_local_raw = a.get("start_date_local") or start_date_raw
|
||||
essential_data.append(
|
||||
{
|
||||
"id": a["id"],
|
||||
"name": a["name"],
|
||||
"sport_type": a.get("sport_type") or a.get("type"),
|
||||
"start_date": format_date_iso(start_date_raw),
|
||||
"start_date_local": format_date_human(start_date_raw),
|
||||
"start_date_local": format_date_human(start_date_local_raw),
|
||||
"distance": f"{a.get('distance', 0) / 1000:.2f} km",
|
||||
"moving_time": f"{a.get('moving_time', 0) / 60:.1f} min",
|
||||
"total_elevation_gain": f"{a.get('total_elevation_gain', 0):.0f} m",
|
||||
@@ -80,12 +82,8 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
||||
)
|
||||
else:
|
||||
markdown_summary = f"### 🚴 Aktivitäten (Seite {page})\n"
|
||||
markdown_summary += (
|
||||
"| Datum | Sport | Name | Distanz | Zeit | Höhenmeter | Ø HR |\n"
|
||||
)
|
||||
markdown_summary += (
|
||||
"|-------|-------|------|---------|------|------------|------|\n"
|
||||
)
|
||||
markdown_summary += "| 📅 Datum | 🏃 Icon | 🏷️ Name | 📐 Distanz | ⏱️ Zeit | 🏔️ Höhenmeter | ❤️ Ø HR |\n"
|
||||
markdown_summary += "|----------|--------|--------|-----------|--------|--------------|--------|\n"
|
||||
for a, raw_act in zip(essential_data, activities):
|
||||
hr = (
|
||||
f"{a['average_heartrate']:.0f} bpm"
|
||||
@@ -93,7 +91,8 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
||||
else "-"
|
||||
)
|
||||
time_md = format_duration_markdown(raw_act.get("moving_time"))
|
||||
markdown_summary += f"| {a['start_date_local']} | {a['sport_type']} | {a['name']} | {a['distance']} | {time_md} | {a['total_elevation_gain']} | {hr} |\n"
|
||||
sport_icon = get_sport_emoji(a["sport_type"])
|
||||
markdown_summary += f"| {a['start_date_local']} | {sport_icon} | {a['name']} | {a['distance']} | {time_md} | {a['total_elevation_gain']} | {hr} |\n"
|
||||
|
||||
return [
|
||||
_user_text(markdown_summary.strip()),
|
||||
@@ -125,7 +124,9 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
||||
|
||||
name = activity.get("name", "N/A")
|
||||
sport = activity.get("sport_type") or activity.get("type", "N/A")
|
||||
date = format_date_human(activity.get("start_date"))
|
||||
date = format_date_human(
|
||||
activity.get("start_date_local") or activity.get("start_date")
|
||||
)
|
||||
dist = f"{activity.get('distance', 0) / 1000:.2f} km"
|
||||
time = format_duration_markdown(activity.get("moving_time"))
|
||||
elev = f"{activity.get('total_elevation_gain', 0):.0f} m"
|
||||
|
||||
@@ -59,10 +59,13 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
||||
)
|
||||
time_elapsed = format_duration_markdown(e.get("elapsed_time"))
|
||||
time_moving = format_duration_markdown(e.get("moving_time"))
|
||||
date_local = format_date_human(
|
||||
e.get("start_date_local") or e.get("start_date")
|
||||
)
|
||||
md = f"""### 🏅 Segment-Effort: {data["name"]}
|
||||
| Feld | Wert |
|
||||
|------|------|
|
||||
| Datum | {format_date_human(data["start_date"])} |
|
||||
| Datum | {date_local} |
|
||||
| Distanz | {data["distance"]} |
|
||||
| Zeit (gesamt) | {time_elapsed} |
|
||||
| Fahrzeit | {time_moving} |
|
||||
@@ -124,7 +127,10 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
||||
pr = f"#{effort['pr_rank']}" if effort["pr_rank"] else "-"
|
||||
elapsed_md = format_duration_markdown(raw_eff.get("elapsed_time"))
|
||||
moving_md = format_duration_markdown(raw_eff.get("moving_time"))
|
||||
md += f"| {format_date_human(effort['start_date'])} | {elapsed_md} | {moving_md} | {pr} |\n"
|
||||
date_local = format_date_human(
|
||||
raw_eff.get("start_date_local") or raw_eff.get("start_date")
|
||||
)
|
||||
md += f"| {date_local} | {elapsed_md} | {moving_md} | {pr} |\n"
|
||||
return [
|
||||
_user_text(md.strip()),
|
||||
_resource(f"internal://segments/{segment_id}/efforts", data),
|
||||
|
||||
@@ -125,3 +125,39 @@ def format_duration_markdown(seconds_input: Optional[float | int]) -> str:
|
||||
return f"{minutes}min"
|
||||
except Exception:
|
||||
return str(seconds_input)
|
||||
|
||||
|
||||
def get_sport_emoji(sport_type: Optional[str]) -> str:
|
||||
"""
|
||||
Returns an emoji corresponding to the given sport type.
|
||||
"""
|
||||
if not sport_type:
|
||||
return "❓"
|
||||
|
||||
sport_lower = sport_type.lower()
|
||||
if "run" in sport_lower:
|
||||
return "🏃"
|
||||
elif "ride" in sport_lower or "bike" in sport_lower:
|
||||
return "🚴"
|
||||
elif "swim" in sport_lower:
|
||||
return "🏊"
|
||||
elif "walk" in sport_lower:
|
||||
return "🚶"
|
||||
elif "hike" in sport_lower:
|
||||
return "🥾"
|
||||
elif "ski" in sport_lower or "snowboard" in sport_lower:
|
||||
return "🎿"
|
||||
elif "row" in sport_lower or "canoe" in sport_lower or "kayak" in sport_lower:
|
||||
return "🚣"
|
||||
elif (
|
||||
"weight" in sport_lower or "workout" in sport_lower or "crossfit" in sport_lower
|
||||
):
|
||||
return "🏋️"
|
||||
elif "yoga" in sport_lower:
|
||||
return "🧘"
|
||||
elif "surf" in sport_lower:
|
||||
return "🏄"
|
||||
elif "skate" in sport_lower:
|
||||
return "🛹"
|
||||
else:
|
||||
return "🏃"
|
||||
|
||||
@@ -9,6 +9,7 @@ from strava_mcp_server.utils import (
|
||||
format_duration_text,
|
||||
format_duration_table,
|
||||
format_duration_markdown,
|
||||
get_sport_emoji,
|
||||
)
|
||||
|
||||
|
||||
@@ -188,3 +189,37 @@ class TestFormatDurationMarkdown:
|
||||
|
||||
def test_fractional_input(self):
|
||||
assert format_duration_markdown(2700.2) == "45min"
|
||||
|
||||
|
||||
class TestGetSportEmoji:
|
||||
def test_none_returns_question_mark(self):
|
||||
assert get_sport_emoji(None) == "❓"
|
||||
|
||||
def test_empty_string_returns_question_mark(self):
|
||||
assert get_sport_emoji("") == "❓"
|
||||
|
||||
def test_running(self):
|
||||
assert get_sport_emoji("Run") == "🏃"
|
||||
assert get_sport_emoji("VirtualRun") == "🏃"
|
||||
|
||||
def test_riding(self):
|
||||
assert get_sport_emoji("Ride") == "🚴"
|
||||
assert get_sport_emoji("VirtualRide") == "🚴"
|
||||
assert get_sport_emoji("MountainBikeRide") == "🚴"
|
||||
|
||||
def test_swimming(self):
|
||||
assert get_sport_emoji("Swim") == "🏊"
|
||||
|
||||
def test_walking_and_hiking(self):
|
||||
assert get_sport_emoji("Walk") == "🚶"
|
||||
assert get_sport_emoji("Hike") == "🥾"
|
||||
|
||||
def test_workout(self):
|
||||
assert get_sport_emoji("Workout") == "🏋️"
|
||||
assert get_sport_emoji("WeightTraining") == "🏋️"
|
||||
|
||||
def test_yoga(self):
|
||||
assert get_sport_emoji("Yoga") == "🧘"
|
||||
|
||||
def test_unknown_returns_default(self):
|
||||
assert get_sport_emoji("SomeOddSport") == "🏃"
|
||||
|
||||
Reference in New Issue
Block a user