コード例 #1
0
ファイル: gpx.py プロジェクト: soalhn/tapiriik
    def test_constant_representation(self):
        ''' ensures that gpx import/export is symetric '''

        svcA, other = TestTools.create_mock_services()
        svcA.SupportsHR = svcA.SupportsCadence = svcA.SupportsTemp = True
        svcA.SupportsPower = svcA.SupportsCalories = False
        act = TestTools.create_random_activity(svcA, tz=True)

        mid = GPXIO.Dump(act)

        act2 = GPXIO.Parse(bytes(mid,"UTF-8"))
        act2.TZ = act.TZ  # we need to fake this since local TZ isn't defined in GPX files, and TZ discovery will flail with random activities
        act2.AdjustTZ()
        act.Stats.Distance = act2.Stats.Distance = None  # same here

        self.assertActivitiesEqual(act2, act)
コード例 #2
0
    def _getActivity(self, serviceRecord, dbcl, path):
        activityData = None

        try:
            f, metadata = dbcl.get_file_and_metadata(path)
        except rest.ErrorResponse as e:
            self._raiseDbException(e)

        if not activityData:
            activityData = f.read()

        try:
            if path.lower().endswith(".tcx"):
                act = TCXIO.Parse(activityData)
            else:
                act = GPXIO.Parse(activityData)
        except ValueError as e:
            raise APIExcludeActivity("Invalid GPX/TCX " + str(e),
                                     activityId=path,
                                     userException=UserException(
                                         UserExceptionType.Corrupt))
        except lxml.etree.XMLSyntaxError as e:
            raise APIExcludeActivity("LXML parse error " + str(e),
                                     activityId=path,
                                     userException=UserException(
                                         UserExceptionType.Corrupt))
        return act, metadata["rev"]
コード例 #3
0
ファイル: beginnertriathlete.py プロジェクト: rayhe/tapiriik
    def DownloadActivity(self, serviceRecord, activity):
        deviceUploadFile = activity.ServiceData.get("DeviceUploadFile")

        # No additional data about this event is available.
        if not deviceUploadFile:
            return activity

        logger.info("Downloading device file %s" % deviceUploadFile)
        session = self._prepare_request(self._getUserToken(serviceRecord))
        res = session.get(deviceUploadFile)

        if res.status_code == 200:
            try:
                contentType = self._mimeTypeMappings[res.headers["content-type"]]
                if not contentType:
                    remoteUrl = urlparse(deviceUploadFile).path
                    extension = os.path.splitext(remoteUrl)[1]
                    contentType = self._fileExtensionMappings[extension]

                if contentType:
                    if contentType == _DeviceFileTypes.FIT:
                        # Oh no! Not supported! So close ....
                        # FITIO.Parse(res.content, activity)
                        return activity
                    if contentType == _DeviceFileTypes.TCX:
                        TCXIO.Parse(res.content, activity)
                    if contentType == _DeviceFileTypes.GPX:
                        GPXIO.Parse(res.content, activity)
            except ValueError as e:
                raise APIExcludeActivity("Parse error " + deviceUploadFile + " " + str(e),
                                         user_exception=UserException(UserExceptionType.Corrupt),
                                         permanent=True)

        return activity
コード例 #4
0
    def DownloadActivity(self, serviceRecord, activity):
        #http://connect.garmin.com/proxy/activity-service-1.1/tcx/activity/#####?full=true
        activityID = activity.ServiceData["ActivityID"]
        cookies = self._get_cookies(record=serviceRecord)
        self._rate_limit()
        res = requests.get(
            "http://connect.garmin.com/proxy/activity-service-1.1/tcx/activity/"
            + str(activityID) + "?full=true",
            cookies=cookies)
        try:
            TCXIO.Parse(res.content, activity)
        except ValueError as e:
            raise APIExcludeActivity("TCX parse error " + str(e),
                                     userException=UserException(
                                         UserExceptionType.Corrupt))

        if activity.ServiceData["RecalcHR"]:
            logger.debug("Recalculating HR")
            avgHR, maxHR = ActivityStatisticCalculator.CalculateAverageMaxHR(
                activity)
            activity.Stats.HR.coalesceWith(
                ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute,
                                  max=maxHR,
                                  avg=avgHR))

        if len(activity.Laps) == 1:
            activity.Laps[0].Stats.update(
                activity.Stats
            )  # I trust Garmin Connect's stats more than whatever shows up in the TCX
            activity.Stats = activity.Laps[
                0].Stats  # They must be identical to pass the verification

        if activity.Stats.Temperature.Min is not None or activity.Stats.Temperature.Max is not None or activity.Stats.Temperature.Average is not None:
            logger.debug("Retrieving additional temperature data")
            # TCX doesn't have temperature, for whatever reason...
            self._rate_limit()
            res = requests.get(
                "http://connect.garmin.com/proxy/activity-service-1.1/gpx/activity/"
                + str(activityID) + "?full=true",
                cookies=cookies)
            try:
                temp_act = GPXIO.Parse(res.content,
                                       suppress_validity_errors=True)
            except ValueError as e:
                pass
            else:
                logger.debug("Merging additional temperature data")
                full_waypoints = activity.GetFlatWaypoints()
                temp_waypoints = temp_act.GetFlatWaypoints()

                merge_idx = 0

                for x in range(len(temp_waypoints)):
                    while full_waypoints[merge_idx].Timestamp < temp_waypoints[
                            x].Timestamp and merge_idx < len(
                                full_waypoints) - 1:
                        merge_idx += 1
                    full_waypoints[merge_idx].Temp = temp_waypoints[x].Temp

        return activity
