From a05ebe691bc14376251282b68d393c1813a889ed Mon Sep 17 00:00:00 2001 From: Phyks Date: Sat, 26 Sep 2015 00:04:41 +0200 Subject: [PATCH] Initial commit --- .gitignore | 3 + README.md | 16 +++++ config.py.example | 7 ++ velib.py | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config.py.example create mode 100755 velib.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..943ff0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +config.py +data.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..99e1c3d --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +VelibDataSet +============ + +This code can be used to dump periodically all the available data from the Velib API. These data are under an OpenData license. (Velib is a bike sharing system in Paris). + +## Usage + +* Clone this repo. +* Create an account to access the velib API: https://developer.jcdecaux.com/#/home. +* Copy `config.py.example` to `config.py` and set your API key there. +* Run `velib.py`. + +## Links + +* Velib API: https://developer.jcdecaux.com/#/home +* Velib website: http://velib.paris/ diff --git a/config.py.example b/config.py.example new file mode 100644 index 0000000..7ce8222 --- /dev/null +++ b/config.py.example @@ -0,0 +1,7 @@ +# Global common endpoints +api_endpoint = "https://api.jcdecaux.com/vls/v1/stations" + +# Configuration +debug = True +api_key = "SET_YOUR_API_KEY_HERE" +contract = "Paris" diff --git a/velib.py b/velib.py new file mode 100755 index 0000000..8969149 --- /dev/null +++ b/velib.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +from config import * + +import json +import requests +import sqlite3 +import time + + +def db_init(): + """ + Initialize a database connection, initialize the tables. + + Returns a new connection. + """ + conn = sqlite3.connect("data.db") + c = conn.cursor() + # TODO: Init tables + c.execute("CREATE TABLE IF NOT EXISTS stations(" + + "id INTEGER PRIMARY KEY, " + + "name TEXT, " + + "address TEXT, " + + "latitude REAL, " + + "longitude REAL, " + + "banking INTEGER, " + + "bonus INTEGER, " + + "bike_stands INTEGER)") + c.execute("CREATE TABLE IF NOT EXISTS stationsstats(" + + "station_id INTEGER, " + + "available_bikes INTEGER, " + + "free_stands INTEGER, " + + "status TEXT, " + + "updated INTEGER, " + + "FOREIGN KEY(station_id) REFERENCES stations(id) ON DELETE CASCADE)") + c.execute("CREATE TABLE IF NOT EXISTS stationsevents(" + + "station_id INTEGER PRIMARY KEY, " + + "timestamp INTEGER, " + + "event TEXT, " + + "FOREIGN KEY(station_id) REFERENCES stations(id) ON DELETE CASCADE)") + conn.commit() + return conn + + +def retrieve_stations(): + """ + Retrieve list of stations. + + Returns the new stations list. + """ + # Fetch the endpoint + r = requests.get(api_endpoint, + params={"apiKey": api_key, "contract": contract}) + # Handle the JSON response + stations_list_json = json.loads(r.text) + stations_list = [] + for station in stations_list_json: + stations_list.append(station) + return stations_list + + +def update_stations(): + """ + Update the stored station list. + """ + conn = db_init() + c = conn.cursor() + stations = retrieve_stations() + database_stations = {i[0]: i + for i in + c.execute("SELECT id, name, address, latitude, longitude, banking, bonus, bike_stands FROM stations").fetchall()} + for station in stations: + try: + # Get old station entry if it exists + old_station = database_stations[station["number"]] + # Diff the two stations + event = [] + if station["name"] != old_station[1]: + event.append({"key": "name", + "old_value": old_station[1], + "new_value": station["name"]}) + if station["address"] != old_station[2]: + event.append({"key": "address", + "old_value": old_station[2], + "new_value": station["address"]}) + if station["position"]["lat"] != old_station[3]: + event.append({"key": "latitude", + "old_value": old_station[3], + "new_value": station["position"]["lat"]}) + if station["position"]["lng"] != old_station[4]: + event.append({"key": "longitude", + "old_value": old_station[4], + "new_value": station["position"]["lng"]}) + if station["banking"] != old_station[5]: + event.append({"key": "banking", + "old_value": old_station[5], + "new_value": station["banking"]}) + if station["bonus"] != old_station[6]: + event.append({"key": "bonus", + "old_value": old_station[6], + "new_value": station["bonus"]}) + if station["bike_stands"] != old_station[7]: + event.append({"key": "bike_stands", + "old_value": old_station[7], + "new_value": station["bike_stands"]}) + # If diff was found + if len(event) > 0: + # Update + c.execute("UPDATE " + + "stations " + + "SET name=?, address=?, latitude=?, longitude=?, " + + "banking=?, bonus=?, bike_stands=? WHERE id=?)", + (station["name"], + station["address"], + station["position"]["lat"], + station["position"]["lng"], + station["banking"], + station["bonus"], + station["bike_stands"], + station["number"])) + # And insert event in the table + c.execute("INSERT INTO " + + "stationsevents(station_id, timestamp, event) " + + "VALUES(?, ?, ?)", + (station["number"], + time.time(), + json.dumps(event))) + except KeyError: + c.execute("INSERT INTO " + + "stations(id, name, address, latitude, longitude, banking, bonus, bike_stands) " + + "VALUES(?, ?, ?, ?, ?, ?, ?, ?)", + (station["number"], + station["name"], + station["address"], + station["position"]["lat"], + station["position"]["lng"], + station["banking"], + station["bonus"], + station["bike_stands"])) + + c.execute("INSERT INTO " + + "stationsstats(station_id, available_bikes, free_stands, status, updated) " + + "VALUES(?, ?, ?, ?, ?)", + (station["number"], + station["available_bikes"], + station["available_bike_stands"], + station["status"], + station["last_update"])) + conn.commit() + + +def main(): + """ + Handle main operations. + """ + # Get updated list of stations + update_stations() + + +if __name__ == "__main__": + main()