-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add strava_to_garmin sync for #146
- Loading branch information
1 parent
dc31a50
commit b11d846
Showing
8 changed files
with
206 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import argparse | ||
import asyncio | ||
from io import BytesIO | ||
import gpxpy | ||
from xml.etree import ElementTree | ||
import gpxpy.gpx | ||
from datetime import datetime, timedelta | ||
from utils import make_strava_client | ||
from garmin_sync import Garmin | ||
|
||
from config import STRAVA_GARMIN_TYPE_DICT | ||
|
||
|
||
def generate_strava_run_points(start_time, strava_streams): | ||
""" | ||
strava return same len data list | ||
""" | ||
if not (strava_streams.get("time") and strava_streams.get("latlng")): | ||
return None | ||
points_dict_list = [] | ||
time_list = strava_streams["time"].data | ||
time_list = [start_time + timedelta(seconds=int(i)) for i in time_list] | ||
latlng_list = strava_streams["latlng"].data | ||
|
||
for i, j in zip(time_list, latlng_list): | ||
points_dict_list.append( | ||
{ | ||
"latitude": j[0], | ||
"longitude": j[1], | ||
"time": i, | ||
} | ||
) | ||
# add heart rate | ||
if strava_streams.get("heartrate"): | ||
heartrate_list = strava_streams.get("heartrate").data | ||
for index, h in enumerate(heartrate_list): | ||
points_dict_list[index]["heart_rate"] = h | ||
# add altitude | ||
if strava_streams.get("altitude"): | ||
heartrate_list = strava_streams.get("altitude").data | ||
for index, h in enumerate(heartrate_list): | ||
points_dict_list[index]["elevation"] = h | ||
return points_dict_list | ||
|
||
|
||
def make_gpx_from_points(title, points_dict_list): | ||
gpx = gpxpy.gpx.GPX() | ||
gpx.nsmap["gpxtpx"] = "http://www.garmin.com/xmlschemas/TrackPointExtension/v1" | ||
gpx_track = gpxpy.gpx.GPXTrack() | ||
gpx_track.name = title | ||
gpx_track.type = "Run" | ||
gpx.tracks.append(gpx_track) | ||
|
||
# Create first segment in our GPX track: | ||
gpx_segment = gpxpy.gpx.GPXTrackSegment() | ||
gpx_track.segments.append(gpx_segment) | ||
for p in points_dict_list: | ||
if p.get("heart_rate") is None: | ||
point = gpxpy.gpx.GPXTrackPoint(**p) | ||
else: | ||
heart_rate_num = p.pop("heart_rate") | ||
point = gpxpy.gpx.GPXTrackPoint(**p) | ||
gpx_extension_hr = ElementTree.fromstring( | ||
f"""<gpxtpx:TrackPointExtension xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"> | ||
<gpxtpx:hr>{heart_rate_num}</gpxtpx:hr> | ||
</gpxtpx:TrackPointExtension> | ||
""" | ||
) | ||
point.extensions.append(gpx_extension_hr) | ||
gpx_segment.points.append(point) | ||
return gpx.to_xml() | ||
|
||
|
||
async def upload_to_activities(garmin_client, strava_client): | ||
last_activity = await garmin_client.get_activities(0, 1) | ||
if not last_activity: | ||
filters = {} | ||
else: | ||
# is this startTimeGMT must have ? | ||
after_datetime_str = last_activity[0]["startTimeGMT"] | ||
after_datetime = datetime.strptime(after_datetime_str, "%Y-%m-%d %H:%M:%S") | ||
filters = {"after": after_datetime} | ||
strava_activities = list(strava_client.get_activities(**filters)) | ||
files_list = [] | ||
# strava rate limit | ||
for i in strava_activities[:50]: | ||
start_time = i.start_date | ||
activity_type = i.type | ||
types = ["time", "latlng", "altitude", "heartrate", "velocity_smooth", "temp"] | ||
s = strava_client.get_activity_streams(i.id, types=types, resolution="medium") | ||
points = generate_strava_run_points(start_time, s) | ||
if points: | ||
garmin_type = STRAVA_GARMIN_TYPE_DICT.get(activity_type, "running") | ||
gpx_doc = make_gpx_from_points("test", points) | ||
file = BytesIO(bytes(gpx_doc, "utf8")) | ||
files_list.append((file, garmin_type)) | ||
await garmin_client.upload_activities(files_list) | ||
return files_list | ||
|
||
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("client_id", help="strava client id") | ||
parser.add_argument("client_secret", help="strava client secret") | ||
parser.add_argument("strava_refresh_token", help="strava refresh token") | ||
parser.add_argument("email", nargs="?", help="email of garmin") | ||
parser.add_argument("password", nargs="?", help="password of garmin") | ||
parser.add_argument( | ||
"--is-cn", | ||
dest="is_cn", | ||
action="store_true", | ||
help="if garmin accout is com", | ||
) | ||
options = parser.parse_args() | ||
strava_client = make_strava_client( | ||
options.client_id, options.client_secret, options.strava_refresh_token | ||
) | ||
auth_domain = "CN" if options.is_cn else "" | ||
|
||
garmin_client = Garmin(options.email, options.password, auth_domain) | ||
loop = asyncio.get_event_loop() | ||
future = asyncio.ensure_future(upload_to_activities(garmin_client, strava_client)) | ||
loop.run_until_complete(future) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
b11d846
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: