def resolve_webhook(self, object_id: int): """ Resolves a webhook event from Strava by retrieving the activity data and checking it against the user's existing rules. If a match is found, sends the request to Strava to rename it. """ self.refresh() client = Client(access_token=self.access_token) activity = client.get_activity(object_id) # The Strava API doesn't give enough precision in its start latitude and # longitude values, so we have to call the raw stream of points to get what we # need. points = client.get_activity_streams( activity.id, types=["latlng"], resolution="low" ) activity_start = points["latlng"].data[0] current_app.logger.info( f"Webhook event received: Activity {object_id}, User {self.id}, Starting" f"point {activity_start}, Starting time {activity.start_date_local}" ) for rule in self.rules.all(): current_app.logger.info(f"Checking {rule} against activity {object_id}") if rule.check_rule(activity_start, activity.start_date_local): # if not current_app.config.TESTING: client.update_activity(object_id, name=rule.activity_name) current_app.logger.info( f"Activity {activity.id} renamed to {rule.activity_name} for {self}" ) break # No need to check any more activities
def give_kudos(activity_id): client = Client(access_token=os.environ.get('STRAVA_ACCESS_TOKEN')) activity = client.get_activity(activity_id) logger.info('activity = {}'.format(activity)) athlete = client.get_athlete(activity.athlete.id) logger.info('Giving kudos to {}'.format(athlete.username)) logger.info('Email: {}'.format(athlete.email)) send_email(athlete, activity)
def segment_ids_from_activities(client, max_activities=5): # get segment ids from most recent runs client = Client(access_token=session['access_token']) segment_ids = [] for a in client.get_activities(limit=max_activities): # this is jank but I can't pass include_all_efforts to get_activities activity = client.get_activity( a.id, include_all_efforts=True) # return all segment efforts for effort in activity.segment_efforts: segment_ids.append(effort.segment.id) session['segment_ids_unique'] = list(set(segment_ids)) session['uniques'] = len(session['segment_ids_unique']) return
def get_activities_from_strava(): last_activity = None if not os.path.exists(STRAVA_ACCESS_TOKEN_STRING_FNAME): print '* Obtain a request token ...' strava_client = Client() auth_url = strava_client.authorization_url(client_id='601', redirect_uri='http://127.0.0.1:5000/authorisation') print auth_url auth_token = strava_client.exchange_code_for_token(client_id='601', client_secret='600580e02b4814c75c93d3a60e15077147895776', code = '74cc257e6bc370d9da44cabc8852f3667ad95515') print auth_token # write the access token to file; next time we just read it from file if DEBUG: print 'Writing file', STRAVA_ACCESS_TOKEN_STRING_FNAME fobj = open(STRAVA_ACCESS_TOKEN_STRING_FNAME, 'w') fobj.write(auth_token) fobj.close() else: if DEBUG: print 'Reading file', STRAVA_ACCESS_TOKEN_STRING_FNAME fobj = open(STRAVA_ACCESS_TOKEN_STRING_FNAME) access_token_string = fobj.read() print access_token_string #access_token = oauth.OAuthToken.from_string(access_token_string) strava_client = Client(access_token=access_token_string) activities = strava_client.get_activities(limit=10) # for activity in activities: # details = strava_client.get_activity(activity_id=activity.id) # print details.name # print unithelper.kilometers(details.distance) # print details.start_date_local # print details.elapsed_time # print details.calories # print details.type # print "------" # fobj.close() for activity in activities: last_activity = activity return strava_client.get_activity(activity_id=activity.id)
def load_strava_data(user_id): user = User.objects.get(id=user_id) token = user.social_auth.get(provider='strava').tokens c = StravaClient(token) # fetch 200 activities activities = c.get_activities(limit=200) for track in activities: activity, created = Activity.objects.get_or_create( guID=track.id, user=user ) print track.id activity.provider = Activity.STRAVA_PROVIDER activity.location_city = track.location_city activity.location_country = track.location_country full_activity = c.get_activity(track.id) activity.polyline = full_activity.map.polyline activity.moving_time = full_activity.moving_time activity.start_date = full_activity.start_date activity.distance = float( unithelper.meters( track.distance ) ) activity.total_elevation_gain = float( unithelper.meters( track.total_elevation_gain ) ) activity.resource_state = track.resource_state activity.description = track.description if hasattr(track, 'start_latlng') and track.start_latlng is not None: activity.start_point = Point( track.start_latlng.lon, track.start_latlng.lat ) activity.save() if activity.polyline: activity.route = LineString(polyline_decode(activity.polyline)) activity.save()
def get_activity(event: dict, context=None): """ Retrieve the given activity, and its streams, and save the data as one json file to Cloud Storage. Args: event (dict): Dict in Pub/Sub format context: Not used """ # Initialise Google Cloud Storage. Doing this first because to save an # API call if there is a problem loading the bucket. storage_client = storage.Client() bucket = storage_client.bucket(os.getenv('STORAGE_BUCKET_NAME')) # Parse the Pub/Sub message data. data = json.loads(base64.b64decode(event['data']).decode('utf-8')) logging.info(data) # Check if a fresh token is required. if sm.expires_at < datetime.now().timestamp(): refresh_access_token() client = Client(access_token=sm.access_token) # Check we're logged in correctly. logger.info('Logged in as athlete %s', client.get_athlete()) # Download the activity data. activity = client.get_activity(data['object_id']) activity_dict = activity.to_dict() logger.debug('Activity %s: %s', activity.id, activity_dict) # Download the streams data. streams = client.get_activity_streams(activity.id, STREAM_TYPES) logger.debug(streams) # Append the streams data to the activity data. This is the only # manipulation in this workflow; everything else should be manipulated # when you read this file from Cloud Storage. activity_dict['streams'] = {k: v.to_dict() for k, v in streams.items()} logger.debug(activity_dict) # Save to Cloud Storage. blob = bucket.blob(f'{activity.id}.json') blob.upload_from_string(json.dumps(activity_dict), content_type="application/json") return 0
def getCourseInfo(): STORED_ACCESS_TOKEN = "903910abaf69b186957a22c1227d6f19237cd233" client = Client(access_token=STORED_ACCESS_TOKEN) athlete_data = client.get_athlete() activity_id = 887327831 activity = client.get_activity(887327831) types = ['time', 'altitude', 'velocity_smooth', 'distance', 'grade_smooth', 'watts','latlng'] streams = client.get_activity_streams(activity_id, types = types, resolution='medium') # for i in range(0, len(streams['time'].data)): # print "{} {}".format(streams['distance'].data[i], streams['watts'].data[i]) x = streams['distance'].data y = streams['altitude'].data inclin = streams['grade_smooth'].data latlng = streams['latlng'].data return {'x':x,'y': y, 'inclin':inclin, 'latlng': latlng}
def sync_activity(request): activity = get_item_from_request(request) client = Client(access_token=get_access_token(request)) strava = client.get_activity(activity.strava_id) # Sync description this is not included in the activity overview. if strava.description: activity.description = strava.description # Activities can have many streams, you can request desired stream types # 'time', 'latlng', 'altitude', 'heartrate', 'temp' types = [ 'time', 'latlng', 'distance', 'altitude', 'velocity_smooth', 'heartrate', 'cadence', 'watts', 'temp', 'moving', 'grade_smooth' ] streams = client.get_activity_streams(activity.strava_id, types=types) if "time" in streams: activity.time_stream = streams["time"].data if "latlng" in streams: activity.latlng_stream = streams["latlng"].data if "distance" in streams: activity.distance_stream = streams["distance"].data if "altitude" in streams: activity.altitude_stream = streams["altitude"].data if "velocity_smooth" in streams: activity.velocity_smooth_stream = streams["velocity_smooth"].data if "heartrate" in streams: activity.heartrate_stream = streams["heartrate"].data if "cadence" in streams: activity.cadence_stream = streams["cadence"].data if "watts" in streams: activity.watts_stream = streams["watts"].data if "temp" in streams: activity.temp_stream = streams["temp"].data if "moving" in streams: activity.moving_stream = streams["moving"].data if "grade_smooth" in streams: activity.grade_smoth_stream = streams["grade_smooth"].data return activity
def load_strava_data(user_id): user = User.objects.get(id=user_id) token = user.social_auth.get(provider='strava').tokens c = StravaClient(token) # fetch 200 activities activities = c.get_activities(limit=200) for track in activities: activity, created = Activity.objects.get_or_create(guID=track.id, user=user) print track.id activity.provider = Activity.STRAVA_PROVIDER activity.location_city = track.location_city activity.location_country = track.location_country full_activity = c.get_activity(track.id) activity.polyline = full_activity.map.polyline activity.moving_time = full_activity.moving_time activity.start_date = full_activity.start_date activity.distance = float(unithelper.meters(track.distance)) activity.total_elevation_gain = float( unithelper.meters(track.total_elevation_gain)) activity.resource_state = track.resource_state activity.description = track.description if hasattr(track, 'start_latlng') and track.start_latlng is not None: activity.start_point = Point(track.start_latlng.lon, track.start_latlng.lat) activity.save() if activity.polyline: activity.route = LineString(polyline_decode(activity.polyline)) activity.save()
def getactivity(activity_id=0, client=None): if client == None: token = gettoken() client = Client(access_token=token) if activity_id == 0: activities = client.get_activities(limit=1) for i in activities: activity = i else: try: activity = client.get_activity(activity_id) except: print('Activity not found') return int(0), client athlete = activity.athlete if not athlete.is_authenticated_athlete(): print('Activity does not belong to user') return int(1), client return activity, client
def map(request, athlete_id, activity_id): # athlete_id = athlete_id #421122 #750228 # activity_id = activity_id #577320490 #476912675 path = "../../../../../data/" + format(athlete_id) + "_" + format(activity_id) + ".txt" file_to_write_to = open(path, "w") # print(file_to_write_to.path) writer = csv.writer(file_to_write_to, delimiter=',', quotechar='', quoting=csv.QUOTE_NONE) #write header row for text file activity_tuple = "AthleteID", "ActivityID", "StartTime", "TotalElapsedTime", "TotalDistanceMeters", "MaxSpeedMPH", "MeasuredTime", "Latitude", "Longitude", "AltitudeMeters", "DistanceMeters", "current_speedMPH", "CurrentGrade" writer.writerow(activity_tuple) # By now the athlete should exist current_athlete = Athlete.objects.get(id_strava = athlete_id) # Use the access_token ### "58298d33c3a183c12673691a1ae53d261b08c3a4" client = Client(access_token=current_athlete.access_token) #activity id strava_ride = client.get_activity(activity_id) # values we are using in calculations and sending to the template max_speed = format(float(strava_ride.max_speed * 2.23693629), '.9f') average_speed = format(float(strava_ride.average_speed * 2.23693629), '.9f') ride_name = strava_ride.name # Streams stream_types = "time","distance","latlng","altitude","grade_smooth","velocity_smooth" streams = client.get_activity_streams(activity_id, types=stream_types) stream_time = streams["time"].data stream_distance = streams["distance"].data stream_lat_lng = streams["latlng"].data stream_altitude = streams["altitude"].data stream_grade = streams["grade_smooth"].data stream_velocity = streams["velocity_smooth"].data stream_tuple = zip(stream_time, stream_distance, stream_lat_lng, stream_altitude, stream_grade, stream_velocity) # combined_array is to collect all the values to do some calculations later. combined_array = [] # combined_string is a string version of the array to send to the template. combined_string = "" # Getting info from the streams and combining it all into a CSV format. for (tTime,tDistance,tLatLng,tAltitude,tGrade,tVelocity) in stream_tuple: current_time = strava_ride.start_date_local + timedelta(seconds=tTime) # current_speed = format(float(tVelocity * 2.23693629), '.9f') current_speed = tVelocity * 2.23693629 activity_tuple = athlete_id, activity_id, strava_ride.start_date_local, strava_ride.elapsed_time, \ float(strava_ride.distance), max_speed, current_time, tLatLng[0], tLatLng[1], \ tAltitude, tDistance, current_speed, tGrade writer.writerow(activity_tuple) temp_stuff = [] temp_stuff.append(format(current_time)) temp_stuff.append(format(tLatLng[0])) temp_stuff.append(format(tLatLng[1])) temp_stuff.append(format(current_speed)) combined_array.append(temp_stuff) file_to_write_to.close() # make special Shred Analytics average speeds that remove all 0 values. sa_average_speed = 0.0 sa_avg_index = 0 for i in combined_array: # i[3] is speed if float(i[3]) > 0.5: sa_average_speed = sa_average_speed + float(i[3]) sa_avg_index = sa_avg_index + 1 # Make a string version of the arracy to send to Javascript. combined_string += ','.join(i) + "@" # the important calculation sa_average_speed = sa_average_speed / sa_avg_index context = {'sa_average_speed': sa_average_speed, 'max_speed': max_speed, 'average_speed': average_speed, 'ride_name': ride_name, 'athlete_id': athlete_id, 'activity_id': activity_id, 'start_lat': combined_array[3][1], 'start_lon': combined_array[3][2], 'file_string': combined_string} template = loader.get_template('shred/map.html') return HttpResponse(template.render(context))
def compare (request): athlete_id = request.GET.get("athlete_id") # how do I get what is in the URL? # I want to send an arbitrary array and get that in the URL # code = request.GET.get("code") <-- works for one item # what if the URL is just /compare/runs=123,456,789 # runs = request.GET.get("runs") # Then split that string into an array... # use the token URL pattern as a template # By now the athlete should exist current_athlete = Athlete.objects.get(id_strava = athlete_id) # Use the access_token ### "58298d33c3a183c12673691a1ae53d261b08c3a4" client = Client(access_token=current_athlete.access_token) # hardcoded sleighride activities. activities = [547540459, 547545741, 547550929, 559626235] max_speed = 0.0 ride_names = [] # combined_array is to collect all the values to do some calculations later. combined_array = [] all_speeds = [] for ac in activities: #activity id strava_ride = client.get_activity(ac) # Get and update the highest maxspeed across rides. t_max_speed = float(strava_ride.max_speed * 2.23693629) if t_max_speed > max_speed: max_speed = t_max_speed # Add the activity name to this array ride_names.append(strava_ride.name) # Streams stream_types = "time","distance","latlng","altitude","grade_smooth","velocity_smooth" streams = client.get_activity_streams(strava_ride.id, types=stream_types) stream_time = streams["time"].data stream_distance = streams["distance"].data stream_lat_lng = streams["latlng"].data stream_altitude = streams["altitude"].data stream_grade = streams["grade_smooth"].data stream_velocity = streams["velocity_smooth"].data stream_tuple = zip(stream_time, stream_distance, stream_lat_lng, stream_altitude, stream_grade, stream_velocity) # Getting info from the streams and combining it all into a CSV format. for (tTime,tDistance,tLatLng,tAltitude,tGrade,tVelocity) in stream_tuple: current_time = strava_ride.start_date_local + timedelta(seconds=tTime) # current_speed = format(float(tVelocity * 2.23693629), '.9f') current_speed = tVelocity * 2.23693629 if current_speed > 0.5: all_speeds.append(current_speed) temp_stuff = [] temp_stuff.append(format(current_time)) temp_stuff.append(format(tLatLng[0])) temp_stuff.append(format(tLatLng[1])) temp_stuff.append(format(current_speed)) combined_array.append(temp_stuff) # End inner FOR # insert splitter between runs combined_array.append(["$$$"]) # END outer FOR # make special Shred Analytics average speeds that remove all 0 values. sa_average_speed = mean(all_speeds) # combined_string is a string version of the array to send to the template. combined_string = "" # Make a string version of the arracy to send to Javascript. for i in combined_array: combined_string += ','.join(i) + "@" context = {'sa_average_speed': sa_average_speed, 'max_speed': max_speed, 'ride_names': ride_names, 'start_lat': combined_array[3][1], 'start_lon': combined_array[3][2], 'file_string': combined_string} template = loader.get_template('shred/compare.html') return HttpResponse(template.render(context))
class artbot(PyBot): def bot_init(self): """ Custom initialization. Specify any configuration options you want to override, as in particular your OAuth credentials. """ ############################# # # # Twitter OAuth Credentials # # # # FILL THESE IN! # # # ############################# self.config['api_key'] = 'your_api_key' self.config['api_secret'] = 'your_api_secret' self.config['access_key'] = 'your_access_key' self.config['access_secret'] = 'your_access_secret' ############################# # # # Other config options # # # # Fill these in if you want # # or otherwise need to. # # # ############################# self.config['strava_access_token'] = 'your_strava_token' self.config['update_day'] = 0 self.config['update_hour'] = 13 self.config['update_minute'] = 13 self.config['tweet_interval'] = self._compute_interval # Create the Strava client. self.client = Client(access_token=self.config['strava_access_token']) def on_tweet(self): # First, pull in the stats from Strava. current = datetime.datetime.now() last_week = current + datetime.timedelta(weeks=-1) after = datetime.datetime(last_week.year, last_week.month, last_week.day) activities = self.client.get_activities(after=after) # Second, filter by activity type and time frame. lf = [a for a in activities if a.start_date_local.day != current.day] num_activities = len(lf) l = [a.id for a in lf if a.type == 'Run'] # Third, tabulate up the stats for mileage and calories. mileage = 0.0 calories = 0.0 for activity_id in l: activity = self.client.get_activity(activity_id) distance = unithelper.miles(activity.distance) mileage += round(distance.num, 2) # Rounds to 2 sig figs. calories += activity.calories calories = int(calories) # Finally, use the stats to craft a tweet. This can be any format # you want, but I'll use the example one from the start of the post. tweet = "My training last week: {:d} workouts for {:.2f} miles and {:d} calories burned.".format( num_activities, mileage, calories) self.update_status(tweet) def _compute_interval(self): """ This is a little more sophisticated than the method in the original blog post. This is to provide for *exactly* specifying when we want a post to be made, down to the minute. """ now = datetime.datetime.now() target = datetime.datetime(year=now.year, month=now.month, day=now.day, hour=self.config['update_hour'], minute=self.config['update_minute']) days_ahead = self.config['update_day'] - now.weekday() if (days_ahead < 0) or (days_ahead == 0 and (target - now).days < 0): days_ahead += 7 td = target + datetime.timedelta(days=days_ahead) interval = int((td - datetime.datetime.now()).total_seconds()) return interval
def massive_test(access_token, athlete_id): client = Client(access_token=access_token) mysegments = {} # all segments a user has ridden # get athlete activities athlete_from_db = Athlete.objects.get(strava_id=athlete_id) activities = client.get_activities(limit=5, before=athlete_from_db.oldest_activity_date) # API call # per activity, get segment efforts for activity in activities: if activity.type not in ['Ride', 'ride']: continue try: # if activity already exists in db, skip it Activity.objects.get(strava_id=activity.id) continue except Activity.DoesNotExist: new_activity = Activity() new_activity.strava_id = activity.id new_activity.start_lat = activity.start_latitude new_activity.start_long = activity.start_longitude new_activity.start_date = activity.start_date new_activity.save() # update newest / oldest activity dates if athlete_from_db.newest_activity_date is None: athlete_from_db.newest_activity_date = activity.start_date athlete_from_db.oldest_activity_date = activity.start_date else: if activity.start_date > athlete_from_db.newest_activity_date: athlete_from_db.newest_activity_date = activity.start_date elif activity.start_date < athlete_from_db.oldest_activity_date: athlete_from_db.oldest_activity_date = activity.start_date athlete_from_db.save() segment_efforts = client.get_activity(activity.id).segment_efforts # API call # per segment effort for segment in segment_efforts: mysegments[segment.segment.id] = segment.segment # save to db # check if segment leaderboard contains any friends for key, segment in mysegments.iteritems(): leaderboard = client.get_segment_leaderboard(key, following=True).entries # API call (possibly lots, depends on number of segments) # get friend with time < athlete time for i, entry in enumerate(leaderboard): if entry.athlete_id == athlete_id: me = entry if i == 0: # I'm already the winner! break j = 1 while j <= i and leaderboard[i - j].elapsed_time == me.elapsed_time: # check for ties, compare each entry from i to zero (possibly) j += 1 if leaderboard[i - j].elapsed_time == me.elapsed_time: # if they're still tied at the end of the loop, I don't want to see it break other = leaderboard[i - j] try: new_segment = ChallengedSegment.objects.get(my_id=athlete_id, segment_id=segment.id) except ChallengedSegment.DoesNotExist: new_segment = ChallengedSegment() new_segment.my_id = athlete_id new_segment.their_id = other.athlete_id new_segment.their_name = other.athlete_name new_segment.my_pr = me.activity_id new_segment.their_pr = other.activity_id new_segment.my_time = str(me.elapsed_time) new_segment.their_time = str(other.elapsed_time) new_segment.difference = str(me.elapsed_time - other.elapsed_time) new_segment.segment_id = segment.id new_segment.segment_name = segment.name new_segment.segment_distance = str(unithelper.miles(segment.distance)) new_segment.save() break # we already found my entry, why keep looking through the list?
def get_activities(self, token): client = Client(token) user = client.get_athlete() # Update StavaUser lastUpdate = datetime.now() strUser = StravaUser.objects.filter(uid=user.id) print('strUser='******'lastUpdate=', lastUpdate) #d = datetime(2018, 5, 5) date_1_day_ago = lastUpdate - timedelta(days=1) activities = client.get_activities(after=date_1_day_ago, limit=50) #activities = client.get_activities(after=d,limit=15) act = None nbItem = 0 nbAct = 0 for activity in activities: nbAct += 1 print('NbAct=', nbAct) initNewActivities = False newUser = False segment = 15 begin = 0 end = begin + segment currentList = Activity.objects.filter( uid=client.get_athlete().id).order_by('-strTime')[begin:end] if not currentList.exists(): newUser = True while (currentList.exists() or newUser): print('currentList=', currentList) print('begin=', begin) print('end=', end) actList = [] for actItem in currentList: #print (actItem) serializer = ActivityItemSerializer(actItem) #print ('serializer.data: ',serializer.data) actList.append(serializer.data) data = {'nbAct': nbAct, 'currentAct': nbItem, 'activities': actList} sendMessage('actList', data, strUser[0].channel_name) actList.clear() if not initNewActivities: for activity in activities: StravaUser.objects.filter(uid=user.id).update( currentActIndex=nbItem, nbActToRetreive=nbAct) act = client.get_activity(activity.id) strDate = act.start_date.strftime("%Y-%m-%d %H:%M:%S") #print ('uid=',user.id) #print ('start_date=',strDate) #print ('act.distance=',act.distance) #print ('act.type=',act.type) dist = re.sub(' .*$', '', str(act.distance)) #print ('dist=',dist) strDistance = format(float(dist) / 1000, '.2f') #print ('distance=',strDistance) #print ('stravaId=',act.upload_id) print('name=', act.name) #print ('time=',act.elapsed_time) #print ('splits_metric=',act.splits_metric) if not Activity.objects.filter(stravaId=activity.id).exists(): workout = Workout.objects.create(name=act.name) print('wid=', workout.id) print('stravaId=', activity.id) activity.wid = workout.id stravaAct = Activity(strTime=strDate,strDist=strDistance,distance=act.distance,\ time=act.elapsed_time,label=act.name,stravaId=activity.id,wid=workout.id,workout_id=workout.id,\ resolution=strUser[0].resolution,uid=user.id,type=act.type,state="c",progress=0) stravaAct.save() Workout.objects.filter(id=workout.id).update( actId=stravaAct.id) split = Split.objects.filter(workout__id=workout.id) print('Split first element=', split.count()) if not split.count(): if split is not None: objs = [ Split(split_index=i, split_distance=split.distance, split_time=split.elapsed_time, workout=workout) for i, split in enumerate(act.splits_metric) ] split = Split.objects.bulk_create(objs) # Send result list to client for actItem in Activity.objects.filter( stravaId=activity.id): #print (actItem) serializer = ActivityItemSerializer(actItem) # pre-process Json for client response to get workout self.result = processJsonDataBackup.delay( token, workout.id, json.dumps(serializer.data)) #print ('serializer.data: ',serializer.data) actList.insert(0, serializer.data) else: Activity.objects.filter(stravaId=activity.id).update( strTime=strDate, strDist=strDistance, resolution=strUser[0].resolution) nbItem += 1 data = { 'nbAct': nbAct, 'currentAct': nbItem, 'activities': actList } sendMessage('actList', data, strUser[0].channel_name) initNewActivities = True begin = end end = begin + segment currentList = Activity.objects.filter( uid=client.get_athlete().id).order_by('-strTime')[begin:end] newUser = False if act is not None: print('Update user last_date') strUser.update(lastUpdate=datetime.now()) return {'current': nbItem, 'total': nbAct}
def main(event, context): """Triggered from a message on a Cloud Pub/Sub topic. Args: event (dict): Event payload. context (google.cloud.functions.Context): Metadata for the event. """ STRAVA_CLIENT_ID = os.environ.get('strava_client_id') STRAVA_CLIENT_SECRET = os.environ.get('strava_client_secret') pubsub_message = base64.b64decode(event['data']).decode('utf-8') pubsub_dict = json.loads(pubsub_message.replace("'", '"')) print(pubsub_dict) datastore_client = datastore.Client(project='strava-int') bq_client = bigquery.Client(project='strava-int') strava_client = Client() # event notification from strava aspect_type = pubsub_dict['aspect_type'] object_id = pubsub_dict['object_id'] owner_id = pubsub_dict['owner_id'] object_type = pubsub_dict['object_type'] event_time = pubsub_dict['event_time'] event_datetime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(event_time)) if object_type == 'activity': now = time.time() if aspect_type == 'delete': athlete_activity_dict = [{ 'activity_hub_seq': hashlib.md5(str(object_id).encode()).hexdigest(), 'sat_load_date': event_datetime, 'delete_ind': True }] # athlete_activity_obj = AthleteActivity(athlete_activity_dict) # sat_athlete_activity = athlete_activity_obj.satellite() sat_table_ref = bq_client.dataset('strava_datavault').table( 'activity_sat') bq_client.load_table_from_json(athlete_activity_dict, sat_table_ref) if aspect_type != 'delete': # stored athlete from datastore athlete_key = datastore_client.key('Athlete', owner_id) athlete = datastore_client.get(athlete_key) if now > athlete['expires_at']: access_token = strava_client.refresh_access_token( client_id=STRAVA_CLIENT_ID, client_secret=STRAVA_CLIENT_SECRET, refresh_token=athlete['refresh_token']) athlete.update(access_token) datastore_client.put(athlete) # create new client for authenticated athlete athlete_client = Client(access_token=athlete['access_token']) activity = athlete_client.get_activity(object_id) activity_dict = activity.to_dict() supplement = { 'athlete_id': owner_id, 'activity_id': object_id, 'load_date': event_datetime } activity_dict.update(supplement) # GCS Storage upload_blob(activity_dict, owner_id, object_id, event_time, aspect_type) converted_units = { 'distance_mi': unithelper.mile(getattr(activity, 'distance', None)).get_num(), 'average_speed_mi': unithelper.mph(getattr(activity, 'average_speed', None)).get_num(), 'max_speed_mi': unithelper.mph(getattr(activity, 'max_speed', None)).get_num(), 'elapsed_time_s': int( unithelper.timedelta_to_seconds( getattr(activity, 'elapsed_time', None))), 'moving_time_s': int( unithelper.timedelta_to_seconds( getattr(activity, 'moving_time', None))) } activity_dict.update(converted_units) athlete_activity_obj = AthleteActivity(activity_dict) sat_athlete_activity = athlete_activity_obj.satellite() print(sat_athlete_activity) # BQ insert sat_table_ref = bq_client.dataset('strava_datavault').table( 'activity_sat') bq_client.load_table_from_json(sat_athlete_activity, sat_table_ref) if aspect_type == 'create': link_athlete_activity = athlete_activity_obj.link() link_table_ref = bq_client.dataset('strava_datavault').table( 'athlete_activity_link') bq_client.load_table_from_json(link_athlete_activity, link_table_ref) hub_activity = athlete_activity_obj.hub() hub_table_ref = bq_client.dataset('strava_datavault').table( 'activity_hub') bq_client.load_table_from_json(hub_activity, hub_table_ref)
title = nametag and nametag.text desc = desctag and desctag.text elif ext.lower()=='.tcx': x = etree.parse(uf) notestag = x.find("{*}Activities/{*}Activity/{*}Notes") if notestag is not None: title, desc = (notestag.text.split('\n',1)+[None])[:2] else: title = args.title desc = args.description # upload activity try: cf.seek(0, 0) upstat = client.upload_activity(cf, ext[1:] + '.gz', title, desc, private=args.private, activity_type=args.activity_type) activity = upstat.wait() duplicate = False except exc.ActivityUploadFailed as e: words = e.args[0].split() if words[-4:-1]==['duplicate','of','activity']: activity = client.get_activity(words[-1]) duplicate = True else: raise # show results uri = "http://strava.com/activities/{:d}".format(activity.id) print(" {}{}".format(uri, " (duplicate)" if duplicate else ''), file=stderr) if not args.no_popup: webbrowser.open_new_tab(uri)
def main(args=None): allowed_exts = { '.tcx': lambda v: '<TrainingCenterDatabase' in v[:200], '.gpx': lambda v: '<gpx' in v[:200], '.fit': lambda v: v[8:12] == '.FIT' } p = argparse.ArgumentParser( description='''Uploads activities to Strava.''') p.add_argument( 'activities', nargs='*', type=argparse.FileType("rb"), default=(stdin, ), help="Activity files to upload (plain or gzipped {})".format( ', '.join(allowed_exts))) p.add_argument('-P', '--no-popup', action='store_true', help="Don't browse to activities after upload.") p.add_argument( '-E', '--env', help= 'Look for ACCESS_TOKEN in environment variable rather than ~/.stravacli' ) g = p.add_argument_group('Activity file details') g.add_argument('-p', '--private', action='store_true', help='Make activities private') g.add_argument( '-t', '--type', choices=allowed_exts, default=None, help= 'Force files to be interpreted as being of given type (default is to autodetect based on name, or contents for stdin)' ) g.add_argument( '-x', '--xml-desc', action='store_true', help="Parse name/description fields from GPX and TCX files.") g.add_argument('-T', '--title', help='Activity title') g.add_argument('-D', '--desc', dest='description', help='Activity description') g.add_argument( '-A', '--activity-type', default=None, help='''Type of activity. If not specified, the default value is taken from user profile. Supported values: ride, run, swim, workout, hike, walk, nordicski, alpineski, backcountryski, iceskate, inlineskate, kitesurf, rollerski, windsurf, workout, snowboard, snowshoe''' ) args = p.parse_args(args) if args.xml_desc: if args.title: p.error( 'argument -T/--title not allowed with argument -x/--xml-desc') if args.description: p.error( 'argument -D/--desc not allowed with argument -x/--xml-desc') ##### # Authorize Strava cid = 3163 # CLIENT_ID if args.env: cat = os.environ.get('ACCESS_TOKEN') else: cp = ConfigParser.ConfigParser() cp.read(os.path.expanduser('~/.stravacli')) cat = None if cp.has_section('API'): cat = cp.get('API', 'ACCESS_TOKEN' ) if 'access_token' in cp.options('API') else None while True: client = Client(cat) try: athlete = client.get_athlete() except requests.exceptions.ConnectionError: p.error("Could not connect to Strava API") except Exception as e: print("NOT AUTHORIZED", file=stderr) print( "Need Strava API access token. Launching web browser to obtain one.", file=stderr) client = Client() authorize_url = client.authorization_url( client_id=cid, redirect_uri='http://stravacli-dlenski.rhcloud.com/auth', scope='view_private,write') webbrowser.open_new_tab(authorize_url) client.access_token = cat = raw_input("Enter access token: ") else: if not cp.has_section('API'): cp.add_section('API') if not 'ACCESS_TOKEN' in cp.options('API') or cp.get( 'API', 'ACCESS_TOKEN', None) != cat: cp.set('API', 'ACCESS_TOKEN', cat) cp.write(open(os.path.expanduser('~/.stravacli'), "w")) break print(u"Authorized to access account of {} {} (id {:d}).".format( athlete.firstname, athlete.lastname, athlete.id)) ##### for ii, f in enumerate(args.activities): if f is stdin: fn = 'stdin' contents = f.read() f = StringIO(contents) if args.type is None: # autodetect gzip and extension based on content if contents.startswith('\x1f\x8b'): gz, cf, uf = '.gz', f, gzip.GzipFile(fileobj=f, mode='rb') contents = uf.read() else: gz, uf, cf = '', f, NamedTemporaryFile(suffix='.gz') gzip.GzipFile(fileobj=cf, mode='w+b').writelines(f) for ext, checker in allowed_exts.items(): if checker(contents): print( u"Uploading {} activity from stdin...".format(ext + gz)) break else: p.error("Could not determine file type of stdin") else: base, ext = 'activity', args.type else: base, ext = os.path.splitext( f.name if args.type is None else 'activity.' + args.type) # autodetect based on extensions if ext.lower() == '.gz': base, ext = os.path.splitext(base) # un-gzip it in order to parse it gz, cf, uf = '.gz', f, None if args.no_parse else gzip.GzipFile( fileobj=f, mode='rb') else: gz, uf, cf = '', f, NamedTemporaryFile(suffix='.gz') gzip.GzipFile(fileobj=cf, mode='w+b').writelines(f) if ext.lower() not in allowed_exts: p.error( "Don't know how to handle extension {} (allowed are {}).". format(ext, ', '.join(allowed_exts))) print(u"Uploading {} activity from {}...".format(ext + gz, f.name)) # try to parse activity name, description from file if requested if args.xml_desc: uf.seek(0, 0) if ext.lower() == '.gpx': x = etree.parse(uf) nametag, desctag = x.find("{*}name"), x.find("{*}desc") title = nametag and nametag.text desc = desctag and desctag.text elif ext.lower() == '.tcx': x = etree.parse(uf) notestag = x.find("{*}Activities/{*}Activity/{*}Notes") if notestag is not None: title, desc = (notestag.text.split('\n', 1) + [None])[:2] else: title = args.title desc = args.description # upload activity try: cf.seek(0, 0) upstat = client.upload_activity(cf, ext[1:] + '.gz', title, desc, private=args.private, activity_type=args.activity_type) activity = upstat.wait() duplicate = False except exc.ActivityUploadFailed as e: words = e.args[0].split() if words[-4:-1] == ['duplicate', 'of', 'activity']: activity = client.get_activity(words[-1]) duplicate = True else: raise # show results uri = "http://strava.com/activities/{:d}".format(activity.id) print(u" {}{}".format(uri, " (duplicate)" if duplicate else ''), file=stderr) if not args.no_popup: webbrowser.open_new_tab(uri)
me["bikes"] = bikes # Fetch activities this year my_activities = client.get_activities(after=datetime(2015, 1, 1)) #(limit=5) act = [] for a in my_activities: act.append(a) # Fetch every activity % make collections cycling_collection = [] swimming_collection = [] for i in range(len(act)): print "fetching activity " + str(i) id = act[i].id activity = client.get_activity(id) # Activity collections if activity.type == "Ride": my_activity = {} my_activity["type"] = "cycling" my_activity["date"] = str(activity.start_date_local) my_activity["time"] = str(activity.moving_time) my_activity["distance"] = str(activity.distance) my_activity["avg_speed"] = str(activity.average_speed) my_activity["energy"] = str(activity.kilojoules) cycling_collection.append(my_activity) elif activity.type == "Swim": my_activity = {} my_activity["type"] = "swimming" my_activity["date"] = str(activity.start_date_local)
def strava_upload(): """ upload to strava, borrowed from https://github.com/dlenski/stravacli """ allowed_exts = { '.tcx': lambda v: '<TrainingCenterDatabase' in v[:200], '.gpx': lambda v: '<gpx' in v[:200], '.fit': lambda v: v[8:12] == '.FIT' } par = argparse.ArgumentParser(description='Uploads activities to Strava.') par.add_argument( 'activities', nargs='*', type=argparse.FileType("rb"), default=(stdin, ), help="Activity files to upload (plain or gzipped {})".format(', '.join(allowed_exts))) par.add_argument( '-P', '--no-popup', action='store_true', help="Don't browse to activities after upload.") par.add_argument( '-E', '--env', help='Look for ACCESS_TOKEN in environment variable ' 'rather than ~/.stravacli') grp = par.add_argument_group('Activity file details') grp.add_argument('-p', '--private', action='store_true', help='Make activities private') grp.add_argument( '-t', '--type', choices=allowed_exts, default=None, help='Force files to be interpreted as being of given ' 'type (default is to autodetect based on name, or ' 'contents for stdin)') grp.add_argument( '-x', '--xml-desc', action='store_true', help='Parse name/description fields from GPX and TCX ' 'files.') grp.add_argument('-T', '--title', help='Activity title') grp.add_argument('-D', '--desc', dest='description', help='Activity description') grp.add_argument( '-A', '--activity-type', default=None, help='Type of activity. If not specified, the default ' 'value is taken from user profile. ' 'Supported values: \n\t ride, run, swim, workout, ' 'hike, walk, nordicski, alpineski, backcountryski, ' 'iceskate, inlineskate, kitesurf, rollerski, ' 'windsurf, workout, snowboard, snowshoe') args = par.parse_args() if args.xml_desc: if args.title: print('argument -T/--title not allowed with argument ' '-x/--xml-desc', file=stderr) if args.description: print('argument -D/--desc not allowed with argument ' '-x/--xml-desc', file=stderr) # Authorize Strava cp_ = ConfigParser() cp_.read(os.path.expanduser('~/.stravacli')) cat = None if cp_.has_section('API'): if 'access_token' in cp_.options('API'): cat = cp_.get('API', 'ACCESS_TOKEN') cs = cp_.get('API', 'CLIENT_SECRET') cat = cp_.get('API', 'ACCESS_TOKEN') while True: client = Client(cat) try: athlete = client.get_athlete() except requests.exceptions.ConnectionError: print("Could not connect to Strava API", file=stderr) except Exception as e: print("NOT AUTHORIZED %s" % e, file=stderr) print( "Need Strava API access token. Launching web browser to " "obtain one.", file=stderr) client = Client() webserver = QueryGrabber(response='<title>Strava auth code received!</title>This window can be closed.') _scope = 'view_private,write' authorize_url = client.authorization_url(client_id=cid, redirect_uri=webserver.root_uri(), scope=_scope) webbrowser.open_new_tab(authorize_url) webserver.handle_request() client.access_token = cat = client.exchange_code_for_token(client_id=cid,client_secret=cs,code=webserver.received['code']) cp_.add_section('API') cp_.set('API','CLIENT_ID', cid) cp_.set('API','CLIENT_SECRET', cs) cp_.set('API','ACCESS_TOKEN', cat) cp_.write(open(os.path.expanduser('~/.stravacli'),"w")) else: if not cp_.has_section('API'): cp_.add_section('API') if 'ACCESS_TOKEN' not in cp_.options('API') or cp_.get('API', 'ACCESS_TOKEN', None) != cat: cp_.set('API', 'ACCESS_TOKEN', cat) cp_.write(open(os.path.expanduser('~/.stravacli'), "w")) break print("Authorized to access account of {} {} (id {:d}).".format(athlete.firstname, athlete.lastname, athlete.id)) for act in args.activities: if act is stdin: contents = act.read() act = StringIO(contents) if args.type is None: # autodetect gzip and extension based on content if contents.startswith('\x1f\x8b'): gz_, cf_, uf_ = '.gz', act, gzip.GzipFile(fileobj=act, mode='rb') contents = uf_.read() else: gz_, uf_, cf_ = '', act, NamedTemporaryFile(suffix='.gz') gzip.GzipFile(fileobj=cf_, mode='w+b').writelines(act) for ext, checker in allowed_exts.items(): if checker(contents): print("Uploading {} activity from stdin...".format(ext + gz_)) break else: print("Could not determine file type of stdin", file=stderr) else: base, ext = 'activity', args.type else: base, ext = os.path.splitext(act.name if args.type is None else 'activity.' + args.type) # autodetect based on extensions if ext.lower() == '.gz': base, ext = os.path.splitext(base) # un-gzip it in order to parse it gz_, cf_, uf_ = '.gz', act, None if args.no_parse else \ gzip.GzipFile(fileobj=act, mode='rb') else: gz_, uf_, cf_ = '', act, NamedTemporaryFile(suffix='.gz') gzip.GzipFile(fileobj=cf_, mode='w+b').writelines(act) if ext.lower() not in allowed_exts: print( "Don't know how to handle extension " "{} (allowed are {}).".format(ext, ', '.join(allowed_exts)), file=stderr) print("Uploading {} activity from {}...".format(ext + gz_, act.name)) # try to parse activity name, description from file if requested if args.xml_desc: uf_.seek(0, 0) if ext.lower() == '.gpx': x = etree.parse(uf_) nametag, desctag = x.find("{*}name"), x.find("{*}desc") title = nametag and nametag.text desc = desctag and desctag.text elif ext.lower() == '.tcx': x = etree.parse(uf_) notestag = x.find("{*}Activities/{*}Activity/{*}Notes") if notestag is not None: title, desc = (notestag.text.split('\n', 1) + [None])[:2] else: title = args.title desc = args.description # upload activity try: cf_.seek(0, 0) upstat = client.upload_activity( cf_, ext[1:] + '.gz', title, desc, private=args.private, activity_type=args.activity_type) activity = upstat.wait() duplicate = False except exc.ActivityUploadFailed as e: words = e.args[0].split() if words[-4:-1] == ['duplicate', 'of', 'activity']: activity = client.get_activity(words[-1]) duplicate = True else: raise # show results uri = "http://strava.com/activities/{:d}".format(activity.id) print(" {}{}".format(uri, " (duplicate)" if duplicate else ''), file=stderr) if not args.no_popup: webbrowser.open_new_tab(uri)
class artbot(PyBot): def bot_init(self): """ Custom initialization. Specify any configuration options you want to override, as in particular your OAuth credentials. """ ############################# # # # Twitter OAuth Credentials # # # # FILL THESE IN! # # # ############################# self.config['api_key'] = 'your_api_key' self.config['api_secret'] = 'your_api_secret' self.config['access_key'] = 'your_access_key' self.config['access_secret'] = 'your_access_secret' ############################# # # # Other config options # # # # Fill these in if you want # # or otherwise need to. # # # ############################# self.config['strava_access_token'] = 'your_strava_token' self.config['update_day'] = 0 self.config['update_hour'] = 13 self.config['update_minute'] = 13 self.config['tweet_interval'] = self._compute_interval # Create the Strava client. self.client = Client(access_token = self.config['strava_access_token']) def on_tweet(self): # First, pull in the stats from Strava. current = datetime.datetime.now() last_week = current + datetime.timedelta(weeks = -1) after = datetime.datetime(last_week.year, last_week.month, last_week.day) activities = self.client.get_activities(after = after) # Second, filter by activity type and time frame. lf = [a for a in activities if a.start_date_local.day != current.day] num_activities = len(lf) l = [a.id for a in lf if a.type == 'Run'] # Third, tabulate up the stats for mileage and calories. mileage = 0.0 calories = 0.0 for activity_id in l: activity = self.client.get_activity(activity_id) distance = unithelper.miles(activity.distance) mileage += round(distance.num, 2) # Rounds to 2 sig figs. calories += activity.calories calories = int(calories) # Finally, use the stats to craft a tweet. This can be any format # you want, but I'll use the example one from the start of the post. tweet = "My training last week: {:d} workouts for {:.2f} miles and {:d} calories burned.".format(num_activities, mileage, calories) self.update_status(tweet) def _compute_interval(self): """ This is a little more sophisticated than the method in the original blog post. This is to provide for *exactly* specifying when we want a post to be made, down to the minute. """ now = datetime.datetime.now() target = datetime.datetime(year = now.year, month = now.month, day = now.day, hour = self.config['update_hour'], minute = self.config['update_minute']) days_ahead = self.config['update_day'] - now.weekday() if (days_ahead < 0) or (days_ahead == 0 and (target - now).days < 0): days_ahead += 7 td = target + datetime.timedelta(days = days_ahead) interval = int((td - datetime.datetime.now()).total_seconds()) return interval
def main(): assert len(args.athlete_ids) == len(args.access_tokens) logger.info("app id: %i, fetching activities for ids %s" % \ (args.id_strava_app, str(args.athlete_ids))) for i in range( len(args.access_tokens) ): # for each athlete client = Client() client.access_token = args.access_tokens[i] athlete_id = args.athlete_ids[i] # get summary activities first (filterd before detailed activity call) time.sleep(TIME_PAUSE) activity_ids = get_user_activities(client) # now fetch detailed versions, add to db detailed_activites = [] activity_to_segments = {} # { act_id: # { seg_id : # { "distance" : x, "grade" : y }, }, } segment_ranks = {} # { seg_id : { "auth_athlete_rank" : auth_rank, # "other_athletes" : { other_id : other_rank, } } } for act_id in activity_ids: try: activity_to_segments[act_id] = {} time.sleep(TIME_PAUSE) detailed_activity = client.get_activity( act_id ) detailed_activites.append( detailed_activity ) for seg_effort in detailed_activity.segment_efforts: segment = seg_effort.segment seg_id = int(segment.id) seg_dist = float( unithelper.miles(segment.distance) ) seg_grade = segment.average_grade seg_dct = { "distance" : seg_dist, "grade" : seg_grade } activity_to_segments[act_id][seg_id] = seg_dct if segment_ranks.has_key(seg_id): # already have ranks continue # might be overlap between activities else: try: # some = hazardous = error time.sleep(TIME_PAUSE) # now get ranks for this segment leaderboard_entries = \ client.get_segment_leaderboard(seg_id, top_results_limit=1).entries segment_ranks[seg_id] = { "auth_athlete_rank" : -1, "other_athletes" : {} } for entry in leaderboard_entries: if entry.athlete_id == athlete_id: segment_ranks[seg_id]["auth_athlete_rank"] = entry.rank continue other_id = entry.athlete_id other_rank = entry.rank segment_ranks[seg_id]["other_athletes"][other_id] = other_rank except Exception, e: logger.warning("Error with segment_id %i, removing from activity,"\ " trace:\n %s" % (seg_id, traceback.print_exc())) activity_to_segments[act_id].pop(seg_id) continue if len(activity_to_segments[act_id]) > 0: add_activity(conn, athlete_id, detailed_activity) # if made it here, okay else: logger.info("No segments for activity %i, skipping" % act_id) except Exception, e: # occurs with wrong privaleges, eg private activity logger.warning("Error with activity %i for athlete %i, popping. tracebac:\n%s" % \ (act_id, athlete_id, traceback.print_exc())) activity_to_segments.pop(act_id)