diff --git a/README.md b/README.md
index bf652f5..ca233ab 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,10 @@
This is a set of libraries to gather data from TBM.
-They all work around [the dynamic map](http://plandynamique.infotbm.com).
-
-# What's TBM?
+## What's TBM?
TBM is a french compagny that operates Bordeaux's public transportation.
-# Licence
+## Licence
-These libraries are under GNU GPL v3. See [license](LICENSE) for more details.
\ No newline at end of file
+These libraries are under GNU GPL v3. See [license](LICENSE) for more details.
diff --git a/libs.py b/libs.py
deleted file mode 100644
index e823917..0000000
--- a/libs.py
+++ /dev/null
@@ -1,30 +0,0 @@
-'''
-Common libraries
-'''
-
-from json import loads as read_json
-from urllib import request
-from urllib.error import HTTPError
-
-
-def get_data_from_json (url):
- '''
- gets data from json at url
- '''
- opener = request.build_opener ()
- try:
- return (read_json (opener.open (url).read ().decode ('utf8')))
- except HTTPError:
- return (None)
-
-
-def hms2seconds (hhmmss):
- '''
- Convert H:M:S string to time in seconds
- '''
- try:
- cut_string = hhmmss.split (':')
- cut_time = (int (cut_string [0]), int (cut_string [1]), int (cut_string [2]))
- return (3600 * cut_time [0] + 60 * cut_time [1] + cut_time [2])
- except (IndexError, ValueError, TypeError):
- return (0)
\ No newline at end of file
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..f38a923
--- /dev/null
+++ b/main.py
@@ -0,0 +1,30 @@
+from src.stop import *
+import sys
+from datetime import datetime
+from gmplot import gmplot
+import imgkit
+
+
+if __name__ == "__main__":
+ for word in sys.argv[1:]:
+ for area in search_stop_by_name(word):
+ for stop in show_stops_from_ref(area["ref"])["stop_points"]:
+ for route in stop["routes"]:
+ if "Tram" in route["line_human"]:
+ sr = StopRoute(stop["id"], route["line_id"])
+ line = sr.get_line()
+ v = line.get_vehicle(0)
+ if v.is_realtime:
+ print(
+ str(v.wait_time_text)
+ + " ("
+ + datetime.fromtimestamp(v.arrival).strftime("%H:%M")
+ + ") → "
+ + v.destination
+ + " "
+ + str(v.location)
+ )
+ gmap = gmplot.GoogleMapPlotter(v.location[0], v.location[1], 13)
+ gmap.marker(v.location[0], v.location[1], "cornflowerblue")
+ gmap.draw("map.html")
+ imgkit.from_file("map.html", "map.pdf")
diff --git a/map.html b/map.html
new file mode 100644
index 0000000..96f4b8a
--- /dev/null
+++ b/map.html
@@ -0,0 +1,31 @@
+
+
+
+
+Google Maps - gmplot
+
+
+
+
+
+
+
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..07d40c1
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,126 @@
+[[package]]
+name = "certifi"
+version = "2021.10.8"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "charset-normalizer"
+version = "2.0.12"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.5.0"
+
+[package.extras]
+unicode_backport = ["unicodedata2"]
+
+[[package]]
+name = "gmplot"
+version = "1.4.1"
+description = "A matplotlib-like interface to plot data with Google Maps."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+requests = "*"
+
+[[package]]
+name = "idna"
+version = "3.3"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "imgkit"
+version = "1.2.2"
+description = "Wkhtmltopdf python wrapper to convert html to image using the webkit rendering engine and qt"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "requests"
+version = "2.27.1"
+description = "Python HTTP for Humans."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
+idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "urllib3"
+version = "1.26.9"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+
+[package.extras]
+brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
+secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
+[metadata]
+lock-version = "1.1"
+python-versions = "^3.9"
+content-hash = "38d635e600115509aba955aaee5bd0c5d91d0a621f114d3b1723ca3471bb2900"
+
+[metadata.files]
+certifi = [
+ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
+ {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
+]
+charset-normalizer = [
+ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
+ {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
+]
+gmplot = [
+ {file = "gmplot-1.4.1-py3-none-any.whl", hash = "sha256:873bcecd90f0e63a73b2876e15a2225145d726f3b7cfb47a363a3a5459fc778d"},
+ {file = "gmplot-1.4.1.tar.gz", hash = "sha256:cfe72d251c17b5c05043169d121a9728554bf65b8c96760ce9fabb6d269c6667"},
+]
+idna = [
+ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
+ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
+]
+imgkit = [
+ {file = "imgkit-1.2.2-py3-none-any.whl", hash = "sha256:304de76b83fe2dbb1f70194f5f88e27e46bdc352bdc9c9ec5cf961e986300fc8"},
+ {file = "imgkit-1.2.2.tar.gz", hash = "sha256:382e7b3c280de4a472d0d786d9cea7bb9d6d8df2c2310ad07d4fe440ad1cb218"},
+]
+requests = [
+ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
+ {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+urllib3 = [
+ {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"},
+ {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..9ab5494
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,16 @@
+[tool.poetry]
+name = "info_tbm"
+version = "0.1.0"
+description = ""
+authors = ["mdeboute "]
+
+[tool.poetry.dependencies]
+python = "^3.9"
+gmplot = "^1.4.1"
+imgkit = "^1.2.2"
+
+[tool.poetry.dev-dependencies]
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/src/libs.py b/src/libs.py
new file mode 100644
index 0000000..9f2da9f
--- /dev/null
+++ b/src/libs.py
@@ -0,0 +1,32 @@
+"""
+Common libraries
+"""
+
+from json import loads as read_json
+from urllib import request
+from urllib.error import HTTPError
+
+
+def get_data_from_json(url):
+ """
+ Gets data from json at url
+ """
+
+ opener = request.build_opener()
+ try:
+ return read_json(opener.open(url).read().decode("utf8"))
+ except HTTPError:
+ return None
+
+
+def hms2seconds(hhmmss):
+ """
+ Convert H:M:S string to time in seconds
+ """
+
+ try:
+ cut_string = hhmmss.split(":")
+ cut_time = (int(cut_string[0]), int(cut_string[1]), int(cut_string[2]))
+ return 3600 * cut_time[0] + 60 * cut_time[1] + cut_time[2]
+ except (IndexError, ValueError, TypeError):
+ return None
diff --git a/src/stop.py b/src/stop.py
new file mode 100644
index 0000000..a226a2c
--- /dev/null
+++ b/src/stop.py
@@ -0,0 +1,245 @@
+"""
+Provides stop information
+"""
+
+from src.libs import get_data_from_json, hms2seconds
+from time import time
+from urllib.parse import quote
+from re import search
+
+
+STOP_URL = "https://ws.infotbm.com/ws/1.0/get-schedule/%s"
+INFO_URL = "https://ws.infotbm.com/ws/1.0/network/stoparea-informations/%s"
+SCHEDULE_URL = "https://ws.infotbm.com/ws/1.0/get-realtime-pass/%d/%s"
+
+line_types = (
+ "Tram",
+ "Corol",
+ "Lianes",
+ "Ligne",
+ "Bus Relais",
+ "Citéis",
+)
+line_translate = {
+ "Tram A": "A",
+ "Tram B": "B",
+ "Tram C": "C",
+ "Tram D": "D",
+ "TBNight": "58",
+ "BAT3": "69",
+}
+
+
+def search_stop_by_name(keyword):
+ """
+ Finds the reference of a stop name
+
+ Format of data returned by the site
+ [
+ {
+ id: str, named ref thereafter
+ name: str
+ type: str, but we only manage "stop_area"
+ city: str
+ },
+ ]
+ """
+
+ d = get_data_from_json(STOP_URL % quote(keyword))
+ r = []
+ for i in d:
+ if i["type"] == "stop_area":
+ r.append(
+ {
+ "name": i["name"],
+ "city": i["city"],
+ "ref": i["id"],
+ }
+ )
+ return r
+
+
+def show_stops_from_ref(ref):
+ """
+ Displays the list of stops of a reference given by search_stop_name
+
+ Format of data returned by the site
+ {
+ id: str, content of given ref variable
+ name: str
+ latitude: str, convertible in float
+ longitude: str, convertible in float
+ city: str
+ hasWheelchairBoarding: bool, wheelchair accessibility
+ stopPoints: [
+ id: str
+ name: str, once again the name of the stop
+ routes: [
+ {
+ id: str
+ name: str, name of the terminus
+ line: {
+ name: str, human readable
+ }
+ }
+ ]
+ ]
+ }
+ """
+
+ d = get_data_from_json(INFO_URL % quote(ref))
+ r = {
+ "ref": d["id"],
+ "name": d["name"],
+ "latitude": float(d["latitude"]),
+ "longitude": float(d["longitude"]),
+ "city": d["city"],
+ "stop_points": [],
+ }
+ for i in d["stopPoints"]:
+ s = {
+ "name": i["name"],
+ "routes": [],
+ }
+ s["id"] = int(search("[0-9]+$", i["id"]).group())
+ for j in i["routes"]:
+ rte = {
+ "terminus": j["name"],
+ "line_human": j["line"]["name"],
+ }
+ add = False
+ if rte["line_human"] in line_translate:
+ line_id = line_translate[rte["line_human"]]
+ add = True
+ else:
+ try:
+ line_id = search("[0-9]+$", rte["line_human"]).group()
+ except AttributeError:
+ continue
+ line_id = "%02d" % int(line_id)
+ for i in line_types:
+ if rte["line_human"][0 : len(i)] == i:
+ add = True
+ break
+ if add:
+ rte["line_id"] = line_id
+ s["routes"].append(rte)
+ if s["routes"] != []:
+ r["stop_points"].append(s)
+ return r
+
+
+class StopRoute:
+ """
+ Retrieves information about a stop
+
+ Format of data returned by the site
+ {
+ destinations: {
+ : [
+ {
+ destination_name: str
+ realtime: 1 if followed, 0 otherwise
+ vehicle_id: str
+ vehicle_lattitude: float
+ vehicle_longitude: float
+ waittime: HH:MM:SS
+ waittime_text: str, human readable
+ },
+ ]
+ }
+ }
+ """
+
+ def __init__(
+ self,
+ number,
+ line,
+ autoupdate_at_creation=True,
+ autoupdate=False,
+ autoupdate_delay=-1,
+ ):
+ self.number = number
+ self.line = line
+ self.last_update = 0
+ self.data = None
+ if autoupdate_at_creation:
+ self.update()
+
+ def update(self, auto=False):
+ """
+ Update data
+ """
+
+ d = get_data_from_json(SCHEDULE_URL % (self.number, self.line))
+ if "destinations" in d:
+ d = d["destinations"]
+ else:
+ return ()
+ self.last_update = time()
+ if type(d) == dict:
+ self.data = []
+ # let's simplify the data
+ for i in d:
+ for j in d[i]:
+ loc = None
+ try:
+ loc = (
+ float(j["vehicle_lattitude"]),
+ float(j["vehicle_longitude"]),
+ )
+ except TypeError:
+ pass
+ vehicle = {
+ "id": j["vehicle_id"],
+ "destination": j["destination_name"],
+ "realtime": j["realtime"] == "1",
+ "location": loc,
+ "wait_time": hms2seconds(j["waittime"]),
+ "wait_time_human": j["waittime_text"],
+ "arrival": int(self.last_update + hms2seconds(j["waittime"])),
+ }
+ self.data.append(vehicle)
+ self.data = sorted(self.data, key=lambda item: item["arrival"])
+ else:
+ self.last_update = 0
+
+ def data_age(self):
+ """
+ Returns the age of the data
+ """
+
+ return time() - self.last_update
+
+ def get_line(self):
+ class Line:
+ """
+ Information on the line served at a stop
+ """
+
+ def __init__(self, data):
+ self.ve = data
+
+ def vehicles(self):
+ if self.ve is not None:
+ return list(range(0, len(self.ve)))
+ return []
+
+ def get_vehicle(self, vehicle):
+ class Vehicle:
+ """
+ Information on a vehicle passage
+ """
+
+ def __init__(self, data):
+ self.id = data["id"]
+ self.location = data["location"]
+ self.destination = data["destination"]
+ self.is_realtime = data["realtime"]
+ self.wait_time = data["wait_time"]
+ self.wait_time_text = data["wait_time_human"]
+ self.arrival = data["arrival"]
+
+ return Vehicle(self.ve[vehicle])
+
+ return Line(self.data)
diff --git a/src/utils.py b/src/utils.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/vcub.py b/src/vcub.py
new file mode 100644
index 0000000..44424cd
--- /dev/null
+++ b/src/vcub.py
@@ -0,0 +1,132 @@
+"""
+Provides all info about V³ stations
+"""
+
+from src.libs import get_data_from_json
+from time import time
+
+vcub_url = "https://ws.infotbm.com/ws/1.0/vcubs"
+
+
+class Vcub:
+ """
+ Retrieves information from V³ stations
+
+ Data format, as returned by the infotbm website:
+ {
+ lists: [
+ {
+ 'id': station number,
+ 'name': str,
+ 'connexionState': 'CONNECTED' if in service 'DISCONNECTED' otherwise,
+ 'typeVlsPlus': 'VLS_PLUS' if V³+ 'PAS_VLS_PLUS' otherwise,
+ 'nbPlaceAvailable': 33,
+ 'nbBikeAvailable': 0
+ 'nbElectricBikeAvailable': 6
+ 'latitude': float,
+ 'longitude': float,
+ },
+ ]
+ }
+ """
+
+ def __init__(
+ self,
+ autoupdate_at_creation=None,
+ autoupdate=False,
+ autoupdate_delay=-1,
+ data=None,
+ ):
+ self.last_update = 0
+ if type(data) == dict:
+ self.data = self.update(data=data)
+ else:
+ self.data = None
+ if autoupdate_at_creation or (
+ autoupdate_at_creation is None and self.data is None
+ ):
+ self.update()
+
+ def update(self, auto=False, data=None):
+ """
+ Updates data
+ auto optionnal param is to set if a update is a automatic one,
+ and must be performed as defined in autoupdate_delay variable
+ """
+
+ if data is None or type(data) != dict:
+ d = get_data_from_json(vcub_url)
+ else:
+ d = data
+ # the original format is awfull, so I change it a little
+ if type(d) == dict:
+ self.data = {}
+ d = d["lists"]
+ for i in d:
+ e = {
+ "name": i["name"],
+ "online": i["connexionState"] == "CONNECTEE",
+ "plus": i["typeVlsPlus"] == "VLS_PLUS",
+ "empty": int(i["nbPlaceAvailable"]),
+ "bikes": int(i["nbBikeAvailable"]),
+ "ebikes": int(i["nbElectricBikeAvailable"]),
+ "location": (float(i["latitude"]), float(i["longitude"])),
+ }
+ self.data[int(i["id"])] = e
+ self.last_update = time()
+
+ def data_age(self):
+ """
+ Computes the data's age
+ """
+
+ return time() - self.last_update
+
+ def get_names(self):
+ """
+ Returns all names in a dict with id as data
+ """
+
+ r = {}
+ for i in self.data:
+ r[self.data[i]["name"]] = i
+ return r
+
+ def get_locations(self):
+ """
+ Returns all locations in a dict with id as data
+ """
+
+ r = {}
+ for i in self.data:
+ r[self.data[i]["location"]] = i
+ return r
+
+ def get_by_id(self, id):
+ """
+ Returns a station by its id
+ """
+
+ class Station:
+ """
+ A V³ station
+ """
+
+ def __init__(self, data, id):
+ self.data = data
+ self.id = id
+ self.name = self.data["name"]
+ self.location = self.data["location"]
+ self.online = self.data["online"]
+ self.isplus = self.data["plus"]
+ self.bikes = self.data["bikes"]
+ self.ebikes = self.data["ebikes"]
+ self.empty = self.data["empty"]
+
+ def __int__(self):
+ return self.id
+
+ return Station(self.data[id], id)
+
+ def get_all_ids(self):
+ return tuple(self.data.keys())
diff --git a/stop.py b/stop.py
deleted file mode 100644
index c0b35cf..0000000
--- a/stop.py
+++ /dev/null
@@ -1,246 +0,0 @@
-'''
-Fourni les informations sur les arrêts
-'''
-
-from libs import get_data_from_json, hms2seconds
-from time import time
-from urllib.parse import quote
-from re import search
-
-search_stop_url = 'https://ws.infotbm.com/ws/1.0/get-schedule/%s'
-stop_info_url = 'https://ws.infotbm.com/ws/1.0/network/stoparea-informations/%s'
-stop_schedule_url = 'https://ws.infotbm.com/ws/1.0/get-realtime-pass/%d/%s'
-
-line_types = (
- 'Tram',
- 'Corol',
- 'Lianes',
- 'Ligne',
- 'Bus Relais',
- 'Citéis',
-)
-line_translate = {
- 'Tram A': 'A',
- 'Tram B': 'B',
- 'Tram C': 'C',
- 'Tram D': 'D',
- 'TBNight': '58',
- 'BAT3': '69',
-}
-
-
-def search_stop_by_name (keyword):
- '''
- Recherche la référence d'un nom d'arrêt
-
- Format des données retournées par le site
- [
- {
- id: str, nommé ref par la suite
- name: str
- type: str, mais je ne gère que "stop_area"
- city: str
- },
- ]
- '''
- d = get_data_from_json (search_stop_url % quote (keyword))
- r = []
- for i in d:
- if i ['type'] == 'stop_area':
- r.append ({
- 'name': i ['name'],
- 'city': i ['city'],
- 'ref': i ['id'],
- })
- return (r)
-
-
-def show_stops_from_ref (ref):
- '''
- Affiche la liste des arrêts d'une référence donnée par search_stop_name
-
- Format des données retournées par le site
- {
- id: str, contenu de la variable ref donnée
- name: str
- latitude: str, convertible en float
- longitude: str, convertible en float
- city: str
- hasWheelchairBoarding: bool, accessibilité en fauteuil roulant
- stopPoints: [
- id: str
- name: str, encore le nom
- routes: [
- {
- id: str
- name: str, nom du terminus
- line: {
- name: str, nom pour les humains
- }
- }
- ]
- ]
- }
- '''
- d = get_data_from_json (stop_info_url % quote (ref))
- r = {
- 'ref': d ['id'],
- 'name': d ['name'],
- 'latitude': float (d ['latitude']),
- 'longitude': float (d ['longitude']),
- 'city': d ['city'],
- 'stop_points': [],
- }
- for i in d ['stopPoints']:
- s = {
- 'name': i ['name'],
- 'routes': [],
- }
- s ['id'] = int (search ('[0-9]+$', i ['id']).group ())
- for j in i ['routes']:
- rte = {
- 'terminus': j ['name'],
- 'line_human': j ['line'] ['name'],
- }
- add = False
- if rte ['line_human'] in line_translate:
- line_id = line_translate [rte ['line_human']]
- add = True
- else:
- try:
- line_id = search ('[0-9]+$', rte ['line_human']).group ()
- except AttributeError:
- continue
- line_id = '%02d' % int (line_id)
- for i in line_types:
- if rte ['line_human'] [0:len (i)] == i:
- add = True
- break
- if add:
- rte ['line_id'] = line_id
- s ['routes'].append (rte)
- if s ['routes'] != []:
- r ['stop_points'].append (s)
- return (r)
-
-
-class StopRoute ():
- '''
- Récupère les informations sur un arrêt
-
- Format des données retournées pas le site
- {
- destinations: {
- : [
- {
- destination_name: str
- realtime: 1 si suivi, 0 sinon
- vehicle_id: str
- vehicle_lattitude: float
- vehicle_longitude: float
- waittime: HH:MM:SS
- waittime_text: str, lisible pas un humain
- },
- ]
- }
- }
- '''
-
- def __init__ (self, number, line, autoupdate_at_creation = True, autoupdate = False, autoupdate_delay = -1):
- self.number = number
- self.line = line
- self.last_update = 0
- self.data = None
- if autoupdate_at_creation:
- self.update ()
-
- def update (self, auto = False):
- '''
- Met à jour les données
- '''
- d = get_data_from_json (stop_schedule_url % (self.number, self.line))
- if 'destinations' in d:
- d = d ['destinations']
- else:
- return ()
- self.last_update = time ()
- if type (d) == dict:
- self.data = []
- # let's simplify the data
- for i in d:
- for j in d [i]:
- loc = None
- try:
- loc = (float (j ['vehicle_lattitude']), float (j ['vehicle_longitude']))
- except TypeError:
- pass
- vehicle = {
- 'id': j ['vehicle_id'],
- 'destination': j ['destination_name'],
- 'realtime': j ['realtime'] == '1',
- 'location': loc,
- 'wait_time': hms2seconds (j ['waittime']),
- 'wait_time_human': j ['waittime_text'],
- 'arrival': int (self.last_update + hms2seconds (j ['waittime'])),
- }
- self.data.append (vehicle)
- self.data = sorted (self.data, key = lambda item: item ['arrival'])
- else:
- self.last_update = 0
-
- def data_age (self):
- '''
- Retourne l'âge des données
- '''
- return (time () - self.last_update)
-
- def get_line (self):
- class Line ():
- '''
- Information sur la ligne déservie à un arrêt
- '''
- def __init__ (self, data):
- self.ve = data
-
- def vehicles (self):
- if self.ve is not None:
- return (list (range (0, len (self.ve))))
- return ([])
-
- def get_vehicle (self, vehicle):
- class Vehicle ():
- '''
- Information sur un passage de véhicule
- '''
- def __init__ (self, data):
- self.id = data ['id']
- self.location = data ['location']
- self.destination = data ['destination']
- self.is_realtime = data ['realtime']
- self.wait_time = data ['wait_time']
- self.wait_time_text = data ['wait_time_human']
- self.arrival = data ['arrival']
-
- return (Vehicle (self.ve [vehicle]))
-
- return (Line (self.data))
-
-
-if __name__ == '__main__':
- from datetime import datetime
- for word in ('Gravière', 'Gare Saint Jean', 'Quinconces', 'Zorbut'):
- print (word + ':')
- for area in search_stop_by_name (word):
- print ('\t' + area ['name'] + ' (' + area ['city'] + '):')
- for stop in show_stops_from_ref (area ['ref']) ['stop_points']:
- print ('\t\t' + stop ['name'] + ' (' + str (stop ['id']) + '):')
- for route in stop ['routes']:
- print ('\t\t\t' + route ['line_human'] + ' terminus ' + route ['terminus'] + ':')
- sr = StopRoute (stop ['id'], route ['line_id'])
- line = sr.get_line ()
- for vehicle in line.vehicles ():
- v = line.get_vehicle (vehicle)
- if v.is_realtime:
- print ('\t\t\t\t' + str (v.wait_time) + ' (' + datetime.fromtimestamp (v.arrival).strftime ('%H:%M') + ') → ' + v.destination)
- else:
- print ('\t\t\t\t~' + str (v.wait_time) + ' (' + datetime.fromtimestamp (v.arrival).strftime ('%H:%M') + ') → ' + v.destination)
\ No newline at end of file
diff --git a/vcub.py b/vcub.py
deleted file mode 100644
index 9d814e9..0000000
--- a/vcub.py
+++ /dev/null
@@ -1,126 +0,0 @@
-'''
-Provides all info about V³ stations
-'''
-
-from libs import get_data_from_json
-from time import time
-
-vcub_url = 'https://ws.infotbm.com/ws/1.0/vcubs'
-
-
-class Vcub ():
- '''
- Récupère les informations des stations V³
-
- Format de données, tel que retourné par le site infotbm :
- {
- lists: [
- {
- 'id': numéro de la station,
- 'name': str,
- 'connexionState': 'CONNECTEE' si en service 'DECONNECTEE' sinon,
- 'typeVlsPlus': 'VLS_PLUS' si V³+ 'PAS_VLS_PLUS' sinon,
- 'nbPlaceAvailable': 33,
- 'nbBikeAvailable': 0
- 'nbElectricBikeAvailable': 6
- 'latitude': float,
- 'longitude': float,
- },
- ]
- }
- '''
- def __init__ (self, autoupdate_at_creation = None, autoupdate = False, autoupdate_delay = -1, data = None):
- self.last_update = 0
- if type (data) == dict:
- self.data = self.update (data = data)
- else:
- self.data = None
- if autoupdate_at_creation or (autoupdate_at_creation is None and self.data is None):
- self.update ()
-
- def update (self, auto = False, data = None):
- '''
- Updates data
- auto optionnal param is to set if a update is a automatic one, and must be performed as defined in autoupdate_delay variable
- '''
- if data is None or type (data) != dict:
- d = get_data_from_json (vcub_url)
- else:
- d = data
- # the original format is awfull, so I change it a little
- if type (d) == dict:
- self.data = {}
- d = d ['lists']
- for i in d:
- e = {
- 'name': i ['name'],
- 'online': i ['connexionState'] == 'CONNECTEE',
- 'plus': i ['typeVlsPlus'] == 'VLS_PLUS',
- 'empty': int (i ['nbPlaceAvailable']),
- 'bikes': int (i ['nbBikeAvailable']),
- 'ebikes': int (i ['nbElectricBikeAvailable']),
- 'location': (float (i ['latitude']), float (i ['longitude']))
- }
- self.data [int (i ['id'])] = e
- self.last_update = time ()
-
- def data_age (self):
- '''
- Computes the data's age
- '''
- return (time () - self.last_update)
-
- def get_names (self):
- '''
- Returns all names in a dict with id as data
- '''
- r = {}
- for i in self.data:
- r [self.data [i] ['name']] = i
- return (r)
-
- def get_locations (self):
- '''
- Returns all locations in a dict with id as data
- '''
- r = {}
- for i in self.data:
- r [self.data [i] ['location']] = i
- return (r)
-
- def get_by_id (self, id):
- '''
- Returns a station by its id
- '''
- class Station ():
- '''
- A V³ station
- '''
- def __init__ (self, data, id):
- self.data = data
- self.id = id
- self.name = self.data ['name']
- self.location = self.data ['location']
- self.online = self.data ['online']
- self.isplus = self.data ['plus']
- self.bikes = self.data ['bikes']
- self.ebikes = self.data ['ebikes']
- self.empty = self.data ['empty']
-
- def __int__ (self):
- return (self.id)
-
- return (Station (self.data [id], id))
-
- def get_all_ids (self):
- return (tuple (self.data.keys ()))
-
-
-if __name__ == '__main__':
- v = Vcub ()
- for i in (v.get_by_id (149), v.get_by_id (v.get_names () ['Buttiniere']), ):
- print ('%s (%d) (%f, %f)%s%s\n\tbikes: %d\n\te-bikes: %d\n\tfree: %d\n\t' % (i.name, i, i.location [0], i.location [1], i.isplus and ' (VCUB+)' or '', i.online and ' ' or ' OFFLINE', i.bikes, i.ebikes, i.empty))
- v = Vcub (data = get_data_from_json (vcub_url))
- for i in (v.get_by_id (v.get_locations () [(44.8875, -0.51763)]), ):
- print ('%s (%d) (%f, %f)%s%s\n\tbikes: %d\n\te-bikes: %d\n\tfree: %d\n\t' % (i.name, i, i.location [0], i.location [1], i.isplus and ' (VCUB+)' or '', i.online and ' ' or ' OFFLINE', i.bikes, i.ebikes, i.empty))
- print ('stations :', v.get_all_ids ())
\ No newline at end of file