Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,16 +10,10 @@ CLIENT_ID = os.environ.get('client_id')
|
|
| 10 |
API_KEY = os.environ.get('api_key')
|
| 11 |
|
| 12 |
def get_station_info(pattern, client_id, api_key):
|
| 13 |
-
"""Ermittelt EVA-Nummer und offiziellen Namen."""
|
| 14 |
url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/{pattern}"
|
| 15 |
-
headers = {
|
| 16 |
-
'DB-Client-Id': client_id,
|
| 17 |
-
'DB-Api-Key': api_key,
|
| 18 |
-
'accept': 'application/xml'
|
| 19 |
-
}
|
| 20 |
try:
|
| 21 |
response = requests.get(url, headers=headers, timeout=10)
|
| 22 |
-
response.raise_for_status()
|
| 23 |
root = ET.fromstring(response.content)
|
| 24 |
station = root.find('station')
|
| 25 |
if station is not None:
|
|
@@ -29,13 +23,8 @@ def get_station_info(pattern, client_id, api_key):
|
|
| 29 |
return None
|
| 30 |
|
| 31 |
def fetch_timetable_hour(eva, date_str, hour_str, client_id, api_key):
|
| 32 |
-
"""Ruft den Fahrplan für eine bestimmte Stunde ab."""
|
| 33 |
url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/{eva}/{date_str}/{hour_str}"
|
| 34 |
-
headers = {
|
| 35 |
-
'DB-Client-Id': client_id,
|
| 36 |
-
'DB-Api-Key': api_key,
|
| 37 |
-
'accept': 'application/xml'
|
| 38 |
-
}
|
| 39 |
try:
|
| 40 |
response = requests.get(url, headers=headers, timeout=10)
|
| 41 |
if response.status_code == 200:
|
|
@@ -45,12 +34,9 @@ def fetch_timetable_hour(eva, date_str, hour_str, client_id, api_key):
|
|
| 45 |
return None
|
| 46 |
|
| 47 |
def search_next_3_connections(departure, destination, client_id, api_key):
|
| 48 |
-
"""Hauptfunktion: Findet die nächsten 3 Verbindungen ab 'jetzt'."""
|
| 49 |
-
|
| 50 |
if not all([departure, destination, client_id, api_key]):
|
| 51 |
return {"error": "Bitte alle Felder ausfüllen."}
|
| 52 |
|
| 53 |
-
# 1. IBNRs ermitteln
|
| 54 |
dep_info = get_station_info(departure, client_id, api_key)
|
| 55 |
dest_info = get_station_info(destination, client_id, api_key)
|
| 56 |
|
|
@@ -60,8 +46,7 @@ def search_next_3_connections(departure, destination, client_id, api_key):
|
|
| 60 |
now = datetime.now()
|
| 61 |
found_connections = []
|
| 62 |
|
| 63 |
-
|
| 64 |
-
for hour_offset in range(3): # Wir schauen bis zu 2 Stunden voraus
|
| 65 |
search_time = now + timedelta(hours=hour_offset)
|
| 66 |
datum = search_time.strftime("%y%m%d")
|
| 67 |
stunde = search_time.strftime("%H")
|
|
@@ -71,61 +56,78 @@ def search_next_3_connections(departure, destination, client_id, api_key):
|
|
| 71 |
|
| 72 |
for train in root.findall('.//s'):
|
| 73 |
dp = train.find('dp')
|
| 74 |
-
|
|
|
|
| 75 |
|
| 76 |
-
#
|
| 77 |
path = dp.get('ppth', '')
|
| 78 |
if dest_info['name'].lower() in path.lower() or destination.lower() in path.lower():
|
| 79 |
-
|
| 80 |
-
pt = dp.get('pt', '') # Geplante Zeit: YYMMDDHHMM
|
| 81 |
if not pt: continue
|
| 82 |
|
| 83 |
dep_dt = datetime.strptime(pt, "%y%m%d%H%M")
|
| 84 |
|
| 85 |
-
# Nur Verbindungen in der Zukunft (ab Zeitstempel jetzt)
|
| 86 |
if dep_dt >= now:
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
found_connections.append({
|
| 89 |
"startort": dep_info['name'],
|
| 90 |
"zielort": dest_info['name'],
|
| 91 |
"abfahrtszeit": dep_dt.strftime("%H:%M"),
|
|
|
|
| 92 |
"gleis": dp.get('pp', 'n/a'),
|
| 93 |
-
"
|
| 94 |
-
"_dt": dep_dt
|
| 95 |
})
|
| 96 |
|
| 97 |
-
# 3. Sortieren und Top 3 auswählen
|
| 98 |
found_connections.sort(key=lambda x: x['_dt'])
|
| 99 |
|
| 100 |
-
#
|
| 101 |
-
|
| 102 |
-
for c in found_connections[:3]:
|
| 103 |
-
# Entferne das interne Datetime-Objekt vor der JSON-Konvertierung
|
| 104 |
-
res = {k: v for k, v in c.items() if k != "_dt"}
|
| 105 |
-
final_results.append(res)
|
| 106 |
-
|
| 107 |
-
return final_results
|
| 108 |
|
| 109 |
# --- Gradio UI ---
|
| 110 |
|
| 111 |
def ui_wrapper(dep, dest):
|
| 112 |
if not CLIENT_ID or not API_KEY:
|
| 113 |
-
return {"error": "API credentials not configured.
|
| 114 |
|
| 115 |
results = search_next_3_connections(dep, dest, CLIENT_ID, API_KEY)
|
| 116 |
-
return
|
| 117 |
|
| 118 |
with gr.Blocks(title="DB JSON Fahrplan", theme=gr.themes.Soft()) as demo:
|
| 119 |
gr.Markdown("# │ DB │ Deutsche Bahn train connections")
|
| 120 |
|
| 121 |
with gr.Row():
|
| 122 |
-
dep_input = gr.Textbox(label="Abfahrtsort", placeholder="z.B.
|
| 123 |
-
dest_input = gr.Textbox(label="Zielort", placeholder="z.B.
|
| 124 |
|
| 125 |
btn = gr.Button("Suchen", variant="primary")
|
| 126 |
-
output = gr.JSON(label="JSON Ergebnis")
|
| 127 |
|
| 128 |
btn.click(fn=ui_wrapper, inputs=[dep_input, dest_input], outputs=output)
|
| 129 |
|
| 130 |
if __name__ == "__main__":
|
| 131 |
-
demo.launch(
|
|
|
|
| 10 |
API_KEY = os.environ.get('api_key')
|
| 11 |
|
| 12 |
def get_station_info(pattern, client_id, api_key):
|
|
|
|
| 13 |
url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/{pattern}"
|
| 14 |
+
headers = {'DB-Client-Id': client_id, 'DB-Api-Key': api_key, 'accept': 'application/xml'}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
try:
|
| 16 |
response = requests.get(url, headers=headers, timeout=10)
|
|
|
|
| 17 |
root = ET.fromstring(response.content)
|
| 18 |
station = root.find('station')
|
| 19 |
if station is not None:
|
|
|
|
| 23 |
return None
|
| 24 |
|
| 25 |
def fetch_timetable_hour(eva, date_str, hour_str, client_id, api_key):
|
|
|
|
| 26 |
url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/{eva}/{date_str}/{hour_str}"
|
| 27 |
+
headers = {'DB-Client-Id': client_id, 'DB-Api-Key': api_key, 'accept': 'application/xml'}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
try:
|
| 29 |
response = requests.get(url, headers=headers, timeout=10)
|
| 30 |
if response.status_code == 200:
|
|
|
|
| 34 |
return None
|
| 35 |
|
| 36 |
def search_next_3_connections(departure, destination, client_id, api_key):
|
|
|
|
|
|
|
| 37 |
if not all([departure, destination, client_id, api_key]):
|
| 38 |
return {"error": "Bitte alle Felder ausfüllen."}
|
| 39 |
|
|
|
|
| 40 |
dep_info = get_station_info(departure, client_id, api_key)
|
| 41 |
dest_info = get_station_info(destination, client_id, api_key)
|
| 42 |
|
|
|
|
| 46 |
now = datetime.now()
|
| 47 |
found_connections = []
|
| 48 |
|
| 49 |
+
for hour_offset in range(3):
|
|
|
|
| 50 |
search_time = now + timedelta(hours=hour_offset)
|
| 51 |
datum = search_time.strftime("%y%m%d")
|
| 52 |
stunde = search_time.strftime("%H")
|
|
|
|
| 56 |
|
| 57 |
for train in root.findall('.//s'):
|
| 58 |
dp = train.find('dp')
|
| 59 |
+
tl = train.find('tl')
|
| 60 |
+
if dp is None or tl is None: continue
|
| 61 |
|
| 62 |
+
# Check if destination is in the path
|
| 63 |
path = dp.get('ppth', '')
|
| 64 |
if dest_info['name'].lower() in path.lower() or destination.lower() in path.lower():
|
| 65 |
+
pt = dp.get('pt', '')
|
|
|
|
| 66 |
if not pt: continue
|
| 67 |
|
| 68 |
dep_dt = datetime.strptime(pt, "%y%m%d%H%M")
|
| 69 |
|
|
|
|
| 70 |
if dep_dt >= now:
|
| 71 |
+
# To get arrival time, we must check the destination station for the same train ID
|
| 72 |
+
# For performance, we assume arrival is within +2 hours of departure
|
| 73 |
+
arr_time_str = "N/A"
|
| 74 |
+
duration_str = "N/A"
|
| 75 |
+
|
| 76 |
+
# Search destination timetable for the arrival
|
| 77 |
+
# We check the hour of departure and the next hour for arrival
|
| 78 |
+
arrival_root = fetch_timetable_hour(dest_info['eva'], dep_dt.strftime("%y%m%d"), dep_dt.strftime("%H"), client_id, api_key)
|
| 79 |
+
|
| 80 |
+
if arrival_root is not None:
|
| 81 |
+
# Find the train by number (n) and category (c)
|
| 82 |
+
for arr_train in arrival_root.findall('.//s'):
|
| 83 |
+
arr_tl = arr_train.find('tl')
|
| 84 |
+
arr_ar = arr_train.find('ar')
|
| 85 |
+
if arr_tl is not None and arr_ar is not None:
|
| 86 |
+
if arr_tl.get('n') == tl.get('n'):
|
| 87 |
+
arr_pt = arr_ar.get('pt', '')
|
| 88 |
+
arr_dt = datetime.strptime(arr_pt, "%y%m%d%H%M")
|
| 89 |
+
arr_time_str = arr_dt.strftime("%H:%M")
|
| 90 |
+
|
| 91 |
+
# Calculate duration
|
| 92 |
+
diff = arr_dt - dep_dt
|
| 93 |
+
duration_str = f"{int(diff.total_seconds() // 60)} min"
|
| 94 |
+
break
|
| 95 |
+
|
| 96 |
found_connections.append({
|
| 97 |
"startort": dep_info['name'],
|
| 98 |
"zielort": dest_info['name'],
|
| 99 |
"abfahrtszeit": dep_dt.strftime("%H:%M"),
|
| 100 |
+
"ankunftszeitszeit": arr_time_str,
|
| 101 |
"gleis": dp.get('pp', 'n/a'),
|
| 102 |
+
"duration": duration_str,
|
| 103 |
+
"_dt": dep_dt
|
| 104 |
})
|
| 105 |
|
|
|
|
| 106 |
found_connections.sort(key=lambda x: x['_dt'])
|
| 107 |
|
| 108 |
+
# Return list of first 3 matches formatted without internal sort key
|
| 109 |
+
return [{k: v for k, v in c.items() if k != "_dt"} for c in found_connections[:3]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
# --- Gradio UI ---
|
| 112 |
|
| 113 |
def ui_wrapper(dep, dest):
|
| 114 |
if not CLIENT_ID or not API_KEY:
|
| 115 |
+
return {"error": "API credentials not configured."}
|
| 116 |
|
| 117 |
results = search_next_3_connections(dep, dest, CLIENT_ID, API_KEY)
|
| 118 |
+
return results # Return list directly for gr.JSON
|
| 119 |
|
| 120 |
with gr.Blocks(title="DB JSON Fahrplan", theme=gr.themes.Soft()) as demo:
|
| 121 |
gr.Markdown("# │ DB │ Deutsche Bahn train connections")
|
| 122 |
|
| 123 |
with gr.Row():
|
| 124 |
+
dep_input = gr.Textbox(label="Abfahrtsort", placeholder="z.B. Schweinfurt Hbf")
|
| 125 |
+
dest_input = gr.Textbox(label="Zielort", placeholder="z.B. Oerlenbach")
|
| 126 |
|
| 127 |
btn = gr.Button("Suchen", variant="primary")
|
| 128 |
+
output = gr.JSON(label="JSON Ergebnis")
|
| 129 |
|
| 130 |
btn.click(fn=ui_wrapper, inputs=[dep_input, dest_input], outputs=output)
|
| 131 |
|
| 132 |
if __name__ == "__main__":
|
| 133 |
+
demo.launch()
|