| 
					
				 | 
			
			
				@@ -0,0 +1,1490 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+"""Get information about bike sharing in Pisa. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Available bikes in bike sharing stations. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Standard library modules 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import asyncio 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import datetime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import math 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Third party modules 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from davtelepot.utilities import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async_wrapper, CachedPage, extract, get_cleaned_text, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    line_drawing_unordered_list, make_button, make_inline_keyboard, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    make_lines_of_buttons 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+_URL = "http://www.ciclopi.eu/frmLeStazioni.aspx" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ciclopi_webpage = CachedPage.get( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    _URL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    datetime.timedelta(seconds=15), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    mode='html' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+UNIT_TO_KM = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'km': 1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'm': 1000, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'mi': 0.621371192, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'nmi': 0.539956803, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'ft': 3280.839895013, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'in': 39370.078740158 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+CICLOPI_SETTINGS = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'sort': dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name="Ordina", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description="scegli in che ordine visualizzare le stazioni CicloPi.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol="⏬" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'limit': dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name="Numero di stazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description="scegli quante stazioni visualizzare.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol="#️⃣" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'fav': dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name="Stazioni preferite", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description="cambia le tue stazioni preferite.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol="⭐️" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'setpos': dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name="Cambia posizione", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "imposta una posizione da cui ordinare le stazioni per distanza." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='🧭' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+CICLOPI_SORTING_CHOICES = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    0: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='Scuola', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description='in ordine di distanza crescente da Scuola.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        short_description='per distanza da Scuola', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='🏫' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    1: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='Alfabetico', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description='in ordine alfabetico.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        short_description='per nome', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='🔤' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    2: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='Posizione', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description='in ordine di distanza crescente dall\'ultima posizione ' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    'inviata. Di default sarà la posizione di Scuola.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        short_description='per distanza', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='🧭' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    3: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='Preferite', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        description='nell\'ordine che hai scelto.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        short_description='in ordine personalizzato', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='⭐️' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+CICLOPI_STATIONS_TO_SHOW = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    -1: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name="Solo le preferite", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='⭐️' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    0: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='Tutte', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='💯' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    3: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='3', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='3️⃣' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    5: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='5', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='5️⃣' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    10: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name='10', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        symbol='🔟' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def haversine_distance(lat1, lon1, lat2, lon2, degrees='dec', unit='m'): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Calculate the great circle distance between two points on Earth. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (specified in decimal degrees) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    assert unit in UNIT_TO_KM, "Invalid distance unit of measurement!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    assert degrees in ['dec', 'rad'], "Invalid angle unit of measurement!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # Convert decimal degrees to radians 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if degrees == 'dec': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        lon1, lat1, lon2, lat2 = map( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            math.radians, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [lon1, lat1, lon2, lat2] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    average_earth_radius = 6371.0088 * UNIT_TO_KM[unit] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        * average_earth_radius 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        * math.asin( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            math.sqrt( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                math.sin((lat2 - lat1) * 0.5) ** 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                + math.cos(lat1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                * math.cos(lat2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                * math.sin((lon2 - lon1) * 0.5) ** 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class Location(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Location in world map.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, coordinates): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Check and set instance attributes.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert type(coordinates) is tuple, "`coordinates` must be a tuple" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            len(coordinates) == 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            and all(type(c) is float for c in coordinates) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), "`coordinates` must be two floats" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._coordinates = coordinates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def coordinates(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return a tuple (latitude, longitude).""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._coordinates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def latitude(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return latitude.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._coordinates[0] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def longitude(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return longitude.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._coordinates[1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def get_distance(self, other, *args, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return the distance between two `Location`s.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return haversine_distance( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.latitude, self.longitude, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            other.latitude, other.longitude, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            *args, **kwargs 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+default_location = Location( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (43.719821, 10.403021)  # M. Libertà station 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class Station(Location): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """CicloPi bike sharing station.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stations = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        1: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Aeroporto', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.699455, 10.400075), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        2: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Stazione F.S.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.708627, 10.399051), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        3: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Comune Palazzo Blu', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.715541, 10.400505), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        4: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Teatro Tribunale', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.716391, 10.405136), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        5: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Borgo Stretto', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.718518, 10.402165), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        6: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Polo Marzotto', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.719772, 10.407291), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        7: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Duomo', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.722855, 10.391977), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        8: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Pietrasantina', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.729020, 10.392726), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        9: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Paparelli', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.724449, 10.410438), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        10: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Pratale', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.7212554, 10.4180257), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        11: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Ospedale Cisanello', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.705752, 10.441740), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        12: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Sms Biblioteca', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.706565, 10.419136), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        13: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Vittorio Emanuele', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.710182, 10.398751), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        14: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Palacongressi', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.710014, 10.410232), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        15: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Porta a Lucca', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.724247, 10.402269), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        16: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Solferino', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.715698, 10.394999), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        17: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='San Rossore F.S.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.718992, 10.384391), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        18: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Guerrazzi', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.710358, 10.405337), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        19: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Livornese', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.708114, 10.384021), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        20: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Cavalieri', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.719856, 10.400194), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        21: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='M. Libertà', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.719821, 10.403021), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        22: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Galleria Gerace', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.710791, 10.420456), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        23: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='C. Marchesi', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.714971, 10.419322), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        24: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='CNR-Praticelli', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.719256, 10.424012), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        25: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Sesta Porta', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.709162, 10.395837), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        26: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Qualconia', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.713011, 10.394458), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        27: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Donatello', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.711715, 10.372480), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        28: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Spadoni', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.716850, 10.391347), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        29: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Nievo', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.738286, 10.400865), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        30: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Cisanello', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.701159, 10.438863), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        31: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Edificio 3', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.707869, 10.441698), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        32: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Edificio 6', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.709046, 10.442541), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        33: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Frascani', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.710157, 10.433339), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        34: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Chiarugi', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.726244, 10.412882), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        35: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Praticelli 2', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.719619, 10.427469), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        36: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Carducci', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.726700, 10.420562), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        37: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Garibaldi', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.718077, 10.418168), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        38: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Silvestro', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.714128, 10.409065), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        39: dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name='Pardi', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates=(43.702273, 10.399793), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, id=0, name='unknown', coordinates=(91.0, 181.0)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Check and set instance attributes.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if id in self.__class__.stations: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            coordinates = self.__class__.stations[id]['coordinates'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            name = self.__class__.stations[id]['name'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Location.__init__(self, coordinates) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._id = id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._name = name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._active = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._location = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._description = '' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._distance = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._bikes = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._free = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def id(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return station identification number.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def name(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return station name.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def description(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return station description.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._description 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def is_active(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return True if station is active.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._active 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def location(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return location from which distance should be evaluated.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if self._location is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return default_location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def distance(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return distance from `self.location`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        If distance is not evaluated yet, do it and store the result. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Otherwise, return stored value. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if self._distance is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self._distance = self.get_distance(self.location) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._distance 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def bikes(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return number of available bikes.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._bikes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def free(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return number of free slots.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._free 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def set_active(self, active): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Change station status to `active`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        `active` should be either `True` or `False`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert type(active) is bool, "`active` should be a boolean." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._active = active 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def set_description(self, description): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Change station description to `description`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        `description` should be a string. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert type(description) is str, "`description` should be a boolean." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._description = description 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def set_location(self, location): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Change station location to `location`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        `location` should be a Location object. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            isinstance(location, Location) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), "`location` should be a Location." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._location = location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def set_bikes(self, bikes): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Change number of available `bikes`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        `bikes` should be an int. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            type(bikes) is int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), "`bikes` should be an int." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._bikes = bikes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def set_free(self, free): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Change number of `free` bike parking slots. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        `free` should be an int. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            type(free) is int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), "`free` should be an int." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._free = free 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def status(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return station status to be shown to users. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        It includes distance, location, available bikes and free stalls. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if self.bikes + self.free == 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bikes_and_stalls = "<i>⚠️ Non disponibile</i>" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bikes_and_stalls = f"🚲 {self.bikes}  |  🅿️ {self.free}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            f"<b>{self.name}</b> | <i>{self.description}</i>\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            f"<code>   </code>{bikes_and_stalls}  | 📍 {self.distance:.0f} m" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            s=self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def ciclopi_custom_sorter(custom_order): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Return a function to sort stations by a `custom_order`.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    custom_values = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        record['station']: record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for record in custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def sorter(station): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Take a station and return its queue value. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Stations will be sorted by queue value in ascending order. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if station.id in custom_values: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return (custom_values[station.id], station.name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return (100, station.name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return sorter 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def _get_stations(data, location): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stations = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for _station in data.find_all( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "li", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        attrs={"class": "rrItem"} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station_name = _station.find( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "span", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            attrs={"class": "Stazione"} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ).text 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'Non operativa' in station_name: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            active = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            active = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station_id = _station.find( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "div", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            attrs={"class": "cssNumero"} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ).text 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            station_id is None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            or type(station_id) is not str 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            or not station_id.isnumeric() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            station_id = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            station_id = int(station_id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station = Station(station_id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station.set_active(active) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station.set_description( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            _station.find( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "span", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                attrs={"class": "TableComune"} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ).text.replace( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                'a`', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                'à' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        bikes_text = _station.find( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "span", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            attrs={"class": "Red"} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ).get_text('\t') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if bikes_text.count('\t') < 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bikes = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            free = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bikes, free, *other = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                int( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ''.join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        char 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        for char in s 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if char.isnumeric() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for s in bikes_text.split('\t') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station.set_bikes(bikes) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station.set_free(free) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station.set_location(location) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        stations.append( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            station 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def set_ciclopi_location(bot, update, user_record): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Take a location update and store it as CicloPi place. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    CicloPi stations will be sorted by distance from this place. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    location = update['location'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    chat_id = update['chat']['id'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    telegram_id = update['from']['id'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        db['ciclopi'].upsert( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                latitude=location['latitude'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                longitude=location['longitude'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ['chat_id'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    await bot.send_message( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        text=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "Ho salvato questa posizione!\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "D'ora in poi ordinerò le stazioni dalla più vicina alla più " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "lontana da qui." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # Remove individual text message handler which was set to catch `/cancel` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bot.remove_individual_text_message_handler(telegram_id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return await _ciclopi_command(bot, update, user_record) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def cancel_ciclopi_location(bot, update, user_record): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Handle the situation in which a user does not send location on request. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    This function is set as custom_parser when the bot requests user's location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    and is removed if user does. If not, return a proper message. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text = get_cleaned_text(bot=bot, update=update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if text.lower() == 'annulla': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return "Operazione annullata." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Non ho capito la tua posizione. Fai /ciclopi > Ordina... > " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Posizione 🧭 per riprovare." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_command(bot, update, user_record, sent_message=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           show_all=False): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    chat_id = update['chat']['id'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    default_stations_to_show = 5 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if sent_message is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await bot.sendChatAction( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            action='typing' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await bot.edit_message_text( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            update=sent_message, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            text="<i>Aggiornamento in corso...</i>", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parse_mode='HTML', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            reply_markup=None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ciclopi_data = await ciclopi_webpage.get_page() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if ciclopi_data is None or isinstance(ciclopi_data, Exception): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "Il sito del CicloPi è momentaneamente irraggiungibile, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "riprova tra un po' :/" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ciclopi_record = db['ciclopi'].find_one( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                chat_id=chat_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            custom_order = list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                db['ciclopi_custom_order'].find( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ciclopi_record is not None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                and isinstance(ciclopi_record, dict) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                and 'sorting' in ciclopi_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                and ciclopi_record['sorting'] in CICLOPI_SORTING_CHOICES 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                sorting_code = ciclopi_record['sorting'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    'latitude' in ciclopi_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    and ciclopi_record['latitude'] is not None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    and 'longitude' in ciclopi_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    and ciclopi_record['longitude'] is not None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    saved_place = Location( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            ciclopi_record['latitude'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            ciclopi_record['longitude'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    saved_place = default_location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                sorting_code = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ciclopi_record is not None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                and isinstance(ciclopi_record, dict) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                and 'stations_to_show' in ciclopi_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                and ciclopi_record[ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    'stations_to_show' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ] in CICLOPI_STATIONS_TO_SHOW 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stations_to_show = ciclopi_record[ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    'stations_to_show' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stations_to_show = default_stations_to_show 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        location = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            saved_place if sorting_code != 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else default_location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sorting_method = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (lambda station: station.distance) if sorting_code in [0, 2] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else (lambda station: station.name) if sorting_code == 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else ciclopi_custom_sorter(custom_order) if sorting_code == 3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else (lambda station: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        stations = sorted( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            _get_stations( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ciclopi_data, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            key=sorting_method 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stations_to_show == -1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            and not show_all 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stations = list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                filter( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    lambda station: station.id in [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        record['station'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        for record in custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stations_to_show > 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            and sorting_code != 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            and not show_all 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stations = stations[:stations_to_show] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "🚲 Stazioni ciclopi {sort[short_description]}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "{lim} {sort[symbol]}\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "{s}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            s=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                '\n\n'.join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    station.status 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for station in stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) if len(stations) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else "<i>- Nessuna stazione -</i>" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            sort=CICLOPI_SORTING_CHOICES[sorting_code], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            lim=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                " ({adv} le preferite)".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    adv='prima' if show_all else 'solo' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) if stations_to_show == -1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else " (prime {n})".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    n=stations_to_show 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if len(stations) < len(Station.stations) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if not text: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reply_markup = make_inline_keyboard( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    "💯 Tutte", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['show', 'all'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] if len(stations) < len(Station.stations) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    "{sy[symbol]} {t}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        t=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            "Solo preferite" if stations_to_show == -1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            else "Prime {n}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                n=stations_to_show 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        sy=CICLOPI_STATIONS_TO_SHOW[stations_to_show] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] if show_all 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) + [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "🔄 Aggiorna", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ['show'] + ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        [] if len(stations) < len(Station.stations) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        else ['all'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "📜 Legenda", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['legend'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "⚙️ Impostazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['main'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    parameters = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update=update, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        text=text, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        parse_mode='HTML', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        reply_markup=reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    method = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        bot.send_message 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if sent_message is None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else bot.edit_message_text 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    await method(**parameters) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_main(bot, update, user_record, arguments): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "⚙️ Impostazioni CicloPi 🚲\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "{c}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        c='\n'.join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "- {s[symbol]} {s[name]}: {s[description]}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                s=setting 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for setting in CICLOPI_SETTINGS.values() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reply_markup = make_inline_keyboard( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="{s[symbol]} {s[name]}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    s=setting 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=[code] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for code, setting in CICLOPI_SETTINGS.items() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] + [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="🚲 Torna alle stazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_sort(bot, update, user_record, arguments): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    chat_id = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update['message']['chat']['id'] if 'message' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else update['chat']['id'] if 'chat' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ciclopi_record = db['ciclopi'].find_one( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            chat_id=chat_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ciclopi_record is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ciclopi_record = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                sorting=0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if len(arguments) == 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            new_choice = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                int(arguments[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if arguments[0].isnumeric() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if new_choice == ciclopi_record['sorting']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return "È già così!", '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif new_choice not in CICLOPI_SORTING_CHOICES: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return "Opzione sconosciuta!", '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db['ciclopi'].upsert( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    sorting=new_choice 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ['chat_id'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ensure=True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ciclopi_record['sorting'] = new_choice 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            result = "Fatto!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "📜 Modalità di visualizzazione delle stazioni CicloPi 🚲\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "{options}\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Scegli una nuova modalità o torna all'elenco delle stazioni." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        options='\n'.join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "- {c[symbol]} {c[name]}: {c[description]}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                c=choice 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for choice in CICLOPI_SORTING_CHOICES.values() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reply_markup = make_inline_keyboard( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="{s} {c[name]} {c[symbol]}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    c=choice, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    s=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        '✅' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if code == ciclopi_record['sorting'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        else '☑️' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['sort', code] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for code, choice in CICLOPI_SORTING_CHOICES.items() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] + [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="⚙️ Torna alle impostazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['main'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="🚲 Torna alle stazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_limit(bot, update, user_record, arguments): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    chat_id = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update['message']['chat']['id'] if 'message' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else update['chat']['id'] if 'chat' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ciclopi_record = db['ciclopi'].find_one( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            chat_id=chat_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ciclopi_record is None or 'stations_to_show' not in ciclopi_record: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ciclopi_record = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stations_to_show=5 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if len(arguments) == 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            new_choice = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                int(arguments[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if arguments[0].lstrip('+-').isnumeric() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if new_choice == ciclopi_record['stations_to_show']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return "È già così!", '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif new_choice not in CICLOPI_STATIONS_TO_SHOW: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return "Opzione sconosciuta!", '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db['ciclopi'].upsert( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    stations_to_show=new_choice 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ['chat_id'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ensure=True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ciclopi_record['stations_to_show'] = new_choice 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            result = "Fatto!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "📜 Modalità di visualizzazione delle stazioni CicloPi 🚲\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "{options}\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Scegli quante stazioni vedere (quando filtrate per distanza) o torna " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "alle impostazioni o all'elenco delle stazioni." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        options='\n'.join( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "- {c[symbol]} {c[name]}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                c=choice 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for choice in CICLOPI_STATIONS_TO_SHOW.values() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reply_markup = make_inline_keyboard( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="{s} {c[name]} {c[symbol]}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    c=choice, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    s=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        '✅' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if code == ciclopi_record['stations_to_show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        else '☑️' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['limit', code] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for code, choice in CICLOPI_STATIONS_TO_SHOW.items() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] + [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="⚙️ Torna alle impostazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['main'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="🚲 Torna alle stazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_show(bot, update, user_record, arguments): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fake_update = update['message'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fake_update['from'] = update['from'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    asyncio.ensure_future( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        _ciclopi_command( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bot=bot, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            update=fake_update, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            user_record=user_record, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            sent_message=fake_update, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            show_all=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                True if len(arguments) == 1 and arguments[0] == 'all' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_legend(bot, update, user_record, arguments): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "<b>{s[name]}</b> | <i>{s[description]}</i>\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "<code>  </code>🚲 {s[bikes]}  |  🅿️ {s[free]}  | 📍 {s[distance]}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        s={ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'name': "Nome della stazione", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'distance': "Distanza in m", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'description': "Indirizzo della stazione", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'bikes': "Bici disponibili", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'free': "Posti liberi" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reply_markup = make_inline_keyboard( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="⚙️ Torna alle impostazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['main'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text="🚲 Torna alle stazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=['show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_favorites_add(bot, update, user_record, arguments, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        order_record, ordered_stations): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = "Seleziona le stazioni da aggiungere" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if len(arguments) == 2 and arguments[1].isnumeric(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station_id = int(arguments[1]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        chat_id = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            update['message']['chat']['id'] if 'message' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else update['chat']['id'] if 'chat' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if station_id in (s.id for s in ordered_stations):  # Remove 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # Find `old_record` to be removed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for old_record in order_record: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if old_record['station'] == station_id: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                db.query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    """UPDATE ciclopi_custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    SET value = value - 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    WHERE chat_id = {chat_id} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        AND value > {val} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    """.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        val=old_record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                db['ciclopi_custom_order'].delete( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    id=old_record['id'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                order_record = list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    filter( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        (lambda r: r['station'] != station_id), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        order_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ordered_stations = list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    filter( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        (lambda s: s.id != station_id), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ordered_stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else:  # Add 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                new_record = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    station=station_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    value=(len(order_record) + 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                db['ciclopi_custom_order'].upsert( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    new_record, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ['chat_id', 'station'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ensure=True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                order_record.append(new_record) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ordered_stations.append( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Station(station_id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "🚲 <b>Stazioni preferite</b> ⭐️\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "{options}\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Aggiungi o togli le tue stazioni preferite." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        options=line_drawing_unordered_list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                station.name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for station in ordered_stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reply_markup = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        inline_keyboard=make_lines_of_buttons( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "{sy} {n}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        sy=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            '✅' if station_id in [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                s.id for s in ordered_stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            else '☑️' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        n=station['name'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['fav', 'add', station_id] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for station_id, station in sorted( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Station.stations.items(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    key=lambda t: t[1]['name']  # Sort by station_name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) + make_lines_of_buttons( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text="🔃 Riordina", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix="ciclopi:///", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=["fav"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text="⚙️ Torna alle impostazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['main'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text="🚲 Torna alle stazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def move_favorite_station( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bot, chat_id, action, station_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    order_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Move a station in `chat_id`-associated custom order. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    `bot`: Bot object, having a `.db` property. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    `action`: should be `up` or `down` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    `order_record`: list of records about `chat_id`-associated custom order. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    assert action in ('up', 'down'), "Invalid action!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for old_record in order_record: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if old_record['station'] == station_id: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if action == 'down': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db.query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """UPDATE ciclopi_custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SET value = 500 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                WHERE chat_id = {chat_id} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    AND value = {val} + 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    val=old_record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db.query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """UPDATE ciclopi_custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SET value = value + 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                WHERE chat_id = {chat_id} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    AND value = {val} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    val=old_record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db.query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """UPDATE ciclopi_custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SET value = {val} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                WHERE chat_id = {chat_id} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    AND value = 500 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    val=old_record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        elif action == 'up': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db.query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """UPDATE ciclopi_custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SET value = 500 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                WHERE chat_id = {chat_id} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    AND value = {val} - 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    val=old_record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db.query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """UPDATE ciclopi_custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SET value = value - 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                WHERE chat_id = {chat_id} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    AND value = {val} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    val=old_record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db.query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """UPDATE ciclopi_custom_order 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SET value = {val} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                WHERE chat_id = {chat_id} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    AND value = 500 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                """.format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    val=old_record['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        order_record = list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db['ciclopi_custom_order'].find( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    order_by=['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ordered_stations = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Station(record['station']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for record in order_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return order_record, ordered_stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_favorites(bot, update, user_record, arguments): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    action = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        arguments[0] if len(arguments) > 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else 'up' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    chat_id = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update['message']['chat']['id'] if 'message' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else update['chat']['id'] if 'chat' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        order_record = list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db['ciclopi_custom_order'].find( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    order_by=['value'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ordered_stations = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Station(record['station']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for record in order_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if action == 'add': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await _ciclopi_button_favorites_add( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bot, update, user_record, arguments, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            order_record, ordered_stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    elif action == 'dummy': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 'Capolinea!', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    elif action == 'set' and len(arguments) > 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        action = arguments[1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    elif ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        action in ['up', 'down'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        and len(arguments) > 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        and arguments[1].isnumeric() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        station_id = int(arguments[1]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        order_record, ordered_stations = move_favorite_station( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bot, chat_id, action, station_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            order_record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "🚲 <b>Stazioni preferite</b> ⭐️\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "{options}\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        "Aggiungi, togli o riordina le tue stazioni preferite." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ).format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        options=line_drawing_unordered_list( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                station.name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for station in ordered_stations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reply_markup = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        inline_keyboard=[ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text="{s.name} {sy}".format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        sy=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            '⬆️' if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                action == 'up' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                and n != 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            ) else '⬇️' if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                action == 'down' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                and n != len(ordered_stations) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            ) else '⏹' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        s=station 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=[ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        'fav', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            action if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                action == 'up' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                and n != 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            ) or ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                action == 'down' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                and n != len(ordered_stations) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            else 'dummy' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        station.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for n, station in enumerate(ordered_stations, 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] + [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text="➕ Aggiungi stazione preferita ⭐️", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['fav', 'add'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] + [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        text='Sposta in basso ⬇️', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        data=['fav', 'set', 'down'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     ) if action == 'up' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     else make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        text='Sposta in alto ⬆️', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        data=['fav', 'set', 'up'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] + [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text="⚙️ Torna alle impostazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['main'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                make_button( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    text="🚲 Torna alle stazioni", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    prefix='ciclopi:///', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data=['show'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button_setpos(bot, update, user_record, arguments): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result, text, reply_markup = '', '', None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    chat_id = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update['message']['chat']['id'] if 'message' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else update['chat']['id'] if 'chat' in update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    result = "Inviami una posizione!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bot.set_individual_location_handler( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await async_wrapper( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            set_ciclopi_location 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bot.set_individual_text_message_handler( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cancel_ciclopi_location, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    asyncio.ensure_future( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        bot.send_message( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            chat_id=chat_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            text=( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "Inviami una posizione.\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "Per inviare la tua posizione attuale, usa il " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "pulsante." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            reply_markup=dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                keyboard=[ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            text="Invia la mia posizione", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            request_location=True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            text="Annulla" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                resize_keyboard=True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result, text, reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+_ciclopi_button_routing_table = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'main': _ciclopi_button_main, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'sort': _ciclopi_button_sort, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'limit': _ciclopi_button_limit, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'show': _ciclopi_button_show, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'setpos': _ciclopi_button_setpos, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'legend': _ciclopi_button_legend, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    'fav': _ciclopi_button_favorites 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async def _ciclopi_button(bot, update, user_record): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    data = update['data'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    command, *arguments = extract(data, ':///').split('|') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if command in _ciclopi_button_routing_table: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        result, text, reply_markup = await _ciclopi_button_routing_table[ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            command 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ]( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bot, update, user_record, arguments 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if text: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            text=result, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            edit=dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                text=text, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                parse_mode='HTML', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                reply_markup=reply_markup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return result 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def init(bot): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Take a bot and assign commands to it.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with bot.db as db: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'ciclopi_stations' not in db.tables: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db['ciclopi_stations'].insert_many( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                sorted( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            station_id=station_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            name=station['name'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            latitude=station['coordinates'][0], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            longitude=station['coordinates'][1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        for station_id, station in Station.stations.items() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    key=(lambda station: station['station_id']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'ciclopi' not in db.tables: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            db['ciclopi'].insert( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    chat_id=0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    sorting=0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    latitude=0.0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    longitude=0.0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    stations_to_show=-1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @bot.command(command='/ciclopi', aliases=["CicloPi 🚲", "🚲 CicloPi 🔴"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 show_in_keyboard=True, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 description="Stato delle stazioni CicloPi", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 authorization_level='everybody') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def ciclopi_command(bot, update, user_record): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await _ciclopi_command(bot, update, user_record) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @bot.button(prefix='ciclopi:///', authorization_level='everybody') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def ciclopi_button(bot, update, user_record): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await _ciclopi_button(bot, update, user_record) 
			 |