예제 #1
0
 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
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
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)
예제 #5
0
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()
예제 #6
0
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
예제 #7
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}
예제 #8
0
파일: strava.py 프로젝트: toirl/trainable
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
예제 #9
0
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()
예제 #10
0
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
예제 #11
0
파일: views.py 프로젝트: rickblair/BikeTrax
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))
예제 #12
0
파일: views.py 프로젝트: rickblair/BikeTrax
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))
예제 #13
0
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
예제 #14
0
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?
예제 #15
0
파일: tasks.py 프로젝트: fakarava94/MyRuns
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}
예제 #16
0
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)
예제 #17
0
            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)
예제 #18
0
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)
예제 #19
0
파일: fetch_data.py 프로젝트: donK23/shed01
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)
예제 #20
0
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)
예제 #21
0
파일: artbot.py 프로젝트: Veterun/pybot
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)