mgokg commited on
Commit
d4fbc40
·
verified ·
1 Parent(s): 23f05a3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -63
app.py CHANGED
@@ -7,7 +7,11 @@ import json
7
  def get_station_info(pattern, client_id, api_key):
8
  """Ermittelt EVA-Nummer und offiziellen Namen."""
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:
12
  response = requests.get(url, headers=headers, timeout=10)
13
  response.raise_for_status()
@@ -22,7 +26,11 @@ def get_station_info(pattern, client_id, api_key):
22
  def fetch_timetable_hour(eva, date_str, hour_str, client_id, api_key):
23
  """Ruft den Fahrplan für eine bestimmte Stunde ab."""
24
  url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/{eva}/{date_str}/{hour_str}"
25
- headers = {'DB-Client-Id': client_id, 'DB-Api-Key': api_key, 'accept': 'application/xml'}
 
 
 
 
26
  try:
27
  response = requests.get(url, headers=headers, timeout=10)
28
  if response.status_code == 200:
@@ -31,22 +39,24 @@ def fetch_timetable_hour(eva, date_str, hour_str, client_id, api_key):
31
  pass
32
  return None
33
 
34
- def get_connections(departure, destination, client_id, api_key):
35
- """Sucht die nächsten 3 Verbindungen ab jetzt."""
 
36
  if not all([departure, destination, client_id, api_key]):
37
- return None, "Bitte alle Felder ausfüllen."
38
 
 
39
  dep_info = get_station_info(departure, client_id, api_key)
40
  dest_info = get_station_info(destination, client_id, api_key)
41
 
42
- if not dep_info or not dest_info:
43
- return None, " Bahnhof nicht gefunden. Bitte Namen prüfen."
44
 
45
  now = datetime.now()
46
  found_connections = []
47
 
48
- # Suche über 3 Stunden-Slots hinweg (aktuelle + nächste zwei)
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")
@@ -58,85 +68,61 @@ def get_connections(departure, destination, client_id, api_key):
58
  dp = train.find('dp')
59
  if dp is None: continue
60
 
 
61
  path = dp.get('ppth', '')
62
  if dest_info['name'].lower() in path.lower() or destination.lower() in path.lower():
63
- pt = dp.get('pt', '')
 
64
  if not pt: continue
65
 
66
  dep_dt = datetime.strptime(pt, "%y%m%d%H%M")
67
 
 
68
  if dep_dt >= now:
69
  tl = train.find('tl')
70
  found_connections.append({
71
  "startort": dep_info['name'],
72
  "zielort": dest_info['name'],
73
- "abfahrtszeit": dep_dt.strftime("%H:%M"),
74
- "datum": dep_dt.strftime("%d.%m.%Y"),
75
- "gleis": dp.get('pp', '-'),
76
  "zug": f"{tl.get('c', '')} {tl.get('n', '')}",
77
- "_dt": dep_dt
78
  })
79
 
 
80
  found_connections.sort(key=lambda x: x['_dt'])
81
- return found_connections[:3], None
82
-
83
- def format_markdown(connections):
84
- """Erstellt eine Smartphone-optimierte Markdown-Ansicht."""
85
- if not connections:
86
- return "### ℹ️ Keine Verbindungen gefunden\nIn den nächsten 3 Stunden wurden keine Direktverbindungen gefunden."
87
-
88
- md = f"## 🚆 {connections[0]['startort']} → {connections[0]['zielort']}\n"
89
- md += f"*{datetime.now().strftime('%H:%M')} Uhr Stand*\n\n"
90
 
91
- for i, c in enumerate(connections, 1):
92
- md += f"### {i}. {c['abfahrtszeit']} Uhr\n"
93
- md += f"**{c['zug']}** \n"
94
- md += f"Gleis: **{c['gleis']}** | Datum: {c['datum']}\n"
95
- md += "---\n" # Trennlinie für bessere Lesbarkeit auf dem Handy
96
-
97
- return md
98
 
99
- def main_interface(dep, dest, cid, akey):
100
- conns, error = get_connections(dep, dest, cid, akey)
101
-
102
- if error:
103
- return error, json.dumps({"error": error}, indent=2)
104
-
105
- # JSON-Bereinigung (internes Sortierfeld entfernen)
106
- clean_json = []
107
- for c in conns:
108
- clean_json.append({k: v for k, v in c.items() if k != "_dt"})
109
-
110
- md_output = format_markdown(clean_json)
111
- json_output = json.dumps(clean_json, indent=4, ensure_ascii=False)
112
-
113
- return md_output, json_output
114
 
115
  # --- Gradio UI ---
116
- with gr.Blocks(title="DB Mobile Fahrplan", theme=gr.themes.Soft()) as demo:
117
- gr.Markdown("# 📱 DB Quick-Check")
 
 
 
 
 
 
118
 
119
- with gr.Accordion("⚙️ API-Konfiguration (Hier klicken)", open=False):
120
  cid_input = gr.Textbox(label="DB Client ID", type="password")
121
  akey_input = gr.Textbox(label="DB API Key", type="password")
122
 
123
  with gr.Row():
124
- dep_input = gr.Textbox(label="Von", placeholder="z.B. Berlin", scale=2)
125
- dest_input = gr.Textbox(label="Nach", placeholder="z.B. Hamburg", scale=2)
126
-
127
- btn = gr.Button("🔍 Verbindungen suchen", variant="primary")
 
128
 
