Esempio n. 1
0
class Segment(LoadableEntity):
    """
    Represents a single Strava segment.
    """
    _leaderboard = None

    name = Attribute(str, (SUMMARY, DETAILED))  #: Name of the segment.
    activity_type = Attribute(str, (SUMMARY, DETAILED))  #: Activity type of segment ('Ride' or 'Run')
    distance = Attribute(float, (SUMMARY, DETAILED), units=uh.meters)  #: Distance of segment
    average_grade = Attribute(float, (SUMMARY, DETAILED))  #: Average grade (%) for segment
    maximum_grade = Attribute(float, (SUMMARY, DETAILED))  #: Maximum grade (%) for segment
    elevation_high = Attribute(float, (SUMMARY, DETAILED), units=uh.meters)  #: The highest point of the segment.
    elevation_low = Attribute(float, (SUMMARY, DETAILED), units=uh.meters)  #: The lowest point of the segment.
    start_latlng = LocationAttribute((SUMMARY, DETAILED))  #: The start lat/lon (:class:`tuple`)
    end_latlng = LocationAttribute((SUMMARY, DETAILED))  #: The end lat/lon (:class:`tuple`)
    start_latitude = Attribute(float, (SUMMARY, DETAILED))  #: The start latitude (:class:`float`)
    end_latitude = Attribute(float, (SUMMARY, DETAILED))  #: The end latitude (:class:`float`)
    start_longitude = Attribute(float, (SUMMARY, DETAILED))  #: The start longitude (:class:`float`)

    end_longitude = Attribute(float, (SUMMARY, DETAILED))  #: The end longitude (:class:`float`)
    climb_category = Attribute(int, (SUMMARY, DETAILED))  # 0-5, lower is harder
    city = Attribute(str, (SUMMARY, DETAILED))  #: The city this segment is in.
    state = Attribute(str, (SUMMARY, DETAILED))  #: The state this segment is in.
    country = Attribute(str, (SUMMARY, DETAILED))  #: The country this segment is in.
    private = Attribute(bool, (SUMMARY, DETAILED))  #: Whether this is a private segment.
    starred = Attribute(bool, (SUMMARY, DETAILED))  #: Whether this segment is starred by authenticated athlete

    athlete_segment_stats = EntityAttribute(AthleteSegmentStats, (DETAILED,)) #: Undocumented attrib holding stats for current athlete.

    # detailed attribs
    created_at = TimestampAttribute((DETAILED,))  #: :class:`datetime.datetime` when was segment created.
    updated_at = TimestampAttribute((DETAILED,))  #: :class:`datetime.datetime` when was segment last updated.
    total_elevation_gain = Attribute(float, (DETAILED,), units=uh.meters)  #: What is total elevation gain for segment.
    map = EntityAttribute(Map, (DETAILED,))  #: :class:`stravalib.model.Map` object for segment.
    effort_count = Attribute(int, (DETAILED,))  #: How many times has this segment been ridden.
    athlete_count = Attribute(int, (DETAILED,))  #: How many athletes have ridden this segment
    hazardous = Attribute(bool, (DETAILED,))  #: Whether this segment has been flagged as hazardous
    star_count = Attribute(int, (DETAILED,))  #: number of stars on this segment.

    @property
    def leaderboard(self):
        """
        The :class:`stravalib.model.SegmentLeaderboard` object for this segment.
        """
        if self._leaderboard is None:
            self.assert_bind_client()
            if self.id is not None:
                self._leaderboard = self.bind_client.get_segment_leaderboard(self.id)
        return self._leaderboard
