add upload_activity client method and upload_activity.py example
This commit is contained in:
		
							parent
							
								
									c91568d302
								
							
						
					
					
						commit
						c5feaf86c2
					
				@ -12,6 +12,7 @@ from StringIO import StringIO
 | 
				
			|||||||
import sys
 | 
					import sys
 | 
				
			||||||
import zipfile
 | 
					import zipfile
 | 
				
			||||||
import dateutil
 | 
					import dateutil
 | 
				
			||||||
 | 
					import os.path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Note: For more detailed information about the API services
 | 
					# Note: For more detailed information about the API services
 | 
				
			||||||
@ -341,3 +342,63 @@ class GarminClient(object):
 | 
				
			|||||||
        # and cannot be exported to fit
 | 
					        # and cannot be exported to fit
 | 
				
			||||||
        return orig_file if fmt=='fit' else None
 | 
					        return orig_file if fmt=='fit' else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @require_session
 | 
				
			||||||
 | 
					    def upload_activity(self, file, format=None, name=None, description=None, activity_type=None, private=None):
 | 
				
			||||||
 | 
					        """Upload a GPX, TCX, or FIT file for an activity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param file: Path or open file
 | 
				
			||||||
 | 
					        :param format: File format (gpx, tcx, or fit); guessed from filename if None
 | 
				
			||||||
 | 
					        :param name: Optional name for the activity on Garmin Connect
 | 
				
			||||||
 | 
					        :param description: Optional description for the activity on Garmin Connect
 | 
				
			||||||
 | 
					        :param activity_type: Optional activityType key (lowercase: e.g. running, cycling)
 | 
				
			||||||
 | 
					        :param private: If true, then activity will be set as private.
 | 
				
			||||||
 | 
					        :returns: ID of the newly-uploaded activity
 | 
				
			||||||
 | 
					        :rtype: int
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(file, basestring):
 | 
				
			||||||
 | 
					            file = open(file, "rb")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # guess file type if unspecified
 | 
				
			||||||
 | 
					        fn = os.path.basename(file.name)
 | 
				
			||||||
 | 
					        _, ext = os.path.splitext(fn)
 | 
				
			||||||
 | 
					        if format is None:
 | 
				
			||||||
 | 
					            if ext.lower() in ('.gpx','.tcx','.fit'):
 | 
				
			||||||
 | 
					                format = ext.lower()[1:]
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise Exception(u"could not guess file type for {}".format(fn))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # upload it
 | 
				
			||||||
 | 
					        files = dict(data=(fn, file))
 | 
				
			||||||
 | 
					        response = self.session.post("https://connect.garmin.com/proxy/upload-service-1.1/json/upload/.{}".format(format),
 | 
				
			||||||
 | 
					                                     files=files)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check response and get activity ID
 | 
				
			||||||
 | 
					        if response.status_code != 200:
 | 
				
			||||||
 | 
					            raise Exception(u"failed to upload {} for activity: {}\n{}".format(
 | 
				
			||||||
 | 
					                format, response.status_code, response.text))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        j = response.json()
 | 
				
			||||||
 | 
					        if len(j["detailedImportResult"]["failures"]) or len(j["detailedImportResult"]["successes"])!=1:
 | 
				
			||||||
 | 
					            raise Exception(u"failed to upload {} for activity")
 | 
				
			||||||
 | 
					        activity_id = j["detailedImportResult"]["successes"][0]["internalId"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # add optional fields
 | 
				
			||||||
 | 
					        fields = ( ('name',name,("display","value")),
 | 
				
			||||||
 | 
					                   ('description',description,("display","value")),
 | 
				
			||||||
 | 
					                   ('type',activity_type,("activityType","key")),
 | 
				
			||||||
 | 
					                   ('privacy','private' if private else None,("definition","key")) )
 | 
				
			||||||
 | 
					        for endpoint, value, path in fields:
 | 
				
			||||||
 | 
					            if value is not None:
 | 
				
			||||||
 | 
					                response = self.session.post("https://connect.garmin.com/proxy/activity-service-1.2/json/{}/{}".format(endpoint, activity_id),
 | 
				
			||||||
 | 
					                                             data={'value':value})
 | 
				
			||||||
 | 
					                if response.status_code != 200:
 | 
				
			||||||
 | 
					                    raise Exception(u"failed to set {} for activity {}: {}\n{}".format(
 | 
				
			||||||
 | 
					                        endpoint, activity_id, response.status_code, response.text))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                j = response.json()
 | 
				
			||||||
 | 
					                p0, p1 = path
 | 
				
			||||||
 | 
					                if p0 not in j or j[p0][p1] != value:
 | 
				
			||||||
 | 
					                    raise Exception(u"failed to set {} for activity {}\n".format(endpoint, activity_id))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return activity_id
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										66
									
								
								upload_activity.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								upload_activity.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					#! /usr/bin/env python
 | 
				
			||||||
 | 
					"""A program that uploads an activity file to a Garmin
 | 
				
			||||||
 | 
					Connect account.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import argparse
 | 
				
			||||||
 | 
					import getpass
 | 
				
			||||||
 | 
					from garminexport.garminclient import GarminClient
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import traceback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logging.basicConfig(
 | 
				
			||||||
 | 
					    level=logging.INFO, format="%(asctime)-15s [%(levelname)s] %(message)s")
 | 
				
			||||||
 | 
					log = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG_LEVELS = {
 | 
				
			||||||
 | 
					    "DEBUG": logging.DEBUG,
 | 
				
			||||||
 | 
					    "INFO": logging.INFO,
 | 
				
			||||||
 | 
					    "WARNING": logging.WARNING,
 | 
				
			||||||
 | 
					    "ERROR": logging.ERROR
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					"""Command-line (string-based) log-level mapping to logging module levels."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser = argparse.ArgumentParser(
 | 
				
			||||||
 | 
					        description=("Uploads an activity file to a Garmin Connect account."))
 | 
				
			||||||
 | 
					    # positional args
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        "username", metavar="<username>", type=str, help="Account user name.")
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        "activity", metavar="<file>", type=argparse.FileType("rb"),
 | 
				
			||||||
 | 
					        help="Activity file (.gpx, .tcx, or .fit).")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # optional args
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        "--password", type=str, help="Account password.")
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        '-N', '--name', help="Activity name on Garmin Connect.")
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        '-D', '--description', help="Activity description on Garmin Connect.")
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        '-P', '--private', action='store_true', help="Make activity private on Garmin Connect.")
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        "--log-level", metavar="LEVEL", type=str,
 | 
				
			||||||
 | 
					        help=("Desired log output level (DEBUG, INFO, WARNING, ERROR). "
 | 
				
			||||||
 | 
					              "Default: INFO."), default="INFO")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    args = parser.parse_args()
 | 
				
			||||||
 | 
					    if not args.log_level in LOG_LEVELS:
 | 
				
			||||||
 | 
					        raise ValueError("Illegal log-level argument: {}".format(
 | 
				
			||||||
 | 
					            args.log_level))
 | 
				
			||||||
 | 
					    logging.root.setLevel(LOG_LEVELS[args.log_level])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        if not args.password:
 | 
				
			||||||
 | 
					            args.password = getpass.getpass("Enter password: ")
 | 
				
			||||||
 | 
					        with GarminClient(args.username, args.password) as client:
 | 
				
			||||||
 | 
					            log.info("uploading activity file {} ...".format(args.activity.name))
 | 
				
			||||||
 | 
					            id = client.upload_activity(args.activity, name=args.name, description=args.description, private=args.private)
 | 
				
			||||||
 | 
					            log.info("upload successful: https://connect.garmin.com/activity/{}".format(id))
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        exc_type, exc_value, exc_traceback = sys.exc_info()
 | 
				
			||||||
 | 
					        log.error(u"failed with exception: %s", e)
 | 
				
			||||||
 | 
					        raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user