129
- with gr.Tabs():
130
- with gr.TabItem("📱 Ansicht"):
131
- md_display = gr.Markdown("Geben Sie Start und Ziel ein und drücken Sie Suchen.")
132
- with gr.TabItem("💻 JSON"):
133
- json_display = gr.Code(label="API Response", language="json")
134
-
135
- btn.click(
136
- fn=main_interface,
137
- inputs=[dep_input, dest_input, cid_input, akey_input],
138
- outputs=[md_display, json_display]
139
- )
140
 
141
  if __name__ == "__main__":
142
  demo.launch()
 
7
  def get_station_info(pattern, client_id, api_key):
8
  """Ermittelt EVA-Nummer und offiziellen Namen."""
9
  url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/{pattern}"
10
+ headers = {
11
+ 'DB-Client-Id': client_id,
12
+ 'DB-Api-Key': api_key,
13
+ 'accept': 'application/xml'
14
+ }
15
  try:
16
  response = requests.get(url, headers=headers, timeout=10)
17
  response.raise_for_status()
 
26
  def fetch_timetable_hour(eva, date_str, hour_str, client_id, api_key):
27
  """Ruft den Fahrplan für eine bestimmte Stunde ab."""
28
  url = f"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/plan/{eva}/{date_str}/{hour_str}"
29
+ headers = {
30
+ 'DB-Client-Id': client_id,
31
+ 'DB-Api-Key': api_key,
32
+ 'accept': 'application/xml'
33
+ }
34
  try:
35
  response = requests.get(url, headers=headers, timeout=10)
36
  if response.status_code == 200:
 
39
  pass
40
  return None
41
 
42
+ def search_next_3_connections(departure, destination, client_id, api_key):
43
+ """Hauptfunktion: Findet die nächsten 3 Verbindungen ab 'jetzt'."""
44
+
45
  if not all([departure, destination, client_id, api_key]):
46
+ return {"error": "Bitte alle Felder ausfüllen."}
47
 
48
+ # 1. IBNRs ermitteln
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: return {"error": f"Startort '{departure}' nicht gefunden."}
53
+ if not dest_info: return {"error": f"Zielort '{destination}' nicht gefunden."}
54
 
55
  now = datetime.now()
56
  found_connections = []
57
 
58
+ # 2. Suche in der aktuellen und der nächsten Stunde (um 3 Verbindungen zu garantieren)
59
+ for hour_offset in range(3): # Wir schauen bis zu 2 Stunden voraus
60
  search_time = now + timedelta(hours=hour_offset)
61
  datum = search_time.strftime("%y%m%d")
62
  stunde = search_time.strftime("%H")
 
68
  dp = train.find('dp')
69
  if dp is None: continue
70
 
71
+ # Prüfen ob Ziel im geplanten Pfad (ppth)
72
  path = dp.get('ppth', '')
73
  if dest_info['name'].lower() in path.lower() or destination.lower() in path.lower():
74
+
75
+ pt = dp.get('pt', '') # Geplante Zeit: YYMMDDHHMM
76
  if not pt: continue
77
 
78
  dep_dt = datetime.strptime(pt, "%y%m%d%H%M")
79
 
80
+ # Nur Verbindungen in der Zukunft (ab Zeitstempel jetzt)
81
  if dep_dt >= now:
82
  tl = train.find('tl')
83
  found_connections.append({
84
  "startort": dep_info['name'],
85
  "zielort": dest_info['name'],
86
+ "abfahrtszeit": dep_dt.strftime("%d.%m.%Y %H:%M"),
87
+ "gleis": dp.get('pp', 'n/a'),
 
88
  "zug": f"{tl.get('c', '')} {tl.get('n', '')}",
89
+ "_dt": dep_dt # Hilfsfeld zum Sortieren
90
  })
91
 
92
+ # 3. Sortieren und Top 3 auswählen
93
  found_connections.sort(key=lambda x: x['_dt'])
 
 
 
 
 
 
 
 
 
94
 
95
+ # Bereinigen der Ergebnisse für die JSON-Ausgabe
96
+ final_results = []
97
+ for c in found_connections[:3]:
98
+ # Entferne das interne Datetime-Objekt vor der JSON-Konvertierung
99
+ res = {k: v for k, v in c.items() if k != "_dt"}
100
+ final_results.append(res)
 
101
 
102
+ return final_results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  # --- Gradio UI ---
105
+
106
+ def ui_wrapper(dep, dest, cid, akey):
107
+ results = search_next_3_connections(dep, dest, cid, akey)
108
+ return json.dumps(results, indent=4, ensure_ascii=False)
109
+
110
+ with gr.Blocks(title="DB JSON Fahrplan", theme=gr.themes.Soft()) as demo:
111
+ gr.Markdown("# 🚆 DB Verbindungs-Suche (JSON)")
112
+ gr.Markdown("Ermittelt die nächsten 3 Verbindungen ab dem aktuellen Zeitstempel.")
113
 
114
+ with gr.Row():
115
  cid_input = gr.Textbox(label="DB Client ID", type="password")
116
  akey_input = gr.Textbox(label="DB API Key", type="password")
117
 
118
  with gr.Row():
119
+ dep_input = gr.Textbox(label="Abfahrtsort", placeholder="z.B. Berlin")
120
+ dest_input = gr.Textbox(label="Zielort", placeholder="z.B. Hamburg")
121
+
122
+ btn = gr.Button("Suchen", variant="primary")
123
+ output = gr.JSON(label="JSON Ergebnis") # Nutzt Gradio JSON Komponente für schöneres Format
124
 
125
+ btn.click(fn=ui_wrapper, inputs=[dep_input, dest_input, cid_input, akey_input], outputs=output)
 
 
 
 
 
 
 
 
 
 
126
 
127
  if __name__ == "__main__":
128
  demo.launch()