This commit is contained in:
Sophia S 2026-06-17 10:05:42 +02:00
commit e66d33eefa
3 changed files with 144 additions and 58 deletions

110
README.md
View file

@ -1,23 +1,95 @@
# hafas-terminal-app
## Description
The goal is to access the Hafas API to retrieve Data about the public transport network. Most important is to achieve Station to Station Routeplanning and Station Arrival/Departure Information.
A Python-based terminal application for accessing public transport information via the HAFAS API. This tool provides station-to-station route planning and real-time arrival/departure information for public transport networks.
## Dependencies
- Python
- prompt_toolkit (tui)
- kivy (mobile-ui) ((optional!!!))
- requests
- json
- Hafas API Endpoint
- Git
## Features
## Timetable
- ~~22.04.2026 - Short project presentation: 3min per Group, incl. UseCase, GitProject, Issues~~
- ~~29.04.2026 - Scrum-Round mia/hafas-terminal-app#1~~
- ~~06.05.2026 - Scrum-Round mia/hafas-terminal-app#2, optionally mia/hafas-terminal-app#3 and mia/hafas-terminal-app#4~~
- 13.05.2026 - Scrum-Round #3, #4, #5
- 20.05.2026 - Scrum-Round
- 27.05.2026 - Scrum-Round
- 03.06.2026 - Scrum-Round
- 10.06.2026 - Presentations
- **Route Planning**: Find optimal routes between stations
- **Station Information**: View real-time arrivals and departures
- **Terminal UI**: User-friendly command-line interface using prompt_toolkit
- **Multiple Transport Methods**: Support for various public transport modes
## Requirements
### Core Dependencies
- **Python 3.7+**: Programming language
- **Hafas API Endpoint**: Access to a HAFAS public transport API
### Python Packages
- `prompt_toolkit`: Terminal user interface (TUI)
- `requests`: HTTP library for API communication
- `kivy` (optional): Mobile UI support for future mobile versions
### Tools
- `Git`: Version control
## Installation
### Option 1: Using Nix (Recommended)
1. Clone the repository:
```bash
git clone https://git.miaig.dev/mia/hafas-terminal-app.git
cd hafas-terminal-app
```
2. Enter the Nix development environment:
```bash
nix develop # Modern Nix with flakes
# or
nix-shell # Traditional Nix
```
All dependencies will be automatically configured.
### Option 2: Using pip
1. Clone the repository:
```bash
git clone https://git.miaig.dev/mia/hafas-terminal-app.git
cd hafas-terminal-app
```
2. Create a virtual environment:
```bash
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
3. Install dependencies:
```bash
pip install -r requirements.txt
```
## Usage
Run the application:
```bash
python main.py
```
### Features
- Navigate between stations and search for routes
- View departure and arrival times
- Display detailed journey information
## Project Structure
```
hafas-terminal-app/
├── main.py # Application entry point
├── backend.py # Backend API communication logic
├── route_planning.py # Route planning algorithms
├── station_monitor.py # Station monitoring functionality
├── flake.nix # Nix environment configuration
├── README.md # This file
└── docs/ # Documentation
```
## License
This project is provided under the terms specified in the [LICENSE](./LICENSE) file.
## Contributing
Contributions are welcome! Please feel free to submit issues and pull requests to help improve the application.

View file

@ -9,7 +9,8 @@ The application will provide routing information and station departures/arrivals
import requests
import json
from datetime import datetime
import re
from datetime import date, datetime
from prompt_toolkit import Application
from prompt_toolkit.completion import WordCompleter
@ -43,6 +44,13 @@ class HafasClient:
headers=self.headers)
return json.loads(res.text)
def journeyRequest(self, origin, destination, via=None):
time = datetime.now()
res = self.session.post(self.baseUrl,
data=json.dumps({"svcReqL": [{"req": {"arrLocL": [{"lid": f"A=1@L={destination}@"}], "viaLocL": via or [], "depLocL": [{"lid": f"A=1@L={origin}@"}], "outDate": time.strftime("%Y%m%d"), "outTime": time.strftime("%H%M%S"), "jnyFltrL": [{"type": "PROD", "mode": "INC", "value": "4087"}], "minChgTime": 0, "maxChg": -1, "numF": 1}, "meth": "TripSearch"}], "client": self.clientInfo, "ver": self.version, "lang": self.language, "auth": self.auth}),
headers=self.headers)
return json.loads(res.text)
def getStationNames(self, stationString):
@ -75,20 +83,13 @@ class HafasClient:
# - Departure/arrival time: departure["stbStop"]["dTimeS"] or ["aTimeS"]
departures.append({
"name": departure.get("dirTxt"),
"line": (
product.get("nameS")
or product.get("number")
or product.get("name")
or product.get("prodCtx", {}).get("line")
or product.get("prodCtx", {}).get("name")
or product.get("prodCtx", {}).get("nameS")
),
"line": re.search(r'#ZB#([^#]+)', departure.get("jid")).group(1).strip(),
"departure_time": self._format_time(stop.get(time_key)),
"departure_time_raw": stop.get(time_key),
"departure_time_real": self._format_time(stop.get(time_key_real)),
"departure_time_real_raw": stop.get(time_key_real),
"jid": departure.get("jid"),
"raw": departure,
# "raw": departure,
})
return departures
@ -114,13 +115,25 @@ class HafasClient:
trip = self.getTrip(departures[0]["jid"])
__import__('pprint').pprint(trip)
def moniterTest(self):
def monitorTest(self):
stationInput = input("Enter Station Name: ")
station = self.getStationNames(stationInput)
print(station)
# departures = self.getArrDep(station[0][1], arrdep="DEP", count=1)
departures = self.arrDepRequest(station[0][1], arrdep="DEP", count=1)
print(departures)
print("\n)------------------\n")
departures = self.getArrDep(station[0][1], arrdep="DEP", count=2)
__import__('pprint').pprint(departures)
print("\n)------------------\n")
arrivals = self.getArrDep(station[0][1], arrdep="ARR", count=2)
__import__('pprint').pprint(arrivals)
#arrivalsRaw = self.arrDepRequest(station[0][1], arrdep="ARR", count=1)
#__import__('pprint').pprint(arrivalsRaw)
def journeyTest(self):
origin = self.getStationNames(input("Origin: "))[0][1]
destination = self.getStationNames(input("Destination: "))[0][1]
journeys = self.journeyRequest(origin, destination)
__import__('pprint').pprint(journeys)
# streq = self.stationRequest(station)["svcResL"][0]["res"]["match"]["locL"]
@ -140,4 +153,5 @@ class HafasClient:
if __name__ == '__main__':
client = HafasClient()
# client.runTests()
client.moniterTest()
# client.monitorTest()
client.journeyTest()

View file

@ -15,7 +15,6 @@ from prompt_toolkit.layout.containers import Window, VSplit, HSplit
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.key_binding import KeyBindings
import threading
import time
def run_station_monitor(hafas: backend.HafasClient | None = None):
@ -71,7 +70,10 @@ def run_station_monitor(hafas: backend.HafasClient | None = None):
user_input = userinputBuffer.text.strip()
if user_input:
if not user_input:
app_state = "MONITOR"
return
app_state = "RESULTS"
results = hafas.getStationNames(user_input)
@ -80,27 +82,25 @@ def run_station_monitor(hafas: backend.HafasClient | None = None):
departureBuffer.text = ""
userinputBuffer.text = ""
app_state = "MONITOR"
return
else:
station_name = results[0][1]
station_id = results[0][0]
station_name = results[0][0]
station_id = results[0][1]
arrivals = hafas.getArrDep(station_id, arrdep="ARR", count=3)
arrivalBuffer.text = f"Ankünfte für {station_name}:\n"
for arrival in arrivals:
arrivalBuffer.insert_text(f"\n {arrival}")
departures = hafas.getArrDep(station_id, arrdep="DEP", count=3)
departureBuffer.text = f"Abfahrten für {station_name}:\n"
for departure in departures:
departureBuffer.insert_text(f"\n {departure}")
arrivals = hafas.getArrDep(station_id, arrdep="ARR", count=5)
arrivalBuffer.text = (
f"Ankünfte für {station_name}:\n"
+ "\n".join(f" {arrival["departure_time_real"]} {arrival["line"]} <-- {arrival["name"]}" for arrival in arrivals)
)
departures = hafas.getArrDep(station_id, arrdep="DEP", count=5)
departureBuffer.text = (
f"Abfahrten für {station_name}:\n"
+ "\n".join(f" {departure["departure_time_real"]} {departure["line"]} --> {departure["name"]}" for departure in departures)
)
userinputBuffer.text = ""
else:
app_state = "MONITOR"
@keyB.add("c-q")
def exit_(event):