Esempio n. 2
0
class BaseEffort(LoadableEntity):
    """
    Base class for a best effort or segment effort.
    """
    name = Attribute(unicode, (SUMMARY,DETAILED)) #: The name of the segment
    segment = EntityAttribute(Segment, (SUMMARY,DETAILED)) #: The associated :class:`stravalib.model.Segment` for this effort 
    activity = EntityAttribute("Activity", (SUMMARY,DETAILED)) #: The associated :class:`stravalib.model.Activity`
    athlete = EntityAttribute(Athlete, (SUMMARY,DETAILED)) #: The associated :class:`stravalib.model.Athlete` 
    kom_rank = Attribute(int, (SUMMARY,DETAILED)) #: 1-10 segment KOM ranking for athlete at time of upload
    pr_rank = Attribute(int, (SUMMARY,DETAILED)) #: 1-3 personal record ranking for athlete at time of upload
    moving_time = TimeIntervalAttribute((SUMMARY,DETAILED)) #: :class:`datetime.timedelta` 
    elapsed_time = TimeIntervalAttribute((SUMMARY,DETAILED))#: :class:`datetime.timedelta` 
    start_date = TimestampAttribute((SUMMARY,DETAILED)) #: :class:`datetime.datetime` when effort was started in GMT
    start_date_local = TimestampAttribute((SUMMARY,DETAILED), tzinfo=None) #: :class:`datetime.datetime` when effort was started in activity timezone for this effort
    distance = Attribute(int, (SUMMARY,DETAILED), units=uh.meters) #: The distance for this effort. 
Esempio n. 3
0
class ActivityComment(LoadableEntity):
    """
    Comments attached to an activity.
    """
    activity_id = Attribute(int, (META,SUMMARY,DETAILED)) #: ID of activity
    text = Attribute(unicode, (META,SUMMARY,DETAILED)) #: Text of comment
    created_at = TimestampAttribute((SUMMARY,DETAILED)) #: :class:`datetime.datetime` when was coment created
    athlete = EntityAttribute(Athlete, (SUMMARY,DETAILED)) #: Associated :class:`stravalib.model.Athlete` (summary-level representation)
Esempio n. 4
0
class ActivityPhotoMeta(BaseEntity):
    """
    The photos structure returned with the activity, not to be confused with the full loaded photos for an activity.
    """
    count = Attribute(int, (META, SUMMARY, DETAILED))
    primary = EntityAttribute(ActivityPhotoPrimary, (META, SUMMARY, DETAILED))
    use_primary_photo = Attribute(bool, (META, SUMMARY, DETAILED))

    def __repr__(self):
        return '<{0} count={1}>'.format(self.__class__.__name__, self.count)
 def test_unmarshal_non_ascii_chars(self):
     NON_ASCII_DATA = {
         u'profile': u'http://dgalywyr863hv.cloudfront.net/pictures/athletes/874283/198397/1/large.jpg',
         u'city': u'Ljubljana',
         u'premium': True,
         u'firstname': u'Bla\u017e',
         u'updated_at': u'2014-05-13T06:16:29Z',
         u'lastname': u'Vizjak',
         u'created_at': u'2012-08-01T07:49:43Z',
         u'follower': None,
         u'sex': u'M',
         u'state': u'Ljubljana',
         u'country': u'Slovenia',
         u'resource_state': 2,
         u'profile_medium': u'http://dgalywyr863hv.cloudfront.net/pictures/athletes/874283/198397/1/medium.jpg',
         u'id': 874283,
         u'friend': None
     }
     athlete = EntityAttribute(Athlete, (SUMMARY,DETAILED))
     athlete.unmarshal(NON_ASCII_DATA)
Esempio n. 6
0
 def test_unmarshal_non_ascii_chars(self):
     NON_ASCII_DATA = {
         'profile': 'http://dgalywyr863hv.cloudfront.net/pictures/athletes/874283/198397/1/large.jpg',
         'city': 'Ljubljana',
         'premium': True,
         'firstname': 'Bla\u017e',
         'updated_at': '2014-05-13T06:16:29Z',
         'lastname': 'Vizjak',
         'created_at': '2012-08-01T07:49:43Z',
         'follower': None,
         'sex': 'M',
         'state': 'Ljubljana',
         'country': 'Slovenia',
         'resource_state': 2,
         'profile_medium': 'http://dgalywyr863hv.cloudfront.net/pictures/athletes/874283/198397/1/medium.jpg',
         'id': 874283,
         'friend': None
     }
     athlete = EntityAttribute(Athlete, (SUMMARY, DETAILED))
     athlete.unmarshal(NON_ASCII_DATA)
