Spaces:
Sleeping
Sleeping
from flask import Flask, jsonify, render_template | |
from bs4 import BeautifulSoup | |
import requests | |
from datetime import datetime | |
import os | |
from dotenv import load_dotenv | |
from rtm_scraper import RTMTraffic # Importer notre nouveau scraper | |
# Charger les variables d'environnement | |
load_dotenv() | |
app = Flask(__name__) | |
TOMTOM_API_KEY = os.getenv("TOMTOM_API_KEY") | |
# Coordonnées du centre de Marseille | |
MARSEILLE_CENTER = {"lat": 43.2965, "lon": 5.3698} | |
def scrape_marseille_ferries(): | |
""" | |
Scrape les données des ferries depuis le site du Port de Marseille | |
Returns: | |
list: Liste des ferries avec leurs horaires | |
""" | |
try: | |
# Headers pour simuler un navigateur web | |
headers = { | |
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', | |
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', | |
'Accept-Language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7' | |
} | |
# Faire la requête | |
response = requests.get("https://pcs.marseille-port.fr/webix/public/escales/ferries", headers=headers) | |
response.raise_for_status() | |
# Parser le HTML | |
soup = BeautifulSoup(response.text, 'html.parser') | |
# Initialiser la liste pour les navires | |
ships_data = [] | |
# Trouver la table des ferries | |
table = soup.find('table', class_='table') | |
if not table: | |
return [] | |
# Trouver toutes les lignes de la table (sauf l'en-tête) | |
rows = table.find_all('tr')[1:] # Skip header | |
for row in rows: | |
# Trouver toutes les cellules de la ligne | |
cells = row.find_all('td') | |
if len(cells) >= 6: # Vérifier qu'il y a assez de cellules | |
try: | |
# Extraire les informations | |
navire = cells[1].get_text(strip=True) | |
quai = cells[3].get_text(strip=True) | |
eta = cells[2].get_text(strip=True) # Heure d'arrivée prévue | |
etd = cells[4].get_text(strip=True) # Heure de départ prévue | |
destination = cells[5].get_text(strip=True) # Destination | |
# Créer l'objet navire seulement si au moins une des dates est spécifiée | |
if eta != "-" and etd != "-": | |
ship_data = { | |
'name': navire, | |
'location': quai, | |
'arrival': eta, | |
'departure': etd, | |
'destination': destination | |
} | |
ships_data.append(ship_data) | |
except Exception: | |
continue | |
# Trier par date d'arrivée | |
ships_data.sort(key=lambda x: x['arrival']) | |
return ships_data | |
except Exception: | |
return [] | |
def get_rtm_traffic(): | |
""" | |
Récupère les informations de trafic RTM | |
Returns: | |
dict: Informations de trafic RTM | |
""" | |
rtm = RTMTraffic() | |
traffic_info = rtm.get_alerts() | |
# Organiser les perturbations par type et période | |
organized_traffic = { | |
'current': { | |
'metro': [], | |
'bus': [], | |
'tram': [], | |
'other': [] | |
}, | |
'upcoming': { | |
'metro': [], | |
'bus': [], | |
'tram': [], | |
'other': [] | |
} | |
} | |
def process_alerts(alerts, is_upcoming=False): | |
for alert in alerts: | |
for line in alert['affected_lines']: | |
# Déterminer le type de transport basé sur le numéro de ligne | |
transport_type = 'other' | |
if line.startswith('M'): | |
transport_type = 'metro' | |
elif line.startswith('T'): | |
transport_type = 'tram' | |
elif line.isdigit() or any(c.isdigit() for c in line): | |
transport_type = 'bus' | |
period = 'upcoming' if is_upcoming else 'current' | |
organized_traffic[period][transport_type].append({ | |
'line': line, | |
'title': alert['title'], | |
'description': alert['description'], | |
'validity': alert['validity'], | |
'type': alert['type'] | |
}) | |
# Traiter les alertes du jour | |
if 'AlertesToday' in traffic_info: | |
process_alerts(traffic_info['AlertesToday']) | |
# Traiter les alertes à venir | |
if 'AlertesComing' in traffic_info: | |
process_alerts(traffic_info['AlertesComing'], True) | |
return organized_traffic | |
def index(): | |
return render_template('marseille_traffic.html', api_key=TOMTOM_API_KEY) | |
def get_ship_schedules(): | |
try: | |
# Récupérer les données des ferries | |
ferry_data = scrape_marseille_ferries() | |
return jsonify({ | |
'ferries': ferry_data, | |
'last_update': datetime.now().strftime('%Y-%m-%d %H:%M:%S') | |
}) | |
except Exception as e: | |
return jsonify({ | |
'error': str(e), | |
'ferries': [], | |
'last_update': datetime.now().strftime('%Y-%m-%d %H:%M:%S') | |
}) | |
def get_rtm_traffic_api(): | |
""" | |
Endpoint API pour obtenir les informations de trafic RTM | |
""" | |
traffic_info = get_rtm_traffic() | |
return jsonify(traffic_info) | |
def here_incidents(): | |
incidents = get_traffic_incidents() | |
if incidents is None: | |
return jsonify([]) | |
# Transformer les incidents en format GeoJSON pour la carte | |
features = [] | |
for incident in incidents.get('results', []): | |
# Les coordonnées sont dans le format OLR, nous devons les extraire | |
location = incident['location'] | |
incident_details = incident['incidentDetails'] | |
# Créer une feature GeoJSON pour chaque incident | |
feature = { | |
'type': 'Feature', | |
'geometry': { | |
'type': 'Point', | |
'coordinates': [5.3698, 43.2965] # Pour l'instant, on utilise le centre de Marseille | |
}, | |
'properties': { | |
'id': incident_details['id'], | |
'type': incident_details['type'], | |
'description': incident_details.get('description', {}).get('value', ''), | |
'startTime': incident_details['startTime'], | |
'endTime': incident_details['endTime'], | |
'criticality': incident_details['criticality'], | |
'roadClosed': incident_details.get('roadClosed', False) | |
} | |
} | |
features.append(feature) | |
return jsonify({ | |
'type': 'FeatureCollection', | |
'features': features | |
}) | |
def get_incidents(): | |
# Définir la zone autour de Marseille (rayon approximatif) | |
bounding_box = f"{MARSEILLE_CENTER['lon']-0.1},{MARSEILLE_CENTER['lat']-0.1},{MARSEILLE_CENTER['lon']+0.1},{MARSEILLE_CENTER['lat']+0.1}" | |
# URL pour les incidents | |
incidents_url = f"https://api.tomtom.com/traffic/services/5/incidentDetails?bbox={bounding_box}&fields=%7Bincidents%7Btype%2Cgeometry%7Btype%2Ccoordinates%7D%2Cproperties%7BiconCategory%7D%7D%7D&language=fr-FR&categoryFilter=0%2C1%2C2%2C3%2C4%2C5%2C7%2C8%2C9%2C10%2C11%2C14&timeValidityFilter=present&key={TOMTOM_API_KEY}" | |
try: | |
print(f"Requête TomTom URL: {incidents_url}") | |
# Récupérer les incidents | |
incidents_response = requests.get(incidents_url) | |
incidents_response.raise_for_status() | |
incidents_data = incidents_response.json() | |
print(f"Réponse TomTom brute: {incidents_data}") | |
# Transformer les incidents en format GeoJSON | |
formatted_incidents = [] | |
for incident in incidents_data.get("incidents", []): | |
try: | |
coordinates = incident.get("geometry", {}).get("coordinates", []) | |
if coordinates: | |
formatted_incident = { | |
"type": "Feature", | |
"geometry": { | |
"type": "Point", | |
"coordinates": coordinates[0] if isinstance(coordinates[0], list) else coordinates | |
}, | |
"properties": { | |
"type": incident.get("type", ""), | |
"description": incident.get("description", ""), | |
"from": incident.get("from", ""), | |
"to": incident.get("to", ""), | |
"delay": incident.get("delay", 0), | |
"length": incident.get("length", 0), | |
"iconCategory": incident.get("properties", {}).get("iconCategory", "default") | |
} | |
} | |
formatted_incidents.append(formatted_incident) | |
print(f"Incident formaté: {formatted_incident}") | |
except Exception as e: | |
print(f"Erreur lors du formatage d'un incident : {str(e)}") | |
continue | |
print(f"Nombre total d'incidents formatés: {len(formatted_incidents)}") | |
return jsonify({"incidents": formatted_incidents}) | |
except Exception as e: | |
print(f"Erreur lors de la récupération des incidents : {str(e)}") | |
return jsonify({"error": str(e), "incidents": []}), 500 | |
if __name__ == '__main__': | |
app.run(debug=True) | |