コード例 #1
0
    def DownloadActivity(self, svcRecord, activity):

        userID = svcRecord.ExternalID
        activity_id = activity.ServiceData["ActivityID"]

        logging.info("\t\t FITBIT LOADING  : " + str(activity_id))
        activity_tcx_uri = 'https://api.fitbit.com/1/user/' + userID + '/activities/' + str(activity_id) + '.tcx'
        resp = self._requestWithAuth(lambda session: session.get(
            activity_tcx_uri,
            headers={
                'Authorization': 'Bearer ' + svcRecord.Authorization.get('AccessToken')
            }), svcRecord)
        # check if request has error
        if resp.status_code != 204 and resp.status_code != 200:
            raise APIException("Unable to find Fitbit TCX activity")

        # Prepare tcxio params
        ns = copy.deepcopy(TCXIO.Namespaces)
        ns["tcx"] = ns[None]
        del ns[None]

        # Read tcx to know if this is a stationary activity or not
        try:
            root = etree.XML(resp.text.encode('utf-8'))
        except:
            root = etree.fromstring(resp.text.encode('utf-8'))

        xacts = root.find("tcx:Activities", namespaces=ns)
        if xacts is None:
            raise ValueError("No activities element in TCX")

        xact = xacts.find("tcx:Activity", namespaces=ns)
        if xact is None:
            raise ValueError("No activity element in TCX")

        # Define activity type from tcx
        if not activity.Type or activity.Type == ActivityType.Other:
            if xact.attrib["Sport"] == "Biking":
                activity.Type = ActivityType.Cycling
            elif xact.attrib["Sport"] == "Running":
                activity.Type = ActivityType.Running

        # Find all lap in tcx
        xlaps = xact.findall("tcx:Lap", namespaces=ns)
        if len(xlaps) > 0:
            activity = TCXIO.Parse(resp.text.encode('utf-8'), activity)
        else:
            # Define lap for activity
            lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime)
            activity.Laps = [lap]
            lap.Waypoints = []
            activity.GPS = False
            activity.Stationary = len(lap.Waypoints) == 0

        return activity
コード例 #2
0
ファイル: strava.py プロジェクト: canssens/exercisync
    def DownloadActivity(self, svcRecord, activity):
        if activity.ServiceData["Manual"]:  # I should really add a param to DownloadActivity for this value as opposed to constantly doing this
            # We've got as much information as we're going to get - we need to copy it into a Lap though.
            activity.Laps = [Lap(startTime=activity.StartTime, endTime=activity.EndTime, stats=activity.Stats)]
            return activity
        activityID = activity.ServiceData["ActivityID"]

        streamdata = requests.get("https://www.strava.com/api/v3/activities/" + str(activityID) + "/streams/time,altitude,heartrate,cadence,watts,temp,moving,latlng,distance,velocity_smooth", headers=self._apiHeaders(svcRecord))
        if streamdata.status_code == 401:
            raise APIException("No authorization to download activity", block=True, user_exception=UserException(UserExceptionType.Authorization, intervention_required=True))

        try:
            streamdata = streamdata.json()
        except:
            raise APIException("Stream data returned is not JSON")

        if "message" in streamdata and streamdata["message"] == "Record Not Found":
            raise APIException("Could not find activity")

        ridedata = {}
        for stream in streamdata:
            ridedata[stream["type"]] = stream["data"]

        if "error" in ridedata:
            raise APIException("Strava error " + ridedata["error"])

        activity.Laps = []

        res = requests.get("https://www.strava.com/api/v3/activities/{}".format(activityID), headers=self._apiHeaders(svcRecord))
        activityDetails = res.json()

        activity.Notes = activityDetails["description"]

        lapsdata = activityDetails["laps"]

        for lapdata in lapsdata:

            lapWaypoints, lapStats = self._process_lap_waypoints(activity, ridedata, lapdata)

            lapStart = pytz.utc.localize(datetime.strptime(lapdata["start_date"], "%Y-%m-%dT%H:%M:%SZ"))
            lapEnd = lapStart + timedelta(0, lapdata["elapsed_time"])
            lap = Lap(startTime=lapStart, endTime=lapEnd)
            lap.Waypoints = lapWaypoints
            
            # In single-lap case lap stats needs to match global stats
            lap.Stats = activity.Stats if len(lapsdata) == 1 else lapStats

            activity.Laps.append(lap)

        return activity
コード例 #3
0
ファイル: smashrun.py プロジェクト: naveed-ahmad/tapiriik
    def DownloadActivity(self, serviceRecord, activity):
        act = self._getActivity(serviceRecord, activity)
        recordingKeys = act.get('recordingKeys')
        if act['source'] == 'manual' or not recordingKeys:
            # it's a manually entered run, can't get much info
            activity.Stationary = True
            activity.Laps = [Lap(startTime=activity.StartTime, endTime=activity.EndTime, stats=activity.Stats)]
            return activity

        # FIXME: technically it could still be stationary if there are no long/lat values...
        activity.Stationary = False

        if not act['laps']:
            # no laps, just make one big lap
            activity.Laps = [Lap(startTime=activity.StartTime, endTime=activity.EndTime, stats=activity.Stats)]

        startTime = activity.StartTime
        for lapRecord in act['laps']:
            endTime = activity.StartTime + timedelta(seconds=lapRecord['endDuration'])
            lap = Lap(startTime=startTime, endTime=endTime)
            activity.Laps.append(lap)
            startTime = endTime + timedelta(seconds=1)

        for value in zip(*act['recordingValues']):
            record = dict(zip(recordingKeys, value))
            ts = activity.StartTime + timedelta(seconds=record['clock'])
            if 'latitude' in record:
                alt = record.get('elevation')
                lat = record['latitude']
                lon = record['longitude']
                # Smashrun seems to replace missing measurements with -1
                if lat == -1:
                    lat = None
                if lon == -1:
                    lon = None
                location = Location(lat=lat, lon=lon, alt=alt)
            hr = record.get('heartRate')
            runCadence = record.get('cadence')
            temp = record.get('temperature')
            distance = record.get('distance') * 1000
            wp = Waypoint(timestamp=ts, location=location, hr=hr,
                          runCadence=runCadence, temp=temp,
                          distance=distance)
            # put the waypoint inside the lap it corresponds to
            for lap in activity.Laps:
                if lap.StartTime <= wp.Timestamp <= lap.EndTime:
                    lap.Waypoints.append(wp)
                    break

        return activity
コード例 #4
0
    def DownloadActivity(self, serviceRecord, activity):
        resp = self._oauthSession(serviceRecord).get("https://api.endomondo.com/api/1/workouts/%d" % activity.ServiceData["WorkoutID"], params={"fields": "points"})
        try:
            resp = resp.json()
        except ValueError:
            self._rateLimitBailout(resp)
            res_txt = resp.text
            raise APIException("Parse failure in Endomondo activity download: %s" % resp.status_code)
        lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime)
        activity.Laps = [lap]

        activity.GPS = False

        old_location = None
        in_pause = False
        for pt in resp["points"]:
            wp = Waypoint()
            if "time" not in pt:
                # Manually-entered activities with a course attached to them have date-less waypoints
                # It'd be nice to transfer those courses, but it's a concept few other sites support AFAIK
                # So, ignore the points entirely
                continue
            wp.Timestamp = self._parseDate(pt["time"])

            if ("lat" in pt and "lng" in pt) or "alt" in pt:
                wp.Location = Location()
                if "lat" in pt and "lng" in pt:
                    wp.Location.Latitude = pt["lat"]
                    wp.Location.Longitude = pt["lng"]
                    activity.GPS = True
                if "alt" in pt:
                    wp.Location.Altitude = pt["alt"]

                if wp.Location == old_location:
                    # We have seen the point with the same coordinates
                    # before. This causes other services (e.g Strava) to
                    # interpret this as if we were standing for a while,
                    # which causes us having wrong activity time when
                    # importing. We mark the point as paused in hopes this
                    # fixes the issue.
                    in_pause = True
                    wp.Type = WaypointType.Pause
                elif in_pause:
                    in_pause = False
                    wp.Type = WaypointType.Resume

                old_location = wp.Location

            if "hr" in pt:
                wp.HR = pt["hr"]

            if "cad" in pt:
                wp.Cadence = pt["cad"]

            if "pow" in pt:
                wp.Power = pt["pow"]

            lap.Waypoints.append(wp)
        activity.Stationary = len(lap.Waypoints) == 0
        return activity
コード例 #5
0
ファイル: pulsstory.py プロジェクト: naveed-ahmad/tapiriik
    def _populateActivityWaypoints(self, rawData, activity):
        ''' populate the Waypoints collection from pulsstory API data '''
        lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime)
        activity.Laps = [lap]

        streamData = {}

        self._convertList(streamData, "heart_rate", rawData, "HeartRate")
        self._convertList(streamData, "distance", rawData, "Distance")
        self._convertList(streamData, "speed", rawData, "Speed")
        self._convertList(streamData, "power", rawData, "Power")
        self._convertList(streamData, "cadence", rawData, "Cadence")
        self._convertPathList(streamData, "path", rawData)

        def _addWaypoint(timestamp, path=None, heart_rate=None, power=None, distance=None, speed=None, cadence=None):
            waypoint = Waypoint(activity.StartTime + timedelta(seconds=timestamp))
            if path:
                if path["latitude"] != 0 and path["longitude"] != 0:
                    waypoint.Location = Location(path["latitude"], path["longitude"], path["altitude"] if "altitude" in path and float(path["altitude"]) != 0 else None)  # if you're running near sea level, well...
                waypoint.Type = WaypointType.Regular
            waypoint.HR = heart_rate
            waypoint.Distance = distance
            waypoint.Speed = speed
            waypoint.Cadence = cadence
            waypoint.Power = power
            lap.Waypoints.append(waypoint)

        StreamSampler.SampleWithCallback(_addWaypoint, streamData)

        activity.Stationary = len(lap.Waypoints) == 0
        activity.GPS = any(wp.Location and wp.Location.Longitude is not None and wp.Location.Latitude is not None for wp in lap.Waypoints)
        if not activity.Stationary:
            lap.Waypoints[0].Type = WaypointType.Start
            lap.Waypoints[-1].Type = WaypointType.End
コード例 #6
0
    def _populateActivityWaypoints(self, rawData, activity):
        ''' populate the Waypoints collection from RK API data '''
        lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime)
        activity.Laps = [lap]

        streamData = {}
        for stream in ["path", "heart_rate", "calories", "distance"]:
            if stream in rawData and len(rawData[stream]):
                if stream == "path":
                    # The path stream doesn't follow the same naming convention, so we cheat and put everything in.
                    streamData[stream] = [(x["timestamp"], x) for x in rawData[stream]]
                else:
                    streamData[stream] = [(x["timestamp"], x[stream]) for x in rawData[stream]] # Change up format for StreamSampler

        def _addWaypoint(timestamp, path=None, heart_rate=None, calories=None, distance=None):
            waypoint = Waypoint(activity.StartTime + timedelta(seconds=timestamp))
            if path:
                waypoint.Location = Location(path["latitude"], path["longitude"], path["altitude"] if "altitude" in path and float(path["altitude"]) != 0 else None)  # if you're running near sea level, well...
                waypoint.Type = self._wayptTypeMappings[path["type"]] if path["type"] in self._wayptTypeMappings else WaypointType.Regular
            waypoint.HR = heart_rate
            waypoint.Calories = calories
            waypoint.Distance = distance

            lap.Waypoints.append(waypoint)
        activity.Stationary = len(lap.Waypoints) == 0
        if not activity.Stationary:
            lap.Waypoints[0].Type = WaypointType.Start
            lap.Waypoints[-1].Type = WaypointType.End

        StreamSampler.SampleWithCallback(_addWaypoint, streamData)
コード例 #7
0
ファイル: nikeplus.py プロジェクト: andylolz/tapiriik
    def DownloadActivity(self, serviceRecord, activity):
        session = self._get_session(serviceRecord)
        act_id = activity.ServiceData["ID"]
        activityDetails = session.get("https://api.nike.com/me/sport/activities/%s" % act_id, params=self._with_auth(session))
        activityDetails = activityDetails.json()

        streams = {metric["metricType"].lower(): self._nikeStream(metric) for metric in activityDetails["metrics"]}

        activity.GPS = activityDetails["isGpsActivity"]

        if activity.GPS:
            activityGps = session.get("https://api.nike.com/me/sport/activities/%s/gps" % act_id, params=self._with_auth(session))
            activityGps = activityGps.json()
            streams["gps"] = self._nikeStream(activityGps, "waypoints")
            activity.Stats.Elevation.update(ActivityStatistic(ActivityStatisticUnit.Meters,
                                                              gain=float(activityGps["elevationGain"]),
                                                              loss=float(activityGps["elevationLoss"]),
                                                              max=float(activityGps["elevationMax"]),
                                                              min=float(activityGps["elevationMin"])))

        lap = Lap(startTime=activity.StartTime, endTime=activity.EndTime)
        lap.Stats = activity.Stats
        activity.Laps = [lap]
        # I thought I wrote StreamSampler to be generator-friendly - nope.
        streams = {k: list(v) for k,v in streams.items()}

        # The docs are unclear on which of these are actually stream metrics, oh well
        def stream_waypoint(offset, speed=None, distance=None, heartrate=None, calories=None, steps=None, watts=None, gps=None, **kwargs):
            wp = Waypoint()
            wp.Timestamp = activity.StartTime + timedelta(seconds=offset)
            wp.Speed = float(speed) if speed else None
            wp.Distance = float(distance) / 1000 if distance else None
            wp.HR = float(heartrate) if heartrate else None
            wp.Calories = float(calories) if calories else None
            wp.Power = float(watts) if watts else None

            if gps:
                wp.Location = Location(lat=float(gps["latitude"]), lon=float(gps["longitude"]), alt=float(gps["elevation"]))
            lap.Waypoints.append(wp)

        StreamSampler.SampleWithCallback(stream_waypoint, streams)

        activity.Stationary = len(lap.Waypoints) == 0

        return activity
コード例 #8
0
    def DownloadActivity(self, serviceRecord, activity):
        activityID = activity.ServiceData["ActivityID"]
        logger.debug("DownloadActivity %s" % activityID)

        lap = Lap(stats=activity.Stats,
                  startTime=activity.StartTime,
                  endTime=activity.EndTime)
        activity.Laps = [lap]
        lap.Waypoints = []

        response = requests.get("https://api.mapmyfitness.com/v7.1/workout/" +
                                activityID + "/?field_set=time_series",
                                headers=self._apiHeaders(serviceRecord))
        data = response.json()

        activity.GPS = False
        activity.Stationary = True

        # add waypoints to laps
        if "time_series" in data and "position" in data["time_series"]:
            activity.Stationary = False
            for pt in data["time_series"]["position"]:
                timestamp = pt[0]
                wp = Waypoint(activity.StartTime +
                              timedelta(seconds=round(timestamp)))

                pos = pt[1]
                if ("lat" in pos and "lng" in pos) or "elevation" in pos:
                    wp.Location = Location()
                    if "lat" in pos and "lng" in pos:
                        wp.Location.Latitude = pos["lat"]
                        wp.Location.Longitude = pos["lng"]
                        activity.GPS = True
                    if "elevation" in pos:
                        wp.Location.Altitude = pos["elevation"]

                lap.Waypoints.append(wp)

        return activity
