diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a9c284..b96d000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.3.2 + +- Added `getTeamEvents(team, {year})` for a team's full Statbotics history + across events (part of issue #9). It reuses the `/team_events` endpoint with + a `team` filter instead of `event`, and optionally a `year`, returning every + team-event row for that team sorted newest season first, then by event key. + List endpoints return an empty list on 404, so an unknown team or a team + with no recorded events answers with an empty list rather than throwing. + ## 0.3.1 - `StatboticsTeamEvent.toJson` now serializes `team_name`, so the diff --git a/README.md b/README.md index 0e5545b..5ae4c0d 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,13 @@ call `close()` when you are done so the underlying HTTP client is released. | `getEvent(eventKey)` | `GET /event/{eventKey}` | `StatboticsEvent?` | | `getEvents(year)` | `GET /events?year={year}` | `List` | | `getEventTeams(eventKey)` | `GET /team_events?event={eventKey}` | `List` | +| `getTeamEvents(team, {year})` | `GET /team_events?team={team}[&year={year}]` | `List` | | `getEventTeamsBasic(eventKey)` | `GET /teams?event={eventKey}` | `List` | | `getEventMatches(eventKey)` | `GET /matches?event={eventKey}` | `List` | - `getEventTeams` sorts results by rank ascending. +- `getTeamEvents` sorts results newest season first, then by event key, so a + team's most recent results lead. Pass `year` to narrow to one season. - `getEvents` sorts results by week then name. - `getEventMatches` sorts results by comp level (`qm`, `ef`, `qf`, `sf`, `f`) then match number. diff --git a/lib/src/statbotics_client.dart b/lib/src/statbotics_client.dart index 66498ab..1a1739d 100644 --- a/lib/src/statbotics_client.dart +++ b/lib/src/statbotics_client.dart @@ -63,6 +63,48 @@ class StatboticsClient { return results; } + /// `GET /v3/team_events?team={team}[&year={year}]&limit=1000` — returns + /// every team-event record for the given team: the full Statbotics history + /// of that team across all events, newest season first. Pass [year] to + /// narrow the history to one season. + /// + /// `/team_events` is the same endpoint `getEventTeams` uses; this method + /// just drops the `event` filter and adds `team` (and optionally `year`), + /// answering with every row that mentions the team instead of every row for + /// one event. Results are sorted by year descending, then event key + /// ascending. Returns an empty list on 404 (an unknown team number) or if + /// the team has no recorded events. + /// + /// The 1000-row cap is fixed rather than a parameter, the way the other + /// list methods fix theirs: a team plays a handful of events a season, so + /// no real team comes close to it and every call answers with the whole + /// history. + Future> getTeamEvents(int team, {int? year}) async { + final queryParameters = { + 'team': team.toString(), + 'limit': '1000', + }; + if (year != null) queryParameters['year'] = year.toString(); + final body = await _get( + '/team_events', + queryParameters: queryParameters, + ); + if (body == null) return const []; + final list = jsonDecode(body) as List; + final results = list + .map( + (json) => StatboticsTeamEvent.fromJson( + (json as Map).cast(), + ), + ) + .toList(growable: true); + results.sort((a, b) { + if (a.year != b.year) return b.year.compareTo(a.year); + return a.event.compareTo(b.event); + }); + return results; + } + /// `GET /v3/events?year={year}` — returns all events for the given year, /// sorted by week then name. Future> getEvents(int year) async { diff --git a/pubspec.yaml b/pubspec.yaml index 5247b49..7a523be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: statbotics_client description: A typed Dart client for the Statbotics API v3 (FRC EPA statistics, events, matches). Pure Dart, no Flutter dependency. -version: 0.3.1 +version: 0.3.2 repository: https://github.com/Project516/statbotics_client topics: diff --git a/test/statbotics_client_test.dart b/test/statbotics_client_test.dart index c91cdd2..7e2cb66 100644 --- a/test/statbotics_client_test.dart +++ b/test/statbotics_client_test.dart @@ -553,5 +553,108 @@ void main() { expect(team.team, 1234); expect(team.nickname, 'Example'); }); + + test('getTeamEvents parses the history and sorts newest season first', + () async { + final mockClient = MockClient((request) async { + expect( + request.url.toString(), + 'https://api.statbotics.io/v3/team_events?team=254&limit=1000', + ); + expect(request.url.queryParameters['team'], '254'); + expect(request.url.queryParameters['limit'], '1000'); + return http.Response( + jsonEncode(>[ + { + 'team': 254, + 'event': '2024cafr', + 'event_name': 'Cal Games', + 'team_name': 'The Cheesy Poofs', + 'year': 2024, + 'wins': 9, + 'losses': 1, + 'ties': 0, + 'rank': 1, + 'num_teams': 40, + 'epa': { + 'total_points': {'mean': 55.0, 'sd': 2.0}, + }, + }, + { + 'team': 254, + 'event': '2023cafr', + 'event_name': 'Cal Games', + 'team_name': 'The Cheesy Poofs', + 'year': 2023, + 'wins': 8, + 'losses': 2, + 'ties': 0, + 'rank': 2, + 'num_teams': 40, + 'epa': { + 'total_points': {'mean': 50.0, 'sd': 2.5}, + }, + }, + { + 'team': 254, + 'event': '2024txaus', + 'event_name': 'Austin', + 'team_name': 'The Cheesy Poofs', + 'year': 2024, + 'wins': 7, + 'losses': 3, + 'ties': 0, + 'rank': 3, + 'num_teams': 38, + 'epa': { + 'total_points': {'mean': 52.0, 'sd': 2.2}, + }, + }, + ]), + 200, + headers: {'content-type': 'application/json'}, + ); + }); + + final client = StatboticsClient(httpClient: mockClient); + final history = await client.getTeamEvents(254); + + expect(history.length, 3); + // Newest season first, then event key ascending within a season. + expect(history[0].year, 2024); + expect(history[0].event, '2024cafr'); + expect(history[0].teamName, 'The Cheesy Poofs'); + expect(history[1].year, 2024); + expect(history[1].event, '2024txaus'); + expect(history[2].year, 2023); + expect(history[2].event, '2023cafr'); + }); + + test('getTeamEvents forwards the optional year filter', () async { + final mockClient = MockClient((request) async { + expect( + request.url.toString(), + 'https://api.statbotics.io/v3/team_events?team=254&limit=1000&year=2024', + ); + expect(request.url.queryParameters['year'], '2024'); + return http.Response( + jsonEncode(>>[]), + 200, + headers: {'content-type': 'application/json'}, + ); + }); + + final client = StatboticsClient(httpClient: mockClient); + final history = await client.getTeamEvents(254, year: 2024); + expect(history, isEmpty); + }); + + test('getTeamEvents returns an empty list on 404 (unknown team)', () async { + final client = StatboticsClient( + httpClient: MockClient((_) async => http.Response('', 404)), + ); + final history = await client.getTeamEvents(999999); + expect(history, isEmpty); + }); }); }