Yorrick Jansen commited on
Commit
be4141a
·
1 Parent(s): a8088c9

Remove segment leaderboard

Browse files
strava_mcp/api.py CHANGED
@@ -9,7 +9,6 @@ from strava_mcp.models import (
9
  Activity,
10
  DetailedActivity,
11
  ErrorResponse,
12
- Leaderboard,
13
  SegmentEffort,
14
  )
15
 
@@ -202,62 +201,3 @@ class StravaAPI:
202
 
203
  return segment_efforts
204
 
205
- async def get_segment_leaderboard(
206
- self,
207
- segment_id: int,
208
- gender: str | None = None,
209
- age_group: int | None = None,
210
- weight_class: int | None = None,
211
- following: bool | None = None,
212
- club_id: int | None = None,
213
- date_range: int | None = None,
214
- context_entries: int | None = None,
215
- page: int = 1,
216
- per_page: int = 30,
217
- ) -> Leaderboard:
218
- """Get the leaderboard for a given segment.
219
-
220
- Args:
221
- segment_id: The ID of the segment
222
- gender: Filter by gender ('M' or 'F')
223
- age_group: Filter by age group
224
- weight_class: Filter by weight class
225
- following: Filter by friends of the authenticated athlete
226
- club_id: Filter by club
227
- date_range: Filter by date range
228
- context_entries: Number of context entries
229
- page: Page number
230
- per_page: Number of items per page
231
-
232
- Returns:
233
- The segment leaderboard
234
- """
235
- params = {"page": page, "per_page": per_page}
236
-
237
- if gender:
238
- # Convert gender string to integer code
239
- gender_code = 0 # Default
240
- if gender.upper() == "M":
241
- gender_code = 1
242
- elif gender.upper() == "F":
243
- gender_code = 2
244
- params["gender"] = gender_code
245
- if age_group:
246
- params["age_group"] = int(age_group)
247
- if weight_class:
248
- params["weight_class"] = int(weight_class)
249
- if following is not None:
250
- params["following"] = 1 if following else 0
251
- if club_id:
252
- params["club_id"] = club_id
253
- if date_range:
254
- params["date_range"] = int(date_range)
255
- if context_entries:
256
- params["context_entries"] = context_entries
257
-
258
- response = await self._request(
259
- "GET", f"/segments/{segment_id}/leaderboard", params=params
260
- )
261
- data = response.json()
262
-
263
- return Leaderboard(**data)
 
9
  Activity,
10
  DetailedActivity,
11
  ErrorResponse,
 
12
  SegmentEffort,
13
  )
14
 
 
201
 
202
  return segment_efforts
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
strava_mcp/models.py CHANGED
@@ -137,44 +137,6 @@ class SegmentEffort(BaseModel):
137
  segment: Segment = Field(..., description="The segment")
138
 
139
 
140
- class Leaderboard(BaseModel):
141
- """Represents a Strava segment leaderboard."""
142
-
143
- entry_count: int = Field(
144
- ..., description="The total number of entries for this leaderboard"
145
- )
146
- effort_count: int = Field(
147
- ..., description="The total number of efforts for this leaderboard"
148
- )
149
- kom_type: str | None = Field(None, description="KOM/QOM type")
150
- entries: list[dict] = Field(..., description="List of leaderboard entries")
151
-
152
-
153
- class LeaderboardEntry(BaseModel):
154
- """Represents a Strava segment leaderboard entry."""
155
-
156
- athlete_name: str = Field(..., description="The name of the athlete")
157
- athlete_id: int = Field(..., description="The unique identifier of the athlete")
158
- athlete_gender: str = Field(..., description="The gender of the athlete")
159
- average_hr: float | None = Field(
160
- None, description="The athlete's average heart rate"
161
- )
162
- average_watts: float | None = Field(None, description="The athlete's average watts")
163
- distance: float = Field(..., description="The distance in meters")
164
- elapsed_time: int = Field(..., description="The elapsed time in seconds")
165
- moving_time: int = Field(..., description="The moving time in seconds")
166
- start_date: datetime = Field(..., description="The timestamp of the effort in UTC")
167
- start_date_local: datetime = Field(
168
- ..., description="The timestamp of the effort in local time"
169
- )
170
- activity_id: int = Field(..., description="The unique identifier of the activity")
171
- effort_id: int = Field(
172
- ..., description="The unique identifier of the segment effort"
173
- )
174
- rank: int = Field(
175
- ..., description="The rank of the effort on the segment leaderboard"
176
- )
177
- neighborhood_index: int | None = Field(None, description="Neighborhood index")
178
 
179
 
180
  class ErrorResponse(BaseModel):
 
137
  segment: Segment = Field(..., description="The segment")
138
 
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
 
142
  class ErrorResponse(BaseModel):
strava_mcp/server.py CHANGED
@@ -134,54 +134,3 @@ async def get_activity_segments(
134
  raise
135
 
136
 
137
- @mcp.tool()
138
- async def get_segment_leaderboard(
139
- ctx: Context,
140
- segment_id: int,
141
- gender: str | None = None,
142
- age_group: str | None = None,
143
- weight_class: str | None = None,
144
- following: bool | None = None,
145
- club_id: int | None = None,
146
- date_range: str | None = None,
147
- context_entries: int | None = None,
148
- page: int = 1,
149
- per_page: int = 30,
150
- ) -> dict:
151
- """Get the leaderboard for a given segment.
152
-
153
- Args:
154
- ctx: The MCP request context
155
- segment_id: The ID of the segment
156
- gender: Filter by gender ('M' or 'F')
157
- age_group: Filter by age group
158
- weight_class: Filter by weight class
159
- following: Filter by friends of the authenticated athlete
160
- club_id: Filter by club
161
- date_range: Filter by date range
162
- context_entries: Number of context entries
163
- page: Page number
164
- per_page: Number of items per page
165
-
166
- Returns:
167
- The segment leaderboard
168
- """
169
- service = ctx.request_context.lifespan_context["service"]
170
-
171
- try:
172
- leaderboard = await service.get_segment_leaderboard(
173
- segment_id,
174
- gender,
175
- age_group,
176
- weight_class,
177
- following,
178
- club_id,
179
- date_range,
180
- context_entries,
181
- page,
182
- per_page,
183
- )
184
- return leaderboard.model_dump()
185
- except Exception as e:
186
- logger.error(f"Error in get_segment_leaderboard tool: {str(e)}")
187
- raise
 
134
  raise
135
 
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
strava_mcp/service.py CHANGED
@@ -2,7 +2,7 @@ import logging
2
 
3
  from strava_mcp.api import StravaAPI
4
  from strava_mcp.config import StravaSettings
5
- from strava_mcp.models import Activity, DetailedActivity, Leaderboard, SegmentEffort
6
 
7
  logger = logging.getLogger(__name__)
8
 
@@ -89,54 +89,3 @@ class StravaService:
89
  logger.error(f"Error getting segments for activity {activity_id}: {str(e)}")
90
  raise
91
 
92
- async def get_segment_leaderboard(
93
- self,
94
- segment_id: int,
95
- gender: str | None = None,
96
- age_group: int | None = None,
97
- weight_class: int | None = None,
98
- following: bool | None = None,
99
- club_id: int | None = None,
100
- date_range: int | None = None,
101
- context_entries: int | None = None,
102
- page: int = 1,
103
- per_page: int = 30,
104
- ) -> Leaderboard:
105
- """Get the leaderboard for a given segment.
106
-
107
- Args:
108
- segment_id: The ID of the segment
109
- gender: Filter by gender ('M' or 'F')
110
- age_group: Filter by age group
111
- weight_class: Filter by weight class
112
- following: Filter by friends of the authenticated athlete
113
- club_id: Filter by club
114
- date_range: Filter by date range
115
- context_entries: Number of context entries
116
- page: Page number
117
- per_page: Number of items per page
118
-
119
- Returns:
120
- The segment leaderboard
121
- """
122
- try:
123
- logger.info(f"Getting leaderboard for segment {segment_id}")
124
- leaderboard = await self.api.get_segment_leaderboard(
125
- segment_id,
126
- gender,
127
- age_group,
128
- weight_class,
129
- following,
130
- club_id,
131
- date_range,
132
- context_entries,
133
- page,
134
- per_page,
135
- )
136
- logger.info(f"Retrieved leaderboard with {leaderboard.entry_count} entries")
137
- return leaderboard
138
- except Exception as e:
139
- logger.error(
140
- f"Error getting leaderboard for segment {segment_id}: {str(e)}"
141
- )
142
- raise
 
2
 
3
  from strava_mcp.api import StravaAPI
4
  from strava_mcp.config import StravaSettings
5
+ from strava_mcp.models import Activity, DetailedActivity, SegmentEffort
6
 
7
  logger = logging.getLogger(__name__)
8
 
 
89
  logger.error(f"Error getting segments for activity {activity_id}: {str(e)}")
90
  raise
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_api.py CHANGED
@@ -5,7 +5,7 @@ import pytest
5
 
6
  from strava_mcp.api import StravaAPI
7
  from strava_mcp.config import StravaSettings
8
- from strava_mcp.models import Activity, DetailedActivity, Leaderboard
9
 
10
 
11
  @pytest.fixture
@@ -177,47 +177,3 @@ async def test_get_activity(api, mock_response):
177
  assert activity.description == activity_data["description"]
178
 
179
 
180
- @pytest.mark.asyncio
181
- async def test_get_segment_leaderboard(api, mock_response):
182
- # Setup mock response
183
- leaderboard_data = {
184
- "entry_count": 100,
185
- "effort_count": 200,
186
- "kom_type": "kom",
187
- "entries": [
188
- {
189
- "athlete_name": "John Doe",
190
- "athlete_id": 123,
191
- "athlete_gender": "M",
192
- "average_hr": 160,
193
- "average_watts": 250,
194
- "distance": 1000,
195
- "elapsed_time": 180,
196
- "moving_time": 180,
197
- "start_date": "2023-01-01T10:00:00Z",
198
- "start_date_local": "2023-01-01T10:00:00Z",
199
- "activity_id": 12345,
200
- "effort_id": 67890,
201
- "rank": 1,
202
- }
203
- ],
204
- }
205
- mock_response.json.return_value = leaderboard_data
206
- api._client.request.return_value = mock_response
207
-
208
- # Test get_segment_leaderboard
209
- leaderboard = await api.get_segment_leaderboard(12345)
210
-
211
- # Verify request
212
- api._client.request.assert_called_once()
213
- args, kwargs = api._client.request.call_args
214
- assert args[0] == "GET"
215
- assert args[1] == "/segments/12345/leaderboard"
216
- assert kwargs["params"] == {"page": 1, "per_page": 30}
217
-
218
- # Verify response
219
- assert isinstance(leaderboard, Leaderboard)
220
- assert leaderboard.entry_count == leaderboard_data["entry_count"]
221
- assert leaderboard.effort_count == leaderboard_data["effort_count"]
222
- assert leaderboard.kom_type == leaderboard_data["kom_type"]
223
- assert len(leaderboard.entries) == 1
 
5
 
6
  from strava_mcp.api import StravaAPI
7
  from strava_mcp.config import StravaSettings
8
+ from strava_mcp.models import Activity, DetailedActivity
9
 
10
 
11
  @pytest.fixture
 
177
  assert activity.description == activity_data["description"]
178
 
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_server.py CHANGED
@@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, MagicMock
2
 
3
  import pytest
4
 
5
- from strava_mcp.models import Activity, DetailedActivity, Leaderboard, SegmentEffort
6
 
7
 
8
  class MockContext:
@@ -19,7 +19,6 @@ def mock_service():
19
  mock.get_activities = AsyncMock()
20
  mock.get_activity = AsyncMock()
21
  mock.get_activity_segments = AsyncMock()
22
- mock.get_segment_leaderboard = AsyncMock()
23
  return mock
24
 
25
 
@@ -171,45 +170,3 @@ async def test_get_activity_segments(mock_ctx, mock_service):
171
  assert result[0]["name"] == mock_segment.name
172
 
173
 
174
- @pytest.mark.asyncio
175
- async def test_get_segment_leaderboard(mock_ctx, mock_service):
176
- # Setup mock data
177
- mock_leaderboard = Leaderboard(
178
- entry_count=100,
179
- effort_count=200,
180
- kom_type="kom",
181
- entries=[
182
- {
183
- "athlete_name": "John Doe",
184
- "athlete_id": 123,
185
- "athlete_gender": "M",
186
- "average_hr": 160,
187
- "average_watts": 250,
188
- "distance": 1000,
189
- "elapsed_time": 180,
190
- "moving_time": 180,
191
- "start_date": "2023-01-01T10:00:00Z",
192
- "start_date_local": "2023-01-01T10:00:00Z",
193
- "activity_id": 12345,
194
- "effort_id": 67890,
195
- "rank": 1,
196
- }
197
- ],
198
- )
199
- mock_service.get_segment_leaderboard.return_value = mock_leaderboard
200
-
201
- # Test tool
202
- from strava_mcp.server import get_segment_leaderboard
203
-
204
- result = await get_segment_leaderboard(mock_ctx, 12345)
205
-
206
- # Verify service call
207
- mock_service.get_segment_leaderboard.assert_called_once_with(
208
- 12345, None, None, None, None, None, None, None, 1, 30
209
- )
210
-
211
- # Verify result
212
- assert result["entry_count"] == mock_leaderboard.entry_count
213
- assert result["effort_count"] == mock_leaderboard.effort_count
214
- assert result["kom_type"] == mock_leaderboard.kom_type
215
- assert len(result["entries"]) == 1
 
2
 
3
  import pytest
4
 
5
+ from strava_mcp.models import Activity, DetailedActivity, SegmentEffort
6
 
7
 
8
  class MockContext:
 
19
  mock.get_activities = AsyncMock()
20
  mock.get_activity = AsyncMock()
21
  mock.get_activity_segments = AsyncMock()
 
22
  return mock
23
 
24
 
 
170
  assert result[0]["name"] == mock_segment.name
171
 
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_service.py CHANGED
@@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, patch
3
  import pytest
4
 
5
  from strava_mcp.config import StravaSettings
6
- from strava_mcp.models import Activity, DetailedActivity, Leaderboard, SegmentEffort
7
  from strava_mcp.service import StravaService
8
 
9
 
@@ -22,7 +22,6 @@ def mock_api():
22
  mock.get_activities = AsyncMock()
23
  mock.get_activity = AsyncMock()
24
  mock.get_activity_segments = AsyncMock()
25
- mock.get_segment_leaderboard = AsyncMock()
26
  return mock
27
 
28
 
@@ -166,40 +165,3 @@ async def test_get_activity_segments(service, mock_api):
166
  assert segments[0] == mock_segment
167
 
168
 
169
- @pytest.mark.asyncio
170
- async def test_get_segment_leaderboard(service, mock_api):
171
- # Setup mocked data
172
- mock_leaderboard = Leaderboard(
173
- entry_count=100,
174
- effort_count=200,
175
- kom_type="kom",
176
- entries=[
177
- {
178
- "athlete_name": "John Doe",
179
- "athlete_id": 123,
180
- "athlete_gender": "M",
181
- "average_hr": 160,
182
- "average_watts": 250,
183
- "distance": 1000,
184
- "elapsed_time": 180,
185
- "moving_time": 180,
186
- "start_date": "2023-01-01T10:00:00Z",
187
- "start_date_local": "2023-01-01T10:00:00Z",
188
- "activity_id": 12345,
189
- "effort_id": 67890,
190
- "rank": 1,
191
- }
192
- ],
193
- )
194
- mock_api.get_segment_leaderboard.return_value = mock_leaderboard
195
-
196
- # Test get_segment_leaderboard
197
- leaderboard = await service.get_segment_leaderboard(12345)
198
-
199
- # Verify API call
200
- mock_api.get_segment_leaderboard.assert_called_once_with(
201
- 12345, None, None, None, None, None, None, None, 1, 30
202
- )
203
-
204
- # Verify response
205
- assert leaderboard == mock_leaderboard
 
3
  import pytest
4
 
5
  from strava_mcp.config import StravaSettings
6
+ from strava_mcp.models import Activity, DetailedActivity, SegmentEffort
7
  from strava_mcp.service import StravaService
8
 
9
 
 
22
  mock.get_activities = AsyncMock()
23
  mock.get_activity = AsyncMock()
24
  mock.get_activity_segments = AsyncMock()
 
25
  return mock
26
 
27
 
 
165
  assert segments[0] == mock_segment
166
 
167