From afdd23224ab712c22bd891dcf46252e8c500bead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Gonz=C3=A1lez=20G=C3=B3mez?= Date: Sat, 21 Oct 2023 16:00:55 -0400 Subject: [PATCH 1/2] Add mobhis system --- pybikes/data/mobhis.json | 28 +++++++++++++++++ pybikes/mobhis.py | 67 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 pybikes/data/mobhis.json create mode 100644 pybikes/mobhis.py diff --git a/pybikes/data/mobhis.json b/pybikes/data/mobhis.json new file mode 100644 index 000000000..bcb40a975 --- /dev/null +++ b/pybikes/data/mobhis.json @@ -0,0 +1,28 @@ +{ + "instances": [ + { + "tag": "passofundo", + "meta": { + "city": "Passo Fundo", + "name": "Passo Fundo Vai de Bici", + "latitude": -28.2555, + "longitude": -52.3981, + "country": "BR" + }, + "feed_url": "https://pfvaidebici.mobhis.com.br/" + }, + { + "tag": "cascavel", + "meta": { + "city": "Cascavel", + "name": "Cascavel Vai de Bici", + "latitude": -24.9558, + "longitude": -53.4566, + "country": "BR" + }, + "feed_url": "https://www.cascavelvaidebici.mobhis.com.br/" + } + ], + "system": "mobhis", + "class": "Mobhis" +} diff --git a/pybikes/mobhis.py b/pybikes/mobhis.py new file mode 100644 index 000000000..51ac77b77 --- /dev/null +++ b/pybikes/mobhis.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2023, Martín González Gómez +# Distributed under the LGPL license, see LICENSE.txt + +import re + +from pybikes import BikeShareSystem, BikeShareStation, PyBikesScraper + + +class Mobhis(BikeShareSystem): + meta = { + 'system': 'Mobhis', + 'company': ['Mobhis Automação Urbana'] + } + + def __init__(self, tag, feed_url, meta): + super(Mobhis, self).__init__(tag, meta) + self.feed_url = feed_url + + def update(self, scraper=None): + scraper = scraper or PyBikesScraper() + + html = scraper.request(self.feed_url) + + # coords are on the first line, info on the second + pattern = r'var marker = L\.marker\((.*?)\)\.addTo\(map\);\n(.*?)(?=\n\n|\n\Z)' + all = re.findall(pattern, html, re.DOTALL) + + # filter out offline stations + stations = filter(lambda item: item[1].find('Estação offline ou não instalada') == -1, all) + + self.stations = list(map(lambda d: MobhisStation(* d), stations)) + + +class MobhisStation(BikeShareStation): + def __init__(self, coords, info): + super(MobhisStation, self).__init__() + + lat, lon = re.search(r'\[([^\]]+)\]', coords).group(1).split(', ') + id, name = re.search(r'bindPopup\("
([^<]+)', info).group(1).split(' - ') + + info = re.search(r'(.*?)
', info).group(1).split('
') + info = list(filter(lambda item: item != '', info)) + info = list(map(lambda s: re.findall(r'\d+', s)[0], info)) + + bikes_kids = None + free_kids = None + + # some systems have data for kid-sized bikes and spaces + if len(info) > 2: + bikes, bikes_kids, free, free_kids = info + else: + bikes, free = info + + self.name = name + self.latitude = float(lat) + self.longitude = float(lon) + self.bikes = int(bikes) + self.free = int(free) + self.extra = { + 'uid': id + } + + if bikes_kids: + self.extra['bikes_kids'] = int(bikes_kids) + if free_kids: + self.extra['free_kids'] = int(free_kids) From cd19f40072be7c8b09d0e52d1cccf84de265d97e Mon Sep 17 00:00:00 2001 From: eskerda Date: Mon, 30 Oct 2023 13:47:16 +0100 Subject: [PATCH 2/2] Cleanup mobhis --- pybikes/mobhis.py | 61 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/pybikes/mobhis.py b/pybikes/mobhis.py index 51ac77b77..c660b6e40 100644 --- a/pybikes/mobhis.py +++ b/pybikes/mobhis.py @@ -4,6 +4,8 @@ import re +from lxml import html + from pybikes import BikeShareSystem, BikeShareStation, PyBikesScraper @@ -20,48 +22,49 @@ def __init__(self, tag, feed_url, meta): def update(self, scraper=None): scraper = scraper or PyBikesScraper() - html = scraper.request(self.feed_url) - - # coords are on the first line, info on the second - pattern = r'var marker = L\.marker\((.*?)\)\.addTo\(map\);\n(.*?)(?=\n\n|\n\Z)' - all = re.findall(pattern, html, re.DOTALL) + data = scraper.request(self.feed_url) - # filter out offline stations - stations = filter(lambda item: item[1].find('Estação offline ou não instalada') == -1, all) + latlngs = re.findall(r'var marker = L\.marker\(\[(-?\d+\.\d+), (-?\d+\.\d+)\]', data) + infos = re.findall(r'marker.bindPopup\("(.*)"\)', data) + stations = zip(latlngs, infos) - self.stations = list(map(lambda d: MobhisStation(* d), stations)) + self.stations = list(map(lambda i: MobhisStation(*i), stations)) class MobhisStation(BikeShareStation): def __init__(self, coords, info): super(MobhisStation, self).__init__() - lat, lon = re.search(r'\[([^\]]+)\]', coords).group(1).split(', ') - id, name = re.search(r'bindPopup\("
([^<]+)', info).group(1).split(' - ') + lat, lng = map(float, coords) - info = re.search(r'(.*?)
', info).group(1).split('
') - info = list(filter(lambda item: item != '', info)) - info = list(map(lambda s: re.findall(r'\d+', s)[0], info)) + popup = html.fromstring(info) + elems = popup.xpath('//text()') - bikes_kids = None - free_kids = None + name = elems.pop(0) + uid, _ = name.split(' - ') + online = 'Estação offline' not in info + + self.name = name + self.latitude = lat + self.longitude = lng + self.extra = {'uid': uid, 'online': online} + + if not online: + self.bikes, self.free = 0, 0 + return + + # find ints on remaining elements + elems = list(map(lambda s: re.search(r'\d+', s).group(), elems)) # some systems have data for kid-sized bikes and spaces - if len(info) > 2: - bikes, bikes_kids, free, free_kids = info + if len(elems) > 2: + bikes, bikes_kids, free, free_kids = elems + self.extra.update({ + 'kid_bikes': int(bikes_kids), + 'kid_slots': int(free_kids), + }) else: - bikes, free = info + bikes, free = elems - self.name = name - self.latitude = float(lat) - self.longitude = float(lon) self.bikes = int(bikes) self.free = int(free) - self.extra = { - 'uid': id - } - - if bikes_kids: - self.extra['bikes_kids'] = int(bikes_kids) - if free_kids: - self.extra['free_kids'] = int(free_kids)