also download .gpx and .fit file for each activity
This commit is contained in:
parent
a8572d7e13
commit
35aea411d0
19
README.md
19
README.md
@ -35,10 +35,21 @@ Once started, the program will prompt you for your account password and then
|
|||||||
log in to your Garmin Connect account to download all activities to a destination
|
log in to your Garmin Connect account to download all activities to a destination
|
||||||
directory on your machine.
|
directory on your machine.
|
||||||
|
|
||||||
For each activity, three files are stored: an activity summary (JSON),
|
For each activity, these files are stored:
|
||||||
activity details (JSON) and the activity GPX file. All files are written
|
|
||||||
to the same directory (``activities/<timestamp>/`` by default).
|
- an activity summary file (JSON)
|
||||||
Each activity file is prefixed by its upload timestamp and its activity id.
|
|
||||||
|
- an activity details file (JSON)
|
||||||
|
|
||||||
|
- an activity GPX file (XML)
|
||||||
|
|
||||||
|
- an activity TCX file (XML)
|
||||||
|
|
||||||
|
- an activity FIT file (binary)
|
||||||
|
|
||||||
|
All files are written to the same directory (``activities/<timestamp>/``
|
||||||
|
by default). Each activity file is prefixed by its upload timestamp and its
|
||||||
|
activity id.
|
||||||
|
|
||||||
|
|
||||||
Library import
|
Library import
|
||||||
|
@ -67,6 +67,8 @@ if __name__ == "__main__":
|
|||||||
activity_summary = client.get_activity_summary(id)
|
activity_summary = client.get_activity_summary(id)
|
||||||
activity_details = client.get_activity_details(id)
|
activity_details = client.get_activity_details(id)
|
||||||
activity_gpx = client.get_activity_gpx(id)
|
activity_gpx = client.get_activity_gpx(id)
|
||||||
|
activity_tcx = client.get_activity_tcx(id)
|
||||||
|
activity_fit = client.get_activity_fit(id)
|
||||||
|
|
||||||
# for each activitity save the summary, details and GPX file.
|
# for each activitity save the summary, details and GPX file.
|
||||||
creation_millis = activity_summary["activity"]["uploadDate"]["millis"]
|
creation_millis = activity_summary["activity"]["uploadDate"]["millis"]
|
||||||
@ -78,6 +80,8 @@ if __name__ == "__main__":
|
|||||||
summary_file = path_prefix + "_summary.json"
|
summary_file = path_prefix + "_summary.json"
|
||||||
details_file = path_prefix + "_details.json"
|
details_file = path_prefix + "_details.json"
|
||||||
gpx_file = path_prefix + ".gpx"
|
gpx_file = path_prefix + ".gpx"
|
||||||
|
tcx_file = path_prefix + ".tcx"
|
||||||
|
fit_file = path_prefix + ".fit"
|
||||||
with codecs.open(summary_file, encoding="utf-8", mode="w") as f:
|
with codecs.open(summary_file, encoding="utf-8", mode="w") as f:
|
||||||
f.write(json.dumps(
|
f.write(json.dumps(
|
||||||
activity_summary, ensure_ascii=False, indent=4))
|
activity_summary, ensure_ascii=False, indent=4))
|
||||||
@ -86,6 +90,10 @@ if __name__ == "__main__":
|
|||||||
activity_details, ensure_ascii=False, indent=4))
|
activity_details, ensure_ascii=False, indent=4))
|
||||||
with codecs.open(gpx_file, encoding="utf-8", mode="w") as f:
|
with codecs.open(gpx_file, encoding="utf-8", mode="w") as f:
|
||||||
f.write(activity_gpx)
|
f.write(activity_gpx)
|
||||||
|
with codecs.open(tcx_file, encoding="utf-8", mode="w") as f:
|
||||||
|
f.write(activity_tcx)
|
||||||
|
with open(fit_file, mode="wb") as f:
|
||||||
|
f.write(activity_fit)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||||
log.error(u"failed with exception: %s", e)
|
log.error(u"failed with exception: %s", e)
|
||||||
|
@ -7,7 +7,9 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
from StringIO import StringIO
|
||||||
import sys
|
import sys
|
||||||
|
import zipfile
|
||||||
|
|
||||||
#
|
#
|
||||||
# Note: For more detailed information about the API services
|
# Note: For more detailed information about the API services
|
||||||
@ -250,3 +252,37 @@ class GarminClient(object):
|
|||||||
activity_id, response.status_code, response.text))
|
activity_id, response.status_code, response.text))
|
||||||
return response.text
|
return response.text
|
||||||
|
|
||||||
|
|
||||||
|
def get_activity_tcx(self, activity_id):
|
||||||
|
"""Return a TCX (Training Center XML) representation of a
|
||||||
|
given activity.
|
||||||
|
|
||||||
|
:param activity_id: Activity identifier.
|
||||||
|
:type activity_id: int
|
||||||
|
:returns: The TCX representation of the activity as an XML string.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
response = self.session.get("https://connect.garmin.com/proxy/activity-service-1.3/tcx/course/{}".format(activity_id))
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(u"failed to fetch TCX for activity {}: {}\n{}".format(
|
||||||
|
activity_id, response.status_code, response.text))
|
||||||
|
return response.text
|
||||||
|
|
||||||
|
def get_activity_fit(self, activity_id):
|
||||||
|
"""Return a FIT representation for a given activity.
|
||||||
|
|
||||||
|
:param activity_id: Activity identifier.
|
||||||
|
:type activity_id: int
|
||||||
|
:returns: A string with a FIT file for the activity.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.session.get("https://connect.garmin.com/proxy/download-service/files/activity/{}".format(activity_id))
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(u"failed to fetch FIT for activity {}: {}\n{}".format(
|
||||||
|
activity_id, response.status_code, response.text))
|
||||||
|
# fit file returned from server is in a zip archive
|
||||||
|
zipped_fit_file = response.content
|
||||||
|
zip = zipfile.ZipFile(StringIO(zipped_fit_file), mode="r")
|
||||||
|
# return the "<activity-id>.fit" entry from the zip archive
|
||||||
|
return zip.open(str(activity_id) + ".fit").read()
|
||||||
|
Loading…
Reference in New Issue
Block a user