コード例 #9
0
    def DownloadActivity(self, serviceRecord, activity):
        resp = self._oauthSession(serviceRecord).get(
            "https://api.endomondo.com/api/1/workouts/%d" %
            activity.ServiceData["WorkoutID"],
            params={"fields": "points"})
        try:
            resp = resp.json()
        except ValueError:
            self._rateLimitBailout(resp)
            res_txt = resp.text
            raise APIException(
                "Parse failure in Endomondo activity download: %s" %
                resp.status_code)
        lap = Lap(stats=activity.Stats,
                  startTime=activity.StartTime,
                  endTime=activity.EndTime)
        activity.Laps = [lap]

        activity.GPS = False

        for pt in resp["points"]:
            wp = Waypoint()
            if "time" not in pt:
                # Manually-entered activities with a course attached to them have date-less waypoints
                # It'd be nice to transfer those courses, but it's a concept few other sites support AFAIK
                # So, ignore the points entirely
                continue
            wp.Timestamp = self._parseDate(pt["time"])

            if ("lat" in pt and "lng" in pt) or "alt" in pt:
                wp.Location = Location()
                if "lat" in pt and "lng" in pt:
                    wp.Location.Latitude = pt["lat"]
                    wp.Location.Longitude = pt["lng"]
                    activity.GPS = True
                if "alt" in pt:
                    wp.Location.Altitude = pt["alt"]

            if "hr" in pt:
                wp.HR = pt["hr"]

            if "cad" in pt:
                wp.Cadence = pt["cad"]

            lap.Waypoints.append(wp)
        activity.Stationary = len(lap.Waypoints) == 0
        return activity
コード例 #10
0
    def DownloadActivity(self, serviceRecord, activity):
        workoutID = activity.ServiceData["WorkoutID"]
        logger.debug("DownloadActivity for %s" % workoutID)

        session = self._get_session(record=serviceRecord)

        resp = session.get(self._urlRoot + "/api/workout/%d" % workoutID)

        try:
            res = resp.json()
        except ValueError:
            raise APIException(
                "Parse failure in Motivato activity (%d) download: %s" %
                (workoutID, res.text))

        lap = Lap(stats=activity.Stats,
                  startTime=activity.StartTime,
                  endTime=activity.EndTime)
        activity.Laps = [lap]
        activity.GPS = False
        if "track" in res and "points" in res["track"]:
            for pt in res["track"]["points"]:
                wp = Waypoint()
                if "moment" not in pt:
                    continue
                wp.Timestamp = self._parseDateTime(pt["moment"])

                if ("lat" in pt and "lon" in pt) or "ele" in pt:
                    wp.Location = Location()
                    if "lat" in pt and "lon" in pt:
                        wp.Location.Latitude = pt["lat"]
                        wp.Location.Longitude = pt["lon"]
                        activity.GPS = True
                    if "ele" in pt:
                        wp.Location.Altitude = float(pt["ele"])

                if "bpm" in pt:
                    wp.HR = pt["bpm"]

                lap.Waypoints.append(wp)

        activity.Stationary = len(lap.Waypoints) == 0

        return activity
コード例 #11
0
    def DownloadActivity(self, serviceRecord, activity):
        resp = self._oauthSession(serviceRecord).get(
            "https://api.endomondo.com/api/1/workouts/%d" %
            activity.ServiceData["WorkoutID"],
            params={"fields": "points"})
        try:
            resp = resp.json()
        except ValueError:
            res_txt = resp.text
            raise APIException(
                "Parse failure in Endomondo activity download: %s" %
                resp.status_code)
        lap = Lap(stats=activity.Stats,
                  startTime=activity.StartTime,
                  endTime=activity.EndTime)
        activity.Laps = [lap]

        activity.GPS = False

        for pt in resp["points"]:
            wp = Waypoint()
            wp.Timestamp = self._parseDate(pt["time"])

            if ("lat" in pt and "lng" in pt) or "alt" in pt:
                wp.Location = Location()
                if "lat" in pt and "lng" in pt:
                    wp.Location.Latitude = pt["lat"]
                    wp.Location.Longitude = pt["lng"]
                    activity.GPS = True
                if "alt" in pt:
                    wp.Location.Altitude = pt["alt"]

            if "hr" in pt:
                wp.HR = pt["hr"]

            if "cad" in pt:
                wp.Cadence = pt["cad"]

            lap.Waypoints.append(wp)
        activity.Stationary = len(lap.Waypoints) == 0
        return activity
コード例 #12
0
    def _downloadActivity(self,
                          serviceRecord,
                          activity,
                          returnFirstLocation=False):
        activityURI = activity.ServiceData["ActivityURI"]
        headers = self._getAuthHeaders(serviceRecord)
        activityData = requests.get(activityURI, headers=headers)
        activityData = activityData.json()

        if "clock_duration" in activityData:
            activity.EndTime = activity.StartTime + timedelta(
                seconds=float(activityData["clock_duration"]))

        activity.Private = "sharing" in activityData and activityData[
            "sharing"] != "public"

        activity.GPS = False  # Gets set back if there is GPS data

        if "notes" in activityData:
            activity.Notes = activityData["notes"]

        activity.Stats.Energy = ActivityStatistic(
            ActivityStatisticUnit.Kilojoules,
            value=float(activityData["calories"]))

        activity.Stats.Elevation = ActivityStatistic(
            ActivityStatisticUnit.Meters,
            gain=float(activityData["elevation_gain"])
            if "elevation_gain" in activityData else None,
            loss=float(activityData["elevation_loss"])
            if "elevation_loss" in activityData else None)

        activity.Stats.HR = ActivityStatistic(
            ActivityStatisticUnit.BeatsPerMinute,
            avg=activityData["avg_heartrate"]
            if "avg_heartrate" in activityData else None,
            max=activityData["max_heartrate"]
            if "max_heartrate" in activityData else None)
        activity.Stats.Cadence = ActivityStatistic(
            ActivityStatisticUnit.RevolutionsPerMinute,
            avg=activityData["avg_cadence"]
            if "avg_cadence" in activityData else None,
            max=activityData["max_cadence"]
            if "max_cadence" in activityData else None)
        activity.Stats.Power = ActivityStatistic(
            ActivityStatisticUnit.Watts,
            avg=activityData["avg_power"]
            if "avg_power" in activityData else None,
            max=activityData["max_power"]
            if "max_power" in activityData else None)

        laps_info = []
        laps_starts = []
        if "laps" in activityData:
            laps_info = activityData["laps"]
            for lap in activityData["laps"]:
                laps_starts.append(dateutil.parser.parse(lap["start_time"]))
        lap = None
        for lapinfo in laps_info:
            lap = Lap()
            activity.Laps.append(lap)
            lap.StartTime = dateutil.parser.parse(lapinfo["start_time"])
            lap.EndTime = lap.StartTime + timedelta(
                seconds=lapinfo["clock_duration"])
            if "type" in lapinfo:
                lap.Intensity = LapIntensity.Active if lapinfo[
                    "type"] == "ACTIVE" else LapIntensity.Rest
            if "distance" in lapinfo:
                lap.Stats.Distance = ActivityStatistic(
                    ActivityStatisticUnit.Meters,
                    value=float(lapinfo["distance"]))
            if "duration" in lapinfo:
                lap.Stats.TimerTime = ActivityStatistic(
                    ActivityStatisticUnit.Seconds, value=lapinfo["duration"])
            if "calories" in lapinfo:
                lap.Stats.Energy = ActivityStatistic(
                    ActivityStatisticUnit.Kilojoules,
                    value=lapinfo["calories"])
            if "elevation_gain" in lapinfo:
                lap.Stats.Elevation.update(
                    ActivityStatistic(ActivityStatisticUnit.Meters,
                                      gain=float(lapinfo["elevation_gain"])))
            if "elevation_loss" in lapinfo:
                lap.Stats.Elevation.update(
                    ActivityStatistic(ActivityStatisticUnit.Meters,
                                      loss=float(lapinfo["elevation_loss"])))
            if "max_speed" in lapinfo:
                lap.Stats.Speed.update(
                    ActivityStatistic(ActivityStatisticUnit.MetersPerSecond,
                                      max=float(lapinfo["max_speed"])))
            if "max_speed" in lapinfo:
                lap.Stats.Speed.update(
                    ActivityStatistic(ActivityStatisticUnit.MetersPerSecond,
                                      max=float(lapinfo["max_speed"])))
            if "avg_speed" in lapinfo:
                lap.Stats.Speed.update(
                    ActivityStatistic(ActivityStatisticUnit.MetersPerSecond,
                                      avg=float(lapinfo["avg_speed"])))
            if "max_heartrate" in lapinfo:
                lap.Stats.HR.update(
                    ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute,
                                      max=float(lapinfo["max_heartrate"])))
            if "avg_heartrate" in lapinfo:
                lap.Stats.HR.update(
                    ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute,
                                      avg=float(lapinfo["avg_heartrate"])))
        if lap is None:  # No explicit laps => make one that encompasses the entire activity
            lap = Lap()
            activity.Laps.append(lap)
            lap.Stats = activity.Stats
            lap.StartTime = activity.StartTime
            lap.EndTime = activity.EndTime
        elif len(activity.Laps) == 1:
            activity.Stats.update(
                activity.Laps[0].Stats
            )  # Lap stats have a bit more info generally.
            activity.Laps[0].Stats = activity.Stats

        timerStops = []
        if "timer_stops" in activityData:
            for stop in activityData["timer_stops"]:
                timerStops.append([
                    dateutil.parser.parse(stop[0]),
                    dateutil.parser.parse(stop[1])
                ])

        def isInTimerStop(timestamp):
            for stop in timerStops:
                if timestamp >= stop[0] and timestamp < stop[1]:
                    return True
                if timestamp >= stop[1]:
                    return False
            return False

        # Collate the individual streams into our waypoints.
        # Global sample rate is variable - will pick the next nearest stream datapoint.
        # Resampling happens on a lookbehind basis - new values will only appear their timestamp has been reached/passed

        wasInPause = False
        currentLapIdx = 0
        lap = activity.Laps[currentLapIdx]

        streams = []
        for stream in [
                "location", "elevation", "heartrate", "power", "cadence",
                "distance"
        ]:
            if stream in activityData:
                streams.append(stream)
        stream_indices = dict([(stream, -1) for stream in streams
                               ])  # -1 meaning the stream has yet to start
        stream_lengths = dict([(stream, len(activityData[stream]) / 2)
                               for stream in streams])
        # Data comes as "stream":[timestamp,value,timestamp,value,...]
        stream_values = {}
        for stream in streams:
            values = []
            for x in range(0, int(len(activityData[stream]) / 2)):
                values.append((activityData[stream][x * 2],
                               activityData[stream][x * 2 + 1]))
            stream_values[stream] = values

        currentOffset = 0

        def streamVal(stream):
            nonlocal stream_values, stream_indices
            return stream_values[stream][stream_indices[stream]][1]

        def hasStreamData(stream):
            nonlocal stream_indices, streams
            return stream in streams and stream_indices[stream] >= 0

        while True:
            advance_stream = None
            advance_offset = None
            for stream in streams:
                if stream_indices[stream] + 1 == stream_lengths[stream]:
                    continue  # We're at the end - can't advance
                if advance_offset is None or stream_values[stream][
                        stream_indices[stream] +
                        1][0] - currentOffset < advance_offset:
                    advance_offset = stream_values[stream][
                        stream_indices[stream] + 1][0] - currentOffset
                    advance_stream = stream
            if not advance_stream:
                break  # We've hit the end of every stream, stop
            # Advance streams sharing the current timestamp
            for stream in streams:
                if stream == advance_stream:
                    continue  # For clarity, we increment this later
                if stream_indices[stream] + 1 == stream_lengths[stream]:
                    continue  # We're at the end - can't advance
                if stream_values[stream][
                        stream_indices[stream] +
                        1][0] == stream_values[advance_stream][
                            stream_indices[advance_stream] + 1][0]:
                    stream_indices[stream] += 1
            stream_indices[
                advance_stream] += 1  # Advance the key stream for this waypoint
            currentOffset = stream_values[advance_stream][stream_indices[
                advance_stream]][0]  # Update the current time offset

            waypoint = Waypoint(activity.StartTime +
                                timedelta(seconds=currentOffset))

            if hasStreamData("location"):
                waypoint.Location = Location(
                    streamVal("location")[0],
                    streamVal("location")[1], None)
                activity.GPS = True
                if returnFirstLocation:
                    return waypoint.Location

            if hasStreamData("elevation"):
                if not waypoint.Location:
                    waypoint.Location = Location(None, None, None)
                waypoint.Location.Altitude = streamVal("elevation")

            if hasStreamData("heartrate"):
                waypoint.HR = streamVal("heartrate")

            if hasStreamData("power"):
                waypoint.Power = streamVal("power")

            if hasStreamData("cadence"):
                waypoint.Cadence = streamVal("cadence")

            if hasStreamData("distance"):
                waypoint.Distance = streamVal("distance")

            inPause = isInTimerStop(waypoint.Timestamp)
            waypoint.Type = WaypointType.Regular if not inPause else WaypointType.Pause
            if wasInPause and not inPause:
                waypoint.Type = WaypointType.Resume
            wasInPause = inPause

            # We only care if it's possible to start a new lap, i.e. there are more left
            if currentLapIdx + 1 < len(laps_starts):
                if laps_starts[currentLapIdx + 1] < waypoint.Timestamp:
                    # A new lap has started
                    currentLapIdx += 1
                    lap = activity.Laps[currentLapIdx]

            lap.Waypoints.append(waypoint)

        if returnFirstLocation:
            return None  # I guess there were no waypoints?
        if activity.CountTotalWaypoints():
            activity.GetFlatWaypoints()[0].Type = WaypointType.Start
            activity.GetFlatWaypoints()[-1].Type = WaypointType.End
            activity.Stationary = False
        else:
            activity.Stationary = True

        return activity
