mgokg commited on
Commit
21dc540
·
verified ·
1 Parent(s): 3950cf5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -68
app.py CHANGED
@@ -5,6 +5,7 @@ import xml.etree.ElementTree as ET
5
  import json
6
 
7
  def get_station_info(pattern, client_id, api_key):
 
8
  url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/{pattern}"
9
  headers = {'DB-Client-Id': client_id, 'DB-Api-Key': api_key, 'accept': 'application/xml'}
10
  try:
@@ -13,11 +14,12 @@ def get_station_info(pattern, client_id, api_key):
13
  station = root.find('station')
14
  if station is not None:
15
  return {'name': station.get('name'), 'eva': station.get('eva')}
16
- except: return None
 
17
  return None
18
 
19
  def get_arrival_info(trip_id, dest_eva, client_id, api_key):
20
- """Holt die Ankunftszeit am Zielort für eine spezifische Fahrt."""
21
  url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/journey/{trip_id}"
22
  headers = {'DB-Client-Id': client_id, 'DB-Api-Key': api_key, 'accept': 'application/xml'}
23
  try:
@@ -29,35 +31,40 @@ def get_arrival_info(trip_id, dest_eva, client_id, api_key):
29
  ar = stop.find('ar')
30
  if ar is not None:
31
  return ar.get('pt') # Format: YYMMDDHHMM
32
- except: pass
 
33
  return None
34
 
35
  def calculate_duration(dep_raw, arr_raw):
36
- """Berechnet die Reisedauer."""
37
  try:
38
  fmt = "%y%m%d%H%M"
39
- diff = datetime.strptime(arr_raw, fmt) - datetime.strptime(dep_raw, fmt)
40
- hours, remainder = divmod(diff.total_seconds(), 3600)
 
 
41
  minutes = remainder // 60
42
- return f"{int(hours)}h {int(minutes)}min"
43
- except: return "n/a"
 
44
 
45
- def get_connections(departure, destination, client_id, api_key):
46
  if not all([departure, destination, client_id, api_key]):
47
- return None, "❌ Bitte alle Felder ausfüllen."
48
 
49
  dep_info = get_station_info(departure, client_id, api_key)
50
  dest_info = get_station_info(destination, client_id, api_key)
51
-
52
  if not dep_info or not dest_info:
53
- return None, "❌ Bahnhof nicht gefunden."
54
 
55
  now = datetime.now()
56
- found_connections = []
57
-
58
- # Suche über 12 Stunden hinweg, um sicher 3 Verbindungen zu finden
59
  for hour_offset in range(12):
60
- if len(found_connections) >= 3: break
 
61
 
62
  search_time = now + timedelta(hours=hour_offset)
63
  datum = search_time.strftime("%y%m%d")
@@ -68,81 +75,85 @@ def get_connections(departure, destination, client_id, api_key):
68
 
69
  try:
70
  response = requests.get(url, headers=headers, timeout=10)
71
- if response.status_code != 200: continue
72
- root = ET.fromstring(response.content)
73
 
74
- for train in root.findall('.//s'):
75
- dp = train.find('dp')
76
- if dp is None: continue
 
 
77
 
78
  path = dp.get('ppth', '')
79
- # Suche im Pfad nach dem Namen oder der EVA des Ziels
80
  if dest_info['name'].lower() in path.lower() or destination.lower() in path.lower():
81
  dep_raw = dp.get('pt', '')
82
  dep_dt = datetime.strptime(dep_raw, "%y%m%d%H%M")
83
 
84
- if dep_dt >= now:
85
- # Journey Details für Ankunft und Dauer
86
- trip_id = train.get('id')
87
  arr_raw = get_arrival_info(trip_id, dest_info['eva'], client_id, api_key)
88
 
89
- arrival_time = datetime.strptime(arr_raw, "%y%m%d%H%M").strftime("%H:%M") if arr_raw else "n/a"
90
- duration = calculate_duration(dep_raw, arr_raw) if arr_raw else "n/a"
91
-
92
- tl = train.find('tl')
93
- found_connections.append({
94
- "zug": f"{tl.get('c', '')}{tl.get('n', '')}",
95
- "abfahrt": dep_dt.strftime("%H:%M"),
96
- "ankunft": arrival_time,
97
- "dauer": duration,
98
- "gleis": dp.get('pp', '-'),
99
- "start": dep_info['name'],
100
- "ziel": dest_info['name'],
101
- "_dt": dep_dt
102
- })
103
- if len(found_connections) >= 3: break
104
- except: continue
 
 
105
 