コード例 #5
0
    def UploadActivity(self, serviceRecord, activity):
        format = serviceRecord.GetConfiguration()["Format"]
        if format == "tcx":
            if "tcx" in activity.PrerenderedFormats:
                logger.debug("Using prerendered TCX")
                data = activity.PrerenderedFormats["tcx"]
            else:
                data = TCXIO.Dump(activity)
        else:
            if "gpx" in activity.PrerenderedFormats:
                logger.debug("Using prerendered GPX")
                data = activity.PrerenderedFormats["gpx"]
            else:
                data = GPXIO.Dump(activity)

        dbcl = self._getClient(serviceRecord)
        fname = self._format_file_name(serviceRecord.GetConfiguration()["Filename"], activity)[:250] + "." + format # DB has a max path component length of 255 chars, and we have to save for the file ext (4) and the leading slash (1)

        if not serviceRecord.Authorization["Full"]:
            fpath = "/" + fname
        else:
            fpath = serviceRecord.Config["SyncRoot"] + "/" + fname

        try:
            metadata = dbcl.files_upload(data.encode("UTF-8"), fpath, mode=dropbox.files.WriteMode.overwrite)
        except dropbox.exceptions.DropboxException as e:
            self._raiseDbException(e)
        # Fake this in so we don't immediately redownload the activity next time 'round
        cache = cachedb.dropbox_cache.find_one({"ExternalID": serviceRecord.ExternalID})
        cache["Activities"][self._hash_path("/" + fname)] = {"Rev": metadata.rev, "UID": activity.UID, "StartTime": activity.StartTime.strftime("%H:%M:%S %d %m %Y %z"), "EndTime": activity.EndTime.strftime("%H:%M:%S %d %m %Y %z")}
        cachedb.dropbox_cache.update({"ExternalID": serviceRecord.ExternalID}, cache)  # not upsert, hope the record exists at this time...
        return fpath
コード例 #6
0
    def _getActivity(self, serviceRecord, dbcl, path, base_activity=None):
        try:
            metadata, file = dbcl.files_download(path)
        except dropbox.exceptions.DropboxException as e:
            self._raiseDbException(e)

        try:
            if path.lower().endswith(".tcx"):
                act = TCXIO.Parse(file.content, base_activity)
            else:
                act = GPXIO.Parse(file.content, base_activity)
        except ValueError as e:
            raise APIExcludeActivity("Invalid GPX/TCX " + str(e), activity_id=path, user_exception=UserException(UserExceptionType.Corrupt))
        except lxml.etree.XMLSyntaxError as e:
            raise APIExcludeActivity("LXML parse error " + str(e), activity_id=path, user_exception=UserException(UserExceptionType.Corrupt))
        return act, metadata.rev
コード例 #7
0
    def UploadActivity(self, serviceRecord, activity):
        """
        POST a Multipart-Encoded File
        
        URL: https://app.velohero.com/upload/file
        Parameters:
        user = username
        pass = password
        view = json
        file = multipart-encodes file (fit, tcx, pwx, gpx, srm, hrm...)
        
        Maximum file size per file is 16 MB.
        """
        
        has_location = has_distance = has_speed = False

        for lap in activity.Laps:
            for wp in lap.Waypoints:
                if wp.Location and wp.Location.Latitude and wp.Location.Longitude:
                    has_location = True
                if wp.Distance:
                    has_distance = True
                if wp.Speed:
                    has_speed = True

        if has_location and has_distance and has_speed:
            format = "fit"
            data = FITIO.Dump(activity)
        elif has_location and has_distance:
            format = "tcx"
            data = TCXIO.Dump(activity)
        elif has_location:
            format = "gpx"
            data = GPXIO.Dump(activity)
        else:
            format = "fit"
            data = FITIO.Dump(activity)

        # Upload
        files = {"file": ("tap-sync-" + str(os.getpid()) + "-" + activity.UID + "." + format, data)}
        params = self._add_auth_params({"view":"json"}, record=serviceRecord)
        res = requests.post(self._urlRoot + "/upload/file",
                            headers=self._obligatory_headers,
                            files=files,
                            params=params)

        if res.status_code != 200:
            if res.status_code == 403:
                raise APIException("Invalid login", block=True, user_exception=UserException(UserExceptionType.Authorization, intervention_required=True))
            raise APIException("Unable to upload activity")

        res.raise_for_status()
        try:
            res = res.json()
        except ValueError:
            raise APIException("Could not decode activity list")
        
        if "error" in res:
            raise APIException(res["error"])

        # Set date, start time, comment and sport
        if "id" in res:
            workoutId = res["id"]
            params = self._add_auth_params({ 
                "workout_date" : activity.StartTime.strftime("%Y-%m-%d"), 
                "workout_start_time" : activity.StartTime.strftime("%H:%M:%S"),
                "workout_comment" : activity.Notes, 
                "sport_id" : self._activityMappings[activity.Type],
                "workout_hide": "yes" if activity.Private else "no"
            }, record=serviceRecord)
            res = requests.get(self._urlRoot + "/workouts/change/{}".format(workoutId),
                               headers=self._obligatory_headers,
                               params=params)
            if res.status_code != 200:
                if res.status_code == 403:
                    raise APIException("No authorization to change activity with workout ID: {}".format(workoutId), block=True, user_exception=UserException(UserExceptionType.Authorization, intervention_required=True))
                raise APIException("Unable to change activity with workout ID: {}".format(workoutId))

            return workoutId