コード例 #13
0
ファイル: garminconnect.py プロジェクト: 7e7/tapiriik
    def _downloadActivitySummary(self, serviceRecord, activity):
        activityID = activity.ServiceData["ActivityID"]
        session = self._get_session(record=serviceRecord)
        self._rate_limit()
        res = session.get("http://connect.garmin.com/proxy/activity-service-1.3/json/activity/" + str(activityID))



        try:
            raw_data = res.json()
        except ValueError:
            raise APIException("Failure downloading activity summary %s:%s" % (res.status_code, res.text))
        stat_map = {}
        def mapStat(gcKey, statKey, type):
            stat_map[gcKey] = {
                "key": statKey,
                "attr": type
            }

        def applyStats(gc_dict, stats_obj):
            for gc_key, stat in stat_map.items():
                if gc_key in gc_dict:
                    value = float(gc_dict[gc_key]["value"])
                    units = self._unitMap[gc_dict[gc_key]["uom"]]
                    if math.isinf(value):
                        continue # GC returns the minimum speed as "-Infinity" instead of 0 some times :S
                    getattr(stats_obj, stat["key"]).update(ActivityStatistic(units, **({stat["attr"]: value})))

        mapStat("SumMovingDuration", "MovingTime", "value")
        mapStat("SumDuration", "TimerTime", "value")
        mapStat("SumDistance", "Distance", "value")
        mapStat("MinSpeed", "Speed", "min")
        mapStat("MaxSpeed", "Speed", "max")
        mapStat("WeightedMeanSpeed", "Speed", "avg")
        mapStat("MinAirTemperature", "Temperature", "min")
        mapStat("MaxAirTemperature", "Temperature", "max")
        mapStat("WeightedMeanAirTemperature", "Temperature", "avg")
        mapStat("SumEnergy", "Energy", "value")
        mapStat("MaxHeartRate", "HR", "max")
        mapStat("WeightedMeanHeartRate", "HR", "avg")
        mapStat("MaxDoubleCadence", "RunCadence", "max")
        mapStat("WeightedMeanDoubleCadence", "RunCadence", "avg")
        mapStat("MaxBikeCadence", "Cadence", "max")
        mapStat("WeightedMeanBikeCadence", "Cadence", "avg")
        mapStat("MinPower", "Power", "min")
        mapStat("MaxPower", "Power", "max")
        mapStat("WeightedMeanPower", "Power", "avg")
        mapStat("MinElevation", "Elevation", "min")
        mapStat("MaxElevation", "Elevation", "max")
        mapStat("GainElevation", "Elevation", "gain")
        mapStat("LossElevation", "Elevation", "loss")

        applyStats(raw_data["activity"]["activitySummary"], activity.Stats)

        for lap_data in raw_data["activity"]["totalLaps"]["lapSummaryList"]:
            lap = Lap()
            if "BeginTimestamp" in lap_data:
                lap.StartTime = pytz.utc.localize(datetime.utcfromtimestamp(float(lap_data["BeginTimestamp"]["value"]) / 1000))
            if "EndTimestamp" in lap_data:
                lap.EndTime = pytz.utc.localize(datetime.utcfromtimestamp(float(lap_data["EndTimestamp"]["value"]) / 1000))

            elapsed_duration = None
            if "SumElapsedDuration" in lap_data:
                elapsed_duration = timedelta(seconds=round(float(lap_data["SumElapsedDuration"]["value"])))
            elif "SumDuration" in lap_data:
                elapsed_duration = timedelta(seconds=round(float(lap_data["SumDuration"]["value"])))

            if lap.StartTime and elapsed_duration:
                # Always recalculate end time based on duration, if we have the start time
                lap.EndTime = lap.StartTime + elapsed_duration
            if not lap.StartTime and lap.EndTime and elapsed_duration:
                # Sometimes calculate start time based on duration
                lap.StartTime = lap.EndTime - elapsed_duration

            if not lap.StartTime or not lap.EndTime:
                # Garmin Connect is weird.
                raise APIExcludeActivity("Activity lap has no BeginTimestamp or EndTimestamp", userException=UserException(UserExceptionType.Corrupt))

            applyStats(lap_data, lap.Stats)
            activity.Laps.append(lap)

        # In Garmin Land, max can be smaller than min for this field :S
        if activity.Stats.Power.Max is not None and activity.Stats.Power.Min is not None and activity.Stats.Power.Min > activity.Stats.Power.Max:
            activity.Stats.Power.Min = None
コード例 #14
0
    def DownloadActivity(self, svcRecord, activity):

        service_id = svcRecord._id
        user = db.users.find_one({
            'ConnectedServices': {
                '$elemMatch': {
                    'ID': service_id,
                    'Service': self.ID
                }
            }
        })

        userID = svcRecord.ExternalID
        oauth_token = svcRecord.Authorization.get('OAuthToken')
        user_access_token = svcRecord.Authorization.get('AccessToken')
        user_access_token_secret = svcRecord.Authorization.get(
            'AccessTokenSecret')

        logging.info("\t Building signin for activity detail")

        user_tokens = {
            'access_token': user_access_token,
            'access_token_secret': user_access_token_secret,
            'oauth_token': oauth_token
        }
        payload = ""

        start_date = datetime.now() - timedelta(days=1)
        end_date = start_date + timedelta(seconds=86400)
        start_date_tmstmp = str(int(start_date.timestamp()))
        end_date_tmstmp = str(int(end_date.timestamp()))
        start_date_str = start_date.strftime("%Y-%m-%d")
        end_date_str = end_date.strftime("%Y-%m-%d")

        signin_parameters = {
            'upload_start_time': start_date_tmstmp,
            'upload_end_time': end_date_tmstmp,
        }
        signin_info = self._request_signin('GET',
                                           self.URI_ACTIVITIES_DETAIL,
                                           user_tokens,
                                           parameters=signin_parameters)

        resp = requests.request("GET",
                                signin_info['path'],
                                data=payload,
                                headers=signin_info['header'])

        if resp.status_code != 204 and resp.status_code != 200:
            logging.info(
                "\t An error occured while downloading Garmin Health activities from %s to %s "
                % (start_date_str, end_date_str))

        json_data = resp.json()
        activity_id = activity.ServiceData["ActivityID"]
        activity_detail_id = activity_id + '-detail'
        if json_data:
            for item in json_data:
                if activity_detail_id == item['summaryId']:
                    lapsdata = []

                    if "laps" in item:
                        for lap in item['laps']:
                            lapsdata.append(lap['startTimeInSeconds'])

                    ridedata = {}
                    lapWaypoints = []
                    startTimeLap = activity.StartTime
                    endTimeLap = activity.EndTime

                    if "samples" in item:
                        activity.GPS = True
                        activity.Stationary = False
                        for pt in item['samples']:
                            wp = Waypoint()

                            delta = int(pt.get('clockDurationInSeconds'))
                            dateStartPoint = int(pt.get('startTimeInSeconds'))
                            dateStartPointDt = datetime.utcfromtimestamp(
                                dateStartPoint)

                            wp.Timestamp = dateStartPointDt

                            wp.Location = Location()
                            if "latitudeInDegree" in pt:
                                wp.Location.Latitude = float(
                                    pt.get('latitudeInDegree'))
                            if "longitudeInDegree" in pt:
                                wp.Location.Longitude = float(
                                    pt.get('longitudeInDegree'))
                            if "elevationInMeters" in pt:
                                wp.Location.Altitude = int(
                                    pt.get('elevationInMeters'))

                            if "totalDistanceInMeters" in pt:
                                wp.Distance = int(
                                    pt.get('totalDistanceInMeters'))

                            if "speedMetersPerSecond" in pt:
                                wp.Speed = int(pt.get('speedMetersPerSecond'))

                            if "heartRate" in pt:
                                wp.HR = int(pt.get('heartRate'))

                            # current sample is = to lap occur , so store current nap and build a new one
                            if dateStartPoint in lapsdata:

                                lap = Lap(stats=activity.Stats,
                                          startTime=startTimeLap,
                                          endTime=dateStartPointDt)
                                lap.Waypoints = lapWaypoints
                                activity.Laps.append(lap)
                                # re init a new lap
                                startTimeLap = datetime.utcfromtimestamp(
                                    dateStartPoint)
                                lapWaypoints = []
                            # add occur
                            lapWaypoints.append(wp)

                        # build last lap
                        if len(lapWaypoints) > 0:
                            lap = Lap(stats=activity.Stats,
                                      startTime=startTimeLap,
                                      endTime=endTimeLap)
                            lap.Waypoints = lapWaypoints
                            activity.Laps.append(lap)
                    else:
                        activity.Laps = [
                            Lap(startTime=activity.StartTime,
                                endTime=activity.EndTime,
                                stats=activity.Stats)
                        ]

                    break
        return activity
コード例 #15
0
    def DownloadActivity(self, serviceRecord, activity):
        session = self._get_session(serviceRecord)
        act_id = activity.ServiceData["ID"]
        activityDetails = session.get(
            "https://api.nike.com/me/sport/activities/%s" % act_id,
            params=self._with_auth(session))
        activityDetails = activityDetails.json()

        streams = {
            metric["metricType"].lower(): self._nikeStream(metric)
            for metric in activityDetails["metrics"]
        }

        activity.GPS = activityDetails["isGpsActivity"]

        if activity.GPS:
            activityGps = session.get(
                "https://api.nike.com/me/sport/activities/%s/gps" % act_id,
                params=self._with_auth(session))
            activityGps = activityGps.json()
            streams["gps"] = self._nikeStream(activityGps, "waypoints")
            activity.Stats.Elevation.update(
                ActivityStatistic(ActivityStatisticUnit.Meters,
                                  gain=float(activityGps["elevationGain"]),
                                  loss=float(activityGps["elevationLoss"]),
                                  max=float(activityGps["elevationMax"]),
                                  min=float(activityGps["elevationMin"])))

        lap = Lap(startTime=activity.StartTime, endTime=activity.EndTime)
        lap.Stats = activity.Stats
        activity.Laps = [lap]
        # I thought I wrote StreamSampler to be generator-friendly - nope.
        streams = {k: list(v) for k, v in streams.items()}

        # The docs are unclear on which of these are actually stream metrics, oh well
        def stream_waypoint(offset,
                            speed=None,
                            distance=None,
                            heartrate=None,
                            calories=None,
                            steps=None,
                            watts=None,
                            gps=None,
                            **kwargs):
            wp = Waypoint()
            wp.Timestamp = activity.StartTime + timedelta(seconds=offset)
            wp.Speed = float(speed) if speed else None
            wp.Distance = float(distance) / 1000 if distance else None
            wp.HR = float(heartrate) if heartrate else None
            wp.Calories = float(calories) if calories else None
            wp.Power = float(watts) if watts else None

            if gps:
                wp.Location = Location(lat=float(gps["latitude"]),
                                       lon=float(gps["longitude"]),
                                       alt=float(gps["elevation"]))
            lap.Waypoints.append(wp)

        StreamSampler.SampleWithCallback(stream_waypoint, streams)

        activity.Stationary = len(lap.Waypoints) == 0

        return activity
コード例 #16
0
ファイル: sporttracks.py プロジェクト: CptanPanic/tapiriik
    def _downloadActivity(self, serviceRecord, activity, returnFirstLocation=False):
        activityURI = activity.ServiceData["ActivityURI"]
        cookies = self._get_cookies(record=serviceRecord)
        activityData = requests.get(activityURI, cookies=cookies)
        activityData = activityData.json()

        if "clock_duration" in activityData:
            activity.EndTime = activity.StartTime + timedelta(seconds=float(activityData["clock_duration"]))

        activity.Private = "sharing" in activityData and activityData["sharing"] != "public"

        if "notes" in activityData:
            activity.Notes = activityData["notes"]

        activity.Stats.Energy = ActivityStatistic(ActivityStatisticUnit.Kilojoules, value=float(activityData["calories"]))

        activity.Stats.Elevation = ActivityStatistic(ActivityStatisticUnit.Meters, gain=float(activityData["elevation_gain"]) if "elevation_gain" in activityData else None, loss=float(activityData["elevation_loss"]) if "elevation_loss" in activityData else None)

        activity.Stats.HR = ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, avg=activityData["avg_heartrate"] if "avg_heartrate" in activityData else None, max=activityData["max_heartrate"] if "max_heartrate" in activityData else None)
        activity.Stats.Cadence = ActivityStatistic(ActivityStatisticUnit.RevolutionsPerMinute, avg=activityData["avg_cadence"] if "avg_cadence" in activityData else None, max=activityData["max_cadence"] if "max_cadence" in activityData else None)
        activity.Stats.Power = ActivityStatistic(ActivityStatisticUnit.Watts, avg=activityData["avg_power"] if "avg_power" in activityData else None, max=activityData["max_power"] if "max_power" in activityData else None)

        laps_info = []
        laps_starts = []
        if "laps" in activityData:
            laps_info = activityData["laps"]
            for lap in activityData["laps"]:
                laps_starts.append(dateutil.parser.parse(lap["start_time"]))
        lap = None
        for lapinfo in laps_info:
            lap = Lap()
            activity.Laps.append(lap)
            lap.StartTime = dateutil.parser.parse(lapinfo["start_time"])
            lap.EndTime = lap.StartTime + timedelta(seconds=lapinfo["clock_duration"])
            if "type" in lapinfo:
                lap.Intensity = LapIntensity.Active if lapinfo["type"] == "ACTIVE" else LapIntensity.Rest
            if "distance" in lapinfo:
                lap.Stats.Distance = ActivityStatistic(ActivityStatisticUnit.Meters, value=float(lapinfo["distance"]))
            if "duration" in lapinfo:
                lap.Stats.MovingTime = ActivityStatistic(ActivityStatisticUnit.Time, value=timedelta(seconds=lapinfo["duration"]))
            if "calories" in lapinfo:
                lap.Stats.Energy = ActivityStatistic(ActivityStatisticUnit.Kilojoules, value=lapinfo["calories"])
            if "elevation_gain" in lapinfo:
                lap.Stats.Elevation.update(ActivityStatistic(ActivityStatisticUnit.Meters, gain=float(lapinfo["elevation_gain"])))
            if "elevation_loss" in lapinfo:
                lap.Stats.Elevation.update(ActivityStatistic(ActivityStatisticUnit.Meters, loss=float(lapinfo["elevation_loss"])))
            if "max_speed" in lapinfo:
                lap.Stats.Speed.update(ActivityStatistic(ActivityStatisticUnit.MetersPerSecond, max=float(lapinfo["max_speed"])))
            if "max_speed" in lapinfo:
                lap.Stats.Speed.update(ActivityStatistic(ActivityStatisticUnit.MetersPerSecond, max=float(lapinfo["max_speed"])))
            if "avg_speed" in lapinfo:
                lap.Stats.Speed.update(ActivityStatistic(ActivityStatisticUnit.MetersPerSecond, avg=float(lapinfo["avg_speed"])))
            if "max_heartrate" in lapinfo:
                lap.Stats.HR.update(ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, max=float(lapinfo["max_heartrate"])))
            if "avg_heartrate" in lapinfo:
                lap.Stats.HR.update(ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, avg=float(lapinfo["avg_heartrate"])))
        if lap is None: # No explicit laps => make one that encompasses the entire activity
            lap = Lap()
            activity.Laps.append(lap)
            lap.Stats = activity.Stats
            lap.StartTime = activity.StartTime
            lap.EndTime = activity.EndTime

        if "location" not in activityData:
            activity.Stationary = True
        else:
            activity.Stationary = False
            timerStops = []
            if "timer_stops" in activityData:
                for stop in activityData["timer_stops"]:
                    timerStops.append([dateutil.parser.parse(stop[0]), dateutil.parser.parse(stop[1])])

            def isInTimerStop(timestamp):
                for stop in timerStops:
                    if timestamp >= stop[0] and timestamp < stop[1]:
                        return True
                    if timestamp >= stop[1]:
                        return False
                return False

              # Collate the individual streams into our waypoints.
            # Everything is resampled by nearest-neighbour to the rate of the location stream.
            parallel_indices = {}
            parallel_stream_lengths = {}
            for secondary_stream in ["elevation", "heartrate", "power", "cadence", "distance"]:
                if secondary_stream in activityData:
                    parallel_indices[secondary_stream] = 0
                    parallel_stream_lengths[secondary_stream] = len(activityData[secondary_stream])

            wasInPause = False
            currentLapIdx = 0
            lap = activity.Laps[currentLapIdx]
            for idx in range(0, len(activityData["location"]), 2):
                # Pick the nearest indices in the parallel streams
                for parallel_stream, parallel_index in parallel_indices.items():
                    if parallel_index + 2 == parallel_stream_lengths[parallel_stream]:
                        continue  # We're at the end of this stream
                    # Is the next datapoint a better choice than the current?
                    if abs(activityData["location"][idx] - activityData[parallel_stream][parallel_index + 2]) < abs(activityData["location"][idx] - activityData[parallel_stream][parallel_index]):
                        parallel_indices[parallel_stream] += 2

                waypoint = Waypoint(activity.StartTime + timedelta(0, activityData["location"][idx]))
                waypoint.Location = Location(activityData["location"][idx+1][0], activityData["location"][idx+1][1], None)
                if "elevation" in parallel_indices:
                    waypoint.Location.Altitude = activityData["elevation"][parallel_indices["elevation"]+1]

                if returnFirstLocation:
                    return waypoint.Location

                if "heartrate" in parallel_indices:
                    waypoint.HR = activityData["heartrate"][parallel_indices["heartrate"]+1]

                if "power" in parallel_indices:
                    waypoint.Power = activityData["power"][parallel_indices["power"]+1]

                if "cadence" in parallel_indices:
                    waypoint.Cadence = activityData["cadence"][parallel_indices["cadence"]+1]

                if "distance" in parallel_indices:
                    waypoint.Distance = activityData["distance"][parallel_indices["distance"]+1]

                inPause = isInTimerStop(waypoint.Timestamp)
                waypoint.Type = WaypointType.Regular if not inPause else WaypointType.Pause
                if wasInPause and not inPause:
                    waypoint.Type = WaypointType.Resume
                wasInPause = inPause

                # We only care if it's possible to start a new lap, i.e. there are more left
                if currentLapIdx + 1 < len(laps_starts):
                    if laps_starts[currentLapIdx + 1] < waypoint.Timestamp:
                        # A new lap has started
                        currentLapIdx += 1
                        lap = activity.Laps[currentLapIdx]

                lap.Waypoints.append(waypoint)

            if returnFirstLocation:
                return None  # I guess there were no waypoints?
            if activity.CountTotalWaypoints():
                activity.Laps[0].Waypoints[0].Type = WaypointType.Start
                activity.Laps[-1].Waypoints[-1].Type = WaypointType.End
        return activity
