mgokg commited on
Commit
015eda9
·
verified ·
1 Parent(s): 655abac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -41
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
- # 2. Suche in der aktuellen und der nächsten Stunde (um 3 Verbindungen zu garantieren)
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
- if dp is None: continue
 
75
 
76
- # Prüfen ob Ziel im geplanten Pfad (ppth)
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
- tl = train.find('tl')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- "zug": f"{tl.get('c', '')} {tl.get('n', '')}",
94
- "_dt": dep_dt # Hilfsfeld zum Sortieren
95
  })
96
 
97
- # 3. Sortieren und Top 3 auswählen
98
  found_connections.sort(key=lambda x: x['_dt'])
99
 
100
- # Bereinigen der Ergebnisse für die JSON-Ausgabe
101
- final_results = []
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. Please set client_id and api_key as environment variables."}
114
 
115
  results = search_next_3_connections(dep, dest, CLIENT_ID, API_KEY)
116
- return json.dumps(results, indent=4, ensure_ascii=False)
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. Berlin")
123
- dest_input = gr.Textbox(label="Zielort", placeholder="z.B. Hamburg")
124
 
125
  btn = gr.Button("Suchen", variant="primary")
126
- output = gr.JSON(label="JSON Ergebnis") # Nutzt Gradio JSON Komponente für schöneres Format
127
 
128
  btn.click(fn=ui_wrapper, inputs=[dep_input, dest_input], outputs=output)
129
 
130
  if __name__ == "__main__":
131
- demo.launch(mcp_server=True)
 
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()