Example #1
0
    def UploadActivity(self, serviceRecord, activity):
        #/proxy/upload-service-1.1/json/upload/.fit
        fit_file = FITIO.Dump(activity)
        files = {"data": ("tap-sync-" + str(os.getpid()) + "-" + activity.UID + ".fit", fit_file)}

        res = self._request_with_reauth(
            lambda session: session.post("https://connect.garmin.com/modern/proxy/upload-service/upload/.fit",
                                         files=files,
                                         headers={"nk": "NT"}),
            serviceRecord)
        try:
            res = res.json()["detailedImportResult"]
        except ValueError:
            raise APIException("Bad response during GC upload: %s %s" % (res.status_code, res.text))

        if len(res["successes"]) == 0:
            if len(res["failures"]) and len(res["failures"][0]["messages"]):
                if res["failures"][0]["messages"][0]["content"] == "Duplicate activity":
                    logger.debug("Duplicate")
                    return # ...cool?
                if res["failures"][0]["messages"][0]["content"] == "The user is from EU location, but upload consent is not yet granted or revoked":
                    raise APIException("EU user with no upload consent", block=True, user_exception=UserException(UserExceptionType.GCUploadConsent, intervention_required=True))
            raise APIException("Unable to upload activity %s" % res)
        if len(res["successes"]) > 1:
            raise APIException("Uploaded succeeded, resulting in too many activities")
        actid = res["successes"][0]["internalId"]

        name = activity.Name # Capture in logs
        notes = activity.Notes

        # Update activity metadata not included in the FIT file.
        metadata_object = {}
        if activity.Name and activity.Name.strip():
            metadata_object["activityName"] = activity.Name
        if activity.Notes and activity.Notes.strip():
            metadata_object["description"] = activity.Notes
        if activity.Type not in [ActivityType.Running, ActivityType.Cycling, ActivityType.Other]:
            # Set the legit activity type - whatever it is, it's not supported by the FIT schema
            acttype = [k for k, v in self._reverseActivityMappings.items() if v == activity.Type]
            if len(acttype) == 0:
                raise APIWarning("GarminConnect does not support activity type " + activity.Type)
            else:
                acttype = acttype[0]
            metadata_object["activityTypeDTO"] = {"typeKey": acttype}
        if activity.Private:
            metadata_object["accessControlRuleDTO"] = {"typeKey": "private"}

        if metadata_object:
            metadata_object["activityId"] = actid
            encoding_headers = {"Content-Type": "application/json; charset=UTF-8"} # GC really, really needs this part, otherwise it throws obscure errors like "Invalid signature for signature method HMAC-SHA1"
            res = self._request_with_reauth(lambda session: session.put("https://connect.garmin.com/proxy/activity-service/activity/" + str(actid), data=json.dumps(metadata_object), headers=encoding_headers), serviceRecord)
            if res.status_code != 204:
                raise APIWarning("Unable to set activity metadata - %d %s" % (res.status_code, res.text))

        return actid
Example #2
0
    def UploadActivity(self, serviceRecord, activity):
        #/proxy/upload-service-1.1/json/upload/.tcx
        activity.EnsureTZ()
        tcx_file = TCXIO.Dump(activity)
        files = {"data": ("tap-sync-" + str(os.getpid()) + "-" + activity.UID + ".tcx", tcx_file)}
        cookies = self._get_cookies(record=serviceRecord)
        res = requests.post("http://connect.garmin.com/proxy/upload-service-1.1/json/upload/.tcx", files=files, cookies=cookies)
        res = res.json()["detailedImportResult"]

        if len(res["successes"]) != 1:
            raise APIException("Unable to upload activity")
        actid = res["successes"][0]["internalId"]

        if activity.Type not in [ActivityType.Running, ActivityType.Cycling, ActivityType.Other]:
            # Set the legit activity type - whatever it is, it's not supported by the TCX schema
            acttype = [k for k, v in self._reverseActivityMappings.items() if v == activity.Type]
            if len(acttype) == 0:
                raise APIWarning("GarminConnect does not support activity type " + activity.Type)
            else:
                acttype = acttype[0]
            res = requests.post("http://connect.garmin.com/proxy/activity-service-1.2/json/type/" + str(actid), data={"value": acttype}, cookies=cookies)
            res = res.json()
            if "activityType" not in res or res["activityType"]["key"] != acttype:
                raise APIWarning("Unable to set activity type")
