Skip to content

Commit 69bb07c

Browse files
authored
Merge pull request #11 from PlayerData/10-direct-queries
feat: adding direct queries
2 parents ad3844f + 4f38e9b commit 69bb07c

7 files changed

Lines changed: 426 additions & 12 deletions

File tree

examples/direct/example_use.ipynb

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "a81bd7ff",
6+
"metadata": {},
7+
"source": [
8+
"# Using the PlayerData API with Direct\n",
9+
"\n",
10+
"This notebook demonstrates how to use the PlayerData API with Direct Queries to query session details and raw data.\n",
11+
"\n",
12+
"## Prerequisites\n",
13+
"\n",
14+
"Before using this notebook, you will need to:\n",
15+
"- Create a virtual environment\n",
16+
"- Install all dependencies from the `pyproject.toml` file\n",
17+
"\n",
18+
"## Authentication\n",
19+
"\n",
20+
"First, we need to authenticate with the PlayerData API. There are three authentication methods available:\n",
21+
"\n",
22+
"1. **Client Credentials Flow** (shown in this example)\n",
23+
"2. **Authorization Code Flow**\n",
24+
"3. **Authorization Code Flow with PKCE**\n",
25+
"\n",
26+
"To use a different authentication method, change the `authentication_type` parameter to `AuthenticationType.AUTHORIZATION_CODE_FLOW` or `AuthenticationType.AUTHORIZATION_CODE_FLOW_WITH_PKCE` and provide the required parameters."
27+
]
28+
},
29+
{
30+
"cell_type": "code",
31+
"execution_count": null,
32+
"id": "227644c3",
33+
"metadata": {},
34+
"outputs": [],
35+
"source": [
36+
"from playerdatapy.gqlauth import GraphqlAuth\n",
37+
"from playerdatapy.gqlauth import AuthenticationType\n",
38+
"from playerdatapy.gqlclient import Client\n",
39+
"from playerdatapy.constants import API_BASE_URL\n",
40+
"\n",
41+
"# Add your client id, client secret and one of your club ids\n",
42+
"CLIENT_ID = \"your_client_id\"\n",
43+
"CLIENT_SECRET = \"your_client_secret\"\n",
44+
"CLUB_ID = \"your_club_id\"\n",
45+
"\n",
46+
"# Example usage of the GraphqlClient class.\n",
47+
"auth = GraphqlAuth(\n",
48+
" client_id=CLIENT_ID,\n",
49+
" client_secret=CLIENT_SECRET,\n",
50+
" type=AuthenticationType.CLIENT_CREDENTIALS_FLOW,\n",
51+
")\n",
52+
"\n",
53+
"# Create a Client instance.\n",
54+
"client = Client(\n",
55+
" url=f\"{API_BASE_URL}/api/graphql\",\n",
56+
" headers={\"Authorization\": f\"Bearer {auth._get_authentication_token()}\"},\n",
57+
")"
58+
]
59+
},
60+
{
61+
"cell_type": "markdown",
62+
"id": "66e76500",
63+
"metadata": {},
64+
"source": [
65+
"## Defining Queries\n",
66+
"\n",
67+
"There are example queries in the `queries` folder that you can use as a starting point. For example, `ClubSessionsFilteredByTimeRange` retrieves all sessions for a club in a specified time period."
68+
]
69+
},
70+
{
71+
"cell_type": "markdown",
72+
"id": "a8f0c3d1",
73+
"metadata": {},
74+
"source": [
75+
"## Running Example Queries\n",
76+
"\n",
77+
"For now, let's use the example queries, run them, and print the results."
78+
]
79+
},
80+
{
81+
"cell_type": "code",
82+
"execution_count": null,
83+
"id": "4534adda",
84+
"metadata": {},
85+
"outputs": [],
86+
"source": [
87+
"from datetime import datetime, timedelta\n",
88+
"\n",
89+
"with open(\"queries/club_sessions_filtered_by_time_range.graphql\", \"r\") as f:\n",
90+
" club_sessions_filtered_by_time_range = f.read()\n",
91+
"\n",
92+
"# Query for all sessions in the last 30 days\n",
93+
"start_time = datetime.now() - timedelta(days=30)\n",
94+
"end_time = datetime.now()\n",
95+
"\n",
96+
"last_thirty_days_sessions_response = await client.execute(\n",
97+
" query=club_sessions_filtered_by_time_range,\n",
98+
" variables={\"clubId\": CLUB_ID, \"startTime\": start_time, \"endTime\": end_time},\n",
99+
")\n",
100+
"\n",
101+
"last_thirty_days_sessions_response = client.get_data(last_thirty_days_sessions_response)\n",
102+
"\n",
103+
"print(last_thirty_days_sessions_response)"
104+
]
105+
},
106+
{
107+
"cell_type": "markdown",
108+
"id": "15a1ca44",
109+
"metadata": {},
110+
"source": [
111+
"## Getting Session Details\n",
112+
"\n",
113+
"From the response above, we can see the sessions returned. Now let's get detailed information for a specific session using the session ID from the first session in the response."
114+
]
115+
},
116+
{
117+
"cell_type": "code",
118+
"execution_count": null,
119+
"id": "8a8ef4b4",
120+
"metadata": {},
121+
"outputs": [],
122+
"source": [
123+
"with open(\"queries/session_details.graphql\", \"r\") as f:\n",
124+
" session_details = f.read()\n",
125+
"\n",
126+
"# Extract the session ID from the first session\n",
127+
"first_session_id = last_thirty_days_sessions_response[\"sessions\"][0][\"id\"]\n",
128+
"\n",
129+
"session_details_response = await client.execute(\n",
130+
" query=session_details, variables={\"sessionId\": first_session_id}\n",
131+
")\n",
132+
"\n",
133+
"session_details_response = client.get_data(session_details_response)\n",
134+
"print(session_details_response)"
135+
]
136+
},
137+
{
138+
"cell_type": "markdown",
139+
"id": "1a947b52",
140+
"metadata": {},
141+
"source": [
142+
"## Getting Session Metrics\n",
143+
"\n",
144+
"For the same session, you can retrieve configured metrics at different levels:\n",
145+
"- Team aggregate level\n",
146+
"- Session participation level\n",
147+
"- Segment level"
148+
]
149+
},
150+
{
151+
"cell_type": "code",
152+
"execution_count": null,
153+
"id": "4f9f7708",
154+
"metadata": {},
155+
"outputs": [],
156+
"source": [
157+
"with open(\"queries/session_metrics.graphql\", \"r\") as f:\n",
158+
" session_metrics = f.read()\n",
159+
"\n",
160+
"# Query for session metrics at all levels\n",
161+
"session_metrics_response = await client.execute(\n",
162+
" query=session_metrics, variables={\"sessionId\": first_session_id}\n",
163+
")\n",
164+
"\n",
165+
"session_metrics_response = client.get_data(session_metrics_response)\n",
166+
"print(session_metrics_response)"
167+
]
168+
},
169+
{
170+
"cell_type": "markdown",
171+
"id": "9c5c065e",
172+
"metadata": {},
173+
"source": [
174+
"## Advanced Usage: Raw Data\n",
175+
"\n",
176+
"To query for raw data, you can use the `session_participation_urls` query. This returns URLs for the raw data for a given list of session participation IDs.\n",
177+
"\n",
178+
"We'll use the first session participation ID from the `session_details_response`."
179+
]
180+
},
181+
{
182+
"cell_type": "code",
183+
"execution_count": null,
184+
"id": "1730ccec",
185+
"metadata": {},
186+
"outputs": [],
187+
"source": [
188+
"with open(\"queries/session_participations_urls.graphql\", \"r\") as f:\n",
189+
" session_participations_urls = f.read()\n",
190+
"\n",
191+
"# Extract the first session participation ID\n",
192+
"first_session_participation_id = session_details_response[\"session\"][\n",
193+
" \"sessionParticipations\"\n",
194+
"][0][\"id\"]\n",
195+
"\n",
196+
"# Query for raw data URLs\n",
197+
"session_participation_urls_response = await client.execute(\n",
198+
" query=session_participations_urls,\n",
199+
" variables={\"ids\": [first_session_participation_id]},\n",
200+
")\n",
201+
"\n",
202+
"session_participation_urls_response = client.get_data(\n",
203+
" session_participation_urls_response\n",
204+
")\n",
205+
"print(session_participation_urls_response)"
206+
]
207+
},
208+
{
209+
"cell_type": "markdown",
210+
"id": "96aafdd9",
211+
"metadata": {},
212+
"source": [
213+
"## Downloading Raw Data\n",
214+
"\n",
215+
"The response contains a list of URLs for the raw data for the given session participation ID.\n",
216+
"\n",
217+
"You can download the raw JSON data in two ways:\n",
218+
"1. Use the `url_to_csv` function from `raw_data_utils.url_to_csv.py` (shown below)\n",
219+
"2. Use the `requests` library directly\n",
220+
"\n",
221+
"The `url_to_csv` function downloads the raw JSON data from the URL and saves it to CSV files:\n",
222+
"- One file for GPS data\n",
223+
"- One file for IMU acceleration data\n",
224+
"- One file for IMU orientation data\n",
225+
"\n",
226+
"The session participation ID is used as a prefix in the filename to differentiate between files."
227+
]
228+
},
229+
{
230+
"cell_type": "code",
231+
"execution_count": null,
232+
"id": "ec55f919",
233+
"metadata": {},
234+
"outputs": [],
235+
"source": [
236+
"import sys\n",
237+
"from pathlib import Path\n",
238+
"\n",
239+
"# Add project root to Python path to get access to url_to_csv package\n",
240+
"# Only required for notebook example\n",
241+
"sys.path.append(str(Path().resolve().parent))\n",
242+
"from raw_data_utils.url_to_csv import url_to_csv\n",
243+
"\n",
244+
"# Extract the URL from the first session participation\n",
245+
"first_session_participation_url = session_participation_urls_response[\n",
246+
" \"sessionParticipations\"\n",
247+
"][0][\"datafiles\"][0][\"url\"]\n",
248+
"\n",
249+
"# Download and convert raw data to CSV files\n",
250+
"url_to_csv(first_session_participation_url, first_session_participation_id)"
251+
]
252+
},
253+
{
254+
"cell_type": "markdown",
255+
"id": "706f8730",
256+
"metadata": {},
257+
"source": [
258+
"This will create a folder in your root directory with the session participation ID and the three CSV files saved inside.\n",
259+
"\n",
260+
"**Note:** This method of retrieving raw data via the API is currently only available for GPS and IMU data. This method will be deprecated in the future, making way for a simpler method of raw data retrieval where raw LPS data will also be available."
261+
]
262+
}
263+
],
264+
"metadata": {
265+
"kernelspec": {
266+
"display_name": ".venv",
267+
"language": "python",
268+
"name": "python3"
269+
},
270+
"language_info": {
271+
"codemirror_mode": {
272+
"name": "ipython",
273+
"version": 3
274+
},
275+
"file_extension": ".py",
276+
"mimetype": "text/x-python",
277+
"name": "python",
278+
"nbconvert_exporter": "python",
279+
"pygments_lexer": "ipython3",
280+
"version": "3.13.7"
281+
}
282+
},
283+
"nbformat": 4,
284+
"nbformat_minor": 5
285+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
query ClubSessionsFilteredByTimeRange($clubId: ID, $startTime:ISO8601DateTime,$endTime:ISO8601DateTime){
2+
sessions(filter: {clubIdEq: $clubId, startTimeGteq: $startTime, endTimeLteq:$endTime}, offset: 0, limit:30){
3+
id
4+
startTime
5+
endTime
6+
}
7+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
query SessionDetails($sessionId: ID!) {
2+
session(id: $sessionId) {
3+
sessionParticipations {
4+
id
5+
athlete {
6+
id
7+
name
8+
}
9+
segmentParticipations {
10+
segment {
11+
id
12+
title
13+
startTime
14+
endTime
15+
}
16+
}
17+
}
18+
}
19+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
query SessionMetrics($sessionId: ID!) {
2+
session(id: $sessionId) {
3+
startTime
4+
5+
configuredAggMetrics {
6+
data {
7+
label
8+
localUnitLabel
9+
localValue {
10+
... on FloatMetricValue {
11+
floatValue
12+
}
13+
... on IntMetricValue {
14+
intValue
15+
}
16+
... on JsonMetricValue {
17+
jsonValue
18+
}
19+
}
20+
}
21+
}
22+
23+
sessionParticipations {
24+
configuredMetrics {
25+
data {
26+
label
27+
localUnitLabel
28+
localValue {
29+
... on FloatMetricValue {
30+
floatValue
31+
}
32+
... on IntMetricValue {
33+
intValue
34+
}
35+
... on JsonMetricValue {
36+
jsonValue
37+
}
38+
}
39+
}
40+
}
41+
42+
segmentParticipations {
43+
segment {
44+
title
45+
}
46+
configuredMetrics {
47+
data {
48+
label
49+
localUnitLabel
50+
localValue {
51+
... on FloatMetricValue {
52+
floatValue
53+
}
54+
... on IntMetricValue {
55+
intValue
56+
}
57+
... on JsonMetricValue {
58+
jsonValue
59+
}
60+
}
61+
}
62+
}
63+
}
64+
}
65+
}
66+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
query SessionParticipationsUrls($ids: [ID!]!) {
2+
sessionParticipations(ids: $ids) {
3+
id
4+
datafiles {
5+
url(format: json)
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)