def update_ride_basic(self, strava_activity: Activity, ride: Ride): """ Set basic ride properties from the Strava Activity object. :param strava_activity: The Strava Activity :param ride: The ride model object. """ # Should apply to both new and preexisting rides ... # If there are multiple instagram photos, then request syncing of non-primary photos too. if strava_activity.photo_count > 1 and ride.photos_fetched is None: self.logger.debug( "Scheduling non-primary photos sync for {!r}".format(ride)) ride.photos_fetched = False ride.private = bool(strava_activity.private) ride.name = strava_activity.name ride.start_date = strava_activity.start_date_local # We need to round so that "1.0" miles in data is "1.0" miles when we convert back from meters. ride.distance = round( float(unithelper.miles(strava_activity.distance)), 3) ride.average_speed = float( unithelper.mph(strava_activity.average_speed)) ride.maximum_speed = float(unithelper.mph(strava_activity.max_speed)) ride.elapsed_time = timedelta_to_seconds(strava_activity.elapsed_time) ride.moving_time = timedelta_to_seconds(strava_activity.moving_time) location_parts = [] if strava_activity.location_city: location_parts.append(strava_activity.location_city) if strava_activity.location_state: location_parts.append(strava_activity.location_state) location_str = ', '.join(location_parts) ride.location = location_str ride.commute = strava_activity.commute ride.trainer = strava_activity.trainer ride.manual = strava_activity.manual ride.elevation_gain = float( unithelper.feet(strava_activity.total_elevation_gain)) ride.timezone = str(strava_activity.timezone) # # Short-circuit things that might result in more obscure db errors later. if ride.elapsed_time is None: raise DataEntryError("Activities cannot have null elapsed time.") if ride.moving_time is None: raise DataEntryError("Activities cannot have null moving time.") if ride.distance is None: raise DataEntryError("Activities cannot have null distance.") self.logger.debug( "Writing ride for {athlete!r}: \"{ride!r}\" on {date}".format( athlete=ride.athlete.name, ride=ride.name, date=ride.start_date.strftime('%m/%d/%y')))
def stats_general(): q = text("""select count(*) as num_contestants from athletes WHERE team_id is not null""") indiv_count_res = db.session.execute(q).fetchone() # @UndefinedVariable contestant_count = indiv_count_res['num_contestants'] q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time, coalesce(sum(R.distance),0) as distance from rides R ; """) all_res = db.session.execute(q).fetchone() # @UndefinedVariable total_miles = int(all_res['distance']) total_hours = uh.timedelta_to_seconds(timedelta(seconds=int(all_res['moving_time']))) / 3600 total_rides = all_res['num_rides'] q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_temp_avg < 32 ; """) sub32_res = db.session.execute(q).fetchone() # @UndefinedVariable sub_freezing_hours = uh.timedelta_to_seconds(timedelta(seconds=int(sub32_res['moving_time']))) / 3600 q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_rain = 1 ; """) rain_res = db.session.execute(q).fetchone() # @UndefinedVariable rain_hours = uh.timedelta_to_seconds(timedelta(seconds=int(rain_res['moving_time']))) / 3600 q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_snow = 1 ; """) snow_res = db.session.execute(q).fetchone() # @UndefinedVariable snow_hours = uh.timedelta_to_seconds(timedelta(seconds=int(snow_res['moving_time']))) / 3600 return jsonify( team_count=len(app.config['BAFS_TEAMS']), contestant_count=contestant_count, total_rides=total_rides, total_hours=total_hours, total_miles=total_miles, rain_hours=rain_hours, snow_hours=snow_hours, sub_freezing_hours=sub_freezing_hours)
def stats_general(): q = text("""select count(*) as num_contestants from lbd_athletes""") indiv_count_res = db.session.execute(q).fetchone() # @UndefinedVariable contestant_count = indiv_count_res['num_contestants'] q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time, coalesce(sum(R.distance),0) as distance from rides R ; """) all_res = db.session.execute(q).fetchone() # @UndefinedVariable total_miles = int(all_res['distance']) total_hours = uh.timedelta_to_seconds(timedelta(seconds=int(all_res['moving_time']))) / 3600 total_rides = all_res['num_rides'] q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_temp_avg < 32 ; """) sub32_res = db.session.execute(q).fetchone() # @UndefinedVariable sub_freezing_hours = uh.timedelta_to_seconds(timedelta(seconds=int(sub32_res['moving_time']))) / 3600 q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_rain = 1 ; """) rain_res = db.session.execute(q).fetchone() # @UndefinedVariable rain_hours = uh.timedelta_to_seconds(timedelta(seconds=int(rain_res['moving_time']))) / 3600 q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_snow = 1 ; """) snow_res = db.session.execute(q).fetchone() # @UndefinedVariable snow_hours = uh.timedelta_to_seconds(timedelta(seconds=int(snow_res['moving_time']))) / 3600 return jsonify( team_count=len(app.config['BAFS_TEAMS']), contestant_count=contestant_count, total_rides=total_rides, total_hours=total_hours, total_miles=total_miles, rain_hours=rain_hours, snow_hours=snow_hours, sub_freezing_hours=sub_freezing_hours)
def generate_activity_summary_for_supported_activities( self, activity, athlete_details): activity_summary = "*New Activity Summary*:\n\n" \ "Athlete Name: {athlete_name}\n" \ "Activity: [{activity_name}](https://www.strava.com/activities/{activity_id})\n" \ "Activity Date: {activity_date}\n" \ "Activity Type: {activity_type}\n\n" \ "Distance: {distance} km\n" \ "Moving Time: {moving_time}\n" \ "Elapsed Time: {elapsed_time}\n" \ "Calories: {calories}\n".format( athlete_name=athlete_details['name'], activity_name=activity.name, activity_id=activity.id, activity_type=activity.type, activity_date=activity.start_date_local, distance=self.operations.meters_to_kilometers(float(activity.distance)), moving_time=self.operations.seconds_to_human_readable( unithelper.timedelta_to_seconds(activity.moving_time)), elapsed_time=self.operations.seconds_to_human_readable( unithelper.timedelta_to_seconds(activity.elapsed_time)), calories=self.operations.remove_decimal_point(activity.calories)) if self.operations.is_activity_a_ride(activity): activity_summary += "Avg Speed: {avg_speed}\n" \ "Max Speed: {max_speed}\n".format( avg_speed=unithelper.kilometers_per_hour(activity.average_speed), max_speed=unithelper.kilometers_per_hour(activity.max_speed)) else: activity_summary += "Avg Pace: {avg_pace}\n" \ "Max Pace: {max_pace}\n".format( avg_pace=unithelper.kilometers_per_hour(activity.average_speed), max_pace=unithelper.kilometers_per_hour(activity.max_speed)) if not self.operations.is_indoor(activity): activity_summary += "\nElevation Gain: {elevation_gain} meters".format( elevation_gain=self.operations.remove_decimal_point( activity.total_elevation_gain)) if activity.average_cadence: activity_summary += "\nAvg Cadence: {avg_cadence}".format( avg_cadence=self.operations.remove_decimal_point( activity.average_cadence)) if activity.has_heartrate: activity_summary += "\nAvg HR: {avg_hr} bpm\nMax HR: {max_hr} bpm".format( avg_hr=self.operations.remove_decimal_point( activity.average_heartrate), max_hr=self.operations.remove_decimal_point( activity.max_heartrate)) if self.operations.is_activity_a_ride( activity) and activity.device_watts: activity_summary += "\nAvg Watts: {avg_watts}\nMax Watts: {max_watts}".format( avg_watts=activity.average_watts, max_watts=activity.max_watts) activity_summary += "\n\n_Click_ /stats _to check your updated stats_" return activity_summary
def get_xtraining(self, after='2017-11-01', before='2018-09-05'): """ """ activities = self.client.get_activities(after=after, before=before) self.x_training = pd.DataFrame([{ 'name': activity.name, 'id': activity.id, 'activity': activity.type, 'elapsed_time': unithelper.timedelta_to_seconds(activity.elapsed_time), 'start_date': activity.start_date, 'distance': float(unithelper.meters(activity.distance)) * self.M_TO_YD } for activity in activities if activity.type != 'Swim']) self.x_training = self.x_training.set_index('start_date') self.x_training.index = pd.DatetimeIndex(self.x_training.index) self.x_training.index = self.x_training.index.tz_convert('US/Pacific') self.x_training['month'] = self.x_training.index.month self.x_training['month_name'] = self.x_training['month'].apply( lambda x: calendar.month_abbr[x]) self.x_training['ds_week_ending'] = self.x_training.index.to_period( 'W').end_time.floor('d') self.x_training['ds_month_ending'] = self.x_training.index.to_period( 'M').end_time.floor('d') self.x_training['hours'] = self.x_training.elapsed_time / 3600
def get_swims(self, after='2017-11-01', before='2018-09-05'): """ """ activities = self.client.get_activities(after=after, before=before) self.swims = pd.DataFrame([{ 'name': activity.name, 'id': activity.id, 'stroke_count': 2 * activity.average_cadence if activity.average_cadence is not None else None, 'average_speed': float(unithelper.meters_per_second(activity.average_speed)), 'elapsed_time': unithelper.timedelta_to_seconds(activity.elapsed_time), 'start_date': activity.start_date, 'distance': float(unithelper.meters(activity.distance)) * self.M_TO_YD } for activity in activities if activity.type == 'Swim']) self.swims = self.swims.set_index('start_date') self.swims.index = pd.DatetimeIndex(self.swims.index) self.swims.index = self.swims.index.tz_convert('US/Pacific') self.swims['month'] = self.swims.index.month self.swims['month_name'] = self.swims['month'].apply( lambda x: calendar.month_abbr[x]) self.swims['ds_week_ending'] = self.swims.index.to_period( 'W').end_time.floor('d') self.swims['ds_month_ending'] = self.swims.index.to_period( 'M').end_time.floor('d') self.swims['is_pool'] = [ 'pool' if "[POOL]" in name else 'open water' for name in self.swims.name ] self.swims['hours'] = self.swims.elapsed_time / 3600 self.total_yards = int(self.swims.distance.sum()) self.in_out = self.swims.groupby(['is_pool' ])['distance'].agg({'total': 'sum'}) self.in_out['percentage'] = 100 * self.in_out['total'] / self.in_out[ 'total'].sum() # TODO: wrap in utility # TODO: sort by ds_month_ending self.monthly_yardage = self.swims.groupby( self.swims.index.month)['distance', 'hours'].sum() self.monthly_yardage[ "weekly distance"] = self.monthly_yardage.distance / 4.0 self.monthly_yardage.index = self.monthly_yardage.index.map( lambda x: datetime.date(1900, x, 1).strftime('%B'))
def write_ride_efforts(self, strava_activity: Activity, ride: Ride): """ Writes out all effort associated with a ride to the database. :param strava_activity: The :class:`stravalib.orm.Activity` that is associated with this effort. :param ride: The db model object for ride. """ session = meta.scoped_session() try: # Start by removing any existing segments for the ride. session.execute(RideEffort.__table__.delete().where( RideEffort.ride_id == strava_activity.id)) # Then add them back in for se in strava_activity.segment_efforts: effort = RideEffort( id=se.id, ride_id=strava_activity.id, elapsed_time=timedelta_to_seconds(se.elapsed_time), segment_name=se.segment.name, segment_id=se.segment.id, ) self.logger.debug( "Writing ride effort: {se_id}: {effort!r}".format( se_id=se.id, effort=effort.segment_name)) session.add(effort) session.flush() # It would appear that Strava has some delayed consistency. Sometimes, no efforts are returned # and sometimes partial attempts are returned, so use an exponential backoff to re-fetch every # activity at least _MAX_EFFORT_RESYNCS times over an extended period of time. _MAX_EFFORT_RESYNCS = 3 sync_count = ride.resync_count or 0 if sync_count > _MAX_EFFORT_RESYNCS: ride.efforts_fetched = True else: ride.resync_count = 1 + sync_count ride.resync_date = datetime.now() + timedelta( hours=6**sync_count) # 1, 6, 36 hours except: self.logger.exception( "Error adding effort for ride: {0}".format(ride)) raise
def create_activity(self, name, activity_type, start_date_local, elapsed_time, description=None, distance=None): """ Create a new manual activity. If you would like to create an activity from an uploaded GPS file, see the :meth:`stravalib.client.Client.upload_activity` method instead. :param name: The name of the activity. :param activity_type: The activity type (case-insensitive). Possible values: ride, run, swim, workout, hike, walk, nordicski, alpineski, backcountryski, iceskate, inlineskate, kitesurf, rollerski, windsurf, workout, snowboard, snowshoe :param start_date: Local date/time of activity start. (TZ info will be ignored) :type start_date: :class:`datetime.datetime` or string in ISO8601 format. :param elapsed_time: The time in seconds or a :class:`datetime.timedelta` object. :type elapsed_time: :class:`datetime.timedelta` or int (seconds) :param description: The description for the activity. :type description: str :param distance: The distance in meters (float) or a :class:`units.quantity.Quantity` instance. :type distance: :class:`units.quantity.Quantity` or float (meters) """ if isinstance(elapsed_time, timedelta): elapsed_time = unithelper.timedelta_to_seconds(elapsed_time) if isinstance(distance, Quantity): distance = float(unithelper.meters(distance)) if isinstance(start_date_local, datetime): start_date_local = start_date_local.strftime("%Y-%m-%dT%H:%M:%SZ") if not activity_type.lower() in [t.lower() for t in model.Activity.TYPES]: raise ValueError("Invalid activity type: {0}. Possible values: {1!r}".format(activity_type, model.Activity.TYPES)) params = dict(name=name, type=activity_type, start_date_local=start_date_local, elapsed_time=elapsed_time) if description is not None: params['description'] = description if distance is not None: params['distance'] = distance raw_activity = self.protocol.post('/activities', **params) return model.Activity.deserialize(raw_activity, bind_client=self)
def write_ride_efforts(self, strava_activity: Activity, ride: Ride): """ Writes out all effort associated with a ride to the database. :param strava_activity: The :class:`stravalib.orm.Activity` that is associated with this effort. :param ride: The db model object for ride. """ session = meta.scoped_session() try: # Start by removing any existing segments for the ride. session.execute(RideEffort.__table__.delete().where( RideEffort.ride_id == strava_activity.id)) # Then add them back in for se in strava_activity.segment_efforts: effort = RideEffort(id=se.id, ride_id=strava_activity.id, elapsed_time=timedelta_to_seconds( se.elapsed_time), segment_name=se.segment.name, segment_id=se.segment.id) self.logger.debug( "Writing ride effort: {se_id}: {effort!r}".format( se_id=se.id, effort=effort.segment_name)) session.add(effort) session.flush() ride.efforts_fetched = True except: self.logger.exception( "Error adding effort for ride: {0}".format(ride)) raise
def index(): q = text("""select count(*) as num_contestants from lbd_athletes""") indiv_count_res = meta.session_factory().execute( q).fetchone() # @UndefinedVariable contestant_count = indiv_count_res['num_contestants'] q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time, coalesce(sum(R.distance),0) as distance from rides R ; """) all_res = meta.session_factory().execute( q).fetchone() # @UndefinedVariable total_miles = int(all_res['distance']) total_hours = uh.timedelta_to_seconds( timedelta(seconds=int(all_res['moving_time']))) / 3600 total_rides = all_res['num_rides'] q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_temp_avg < 32 ; """) sub32_res = meta.session_factory().execute( q).fetchone() # @UndefinedVariable sub_freezing_hours = uh.timedelta_to_seconds( timedelta(seconds=int(sub32_res['moving_time']))) / 3600 q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_rain = 1 ; """) rain_res = meta.session_factory().execute( q).fetchone() # @UndefinedVariable rain_hours = uh.timedelta_to_seconds( timedelta(seconds=int(rain_res['moving_time']))) / 3600 q = text(""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_snow = 1 ; """) snow_res = meta.session_factory().execute( q).fetchone() # @UndefinedVariable snow_hours = uh.timedelta_to_seconds( timedelta(seconds=int(snow_res['moving_time']))) / 3600 # Grab some recent photos photos = meta.session_factory().query(RidePhoto).join(Ride).order_by( Ride.start_date.desc()).limit(11) return render_template('index.html', team_count=len(app.config['BAFS_TEAMS']), contestant_count=contestant_count, total_rides=total_rides, total_hours=total_hours, total_miles=total_miles, rain_hours=rain_hours, snow_hours=snow_hours, sub_freezing_hours=sub_freezing_hours, photos=photos)
def calculate(self): logging.info("Calculating stats..") athlete_info = self.strava_resource.get_athlete_info( self.athlete_token) activities = self.strava_resource.get_strava_activities_after_date( self.athlete_token, "1970-01-01T00:00:00Z") today_date = date.today() current_month = today_date.month previous_month = (current_month - 1) if (current_month > 1) else 12 current_year = date.today().year previous_year = today_date.year - 1 rider_stats = self.get_rider_stats() rider_stats["updated"] = datetime.now().strftime("%d-%m-%Y %H:%M:%S") rider_stats["athlete_name"] = "{first_name} {last_name}".format( first_name=athlete_info.firstname, last_name=athlete_info.lastname) rider_stats[ "athlete_strava_joined_date"] = athlete_info.created_at.date( ).strftime('%Y-%m-%d') rider_stats["athlete_followers"] = athlete_info.follower_count rider_stats["athlete_following"] = athlete_info.friend_count for activity in activities: if not self.operations.is_flagged(activity): distance = float(activity.distance) moving_time = unithelper.timedelta_to_seconds( activity.moving_time) total_elevation_gain = float(activity.total_elevation_gain) activity_year = activity.start_date_local.year activity_month = activity.start_date_local.month if self.operations.is_activity_a_ride(activity): rider_stats["ride_at_total"] += 1 rider_stats["ride_at_distance"] += distance rider_stats["ride_at_moving_time"] += moving_time rider_stats[ "ride_at_elevation_gain"] += total_elevation_gain rider_stats[ "ride_at_achievements"] += activity.achievement_count rider_stats["ride_at_pr"] += activity.pr_count if activity.kilojoules: rider_stats["ride_at_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["ride_at_indoor_total"] += 1 rider_stats["ride_at_indoor_distance"] += distance rider_stats[ "ride_at_indoor_moving_time"] += moving_time if activity.commute: rider_stats["ride_at_commutes"] += 1 if distance > rider_stats["ride_at_biggest_ride"]: rider_stats["ride_at_biggest_ride"] = distance if total_elevation_gain > rider_stats[ "ride_at_max_elevation_gain"]: rider_stats[ "ride_at_max_elevation_gain"] = total_elevation_gain if 50000.0 <= distance < 100000.0: rider_stats["ride_at_fifty"] += 1 elif distance > 100000.0: rider_stats["ride_at_hundred"] += 1 elif self.operations.is_activity_a_run(activity): rider_stats["run_at_total"] += 1 rider_stats["run_at_distance"] += distance rider_stats["run_at_moving_time"] += moving_time rider_stats[ "run_at_elevation_gain"] += total_elevation_gain rider_stats[ "run_at_achievements"] += activity.achievement_count rider_stats["run_at_pr"] += activity.pr_count if activity.kilojoules: rider_stats["run_at_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["run_at_indoor_total"] += 1 rider_stats["run_at_indoor_distance"] += distance rider_stats["run_at_indoor_moving_time"] += moving_time if activity.commute: rider_stats["run_at_commutes"] += 1 if distance > rider_stats["run_at_biggest_run"]: rider_stats["run_at_biggest_run"] = distance if total_elevation_gain > rider_stats[ "run_at_max_elevation_gain"]: rider_stats[ "run_at_max_elevation_gain"] = total_elevation_gain if 5000.0 <= distance < 10000.0: rider_stats["run_at_five"] += 1 elif 10000.0 <= distance < 21000.0: rider_stats["run_at_ten"] += 1 elif 21000.0 <= distance < 42000.0: rider_stats["run_at_hm"] += 1 elif 42000.0 <= distance < 44000.0: rider_stats["run_at_fm"] += 1 elif distance > 44000.0: rider_stats["run_at_ultra"] += 1 elif self.operations.is_activity_a_swim(activity): rider_stats["swim_at_total"] += 1 rider_stats["swim_at_distance"] += distance rider_stats["swim_at_moving_time"] += moving_time rider_stats[ "swim_at_achievements"] += activity.achievement_count rider_stats["swim_at_pr"] += activity.pr_count if activity.kilojoules: rider_stats["swim_at_calories"] += activity.kilojoules if distance > rider_stats["swim_at_biggest_swim"]: rider_stats["swim_at_biggest_swim"] = distance if 50.0 <= distance < 100.0: rider_stats["swim_at_50"] += 1 elif 100.0 <= distance < 200.0: rider_stats["swim_at_100"] += 1 elif 200.0 <= distance < 400.0: rider_stats["swim_at_200"] += 1 elif 400.0 <= distance < 800.0: rider_stats["swim_at_400"] += 1 elif 800.0 <= distance < 1500.0: rider_stats["swim_at_800"] += 1 elif distance > 1500.0: rider_stats["swim_at_1500"] += 1 if activity_year == current_year: if self.operations.is_activity_a_ride(activity): rider_stats["ride_ytd_total"] += 1 rider_stats["ride_ytd_distance"] += distance rider_stats["ride_ytd_moving_time"] += moving_time rider_stats[ "ride_ytd_elevation_gain"] += total_elevation_gain rider_stats[ "ride_ytd_achievements"] += activity.achievement_count rider_stats["ride_ytd_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "ride_ytd_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["ride_ytd_indoor_total"] += 1 rider_stats["ride_ytd_indoor_distance"] += distance rider_stats[ "ride_ytd_indoor_moving_time"] += moving_time if activity.commute: rider_stats["ride_ytd_commutes"] += 1 if distance > rider_stats["ride_ytd_biggest_ride"]: rider_stats["ride_ytd_biggest_ride"] = distance if total_elevation_gain > rider_stats[ "ride_ytd_max_elevation_gain"]: rider_stats[ "ride_ytd_max_elevation_gain"] = total_elevation_gain if 50000.0 <= distance < 100000.0: rider_stats["ride_ytd_fifty"] += 1 elif distance > 100000.0: rider_stats["ride_ytd_hundred"] += 1 elif self.operations.is_activity_a_run(activity): rider_stats["run_ytd_total"] += 1 rider_stats["run_ytd_distance"] += distance rider_stats["run_ytd_moving_time"] += moving_time rider_stats[ "run_ytd_elevation_gain"] += total_elevation_gain rider_stats[ "run_ytd_achievements"] += activity.achievement_count rider_stats["run_ytd_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "run_ytd_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["run_ytd_indoor_total"] += 1 rider_stats["run_ytd_indoor_distance"] += distance rider_stats[ "run_ytd_indoor_moving_time"] += moving_time if activity.commute: rider_stats["run_ytd_commutes"] += 1 if distance > rider_stats["run_ytd_biggest_run"]: rider_stats["run_ytd_biggest_run"] = distance if total_elevation_gain > rider_stats[ "run_ytd_max_elevation_gain"]: rider_stats[ "run_ytd_max_elevation_gain"] = total_elevation_gain if 5000.0 <= distance < 10000.0: rider_stats["run_ytd_five"] += 1 elif 10000.0 <= distance < 21000.0: rider_stats["run_ytd_ten"] += 1 elif 21000.0 <= distance < 42000.0: rider_stats["run_ytd_hm"] += 1 elif 42000.0 <= distance < 44000.0: rider_stats["run_ytd_fm"] += 1 elif distance > 44000.0: rider_stats["run_ytd_ultra"] += 1 elif self.operations.is_activity_a_swim(activity): rider_stats["swim_ytd_total"] += 1 rider_stats["swim_ytd_distance"] += distance rider_stats["swim_ytd_moving_time"] += moving_time rider_stats[ "swim_ytd_achievements"] += activity.achievement_count rider_stats["swim_ytd_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "swim_ytd_calories"] += activity.kilojoules if distance > rider_stats["swim_ytd_biggest_swim"]: rider_stats["swim_ytd_biggest_swim"] = distance if 50.0 <= distance < 100.0: rider_stats["swim_ytd_50"] += 1 elif 100.0 <= distance < 200.0: rider_stats["swim_ytd_100"] += 1 elif 200.0 <= distance < 400.0: rider_stats["swim_ytd_200"] += 1 elif 400.0 <= distance < 800.0: rider_stats["swim_ytd_400"] += 1 elif 800.0 <= distance < 1500.0: rider_stats["swim_ytd_800"] += 1 elif distance > 1500.0: rider_stats["swim_ytd_1500"] += 1 if activity_year == previous_year: if self.operations.is_activity_a_ride(activity): rider_stats["ride_py_total"] += 1 rider_stats["ride_py_distance"] += distance rider_stats["ride_py_moving_time"] += moving_time rider_stats[ "ride_py_elevation_gain"] += total_elevation_gain rider_stats[ "ride_py_achievements"] += activity.achievement_count rider_stats["ride_py_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "ride_py_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["ride_py_indoor_total"] += 1 rider_stats["ride_py_indoor_distance"] += distance rider_stats[ "ride_py_indoor_moving_time"] += moving_time if activity.commute: rider_stats["ride_py_commutes"] += 1 if distance > rider_stats["ride_py_biggest_ride"]: rider_stats["ride_py_biggest_ride"] = distance if total_elevation_gain > rider_stats[ "ride_py_max_elevation_gain"]: rider_stats[ "ride_py_max_elevation_gain"] = total_elevation_gain if 50000.0 <= distance < 100000.0: rider_stats["ride_py_fifty"] += 1 elif distance > 100000.0: rider_stats["ride_py_hundred"] += 1 elif self.operations.is_activity_a_run(activity): rider_stats["run_py_total"] += 1 rider_stats["run_py_distance"] += distance rider_stats["run_py_moving_time"] += moving_time rider_stats[ "run_py_elevation_gain"] += total_elevation_gain rider_stats[ "run_py_achievements"] += activity.achievement_count rider_stats["run_py_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "run_py_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["run_py_indoor_total"] += 1 rider_stats["run_py_indoor_distance"] += distance rider_stats[ "run_py_indoor_moving_time"] += moving_time if activity.commute: rider_stats["run_py_commutes"] += 1 if distance > rider_stats["run_py_biggest_run"]: rider_stats["run_py_biggest_run"] = distance if total_elevation_gain > rider_stats[ "run_py_max_elevation_gain"]: rider_stats[ "run_py_max_elevation_gain"] = total_elevation_gain if 5000.0 <= distance < 10000.0: rider_stats["run_py_five"] += 1 elif 10000.0 <= distance < 21000.0: rider_stats["run_py_ten"] += 1 elif 21000.0 <= distance < 42000.0: rider_stats["run_py_hm"] += 1 elif 42000.0 <= distance < 44000.0: rider_stats["run_py_fm"] += 1 elif distance > 44000.0: rider_stats["run_py_ultra"] += 1 elif self.operations.is_activity_a_swim(activity): rider_stats["swim_py_total"] += 1 rider_stats["swim_py_distance"] += distance rider_stats["swim_py_moving_time"] += moving_time rider_stats[ "swim_py_achievements"] += activity.achievement_count rider_stats["swim_py_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "swim_py_calories"] += activity.kilojoules if distance > rider_stats["swim_py_biggest_swim"]: rider_stats["swim_py_biggest_swim"] = distance if 50.0 <= distance < 100.0: rider_stats["swim_py_50"] += 1 elif 100.0 <= distance < 200.0: rider_stats["swim_py_100"] += 1 elif 200.0 <= distance < 400.0: rider_stats["swim_py_200"] += 1 elif 400.0 <= distance < 800.0: rider_stats["swim_py_400"] += 1 elif 800.0 <= distance < 1500.0: rider_stats["swim_py_800"] += 1 elif distance > 1500.0: rider_stats["swim_py_1500"] += 1 if activity_month == current_month and activity_year == current_year: if self.operations.is_activity_a_ride(activity): rider_stats["ride_cm_total"] += 1 rider_stats["ride_cm_distance"] += distance rider_stats["ride_cm_moving_time"] += moving_time rider_stats[ "ride_cm_elevation_gain"] += total_elevation_gain rider_stats[ "ride_cm_achievements"] += activity.achievement_count rider_stats["ride_cm_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "ride_cm_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["ride_cm_indoor_total"] += 1 rider_stats["ride_cm_indoor_distance"] += distance rider_stats[ "ride_cm_indoor_moving_time"] += moving_time if activity.commute: rider_stats["ride_cm_commutes"] += 1 if distance > rider_stats["ride_cm_biggest_ride"]: rider_stats["ride_cm_biggest_ride"] = distance if total_elevation_gain > rider_stats[ "ride_cm_max_elevation_gain"]: rider_stats[ "ride_cm_max_elevation_gain"] = total_elevation_gain if 50000.0 <= distance < 100000.0: rider_stats["ride_cm_fifty"] += 1 elif distance > 100000.0: rider_stats["ride_cm_hundred"] += 1 elif self.operations.is_activity_a_run(activity): rider_stats["run_cm_total"] += 1 rider_stats["run_cm_distance"] += distance rider_stats["run_cm_moving_time"] += moving_time rider_stats[ "run_cm_elevation_gain"] += total_elevation_gain rider_stats[ "run_cm_achievements"] += activity.achievement_count rider_stats["run_cm_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "run_cm_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["run_cm_indoor_total"] += 1 rider_stats["run_cm_indoor_distance"] += distance rider_stats[ "run_cm_indoor_moving_time"] += moving_time if activity.commute: rider_stats["run_cm_commutes"] += 1 if distance > rider_stats["run_cm_biggest_run"]: rider_stats["run_cm_biggest_run"] = distance if total_elevation_gain > rider_stats[ "run_cm_max_elevation_gain"]: rider_stats[ "run_cm_max_elevation_gain"] = total_elevation_gain if 5000.0 <= distance < 10000.0: rider_stats["run_cm_five"] += 1 elif 10000.0 <= distance < 21000.0: rider_stats["run_cm_ten"] += 1 elif 21000.0 <= distance < 42000.0: rider_stats["run_cm_hm"] += 1 elif 42000.0 <= distance < 44000.0: rider_stats["run_cm_fm"] += 1 elif distance > 44000.0: rider_stats["run_cm_ultra"] += 1 elif self.operations.is_activity_a_swim(activity): rider_stats["swim_cm_total"] += 1 rider_stats["swim_cm_distance"] += distance rider_stats["swim_cm_moving_time"] += moving_time rider_stats[ "swim_cm_achievements"] += activity.achievement_count rider_stats["swim_cm_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "swim_cm_calories"] += activity.kilojoules if distance > rider_stats["swim_cm_biggest_swim"]: rider_stats["swim_cm_biggest_swim"] = distance if 50.0 <= distance < 100.0: rider_stats["swim_cm_50"] += 1 elif 100.0 <= distance < 200.0: rider_stats["swim_cm_100"] += 1 elif 200.0 <= distance < 400.0: rider_stats["swim_cm_200"] += 1 elif 400.0 <= distance < 800.0: rider_stats["swim_cm_400"] += 1 elif 800.0 <= distance < 1500.0: rider_stats["swim_cm_800"] += 1 elif distance > 1500.0: rider_stats["swim_cm_1500"] += 1 # TODO This condition fails for January. Fix it. if activity_month == previous_month and activity_year == current_year: if self.operations.is_activity_a_ride(activity): rider_stats["ride_pm_total"] += 1 rider_stats["ride_pm_distance"] += distance rider_stats["ride_pm_moving_time"] += moving_time rider_stats[ "ride_pm_elevation_gain"] += total_elevation_gain rider_stats[ "ride_pm_achievements"] += activity.achievement_count rider_stats["ride_pm_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "ride_pm_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["ride_pm_indoor_total"] += 1 rider_stats["ride_pm_indoor_distance"] += distance rider_stats[ "ride_pm_indoor_moving_time"] += moving_time if activity.commute: rider_stats["ride_pm_commutes"] += 1 if distance > rider_stats["ride_pm_biggest_ride"]: rider_stats["ride_pm_biggest_ride"] = distance if total_elevation_gain > rider_stats[ "ride_pm_max_elevation_gain"]: rider_stats[ "ride_pm_max_elevation_gain"] = total_elevation_gain if 50000.0 <= distance < 100000.0: rider_stats["ride_pm_fifty"] += 1 elif distance > 100000.0: rider_stats["ride_pm_hundred"] += 1 elif self.operations.is_activity_a_run(activity): rider_stats["run_pm_total"] += 1 rider_stats["run_pm_distance"] += distance rider_stats["run_pm_moving_time"] += moving_time rider_stats[ "run_pm_elevation_gain"] += total_elevation_gain rider_stats[ "run_pm_achievements"] += activity.achievement_count rider_stats["run_pm_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "run_pm_calories"] += activity.kilojoules if self.operations.is_indoor(activity): rider_stats["run_pm_indoor_total"] += 1 rider_stats["run_pm_indoor_distance"] += distance rider_stats[ "run_pm_indoor_moving_time"] += moving_time if activity.commute: rider_stats["run_pm_commutes"] += 1 if distance > rider_stats["run_pm_biggest_run"]: rider_stats["run_pm_biggest_run"] = distance if total_elevation_gain > rider_stats[ "run_pm_max_elevation_gain"]: rider_stats[ "run_pm_max_elevation_gain"] = total_elevation_gain if 5000.0 <= distance < 10000.0: rider_stats["run_pm_five"] += 1 elif 10000.0 <= distance < 21000.0: rider_stats["run_pm_ten"] += 1 elif 21000.0 <= distance < 42000.0: rider_stats["run_pm_hm"] += 1 elif 42000.0 <= distance < 44000.0: rider_stats["run_pm_fm"] += 1 elif distance > 44000.0: rider_stats["run_pm_ultra"] += 1 elif self.operations.is_activity_a_swim(activity): rider_stats["swim_pm_total"] += 1 rider_stats["swim_pm_distance"] += distance rider_stats["swim_pm_moving_time"] += moving_time rider_stats[ "swim_pm_achievements"] += activity.achievement_count rider_stats["swim_pm_pr"] += activity.pr_count if activity.kilojoules: rider_stats[ "swim_pm_calories"] += activity.kilojoules if distance > rider_stats["swim_pm_biggest_swim"]: rider_stats["swim_pm_biggest_swim"] = distance if 50.0 <= distance < 100.0: rider_stats["swim_pm_50"] += 1 elif 100.0 <= distance < 200.0: rider_stats["swim_pm_100"] += 1 elif 200.0 <= distance < 400.0: rider_stats["swim_pm_200"] += 1 elif 400.0 <= distance < 800.0: rider_stats["swim_pm_400"] += 1 elif 800.0 <= distance < 1500.0: rider_stats["swim_pm_800"] += 1 elif distance > 1500.0: rider_stats["swim_pm_1500"] += 1 return rider_stats
def index(): q = text ("""select count(*) as num_contestants from lbd_athletes""") indiv_count_res = meta.scoped_session().execute(q).fetchone() # @UndefinedVariable contestant_count = indiv_count_res['num_contestants'] q = text (""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time, coalesce(sum(R.distance),0) as distance from rides R ; """) all_res = meta.scoped_session().execute(q).fetchone() # @UndefinedVariable total_miles = int(all_res['distance']) total_hours = uh.timedelta_to_seconds(timedelta(seconds=int(all_res['moving_time']))) / 3600 total_rides = all_res['num_rides'] q = text (""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_temp_avg < 32 ; """) sub32_res = meta.scoped_session().execute(q).fetchone() # @UndefinedVariable sub_freezing_hours = uh.timedelta_to_seconds(timedelta(seconds=int(sub32_res['moving_time']))) / 3600 q = text (""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_rain = 1 ; """) rain_res = meta.scoped_session().execute(q).fetchone() # @UndefinedVariable rain_hours = uh.timedelta_to_seconds(timedelta(seconds=int(rain_res['moving_time']))) / 3600 q = text (""" select count(*) as num_rides, coalesce(sum(R.moving_time),0) as moving_time from rides R join ride_weather W on W.ride_id = R.id where W.ride_snow = 1 ; """) snow_res = meta.scoped_session().execute(q).fetchone() # @UndefinedVariable snow_hours = uh.timedelta_to_seconds(timedelta(seconds=int(snow_res['moving_time']))) / 3600 # Grab some recent photos photos = meta.scoped_session().query(RidePhoto).join(Ride).order_by(Ride.start_date.desc()).limit(11) return render_template('index.html', team_count=len(config.COMPETITION_TEAMS), contestant_count=contestant_count, total_rides=total_rides, total_hours=total_hours, total_miles=total_miles, rain_hours=rain_hours, snow_hours=snow_hours, sub_freezing_hours=sub_freezing_hours, photos=photos, competition_title=config.COMPETITION_TITLE)
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)
def get_activities_calendar(self, athlete_details): activities_calendar = { "calendar": { "2019916": { "result": False, "data": { "activities": [] } }, "2019917": { "result": False, "data": { "activities": [] } }, "2019918": { "result": False, "data": { "activities": [] } }, "2019919": { "result": False, "data": { "activities": [] } }, "2019920": { "result": False, "data": { "activities": [] } }, "2019921": { "result": False, "data": { "activities": [] } }, "2019922": { "result": False, "data": { "activities": [] } }, "2019923": { "result": False, "data": { "activities": [] } }, "2019924": { "result": False, "data": { "activities": [] } }, "2019925": { "result": False, "data": { "activities": [] } }, "2019926": { "result": False, "data": { "activities": [] } }, "2019927": { "result": False, "data": { "activities": [] } }, "2019928": { "result": False, "data": { "activities": [] } }, "2019929": { "result": False, "data": { "activities": [] } }, "2019930": { "result": False, "data": { "activities": [] } }, "2019101": { "result": False, "data": { "activities": [] } }, "2019102": { "result": False, "data": { "activities": [] } }, "2019103": { "result": False, "data": { "activities": [] } }, "2019104": { "result": False, "data": { "activities": [] } }, "2019105": { "result": False, "data": { "activities": [] } }, "2019106": { "result": False, "data": { "activities": [] } }, "2019107": { "result": False, "data": { "activities": [] } }, "2019108": { "result": False, "data": { "activities": [] } }, "2019109": { "result": False, "data": { "activities": [] } }, "20191010": { "result": False, "data": { "activities": [] } }, "20191011": { "result": False, "data": { "activities": [] } }, "20191012": { "result": False, "data": { "activities": [] } }, "20191013": { "result": False, "data": { "activities": [] } }, "20191014": { "result": False, "data": { "activities": [] } }, "20191015": { "result": False, "data": { "activities": [] } }, "20191016": { "result": False, "activities": [] }, "20191017": { "result": False, "data": { "activities": [] } }, "20191018": { "result": False, "data": { "activities": [] } }, "20191019": { "result": False, "data": { "activities": [] } }, "20191020": { "result": False, "data": { "activities": [] } }, "20191021": { "result": False, "data": { "activities": [] } }, "20191022": { "result": False, "data": { "activities": [] } }, "20191023": { "result": False, "data": { "activities": [] } }, "20191024": { "result": False, "data": { "activities": [] } }, "20191025": { "result": False, "data": { "activities": [] } }, "20191026": { "result": False, "data": { "activities": [] } }, "20191027": { "result": False, "data": { "activities": [] } }, "20191028": { "result": False, "data": { "activities": [] } }, "20191029": { "result": False, "data": { "activities": [] } }, "20191030": { "result": False, "data": { "activities": [] } }, "20191031": { "result": False, "data": { "activities": [] } }, "2019111": { "result": False, "data": { "activities": [] } }, "2019112": { "result": False, "data": { "activities": [] } }, "2019113": { "result": False, "data": { "activities": [] } }, "2019114": { "result": False, "data": { "activities": [] } }, "2019115": { "result": False, "data": { "activities": [] } }, "2019116": { "result": False, "data": { "activities": [] } }, "2019117": { "result": False, "data": { "activities": [] } }, "2019118": { "result": False, "data": { "activities": [] } }, "2019119": { "result": False, "data": { "activities": [] } }, "20191110": { "result": False, "data": { "activities": [] } }, "20191111": { "result": False, "data": { "activities": [] } }, "20191112": { "result": False, "data": { "activities": [] } }, "20191113": { "result": False, "data": { "activities": [] } }, "20191114": { "result": False, "data": { "activities": [] } }, "20191115": { "result": False, "data": { "activities": [] } }, "20191116": { "result": False, "data": { "activities": [] } }, "20191117": { "result": False, "data": { "activities": [] } }, "20191118": { "result": False, "data": { "activities": [] } }, "20191119": { "result": False, "data": { "activities": [] } }, "20191120": { "result": False, "data": { "activities": [] } }, "20191121": { "result": False, "data": { "activities": [] } }, "20191122": { "result": False, "data": { "activities": [] } }, "20191123": { "result": False, "data": { "activities": [] } }, "20191124": { "result": False, "data": { "activities": [] } } } } try: for activity in self.strava_resource.get_strava_activities_after_date_before_date( athlete_details['athlete_token'], self.app_variables.tok_odd_challenges_from_date, self.app_variables.tok_odd_challenges_to_date): activity_type = activity.type activity_year = activity.start_date_local.year activity_month = activity.start_date_local.month activity_day = activity.start_date_local.day activity_distance = float(activity.distance) activity_elevation = float( activity.total_elevation_gain ) if not self.operations.is_indoor(activity) else 0.0 activity_time = unithelper.timedelta_to_seconds( activity.moving_time) calendar_key = "{activity_year}{activity_month}{activity_day}".format( activity_year=activity_year, activity_month=activity_month, activity_day=activity_day) logging.info( "Type: %s | Year: %s | Month: %s | Day: %s | Distance: %s | Time: %s | Elevation: %s, Calendar Key: %s", activity_type, activity_year, activity_month, activity_day, activity_distance, activity_time, activity_elevation, calendar_key) if self.operations.supported_activities_for_tok_challenges( activity ) and activity_month in self.app_variables.tok_odd_challenges_month and activity_year in self.app_variables.tok_odd_challenges_year: logging.info("Activity considered.") activities_calendar["calendar"][calendar_key][ "result"] = True activities_calendar["calendar"][calendar_key]["data"][ "activities"].append({ "type": self.get_activity_type(activity), "distance": activity_distance, "elevation": activity_elevation }) except ValueError as exception_message: if str(exception_message) == "day is out of range for month": logging.info("Future date") else: logging.info(exception_message) except Exception: logging.info(traceback.format_exc()) finally: return ujson.dumps(activities_calendar)