コード例 #17
0
ファイル: strava.py プロジェクト: cpfair/tapiriik
    def DownloadActivity(self, svcRecord, activity):
        if activity.ServiceData["Manual"]:  # I should really add a param to DownloadActivity for this value as opposed to constantly doing this
            # We've got as much information as we're going to get - we need to copy it into a Lap though.
            activity.Laps = [Lap(startTime=activity.StartTime, endTime=activity.EndTime, stats=activity.Stats)]
            return activity
        activityID = activity.ServiceData["ActivityID"]

        streamdata = self._requestWithAuth(lambda session: session.get("https://www.strava.com/api/v3/activities/" + str(activityID) + "/streams/time,altitude,heartrate,cadence,watts,temp,moving,latlng,distance,velocity_smooth"), svcRecord)
        if streamdata.status_code == 401:
            raise APIException("No authorization to download activity", block=True, user_exception=UserException(UserExceptionType.Authorization, intervention_required=True))

        try:
            streamdata = streamdata.json()
        except:
            raise APIException("Stream data returned is not JSON")

        if "message" in streamdata and streamdata["message"] == "Record Not Found":
            raise APIException("Could not find activity")

        ridedata = {}
        for stream in streamdata:
            ridedata[stream["type"]] = stream["data"]

        lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime) # Strava doesn't support laps, but we need somewhere to put the waypoints.
        activity.Laps = [lap]
        lap.Waypoints = []

        hasHR = "heartrate" in ridedata and len(ridedata["heartrate"]) > 0
        hasCadence = "cadence" in ridedata and len(ridedata["cadence"]) > 0
        hasTemp = "temp" in ridedata and len(ridedata["temp"]) > 0
        hasPower = ("watts" in ridedata and len(ridedata["watts"]) > 0)
        hasAltitude = "altitude" in ridedata and len(ridedata["altitude"]) > 0
        hasDistance = "distance" in ridedata and len(ridedata["distance"]) > 0
        hasVelocity = "velocity_smooth" in ridedata and len(ridedata["velocity_smooth"]) > 0

        if "error" in ridedata:
            raise APIException("Strava error " + ridedata["error"])

        inPause = False

        waypointCt = len(ridedata["time"])
        for idx in range(0, waypointCt - 1):

            waypoint = Waypoint(activity.StartTime + timedelta(0, ridedata["time"][idx]))
            if "latlng" in ridedata:
                latlng = ridedata["latlng"][idx]
                waypoint.Location = Location(latlng[0], latlng[1], None)
                if waypoint.Location.Longitude == 0 and waypoint.Location.Latitude == 0:
                    waypoint.Location.Longitude = None
                    waypoint.Location.Latitude = None

            if hasAltitude:
                if not waypoint.Location:
                    waypoint.Location = Location(None, None, None)
                waypoint.Location.Altitude = float(ridedata["altitude"][idx])

            # When pausing, Strava sends this format:
            # idx = 100 ; time = 1000; moving = true
            # idx = 101 ; time = 1001; moving = true  => convert to Pause
            # idx = 102 ; time = 2001; moving = false => convert to Resume: (2001-1001) seconds pause
            # idx = 103 ; time = 2002; moving = true

            if idx == 0:
                waypoint.Type = WaypointType.Start
            elif idx == waypointCt - 2:
                waypoint.Type = WaypointType.End
            elif idx < waypointCt - 2 and ridedata["moving"][idx+1] and inPause:
                waypoint.Type = WaypointType.Resume
                inPause = False
            elif idx < waypointCt - 2 and not ridedata["moving"][idx+1] and not inPause:
                waypoint.Type = WaypointType.Pause
                inPause = True

            if hasHR:
                waypoint.HR = ridedata["heartrate"][idx]
            if hasCadence:
                waypoint.Cadence = ridedata["cadence"][idx]
            if hasTemp:
                waypoint.Temp = ridedata["temp"][idx]
            if hasPower:
                waypoint.Power = ridedata["watts"][idx]
            if hasVelocity:
                waypoint.Speed = ridedata["velocity_smooth"][idx]
            if hasDistance:
                waypoint.Distance = ridedata["distance"][idx]
            lap.Waypoints.append(waypoint)

        return activity
コード例 #18
0
ファイル: strava.py プロジェクト: andylolz/tapiriik
    def DownloadActivity(self, svcRecord, activity):
        if activity.ServiceData["Manual"]:  # I should really add a param to DownloadActivity for this value as opposed to constantly doing this
            # We've got as much information as we're going to get - we need to copy it into a Lap though.
            activity.Laps = [Lap(startTime=activity.StartTime, endTime=activity.EndTime, stats=activity.Stats)]
            return activity
        activityID = activity.ServiceData["ActivityID"]

        self._globalRateLimit()
        streamdata = requests.get("https://www.strava.com/api/v3/activities/" + str(activityID) + "/streams/time,altitude,heartrate,cadence,watts,temp,moving,latlng", headers=self._apiHeaders(svcRecord))
        if streamdata.status_code == 401:
            raise APIException("No authorization to download activity", block=True, user_exception=UserException(UserExceptionType.Authorization, intervention_required=True))

        try:
            streamdata = streamdata.json()
        except:
            raise APIException("Stream data returned is not JSON")

        if "message" in streamdata and streamdata["message"] == "Record Not Found":
            raise APIException("Could not find activity")

        ridedata = {}
        for stream in streamdata:
            ridedata[stream["type"]] = stream["data"]

        lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime) # Strava doesn't support laps, but we need somewhere to put the waypoints.
        activity.Laps = [lap]
        lap.Waypoints = []

        hasHR = "heartrate" in ridedata and len(ridedata["heartrate"]) > 0
        hasCadence = "cadence" in ridedata and len(ridedata["cadence"]) > 0
        hasTemp = "temp" in ridedata and len(ridedata["temp"]) > 0
        hasPower = ("watts" in ridedata and len(ridedata["watts"]) > 0)
        hasAltitude = "altitude" in ridedata and len(ridedata["altitude"]) > 0

        if "error" in ridedata:
            raise APIException("Strava error " + ridedata["error"])

        waypointCt = len(ridedata["time"])
        for idx in range(0, waypointCt - 1):

            waypoint = Waypoint(activity.StartTime + timedelta(0, ridedata["time"][idx]))
            if "latlng" in ridedata:
                latlng = ridedata["latlng"][idx]
                waypoint.Location = Location(latlng[0], latlng[1], None)
                if waypoint.Location.Longitude == 0 and waypoint.Location.Latitude == 0:
                    waypoint.Location.Longitude = None
                    waypoint.Location.Latitude = None

            if hasAltitude:
                if not waypoint.Location:
                    waypoint.Location = Location(None, None, None)
                waypoint.Location.Altitude = float(ridedata["altitude"][idx])

            if idx == 0:
                waypoint.Type = WaypointType.Start
            elif idx == waypointCt - 2:
                waypoint.Type = WaypointType.End

            if hasHR:
                waypoint.HR = ridedata["heartrate"][idx]
            if hasCadence:
                waypoint.Cadence = ridedata["cadence"][idx]
            if hasTemp:
                waypoint.Temp = ridedata["temp"][idx]
            if hasPower:
                waypoint.Power = ridedata["watts"][idx]
            lap.Waypoints.append(waypoint)

        return activity
コード例 #19
0
    def _downloadActivitySummary(self, serviceRecord, activity):
        activityID = activity.ServiceData["ActivityID"]

        summary_resp = self._request_with_reauth(lambda session: session.get("https://connect.garmin.com/modern/proxy/activity-service/activity/" + str(activityID)), serviceRecord)

        try:
            summary_data = summary_resp.json()
        except ValueError:
            raise APIException("Failure downloading activity summary %s:%s" % (summary_resp.status_code, summary_resp.text))
        stat_map = {}


        def mapStat(gcKey, statKey, type, units):
            stat_map[gcKey] = {
                "key": statKey,
                "attr": type,
                "units": units
            }

        def applyStats(gc_dict, stats_obj):
            for gc_key, stat in stat_map.items():
                if gc_key in gc_dict:
                    value = float(gc_dict[gc_key])
                    if math.isinf(value):
                        continue # GC returns the minimum speed as "-Infinity" instead of 0 some times :S
                    getattr(stats_obj, stat["key"]).update(ActivityStatistic(stat["units"], **({stat["attr"]: value})))

        mapStat("movingDuration", "MovingTime", "value", ActivityStatisticUnit.Seconds)
        mapStat("duration", "TimerTime", "value", ActivityStatisticUnit.Seconds)
        mapStat("distance", "Distance", "value", ActivityStatisticUnit.Meters)
        mapStat("maxSpeed", "Speed", "max", ActivityStatisticUnit.MetersPerSecond)
        mapStat("averageSpeed", "Speed", "avg", ActivityStatisticUnit.MetersPerSecond)
        mapStat("calories", "Energy", "value", ActivityStatisticUnit.Kilocalories)
        mapStat("maxHR", "HR", "max", ActivityStatisticUnit.BeatsPerMinute)
        mapStat("averageHR", "HR", "avg", ActivityStatisticUnit.BeatsPerMinute)
        mapStat("minElevation", "Elevation", "min", ActivityStatisticUnit.Meters)
        mapStat("maxElevation", "Elevation", "max", ActivityStatisticUnit.Meters)
        mapStat("elevationGain", "Elevation", "gain", ActivityStatisticUnit.Meters)
        mapStat("elevationLoss", "Elevation", "loss", ActivityStatisticUnit.Meters)
        mapStat("averageBikeCadence", "Cadence", "avg", ActivityStatisticUnit.RevolutionsPerMinute)
        mapStat("averageCadence", "Cadence", "avg", ActivityStatisticUnit.StepsPerMinute)

        applyStats(summary_data["summaryDTO"], activity.Stats)

        laps_resp = self._request_with_reauth(lambda session: session.get("https://connect.garmin.com/modern/proxy/activity-service/activity/%s/splits" % str(activityID)), serviceRecord)
        try:
            laps_data = laps_resp.json()
        except ValueError:
            raise APIException("Failure downloading activity laps summary %s:%s" % (laps_resp.status_code, laps_resp.text))

        for lap_data in laps_data["lapDTOs"]:
            lap = Lap()
            if "startTimeGMT" in lap_data:
                lap.StartTime = pytz.utc.localize(datetime.strptime(lap_data["startTimeGMT"], "%Y-%m-%dT%H:%M:%S.0"))

            elapsed_duration = None
            if "elapsedDuration" in lap_data:
                elapsed_duration = timedelta(seconds=round(float(lap_data["elapsedDuration"])))
            elif "duration" in lap_data:
                elapsed_duration = timedelta(seconds=round(float(lap_data["duration"])))

            if lap.StartTime and elapsed_duration:
                # Always recalculate end time based on duration, if we have the start time
                lap.EndTime = lap.StartTime + elapsed_duration
            if not lap.StartTime and lap.EndTime and elapsed_duration:
                # Sometimes calculate start time based on duration
                lap.StartTime = lap.EndTime - elapsed_duration

            if not lap.StartTime or not lap.EndTime:
                # Garmin Connect is weird.
                raise APIExcludeActivity("Activity lap has no BeginTimestamp or EndTimestamp", user_exception=UserException(UserExceptionType.Corrupt))

            applyStats(lap_data, lap.Stats)
            activity.Laps.append(lap)

        # In Garmin Land, max can be smaller than min for this field :S
        if activity.Stats.Power.Max is not None and activity.Stats.Power.Min is not None and activity.Stats.Power.Min > activity.Stats.Power.Max:
            activity.Stats.Power.Min = None