Esempio n. 7
0
class ActivityLap(LoadableEntity):

    name = Attribute(unicode, (SUMMARY,DETAILED)) #: Name of lap
    activity = EntityAttribute("Activity", (SUMMARY,DETAILED)) #: The associated :class:`stravalib.model.Activity`
    athlete = EntityAttribute(Athlete, (SUMMARY,DETAILED)) #: The associated :class:`stravalib.model.Athlete`


    elapsed_time = TimeIntervalAttribute((SUMMARY, DETAILED)) #: :class:`datetime.timedelta` of elapsed time for lap
    moving_time = TimeIntervalAttribute((SUMMARY, DETAILED)) #: :class:`datetime.timedelta` of moving time for lap
    start_date = TimestampAttribute((SUMMARY,DETAILED)) #: :class:`datetime.datetime` when lap was started in GMT
    start_date_local = TimestampAttribute((SUMMARY,DETAILED), tzinfo=None) #: :class:`datetime.datetime` when lap was started local
    distance = Attribute(float, (SUMMARY,DETAILED), units=uh.meters) #: The distance for this lap.
    start_index= Attribute(int, (SUMMARY,DETAILED)) #:
    end_index= Attribute(int, (SUMMARY,DETAILED)) #:
    total_elevation_gain = Attribute(float, (SUMMARY,DETAILED,), units=uh.meters) #: What is total elevation gain for lap
    average_speed = Attribute(float, (SUMMARY,DETAILED,), units=uh.meters_per_second) #: Average speed for lap
    max_speed = Attribute(float, (SUMMARY,DETAILED,), units=uh.meters_per_second) #: Max speed for lap
    average_cadence = Attribute(float, (SUMMARY,DETAILED,)) #: Average cadence for lap
    average_watts = Attribute(float, (SUMMARY,DETAILED,)) #: Average watts for lap
    average_heartrate = Attribute(float, (SUMMARY,DETAILED,)) #: Average heartrate for lap
    max_heartrate = Attribute(float, (SUMMARY,DETAILED,)) #: Max heartrate for lap
    lap_index = Attribute(int, (SUMMARY,DETAILED)) #: Index of lap
Esempio n. 8
0
class BaseEffort(LoadableEntity):
    """
    Base class for a best effort or segment effort.
    """
    name = Attribute(str, (SUMMARY, DETAILED))  #: The name of the segment
    segment = EntityAttribute(Segment, (SUMMARY, DETAILED))  #: The associated :class:`stravalib.model.Segment` for this effort
    activity = EntityAttribute("Activity", (SUMMARY, DETAILED))  #: The associated :class:`stravalib.model.Activity`
    athlete = EntityAttribute(Athlete, (SUMMARY, DETAILED))  #: The associated :class:`stravalib.model.Athlete`
    kom_rank = Attribute(int, (SUMMARY, DETAILED))  #: 1-10 segment KOM ranking for athlete at time of upload
    pr_rank = Attribute(int, (SUMMARY, DETAILED))  #: 1-3 personal record ranking for athlete at time of upload
    moving_time = TimeIntervalAttribute((SUMMARY, DETAILED))  #: :class:`datetime.timedelta`
    elapsed_time = TimeIntervalAttribute((SUMMARY, DETAILED))  #: :class:`datetime.timedelta`
    start_date = TimestampAttribute((SUMMARY, DETAILED))  #: :class:`datetime.datetime` when effort was started in GMT
    start_date_local = TimestampAttribute((SUMMARY, DETAILED), tzinfo=None)  #: :class:`datetime.datetime` when effort was started in activity timezone for this effort
    distance = Attribute(int, (SUMMARY, DETAILED), units=uh.meters)  #: The distance for this effort.
    average_watts = Attribute(float, (SUMMARY, DETAILED))  #: Average power during effort
    device_watts = Attribute(bool, (SUMMARY, DETAILED))  #: True if the watts are from a power meter, false if estimated
    average_heartrate = Attribute(float, (SUMMARY, DETAILED))   #: Average HR during effort
    max_heartrate = Attribute(float, (SUMMARY, DETAILED))   #: Max HR during effort
    average_cadence = Attribute(float, (SUMMARY, DETAILED))   #: Average cadence during effort
    start_index = Attribute(int, (SUMMARY, DETAILED))  #: The activity stream index of the start of this effort
    end_index = Attribute(int, (SUMMARY, DETAILED))  #: The activity stream index of the end of this effort

    achievements = EntityCollection(SegmentEfforAchievement, (SUMMARY, DETAILED))  #: Undocumented attribute includes list of achievements for this effort.