106
- found_connections.sort(key=lambda x: x['_dt'])
107
- return found_connections[:3], None
108
 
109
- def main_interface(dep, dest, cid, akey):
110
- conns, error = get_connections(dep, dest, cid, akey)
111
- if error: return error, json.dumps({"error": error})
112
- if not conns: return "Keine Verbindungen gefunden.", "[]"
113
 
114
- # JSON-Output (Clean)
115
- json_output = [{k: v for k, v in c.items() if k != "_dt"} for c in conns]
 
 
116
 
117
- # Professional Markdown Table
 
 
 
118
  md = f"### 🚆 {conns[0]['start']} → {conns[0]['ziel']}\n\n"
119
- md += "| Zug | Abfahrt | Gleis |\n"
120
- md += "|:---|:---|:---|\n"
121
- for c in json_output:
122
- md += f"| **{c['zug']}** | {c['abfahrt']} | {c['gleis']} |\n"
123
 
124
- md += f"\n\n*Abfrage: {datetime.now().strftime('%H:%M')}*"
125
 
126
- return md, json.dumps(json_output, indent=4, ensure_ascii=False)
127
 
128
  # --- Gradio UI ---
129
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
130
- gr.Markdown("# 🚄 DB Verbindungs-Check")
131
-
132
  with gr.Row():
133
- cid_input = gr.Textbox(label="Client ID", type="password")
134
- akey_input = gr.Textbox(label="API Key", type="password")
135
-
136
  with gr.Row():
137
- dep_input = gr.Textbox(label="Start", placeholder="z.B. Berlin Hbf")
138
- dest_input = gr.Textbox(label="Ziel", placeholder="z.B. Hamburg Hbf")
139
-
140
- btn = gr.Button("Suchen", variant="primary")
141
 
142
- md_display = gr.Markdown()
143
- json_display = gr.Code(label="JSON", language="json")
144
 
145
- btn.click(main_interface, [dep_input, dest_input, cid_input, akey_input], [md_display, json_display])
 
 
 
146
 
147
  if __name__ == "__main__":
148
  demo.launch()
 
5
  import json
6
 
7
  def get_station_info(pattern, client_id, api_key):
8
+ """Sucht Bahnhof und gibt Name sowie EVA-Nummer zurück."""
9
  url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/{pattern}"
10
  headers = {'DB-Client-Id': client_id, 'DB-Api-Key': api_key, 'accept': 'application/xml'}
11
  try:
 
14
  station = root.find('station')
15
  if station is not None:
16
  return {'name': station.get('name'), 'eva': station.get('eva')}
17
+ except:
18
+ return None
19
  return None
20
 
21
  def get_arrival_info(trip_id, dest_eva, client_id, api_key):
22
+ """Ruft die Ankunftszeit für eine spezifische trip_id am Ziel-Bahnhof ab."""
23
  url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/journey/{trip_id}"
24
  headers = {'DB-Client-Id': client_id, 'DB-Api-Key': api_key, 'accept': 'application/xml'}
25
  try:
 
31
  ar = stop.find('ar')
32
  if ar is not None:
33
  return ar.get('pt') # Format: YYMMDDHHMM
34
+ except:
35
+ pass
36
  return None
37
 
38
  def calculate_duration(dep_raw, arr_raw):
39
+ """Berechnet die Reisedauer aus zwei Zeitstempeln (YYMMDDHHMM)."""
40
  try:
41
  fmt = "%y%m%d%H%M"
42
+ dep_dt = datetime.strptime(dep_raw, fmt)
43
+ arr_dt = datetime.strptime(arr_raw, fmt)
44
+ diff = arr_dt - dep_dt
45
+ hours, remainder = divmod(int(diff.total_seconds()), 3600)
46
  minutes = remainder // 60
47
+ return f"{hours}h {minutes}min"
48
+ except:
49
+ return "n/a"
50
 
51
+ def search_connections(departure, destination, client_id, api_key):
52
  if not all([departure, destination, client_id, api_key]):
53
+ return "❌ Bitte alle Felder ausfüllen.", []
54
 
55
  dep_info = get_station_info(departure, client_id, api_key)
56
  dest_info = get_station_info(destination, client_id, api_key)
57
+
58
  if not dep_info or not dest_info:
59
+ return "❌ Bahnhof nicht gefunden. Bitte Schreibweise prüfen.", []
60
 
61
  now = datetime.now()
62
+ found_conns = []
63
+
64
+ # Suche über die nächsten 12 Stunden hinweg
65
  for hour_offset in range(12):
66
+ if len(found_conns) >= 3:
67
+ break
68
 
69
  search_time = now + timedelta(hours=hour_offset)
70
  datum = search_time.strftime("%y%m%d")
 
75
 
76
  try:
77
  response = requests.get(url, headers=headers, timeout=10)
78
+ if response.status_code != 200:
79
+ continue
80
 
81
+ root = ET.fromstring(response.content)
82
+ for s in root.findall('.//s'):
83
+ dp = s.find('dp')
84
+ if dp is None:
85
+ continue
86
 
87
  path = dp.get('ppth', '')
88
+ # Prüfen ob Ziel im Fahrtverlauf
89
  if dest_info['name'].lower() in path.lower() or destination.lower() in path.lower():
90
  dep_raw = dp.get('pt', '')
91
  dep_dt = datetime.strptime(dep_raw, "%y%m%d%H%M")
92
 
93
+ if dep_dt >= (now - timedelta(minutes=2)):
94
+ trip_id = s.get('id')
 
95
  arr_raw = get_arrival_info(trip_id, dest_info['eva'], client_id, api_key)
96
 
97
+ if arr_raw:
98
+ tl = s.find('tl')
99
+ arr_dt = datetime.strptime(arr_raw, "%y%m%d%H%M")
100
+
101
+ found_conns.append({
102
+ "zug": f"{tl.get('c','')}{tl.get('n','')}",
103
+ "abfahrt": dep_dt.strftime("%H:%M"),
104
+ "ankunft": arr_dt.strftime("%H:%M"),
105
+ "dauer": calculate_duration(dep_raw, arr_raw),
106
+ "gleis": dp.get('pp', '-'),
107
+ "start": dep_info['name'],
108
+ "ziel": dest_info['name'],
109
+ "_dt": dep_dt
110
+ })
111
+ if len(found_conns) >= 3:
112
+ break
113
+ except:
114
+ continue
115
 
116
+ found_conns.sort(key=lambda x: x['_dt'])
117
+ return None, found_conns[:3]
118
 
119
+ def main(departure, destination, client_id, api_key):
120
+ err, conns = search_connections(departure, destination, client_id, api_key)
 
 
121
 
122
+ if err:
123
+ return err, json.dumps({"error": err})
124
+ if not conns:
125
+ return "Keine Direktverbindungen gefunden.", "[]"
126
 
127
+ # JSON Output (ohne internes Sortierfeld)
128
+ clean_json = [{k: v for k, v in c.items() if k != "_dt"} for c in conns]
129
+
130
+ # Markdown Tabelle (Flat & Professional)
131
  md = f"### 🚆 {conns[0]['start']} → {conns[0]['ziel']}\n\n"
132
+ md += "| Zug | Abfahrt | Ankunft | Dauer | Gleis |\n"
133
+ md += "|:---|:---|:---|:---|:---|\n"
134
+ for c in clean_json:
135
+ md += f"| **{c['zug']}** | {c['abfahrt']} | {c['ankunft']} | {c['dauer']} | {c['gleis']} |\n"
136
 
137
+ md += f"\n\n*Abfragezeitpunkt: {datetime.now().strftime('%H:%M')}*"
138
 
139
+ return md, clean_json
140
 
141
  # --- Gradio UI ---
142
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
143
+ gr.Markdown("# 🚄 DB Timetable Service")
 
144
  with gr.Row():
145
+ cid = gr.Textbox(label="Client ID", type="password")
146
+ akey = gr.Textbox(label="API Key", type="password")
 
147
  with gr.Row():
148
+ start = gr.Textbox(label="Start", placeholder="z.B. Berlin Hbf")
149
+ ziel = gr.Textbox(label="Ziel", placeholder="z.B. Hamburg Hbf")
 
 
150
 
151
+ btn = gr.Button("Nächste 3 Verbindungen suchen", variant="primary")
 
152
 
153
+ output_md = gr.Markdown()
154
+ output_json = gr.JSON(label="JSON Result")
155
+
156
+ btn.click(main, [start, ziel, cid, akey], [output_md, output_json])
157
 
158
  if __name__ == "__main__":
159
  demo.launch()