コード例 #20
0
ファイル: decathlon.py プロジェクト: AnaelJrdn/hub-decathlon
    def DownloadActivity(self, svcRecord, activity):
        activityID = activity.ServiceData["ActivityID"]

        logging.info("\t\t DC LOADING  : " + str(activityID))

        headers = self._getAuthHeaders(svcRecord)
        self._rate_limit()
        resp = requests.get(DECATHLON_API_BASE_URL + "/activity/" +
                            activityID + "/fullactivity.xml",
                            headers=headers)

        if resp.status_code == 401:
            raise APIException("No authorization to download activity",
                               block=True,
                               user_exception=UserException(
                                   UserExceptionType.Authorization,
                                   intervention_required=True))

        try:
            root = xml.fromstring(resp.content)
        except:
            raise APIException(
                "Stream data returned from Decathlon is not XML")

        activity.GPS = False
        activity.Stationary = True
        #work on date
        startdate = root.find('.//STARTDATE').text
        timezone = root.find('.//TIMEZONE').text
        datebase = parse(startdate + timezone)

        ridedata = {}
        ridedataindex = []

        for pt in root.iter('LOCATION'):
            delta = int(pt.get('elapsed_time'))
            ridedataindex.append(delta)
            ridedata[delta] = {}
            if activityID == 'eu2132ac60d9a40a1d9a':
                logging.info('========time : ' + str(delta))
                logging.info('========lat : ' +
                             str(float(pt.find('LATITUDE').text[:8])))
            ridedata[delta]['LATITUDE'] = float(pt.find('LATITUDE').text[:8])
            ridedata[delta]['LONGITUDE'] = float(pt.find('LONGITUDE').text[:8])
            ridedata[delta]['ELEVATION'] = int(pt.find('ELEVATION').text[:8])

        if len(ridedata) > 0:
            activity.GPS = True
            activity.Stationary = False

        for measure in root.iter('MEASURE'):
            delta = int(measure.get('elapsed_time'))
            if delta not in ridedataindex:
                ridedataindex.append(delta)
                ridedata[delta] = {}

            for measureValue in measure.iter('VALUE'):
                if measureValue.get('id') == "1":
                    ridedata[delta]['HR'] = int(measureValue.text)
                if measureValue.get('id') == "6":
                    ridedata[delta]['SPEED'] = int(measureValue.text)
                if measureValue.get('id') == "5":
                    ridedata[delta]['DISTANCE'] = int(measureValue.text)
                if measureValue.get('id') == "20":
                    ridedata[delta]['LAP'] = int(measureValue.text)

        ridedataindex.sort()

        if len(ridedata) == 0:
            lap = Lap(stats=activity.Stats,
                      startTime=activity.StartTime,
                      endTime=activity.EndTime)
            activity.Laps = [lap]
        else:
            lapWaypoints = []
            startTimeLap = activity.StartTime
            for elapsedTime in ridedataindex:
                rd = ridedata[elapsedTime]
                wp = Waypoint()
                delta = elapsedTime
                formatedDate = datebase + timedelta(seconds=delta)
                wp.Timestamp = formatedDate  #self._parseDate(formatedDate.isoformat())

                if 'LATITUDE' in rd:
                    wp.Location = Location()
                    wp.Location.Latitude = rd['LATITUDE']
                    wp.Location.Longitude = rd['LONGITUDE']
                    wp.Location.Altitude = rd['ELEVATION']

                if 'HR' in rd:
                    wp.HR = rd['HR']

                if 'SPEED' in rd:
                    wp.Speed = rd['SPEED'] / 3600

                if 'DISTANCE' in rd:
                    wp.Distance = rd['DISTANCE']

                lapWaypoints.append(wp)

                if "LAP" in rd:
                    #build the lap
                    lap = Lap(stats=activity.Stats,
                              startTime=startTimeLap,
                              endTime=formatedDate)
                    lap.Waypoints = lapWaypoints
                    activity.Laps.append(lap)
                    # re init a new lap
                    startTimeLap = formatedDate
                    lapWaypoints = []

            #build last lap
            if len(lapWaypoints) > 0:
                lap = Lap(stats=activity.Stats,
                          startTime=startTimeLap,
                          endTime=formatedDate)
                lap.Waypoints = lapWaypoints
                activity.Laps.append(lap)

        return activity
コード例 #21
0
ファイル: strava.py プロジェクト: 7e7/tapiriik
    def DownloadActivity(self, svcRecord, activity):
        if activity.ServiceData["Manual"]:  # I should really add a param to DownloadActivity for this value as opposed to constantly doing this
            # We've got as much information as we're going to get - we need to copy it into a Lap though.
            activity.Laps = [Lap(startTime=activity.StartTime, endTime=activity.EndTime, stats=activity.Stats)]
            return activity
        activityID = activity.ServiceData["ActivityID"]

        streamdata = requests.get("https://www.strava.com/api/v3/activities/" + str(activityID) + "/streams/time,altitude,heartrate,cadence,watts,temp,moving,latlng", headers=self._apiHeaders(svcRecord))
        if streamdata.status_code == 401:
            self._logAPICall("download", (svcRecord.ExternalID, str(activity.StartTime)), "auth")
            raise APIException("No authorization to download activity", block=True, user_exception=UserException(UserExceptionType.Authorization, intervention_required=True))

        try:
            streamdata = streamdata.json()
        except:
            raise APIException("Stream data returned is not JSON")

        if "message" in streamdata and streamdata["message"] == "Record Not Found":
            self._logAPICall("download", (svcRecord.ExternalID, str(activity.StartTime)), "missing")
            raise APIException("Could not find activity")

        ridedata = {}
        for stream in streamdata:
            ridedata[stream["type"]] = stream["data"]

        lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime) # Strava doesn't support laps, but we need somewhere to put the waypoints.
        activity.Laps = [lap]
        lap.Waypoints = []

        hasHR = "heartrate" in ridedata and len(ridedata["heartrate"]) > 0
        hasCadence = "cadence" in ridedata and len(ridedata["cadence"]) > 0
        hasTemp = "temp" in ridedata and len(ridedata["temp"]) > 0
        hasPower = ("watts" in ridedata and len(ridedata["watts"]) > 0)
        hasAltitude = "altitude" in ridedata and len(ridedata["altitude"]) > 0
        hasMovingData = "moving" in ridedata and len(ridedata["moving"]) > 0
        moving = True

        if "error" in ridedata:
            self._logAPICall("download", (svcRecord.ExternalID, str(activity.StartTime)), "data")
            raise APIException("Strava error " + ridedata["error"])

        hasLocation = False
        waypointCt = len(ridedata["time"])
        for idx in range(0, waypointCt - 1):
            latlng = ridedata["latlng"][idx]

            waypoint = Waypoint(activity.StartTime + timedelta(0, ridedata["time"][idx]))
            latlng = ridedata["latlng"][idx]
            waypoint.Location = Location(latlng[0], latlng[1], None)
            if waypoint.Location.Longitude == 0 and waypoint.Location.Latitude == 0:
                waypoint.Location.Longitude = None
                waypoint.Location.Latitude = None
            else:  # strava only returns 0 as invalid coords, so no need to check for null (update: ??)
                hasLocation = True
            if hasAltitude:
                waypoint.Location.Altitude = float(ridedata["altitude"][idx])

            if idx == 0:
                waypoint.Type = WaypointType.Start
            elif idx == waypointCt - 2:
                waypoint.Type = WaypointType.End
            elif hasMovingData and not moving and ridedata["moving"][idx] is True:
                waypoint.Type = WaypointType.Resume
                moving = True
            elif hasMovingData and ridedata["moving"][idx] is False:
                waypoint.Type = WaypointType.Pause
                moving = False

            if hasHR:
                waypoint.HR = ridedata["heartrate"][idx]
            if hasCadence:
                waypoint.Cadence = ridedata["cadence"][idx]
            if hasTemp:
                waypoint.Temp = ridedata["temp"][idx]
            if hasPower:
                waypoint.Power = ridedata["watts"][idx]
            lap.Waypoints.append(waypoint)
        if not hasLocation:
            self._logAPICall("download", (svcRecord.ExternalID, str(activity.StartTime)), "faulty")
            raise APIExcludeActivity("No waypoints with location", activityId=activityID, userException=UserException(UserExceptionType.Corrupt))
        self._logAPICall("download", (svcRecord.ExternalID, str(activity.StartTime)), None)
        return activity
コード例 #22
0
ファイル: testtools.py プロジェクト: bertvansteen/tapiriik
    def create_random_activity(svc=None, actType=ActivityType.Other, tz=False, record=None, withPauses=True, withLaps=True):
        ''' creates completely random activity with valid waypoints and data '''
        act = TestTools.create_blank_activity(svc, actType, record=record)

        if tz is True:
            tz = pytz.timezone(pytz.all_timezones[random.randint(0, len(pytz.all_timezones) - 1)])
            act.TZ = tz
        elif tz is not False:
            act.TZ = tz

        if act.CountTotalWaypoints() > 0:
            raise ValueError("Waypoint list already populated")
        # this is entirely random in case the testing account already has events in it (API doesn't support delete, etc)
        act.StartTime = datetime(random.randint(2000, 2020), random.randint(1, 12), random.randint(1, 28), random.randint(0, 23), random.randint(0, 59), random.randint(0, 59))
        if tz is not False:
            if hasattr(tz, "localize"):
                act.StartTime = tz.localize(act.StartTime)
            else:
                act.StartTime = act.StartTime.replace(tzinfo=tz)
        act.EndTime = act.StartTime + timedelta(0, random.randint(60 * 5, 60 * 60))  # don't really need to upload 1000s of pts to test this...
        act.Stats.Distance = ActivityStatistic(ActivityStatisticUnit.Meters, value=random.random() * 10000)
        act.Name = str(random.random())
        paused = False
        waypointTime = act.StartTime
        backToBackPauses = False
        act.Laps = []
        lap = Lap(startTime=act.StartTime)
        while waypointTime < act.EndTime:
            wp = Waypoint()
            if waypointTime == act.StartTime:
                wp.Type = WaypointType.Start
            wp.Timestamp = waypointTime
            wp.Location = Location(random.random() * 180 - 90, random.random() * 180 - 90, random.random() * 1000)  # this is gonna be one intense activity

            if not (wp.HR == wp.Cadence == wp.Calories == wp.Power == wp.Temp == None):
                raise ValueError("Waypoint did not initialize cleanly")
            if svc.SupportsHR:
                wp.HR = float(random.randint(90, 180))
            if svc.SupportsPower:
                wp.Power = float(random.randint(0, 1000))
            if svc.SupportsCalories:
                wp.Calories = float(random.randint(0, 500))
            if svc.SupportsCadence:
                wp.Cadence = float(random.randint(0, 100))
            if svc.SupportsTemp:
                wp.Temp = float(random.randint(0, 100))

            if withPauses and (random.randint(40, 50) == 42 or backToBackPauses) and not paused:  # pause quite often
                wp.Type = WaypointType.Pause
                paused = True

            elif paused:
                paused = False
                wp.Type = WaypointType.Resume
                backToBackPauses = not backToBackPauses

            waypointTime += timedelta(0, int(random.random() + 9.5))  # 10ish seconds

            lap.Waypoints.append(wp)
            if waypointTime > act.EndTime:
                wp.Timestamp = act.EndTime
                wp.Type = WaypointType.End
            elif withLaps and wp.Timestamp < act.EndTime and random.randint(40, 60) == 42:
                # occasionally start new laps
                lap.EndTime = wp.Timestamp
                act.Laps.append(lap)
                lap = Lap(startTime=waypointTime)

        # Final lap
        lap.EndTime = act.EndTime
        act.Laps.append(lap)
        if act.CountTotalWaypoints() == 0:
            raise ValueError("No waypoints populated")

        act.CalculateUID()
        act.EnsureTZ()

        return act
コード例 #23
0
ファイル: singletracker.py プロジェクト: OkanEsen/tapiriik
    def DownloadActivity(self, svcRecord, activity):

        activityID = activity.ServiceData["ActivityID"]
        extID = svcRecord.ExternalID
        url = self.SingletrackerDomain + "getRideData"

        payload = {"userId": extID, "rideId": activityID}
        headers = {
            'content-type': "application/json",
            'cache-control': "no-cache",
        }
        streamdata = requests.post(url, data=json.dumps(payload), headers=headers)
        if streamdata.status_code == 500:
            raise APIException("Internal server error")

        if streamdata.status_code == 403:
            raise APIException("No authorization to download activity", block=True,
                               user_exception=UserException(UserExceptionType.Authorization,
                                                            intervention_required=True))
        if streamdata.status_code != 200:
            raise APIException("Unknown Singletracker response %d %s" % (streamdata.status_code, streamdata.text))

        try:
            streamdata = streamdata.json()
        except:
            raise APIException("Stream data returned is not JSON")

        ridedata = {}

        lap = Lap(stats=activity.Stats, startTime=activity.StartTime,
                  endTime=activity.EndTime)  # Singletracker doesn't support laps, but we need somewhere to put the waypoints.
        activity.Laps = [lap]
        lap.Waypoints = []

        wayPointExist = False

        for stream in streamdata:
            waypoint = Waypoint(dateutil.parser.parse(stream["time"], ignoretz=True))

            if "latitude" in stream:
                if "longitude" in stream:
                    latitude = stream["latitude"]
                    longitude = stream["longitude"]
                    waypoint.Location = Location(latitude, longitude, None)
                    if waypoint.Location.Longitude == 0 and waypoint.Location.Latitude == 0:
                        waypoint.Location.Longitude = None
                        waypoint.Location.Latitude = None

            if "elevation" in stream:
                if not waypoint.Location:
                    waypoint.Location = Location(None, None, None)
                waypoint.Location.Altitude = stream["elevation"]

            if "distance" in stream:
                waypoint.Distance = stream["distance"]
            if "speed" in stream:
                waypoint.Speed = stream["speed"]
            waypoint.Type = WaypointType.Regular
            lap.Waypoints.append(waypoint)

        return activity
コード例 #24
0
ファイル: endomondo.py プロジェクト: CptanPanic/tapiriik
    def _populateActivityFromTrackData(self, activity, recordText, minimumWaypoints=False):
        lap = Lap()
        activity.Laps = [lap]
        ###       1ST RECORD      ###
        # userID;
        # timestamp - create date?;
        # type? W=1st
        # User name;
        # activity name;
        # activity type;
        # another timestamp - start time of event?;
        # duration.00;
        # distance (km);
        # kcal;
        #;
        # max alt;
        # min alt;
        # max HR;
        # avg HR;

        ###     TRACK RECORDS     ###
        # timestamp;
        # type (2=start, 3=end, 0=pause, 1=resume);
        # latitude;
        # longitude;
        #;
        #;
        # alt;
        # hr;
        wptsWithLocation = False
        wptsWithNonZeroAltitude = False
        rows = recordText.split("\n")
        for row in rows:
            if row == "OK" or len(row) == 0:
                continue
            split = row.split(";")
            if split[2] == "W":
                # init record
                lap.Stats.MovingTime = ActivityStatistic(ActivityStatisticUnit.Time, value=timedelta(seconds=float(split[7])) if split[7] != "" else None)
                lap.Stats.Distance = ActivityStatistic(ActivityStatisticUnit.Kilometers, value=float(split[8]) if split[8] != "" else None)
                lap.Stats.HR = ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, avg=float(split[14]) if split[14] != "" else None, max=float(split[13]) if split[13] != "" else None)
                lap.Stats.Elevation = ActivityStatistic(ActivityStatisticUnit.Meters, min=float(split[12]) if split[12] != "" else None, max=float(split[11]) if split[11] != "" else None)
                lap.Stats.Energy = ActivityStatistic(ActivityStatisticUnit.Kilocalories, value=float(split[12]) if split[12] != "" else None)
                activity.Stats.update(lap.Stats)
                lap.Stats = activity.Stats
                activity.Name = split[4]
            else:
                wp = Waypoint()
                if split[1] == "2":
                    wp.Type = WaypointType.Start
                elif split[1] == "3":
                    wp.Type = WaypointType.End
                elif split[1] == "0":
                    wp.Type = WaypointType.Pause
                elif split[1] == "1":
                    wp.Type = WaypointType.Resume
                else:
                    wp.Type == WaypointType.Regular

                if split[0] == "":
                    continue  # no timestamp, for whatever reason
                wp.Timestamp = pytz.utc.localize(datetime.strptime(split[0], "%Y-%m-%d %H:%M:%S UTC"))  # it's like this as opposed to %z so I know when they change things (it'll break)
                if split[2] != "":
                    wp.Location = Location(float(split[2]), float(split[3]), None)
                    if wp.Location.Longitude > 180 or wp.Location.Latitude > 90 or wp.Location.Longitude < -180 or wp.Location.Latitude < -90:
                        raise APIExcludeActivity("Out of range lat/lng")
                    if wp.Location.Latitude is not None and wp.Location.Latitude is not None:
                        wptsWithLocation = True
                    if split[6] != "":
                        wp.Location.Altitude = float(split[6])  # why this is missing: who knows?
                        if wp.Location.Altitude != 0:
                            wptsWithNonZeroAltitude = True

                if split[7] != "":
                    wp.HR = float(split[7])
                lap.Waypoints.append(wp)
                if wptsWithLocation and minimumWaypoints:
                    break
        lap.Waypoints = sorted(activity.Waypoints, key=lambda v: v.Timestamp)
        if wptsWithLocation:
            activity.EnsureTZ(recalculate=True)
            if not wptsWithNonZeroAltitude:  # do this here so, should the activity run near sea level, altitude data won't be spotty
                for x in lap.Waypoints:  # clear waypoints of altitude data if all of them were logged at 0m (invalid)
                    if x.Location is not None:
                        x.Location.Altitude = None
        else:
            lap.Waypoints = []  # practically speaking
コード例 #25
0
ファイル: strava.py プロジェクト: bonkzwonil/tapiriik
    def DownloadActivity(self, svcRecord, activity):
        if activity.ServiceData[
                "Manual"]:  # I should really add a param to DownloadActivity for this value as opposed to constantly doing this
            # We've got as much information as we're going to get - we need to copy it into a Lap though.
            activity.Laps = [
                Lap(startTime=activity.StartTime,
                    endTime=activity.EndTime,
                    stats=activity.Stats)
            ]
            return activity
        activityID = activity.ServiceData["ActivityID"]

        streamdata = requests.get(
            "https://www.strava.com/api/v3/activities/" + str(activityID) +
            "/streams/time,altitude,heartrate,cadence,watts,temp,moving,latlng",
            headers=self._apiHeaders(svcRecord))
        if streamdata.status_code == 401:
            self._logAPICall("download",
                             (svcRecord.ExternalID, str(activity.StartTime)),
                             "auth")
            raise APIException("No authorization to download activity",
                               block=True,
                               user_exception=UserException(
                                   UserExceptionType.Authorization,
                                   intervention_required=True))

        try:
            streamdata = streamdata.json()
        except:
            raise APIException("Stream data returned is not JSON")

        if "message" in streamdata and streamdata[
                "message"] == "Record Not Found":
            self._logAPICall("download",
                             (svcRecord.ExternalID, str(activity.StartTime)),
                             "missing")
            raise APIException("Could not find activity")

        ridedata = {}
        for stream in streamdata:
            ridedata[stream["type"]] = stream["data"]

        lap = Lap(
            stats=activity.Stats,
            startTime=activity.StartTime,
            endTime=activity.EndTime
        )  # Strava doesn't support laps, but we need somewhere to put the waypoints.
        activity.Laps = [lap]
        lap.Waypoints = []

        hasHR = "heartrate" in ridedata and len(ridedata["heartrate"]) > 0
        hasCadence = "cadence" in ridedata and len(ridedata["cadence"]) > 0
        hasTemp = "temp" in ridedata and len(ridedata["temp"]) > 0
        hasPower = ("watts" in ridedata and len(ridedata["watts"]) > 0)
        hasAltitude = "altitude" in ridedata and len(ridedata["altitude"]) > 0
        hasMovingData = "moving" in ridedata and len(ridedata["moving"]) > 0
        moving = True

        if "error" in ridedata:
            self._logAPICall("download",
                             (svcRecord.ExternalID, str(activity.StartTime)),
                             "data")
            raise APIException("Strava error " + ridedata["error"])

        hasLocation = False
        waypointCt = len(ridedata["time"])
        for idx in range(0, waypointCt - 1):
            latlng = ridedata["latlng"][idx]

            waypoint = Waypoint(activity.StartTime +
                                timedelta(0, ridedata["time"][idx]))
            latlng = ridedata["latlng"][idx]
            waypoint.Location = Location(latlng[0], latlng[1], None)
            if waypoint.Location.Longitude == 0 and waypoint.Location.Latitude == 0:
                waypoint.Location.Longitude = None
                waypoint.Location.Latitude = None
            else:  # strava only returns 0 as invalid coords, so no need to check for null (update: ??)
                hasLocation = True
            if hasAltitude:
                waypoint.Location.Altitude = float(ridedata["altitude"][idx])

            if idx == 0:
                waypoint.Type = WaypointType.Start
            elif idx == waypointCt - 2:
                waypoint.Type = WaypointType.End
            elif hasMovingData and not moving and ridedata["moving"][
                    idx] is True:
                waypoint.Type = WaypointType.Resume
                moving = True
            elif hasMovingData and ridedata["moving"][idx] is False:
                waypoint.Type = WaypointType.Pause
                moving = False

            if hasHR:
                waypoint.HR = ridedata["heartrate"][idx]
            if hasCadence:
                waypoint.Cadence = ridedata["cadence"][idx]
            if hasTemp:
                waypoint.Temp = ridedata["temp"][idx]
            if hasPower:
                waypoint.Power = ridedata["watts"][idx]
            lap.Waypoints.append(waypoint)
        if not hasLocation:
            self._logAPICall("download",
                             (svcRecord.ExternalID, str(activity.StartTime)),
                             "faulty")
            raise APIExcludeActivity("No waypoints with location",
                                     activityId=activityID,
                                     userException=UserException(
                                         UserExceptionType.Corrupt))
        self._logAPICall("download",
                         (svcRecord.ExternalID, str(activity.StartTime)), None)
        return activity
コード例 #26
0
ファイル: garminconnect.py プロジェクト: neilboyd/tapiriik
    def _downloadActivitySummary(self, serviceRecord, activity):
        activityID = activity.ServiceData["ActivityID"]

        summary_resp = self._request_with_reauth(lambda session: session.get("https://connect.garmin.com/modern/proxy/activity-service/activity/" + str(activityID)), serviceRecord)

        try:
            summary_data = summary_resp.json()
        except ValueError:
            raise APIException("Failure downloading activity summary %s:%s" % (summary_resp.status_code, summary_resp.text))
        stat_map = {}


        def mapStat(gcKey, statKey, type, units):
            stat_map[gcKey] = {
                "key": statKey,
                "attr": type,
                "units": units
            }

        def applyStats(gc_dict, stats_obj):
            for gc_key, stat in stat_map.items():
                if gc_key in gc_dict:
                    value = float(gc_dict[gc_key])
                    if math.isinf(value):
                        continue # GC returns the minimum speed as "-Infinity" instead of 0 some times :S
                    getattr(stats_obj, stat["key"]).update(ActivityStatistic(stat["units"], **({stat["attr"]: value})))

        mapStat("movingDuration", "MovingTime", "value", ActivityStatisticUnit.Seconds)
        mapStat("duration", "TimerTime", "value", ActivityStatisticUnit.Seconds)
        mapStat("distance", "Distance", "value", ActivityStatisticUnit.Meters)
        mapStat("maxSpeed", "Speed", "max", ActivityStatisticUnit.MetersPerSecond)
        mapStat("averageSpeed", "Speed", "avg", ActivityStatisticUnit.MetersPerSecond)
        mapStat("calories", "Energy", "value", ActivityStatisticUnit.Kilocalories)
        mapStat("maxHR", "HR", "max", ActivityStatisticUnit.BeatsPerMinute)
        mapStat("averageHR", "HR", "avg", ActivityStatisticUnit.BeatsPerMinute)
        mapStat("minElevation", "Elevation", "min", ActivityStatisticUnit.Meters)
        mapStat("maxElevation", "Elevation", "max", ActivityStatisticUnit.Meters)
        mapStat("elevationGain", "Elevation", "gain", ActivityStatisticUnit.Meters)
        mapStat("elevationLoss", "Elevation", "loss", ActivityStatisticUnit.Meters)
        mapStat("averageBikeCadence", "Cadence", "avg", ActivityStatisticUnit.RevolutionsPerMinute)
        mapStat("averageCadence", "Cadence", "avg", ActivityStatisticUnit.StepsPerMinute)

        applyStats(summary_data["summaryDTO"], activity.Stats)

        laps_resp = self._request_with_reauth(lambda session: session.get("https://connect.garmin.com/modern/proxy/activity-service/activity/%s/splits" % str(activityID)), serviceRecord)
        try:
            laps_data = laps_resp.json()
        except ValueError:
            raise APIException("Failure downloading activity laps summary %s:%s" % (laps_resp.status_code, laps_resp.text))

        for lap_data in laps_data["lapDTOs"]:
            lap = Lap()
            if "startTimeGMT" in lap_data:
                lap.StartTime = pytz.utc.localize(datetime.strptime(lap_data["startTimeGMT"], "%Y-%m-%dT%H:%M:%S.0"))

            elapsed_duration = None
            if "elapsedDuration" in lap_data:
                elapsed_duration = timedelta(seconds=round(float(lap_data["elapsedDuration"])))
            elif "duration" in lap_data:
                elapsed_duration = timedelta(seconds=round(float(lap_data["duration"])))

            if lap.StartTime and elapsed_duration:
                # Always recalculate end time based on duration, if we have the start time
                lap.EndTime = lap.StartTime + elapsed_duration
            if not lap.StartTime and lap.EndTime and elapsed_duration:
                # Sometimes calculate start time based on duration
                lap.StartTime = lap.EndTime - elapsed_duration

            if not lap.StartTime or not lap.EndTime:
                # Garmin Connect is weird.
                raise APIExcludeActivity("Activity lap has no BeginTimestamp or EndTimestamp", user_exception=UserException(UserExceptionType.Corrupt))

            applyStats(lap_data, lap.Stats)
            activity.Laps.append(lap)

        # In Garmin Land, max can be smaller than min for this field :S
        if activity.Stats.Power.Max is not None and activity.Stats.Power.Min is not None and activity.Stats.Power.Min > activity.Stats.Power.Max:
            activity.Stats.Power.Min = None
コード例 #27
0
    def DownloadActivity(self, svcRecord, activity):
        activityID = activity.ServiceData["ActivityID"]

        logger.info("\t\t DC LOADING  : " + str(activityID))

        headers = self._getAuthHeaders(svcRecord)
        resp = requests.get(self.ApiEndpoint + "/activity/" + activityID +
                            "/fullactivity.xml",
                            headers=headers)
        if resp.status_code == 401:
            raise APIException("No authorization to download activity",
                               block=True,
                               user_exception=UserException(
                                   UserExceptionType.Authorization,
                                   intervention_required=True))

        try:
            root = xml.fromstring(resp.content)
        except:
            raise APIException(
                "Stream data returned from DecathlonCoach is not XML")

        lap = Lap(stats=activity.Stats,
                  startTime=activity.StartTime,
                  endTime=activity.EndTime)
        activity.Laps = [lap]
        lap.Waypoints = []

        activity.GPS = False

        #work on date
        startdate = root.find('.//STARTDATE').text
        timezone = root.find('.//TIMEZONE').text
        datebase = parse(startdate + timezone)

        for pt in root.iter('LOCATION'):
            wp = Waypoint()

            delta = int(pt.get('elapsed_time'))
            formatedDate = datebase + timedelta(seconds=delta)

            wp.Timestamp = formatedDate  #self._parseDate(formatedDate.isoformat())

            wp.Location = Location()
            wp.Location.Latitude = float(pt.find('LATITUDE').text[:8])
            wp.Location.Longitude = float(pt.find('LONGITUDE').text[:8])
            activity.GPS = True
            wp.Location.Altitude = int(pt.find('ELEVATION').text[:8])

            #get the HR value in the Datastream node and measures collection
            for hr in root.iter('MEASURE'):
                if pt.get('elapsed_time') == hr.get('elapsed_time'):
                    for measureValue in hr.iter('VALUE'):
                        if measureValue.get('id') == "1":
                            wp.HR = int(measureValue.text)
                            break
                    break

            lap.Waypoints.append(wp)
        activity.Stationary = len(lap.Waypoints) == 0

        return activity
コード例 #28
0
    def _downloadActivitySummary(self, serviceRecord, activity):
        activityID = activity.ServiceData["ActivityID"]

        res = self._request_with_reauth(
            serviceRecord, lambda session: session.
            get("https://connect.garmin.com/modern/proxy/activity-service-1.3/json/activity/"
                + str(activityID)))

        try:
            raw_data = res.json()
        except ValueError:
            raise APIException("Failure downloading activity summary %s:%s" %
                               (res.status_code, res.text))
        stat_map = {}

        def mapStat(gcKey, statKey, type):
            stat_map[gcKey] = {"key": statKey, "attr": type}

        def applyStats(gc_dict, stats_obj):
            for gc_key, stat in stat_map.items():
                if gc_key in gc_dict:
                    value = float(gc_dict[gc_key]["value"])
                    units = self._unitMap[gc_dict[gc_key]["uom"]]
                    if math.isinf(value):
                        continue  # GC returns the minimum speed as "-Infinity" instead of 0 some times :S
                    getattr(stats_obj, stat["key"]).update(
                        ActivityStatistic(units, **({
                            stat["attr"]: value
                        })))

        mapStat("SumMovingDuration", "MovingTime", "value")
        mapStat("SumDuration", "TimerTime", "value")
        mapStat("SumDistance", "Distance", "value")
        mapStat("MinSpeed", "Speed", "min")
        mapStat("MaxSpeed", "Speed", "max")
        mapStat("WeightedMeanSpeed", "Speed", "avg")
        mapStat("MinAirTemperature", "Temperature", "min")
        mapStat("MaxAirTemperature", "Temperature", "max")
        mapStat("WeightedMeanAirTemperature", "Temperature", "avg")
        mapStat("SumEnergy", "Energy", "value")
        mapStat("MaxHeartRate", "HR", "max")
        mapStat("WeightedMeanHeartRate", "HR", "avg")
        mapStat("MaxDoubleCadence", "RunCadence", "max")
        mapStat("WeightedMeanDoubleCadence", "RunCadence", "avg")
        mapStat("MaxBikeCadence", "Cadence", "max")
        mapStat("WeightedMeanBikeCadence", "Cadence", "avg")
        mapStat("MinPower", "Power", "min")
        mapStat("MaxPower", "Power", "max")
        mapStat("WeightedMeanPower", "Power", "avg")
        mapStat("MinElevation", "Elevation", "min")
        mapStat("MaxElevation", "Elevation", "max")
        mapStat("GainElevation", "Elevation", "gain")
        mapStat("LossElevation", "Elevation", "loss")

        applyStats(raw_data["activity"]["activitySummary"], activity.Stats)

        for lap_data in raw_data["activity"]["totalLaps"]["lapSummaryList"]:
            lap = Lap()
            if "BeginTimestamp" in lap_data:
                lap.StartTime = pytz.utc.localize(
                    datetime.utcfromtimestamp(
                        float(lap_data["BeginTimestamp"]["value"]) / 1000))
            if "EndTimestamp" in lap_data:
                lap.EndTime = pytz.utc.localize(
                    datetime.utcfromtimestamp(
                        float(lap_data["EndTimestamp"]["value"]) / 1000))

            elapsed_duration = None
            if "SumElapsedDuration" in lap_data:
                elapsed_duration = timedelta(seconds=round(
                    float(lap_data["SumElapsedDuration"]["value"])))
            elif "SumDuration" in lap_data:
                elapsed_duration = timedelta(
                    seconds=round(float(lap_data["SumDuration"]["value"])))

            if lap.StartTime and elapsed_duration:
                # Always recalculate end time based on duration, if we have the start time
                lap.EndTime = lap.StartTime + elapsed_duration
            if not lap.StartTime and lap.EndTime and elapsed_duration:
                # Sometimes calculate start time based on duration
                lap.StartTime = lap.EndTime - elapsed_duration

            if not lap.StartTime or not lap.EndTime:
                # Garmin Connect is weird.
                raise APIExcludeActivity(
                    "Activity lap has no BeginTimestamp or EndTimestamp",
                    user_exception=UserException(UserExceptionType.Corrupt))

            applyStats(lap_data, lap.Stats)
            activity.Laps.append(lap)

        # In Garmin Land, max can be smaller than min for this field :S
        if activity.Stats.Power.Max is not None and activity.Stats.Power.Min is not None and activity.Stats.Power.Min > activity.Stats.Power.Max:
            activity.Stats.Power.Min = None
コード例 #29
0
ファイル: sporttracks.py プロジェクト: 7e7/tapiriik
    def _downloadActivity(self, serviceRecord, activity, returnFirstLocation=False):
        activityURI = activity.ServiceData["ActivityURI"]
        headers = self._getAuthHeaders(serviceRecord)
        activityData = requests.get(activityURI, headers=headers)
        activityData = activityData.json()

        if "clock_duration" in activityData:
            activity.EndTime = activity.StartTime + timedelta(seconds=float(activityData["clock_duration"]))

        activity.Private = "sharing" in activityData and activityData["sharing"] != "public"

        activity.GPS = False # Gets set back if there is GPS data

        if "notes" in activityData:
            activity.Notes = activityData["notes"]

        activity.Stats.Energy = ActivityStatistic(ActivityStatisticUnit.Kilojoules, value=float(activityData["calories"]))

        activity.Stats.Elevation = ActivityStatistic(ActivityStatisticUnit.Meters, gain=float(activityData["elevation_gain"]) if "elevation_gain" in activityData else None, loss=float(activityData["elevation_loss"]) if "elevation_loss" in activityData else None)

        activity.Stats.HR = ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, avg=activityData["avg_heartrate"] if "avg_heartrate" in activityData else None, max=activityData["max_heartrate"] if "max_heartrate" in activityData else None)
        activity.Stats.Cadence = ActivityStatistic(ActivityStatisticUnit.RevolutionsPerMinute, avg=activityData["avg_cadence"] if "avg_cadence" in activityData else None, max=activityData["max_cadence"] if "max_cadence" in activityData else None)
        activity.Stats.Power = ActivityStatistic(ActivityStatisticUnit.Watts, avg=activityData["avg_power"] if "avg_power" in activityData else None, max=activityData["max_power"] if "max_power" in activityData else None)

        laps_info = []
        laps_starts = []
        if "laps" in activityData:
            laps_info = activityData["laps"]
            for lap in activityData["laps"]:
                laps_starts.append(dateutil.parser.parse(lap["start_time"]))
        lap = None
        for lapinfo in laps_info:
            lap = Lap()
            activity.Laps.append(lap)
            lap.StartTime = dateutil.parser.parse(lapinfo["start_time"])
            lap.EndTime = lap.StartTime + timedelta(seconds=lapinfo["clock_duration"])
            if "type" in lapinfo:
                lap.Intensity = LapIntensity.Active if lapinfo["type"] == "ACTIVE" else LapIntensity.Rest
            if "distance" in lapinfo:
                lap.Stats.Distance = ActivityStatistic(ActivityStatisticUnit.Meters, value=float(lapinfo["distance"]))
            if "duration" in lapinfo:
                lap.Stats.TimerTime = ActivityStatistic(ActivityStatisticUnit.Seconds, value=lapinfo["duration"])
            if "calories" in lapinfo:
                lap.Stats.Energy = ActivityStatistic(ActivityStatisticUnit.Kilojoules, value=lapinfo["calories"])
            if "elevation_gain" in lapinfo:
                lap.Stats.Elevation.update(ActivityStatistic(ActivityStatisticUnit.Meters, gain=float(lapinfo["elevation_gain"])))
            if "elevation_loss" in lapinfo:
                lap.Stats.Elevation.update(ActivityStatistic(ActivityStatisticUnit.Meters, loss=float(lapinfo["elevation_loss"])))
            if "max_speed" in lapinfo:
                lap.Stats.Speed.update(ActivityStatistic(ActivityStatisticUnit.MetersPerSecond, max=float(lapinfo["max_speed"])))
            if "max_speed" in lapinfo:
                lap.Stats.Speed.update(ActivityStatistic(ActivityStatisticUnit.MetersPerSecond, max=float(lapinfo["max_speed"])))
            if "avg_speed" in lapinfo:
                lap.Stats.Speed.update(ActivityStatistic(ActivityStatisticUnit.MetersPerSecond, avg=float(lapinfo["avg_speed"])))
            if "max_heartrate" in lapinfo:
                lap.Stats.HR.update(ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, max=float(lapinfo["max_heartrate"])))
            if "avg_heartrate" in lapinfo:
                lap.Stats.HR.update(ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, avg=float(lapinfo["avg_heartrate"])))
        if lap is None: # No explicit laps => make one that encompasses the entire activity
            lap = Lap()
            activity.Laps.append(lap)
            lap.Stats = activity.Stats
            lap.StartTime = activity.StartTime
            lap.EndTime = activity.EndTime
        elif len(activity.Laps) == 1:
            activity.Stats.update(activity.Laps[0].Stats) # Lap stats have a bit more info generally.
            activity.Laps[0].Stats = activity.Stats

        timerStops = []
        if "timer_stops" in activityData:
            for stop in activityData["timer_stops"]:
                timerStops.append([dateutil.parser.parse(stop[0]), dateutil.parser.parse(stop[1])])

        def isInTimerStop(timestamp):
            for stop in timerStops:
                if timestamp >= stop[0] and timestamp < stop[1]:
                    return True
                if timestamp >= stop[1]:
                    return False
            return False

        # Collate the individual streams into our waypoints.
        # Global sample rate is variable - will pick the next nearest stream datapoint.
        # Resampling happens on a lookbehind basis - new values will only appear their timestamp has been reached/passed

        wasInPause = False
        currentLapIdx = 0
        lap = activity.Laps[currentLapIdx]

        streams = []
        for stream in ["location", "elevation", "heartrate", "power", "cadence", "distance"]:
            if stream in activityData:
                streams.append(stream)
        stream_indices = dict([(stream, -1) for stream in streams]) # -1 meaning the stream has yet to start
        stream_lengths = dict([(stream, len(activityData[stream])/2) for stream in streams])
        # Data comes as "stream":[timestamp,value,timestamp,value,...]
        stream_values = {}
        for stream in streams:
            values = []
            for x in range(0,int(len(activityData[stream])/2)):
                values.append((activityData[stream][x * 2], activityData[stream][x * 2 + 1]))
            stream_values[stream] = values

        currentOffset = 0

        def streamVal(stream):
            nonlocal stream_values, stream_indices
            return stream_values[stream][stream_indices[stream]][1]

        def hasStreamData(stream):
            nonlocal stream_indices, streams
            return stream in streams and stream_indices[stream] >= 0

        while True:
            advance_stream = None
            advance_offset = None
            for stream in streams:
                if stream_indices[stream] + 1 == stream_lengths[stream]:
                    continue # We're at the end - can't advance
                if advance_offset is None or stream_values[stream][stream_indices[stream] + 1][0] - currentOffset < advance_offset:
                    advance_offset = stream_values[stream][stream_indices[stream] + 1][0] - currentOffset
                    advance_stream = stream
            if not advance_stream:
                break # We've hit the end of every stream, stop
            # Advance streams sharing the current timestamp
            for stream in streams:
                if stream == advance_stream:
                    continue # For clarity, we increment this later
                if stream_indices[stream] + 1 == stream_lengths[stream]:
                    continue # We're at the end - can't advance
                if stream_values[stream][stream_indices[stream] + 1][0] == stream_values[advance_stream][stream_indices[advance_stream] + 1][0]:
                    stream_indices[stream] += 1
            stream_indices[advance_stream] += 1 # Advance the key stream for this waypoint
            currentOffset = stream_values[advance_stream][stream_indices[advance_stream]][0] # Update the current time offset

            waypoint = Waypoint(activity.StartTime + timedelta(seconds=currentOffset))

            if hasStreamData("location"):
                waypoint.Location = Location(streamVal("location")[0], streamVal("location")[1], None)
                activity.GPS = True
                if returnFirstLocation:
                    return waypoint.Location

            if hasStreamData("elevation"):
                if not waypoint.Location:
                    waypoint.Location = Location(None, None, None)
                waypoint.Location.Altitude = streamVal("elevation")

            if hasStreamData("heartrate"):
                waypoint.HR = streamVal("heartrate")

            if hasStreamData("power"):
                waypoint.Power = streamVal("power")

            if hasStreamData("cadence"):
                waypoint.Cadence = streamVal("cadence")

            if hasStreamData("distance"):
                waypoint.Distance = streamVal("distance")

            inPause = isInTimerStop(waypoint.Timestamp)
            waypoint.Type = WaypointType.Regular if not inPause else WaypointType.Pause
            if wasInPause and not inPause:
                waypoint.Type = WaypointType.Resume
            wasInPause = inPause

            # We only care if it's possible to start a new lap, i.e. there are more left
            if currentLapIdx + 1 < len(laps_starts):
                if laps_starts[currentLapIdx + 1] < waypoint.Timestamp:
                    # A new lap has started
                    currentLapIdx += 1
                    lap = activity.Laps[currentLapIdx]

            lap.Waypoints.append(waypoint)

        if returnFirstLocation:
            return None  # I guess there were no waypoints?
        if activity.CountTotalWaypoints():
            activity.GetFlatWaypoints()[0].Type = WaypointType.Start
            activity.GetFlatWaypoints()[-1].Type = WaypointType.End
            activity.Stationary = False
        else:
            activity.Stationary = True

        return activity
コード例 #30
0
    def DownloadActivity(self, svcRecord, activity):

        activityID = activity.ServiceData["ActivityID"]
        extID = svcRecord.ExternalID
        url = self.SingletrackerDomain + "getRideData"

        payload = {"userId": extID, "rideId": activityID}
        headers = {
            'content-type': "application/json",
            'cache-control': "no-cache",
        }
        streamdata = requests.post(url,
                                   data=json.dumps(payload),
                                   headers=headers)
        if streamdata.status_code == 500:
            raise APIException("Internal server error")

        if streamdata.status_code == 403:
            raise APIException("No authorization to download activity",
                               block=True,
                               user_exception=UserException(
                                   UserExceptionType.Authorization,
                                   intervention_required=True))
        if streamdata.status_code == 200:  # Ok
            try:
                streamdata = streamdata.json()
            except:
                raise APIException("Stream data returned is not JSON")

        ridedata = {}

        lap = Lap(
            stats=activity.Stats,
            startTime=activity.StartTime,
            endTime=activity.EndTime
        )  # Singletracker doesn't support laps, but we need somewhere to put the waypoints.
        activity.Laps = [lap]
        lap.Waypoints = []

        wayPointExist = False

        for stream in streamdata:
            waypoint = Waypoint(
                dateutil.parser.parse(stream["time"], ignoretz=True))

            if "latitude" in stream:
                if "longitude" in stream:
                    latitude = stream["latitude"]
                    longitude = stream["longitude"]
                    waypoint.Location = Location(latitude, longitude, None)
                    if waypoint.Location.Longitude == 0 and waypoint.Location.Latitude == 0:
                        waypoint.Location.Longitude = None
                        waypoint.Location.Latitude = None

            if "elevation" in stream:
                if not waypoint.Location:
                    waypoint.Location = Location(None, None, None)
                waypoint.Location.Altitude = stream["elevation"]

            if "distance" in stream:
                waypoint.Distance = stream["distance"]
            if "speed" in stream:
                waypoint.Speed = stream["speed"]
            waypoint.Type = WaypointType.Regular
            lap.Waypoints.append(waypoint)

        return activity
コード例 #31
0
ファイル: beginnertriathlete.py プロジェクト: rayhe/tapiriik
    def _populate_sbr_activity(self, api_sbr_activity, usersettings):
        # Example JSON feed (unimportant fields have been removed)
        # [{
        #    "EventId": 63128401,                   #  Internal ID
        #    "EventType": 3,                        #  Swim (3), bike (1), or run (2)
        #    "EventDate": "4/22/2016",
        #    "EventTime": "7:44 AM",                #  User's time, time zone not specified
        #    "Planned": false,                      #  Training plan or actual data
        #    "TotalMinutes": 34.97,
        #    "TotalKilometers": 1.55448,
        #    "AverageHeartRate": 125,
        #    "MinimumHeartRate": 100,
        #    "MaximumHeartRate": 150,
        #    "MemberId": 999999,
        #    "MemberUsername": "******",
        #    "HasDeviceUpload": true,
        #    "DeviceUploadFile": "http://beginnertriathlete.com/discussion/storage/workouts/555555/abcd-123.fit",
        #    "RouteName": "",                       #  Might contain a description of the event
        #    "Comments": "",                        #  Same as above. Not overly often used.
        # }, ... ]

        activity = UploadedActivity()
        workout_id = api_sbr_activity["EventId"]
        eventType = api_sbr_activity["EventType"]
        eventDate = api_sbr_activity["EventDate"]
        eventTime = api_sbr_activity["EventTime"]
        totalMinutes = api_sbr_activity["TotalMinutes"]
        totalKms = api_sbr_activity["TotalKilometers"]
        averageHr = api_sbr_activity["AverageHeartRate"]
        minimumHr = api_sbr_activity["MinimumHeartRate"]
        maximumHr = api_sbr_activity["MaximumHeartRate"]
        deviceUploadFile = api_sbr_activity["DeviceUploadFile"]

        # Basic SBR data does not include GPS or sensor data. If this event originated from a device upload,
        # DownloadActivity will find it.
        activity.Stationary = True

        # Same as above- The data might be there, but it's not supplied in the basic activity feed.
        activity.GPS = False

        activity.Private = usersettings["Privacy"]
        activity.Type = self._workoutTypeMappings[str(eventType)]

        # Get the user's timezone from their profile. (Activity.TZ should be mentioned in the object hierarchy docs?)
        # Question: I believe if DownloadActivity finds device data, it will overwrite this. Which is OK with me.
        # The device data will most likely be more accurate.
        try:
            activity.TZ = pytz.timezone(usersettings["TimeZone"])
        except pytz.exceptions.UnknownTimeZoneError:
            activity.TZ = pytz.timezone(self._serverDefaultTimezone)

        # activity.StartTime and EndTime aren't mentioned in the object hierarchy docs, but I see them
        # set in all the other providers.
        activity.StartTime = dateutil.parser.parse(
            eventDate + " " + eventTime,
            dayfirst=False).replace(tzinfo=activity.TZ)
        activity.EndTime = activity.StartTime + timedelta(minutes=totalMinutes)

        # We can calculate some metrics from the supplied data. Would love to see some non-source code documentation
        # on each statistic and what it expects as input.
        activity.Stats.Distance = ActivityStatistic(ActivityStatisticUnit.Kilometers,
                                                    value=totalKms)
        activity.Stats.HR = ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute,
                                              avg=float(averageHr),
                                              min=float(minimumHr),
                                              max=float(maximumHr))
        activity.Stats.MovingTime = ActivityStatistic(ActivityStatisticUnit.Seconds,
                                                      value=float(totalMinutes * 60))
        activity.Stats.TimerTime = ActivityStatistic(ActivityStatisticUnit.Seconds,
                                                     value=float(totalMinutes * 60))
        # While BT does support laps, the current API doesn't report on them - a limitation that may need to be
        # corrected in a future update. For now, treat manual entries as a single lap. As more and more people upload
        # workouts using devices anyway, this probably matters much less than it once did.
        lap = Lap(stats=activity.Stats, startTime=activity.StartTime, endTime=activity.EndTime)
        activity.Laps = [lap]

        # Not 100% positive how this is utilized, but it is common for all providers. Detects duplicate downloads?
        activity.CalculateUID()

        # If a device file is attached, we'll get more details about this event in DownloadActivity
        activity.ServiceData = {
            "ID": int(workout_id),
            "DeviceUploadFile": deviceUploadFile
        }

        return activity