Esempio n. 9
0
class AthleteStats(BaseEntity):
    """
    Represents a combined set of an Athlete's statistics.
    """
    biggest_ride_distance = Attribute(float, units=uh.meters)  #: Longest ride for athlete.
    biggest_climb_elevation_gain = Attribute(float, units=uh.meters)  #: Greatest single elevation gain for athlete.
    recent_ride_totals = EntityAttribute(ActivityTotals)  #: Recent totals for rides. (:class:`stravalib.model.ActivityTotals`)
    recent_run_totals = EntityAttribute(ActivityTotals)  #: Recent totals for runs. (:class:`stravalib.model.ActivityTotals`)
    ytd_ride_totals = EntityAttribute(ActivityTotals)  #: Year-to-date totals for rides. (:class:`stravalib.model.ActivityTotals`)
    ytd_run_totals = EntityAttribute(ActivityTotals)  #: Year-to-date totals for runs. (:class:`stravalib.model.ActivityTotals`)
    all_ride_totals = EntityAttribute(ActivityTotals)  #: All-time totals for rides. (:class:`stravalib.model.ActivityTotals`)
    all_run_totals = EntityAttribute(ActivityTotals)  #: All-time totals for runs. (:class:`stravalib.model.ActivityTotals`)
Esempio n. 10
0
class Activity(LoadableEntity):
    """
    Represents an activity (ride, run, etc.).
    """
    # "Constants" for types of activities
    RIDE                = "Ride"
    RUN                 = "Run"
    SWIM                = "Swim"
    WALK                = "Walk"

    ALPINESKI           = "AlpineSki"
    BACKCOUNTRYSKI      = "BackcountrySki"
    CANOEING            = "Canoeing"
    CROSSCOUNTRYSKIING  = "CrossCountrySkiing"
    CROSSFIT            = "Crossfit"
    ELLIPTICAL          = "Elliptical"
    HIKE                = "Hike"
    ICESKATE            = "IceSkate"
    INLINESKATE         = "InlineSkate"
    KAYAKING            = "Kayaking"
    KITESURF            = "Kitesurf"
    NORDICSKI           = "NordicSki"
    ROCKCLIMBING        = "RockClimbing"
    ROLLERSKI           = "RollerSki"
    ROWING              = "Rowing"
    SNOWBOARD           = "Snowboard"
    SNOWSHOE            = "Snowshoe"
    STAIRSTEPPER        = "StairStepper"
    STANDUPPADDLING     = "StandUpPaddling"
    SURFING             = "Surfing"
    WEIGHTTRAINING      = "WeightTraining"
    WINDSURF            = "Windsurf"
    WORKOUT             = "Workout"
    YOGA                = "Yoga"

    _comments = None
    _zones = None
    _kudos = None
    _photos = None
    #_gear = None
    _laps = None

    TYPES = ( RIDE, RUN, SWIM, WALK, ALPINESKI, BACKCOUNTRYSKI, CANOEING,
              CROSSCOUNTRYSKIING, CROSSFIT, ELLIPTICAL, HIKE, ICESKATE,
              INLINESKATE, KAYAKING, KITESURF, NORDICSKI, ROCKCLIMBING,
              ROLLERSKI, ROWING, SNOWBOARD, SNOWSHOE, STAIRSTEPPER,
              STANDUPPADDLING, SURFING, WEIGHTTRAINING, WINDSURF, WORKOUT, YOGA)

    guid = Attribute(unicode, (SUMMARY,DETAILED)) #: (undocumented)

    external_id = Attribute(unicode, (SUMMARY,DETAILED)) #: An external ID for the activity (relevant when specified during upload).
    upload_id = Attribute(unicode, (SUMMARY,DETAILED)) #: The upload ID for an activit.
    athlete = EntityAttribute(Athlete, (SUMMARY,DETAILED)) #: The associated :class:`stravalib.model.Athlete` that performed this activity.
    name = Attribute(unicode, (SUMMARY,DETAILED)) #: The name of the activity.
    distance = Attribute(float, (SUMMARY,DETAILED), units=uh.meters) #: The distance for the activity.
    moving_time = TimeIntervalAttribute((SUMMARY,DETAILED)) #: The moving time duration for this activity.
    elapsed_time = TimeIntervalAttribute((SUMMARY,DETAILED)) #: The total elapsed time (including stopped time) for this activity.
    total_elevation_gain = Attribute(float, (SUMMARY,DETAILED), units=uh.meters) #: Total elevation gain for activity.
    type = Attribute(unicode, (SUMMARY,DETAILED)) #: The activity type.
    start_date = TimestampAttribute((SUMMARY,DETAILED)) #: :class:`datetime.datetime` when activity was started in GMT
    start_date_local = TimestampAttribute((SUMMARY,DETAILED), tzinfo=None) #: :class:`datetime.datetime` when activity was started in activity timezone
    timezone = TimezoneAttribute((SUMMARY,DETAILED)) #: The timezone for activity.
    start_latlng = LocationAttribute((SUMMARY,DETAILED))#: The start location (lat/lon :class:`tuple`)
    end_latlng = LocationAttribute((SUMMARY,DETAILED)) #: The end location (lat/lon :class:`tuple`)

    location_city = Attribute(unicode, (SUMMARY,DETAILED)) #: The activity location city
    location_state = Attribute(unicode, (SUMMARY,DETAILED)) #: The activity location state
    location_country = Attribute(unicode, (SUMMARY,DETAILED)) #: The activity location state
    start_latitude = Attribute(float, (SUMMARY,DETAILED)) #: The start latitude
    start_longitude = Attribute(float, (SUMMARY,DETAILED)) #: The start longitude

    achievement_count = Attribute(int, (SUMMARY,DETAILED)) #: How many achievements earned for the activity
    kudos_count = Attribute(int, (SUMMARY,DETAILED)) #: How many kudos received for activity
    comment_count = Attribute(int, (SUMMARY,DETAILED)) #: How many comments  for activity.
    athlete_count = Attribute(int, (SUMMARY,DETAILED)) #: How many other athlete's participated in activity
    photo_count = Attribute(int, (SUMMARY,DETAILED)) #: How many photos linked to activity
    map = EntityAttribute(Map, (SUMMARY,DETAILED)) #: :class:`stravavlib.model.Map` of activity.

    trainer = Attribute(bool, (SUMMARY,DETAILED)) #: Whether activity was performed on a stationary trainer.
    commute = Attribute(bool, (SUMMARY,DETAILED)) #: Whether activity is a commute.
    manual = Attribute(bool, (SUMMARY,DETAILED)) #: Whether activity was manually entered.
    private = Attribute(bool, (SUMMARY,DETAILED)) #: Whether activity is private
    flagged = Attribute(bool, (SUMMARY,DETAILED))  #: Whether activity was flagged.

    gear_id = Attribute(unicode, (SUMMARY,DETAILED)) #: Which bike/shoes were used on activity.
    gear = EntityAttribute(Gear, (DETAILED,))

    average_speed = Attribute(float, (SUMMARY,DETAILED), units=uh.meters_per_second) #: Average speed for activity.
    max_speed = Attribute(float, (SUMMARY,DETAILED), units=uh.meters_per_second) #: Max speed for activity

    truncated = Attribute(int, (SUMMARY,DETAILED)) #: Only present if activity is owned by authenticated athlete, set to 0 if not truncated by privacy zones
    has_kudoed = Attribute(bool, (SUMMARY,DETAILED)) #: If authenticated user has kudoed this activity

    best_efforts = EntityCollection(BestEffort, (DETAILED,)) #: :class:`list` of metric :class:`stravalib.model.BestEffort` summaries
    segment_efforts = EntityCollection(SegmentEffort, (DETAILED,)) #: :class:`list` of :class:`stravalib.model.SegmentEffort` efforts for activity.
    splits_metric = EntityCollection(Split, (DETAILED,)) #: :class:`list` of metric :class:`stravalib.model.Split` summaries (running activities only)
    splits_standard = EntityCollection(Split, (DETAILED,)) #: :class:`list` of standard/imperial :class:`stravalib.model.Split` summaries (running activities only)

    # Undocumented attributes
    average_watts = Attribute(float, (SUMMARY,DETAILED)) #: (undocumented) Average power during activity
    weighted_average_watts = Attribute(int, (SUMMARY,DETAILED)) # rides with power meter data only similar to xPower or Normalized Power
    average_heartrate = Attribute(float, (SUMMARY,DETAILED))  #: (undocumented) Average HR during activity
    max_heartrate = Attribute(int, (SUMMARY,DETAILED))  #: (undocumented) Max HR during activity
    average_cadence = Attribute(float, (SUMMARY,DETAILED))  #: (undocumented) Average cadence during activity
    kilojoules = Attribute(float, (SUMMARY,DETAILED))  #: (undocumented) Kilojoules of energy used during activity
    device_watts = Attribute(bool, (SUMMARY,DETAILED)) # true if the watts are from a power meter, false if estimated
    average_temp = Attribute(int, (SUMMARY,DETAILED)) #: (undocumented) Average temperature (when available from device) during activity.

    calories = Attribute(float, (DETAILED,))  #: Calculation of how many calories burned on activity
    description = Attribute(unicode, (DETAILED,))  #: (undocumented) Description of activity.
    workout_type = Attribute(unicode, (DETAILED,))  #: (undocumented)

    @property
    def comments(self):
        """
        Iterator of :class:`stravalib.model.ActivityComment` objects for this activity.
        """
        if self._comments is None:
            self.assert_bind_client()
            if self.comment_count > 0:
                self._comments = self.bind_client.get_activity_comments(self.id)
            else:
                # Shortcut if we know there aren't any
                self._comments = []
        return self._comments

    @property
    def laps(self):
        """
        Iterator of :class:`stravalib.model.ActivityLap` objects for this activity.
        """
        if self._laps is None:
            self.assert_bind_client()
            self._laps = self.bind_client.get_activity_laps(self.id)
        return self._laps

    @property
    def zones(self):
        """
        :class:`list` of :class:`stravalib.model.ActivityZone` objects for this activity.
        """
        if self._zones is None:
            self.assert_bind_client()
            self._zones = self.bind_client.get_activity_zones(self.id)
        return self._zones

    @property
    def kudos(self):
        """
        :class:`list` of :class:`stravalib.model.ActivityKudos` objects for this activity.
        """
        if self._kudos is None:
            self.assert_bind_client()
            self._kudos = self.bind_client.get_activity_kudos(self.id)
        return self._kudos

    @property
    def photos(self):
        """
        :class:`list` of :class:`stravalib.model.ActivityPhoto` objects for this activity.
        """
        if self._photos is None:
            if self.photo_count > 0:
                self.assert_bind_client()
                self._photos = self.bind_client.get_activity_photos(self.id)
            else:
                self._photos = []
        return self._photos
