Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/ghisprince/stravalib
Browse files Browse the repository at this point in the history
  • Loading branch information
hozn committed Jul 13, 2014
2 parents 65af848 + 94f876e commit 717f4fc
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 22 deletions.
56 changes: 41 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,40 @@ The new codebase supports only [version 3 API](http://strava.github.io/api/) of
This is currently under active development. Parts have been tested, but some aspects may be broken.

The stravalib project aims to provide a simple API for interacting with Strava v3 web services, in particular
abstracting the v3 REST API around a rich and easy-to-use object model and providing support for date/time/temporarl attributes
and quantities with units (using the [python units library](http://pypi.python.org/pypi/units)).
abstracting the v3 REST API around a rich and easy-to-use object model and providing support for date/time/temporal attributes
and quantities with units (using the [python units library](http://pypi.python.org/pypi/units)).

See the [online documentation](http://pythonhosted.org/stravalib/) for more comprehensive documentation.

## Dependencies

* Python 2.6+. (This is intended to work with Python 3 using 2to3, but is being developed on Python 2.7)
* Setuptools/distribute for installing dependencies
* Other python libraries (installed automatically when using pip/easy_install): requests, pytz, units, python-dateutil
* Other python libraries (installed automatically when using pip/easy_install): requests, pytz, units, python-dateutil

## Installation

The package is avialable on PyPI to be installed using easy_install or pip:
The package is available on PyPI to be installed using easy_install or pip:

``` none
shell$ pip install stravalib
```

(Installing in a [virtual environment](https://pypi.python.org/pypi/virtualenv) is always recommended.)

Of course, by itself this package doesn't do much; it's a library. So it is more likely that you will
list this package as a dependency in your own `install_requires` directive in `setup.py`. Or you can
Of course, by itself this package doesn't do much; it's a library. So it is more likely that you will
list this package as a dependency in your own `install_requires` directive in `setup.py`. Or you can
download it and explore Strava content in your favorite python REPL.

## Basic Usage

Please take a look at the source (in particular the stravalib.client.Client class, if you'd like to play around with the
Please take a look at the source (in particular the stravalib.client.Client class, if you'd like to play around with the
API. Most of the Strava API is implemented at this point; however, certain features such as streams are still on the
to-do list.

### Authentication

In order to make use of this library, you will need to have access keys for one or more Strava users. This
In order to make use of this library, you will need to have access keys for one or more Strava users. This
effectively requires you to run a webserver; this is outside the scope of this library, but stravalib does provide helper methods to make it easier.

```python
Expand All @@ -67,10 +67,6 @@ print("For {id}, I now have an access token {token}".format(id=athlete.id, token
(This is a glimpse into what you can do.)

```python
from stravalib.client import Client

client = Client(access_token='xxxxxxxxx')

# Currently-authenticated (based on provided token) athlete
# Will have maximum detail exposed (resource_state=3)
curr_athlete = client.get_athlete()
Expand All @@ -85,11 +81,41 @@ activity = client.get_activity(123)
# otherwise summary-level detail.
```

### Streams

Streams represent the raw data of the uploaded file. Activities, efforts, and
segments all have streams. There are many types of streams, if activity does
not have requested stream type, it will not be part of returned set.

```python

# Activities have many streams, request desired type
streams = client.get_activity_streams(123, types=['altitude',], resolution='low')

# The return is an enum type object
print(next(streams).data)

# You can request many stream types
types = ['time', 'latlng', 'altitude', 'heartrate', 'temp', ]

# Fetch many streams for an activity
streams = {}
for stream in client.get_activity_streams(123, types=types, resolution='medium'):
# each stream object has a type property
streams[stream.type] = stream

# Length of stream.data is consitent for activity
for t, latlng in zip(streams['time'].data, streams['latlng'].data):
print("{} : {}".format(t, latlng))

```


### Working with Units

stravalib uses the [python units library](https://pypi.python.org/pypi/units/) to facilitate working
with the values in the API that have associated units (e.g. distance, speed). You can use the units library
directly
directly
stravalib.unithelper module for shortcuts

```python
Expand All @@ -108,7 +134,7 @@ print(unithelper.miles(activity.distance))

# And to get the number:
num_value = float(unithelper.miles(activity.distance))
# Or:
# Or:
num_value = unithelper.miles(activity.distance).num
```

Expand Down
194 changes: 192 additions & 2 deletions stravalib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,25 @@ def get_activity_photos(self, activity_id):
bind_client=self,
result_fetcher=result_fetcher)


def get_activity_laps(self, activity_id):
"""
Gets the laps from an activity.
http://strava.github.io/api/v3/activities/#laps
:param activity_id: The activity for which to fetch laps.
:return: An iterator of :class:`stravalib.model.ActivityLaps` objects.
:rtype: :class:`BatchedResultsIterator`
"""
result_fetcher = functools.partial(self.protocol.get,
'/activities/{id}/laps',
id=activity_id)

return BatchedResultsIterator(entity=model.ActivityLaps,
bind_client=self,
result_fetcher=result_fetcher)

def get_gear(self, gear_id):
"""
Get details for an item of gear.
Expand Down Expand Up @@ -774,10 +793,177 @@ def explore_segments(self, bounds, activity_type=None, min_cat=None, max_cat=Non
params['max_cat'] = max_cat

raw = self.protocol.get('/segments/explore', **params)
return [model.SegmentExplorerResult.deserialize(v, bind_client=self) for v in raw['segments']]
return [model.SegmentExplorerResult.deserialize(v, bind_client=self)
for v in raw['segments']]


def get_activity_streams(self, activity_id, types=None,
resolution=None, series_type=None):
"""
Returns an streams for an activity.
Streams represent the raw data of the uploaded file. External
applications may only access this information for activities owned
by the authenticated athlete.
Streams are available in 11 different types. If the stream is not
available for a particular activity it will be left out of the request
results.
Streams types are: time, latlng, distance, altitude, velocity_smooth,
heartrate, cadence, watts, temp, moving, grade_smooth
http://strava.github.io/api/v3/streams/#activity
:param activity_id: The ID of activity.
:type: int
:param types: (optional) A list of the the types of streams to fetch.
:type types: list
:param resolution: (optional, default is 'all') indicates desired number
of data points. 'low' (100), 'medium' (1000),
'high' (10000) or 'all'.
:type resolution: str
:param series_type: (optional, default is 'distance'. Relevant only if
using resolution either 'time' or 'distance'.
Used to index the streams if the stream is being
reduced.
:type series_type: str
:rtype: :class:`stravalib.model.Stream`
"""

# stream are comma seperated list
if types is not None:
types= ",".join(types)

params = {}
if resolution is not None:
params["resolution"] = resolution

if series_type is not None:
params["series_type"] = series_type

result_fetcher = functools.partial(
self.protocol.get,
'/activities/{id}/streams/{types}'.format(id=activity_id,
types=types),
**params)

return BatchedResultsIterator(entity=model.Stream,
bind_client=self,
result_fetcher=result_fetcher)

def get_effort_streams(self, effort_id, types=None,
resolution=None, series_type=None):
"""
Returns an streams for an effort.
Streams represent the raw data of the uploaded file. External
applications may only access this information for activities owned
by the authenticated athlete.
Streams are available in 11 different types. If the stream is not
available for a particular activity it will be left out of the request
results.
Streams types are: time, latlng, distance, altitude, velocity_smooth,
heartrate, cadence, watts, temp, moving, grade_smooth
http://strava.github.io/api/v3/streams/#effort
:param effort_id: The ID of effort.
:type: int
:param types: (optional) A list of the the types of streams to fetch.
:type types: list
:param resolution: (optional, default is 'all') indicates desired number
of data points. 'low' (100), 'medium' (1000),
'high' (10000) or 'all'.
:type resolution: str
:param series_type: (optional, default is 'distance'. Relevant only if
using resolution either 'time' or 'distance'.
Used to index the streams if the stream is being
reduced.
:type series_type: str
:rtype: :class:`stravalib.model.Stream`
"""

# stream are comma seperated list
if types is not None:
types= ",".join(types)

params = {}
if resolution is not None:
params["resolution"] = resolution

if series_type is not None:
params["series_type"] = series_type

result_fetcher = functools.partial(
self.protocol.get,
'/segment_efforts/{id}/streams/{types}'.format(id=effort_id,
types=types),
**params)

return BatchedResultsIterator(entity=model.Stream,
bind_client=self,
result_fetcher=result_fetcher)


def get_segment_streams(self, segment_id, types=None,
resolution=None, series_type=None):
"""
Returns an streams for a segment.
Streams represent the raw data of the uploaded file. External
applications may only access this information for activities owned
by the authenticated athlete.
Streams are available in 11 different types. If the stream is not
available for a particular activity it will be left out of the request
results.
Streams types are: time, latlng, distance, altitude, velocity_smooth,
heartrate, cadence, watts, temp, moving, grade_smooth
http://strava.github.io/api/v3/streams/#effort
:param segment_id: The ID of segment.
:type: int
:param types: (optional) A list of the the types of streams to fetch.
:type types: list
:param resolution: (optional, default is 'all') indicates desired number
of data points. 'low' (100), 'medium' (1000),
'high' (10000) or 'all'.
:type resolution: str
:param series_type: (optional, default is 'distance'. Relevant only if
using resolution either 'time' or 'distance'.
Used to index the streams if the stream is being
reduced.
:type series_type: str
:rtype: :class:`stravalib.model.Stream`
"""

# stream are comma seperated list
if types is not None:
types= ",".join(types)

params = {}
if resolution is not None:
params["resolution"] = resolution

if series_type is not None:
params["series_type"] = series_type

result_fetcher = functools.partial(
self.protocol.get,
'/segments/{id}/streams/{types}'.format(id=segment_id,
types=types),
**params)

return BatchedResultsIterator(entity=model.Stream,
bind_client=self,
result_fetcher=result_fetcher)


# TODO: Streams

class BatchedResultsIterator(object):
"""
Expand Down Expand Up @@ -806,6 +992,7 @@ def __init__(self, entity, result_fetcher, bind_client=None, limit=None, per_pag
:param per_page: How many rows to fetch per page (default is 200).
:type per_page: int
"""

self.log = logging.getLogger('{0.__module__}.{0.__name__}'.format(self.__class__))
self.entity = entity
self.bind_client = bind_client
Expand All @@ -814,6 +1001,8 @@ def __init__(self, entity, result_fetcher, bind_client=None, limit=None, per_pag

if per_page is not None:
self.per_page = per_page
#elif limit is not None:
# self.per_page = limit
else:
self.per_page = self.default_per_page

Expand All @@ -834,6 +1023,7 @@ def _fill_buffer(self):
raise StopIteration

raw_results = self.result_fetcher(page=self._page, per_page=self.per_page)

entities = []
for raw in raw_results:
entities.append(self.entity.deserialize(raw, bind_client=self.bind_client))
Expand Down
Loading

0 comments on commit 717f4fc

Please sign in to comment.