Activity Tracks¶
Endurabeats¶
2024-02-03
Per Halvorsen
This notebook serves as the foudation for the Endurabeats tool. I developed this tool to sync the music I played on Spotify during an activity with the activity data on Strava.
Goal¶
Find the tracks played during an activity.
Load¶
In [150]:
import json
import os
import requests as r
import pandas as pd
In [297]:
def load_tokens(filename):
with open(filename, 'r') as f:
return json.load(f)
spotify_tokens = load_tokens(os.environ["SPOTIFY_TOKENS_PATH"])
strava_tokens = load_tokens(os.environ["STRAVA_TOKENS_PATH"])
# strava_tokens, spotify_tokens
Load data from APIs¶
In [298]:
def get_recent_played(access_token):
URL = "https://api.spotify.com/v1/me/player/recently-played" # api-endpoint for recently played
HEAD = {'Authorization': 'Bearer '+ access_token} # provide auth. credentials
PARAMS = {'limit':50} # default here is 20
content = r.get(url=URL, headers=HEAD, params=PARAMS)
return content.json()
def get_activities(access_token):
URL = "https://www.strava.com/api/v3/athlete/activities" # api-endpoint for recently played
HEAD = {"Authorization": f"Bearer {access_token}"}
content = r.get(URL, headers=HEAD)
return content.json()
raw_recent_played = get_recent_played(spotify_tokens["access_token"])
raw_activities = get_activities(strava_tokens["access_token"])
In [170]:
raw_activities
Out[170]:
[{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Yoga',
'distance': 0.0,
'moving_time': 1628,
'elapsed_time': 1628,
'total_elevation_gain': 0,
'type': 'Yoga',
'sport_type': 'Yoga',
'id': 10688279212,
'start_date': '2024-02-03T14:55:59Z',
'start_date_local': '2024-02-03T15:55:59Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 1,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10688279212', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 28,
'has_heartrate': False,
'heartrate_opt_out': False,
'display_hide_heartrate_option': False,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11431077680,
'upload_id_str': '11431077680',
'external_id': 'garmin_ping_319007367479',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Evening Workout',
'distance': 404.9,
'moving_time': 3862,
'elapsed_time': 3862,
'total_elevation_gain': 0,
'type': 'Workout',
'sport_type': 'Workout',
'id': 10682232510,
'start_date': '2024-02-02T17:03:10Z',
'start_date_local': '2024-02-02T18:03:10Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10682232510', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.105,
'max_speed': 2.413,
'average_cadence': 57.1,
'average_temp': 19,
'has_heartrate': True,
'average_heartrate': 90.3,
'max_heartrate': 104.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11424838041,
'upload_id_str': '11424838041',
'external_id': 'garmin_ping_318839375356',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Morning Run',
'distance': 5906.5,
'moving_time': 1865,
'elapsed_time': 1865,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10678755706,
'start_date': '2024-02-02T05:47:05Z',
'start_date_local': '2024-02-02T06:47:05Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 4,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10678755706', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 3.167,
'max_speed': 3.836,
'average_cadence': 75.7,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 161.7,
'max_heartrate': 178.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11421258441,
'upload_id_str': '11421258441',
'external_id': 'garmin_ping_318743050014',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Morning Weight Training',
'distance': 0.0,
'moving_time': 768,
'elapsed_time': 768,
'total_elevation_gain': 0,
'type': 'WeightTraining',
'sport_type': 'WeightTraining',
'id': 10672180060,
'start_date': '2024-02-01T06:14:17Z',
'start_date_local': '2024-02-01T07:14:17Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10672180060', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 134.9,
'max_heartrate': 176.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11414516045,
'upload_id_str': '11414516045',
'external_id': 'garmin_ping_318555835642',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Morning Run',
'distance': 6130.7,
'moving_time': 1984,
'elapsed_time': 1984,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10672137037,
'start_date': '2024-02-01T05:37:09Z',
'start_date_local': '2024-02-01T06:37:09Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10672137037', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 3.09,
'max_speed': 3.708,
'average_cadence': 74.2,
'average_temp': 24,
'has_heartrate': True,
'average_heartrate': 161.2,
'max_heartrate': 173.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11414471523,
'upload_id_str': '11414471523',
'external_id': 'garmin_ping_318553128934',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Weight Training',
'distance': 0.0,
'moving_time': 2466,
'elapsed_time': 2466,
'total_elevation_gain': 0,
'type': 'WeightTraining',
'sport_type': 'WeightTraining',
'id': 10668078046,
'start_date': '2024-01-31T14:49:16Z',
'start_date_local': '2024-01-31T15:49:16Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 4,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10668078046', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 111.6,
'max_heartrate': 133.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11410330460,
'upload_id_str': '11410330460',
'external_id': 'garmin_ping_318439398884',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 3339.4,
'moving_time': 1145,
'elapsed_time': 1145,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10667818129,
'start_date': '2024-01-31T14:24:34Z',
'start_date_local': '2024-01-31T15:24:34Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10667818129', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.917,
'max_speed': 3.306,
'average_cadence': 71.9,
'average_temp': 24,
'has_heartrate': True,
'average_heartrate': 126.3,
'max_heartrate': 148.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11410063447,
'upload_id_str': '11410063447',
'external_id': 'garmin_ping_318432830339',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Morning Run',
'distance': 10011.5,
'moving_time': 3014,
'elapsed_time': 3014,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10658118191,
'start_date': '2024-01-30T05:30:04Z',
'start_date_local': '2024-01-30T06:30:04Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10658118191', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 3.322,
'max_speed': 4.349,
'average_cadence': 73.6,
'average_temp': 25,
'has_heartrate': True,
'average_heartrate': 176.3,
'max_heartrate': 196.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11400097488,
'upload_id_str': '11400097488',
'external_id': 'garmin_ping_318174904070',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Morning Weight Training',
'distance': 0.0,
'moving_time': 1772,
'elapsed_time': 1772,
'total_elevation_gain': 0,
'type': 'WeightTraining',
'sport_type': 'WeightTraining',
'id': 10651602596,
'start_date': '2024-01-29T05:57:34Z',
'start_date_local': '2024-01-29T06:57:34Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 4,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10651602596', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 129.8,
'max_heartrate': 166.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11393416089,
'upload_id_str': '11393416089',
'external_id': 'garmin_ping_317985527844',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Morning Run',
'distance': 4206.7,
'moving_time': 1460,
'elapsed_time': 1593,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10651559000,
'start_date': '2024-01-29T05:27:37Z',
'start_date_local': '2024-01-29T06:27:37Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10651559000', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.881,
'max_speed': 3.259,
'average_cadence': 72.0,
'average_temp': 23,
'has_heartrate': True,
'average_heartrate': 153.8,
'max_heartrate': 169.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11393370787,
'upload_id_str': '11393370787',
'external_id': 'garmin_ping_317982870988',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 9258.2,
'moving_time': 2658,
'elapsed_time': 2752,
'total_elevation_gain': 57.0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10616549153,
'start_date': '2024-01-23T21:04:33Z',
'start_date_local': '2024-01-23T15:04:33Z',
'timezone': '(GMT-06:00) America/Chicago',
'utc_offset': -21600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 5,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10616549153',
'summary_polyline': 'ekliEn{gmQuBqBQ]Ce@UKW@]UU]_@We@Wg@Au@QYHCB[Pe@Ic@Le@IMQEMAm@Ki@MMMEa@@WPa@Dm@?{@NQ?e@WSWSOe@Q_@As@Fg@IK@g@HQX@`@LbBARETCj@Hf@GRGFK@]YQCc@AkBHW?UCqAU[C]Ew@DUA]m@g@m@KCu@?]AQG[]a@o@[Uq@WGKGi@AeBFgBCcADaBAgBBq@Cg@Fi@Po@FiAEWOQEE{ANBFD?HFAFAJWd@a@GeAc@Y_@y@c@aB]k@Xc@JGAiA]{@OwBi@u@Ac@G_@c@s@MSIE[GcAIMKL?v@[j@w@X_@Xg@|@]\\y@Vs@Dm@C]HMPiCtF{@~AwBdDUl@[fAKHOK_@c@]Wu@}@h@`@?YLUd@Or@m@l@gAh@i@`AwBfA}Ah@}At@sAl@gCCQBSNQNG\\FP@D?PONi@@_@AMGUg@c@GKWcAOYs@s@e@G]U_@r@G`@OJK?GG]w@W_@g@c@cAiBWUc@MsACs@Oi@Ae@FeAXsAb@UNa@FE?GG?EFG`Bs@FIHc@A_@FKjAg@`@Gt@Ct@@bAJxAFxAt@d@Fj@EJBLT?n@Hz@Rd@`@Rj@BREXKd@?dAl@t@T`A^nA^lARFRFDJMNIl@FRFp@^^Ln@\\lAf@vAn@XHj@J`AFp@JNCHGJcAF_BC_DDKh@EnACpAFZHAvEIr@A\\JpA@dDDj@E\\Aj@IpAC?SOAE@KH]Ka@E?AGLKJA|AJHFDVAZMx@UbABbAAbBEhCBhAArBB|A@TJV~@`@TR`@p@\\Tj@D\\?b@Jf@n@N^`@DbBIh@FbART`@D|AH~ALRd@@f@GfAm@h@Of@BJFLX@jBBD`@DlADh@Jh@TVN`@`@b@v@XdAJ`A@|@B@X?L\\HDJ@XGf@CbA?\\KFc@Gy@Am@DgAIuADk@H[DiCe@_CEq@Kw@Dk@VYb@EPB`@CXSf@Bx@Z^ZrAxADL?^DJHFRC^JhBbB',
'resource_state': 2},
'trainer': False,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [33.16196708008647, -96.71195606701076],
'end_latlng': [33.162045953795314, -96.71206151135266],
'average_speed': 3.483,
'max_speed': 5.55,
'average_cadence': 74.9,
'average_temp': 15,
'has_heartrate': True,
'average_heartrate': 172.5,
'max_heartrate': 189.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 231.2,
'elev_low': 214.2,
'upload_id': 11357096980,
'upload_id_str': '11357096980',
'external_id': 'garmin_ping_316996352061',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 11141.8,
'moving_time': 3140,
'elapsed_time': 3363,
'total_elevation_gain': 77.0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10578603390,
'start_date': '2024-01-17T20:37:07Z',
'start_date_local': '2024-01-17T14:37:07Z',
'timezone': '(GMT-06:00) America/Chicago',
'utc_offset': -21600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 8,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10578603390',
'summary_polyline': 'eoliEhwgmQGm@GIUA_@Ii@m@w@k@UOg@Gy@HSNWFg@Ee@F_@MOe@Ey@U]KESAc@Ne@Fu@A}@HMAc@Qe@c@KGm@Iy@Hm@Ig@@C@YXCTNdCK`AFn@MXE?m@[EAcD?q@E_AOoB@QAMK]q@Y[SGs@@g@GiAwAq@[][Eo@?gBFsAAeBHoEEcA@GXaA@WDy@AGISIGSIeB\\HAPF@FCXIPEBU?uAk@W[eAm@i@I]OMA_@Jc@VG@e@Ke@OeAQeA[i@KsAI]SUUOEe@EC@MV_@TI@SCYDe@Pa@h@[^i@T_ANwA@q@jAUd@_AdAqAnCiAnBuAtBUd@CJBXADG?iAaAMUDo@`@Of@]\\]^s@\\]Va@Te@b@gAVc@b@k@Te@^cAt@sATcANk@Bk@JYVQr@@TGHIJg@Ao@OWYYKOOs@O[k@m@a@Um@OWDWZKh@[Fm@iAg@e@Qc@c@u@KW]]e@ImAEy@Q_A@qBf@u@Zg@VU@S_@AiC@}AKyEKa@Yo@]_AS}@Ws@EUU}C?k@KkABqAEa@_@eB_@eAc@s@MKe@GCEBa@jATXZ`Bj@`Aj@`At@NR^\\n@^lA|@v@r@^VTJ`@`@x@TXLIh@Ud@gAv@IHSf@I`ABf@?hAJbAFP|AhAf@Pd@Ft@Bt@Lh@THHVd@FT\\zFPx@d@dAPRZLd@Hh@f@Vb@Hd@N`@^\\P\\?VOx@QTSDe@GMBQNI\\Gr@Od@Mt@IPq@hAm@~AqAnBi@tAMRm@r@_@t@MNi@`@i@XG^HV\\TTXh@b@^P`@Xn@XhAZbAJVAN@TEn@Aj@IjC{@XQZGn@SpAY`BS`@@tBPfAVxElBbBx@~@Zr@J|BCDCJ]AiAEsC?sDAmGQmAHS@MAOGGEMBCRAzAJPT@TGr@JVNHJCHGv@q@RGr@Df@Lb@Dv@?fAMf@?d@Dv@Cl@Bx@A`@Oh@a@f@m@dAcALSJ?^Zl@t@p@j@XRJRY~@?Rj@j@v@XI|HL|A@\\LX~@HjC@b@AJEP[@UA_A@m@FeA@yDHkA?i@FWvB?FV?dBBTZb@ZLnG@HD@D?pBDf@j@Jv@Db@@jACtADFH?jAAxFBJHH',
'resource_state': 2},
'trainer': False,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [33.16298212856054, -96.71198355965316],
'end_latlng': [33.16240788437426, -96.71161383390427],
'average_speed': 3.548,
'max_speed': 5.154,
'average_cadence': 76.0,
'average_temp': 16,
'has_heartrate': True,
'average_heartrate': 171.8,
'max_heartrate': 187.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 235.4,
'elev_low': 214.4,
'upload_id': 11317969580,
'upload_id_str': '11317969580',
'external_id': 'garmin_ping_315891713292',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Evening Run',
'distance': 6666.0,
'moving_time': 2187,
'elapsed_time': 2198,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10532885628,
'start_date': '2024-01-10T18:04:24Z',
'start_date_local': '2024-01-10T19:04:24Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 1,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10532885628', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 3.048,
'max_speed': 3.697,
'average_cadence': 75.4,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 170.7,
'max_heartrate': 186.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11270109900,
'upload_id_str': '11270109900',
'external_id': 'garmin_ping_314587249010',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 10009.3,
'moving_time': 3127,
'elapsed_time': 3216,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10505981253,
'start_date': '2024-01-06T14:48:44Z',
'start_date_local': '2024-01-06T15:48:44Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 4,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10505981253', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 3.201,
'max_speed': 4.667,
'average_cadence': 75.8,
'average_temp': 28,
'has_heartrate': True,
'average_heartrate': 169.9,
'max_heartrate': 194.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11242084563,
'upload_id_str': '11242084563',
'external_id': 'garmin_ping_313801732408',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Lunch Walk',
'distance': 3648.7,
'moving_time': 2416,
'elapsed_time': 3396,
'total_elevation_gain': 28.0,
'type': 'Walk',
'sport_type': 'Walk',
'id': 10505041872,
'start_date': '2024-01-06T11:38:21Z',
'start_date_local': '2024-01-06T12:38:21Z',
'timezone': '(GMT+01:00) Europe/Oslo',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 2,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10505041872',
'summary_polyline': 'abtlJary`Av@|@j@LJLHL\\nAd@hAHJf@Hh@|DBt@P|ATxA~@jEb@rCVlADl@ZdAj@bCNVPz@T|AAf@DCTJLbA`@`CHn@@`@MjE?hAAfAIv@@z@MnC?bBJ~BHp@Pt@LZBRP|B@`@NrARhAZlARf@FXBXEn@S~ACj@ETINEDIGA@MdFQrCMrAMr@Ut@Kh@a@vC]hDQzAO|@Q\\?COx@_@tCSxBYrAOh@c@lEENMLO|@I|@Ah@YpAc@~DO`AKl@INKj@CCEN_@jCELCb@@b@a@rDGTOZIJENCf@i@bEYjAEDKT[tAEl@@zAW`BMZEB[C_@D}@c@SYWAIFSOUCKJA\\ILI`AMn@KXSfAOd@I~@U`AKt@?CXhAA[m@pAq@~B',
'resource_state': 2},
'trainer': False,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [59.91163372993469, 10.785717917606235],
'end_latlng': [59.91357657127082, 10.743066435679793],
'average_speed': 1.51,
'max_speed': 3.821,
'average_cadence': 52.8,
'average_temp': 23,
'has_heartrate': True,
'average_heartrate': 99.9,
'max_heartrate': 126.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 56.2,
'elev_low': 6.4,
'upload_id': 11241107219,
'upload_id_str': '11241107219',
'external_id': 'garmin_ping_313781855488',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 6660.9,
'moving_time': 2239,
'elapsed_time': 2239,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10505041710,
'start_date': '2024-01-04T16:23:24Z',
'start_date_local': '2024-01-04T17:23:24Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10505041710', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.975,
'max_speed': 5.125,
'average_cadence': 73.2,
'average_temp': 27,
'has_heartrate': True,
'average_heartrate': 167.9,
'max_heartrate': 196.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11241107100,
'upload_id_str': '11241107100',
'external_id': 'garmin_ping_313781851789',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Evening Run',
'distance': 4205.0,
'moving_time': 1750,
'elapsed_time': 1750,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10505041676,
'start_date': '2024-01-02T17:46:38Z',
'start_date_local': '2024-01-02T18:46:38Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 0,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10505041676', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.403,
'max_speed': 3.031,
'average_cadence': 72.5,
'average_temp': 27,
'has_heartrate': True,
'average_heartrate': 145.1,
'max_heartrate': 160.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11241106855,
'upload_id_str': '11241106855',
'external_id': 'garmin_ping_313781848961',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Weight Training',
'distance': 0.0,
'moving_time': 1472,
'elapsed_time': 1472,
'total_elevation_gain': 0,
'type': 'WeightTraining',
'sport_type': 'WeightTraining',
'id': 10445623922,
'start_date': '2023-12-27T14:39:03Z',
'start_date_local': '2023-12-27T15:39:03Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10445623922', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 145.8,
'max_heartrate': 171.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11179157577,
'upload_id_str': '11179157577',
'external_id': 'garmin_ping_311953915358',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 6668.2,
'moving_time': 2320,
'elapsed_time': 2320,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10445623811,
'start_date': '2023-12-27T13:52:05Z',
'start_date_local': '2023-12-27T14:52:05Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 2,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10445623811', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.874,
'max_speed': 4.465,
'average_cadence': 74.4,
'average_temp': 23,
'has_heartrate': True,
'average_heartrate': 167.5,
'max_heartrate': 194.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11179157452,
'upload_id_str': '11179157452',
'external_id': 'garmin_ping_311953912708',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Weight Training',
'distance': 0.0,
'moving_time': 1319,
'elapsed_time': 1319,
'total_elevation_gain': 0,
'type': 'WeightTraining',
'sport_type': 'WeightTraining',
'id': 10397019081,
'start_date': '2023-12-17T16:15:57Z',
'start_date_local': '2023-12-17T17:15:57Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10397019081', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 29,
'has_heartrate': True,
'average_heartrate': 119.7,
'max_heartrate': 150.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11128954770,
'upload_id_str': '11128954770',
'external_id': 'garmin_ping_310337252537',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 10074.7,
'moving_time': 3508,
'elapsed_time': 3508,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10397019641,
'start_date': '2023-12-17T15:09:11Z',
'start_date_local': '2023-12-17T16:09:11Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 4,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10397019641', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.872,
'max_speed': 3.974,
'average_cadence': 73.6,
'average_temp': 28,
'has_heartrate': True,
'average_heartrate': 178.2,
'max_heartrate': 191.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11128954520,
'upload_id_str': '11128954520',
'external_id': 'garmin_ping_310337242878',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Nordic Ski',
'distance': 13220.6,
'moving_time': 6498,
'elapsed_time': 7393,
'total_elevation_gain': 357.0,
'type': 'NordicSki',
'sport_type': 'NordicSki',
'id': 10389992274,
'start_date': '2023-12-16T13:33:58Z',
'start_date_local': '2023-12-16T14:33:58Z',
'timezone': '(GMT+01:00) Europe/Oslo',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 6,
'comment_count': 0,
'athlete_count': 3,
'photo_count': 0,
'map': {'id': 'a10389992274',
'summary_polyline': 's{llJwjfaA?_@QiAEw@AoBDaBMkCe@yFEK^zBMuB@wAJgA]oBCiDJkCFo@N]\\UhBIhANdAQ|Ab@p@hAzAtAhCtCd@hAXX`@DfAfATBILHo@r@eANs@VWZeAPKb@iAd@cB@eAj@sBVe@|BwG`@cDW}MBy@Py@Di@McCs@eBGu@VcDHmE@oGGk@@]K_AWcFMoFWoAQU[gBSeBWuAQeB@[[kB]uAw@yBa@uBi@yAOq@E}Ck@}CQIe@TcAtCcAp@iAvBwBdAe@@wCg@{Bq@qBsB}AaC[s@mAaBcAm@_@Em@c@yAyBcAgAmAg@w@GiC}AuEn@sBI_AKwEoAeAMyCl@kAWkEb@_@CeBkAsBQuCf@kCx@sAAiDg@WDMVo@f@QDMOIUC{BVuCGeCBuBL}A`@eBRiB^}AP{ABaAI{IF_AJm@Zu@|@y@z@YMa@MHSKSNo@Cw@}@YGGUaBqBs@gAq@wAaAkAkAoB{@eCq@_E[qAs@oEy@{CUeBCu@]s@cAuAQi@EaALe@JG?UWaCuB}GgBmHs@eBOq@MeCM{AD_APwASsDZyB?[J]@[BEGnALjAMPEx@LnC[nBCl@b@|DVdAn@`BdBzI`A`Cv@fCNlA@`@Cd@UbA@VE@DJV?p@l@d@rAN|@PzBnA`FZlCh@zBl@pDHVr@j@hAnCjAhAb@fAZf@`DdD`A`@v@HPRL^@h@Ov@e@D}@v@e@`AKv@AbBDrBP~Cs@|DO~Aw@|EMfBJzCInAg@nC@n@HOTGjA}@xBPj@Rl@A|A[PHLKf@C\\Ub@GxE@TKdAXrAf@bDQn@a@bAR`BOr@BxB\\V`@jBDfAVvDPhA]`AIpAz@rBRLT~An@b@n@NHN^lAtAnB|@h@j@b@TX`@`@nApDtEfDbBlBAt@ZV@RY`Bq@lAaCl@YVcAl@gAz@Db@rBd@pF\\`A`AdEn@xB@^lA~HJjC|@bBNz@NhCE`@Df@ENDJPrACd@HfAb@lDDhB?nB[|DG|BQx@G~AHl@\\\\b@t@E@?ZFh@BfBKdAMRBfEZ`KI~@k@xB[x@[bBQDa@tAgApCo@rC[|@{BvEm@~ADN[]_@@i@k@g@OkAcCYUEBW_@G_@gBaAyAoBkAi@]KwAJUOwBMSZOr@IvC',
'resource_state': 2},
'trainer': False,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [59.87336757592857, 10.84704838693142],
'end_latlng': [59.87276793457568, 10.852464437484741],
'average_speed': 2.035,
'max_speed': 9.258,
'average_temp': 24,
'has_heartrate': False,
'heartrate_opt_out': False,
'display_hide_heartrate_option': False,
'elev_high': 264.2,
'elev_low': 157.4,
'upload_id': 11121535121,
'upload_id_str': '11121535121',
'external_id': 'garmin_ping_310123433770',
'from_accepted_tag': True,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Nordic Ski',
'distance': 6468.9,
'moving_time': 2994,
'elapsed_time': 8562,
'total_elevation_gain': 181.0,
'type': 'NordicSki',
'sport_type': 'NordicSki',
'id': 10397016677,
'start_date': '2023-12-16T12:11:24Z',
'start_date_local': '2023-12-16T13:11:24Z',
'timezone': '(GMT+01:00) Europe/Oslo',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 1,
'kudos_count': 5,
'comment_count': 0,
'athlete_count': 4,
'photo_count': 0,
'map': {'id': 'a10397016677',
'summary_polyline': 'i|llJosfaA@a@?cBSyCUgCCq@B{@Js@Fu@Eo@Oe@Gm@EeCHkCNaALa@JKLCNAd@Jp@K`ANX?bASPDt@^RBvAjBfAbA^j@ZTbA|ANf@NVHJ\\PVZLH\\N\\DBAFOABD]No@Xg@AMBIJMHMFEF[LOFQLMBIT}@N[BW`@qAFYLYBM@UFKVmA`AgBP}@x@eC\\wABsAO_BGcGG}@DSA_@By@Jc@Fk@Au@MkBUw@Um@Cg@DuAPqBPuE?q@BgCEeAUaCQ_DK{@C_DGqAC]IYm@yAKa@a@wCY{AIkAc@uCsAgF[}AW}@Kg@E]IaDIm@YmAGMYMU?OHU`@g@bBGNSPMBUJC?q@|AERORGDK?QF}@r@QHGBg@?y@Oc@Qs@UiAWYM[CgBcBS[KSGCk@q@Yc@e@cAOUkB}Ao@a@SSWOMOo@cAoA{AYSSGc@IYK_@ASG]Qi@o@MAe@We@?MB[H[Bk@XO?K@IDK@e@?OCMGSAKC[D]BWEGMECk@IyA]oA_@m@Kc@EWDsAf@YDg@@gAEy@FYCo@BKAMBa@KUOYY[USIy@Gi@B]Gc@?E?SNI@SLO?EDS@UDUHs@\\c@NO@iAEOGIAGDK?IDSDK?GEMAIIIAEESGm@EMHI@EHC?KPYVSFOKIOA]?aABKAQDg@CK@EFE?EDEBQFM@YGe@B{@Cg@BKIs@@k@BOAOVeBH[@MNo@VwBHa@JUB]DOBWBELcABy@EOCCEM?g@E]?cGR_ARm@PUb@a@RGZCPGHMDS?SQMCGC?EDE?GJEOEBELGAG@KJKCQBO]IACKEAGSS[IAGKSEEOEACEa@[Y[MQUM[q@a@s@g@y@QOQYW[EWYq@_@e@m@uAGQs@qDq@qEOi@QiAs@{CY_DYg@mAmAS]EWAQB[VcABW?_@QcAQy@y@mBc@yAMi@WuAm@gCGIKi@@OK_@s@yA',
'resource_state': 2},
'trainer': False,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [59.87345843575895, 10.847598826512694],
'end_latlng': [59.891640124842525, 10.907219052314758],
'average_speed': 2.161,
'max_speed': 9.522,
'average_temp': 17,
'average_watts': 105.2,
'max_watts': 380,
'weighted_average_watts': 122,
'kilojoules': 315.1,
'device_watts': True,
'has_heartrate': True,
'average_heartrate': 121.7,
'max_heartrate': 156.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 260.6,
'elev_low': 161.8,
'upload_id': 11128952413,
'upload_id_str': '11128952413',
'external_id': 'garmin_ping_310337228123',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Evening Weight Training',
'distance': 0.0,
'moving_time': 2307,
'elapsed_time': 2307,
'total_elevation_gain': 0,
'type': 'WeightTraining',
'sport_type': 'WeightTraining',
'id': 10380405316,
'start_date': '2023-12-14T18:08:25Z',
'start_date_local': '2023-12-14T19:08:25Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 2,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10380405316', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 28,
'has_heartrate': True,
'average_heartrate': 124.1,
'max_heartrate': 151.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11111516794,
'upload_id_str': '11111516794',
'external_id': 'garmin_ping_309815410734',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Evening Run',
'distance': 2768.6,
'moving_time': 1204,
'elapsed_time': 1204,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10380149955,
'start_date': '2023-12-14T17:41:10Z',
'start_date_local': '2023-12-14T18:41:10Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10380149955', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.3,
'max_speed': 2.986,
'average_cadence': 74.3,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 163.0,
'max_heartrate': 180.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11111255088,
'upload_id_str': '11111255088',
'external_id': 'garmin_ping_309808812331',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Evening Run',
'distance': 8879.2,
'moving_time': 2969,
'elapsed_time': 2969,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10369911775,
'start_date': '2023-12-12T17:31:54Z',
'start_date_local': '2023-12-12T18:31:54Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 4,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10369911775', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.991,
'max_speed': 4.605,
'average_cadence': 74.9,
'average_temp': 24,
'has_heartrate': True,
'average_heartrate': 168.2,
'max_heartrate': 192.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11100619755,
'upload_id_str': '11100619755',
'external_id': 'garmin_ping_309477573591',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Morning Nordic Ski',
'distance': 7998.1,
'moving_time': 2687,
'elapsed_time': 3025,
'total_elevation_gain': 122.0,
'type': 'NordicSki',
'sport_type': 'NordicSki',
'id': 10357303721,
'start_date': '2023-12-10T09:50:38Z',
'start_date_local': '2023-12-10T10:50:38Z',
'timezone': '(GMT+01:00) Europe/Oslo',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 17,
'kudos_count': 8,
'comment_count': 0,
'athlete_count': 2,
'photo_count': 0,
'map': {'id': 'a10357303721',
'summary_polyline': 's}jlJareaARt@RX`@d@NDd@ID@NRrAxDZfABR@r@GtAIn@QXKBc@SW[W]Ug@C[C{AM}@Qg@MU_@]EIa@}BUmBIg@IQKAIBEHET@|@HtA@xABf@Ch@EFE?MQIgBIg@K_@CECFIDIAOIm@g@]i@i@gBe@u@WKm@ISMKQGg@ZcCN_@FELALBHDxAdBjBfC\\X`@N\\BHG?MWoCIWGKOKq@_@s@W][IKKWGc@BYDILMJC\\LNHh@j@l@vA^^JXPvBDfAJn@X`BFRP`@j@r@TBTIH@NJ^dAlAtDBVChBIv@KXKDM?KCm@g@Q[Q{@C_AMgAQi@e@i@Ua@i@{CQoAMc@QKG@GRALJvABrA?`AB\\Eh@GJEACCGSEyAG[GOe@MSKQK]_@Yo@_@wAQ[YY]SKCSASEIEKQCM?_@TuBNg@NKJATJrAxAx@dAl@~@NP\\ZTHN@VCJI@KGc@EcAKm@EKSSSMs@_@i@QQMOQMWGYAO@UBQPWP?XLXTl@bAXp@TXT\\H\\B^TrD\\jBNf@RZb@d@R@NGF?TV\\`Af@bBNRHVJZD^?|AG`AKZGFI@KA[U[a@S[Ie@G}AUuAKYi@q@MWi@qCUgBIMOIGBINAHFhB@bA?tBAl@MOIWGqAMg@KK_@KUMm@e@QEEc@Ow@I]MWe@i@MEq@GICMMKSEi@T_CPe@NKL?h@^l@r@fBfCRb@x@f@^DTG@MGgAGa@Gu@ISGGu@_@eAe@]YEMK_AB]HIPGZNb@Z\\d@f@jA^b@Nv@TrDHr@P~@Pd@Rb@b@h@LBb@AJFNPb@lAt@dCJh@B^CxAGp@KZIFI?UI]_@a@m@Kk@GiAKaAQc@u@}@MW_@uCSsBKUOEGBEFCVBbAHlEC^ELEBEAGIEkAGg@IWQOk@Qc@]c@y@e@}AYg@]Sy@GOIMUEKEY@KRgBH[JYFIFEPAD@JHvBbCdAxAb@`@RJVDT@JE@U[oC[[wBiA[]M_@CWBUFSLKLCJ@d@TTP`@f@\\z@FNNPJNJZFVDr@N~CHf@D`@Pl@HR^h@LVTDd@EPHFJ\\dAZpARh@Tp@BPBh@EfAKbAOVIBIAMEMI]_@QYM_@Ge@A[',
'resource_state': 2},
'trainer': False,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [59.86453916877508, 10.847949357703328],
'end_latlng': [59.863721346482635, 10.846687210723758],
'average_speed': 2.977,
'max_speed': 8.628,
'average_temp': 14,
'average_watts': 127.5,
'max_watts': 507,
'weighted_average_watts': 138,
'kilojoules': 342.7,
'device_watts': True,
'has_heartrate': True,
'average_heartrate': 157.4,
'max_heartrate': 178.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 140.6,
'elev_low': 124.2,
'upload_id': 11087390931,
'upload_id_str': '11087390931',
'external_id': 'garmin_ping_309076417975',
'from_accepted_tag': False,
'pr_count': 6,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Evening Run',
'distance': 7080.1,
'moving_time': 2310,
'elapsed_time': 2742,
'total_elevation_gain': 71.0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10344112132,
'start_date': '2023-12-07T17:18:53Z',
'start_date_local': '2023-12-07T18:18:53Z',
'timezone': '(GMT+01:00) Europe/Oslo',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 7,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10344112132',
'summary_polyline': 'uctlJaty`A`Ax@j@n@NDb@bBJv@FfANXJi@?WFKHDTb@TFNAB@R`Bd@lCb@xDZzBd@`Cl@|BJ|@Dx@l@hB`A~EDp@APRNFRZpAV~ADz@?dBErAAlCEfCKjBChAAlA@n@XvDPjAPtBLfAhAdFDnBEnAMx@Ur@Ix@IhBOnCSvBO|@Sv@g@xDYbC_@nCGXUn@SpAc@pBS`BCv@Oz@IN]dEUfBs@dEC^M^[fBYlAMbAMXK`@SdBUrAAh@Sx@Kt@W~@k@bE[`BQzA]pBK|@CpBQjAG\\Uh@SrBQ|@S|A_@jBOhBYhAKr@Ov@OpAS`@e@fBMv@@Nf@{AF_@DIHa@HQJ[Lk@B]@OCK@UHQZkAPy@NcANe@VwAFi@DKZkCEUAg@F{Aj@gF^eCZ{AHk@^cBRs@Iw@Vo@D[r@kDd@{ADg@D_BLuAb@mBd@kCD_@q@IUOcA[WKMOa@aB[{@AISq@KQaAwA{AcBc@_@SYSOESe@{@Om@E_@[_@Sw@O_BUgAW}@UYAKV{@F]`@}BHcAL{@GMu@QyAcA]a@GaAYgC?SFgADUZg@Di@LSJWTqAD_APaBBu@@iAAa@@e@Pe@JgA^eCDq@`@}BpAiE`@aARs@Lw@Ps@r@{BVo@j@sBNu@d@cATw@FOTmBDG~@e@\\k@^c@j@cADUEi@BUB_BC}@[oBWoCcAgDGkAQw@Se@aAyCYm@W[OGqA]w@O]MIIW_@Oy@',
'resource_state': 2},
'trainer': False,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [59.91287073120475, 10.786831537261605],
'end_latlng': [59.913376830518246, 10.786710418760777],
'average_speed': 3.065,
'max_speed': 4.68,
'average_cadence': 78.4,
'average_temp': 12,
'has_heartrate': True,
'average_heartrate': 157.8,
'max_heartrate': 180.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 60.6,
'elev_low': 6.8,
'upload_id': 11073653382,
'upload_id_str': '11073653382',
'external_id': 'garmin_ping_308637535072',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Weight Training',
'distance': 0.0,
'moving_time': 1516,
'elapsed_time': 1516,
'total_elevation_gain': 0,
'type': 'WeightTraining',
'sport_type': 'WeightTraining',
'id': 10342792090,
'start_date': '2023-12-06T16:50:33Z',
'start_date_local': '2023-12-06T17:50:33Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 3,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10342792090', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 0.0,
'max_speed': 0.0,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 137.6,
'max_heartrate': 175.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11072289129,
'upload_id_str': '11072289129',
'external_id': 'garmin_ping_308598886613',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False},
{'resource_state': 2,
'athlete': {'id': 40990567, 'resource_state': 1},
'name': 'Afternoon Run',
'distance': 4222.7,
'moving_time': 1570,
'elapsed_time': 1570,
'total_elevation_gain': 0,
'type': 'Run',
'sport_type': 'Run',
'workout_type': None,
'id': 10342791527,
'start_date': '2023-12-06T16:18:39Z',
'start_date_local': '2023-12-06T17:18:39Z',
'timezone': '(GMT+01:00) Africa/Algiers',
'utc_offset': 3600.0,
'location_city': None,
'location_state': None,
'location_country': None,
'achievement_count': 0,
'kudos_count': 4,
'comment_count': 0,
'athlete_count': 1,
'photo_count': 0,
'map': {'id': 'a10342791527', 'summary_polyline': '', 'resource_state': 2},
'trainer': True,
'commute': False,
'manual': False,
'private': False,
'visibility': 'everyone',
'flagged': False,
'gear_id': None,
'start_latlng': [],
'end_latlng': [],
'average_speed': 2.69,
'max_speed': 3.488,
'average_cadence': 74.8,
'average_temp': 26,
'has_heartrate': True,
'average_heartrate': 161.6,
'max_heartrate': 179.0,
'heartrate_opt_out': False,
'display_hide_heartrate_option': True,
'elev_high': 0.0,
'elev_low': 0.0,
'upload_id': 11072288620,
'upload_id_str': '11072288620',
'external_id': 'garmin_ping_308598873919',
'from_accepted_tag': False,
'pr_count': 0,
'total_photo_count': 0,
'has_kudoed': False}]
In [299]:
raw_recent_played
Out[299]:
{'error': {'status': 401, 'message': 'The access token expired'}}
Extra data¶
Some extra data saved to help overlap with some activities.
In [236]:
extra_tracks = pd.read_csv("../spotify/history_240130_240203.csv")
extra_tracks
Out[236]:
| start | id | artist | name | |
|---|---|---|---|---|
| 0 | 2024-01-30T04:33:47.317Z | 2h1a5N6j2o6AYATr89sYIb | Channel Tres | Controller - Walker & Royce Remix |
| 1 | 2024-01-30T04:38:39.749Z | 2qNfRjvI0SQ5JUVKWg1eOK | Offer Nissim | Fucker |
| 2 | 2024-01-30T04:40:58.325Z | 37cv9aDLaYWjNF8poD202x | Walker & Royce | My Own Thang (feat. Sophiegrophy) - John Tejad... |
| 3 | 2024-01-30T04:46:24.898Z | 37cv9aDLaYWjNF8poD202x | Walker & Royce | My Own Thang (feat. Sophiegrophy) - John Tejad... |
| 4 | 2024-01-30T04:50:56.829Z | 4WH09DYN9V10uNnPx23EM7 | Freaky Boy | I Fink U Freeky - I Think You're Freaky |
| ... | ... | ... | ... | ... |
| 181 | 2024-02-03T10:45:46.516Z | 1cWHaz5GDrhfnkXTIYlGHh | Brian Eno | Stars - Remastered 2005 |
| 182 | 2024-02-03T10:50:13.092Z | 3an5DoMqoNpJEQwfA48A4c | Brian Eno | Lizard Point - Remastered 2004 |
| 183 | 2024-02-03T10:59:25.110Z | 2JbqOGlvO809TTEtteJFwp | Brian Eno | The Lost Day - Remastered 2004 |
| 184 | 2024-02-03T11:04:53.077Z | 33xP7dQigSmT5apPCcDVBE | Brian Eno | Tal Coat - Remastered 2004 |
| 185 | 2024-02-03T11:07:54.131Z | 0vI3D1jkXAaqX4wmFVTYX7 | Brian Eno | Shadow - Remastered 2004 |
186 rows × 4 columns
Convert to dataframe¶
In [173]:
recent_played_df = pd.DataFrame(raw_recent_played["items"])
recent_played_df.head()
Out[173]:
| track | played_at | context | |
|---|---|---|---|
| 0 | {'album': {'album_type': 'single', 'artists': ... | 2024-02-03T20:22:58.221Z | {'type': 'playlist', 'href': 'https://api.spot... |
| 1 | {'album': {'album_type': 'compilation', 'artis... | 2024-02-03T20:22:51.340Z | {'type': 'playlist', 'href': 'https://api.spot... |
| 2 | {'album': {'album_type': 'compilation', 'artis... | 2024-02-03T20:18:41.765Z | {'type': 'playlist', 'href': 'https://api.spot... |
| 3 | {'album': {'album_type': 'single', 'artists': ... | 2024-02-03T15:31:35.065Z | {'type': 'playlist', 'href': 'https://api.spot... |
| 4 | {'album': {'album_type': 'single', 'artists': ... | 2024-02-03T15:28:47.082Z | {'type': 'playlist', 'href': 'https://api.spot... |
In [174]:
activities_df = pd.DataFrame(raw_activities)
print(*activities_df.columns.to_list(), sep="\n")
resource_state athlete name distance moving_time elapsed_time total_elevation_gain type sport_type id start_date start_date_local timezone utc_offset location_city location_state location_country achievement_count kudos_count comment_count athlete_count photo_count map trainer commute manual private visibility flagged gear_id start_latlng end_latlng average_speed max_speed average_temp has_heartrate heartrate_opt_out display_hide_heartrate_option elev_high elev_low upload_id upload_id_str external_id from_accepted_tag pr_count total_photo_count has_kudoed average_cadence average_heartrate max_heartrate workout_type average_watts max_watts weighted_average_watts kilojoules device_watts
Preprocessing¶
Prepare the dataframes to be easily comparable.
In [193]:
def preprocess_activities(activities_df):
activities_df = activities_df.copy()
# stored in Oslo timezone
activities_df["start"] = pd.to_datetime(activities_df["start_date"])
# build end_date from start_date and elapsed_time
activities_df["end"] = activities_df["start"] + pd.to_timedelta(activities_df["elapsed_time"], unit="s")
# start and end are in UTC, convert to Oslo timezone
activities_df["start"] = activities_df["start"].dt.tz_convert("Europe/Oslo")
activities_df["end"] = activities_df["end"].dt.tz_convert("Europe/Oslo")
# drop unnecessary columns
return activities_df[["athlete", "id", "start", "end"]]
activities = preprocess_activities(activities_df)
activities.head()
Out[193]:
| athlete | id | start | end | |
|---|---|---|---|---|
| 0 | {'id': 40990567, 'resource_state': 1} | 10688279212 | 2024-02-03 15:55:59+01:00 | 2024-02-03 16:23:07+01:00 |
| 1 | {'id': 40990567, 'resource_state': 1} | 10682232510 | 2024-02-02 18:03:10+01:00 | 2024-02-02 19:07:32+01:00 |
| 2 | {'id': 40990567, 'resource_state': 1} | 10678755706 | 2024-02-02 06:47:05+01:00 | 2024-02-02 07:18:10+01:00 |
| 3 | {'id': 40990567, 'resource_state': 1} | 10672180060 | 2024-02-01 07:14:17+01:00 | 2024-02-01 07:27:05+01:00 |
| 4 | {'id': 40990567, 'resource_state': 1} | 10672137037 | 2024-02-01 06:37:09+01:00 | 2024-02-01 07:10:13+01:00 |
In [227]:
def preprocess_tracks(recent_played_df):
recent_played_df = recent_played_df.copy().sort_values("played_at")
# convert to datetime
recent_played_df["start"] = pd.to_datetime(recent_played_df["played_at"], format='mixed')
# build end_time
expected_end_time = recent_played_df["start"] + pd.to_timedelta(recent_played_df["track"].apply(lambda x: x["duration_ms"]), unit="ms")
next_song_start = recent_played_df["start"].shift(1)
recent_played_df["end"] = expected_end_time.combine(next_song_start, min)
# rename to start, end, name, artist, track id
recent_played_df["track_name"] = recent_played_df["track"].apply(lambda x: x["name"])
recent_played_df["artist"] = recent_played_df["track"].apply(lambda x: x["artists"][0]["name"])
recent_played_df["id"] = recent_played_df["track"].apply(lambda x: x["id"])
# start and end are in UTC, convert to Oslo timezone
recent_played_df["start"] = recent_played_df["start"].dt.tz_convert("Europe/Oslo")
recent_played_df["end"] = recent_played_df["end"].dt.tz_convert("Europe/Oslo")
# drop unnecessary columns
return recent_played_df[["start", "end", "track_name", "artist", "id"]]
tracks = preprocess_tracks(recent_played_df)
tracks.head()
Out[227]:
| start | end | track_name | artist | id | |
|---|---|---|---|---|---|
| 49 | 2024-02-03 12:04:53.077000+01:00 | 2024-02-03 12:10:20.983000+01:00 | Tal Coat - Remastered 2004 | Brian Eno | 33xP7dQigSmT5apPCcDVBE |
| 48 | 2024-02-03 12:07:54.131000+01:00 | 2024-02-03 12:04:53.077000+01:00 | Shadow - Remastered 2004 | Brian Eno | 0vI3D1jkXAaqX4wmFVTYX7 |
| 47 | 2024-02-03 12:13:25.107000+01:00 | 2024-02-03 12:07:54.131000+01:00 | Lantern Marsh - Remastered 2004 | Brian Eno | 5QUimuUIhEtuMU475o4xVB |
| 46 | 2024-02-03 12:18:52.089000+01:00 | 2024-02-03 12:13:25.107000+01:00 | Unfamiliar Wind (Leeks Hills) - Remastered 2004 | Brian Eno | 4SngUdjKC7Njrz5UVik26A |
| 45 | 2024-02-03 12:23:00.075000+01:00 | 2024-02-03 12:18:52.089000+01:00 | A Clearing - Remastered 2004 | Brian Eno | 4y5qZUZU5sojHT033PFP26 |
In [237]:
def preprocess_extra_tracks(extra_tracks):
extra_tracks = extra_tracks.copy().sort_values("start", ascending=False)
extra_tracks["start"] = pd.to_datetime(extra_tracks["start"])
# build end_time
expected_end_time = pd.to_datetime(extra_tracks["start"]) + pd.to_timedelta(30, unit="m")
next_song_start = pd.to_datetime(extra_tracks["start"]).shift(1)
extra_tracks["end"] = expected_end_time.combine(next_song_start, min)
# rename name to track_name to help pandas
if "name" in extra_tracks.columns:
extra_tracks["track_name"] = extra_tracks["name"]
# my timezone is off, need to add an hour (hack)
if extra_tracks.start.iloc[0].hour < 6:
extra_tracks["start"] = extra_tracks["start"] + pd.to_timedelta(1, unit="h")
extra_tracks["end"] = extra_tracks["end"] + pd.to_timedelta(1, unit="h")
# start and end are in UTC, convert to Oslo timezone
extra_tracks["start"] = extra_tracks["start"].dt.tz_convert("Europe/Oslo")
extra_tracks["end"] = extra_tracks["end"].dt.tz_convert("Europe/Oslo")
return extra_tracks[["start", "end", "track_name", "artist", "id"]]
extra_tracks = preprocess_extra_tracks(extra_tracks)
extra_tracks.head()
Out[237]:
| start | end | track_name | artist | id | |
|---|---|---|---|---|---|
| 185 | 2024-02-03 12:07:54.131000+01:00 | 2024-02-03 12:37:54.131000+01:00 | Shadow - Remastered 2004 | Brian Eno | 0vI3D1jkXAaqX4wmFVTYX7 |
| 184 | 2024-02-03 12:04:53.077000+01:00 | 2024-02-03 12:07:54.131000+01:00 | Tal Coat - Remastered 2004 | Brian Eno | 33xP7dQigSmT5apPCcDVBE |
| 183 | 2024-02-03 11:59:25.110000+01:00 | 2024-02-03 12:04:53.077000+01:00 | The Lost Day - Remastered 2004 | Brian Eno | 2JbqOGlvO809TTEtteJFwp |
| 182 | 2024-02-03 11:50:13.092000+01:00 | 2024-02-03 11:59:25.110000+01:00 | Lizard Point - Remastered 2004 | Brian Eno | 3an5DoMqoNpJEQwfA48A4c |
| 181 | 2024-02-03 11:45:46.516000+01:00 | 2024-02-03 11:50:13.092000+01:00 | Stars - Remastered 2005 | Brian Eno | 1cWHaz5GDrhfnkXTIYlGHh |
In [293]:
# merge all tracks, drop duplicates and sort by start time
all_tracks = pd.concat([tracks, extra_tracks]).drop_duplicates().sort_values("start").reset_index(drop=True)
all_tracks.head()
Out[293]:
| start | end | track_name | artist | id | |
|---|---|---|---|---|---|
| 0 | 2024-01-30 05:33:47.317000+01:00 | 2024-01-30 05:38:39.749000+01:00 | Controller - Walker & Royce Remix | Channel Tres | 2h1a5N6j2o6AYATr89sYIb |
| 1 | 2024-01-30 05:38:39.749000+01:00 | 2024-01-30 05:40:58.325000+01:00 | Fucker | Offer Nissim | 2qNfRjvI0SQ5JUVKWg1eOK |
| 2 | 2024-01-30 05:40:58.325000+01:00 | 2024-01-30 05:46:24.898000+01:00 | My Own Thang (feat. Sophiegrophy) - John Tejad... | Walker & Royce | 37cv9aDLaYWjNF8poD202x |
| 3 | 2024-01-30 05:46:24.898000+01:00 | 2024-01-30 05:50:56.829000+01:00 | My Own Thang (feat. Sophiegrophy) - John Tejad... | Walker & Royce | 37cv9aDLaYWjNF8poD202x |
| 4 | 2024-01-30 05:50:56.829000+01:00 | 2024-01-30 05:57:07.859000+01:00 | I Fink U Freeky - I Think You're Freaky | Freaky Boy | 4WH09DYN9V10uNnPx23EM7 |
Find songs per activity¶
In [251]:
activities.head()
Out[251]:
| athlete | id | start | end | |
|---|---|---|---|---|
| 0 | {'id': 40990567, 'resource_state': 1} | 10688279212 | 2024-02-03 15:55:59+01:00 | 2024-02-03 16:23:07+01:00 |
| 1 | {'id': 40990567, 'resource_state': 1} | 10682232510 | 2024-02-02 18:03:10+01:00 | 2024-02-02 19:07:32+01:00 |
| 2 | {'id': 40990567, 'resource_state': 1} | 10678755706 | 2024-02-02 06:47:05+01:00 | 2024-02-02 07:18:10+01:00 |
| 3 | {'id': 40990567, 'resource_state': 1} | 10672180060 | 2024-02-01 07:14:17+01:00 | 2024-02-01 07:27:05+01:00 |
| 4 | {'id': 40990567, 'resource_state': 1} | 10672137037 | 2024-02-01 06:37:09+01:00 | 2024-02-01 07:10:13+01:00 |
In [263]:
recent_activities = activities[activities["start"] > all_tracks["start"].min()].copy()
recent_activities
Out[263]:
| athlete | id | start | end | |
|---|---|---|---|---|
| 0 | {'id': 40990567, 'resource_state': 1} | 10688279212 | 2024-02-03 15:55:59+01:00 | 2024-02-03 16:23:07+01:00 |
| 1 | {'id': 40990567, 'resource_state': 1} | 10682232510 | 2024-02-02 18:03:10+01:00 | 2024-02-02 19:07:32+01:00 |
| 2 | {'id': 40990567, 'resource_state': 1} | 10678755706 | 2024-02-02 06:47:05+01:00 | 2024-02-02 07:18:10+01:00 |
| 3 | {'id': 40990567, 'resource_state': 1} | 10672180060 | 2024-02-01 07:14:17+01:00 | 2024-02-01 07:27:05+01:00 |
| 4 | {'id': 40990567, 'resource_state': 1} | 10672137037 | 2024-02-01 06:37:09+01:00 | 2024-02-01 07:10:13+01:00 |
| 5 | {'id': 40990567, 'resource_state': 1} | 10668078046 | 2024-01-31 15:49:16+01:00 | 2024-01-31 16:30:22+01:00 |
| 6 | {'id': 40990567, 'resource_state': 1} | 10667818129 | 2024-01-31 15:24:34+01:00 | 2024-01-31 15:43:39+01:00 |
| 7 | {'id': 40990567, 'resource_state': 1} | 10658118191 | 2024-01-30 06:30:04+01:00 | 2024-01-30 07:20:18+01:00 |
In [264]:
x = recent_activities.iloc[-1]
def overlap(x, y):
return (x.start < y.end) & (x.end > y.start)
def build_track_str(x):
return f"{x.track_name} - {x.artist}"
def get_tracklist(x, y):
return y[overlap(x, y)].apply(build_track_str, axis=1).to_list()
get_tracklist(x, all_tracks)
Out[264]:
['Power - Hoxton Whores', 'Thank You (Not So Bad) - Dimitri Vegas & Like Mike', 'Renaissance - BUNT.', 'Praise The Lord (Da Shine) (feat. Skepta) - Durdenhauer Edit - Durdenhauer', 'Vois sur ton chemin - Techno Mix - BENNETT', 'La leçon particulière - Lavern', 'Tell Me Why - MEDUZA Remix - Supermode', 'Prada - cassö', 'Miss You - southstar', 'Mask Off - Nicolas Julian', 'Tell Me Why - Maddix Remix - Supermode', 'A Milli - SIDEPIECE Remix - Lil Wayne', 'No Type - Pegassi', 'Turn On The Lights again.. (feat. Future) - Fred again..']
In [267]:
recent_activities["tracklist"] = recent_activities.apply(lambda x: get_tracklist(x, all_tracks), axis=1)
recent_activities
Out[267]:
| athlete | id | start | end | tracklist | |
|---|---|---|---|---|---|
| 0 | {'id': 40990567, 'resource_state': 1} | 10688279212 | 2024-02-03 15:55:59+01:00 | 2024-02-03 16:23:07+01:00 | [Sound Bath Escape - The Tibetan Singing Bowls... |
| 1 | {'id': 40990567, 'resource_state': 1} | 10682232510 | 2024-02-02 18:03:10+01:00 | 2024-02-02 19:07:32+01:00 | [] |
| 2 | {'id': 40990567, 'resource_state': 1} | 10678755706 | 2024-02-02 06:47:05+01:00 | 2024-02-02 07:18:10+01:00 | [Thank You (Not So Bad) - Dimitri Vegas & Like... |
| 3 | {'id': 40990567, 'resource_state': 1} | 10672180060 | 2024-02-01 07:14:17+01:00 | 2024-02-01 07:27:05+01:00 | [] |
| 4 | {'id': 40990567, 'resource_state': 1} | 10672137037 | 2024-02-01 06:37:09+01:00 | 2024-02-01 07:10:13+01:00 | [] |
| 5 | {'id': 40990567, 'resource_state': 1} | 10668078046 | 2024-01-31 15:49:16+01:00 | 2024-01-31 16:30:22+01:00 | [Satisfaction - Hardwell & Maddix Remix - Davi... |
| 6 | {'id': 40990567, 'resource_state': 1} | 10667818129 | 2024-01-31 15:24:34+01:00 | 2024-01-31 15:43:39+01:00 | [Heute Nacht - Maddix, Prada - cassö] |
| 7 | {'id': 40990567, 'resource_state': 1} | 10658118191 | 2024-01-30 06:30:04+01:00 | 2024-01-30 07:20:18+01:00 | [Power - Hoxton Whores, Thank You (Not So Bad)... |
Sweet! Now our activities have the list of tracks played during them. NExt step is to update the descriptions of the activities with these tracks.
In [296]:
recent_activities.apply(get_tracklist, y=all_tracks, axis=1)
Out[296]:
0 [Sound Bath Escape - The Tibetan Singing Bowls... 1 [] 2 [Thank You (Not So Bad) - Dimitri Vegas & Like... 3 [] 4 [] 5 [Satisfaction - Hardwell & Maddix Remix - Davi... 6 [Heute Nacht - Maddix, Prada - cassö] 7 [Power - Hoxton Whores, Thank You (Not So Bad)... dtype: object
Add tracklists to activity descriptions¶
To do this properly, we first need to get_activity(id) to make sure the description doesn't already have a tracklist.
If there is no tracklist present, we can then update_activity(id, params={}) with description + tracklist.
In [291]:
def get_activity(id, access_token=strava_tokens["access_token"]):
URL = f"https://www.strava.com/api/v3/activities/{id}" # api-endpoint for recently played
HEAD = {"Authorization": f"Bearer {access_token}"}
content = r.get(URL, headers=HEAD)
return content.json()
def update_activity(id, data, access_token=strava_tokens["access_token"]):
URL = f"https://www.strava.com/api/v3/activities/{id}" # api-endpoint for recently played
HEAD = {"Authorization": f"Bearer {access_token}"}
content = r.put(URL, headers=HEAD, data=data)
return content
def contains_tracklist(description):
return (description is not None) and "Tracklist" in description
def add_tracklist(id, tracklist, access_token=strava_tokens["access_token"]):
activity = get_activity(id, access_token)
description = activity["description"]
tracks = "\n".join(tracklist)
if contains_tracklist(description) or len(tracks) == 0:
return description
description = (
description + tracklist_template.format(tracks)
if description else tracklist_template.format(tracks)
)
content = update_activity(id, {"description": description}, access_token)
print(content.json()) if content.status_code != 200 else None
return get_activity(id, access_token)["description"]
tracklist_template = """
Tracklist:
{}
Uploaded automatically using https://github.com/pmhalvor/endurabeats/
"""
add_tracklist(10672137037, [])
Out[291]:
''
In [292]:
recent_activities_w_descrptions = recent_activities.copy().apply(lambda x: add_tracklist(x.id, x.tracklist), axis=1)
recent_activities_w_descrptions
Out[292]:
0 \nTracklist:\nSound Bath Escape - The Tibetan ... 1 None 2 \nTracklist:\nThank You (Not So Bad) - Dimitri... 3 None 4 5 \nTracklist:\nSatisfaction - Hardwell & Maddix... 6 \nTracklist:\nHeute Nacht - Maddix\nPrada - ca... 7 \nTracklist:\nPower - Hoxton Whores\nThank You... dtype: object
In [ ]: