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
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
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
def _process_lap_waypoints(self, activity, ridedata, lapdata): 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 inPause = False waypointCt = len(ridedata["time"]) lapWaypoints = [] waypoinStartIndex = lapdata["start_index"] waypoinEndIndex = lapdata["end_index"] powerSum = 0 hrSum = 0 hrMax = 0 for idx in range(waypoinStartIndex, waypoinEndIndex): 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] hrSum += waypoint.HR if waypoint.HR else 0 hrMax = waypoint.HR if waypoint.HR > hrMax else hrMax if hasCadence: waypoint.Cadence = ridedata["cadence"][idx] if hasTemp: waypoint.Temp = ridedata["temp"][idx] if hasPower: waypoint.Power = ridedata["watts"][idx] powerSum += waypoint.Power if waypoint.Power else 0 if hasVelocity: waypoint.Speed = ridedata["velocity_smooth"][idx] if hasDistance: waypoint.Distance = ridedata["distance"][idx] lapWaypoints.append(waypoint) pointsCount = len(lapWaypoints) stats = ActivityStatistics() stats.Distance = ActivityStatistic(ActivityStatisticUnit.Meters, value=lapdata["distance"]) if "max_speed" in lapdata or "average_speed" in lapdata: stats.Speed = ActivityStatistic( ActivityStatisticUnit.MetersPerSecond, avg=lapdata["average_speed"] if "average_speed" in lapdata else None, max=lapdata["max_speed"] if "max_speed" in lapdata else None) stats.MovingTime = ActivityStatistic( ActivityStatisticUnit.Seconds, value=lapdata["moving_time"] if "moving_time" in lapdata and lapdata["moving_time"] > 0 else None) if "average_cadence" in lapdata: stats.Cadence.update( ActivityStatistic(ActivityStatisticUnit.RevolutionsPerMinute, avg=lapdata["average_cadence"])) # Activity could have laps with no trackpoints if pointsCount > 0: if hasHR: stats.HR.update( ActivityStatistic(ActivityStatisticUnit.BeatsPerMinute, avg=hrSum / pointsCount, max=hrMax)) if hasPower: stats.Power = ActivityStatistic(ActivityStatisticUnit.Watts, avg=powerSum / pointsCount) return lapWaypoints, stats
def create_random_activity(svc=None, actType=ActivityType.Other, tz=False, record=None): ''' 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 len(act.Waypoints) > 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 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 (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 if waypointTime > act.EndTime: wp.Timestamp = act.EndTime wp.Type = WaypointType.End act.Waypoints.append(wp) if len(act.Waypoints) == 0: raise ValueError("No waypoints populated") return act
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
def DownloadActivity(self, svcRecord, activity): # thanks to Cosmo Catalano for the API reference code activityID = [ x["ActivityID"] for x in activity.UploadedTo if x["Connection"] == svcRecord ][0] streamdata = requests.get( "https://www.strava.com/api/v3/activities/" + str(activityID) + "/streams/time,altitude,heartrate,cadence,watts,watts_calc,temp,resting,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)) streamdata = streamdata.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"] activity.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 hasRestingData = "resting" in ridedata and len(ridedata["resting"]) > 0 moving = True if "error" in ridedata: 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 hasRestingData and not moving and ridedata["resting"][ idx] is False: waypoint.Type = WaypointType.Resume moving = True elif hasRestingData and ridedata["resting"][idx] is True: 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] activity.Waypoints.append(waypoint) if not hasLocation: raise APIExcludeActivity("No waypoints with location", activityId=activityID) return activity
def DownloadActivity(self, svcRecord, activity): # thanks to Cosmo Catalano for the API reference code activityID = [x["ActivityID"] for x in activity.UploadedTo if x["Connection"] == svcRecord][0] streamdata = requests.get("https://www.strava.com/api/v3/activities/" + str(activityID) + "/streams/time,altitude,heartrate,cadence,watts,watts_calc,temp,resting,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)) streamdata = streamdata.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"] activity.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) or ("watts_calc" in ridedata and len(ridedata["watts_calc"]) > 0) hasAltitude = "altitude" in ridedata and len(ridedata["altitude"]) > 0 hasRestingData = "resting" in ridedata and len(ridedata["resting"]) > 0 moving = True if "error" in ridedata: 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 hasRestingData and not moving and ridedata["resting"][idx] is False: waypoint.Type = WaypointType.Resume moving = True elif hasRestingData and ridedata["resting"][idx] is True: 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] if "watts" in ridedata else ridedata["watts_calc"][idx] activity.Waypoints.append(waypoint) if not hasLocation: raise APIExcludeActivity("No waypoints with location", activityId=activityID) return activity
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
def create_random_activity(svc=None, actType=ActivityType.Other, tz=False, record=None): ''' 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 len(act.Waypoints) > 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.Distance = random.random() * 10000 act.Name = str(random.random()) paused = False waypointTime = act.StartTime backToBackPauses = False 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 (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 if waypointTime > act.EndTime: wp.Timestamp = act.EndTime wp.Type = WaypointType.End act.Waypoints.append(wp) if len(act.Waypoints) == 0: raise ValueError("No waypoints populated") return act
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
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