コード例 #32
0
ファイル: strava.py プロジェクト: soalhn/tapiriik
    def DownloadActivity(self, svcRecord, activity):
        if activity.ServiceData[
                "Manual"]:  # I should really add a param to DownloadActivity for this value as opposed to constantly doing this
            # We've got as much information as we're going to get - we need to copy it into a Lap though.
            activity.Laps = [
                Lap(startTime=activity.StartTime,
                    endTime=activity.EndTime,
                    stats=activity.Stats)
            ]
            return activity
        activityID = activity.ServiceData["ActivityID"]

        self._globalRateLimit()
        streamdata = requests.get(
            "https://www.strava.com/api/v3/activities/" + str(activityID) +
            "/streams/time,altitude,heartrate,cadence,watts,temp,moving,latlng,distance,velocity_smooth",
            headers=self._apiHeaders(svcRecord))
        if streamdata.status_code == 401:
            raise APIException("No authorization to download activity",
                               block=True,
                               user_exception=UserException(
                                   UserExceptionType.Authorization,
                                   intervention_required=True))

        try:
            streamdata = streamdata.json()
        except:
            raise APIException("Stream data returned is not JSON")

        if "message" in streamdata and streamdata[
                "message"] == "Record Not Found":
            raise APIException("Could not find activity")

        ridedata = {}
        for stream in streamdata:
            ridedata[stream["type"]] = stream["data"]

        lap = Lap(
            stats=activity.Stats,
            startTime=activity.StartTime,
            endTime=activity.EndTime
        )  # Strava doesn't support laps, but we need somewhere to put the waypoints.
        activity.Laps = [lap]
        lap.Waypoints = []

        hasHR = "heartrate" in ridedata and len(ridedata["heartrate"]) > 0
        hasCadence = "cadence" in ridedata and len(ridedata["cadence"]) > 0
        hasTemp = "temp" in ridedata and len(ridedata["temp"]) > 0
        hasPower = ("watts" in ridedata and len(ridedata["watts"]) > 0)
        hasAltitude = "altitude" in ridedata and len(ridedata["altitude"]) > 0
        hasDistance = "distance" in ridedata and len(ridedata["distance"]) > 0
        hasVelocity = "velocity_smooth" in ridedata and len(
            ridedata["velocity_smooth"]) > 0

        if "error" in ridedata:
            raise APIException("Strava error " + ridedata["error"])

        inPause = False

        waypointCt = len(ridedata["time"])
        for idx in range(0, waypointCt - 1):

            waypoint = Waypoint(activity.StartTime +
                                timedelta(0, ridedata["time"][idx]))
            if "latlng" in ridedata:
                latlng = ridedata["latlng"][idx]
                waypoint.Location = Location(latlng[0], latlng[1], None)
                if waypoint.Location.Longitude == 0 and waypoint.Location.Latitude == 0:
                    waypoint.Location.Longitude = None
                    waypoint.Location.Latitude = None

            if hasAltitude:
                if not waypoint.Location:
                    waypoint.Location = Location(None, None, None)
                waypoint.Location.Altitude = float(ridedata["altitude"][idx])

            # When pausing, Strava sends this format:
            # idx = 100 ; time = 1000; moving = true
            # idx = 101 ; time = 1001; moving = true  => convert to Pause
            # idx = 102 ; time = 2001; moving = false => convert to Resume: (2001-1001) seconds pause
            # idx = 103 ; time = 2002; moving = true

            if idx == 0:
                waypoint.Type = WaypointType.Start
            elif idx == waypointCt - 2:
                waypoint.Type = WaypointType.End
            elif idx < waypointCt - 2 and ridedata["moving"][idx +
                                                             1] and inPause:
                waypoint.Type = WaypointType.Resume
                inPause = False
            elif idx < waypointCt - 2 and not ridedata["moving"][
                    idx + 1] and not inPause:
                waypoint.Type = WaypointType.Pause
                inPause = True

            if hasHR:
                waypoint.HR = ridedata["heartrate"][idx]
            if hasCadence:
                waypoint.Cadence = ridedata["cadence"][idx]
            if hasTemp:
                waypoint.Temp = ridedata["temp"][idx]
            if hasPower:
                waypoint.Power = ridedata["watts"][idx]
            if hasVelocity:
                waypoint.Speed = ridedata["velocity_smooth"][idx]
            if hasDistance:
                waypoint.Distance = ridedata["distance"][idx]
            lap.Waypoints.append(waypoint)

        return activity
コード例 #33
0
ファイル: endomondo.py プロジェクト: julian-r/tapiriik
    def _populateActivityFromTrackData(self,
                                       activity,
                                       recordText,
                                       minimumWaypoints=False):
        lap = Lap()
        activity.Laps = [lap]
        ###       1ST RECORD      ###
        # userID;
        # timestamp - create date?;
        # type? W=1st
        # User name;
        # activity name;
        # activity type;
        # another timestamp - start time of event?;
        # duration.00;
        # distance (km);
        # kcal;
        #;
        # max alt;
        # min alt;
        # max HR;
        # avg HR;

        ###     TRACK RECORDS     ###
        # timestamp;
        # type (2=start, 3=end, 0=pause, 1=resume);
        # latitude;
        # longitude;
        #;
        #;
        # alt;
        # hr;
        wptsWithLocation = False
        wptsWithNonZeroAltitude = False
        rows = recordText.split("\n")
        for row in rows:
            if row == "OK" or len(row) == 0:
                continue
            split = row.split(";")
            if split[2] == "W":
                # init record
                lap.Stats.TimerTime = ActivityStatistic(
                    ActivityStatisticUnit.Time,
                    value=timedelta(
                        seconds=float(split[7])) if split[7] != "" else None)
                lap.Stats.Distance = ActivityStatistic(
                    ActivityStatisticUnit.Kilometers,
                    value=float(split[8]) if split[8] != "" else None)
                lap.Stats.HR = ActivityStatistic(
                    ActivityStatisticUnit.BeatsPerMinute,
                    avg=float(split[14]) if split[14] != "" else None,
                    max=float(split[13]) if split[13] != "" else None)
                lap.Stats.Elevation = ActivityStatistic(
                    ActivityStatisticUnit.Meters,
                    min=float(split[12]) if split[12] != "" else None,
                    max=float(split[11]) if split[11] != "" else None)
                lap.Stats.Energy = ActivityStatistic(
                    ActivityStatisticUnit.Kilocalories,
                    value=float(split[12]) if split[12] != "" else None)
                activity.Stats.update(lap.Stats)
                lap.Stats = activity.Stats
                activity.Name = split[4]
            else:
                wp = Waypoint()
                if split[1] == "2":
                    wp.Type = WaypointType.Start
                elif split[1] == "3":
                    wp.Type = WaypointType.End
                elif split[1] == "0":
                    wp.Type = WaypointType.Pause
                elif split[1] == "1":
                    wp.Type = WaypointType.Resume
                else:
                    wp.Type == WaypointType.Regular

                if split[0] == "":
                    continue  # no timestamp, for whatever reason
                wp.Timestamp = pytz.utc.localize(
                    datetime.strptime(split[0], "%Y-%m-%d %H:%M:%S UTC")
                )  # it's like this as opposed to %z so I know when they change things (it'll break)
                if split[2] != "":
                    wp.Location = Location(float(split[2]), float(split[3]),
                                           None)
                    if wp.Location.Longitude > 180 or wp.Location.Latitude > 90 or wp.Location.Longitude < -180 or wp.Location.Latitude < -90:
                        raise APIExcludeActivity("Out of range lat/lng")
                    if wp.Location.Latitude is not None and wp.Location.Latitude is not None:
                        wptsWithLocation = True
                    if split[6] != "":
                        wp.Location.Altitude = float(
                            split[6])  # why this is missing: who knows?
                        if wp.Location.Altitude != 0:
                            wptsWithNonZeroAltitude = True

                if split[7] != "":
                    wp.HR = float(split[7])
                lap.Waypoints.append(wp)
                if wptsWithLocation and minimumWaypoints:
                    break
        lap.Waypoints = sorted(activity.Waypoints, key=lambda v: v.Timestamp)
        if wptsWithLocation:
            if not wptsWithNonZeroAltitude:  # do this here so, should the activity run near sea level, altitude data won't be spotty
                for x in lap.Waypoints:  # clear waypoints of altitude data if all of them were logged at 0m (invalid)
                    if x.Location is not None:
                        x.Location.Altitude = None
        else:
            lap.Waypoints = []  # practically speaking
コード例 #34
0
    def create_random_activity(svc=None,
                               actType=ActivityType.Other,
                               tz=False,
                               record=None,
                               withPauses=True,
                               withLaps=True):
        ''' creates completely random activity with valid waypoints and data '''
        act = TestTools.create_blank_activity(svc, actType, record=record)

        if tz is True:
            tz = pytz.timezone("America/Atikokan")
            act.TZ = tz
        elif tz is not False:
            act.TZ = tz

        if act.CountTotalWaypoints() > 0:
            raise ValueError("Waypoint list already populated")
        # this is entirely random in case the testing account already has events in it (API doesn't support delete, etc)
        act.StartTime = datetime(2011, 12, 13, 14, 15, 16)
        if tz is not False:
            if hasattr(tz, "localize"):
                act.StartTime = tz.localize(act.StartTime)
            else:
                act.StartTime = act.StartTime.replace(tzinfo=tz)
        act.EndTime = act.StartTime + timedelta(
            0, random.randint(60 * 5, 60 * 60)
        )  # don't really need to upload 1000s of pts to test this...
        act.Stats.Distance = ActivityStatistic(ActivityStatisticUnit.Meters,
                                               value=random.random() * 10000)
        act.Name = str(random.random())
        paused = False
        waypointTime = act.StartTime
        backToBackPauses = False
        act.Laps = []
        lap = Lap(startTime=act.StartTime)
        while waypointTime < act.EndTime:
            wp = Waypoint()
            if waypointTime == act.StartTime:
                wp.Type = WaypointType.Start
            wp.Timestamp = waypointTime
            wp.Location = Location(
                random.random() * 180 - 90,
                random.random() * 180 - 90,
                random.random() *
                1000)  # this is gonna be one intense activity

            if not (wp.HR == wp.Cadence == wp.Calories == wp.Power == wp.Temp
                    == None):
                raise ValueError("Waypoint did not initialize cleanly")
            if svc.SupportsHR:
                wp.HR = float(random.randint(90, 180))
            if svc.SupportsPower:
                wp.Power = float(random.randint(0, 1000))
            if svc.SupportsCalories:
                wp.Calories = float(random.randint(0, 500))
            if svc.SupportsCadence:
                wp.Cadence = float(random.randint(0, 100))
            if svc.SupportsTemp:
                wp.Temp = float(random.randint(0, 100))

            if withPauses and (random.randint(40, 50) == 42 or backToBackPauses
                               ) and not paused:  # pause quite often
                wp.Type = WaypointType.Pause
                paused = True

            elif paused:
                paused = False
                wp.Type = WaypointType.Resume
                backToBackPauses = not backToBackPauses

            waypointTime += timedelta(0, int(random.random() +
                                             9.5))  # 10ish seconds

            lap.Waypoints.append(wp)
            if waypointTime > act.EndTime:
                wp.Timestamp = act.EndTime
                wp.Type = WaypointType.End
            elif withLaps and wp.Timestamp < act.EndTime and random.randint(
                    40, 60) == 42:
                # occasionally start new laps
                lap.EndTime = wp.Timestamp
                act.Laps.append(lap)
                lap = Lap(startTime=waypointTime)

        # Final lap
        lap.EndTime = act.EndTime
        act.Laps.append(lap)
        if act.CountTotalWaypoints() == 0:
            raise ValueError("No waypoints populated")

        act.CalculateUID()
        act.EnsureTZ()

        return act
コード例 #35
0
ファイル: garminconnect.py プロジェクト: jbuckner/tapiriik
    def _downloadActivitySummary(self, serviceRecord, activity):
        activityID = activity.ServiceData["ActivityID"]
        cookies = self._get_cookies(record=serviceRecord)
        self._rate_limit()
        res = requests.get("https://connect.garmin.com/proxy/activity-service-1.3/json/activity/" + str(activityID), cookies=cookies)

        try:
            raw_data = res.json()
        except ValueError:
            raise APIException("Failure downloading activity summary %s:%s" % (res.status_code, res.text))
        stat_map = {}
        def mapStat(gcKey, statKey, type):
            stat_map[gcKey] = {
                "key": statKey,
                "attr": type
            }

        def applyStats(gc_dict, stats_obj):
            for gc_key, stat in stat_map.items():
                if gc_key in gc_dict:
                    value = float(gc_dict[gc_key]["value"])
                    units = self._unitMap[gc_dict[gc_key]["uom"]]
                    if math.isinf(value):
                        continue # GC returns the minimum speed as "-Infinity" instead of 0 some times :S
                    getattr(stats_obj, stat["key"]).update(ActivityStatistic(units, **({stat["attr"]: value})))

        mapStat("SumMovingDuration", "MovingTime", "value")
        mapStat("SumDuration", "TimerTime", "value")
        mapStat("SumDistance", "Distance", "value")
        mapStat("MinSpeed", "Speed", "min")
        mapStat("MaxSpeed", "Speed", "max")
        mapStat("WeightedMeanSpeed", "Speed", "avg")
        mapStat("MinAirTemperature", "Temperature", "min")
        mapStat("MaxAirTemperature", "Temperature", "max")
        mapStat("WeightedMeanAirTemperature", "Temperature", "avg")
        mapStat("SumEnergy", "Energy", "value")
        mapStat("MaxHeartRate", "HR", "max")
        mapStat("WeightedMeanHeartRate", "HR", "avg")
        mapStat("MaxDoubleCadence", "RunCadence", "max")
        mapStat("WeightedMeanDoubleCadence", "RunCadence", "avg")
        mapStat("MaxBikeCadence", "Cadence", "max")
        mapStat("WeightedMeanBikeCadence", "Cadence", "avg")
        mapStat("MinPower", "Power", "min")
        mapStat("MaxPower", "Power", "max")
        mapStat("WeightedMeanPower", "Power", "avg")
        mapStat("MinElevation", "Elevation", "min")
        mapStat("MaxElevation", "Elevation", "max")
        mapStat("GainElevation", "Elevation", "gain")
        mapStat("LossElevation", "Elevation", "loss")

        applyStats(raw_data["activity"]["activitySummary"], activity.Stats)

        for lap_data in raw_data["activity"]["totalLaps"]["lapSummaryList"]:
            lap = Lap()
            lap.StartTime = pytz.utc.localize(datetime.utcfromtimestamp(float(lap_data["BeginTimestamp"]["value"]) / 1000))
            lap.EndTime = pytz.utc.localize(datetime.utcfromtimestamp(float(lap_data["EndTimestamp"]["value"]) / 1000))
            applyStats(lap_data, lap.Stats)
            activity.Laps.append(lap)

        # In Garmin Land, max can be smaller than min for this field :S
        if activity.Stats.Power.Max is not None and activity.Stats.Power.Min is not None and activity.Stats.Power.Min > activity.Stats.Power.Max:
            activity.Stats.Power.Min = None