Esempio n. 11
0
class Athlete(LoadableEntity):
    """
    Represents a Strava athlete.
    """
    firstname = Attribute(unicode, (SUMMARY,DETAILED)) #: Athlete's first name.
    lastname = Attribute(unicode, (SUMMARY,DETAILED)) #: Athlete's last name.
    profile_medium = Attribute(unicode, (SUMMARY,DETAILED)) #: URL to a 62x62 pixel profile picture
    profile = Attribute(unicode, (SUMMARY,DETAILED)) #: URL to a 124x124 pixel profile picture
    city = Attribute(unicode, (SUMMARY,DETAILED)) #: Athlete's home city
    state = Attribute(unicode, (SUMMARY,DETAILED)) #: Athlete's home state
    country = Attribute(unicode, (SUMMARY,DETAILED)) #: Athlete's home country
    sex = Attribute(unicode, (SUMMARY,DETAILED)) #: Athlete's sex ('M', 'F' or null)
    friend = Attribute(unicode, (SUMMARY,DETAILED)) #: 'pending', 'accepted', 'blocked' or 'null' the authenticated athlete's following status of this athlete
    follower = Attribute(unicode, (SUMMARY,DETAILED)) #: 'pending', 'accepted', 'blocked' or 'null' this athlete's following status of the authenticated athlete
    premium = Attribute(bool, (SUMMARY,DETAILED)) #: Whether athlete is a premium member (true/false)

    created_at = TimestampAttribute((SUMMARY,DETAILED)) #: :class:`datetime.datetime` when athlete record was created.
    updated_at = TimestampAttribute((SUMMARY,DETAILED)) #: :class:`datetime.datetime` when athlete record was last updated.

    approve_followers = Attribute(bool, (SUMMARY,DETAILED)) #: Whether athlete has elected to approve followers

    follower_count = Attribute(int, (DETAILED,)) #: (detailed-only) How many people are following this athlete
    friend_count = Attribute(int, (DETAILED,)) #: (detailed-only) How many people is this athlete following
    mutual_friend_count = Attribute(int, (DETAILED,)) #: (detailed-only) How many people are both following and being followed by this athlete
    date_preference = Attribute(unicode, (DETAILED,)) #: (detailed-only) Athlete's preferred date representation (e.g. "%m/%d/%Y")
    measurement_preference = Attribute(unicode, (DETAILED,)) #: (detailed-only) How athlete prefers to see measurements (i.e. "feet" (or what "meters"?))
    email = Attribute(unicode, (DETAILED,)) #: (detailed-only)  Athlete's email address

    clubs = EntityCollection(Club, (DETAILED,)) #: (detailed-only) Which clubs athlete belongs to. (:class:`list` of :class:`stravalib.model.Club`)
    bikes = EntityCollection(Bike, (DETAILED,)) #: (detailed-only) Which bikes this athlete owns. (:class:`list` of :class:`stravalib.model.Bike`)
    shoes = EntityCollection(Shoe, (DETAILED,)) #: (detailed-only) Which shoes this athlete owns. (:class:`list` of :class:`stravalib.model.Shoe`)

    # Some undocumented summary & detailed  attributes
    ytd_run_totals = EntityAttribute(ActivityTotals, (SUMMARY, DETAILED)) #: (undocumented) Year-to-date totals for runs. (:class:`stravalib.model.ActivityTotals`)
    recent_run_totals = EntityAttribute(ActivityTotals, (SUMMARY, DETAILED)) #: (undocumented) Recent totals for runs. (:class:`stravalib.model.ActivityTotals`)
    all_run_totals = EntityAttribute(ActivityTotals, (SUMMARY, DETAILED)) #: (undocumented) All-time totals for runs. (:class:`stravalib.model.ActivityTotals`)

    ytd_ride_totals = EntityAttribute(ActivityTotals, (SUMMARY, DETAILED)) #: (undocumented) Year-to-date totals for rides. (:class:`stravalib.model.ActivityTotals`)
    recent_ride_totals = EntityAttribute(ActivityTotals, (SUMMARY, DETAILED)) #: (undocumented) Recent totals for rides. (:class:`stravalib.model.ActivityTotals`)
    all_ride_totals = EntityAttribute(ActivityTotals, (SUMMARY, DETAILED)) #: (undocumented) All-time totals for rides. (:class:`stravalib.model.ActivityTotals`)

    super_user = Attribute(bool, (SUMMARY,DETAILED)) #: (undocumented) Whether athlete is a super user (not
    biggest_ride_distance = Attribute(float, (SUMMARY,DETAILED), units=uh.meters) #: (undocumented) Longest ride for athlete.
    biggest_climb_elevation_gain = Attribute(float, (SUMMARY,DETAILED), units=uh.meters) #: (undocumented) Greatest single elevation gain for athlete.

    email_language = Attribute(unicode, (SUMMARY,DETAILED)) #: The user's preferred lang/locale (e.g. en-US)

    # A bunch more undocumented detailed-resolution attribs
    weight = Attribute(float, (DETAILED,), units=uh.kg) #: (undocumented, detailed-only)  Athlete's configured weight.
    max_heartrate = Attribute(float, (DETAILED,)) #: (undocumented, detailed-only) Athlete's configured max HR

    username = Attribute(unicode, (DETAILED,)) #:  (undocumented, detailed-only) Athlete's username.
    description = Attribute(unicode, (DETAILED,)) #:  (undocumented, detailed-only) Athlete's personal description
    instagram_username = Attribute(unicode, (DETAILED,)) #:  (undocumented, detailed-only) Associated instagram username

    offer_in_app_payment = Attribute(bool, (DETAILED,)) #:  (undocumented, detailed-only)
    global_privacy = Attribute(bool, (DETAILED,)) #:  (undocumented, detailed-only) Whether athlete has global privacy enabled.
    receive_newsletter = Attribute(bool, (DETAILED,)) #:  (undocumented, detailed-only) Whether athlete has elected to receive newsletter
    email_kom_lost = Attribute(bool, (DETAILED,)) #:  (undocumented, detailed-only) Whether athlete has elected to receive emails when KOMs are lost.
    dateofbirth = DateAttribute((DETAILED,)) #:  (undocumented, detailed-only) Athlete's date of birth
    facebook_sharing_enabled = Attribute(bool, (DETAILED,)) #:  (undocumented, detailed-only) Whether Athlete has enabled sharing on Facebook
    ftp = Attribute(unicode, (DETAILED,))  # (undocumented, detailed-only)
    profile_original = Attribute(unicode, (DETAILED,)) #: (undocumented, detailed-only)
    premium_expiration_date = Attribute(int, (DETAILED,)) #:  (undocumented, detailed-only) When does premium membership expire (:class:`int` unix epoch)
    email_send_follower_notices = Attribute(bool, (DETAILED,)) #: (undocumented, detailed-only)
    plan = Attribute(unicode, (DETAILED,)) #: (undocumented, detailed-only)
    agreed_to_terms = Attribute(unicode, (DETAILED,)) #: (undocumented, detailed-only) Whether athlete has agreed to terms
    follower_request_count = Attribute(int, (DETAILED,)) #: (undocumented, detailed-only) How many people have requested to follow this athlete
    email_facebook_twitter_friend_joins = Attribute(bool, (DETAILED,)) #: (undocumented, detailed-only) Whether athlete has elected to receve emails when a twitter or facebook friend joins Strava
    receive_kudos_emails = Attribute(bool, (DETAILED,)) #: (undocumented, detailed-only) Whether athlete has elected to receive emails on kudos
    receive_follower_feed_emails = Attribute(bool, (DETAILED,)) #: (undocumented, detailed-only) Whether athlete has elected to receive emails on new followers
    receive_comment_emails = Attribute(bool, (DETAILED,)) #: (undocumented, detailed-only) Whether athlete has elected to receive emails on activity comments

    sample_race_distance = Attribute(int, (DETAILED,)) # (undocumented, detailed-only)
    sample_race_time = Attribute(int, (DETAILED,)) # (undocumented, detailed-only)

    _friends = None
    _followers = None


    def __repr__(self):
        fname = self.firstname and self.firstname.encode('utf-8')
        lname = self.lastname and self.lastname.encode('utf-8')
        return '<Athlete id={id} firstname={fname} lastname={lname}>'.format(id=self.id,
                                                                             fname=fname,
                                                                             lname=lname)
    @property
    def friends(self):
        """
        Iterator of :class:`stravalib.model.Athlete` objects for this activity.
        """
        if self._friends is None:
            self.assert_bind_client()
            if self.friend_count > 0:
                self._friends = self.bind_client.get_athlete_friends(self.id)
            else:
                # Shortcut if we know there aren't any
                self._friends = []
        return self._friends

    @property
    def followers(self):
        """
        Iterator of :class:`stravalib.model.Athlete` objects for this activity.
        """
        if self._followers is None:
            self.assert_bind_client()
            if self.follower_count > 0:
                self._followers = self.bind_client.get_athlete_followers(self.id)
            else:
                # Shortcut if we know there aren't any
                self._followers = []
        return self._followers