def DownloadActivityList(self, serviceRecord, exhaustive=False): cookies = self._get_cookies(record=serviceRecord) activities = [] exclusions = [] pageUri = self.OpenFitEndpoint + "/fitnessActivities.json" while True: logger.debug("Req against " + pageUri) res = requests.get(pageUri, cookies=cookies) res = res.json() for act in res["items"]: activity = UploadedActivity() activity.UploadedTo = [{"Connection": serviceRecord, "ActivityURI": act["uri"]}] if len(act["name"].strip()): activity.Name = act["name"] activity.StartTime = dateutil.parser.parse(act["start_time"]) if isinstance(activity.StartTime.tzinfo, tzutc): activity.TZ = pytz.utc # The dateutil tzutc doesn't have an _offset value. else: activity.TZ = pytz.FixedOffset(activity.StartTime.tzinfo._offset.total_seconds() / 60) # Convert the dateutil lame timezones into pytz awesome timezones. activity.StartTime = activity.StartTime.replace(tzinfo=activity.TZ) activity.EndTime = activity.StartTime + timedelta(seconds=float(act["duration"])) # Sometimes activities get returned with a UTC timezone even when they are clearly not in UTC. if activity.TZ == pytz.utc: # So, we get the first location in the activity and calculate the TZ from that. try: firstLocation = self._downloadActivity(serviceRecord, activity, returnFirstLocation=True) except APIExcludeActivity: pass else: activity.CalculateTZ(firstLocation) activity.AdjustTZ() logger.debug("Activity s/t " + str(activity.StartTime)) activity.Distance = float(act["total_distance"]) types = [x.strip().lower() for x in act["type"].split(":")] types.reverse() # The incoming format is like "walking: hiking" and we want the most specific first activity.Type = None for type_key in types: if type_key in self._activityMappings: activity.Type = self._activityMappings[type_key] break if not activity.Type: exclusions.append(APIExcludeActivity("Unknown activity type %s" % act["type"], activityId=act["uri"])) continue activity.CalculateUID() activities.append(activity) if not exhaustive or "next" not in res or not len(res["next"]): break else: pageUri = res["next"] return activities, exclusions
def DownloadActivityList(self, serviceRecord, exhaustive=False): headers = self._getAuthHeaders(serviceRecord) activities = [] exclusions = [] pageUri = self.OpenFitEndpoint + "/fitnessActivities.json" activity_tz_cache_raw = cachedb.sporttracks_meta_cache.find_one( {"ExternalID": serviceRecord.ExternalID}) activity_tz_cache_raw = activity_tz_cache_raw if activity_tz_cache_raw else { "Activities": [] } activity_tz_cache = dict([(x["ActivityURI"], x["TZ"]) for x in activity_tz_cache_raw["Activities"] ]) while True: logger.debug("Req against " + pageUri) res = requests.get(pageUri, headers=headers) try: res = res.json() except ValueError: raise APIException( "Could not decode activity list response %s %s" % (res.status_code, res.text)) for act in res["items"]: activity = UploadedActivity() activity.ServiceData = {"ActivityURI": act["uri"]} if len(act["name"].strip()): activity.Name = act["name"] # Longstanding ST.mobi bug causes it to return negative partial-hour timezones as "-2:-30" instead of "-2:30" fixed_start_time = re.sub(r":-(\d\d)", r":\1", act["start_time"]) activity.StartTime = dateutil.parser.parse(fixed_start_time) if isinstance(activity.StartTime.tzinfo, tzutc): activity.TZ = pytz.utc # The dateutil tzutc doesn't have an _offset value. else: activity.TZ = pytz.FixedOffset( activity.StartTime.tzinfo.utcoffset( activity.StartTime).total_seconds() / 60 ) # Convert the dateutil lame timezones into pytz awesome timezones. activity.StartTime = activity.StartTime.replace( tzinfo=activity.TZ) activity.EndTime = activity.StartTime + timedelta( seconds=float(act["duration"])) activity.Stats.TimerTime = ActivityStatistic( ActivityStatisticUnit.Seconds, value=float(act["duration"] )) # OpenFit says this excludes paused times. # Sometimes activities get returned with a UTC timezone even when they are clearly not in UTC. if activity.TZ == pytz.utc: if act["uri"] in activity_tz_cache: activity.TZ = pytz.FixedOffset( activity_tz_cache[act["uri"]]) else: # So, we get the first location in the activity and calculate the TZ from that. try: firstLocation = self._downloadActivity( serviceRecord, activity, returnFirstLocation=True) except APIExcludeActivity: pass else: try: activity.CalculateTZ(firstLocation, recalculate=True) except: # We tried! pass else: activity.AdjustTZ() finally: activity_tz_cache[ act["uri"]] = activity.StartTime.utcoffset( ).total_seconds() / 60 logger.debug("Activity s/t " + str(activity.StartTime)) activity.Stats.Distance = ActivityStatistic( ActivityStatisticUnit.Meters, value=float(act["total_distance"])) types = [x.strip().lower() for x in act["type"].split(":")] types.reverse( ) # The incoming format is like "walking: hiking" and we want the most specific first activity.Type = None for type_key in types: if type_key in self._activityMappings: activity.Type = self._activityMappings[type_key] break if not activity.Type: exclusions.append( APIExcludeActivity("Unknown activity type %s" % act["type"], activity_id=act["uri"], user_exception=UserException( UserExceptionType.Other))) continue activity.CalculateUID() activities.append(activity) if not exhaustive or "next" not in res or not len(res["next"]): break else: pageUri = res["next"] logger.debug("Writing back meta cache") cachedb.sporttracks_meta_cache.update( {"ExternalID": serviceRecord.ExternalID}, { "ExternalID": serviceRecord.ExternalID, "Activities": [{ "ActivityURI": k, "TZ": v } for k, v in activity_tz_cache.items()] }, upsert=True) return activities, exclusions