From be7b48ac07c67133380f380ed036b59f5ef7bdef Mon Sep 17 00:00:00 2001 From: Simon Rieger Date: Sun, 19 Jan 2025 02:44:11 +0100 Subject: [PATCH] add early version without tracking with using db-vendo-client --- LICENSE | 21 ++++++ docker-compose.yml | 21 ++---- init.sql | 3 +- main.go | 54 +++++---------- main.go.old | 164 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 55 deletions(-) create mode 100644 LICENSE create mode 100644 main.go.old diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8aa2645 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docker-compose.yml b/docker-compose.yml index b8580c9..f515f59 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,26 +5,23 @@ services: build: . depends_on: - mariadb - - db-rest environment: - DB_HOST=mariadb - DB_USER=root - DB_PASSWORD=password - DB_NAME=traindb - - DB_DSN=root:password@tcp(mariadb:3306)/traindb - - API_BASE_URL=http://db-rest:3000 + - DB_DSN=root:password@tcp(172.28.0.66:3306)/traindb + - API_BASE_URL=http://172.28.0.65:3000 - MAX_RESULTS=200 - DURATION=240 - DELETE_AFTER_MINUTES=30 - - BUS=false - - FERRY=false - - TRAM=false - - TAXI=false # Hildesheim HBF, Braunschweig HBF, Hannover HBF - STATION_IDS=8000169,8000049,8000152 restart: always networks: default: + dns: + ipv4_address: 172.28.0.64 mariadb: image: mariadb:10.5 @@ -38,15 +35,7 @@ services: networks: default: dns: - ipv4_address: 172.28.0.65 - - db-rest: - image: docker.io/derhuerst/db-rest:6 - ports: - - 127.0.0.1:3010:3000 - restart: always - networks: - default: + ipv4_address: 172.28.0.66 networks: dns: diff --git a/init.sql b/init.sql index c4d94d6..b63bc61 100644 --- a/init.sql +++ b/init.sql @@ -4,5 +4,6 @@ CREATE TABLE IF NOT EXISTS trips ( longitude DOUBLE, timestamp DATETIME, train_name VARCHAR(50), - fahrt_nr VARCHAR(20) + fahrt_nr VARCHAR(20), + trip_id VARCHAR(255) ); diff --git a/main.go b/main.go index ad0c521..57cc602 100644 --- a/main.go +++ b/main.go @@ -17,12 +17,9 @@ import ( ) type Departure struct { - CurrentTripPosition struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - } `json:"currentTripPosition"` - When time.Time `json:"when"` - Line struct { + TripId string `json:"tripId"` + When string `json:"when"` + Line struct { Name string `json:"name"` FahrtNr string `json:"fahrtNr"` } `json:"line"` @@ -39,30 +36,10 @@ func main() { dbDSN := os.Getenv("DB_DSN") apiBaseURL := os.Getenv("API_BASE_URL") - maxResults, err := strconv.Atoi(os.Getenv("MAX_RESULTS")) - if err != nil { - log.Fatalf("Ungültiger Wert für MAX_RESULTS: %v", err) - } duration, err := strconv.Atoi(os.Getenv("DURATION")) if err != nil { log.Fatalf("Ungültiger Wert für DURATION: %v", err) } - includeBus, err := strconv.ParseBool(os.Getenv("BUS")) - if err != nil { - log.Fatalf("Ungültiger Wert für BUS: %v", err) - } - includeFerry, err := strconv.ParseBool(os.Getenv("FERRY")) - if err != nil { - log.Fatalf("Ungültiger Wert für FERRY: %v", err) - } - includeTram, err := strconv.ParseBool(os.Getenv("TRAM")) - if err != nil { - log.Fatalf("Ungültiger Wert für TRAM: %v", err) - } - includeTaxi, err := strconv.ParseBool(os.Getenv("TAXI")) - if err != nil { - log.Fatalf("Ungültiger Wert für TAXI: %v", err) - } deleteAfter, err := strconv.Atoi(os.Getenv("DELETE_AFTER_MINUTES")) if err != nil { log.Fatalf("Ungültiger Wert für DELETE_AFTER_MINUTES: %v", err) @@ -77,7 +54,7 @@ func main() { for { for _, stationID := range stationIDs { - departures := fetchDepartures(apiBaseURL, stationID, maxResults, duration, includeBus, includeFerry, includeTram, includeTaxi) + departures := fetchDepartures(apiBaseURL, stationID, duration) for _, dep := range departures { savePosition(db, dep) } @@ -87,9 +64,9 @@ func main() { } } -func fetchDepartures(apiBaseURL, stationID string, maxResults, duration int, includeBus, includeFerry, includeTram, includeTaxi bool) []Departure { - url := fmt.Sprintf("%s/stops/%s/departures?results=%d&duration=%d&bus=%t&ferry=%t&tram=%t&taxi=%t", - apiBaseURL, stationID, maxResults, duration, includeBus, includeFerry, includeTram, includeTaxi) +func fetchDepartures(apiBaseURL, stationID string, duration int) []Departure { + url := fmt.Sprintf("%s/stops/%s/departures?duration=%d&linesOfStops=false&remarks=false&language=de&nationalExpress=true&national=true®ionalExpress=true®ional=true&suburban=true&bus=false&ferry=false&subway=false&tram=false&taxi=false&pretty=true", + apiBaseURL, stationID, duration) resp, err := http.Get(url) if err != nil { log.Printf("Fehler beim Abrufen der Abfahrten für Station %s: %v\n", stationID, err) @@ -120,28 +97,29 @@ func fetchDepartures(apiBaseURL, stationID string, maxResults, duration int, inc } func savePosition(db *sql.DB, dep Departure) { - if dep.CurrentTripPosition.Latitude == 0 && dep.CurrentTripPosition.Longitude == 0 { - log.Println("Keine gültige Position verfügbar") + whenTime, err := time.Parse(time.RFC3339, dep.When) + if err != nil { + log.Printf("Fehler beim Parsen der Zeit: %v\n", err) return } - today := time.Now().Format("2006-01-02") + today := whenTime.Format("2006-01-02") var existingID string - err := db.QueryRow("SELECT id FROM trips WHERE fahrt_nr = ? AND DATE(timestamp) = ?", dep.Line.FahrtNr, today).Scan(&existingID) + err = db.QueryRow("SELECT id FROM trips WHERE fahrt_nr = ? AND DATE(timestamp) = ?", dep.Line.FahrtNr, today).Scan(&existingID) if err == sql.ErrNoRows { id := uuid.New().String() - _, err = db.Exec("INSERT INTO trips (id, latitude, longitude, timestamp, train_name, fahrt_nr) VALUES (?, ?, ?, ?, ?, ?)", - id, dep.CurrentTripPosition.Latitude, dep.CurrentTripPosition.Longitude, dep.When, dep.Line.Name, dep.Line.FahrtNr) + _, err = db.Exec("INSERT INTO trips (id, timestamp, train_name, fahrt_nr, trip_id) VALUES (?, ?, ?, ?, ?)", + id, whenTime, dep.Line.Name, dep.Line.FahrtNr, dep.TripId) if err != nil { log.Printf("Fehler beim Speichern der neuen Position: %v\n", err) } else { log.Printf("Neue Position gespeichert (ID: %s, Zug: %s, FahrtNr: %s)\n", id, dep.Line.Name, dep.Line.FahrtNr) } } else if err == nil { - _, err = db.Exec("UPDATE trips SET latitude = ?, longitude = ?, timestamp = ?, train_name = ? WHERE id = ?", - dep.CurrentTripPosition.Latitude, dep.CurrentTripPosition.Longitude, dep.When, dep.Line.Name, existingID) + _, err = db.Exec("UPDATE trips SET timestamp = ?, train_name = ?, trip_id = ? WHERE id = ?", + whenTime, dep.Line.Name, dep.TripId, existingID) if err != nil { log.Printf("Fehler beim Aktualisieren der Position: %v\n", err) } else { diff --git a/main.go.old b/main.go.old new file mode 100644 index 0000000..ad0c521 --- /dev/null +++ b/main.go.old @@ -0,0 +1,164 @@ +package main + +import ( + "database/sql" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "strconv" + "strings" + "time" + + _ "github.com/go-sql-driver/mysql" + "github.com/google/uuid" +) + +type Departure struct { + CurrentTripPosition struct { + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + } `json:"currentTripPosition"` + When time.Time `json:"when"` + Line struct { + Name string `json:"name"` + FahrtNr string `json:"fahrtNr"` + } `json:"line"` +} + +type APIResponse struct { + Departures []Departure `json:"departures"` +} + +func main() { + log.SetOutput(os.Stdout) + log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) + log.Println("Anwendung gestartet") + + dbDSN := os.Getenv("DB_DSN") + apiBaseURL := os.Getenv("API_BASE_URL") + maxResults, err := strconv.Atoi(os.Getenv("MAX_RESULTS")) + if err != nil { + log.Fatalf("Ungültiger Wert für MAX_RESULTS: %v", err) + } + duration, err := strconv.Atoi(os.Getenv("DURATION")) + if err != nil { + log.Fatalf("Ungültiger Wert für DURATION: %v", err) + } + includeBus, err := strconv.ParseBool(os.Getenv("BUS")) + if err != nil { + log.Fatalf("Ungültiger Wert für BUS: %v", err) + } + includeFerry, err := strconv.ParseBool(os.Getenv("FERRY")) + if err != nil { + log.Fatalf("Ungültiger Wert für FERRY: %v", err) + } + includeTram, err := strconv.ParseBool(os.Getenv("TRAM")) + if err != nil { + log.Fatalf("Ungültiger Wert für TRAM: %v", err) + } + includeTaxi, err := strconv.ParseBool(os.Getenv("TAXI")) + if err != nil { + log.Fatalf("Ungültiger Wert für TAXI: %v", err) + } + deleteAfter, err := strconv.Atoi(os.Getenv("DELETE_AFTER_MINUTES")) + if err != nil { + log.Fatalf("Ungültiger Wert für DELETE_AFTER_MINUTES: %v", err) + } + stationIDs := strings.Split(os.Getenv("STATION_IDS"), ",") + + db, err := sql.Open("mysql", dbDSN) + if err != nil { + log.Fatal("Fehler beim Verbinden mit der Datenbank: ", err) + } + defer db.Close() + + for { + for _, stationID := range stationIDs { + departures := fetchDepartures(apiBaseURL, stationID, maxResults, duration, includeBus, includeFerry, includeTram, includeTaxi) + for _, dep := range departures { + savePosition(db, dep) + } + } + deleteOldEntries(db, deleteAfter) + time.Sleep(1 * time.Minute) + } +} + +func fetchDepartures(apiBaseURL, stationID string, maxResults, duration int, includeBus, includeFerry, includeTram, includeTaxi bool) []Departure { + url := fmt.Sprintf("%s/stops/%s/departures?results=%d&duration=%d&bus=%t&ferry=%t&tram=%t&taxi=%t", + apiBaseURL, stationID, maxResults, duration, includeBus, includeFerry, includeTram, includeTaxi) + resp, err := http.Get(url) + if err != nil { + log.Printf("Fehler beim Abrufen der Abfahrten für Station %s: %v\n", stationID, err) + return nil + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Printf("Fehler beim Lesen der Antwort für Station %s: %v\n", stationID, err) + return nil + } + + if len(body) == 0 { + log.Printf("Leere Antwort vom Server für Station %s erhalten\n", stationID) + return nil + } + + var response APIResponse + err = json.Unmarshal(body, &response) + if err != nil { + log.Printf("Fehler beim Dekodieren der Abfahrten für Station %s: %v\nAntwort-Body: %s\n", stationID, err, string(body)) + return nil + } + + log.Printf("Erfolgreich %d Abfahrten für Station %s abgerufen\n", len(response.Departures), stationID) + return response.Departures +} + +func savePosition(db *sql.DB, dep Departure) { + if dep.CurrentTripPosition.Latitude == 0 && dep.CurrentTripPosition.Longitude == 0 { + log.Println("Keine gültige Position verfügbar") + return + } + + today := time.Now().Format("2006-01-02") + + var existingID string + err := db.QueryRow("SELECT id FROM trips WHERE fahrt_nr = ? AND DATE(timestamp) = ?", dep.Line.FahrtNr, today).Scan(&existingID) + + if err == sql.ErrNoRows { + id := uuid.New().String() + _, err = db.Exec("INSERT INTO trips (id, latitude, longitude, timestamp, train_name, fahrt_nr) VALUES (?, ?, ?, ?, ?, ?)", + id, dep.CurrentTripPosition.Latitude, dep.CurrentTripPosition.Longitude, dep.When, dep.Line.Name, dep.Line.FahrtNr) + if err != nil { + log.Printf("Fehler beim Speichern der neuen Position: %v\n", err) + } else { + log.Printf("Neue Position gespeichert (ID: %s, Zug: %s, FahrtNr: %s)\n", id, dep.Line.Name, dep.Line.FahrtNr) + } + } else if err == nil { + _, err = db.Exec("UPDATE trips SET latitude = ?, longitude = ?, timestamp = ?, train_name = ? WHERE id = ?", + dep.CurrentTripPosition.Latitude, dep.CurrentTripPosition.Longitude, dep.When, dep.Line.Name, existingID) + if err != nil { + log.Printf("Fehler beim Aktualisieren der Position: %v\n", err) + } else { + log.Printf("Position aktualisiert (ID: %s, Zug: %s, FahrtNr: %s)\n", existingID, dep.Line.Name, dep.Line.FahrtNr) + } + } else { + log.Printf("Fehler bei der Überprüfung des existierenden Eintrags: %v\n", err) + } +} + +func deleteOldEntries(db *sql.DB, deleteAfterMinutes int) { + deleteTime := time.Now().Add(time.Duration(-deleteAfterMinutes) * time.Minute) + result, err := db.Exec("DELETE FROM trips WHERE timestamp < ?", deleteTime) + if err != nil { + log.Printf("Fehler beim Löschen alter Einträge: %v\n", err) + return + } + rowsAffected, _ := result.RowsAffected() + log.Printf("%d alte Einträge gelöscht\n", rowsAffected) +}