Example #3
0
    def UploadActivity(self, serviceRecord, activity):
        #/proxy/upload-service-1.1/json/upload/.fit
        fit_file = FITIO.Dump(activity)
        files = {
            "data":
            ("tap-sync-" + str(os.getpid()) + "-" + activity.UID + ".fit",
             fit_file)
        }
        session = self._get_session(record=serviceRecord)
        self._rate_limit()
        res = session.post(
            "http://connect.garmin.com/proxy/upload-service-1.1/json/upload/.fit",
            files=files)
        res = res.json()["detailedImportResult"]

        if len(res["successes"]) == 0:
            raise APIException("Unable to upload activity %s" % res)
        if len(res["successes"]) > 1:
            raise APIException(
                "Uploaded succeeded, resulting in too many activities")
        actid = res["successes"][0]["internalId"]

        name = activity.Name  # Capture in logs
        notes = activity.Notes
        encoding_headers = {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
        }  # GC really, really needs this part, otherwise it throws obscure errors like "Invalid signature for signature method HMAC-SHA1"
        warnings = []
        try:
            if activity.Name and activity.Name.strip():
                self._rate_limit()
                res = session.post(
                    "http://connect.garmin.com/proxy/activity-service-1.2/json/name/"
                    + str(actid),
                    data=urlencode({
                        "value": activity.Name
                    }).encode("UTF-8"),
                    headers=encoding_headers)
                try:
                    res = res.json()
                except:
                    raise APIWarning("Activity name request failed - %s" %
                                     res.text)
                if "display" not in res or res["display"][
                        "value"] != activity.Name:
                    raise APIWarning("Unable to set activity name")
        except APIWarning as e:
            warnings.append(e)

        try:
            if activity.Notes and activity.Notes.strip():
                self._rate_limit()
                res = session.post(
                    "http://connect.garmin.com/proxy/activity-service-1.2/json/description/"
                    + str(actid),
                    data=urlencode({
                        "value": activity.Notes
                    }).encode("UTF-8"),
                    headers=encoding_headers)
                try:
                    res = res.json()
                except:
                    raise APIWarning("Activity notes request failed - %s" %
                                     res.text)
                if "display" not in res or res["display"][
                        "value"] != activity.Notes:
                    raise APIWarning("Unable to set activity notes")
        except APIWarning as e:
            warnings.append(e)

        try:
            if activity.Type not in [
                    ActivityType.Running, ActivityType.Cycling,
                    ActivityType.Other
            ]:
                # Set the legit activity type - whatever it is, it's not supported by the TCX schema
                acttype = [
                    k for k, v in self._reverseActivityMappings.items()
                    if v == activity.Type
                ]
                if len(acttype) == 0:
                    raise APIWarning(
                        "GarminConnect does not support activity type " +
                        activity.Type)
                else:
                    acttype = acttype[0]
                self._rate_limit()
                res = session.post(
                    "http://connect.garmin.com/proxy/activity-service-1.2/json/type/"
                    + str(actid),
                    data={"value": acttype})
                res = res.json()
                if "activityType" not in res or res["activityType"][
                        "key"] != acttype:
                    raise APIWarning("Unable to set activity type")
        except APIWarning as e:
            warnings.append(e)

        try:
            if activity.Private:
                self._rate_limit()
                res = session.post(
                    "http://connect.garmin.com/proxy/activity-service-1.2/json/privacy/"
                    + str(actid),
                    data={"value": "private"})
                res = res.json()
                if "definition" not in res or res["definition"][
                        "key"] != "private":
                    raise APIWarning("Unable to set activity privacy")
        except APIWarning as e:
            warnings.append(e)

        if len(warnings):
            raise APIWarning(str(warnings))  # Meh
        return actid