Lo and behold cmdbikes, a client to get bike share information right at your terminal.
You can install it by
$ pip install cmdbikes
Looks good, but why?
Most of the time I spend on Citybikes is either working on pybikes or the API but rarely using it, aka eating my own dog food. For a while I thought the API syntax was sufficiently straightforward to not even need an interface for a language. Turns out I was wrong: the v2 syntax of the API has some caveats and having an abstraction over it helps make everything easier and cleaner.
I first noticed this whilst attending an open data Hackatiño organized by Coruña Dixital and GPUL. Even though I was invited to just be part of the jury, being in a room full of people coding made me want to join in on the fun and prompted me to start working on a telegram bot for Citybikes (which I plan to release on the following weeks/months).
It’s impossible to assume an API is going to have a perfect representation, so abstracting it in some way will always hide the ugly parts and will make building API consumers easier. There are already some interfaces for other languages but I haven’t find any for python, most probably due to the fact that requests is an ubiquitous library that makes things much easier.
Precisely for that reason I decided to create python-citybikes, an interface for the Citybikes API that makes working with it a real pleasure. Some things might change so I will keep it under 0.1 for a while.
The cool part of python-citybikes is that it abstracts all code relating to requesting stuff and from the code point of view, all resources are accessed just when they are needed and saved for later reuse (though an update on the data can be forced).
This snippet, for instance, gives you the first station of the first network on the API.
import citybikes client = citybikes.Client() # This will do a request to https://api.citybik.es/v2/networks a_network = next(iter(client.networks)) # This will do a request to https://api.citybik.es/v2/networks/foobar an_station = next(iter(a_network.stations)) # This will do no further requests len(client.networks) # Neither will this len(an_station.stations)
This one instantiates a network by its id
import citybikes client = citybikes.Client() bicing = client.Network(client, uid='bicing') # This will do a request to https://api.citybik.es/v2/networks/bicing print(bicing) # This will not do further requests len(bicing.stations)
Another good thing of having an interface for an API is that it allows the addition of utility functions. In this case, I added a near function that will return resources based on distance to a given point.
import citybikes # Gives you 5 nearest stations on a given lat / lng lat, lng = 40.7831, -73.9712 client = citybikes.Client() network = next(iter(client.networks.near(lat, lng))) stations = list(network.stations.near(lat, lng))[:5]
See where this is going? The best way to assert that an interface for an API is useful is by writing a simple terminal client to play with the API, and that’s what cmdbikes is. Even though it would be fairly easy to bundle cmdbikes together with python-citybikes, I have decided against it. This way I get to feel what’s like to install and use it separately.
This is not the first time I think about writing a terminal client for Citybikes. Some years ago I did a live coding exercise on a talk at pycones 2014 precisely about that. Although, since the talk was about pybikes, the exercise used pybikes and not the API directly.
Turns out writing a proper package and publishing on pypi is not that difficult (at least anymore) and it’s straightforward to write new python 3 code that is compatible with python 2.7 (at least for a simple case like this).