add median support
This commit is contained in:
parent
0dccca5b03
commit
451063dfc1
4 changed files with 349 additions and 487 deletions
|
@ -1 +1,2 @@
|
||||||
db/
|
db/
|
||||||
|
.git/
|
||||||
|
|
1
init.sql
1
init.sql
|
@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS delay_stats (
|
||||||
total_trips INT,
|
total_trips INT,
|
||||||
delayed_trips INT,
|
delayed_trips INT,
|
||||||
avg_delay FLOAT,
|
avg_delay FLOAT,
|
||||||
|
median_delay FLOAT,
|
||||||
last_updated DATETIME,
|
last_updated DATETIME,
|
||||||
INDEX idx_fahrt_nr (fahrt_nr)
|
INDEX idx_fahrt_nr (fahrt_nr)
|
||||||
);
|
);
|
||||||
|
|
670
main.go
670
main.go
|
@ -1,434 +1,458 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"sort"
|
||||||
"strings"
|
"strconv"
|
||||||
"time"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Departure struct {
|
type Departure struct {
|
||||||
TripId string `json:"tripId"`
|
TripId string `json:"tripId"`
|
||||||
When string `json:"when"`
|
When string `json:"when"`
|
||||||
PlannedWhen string `json:"plannedWhen"`
|
PlannedWhen string `json:"plannedWhen"`
|
||||||
Delay int `json:"delay"`
|
Delay int `json:"delay"`
|
||||||
Line struct {
|
Line struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
FahrtNr string `json:"fahrtNr"`
|
FahrtNr string `json:"fahrtNr"`
|
||||||
} `json:"line"`
|
} `json:"line"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIResponse struct {
|
type APIResponse struct {
|
||||||
Departures []Departure `json:"departures"`
|
Departures []Departure `json:"departures"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TripDetails struct {
|
type TripDetails struct {
|
||||||
Origin Station `json:"origin"`
|
Origin Station `json:"origin"`
|
||||||
Destination Station `json:"destination"`
|
Destination Station `json:"destination"`
|
||||||
Departure time.Time `json:"departure"`
|
Departure time.Time `json:"departure"`
|
||||||
Arrival time.Time `json:"arrival"`
|
Arrival time.Time `json:"arrival"`
|
||||||
Polyline Polyline `json:"polyline"`
|
Polyline Polyline `json:"polyline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Station struct {
|
type Station struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Location Location `json:"location"`
|
Location Location `json:"location"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Location struct {
|
type Location struct {
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"`
|
||||||
Longitude float64 `json:"longitude"`
|
Longitude float64 `json:"longitude"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Polyline struct {
|
type Polyline struct {
|
||||||
Features []Feature `json:"features"`
|
Features []Feature `json:"features"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Feature struct {
|
type Feature struct {
|
||||||
Geometry Geometry `json:"geometry"`
|
Geometry Geometry `json:"geometry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Geometry struct {
|
type Geometry struct {
|
||||||
Coordinates []float64 `json:"coordinates"`
|
Coordinates []float64 `json:"coordinates"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetOutput(os.Stdout)
|
log.SetOutput(os.Stdout)
|
||||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
log.Println("Anwendung gestartet")
|
log.Println("Anwendung gestartet")
|
||||||
|
|
||||||
dbDSN := os.Getenv("DB_DSN")
|
dbDSN := os.Getenv("DB_DSN")
|
||||||
apiBaseURL := os.Getenv("API_BASE_URL")
|
apiBaseURL := os.Getenv("API_BASE_URL")
|
||||||
duration, err := strconv.Atoi(os.Getenv("DURATION"))
|
duration, err := strconv.Atoi(os.Getenv("DURATION"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Ungültiger Wert für DURATION: %v", err)
|
log.Fatalf("Ungültiger Wert für DURATION: %v", err)
|
||||||
}
|
}
|
||||||
deleteAfter, err := strconv.Atoi(os.Getenv("DELETE_AFTER_MINUTES"))
|
deleteAfter, err := strconv.Atoi(os.Getenv("DELETE_AFTER_MINUTES"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Ungültiger Wert für DELETE_AFTER_MINUTES: %v", err)
|
log.Fatalf("Ungültiger Wert für DELETE_AFTER_MINUTES: %v", err)
|
||||||
}
|
}
|
||||||
stationIDs := strings.Split(os.Getenv("STATION_IDS"), ",")
|
stationIDs := strings.Split(os.Getenv("STATION_IDS"), ",")
|
||||||
|
|
||||||
updateInterval, err := strconv.Atoi(os.Getenv("UPDATE_INTERVAL_MINUTES"))
|
updateInterval, err := strconv.Atoi(os.Getenv("UPDATE_INTERVAL_MINUTES"))
|
||||||
if err != nil || updateInterval <= 0 {
|
if err != nil || updateInterval <= 0 {
|
||||||
log.Println("Ungültiger oder fehlender Wert für UPDATE_INTERVAL_MINUTES, verwende Standardwert von 1 Minute")
|
log.Println("Ungültiger oder fehlender Wert für UPDATE_INTERVAL_MINUTES, verwende Standardwert von 1 Minute")
|
||||||
updateInterval = 1
|
updateInterval = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
transferTimeStr := os.Getenv("TRANSFER_TIME")
|
transferTimeStr := os.Getenv("TRANSFER_TIME")
|
||||||
transferTime, err := time.Parse("15:04", transferTimeStr)
|
transferTime, err := time.Parse("15:04", transferTimeStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Ungültiger Wert für TRANSFER_TIME, verwende Standardwert 23:00")
|
log.Printf("Ungültiger Wert für TRANSFER_TIME, verwende Standardwert 23:00")
|
||||||
transferTime = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 23, 0, 0, 0, time.Local)
|
transferTime = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 23, 0, 0, 0, time.Local)
|
||||||
} else {
|
} else {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
transferTime = time.Date(now.Year(), now.Month(), now.Day(), transferTime.Hour(), transferTime.Minute(), 0, 0, time.Local)
|
transferTime = time.Date(now.Year(), now.Month(), now.Day(), transferTime.Hour(), transferTime.Minute(), 0, 0, time.Local)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sql.Open("mysql", dbDSN)
|
db, err := sql.Open("mysql", dbDSN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Fehler beim Verbinden mit der Datenbank: ", err)
|
log.Fatal("Fehler beim Verbinden mit der Datenbank: ", err)
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
ticker := time.NewTicker(5 * time.Minute)
|
ticker := time.NewTicker(5 * time.Minute)
|
||||||
updateTicker := time.NewTicker(time.Duration(updateInterval) * time.Minute)
|
updateTicker := time.NewTicker(time.Duration(updateInterval) * time.Minute)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
defer updateTicker.Stop()
|
defer updateTicker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-updateTicker.C:
|
case <-updateTicker.C:
|
||||||
for _, stationID := range stationIDs {
|
for _, stationID := range stationIDs {
|
||||||
departures := fetchDepartures(apiBaseURL, stationID, duration)
|
departures := fetchDepartures(apiBaseURL, stationID, duration)
|
||||||
// Füge einen 1-Sekunden-Sleeper hinzu
|
// Füge einen 1-Sekunden-Sleeper hinzu
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
for _, dep := range departures {
|
for _, dep := range departures {
|
||||||
savePosition(db, dep, apiBaseURL)
|
savePosition(db, dep, apiBaseURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteOldEntries(db, deleteAfter)
|
deleteOldEntries(db, deleteAfter)
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
logDatabaseStats(db)
|
logDatabaseStats(db)
|
||||||
default:
|
default:
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if now.After(transferTime) {
|
if now.After(transferTime) {
|
||||||
transferDailyDelayStats(db)
|
transferDailyDelayStats(db)
|
||||||
transferTime = time.Date(now.Year(), now.Month(), now.Day()+1, transferTime.Hour(), transferTime.Minute(), 0, 0, time.Local)
|
transferTime = time.Date(now.Year(), now.Month(), now.Day()+1, transferTime.Hour(), transferTime.Minute(), 0, 0, time.Local)
|
||||||
}
|
}
|
||||||
time.Sleep(1 * time.Minute)
|
time.Sleep(1 * time.Minute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchDepartures(apiBaseURL, stationID string, duration int) []Departure {
|
func fetchDepartures(apiBaseURL, stationID string, duration int) []Departure {
|
||||||
url := fmt.Sprintf("%s/stops/%s/departures?duration=%d&linesOfStops=false&remarks=true&language=en&nationalExpress=true&national=true®ionalExpress=true®ional=true&suburban=true&bus=false&ferry=false&subway=false&tram=false&taxi=false&pretty=false",
|
url := fmt.Sprintf("%s/stops/%s/departures?duration=%d&linesOfStops=false&remarks=true&language=en&nationalExpress=true&national=true®ionalExpress=true®ional=true&suburban=true&bus=false&ferry=false&subway=false&tram=false&taxi=false&pretty=false",
|
||||||
apiBaseURL, stationID, duration)
|
apiBaseURL, stationID, duration)
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Abrufen der Abfahrten für Station %s: %v\n", stationID, err)
|
log.Printf("Fehler beim Abrufen der Abfahrten für Station %s: %v\n", stationID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Lesen der Antwort für Station %s: %v\n", stationID, err)
|
log.Printf("Fehler beim Lesen der Antwort für Station %s: %v\n", stationID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
log.Printf("Leere Antwort vom Server für Station %s erhalten\n", stationID)
|
log.Printf("Leere Antwort vom Server für Station %s erhalten\n", stationID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var response APIResponse
|
var response APIResponse
|
||||||
err = json.Unmarshal(body, &response)
|
err = json.Unmarshal(body, &response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Dekodieren der Abfahrten für Station %s: %v\nAntwort-Body: %s\n", stationID, err, string(body))
|
log.Printf("Fehler beim Dekodieren der Abfahrten für Station %s: %v\nAntwort-Body: %s\n", stationID, err, string(body))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Erfolgreich %d Abfahrten für Station %s abgerufen\n", len(response.Departures), stationID)
|
log.Printf("Erfolgreich %d Abfahrten für Station %s abgerufen\n", len(response.Departures), stationID)
|
||||||
return response.Departures
|
return response.Departures
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchTripDetails(apiBaseURL, tripID string) (*TripDetails, error) {
|
func fetchTripDetails(apiBaseURL, tripID string) (*TripDetails, error) {
|
||||||
escapedTripID := url.QueryEscape(tripID)
|
escapedTripID := url.QueryEscape(tripID)
|
||||||
url := fmt.Sprintf("%s/trips/%s?stopovers=true&remarks=true&polyline=true&language=en&pretty=false", apiBaseURL, escapedTripID)
|
url := fmt.Sprintf("%s/trips/%s?stopovers=true&remarks=true&polyline=true&language=en&pretty=false", apiBaseURL, escapedTripID)
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Fehler beim Abrufen der Zugdetails: %v", err)
|
return nil, fmt.Errorf("Fehler beim Abrufen der Zugdetails: %v", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Fehler beim Lesen der Antwort: %v", err)
|
return nil, fmt.Errorf("Fehler beim Lesen der Antwort: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
return nil, fmt.Errorf("Leere Antwort vom Server erhalten")
|
return nil, fmt.Errorf("Leere Antwort vom Server erhalten")
|
||||||
}
|
}
|
||||||
|
|
||||||
var tripResponse struct {
|
var tripResponse struct {
|
||||||
Trip TripDetails `json:"trip"`
|
Trip TripDetails `json:"trip"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(body, &tripResponse); err != nil {
|
if err := json.Unmarshal(body, &tripResponse); err != nil {
|
||||||
return nil, fmt.Errorf("Fehler beim Dekodieren der Zugdetails: %v", err)
|
return nil, fmt.Errorf("Fehler beim Dekodieren der Zugdetails: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tripResponse.Trip.Origin.Name == "" || tripResponse.Trip.Destination.Name == "" {
|
if tripResponse.Trip.Origin.Name == "" || tripResponse.Trip.Destination.Name == "" {
|
||||||
return nil, fmt.Errorf("Unvollständige Tripdaten erhalten")
|
return nil, fmt.Errorf("Unvollständige Tripdaten erhalten")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &tripResponse.Trip, nil
|
return &tripResponse.Trip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func savePosition(db *sql.DB, dep Departure, apiBaseURL string) {
|
func savePosition(db *sql.DB, dep Departure, apiBaseURL string) {
|
||||||
// Füge einen 1-Sekunden-Sleeper hinzu
|
// Füge einen 1-Sekunden-Sleeper hinzu
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
tripDetails, err := fetchTripDetails(apiBaseURL, dep.TripId)
|
tripDetails, err := fetchTripDetails(apiBaseURL, dep.TripId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Abrufen der Zugdetails für TripID %s: %v\n", dep.TripId, err)
|
log.Printf("Fehler beim Abrufen der Zugdetails für TripID %s: %v\n", dep.TripId, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTime := time.Now()
|
currentTime := time.Now()
|
||||||
longitude, latitude := calculateCurrentPosition(tripDetails, currentTime)
|
longitude, latitude := calculateCurrentPosition(tripDetails, currentTime)
|
||||||
|
|
||||||
if dep.When == "" {
|
if dep.When == "" {
|
||||||
log.Printf("Warnung: Leerer Zeitstempel für FahrtNr %s, überspringe Eintrag\n", dep.Line.FahrtNr)
|
log.Printf("Warnung: Leerer Zeitstempel für FahrtNr %s, überspringe Eintrag\n", dep.Line.FahrtNr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
whenTime, err := time.Parse(time.RFC3339, dep.When)
|
whenTime, err := time.Parse(time.RFC3339, dep.When)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Parsen der Zeit für TripID %s: %v\n", dep.TripId, err)
|
log.Printf("Fehler beim Parsen der Zeit für TripID %s: %v\n", dep.TripId, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
plannedWhenTime, err := time.Parse(time.RFC3339, dep.PlannedWhen)
|
plannedWhenTime, err := time.Parse(time.RFC3339, dep.PlannedWhen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Parsen der geplanten Zeit für TripID %s: %v\n", dep.TripId, err)
|
log.Printf("Fehler beim Parsen der geplanten Zeit für TripID %s: %v\n", dep.TripId, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingID string
|
var existingID string
|
||||||
err = db.QueryRow("SELECT id FROM trips WHERE fahrt_nr = ?", dep.Line.FahrtNr).Scan(&existingID)
|
err = db.QueryRow("SELECT id FROM trips WHERE fahrt_nr = ?", dep.Line.FahrtNr).Scan(&existingID)
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
id := uuid.New().String()
|
id := uuid.New().String()
|
||||||
_, err = db.Exec("INSERT INTO trips (id, timestamp, planned_timestamp, delay, train_name, fahrt_nr, trip_id, latitude, longitude, destination) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
_, err = db.Exec("INSERT INTO trips (id, timestamp, planned_timestamp, delay, train_name, fahrt_nr, trip_id, latitude, longitude, destination) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
id, whenTime, plannedWhenTime, dep.Delay, dep.Line.Name, dep.Line.FahrtNr, dep.TripId, latitude, longitude, tripDetails.Destination.Name)
|
id, whenTime, plannedWhenTime, dep.Delay, dep.Line.Name, dep.Line.FahrtNr, dep.TripId, latitude, longitude, tripDetails.Destination.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Speichern der neuen Position für TripID %s: %v\n", dep.TripId, err)
|
log.Printf("Fehler beim Speichern der neuen Position für TripID %s: %v\n", dep.TripId, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Neue Position gespeichert (ID: %s, Zug: %s, FahrtNr: %s, Lat: %f, Lon: %f, Verspätung: %d, Ziel: %s)\n", id, dep.Line.Name, dep.Line.FahrtNr, latitude, longitude, dep.Delay, tripDetails.Destination.Name)
|
log.Printf("Neue Position gespeichert (ID: %s, Zug: %s, FahrtNr: %s, Lat: %f, Lon: %f, Verspätung: %d, Ziel: %s)\n", id, dep.Line.Name, dep.Line.FahrtNr, latitude, longitude, dep.Delay, tripDetails.Destination.Name)
|
||||||
}
|
}
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
_, err = db.Exec("UPDATE trips SET timestamp = ?, planned_timestamp = ?, delay = ?, train_name = ?, trip_id = ?, latitude = ?, longitude = ?, destination = ? WHERE id = ?",
|
_, err = db.Exec("UPDATE trips SET timestamp = ?, planned_timestamp = ?, delay = ?, train_name = ?, trip_id = ?, latitude = ?, longitude = ?, destination = ? WHERE id = ?",
|
||||||
whenTime, plannedWhenTime, dep.Delay, dep.Line.Name, dep.TripId, latitude, longitude, tripDetails.Destination.Name, existingID)
|
whenTime, plannedWhenTime, dep.Delay, dep.Line.Name, dep.TripId, latitude, longitude, tripDetails.Destination.Name, existingID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Aktualisieren der Position für TripID %s: %v\n", dep.TripId, err)
|
log.Printf("Fehler beim Aktualisieren der Position für TripID %s: %v\n", dep.TripId, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Position aktualisiert (ID: %s, Zug: %s, FahrtNr: %s, Lat: %f, Lon: %f, Verspätung: %d, Ziel: %s)\n", existingID, dep.Line.Name, dep.Line.FahrtNr, latitude, longitude, dep.Delay, tripDetails.Destination.Name)
|
log.Printf("Position aktualisiert (ID: %s, Zug: %s, FahrtNr: %s, Lat: %f, Lon: %f, Verspätung: %d, Ziel: %s)\n", existingID, dep.Line.Name, dep.Line.FahrtNr, latitude, longitude, dep.Delay, tripDetails.Destination.Name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Fehler bei der Überprüfung des existierenden Eintrags für TripID %s: %v\n", dep.TripId, err)
|
log.Printf("Fehler bei der Überprüfung des existierenden Eintrags für TripID %s: %v\n", dep.TripId, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTodayDelayStats(db, dep.Line.FahrtNr, dep.Line.Name, dep.Delay, whenTime)
|
updateTodayDelayStats(db, dep.Line.FahrtNr, dep.Line.Name, dep.Delay, whenTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTodayDelayStats(db *sql.DB, fahrtNr, trainName string, delay int, timestamp time.Time) {
|
func updateTodayDelayStats(db *sql.DB, fahrtNr, trainName string, delay int, timestamp time.Time) {
|
||||||
var existingID string
|
var existingID string
|
||||||
err := db.QueryRow("SELECT id FROM today_delay_stats WHERE fahrt_nr = ?", fahrtNr).Scan(&existingID)
|
err := db.QueryRow("SELECT id FROM today_delay_stats WHERE fahrt_nr = ?", fahrtNr).Scan(&existingID)
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// Kein existierender Eintrag, führe INSERT aus
|
// Kein existierender Eintrag, führe INSERT aus
|
||||||
_, err = db.Exec(`
|
_, err = db.Exec(`
|
||||||
INSERT INTO today_delay_stats (id, fahrt_nr, train_name, delay, timestamp)
|
INSERT INTO today_delay_stats (id, fahrt_nr, train_name, delay, timestamp)
|
||||||
VALUES (UUID(), ?, ?, ?, ?)
|
VALUES (UUID(), ?, ?, ?, ?)
|
||||||
`, fahrtNr, trainName, delay, timestamp)
|
`, fahrtNr, trainName, delay, timestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Einfügen der heutigen Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
log.Printf("Fehler beim Einfügen der heutigen Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
||||||
}
|
}
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
// Existierender Eintrag gefunden, führe UPDATE aus
|
// Existierender Eintrag gefunden, führe UPDATE aus
|
||||||
_, err = db.Exec(`
|
_, err = db.Exec(`
|
||||||
UPDATE today_delay_stats
|
UPDATE today_delay_stats
|
||||||
SET train_name = ?, delay = ?, timestamp = ?
|
SET train_name = ?, delay = ?, timestamp = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, trainName, delay, timestamp, existingID)
|
`, trainName, delay, timestamp, existingID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Aktualisieren der heutigen Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
log.Printf("Fehler beim Aktualisieren der heutigen Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Fehler beim Überprüfen der heutigen Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
log.Printf("Fehler beim Überprüfen der heutigen Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateMedian(delays []int) float64 {
|
||||||
|
sort.Ints(delays)
|
||||||
|
length := len(delays)
|
||||||
|
if length == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if length%2 == 0 {
|
||||||
|
return float64(delays[length/2-1]+delays[length/2]) / 2
|
||||||
|
}
|
||||||
|
return float64(delays[length/2])
|
||||||
}
|
}
|
||||||
|
|
||||||
func transferDailyDelayStats(db *sql.DB) {
|
func transferDailyDelayStats(db *sql.DB) {
|
||||||
log.Println("Starte tägliche Übertragung der Verspätungsstatistiken")
|
log.Println("Starte tägliche Übertragung der Verspätungsstatistiken")
|
||||||
|
|
||||||
rows, err := db.Query("SELECT fahrt_nr, train_name, delay FROM today_delay_stats")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Fehler beim Abrufen der heutigen Verspätungsstatistiken: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var totalTransferred, totalUpdated, totalInserted int
|
rows, err := db.Query("SELECT fahrt_nr, train_name, delay FROM today_delay_stats")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Fehler beim Abrufen der heutigen Verspätungsstatistiken: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
delayMap := make(map[string][]int)
|
||||||
var fahrtNr, trainName string
|
for rows.Next() {
|
||||||
var delay int
|
var fahrtNr, trainName string
|
||||||
if err := rows.Scan(&fahrtNr, &trainName, &delay); err != nil {
|
var delay int
|
||||||
log.Printf("Fehler beim Scannen der Verspätungsdaten: %v\n", err)
|
if err := rows.Scan(&fahrtNr, &trainName, &delay); err != nil {
|
||||||
continue
|
log.Printf("Fehler beim Scannen der Verspätungsdaten: %v\n", err)
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
delayMap[fahrtNr] = append(delayMap[fahrtNr], delay)
|
||||||
|
}
|
||||||
|
|
||||||
var existingID string
|
for fahrtNr, delays := range delayMap {
|
||||||
err := db.QueryRow("SELECT id FROM delay_stats WHERE fahrt_nr = ?", fahrtNr).Scan(&existingID)
|
avgDelay := calculateAverage(delays)
|
||||||
|
medianDelay := calculateMedian(delays)
|
||||||
|
totalTrips := len(delays)
|
||||||
|
delayedTrips := countDelayedTrips(delays)
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
var existingID string
|
||||||
// Kein existierender Eintrag, führe INSERT aus
|
err := db.QueryRow("SELECT id FROM delay_stats WHERE fahrt_nr = ?", fahrtNr).Scan(&existingID)
|
||||||
_, err = db.Exec(`
|
|
||||||
INSERT INTO delay_stats (id, fahrt_nr, total_trips, delayed_trips, avg_delay, last_updated)
|
if err == sql.ErrNoRows {
|
||||||
VALUES (UUID(), ?, 1, ?, ?, NOW())
|
_, err = db.Exec(`
|
||||||
`, fahrtNr, delay > 300, delay)
|
INSERT INTO delay_stats (id, fahrt_nr, total_trips, delayed_trips, avg_delay, median_delay, last_updated)
|
||||||
if err != nil {
|
VALUES (UUID(), ?, ?, ?, ?, ?, NOW())
|
||||||
log.Printf("Fehler beim Einfügen der Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
`, fahrtNr, totalTrips, delayedTrips, avgDelay, medianDelay)
|
||||||
} else {
|
} else if err == nil {
|
||||||
totalInserted++
|
_, err = db.Exec(`
|
||||||
log.Printf("Neue Verspätungsstatistik eingefügt für FahrtNr %s (Zug: %s, Verspätung: %d)\n", fahrtNr, trainName, delay)
|
|
||||||
}
|
|
||||||
} else if err == nil {
|
|
||||||
// Existierender Eintrag gefunden, führe UPDATE aus
|
|
||||||
_, err = db.Exec(`
|
|
||||||
UPDATE delay_stats
|
UPDATE delay_stats
|
||||||
SET total_trips = total_trips + 1,
|
SET total_trips = total_trips + ?,
|
||||||
delayed_trips = delayed_trips + ?,
|
delayed_trips = delayed_trips + ?,
|
||||||
avg_delay = ((avg_delay * total_trips) + ?) / (total_trips + 1),
|
avg_delay = ((avg_delay * total_trips) + ?) / (total_trips + ?),
|
||||||
|
median_delay = ?,
|
||||||
last_updated = NOW()
|
last_updated = NOW()
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, delay > 300, delay, existingID)
|
`, totalTrips, delayedTrips, avgDelay*float64(totalTrips), totalTrips, medianDelay, existingID)
|
||||||
if err != nil {
|
}
|
||||||
log.Printf("Fehler beim Aktualisieren der Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
|
||||||
} else {
|
|
||||||
totalUpdated++
|
|
||||||
log.Printf("Verspätungsstatistik aktualisiert für FahrtNr %s (Zug: %s, Verspätung: %d)\n", fahrtNr, trainName, delay)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Printf("Fehler beim Überprüfen der Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
totalTransferred++
|
if err != nil {
|
||||||
}
|
log.Printf("Fehler beim Aktualisieren der Verspätungsstatistiken für FahrtNr %s: %v\n", fahrtNr, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Verspätungsstatistik aktualisiert für FahrtNr %s (Durchschnitt: %.2f, Median: %.2f)\n", fahrtNr, avgDelay, medianDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Löschen Sie die heutigen Statistiken nach der Übertragung
|
// Löschen Sie die heutigen Statistiken nach der Übertragung
|
||||||
result, err := db.Exec("DELETE FROM today_delay_stats")
|
result, err := db.Exec("DELETE FROM today_delay_stats")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Löschen der heutigen Verspätungsstatistiken: %v\n", err)
|
log.Printf("Fehler beim Löschen der heutigen Verspätungsstatistiken: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
rowsAffected, _ := result.RowsAffected()
|
rowsAffected, _ := result.RowsAffected()
|
||||||
log.Printf("%d Einträge aus today_delay_stats gelöscht\n", rowsAffected)
|
log.Printf("%d Einträge aus today_delay_stats gelöscht\n", rowsAffected)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("Tägliche Übertragung abgeschlossen. Gesamt: %d, Eingefügt: %d, Aktualisiert: %d\n", totalTransferred, totalInserted, totalUpdated)
|
func calculateAverage(delays []int) float64 {
|
||||||
|
sum := 0
|
||||||
|
for _, delay := range delays {
|
||||||
|
sum += delay
|
||||||
|
}
|
||||||
|
return float64(sum) / float64(len(delays))
|
||||||
|
}
|
||||||
|
|
||||||
|
func countDelayedTrips(delays []int) int {
|
||||||
|
count := 0
|
||||||
|
for _, delay := range delays {
|
||||||
|
if delay > 300 {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateCurrentPosition(trip *TripDetails, currentTime time.Time) (float64, float64) {
|
func calculateCurrentPosition(trip *TripDetails, currentTime time.Time) (float64, float64) {
|
||||||
totalDuration := trip.Arrival.Sub(trip.Departure)
|
totalDuration := trip.Arrival.Sub(trip.Departure)
|
||||||
elapsedDuration := currentTime.Sub(trip.Departure)
|
elapsedDuration := currentTime.Sub(trip.Departure)
|
||||||
progress := elapsedDuration.Seconds() / totalDuration.Seconds()
|
progress := elapsedDuration.Seconds() / totalDuration.Seconds()
|
||||||
|
|
||||||
if progress < 0 {
|
if progress < 0 {
|
||||||
return trip.Origin.Location.Longitude, trip.Origin.Location.Latitude
|
return trip.Origin.Location.Longitude, trip.Origin.Location.Latitude
|
||||||
}
|
}
|
||||||
if progress > 1 {
|
if progress > 1 {
|
||||||
return trip.Destination.Location.Longitude, trip.Destination.Location.Latitude
|
return trip.Destination.Location.Longitude, trip.Destination.Location.Latitude
|
||||||
}
|
}
|
||||||
|
|
||||||
polyline := trip.Polyline.Features
|
polyline := trip.Polyline.Features
|
||||||
totalDistance := 0.0
|
totalDistance := 0.0
|
||||||
distances := make([]float64, len(polyline)-1)
|
distances := make([]float64, len(polyline)-1)
|
||||||
|
|
||||||
for i := 0; i < len(polyline)-1; i++ {
|
for i := 0; i < len(polyline)-1; i++ {
|
||||||
dist := distance(
|
dist := distance(
|
||||||
polyline[i].Geometry.Coordinates[1], polyline[i].Geometry.Coordinates[0],
|
polyline[i].Geometry.Coordinates[1], polyline[i].Geometry.Coordinates[0],
|
||||||
polyline[i+1].Geometry.Coordinates[1], polyline[i+1].Geometry.Coordinates[0],
|
polyline[i+1].Geometry.Coordinates[1], polyline[i+1].Geometry.Coordinates[0],
|
||||||
)
|
)
|
||||||
distances[i] = dist
|
distances[i] = dist
|
||||||
totalDistance += dist
|
totalDistance += dist
|
||||||
}
|
}
|
||||||
|
|
||||||
targetDistance := totalDistance * progress
|
targetDistance := totalDistance * progress
|
||||||
coveredDistance := 0.0
|
coveredDistance := 0.0
|
||||||
|
|
||||||
for i, dist := range distances {
|
for i, dist := range distances {
|
||||||
if coveredDistance+dist > targetDistance {
|
if coveredDistance+dist > targetDistance {
|
||||||
remainingDistance := targetDistance - coveredDistance
|
remainingDistance := targetDistance - coveredDistance
|
||||||
ratio := remainingDistance / dist
|
ratio := remainingDistance / dist
|
||||||
return interpolate(
|
return interpolate(
|
||||||
polyline[i].Geometry.Coordinates[0], polyline[i].Geometry.Coordinates[1],
|
polyline[i].Geometry.Coordinates[0], polyline[i].Geometry.Coordinates[1],
|
||||||
polyline[i+1].Geometry.Coordinates[0], polyline[i+1].Geometry.Coordinates[1],
|
polyline[i+1].Geometry.Coordinates[0], polyline[i+1].Geometry.Coordinates[1],
|
||||||
ratio,
|
ratio,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
coveredDistance += dist
|
coveredDistance += dist
|
||||||
}
|
}
|
||||||
|
|
||||||
return trip.Destination.Location.Longitude, trip.Destination.Location.Latitude
|
return trip.Destination.Location.Longitude, trip.Destination.Location.Latitude
|
||||||
}
|
}
|
||||||
|
|
||||||
func distance(lat1, lon1, lat2, lon2 float64) float64 {
|
func distance(lat1, lon1, lat2, lon2 float64) float64 {
|
||||||
const r = 6371 // Earth radius in kilometers
|
const r = 6371 // Earth radius in kilometers
|
||||||
|
|
||||||
dLat := (lat2 - lat1) * math.Pi / 180
|
dLat := (lat2 - lat1) * math.Pi / 180
|
||||||
dLon := (lon2 - lon1) * math.Pi / 180
|
dLon := (lon2 - lon1) * math.Pi / 180
|
||||||
a := math.Sin(dLat/2)*math.Sin(dLat/2) +
|
a := math.Sin(dLat/2)*math.Sin(dLat/2) +
|
||||||
math.Cos(lat1*math.Pi/180)*math.Cos(lat2*math.Pi/180)*
|
math.Cos(lat1*math.Pi/180)*math.Cos(lat2*math.Pi/180)*
|
||||||
math.Sin(dLon/2)*math.Sin(dLon/2)
|
math.Sin(dLon/2)*math.Sin(dLon/2)
|
||||||
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
||||||
return r * c
|
return r * c
|
||||||
}
|
}
|
||||||
|
|
||||||
func interpolate(lon1, lat1, lon2, lat2, ratio float64) (float64, float64) {
|
func interpolate(lon1, lat1, lon2, lat2, ratio float64) (float64, float64) {
|
||||||
return lon1 + (lon2-lon1)*ratio, lat1 + (lat2-lat1)*ratio
|
return lon1 + (lon2-lon1)*ratio, lat1 + (lat2-lat1)*ratio
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteOldEntries(db *sql.DB, deleteAfterMinutes int) {
|
func deleteOldEntries(db *sql.DB, deleteAfterMinutes int) {
|
||||||
deleteTime := time.Now().Add(time.Duration(-deleteAfterMinutes) * time.Minute)
|
deleteTime := time.Now().Add(time.Duration(-deleteAfterMinutes) * time.Minute)
|
||||||
result, err := db.Exec("DELETE FROM trips WHERE timestamp < ?", deleteTime)
|
result, err := db.Exec("DELETE FROM trips WHERE timestamp < ?", deleteTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Löschen alter Einträge: %v\n", err)
|
log.Printf("Fehler beim Löschen alter Einträge: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rowsAffected, _ := result.RowsAffected()
|
rowsAffected, _ := result.RowsAffected()
|
||||||
log.Printf("%d alte Einträge gelöscht\n", rowsAffected)
|
log.Printf("%d alte Einträge gelöscht\n", rowsAffected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logDatabaseStats(db *sql.DB) {
|
func logDatabaseStats(db *sql.DB) {
|
||||||
var count int
|
var count int
|
||||||
err := db.QueryRow("SELECT COUNT(*) FROM trips").Scan(&count)
|
err := db.QueryRow("SELECT COUNT(*) FROM trips").Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Abrufen der Datenbankstatistiken: %v\n", err)
|
log.Printf("Fehler beim Abrufen der Datenbankstatistiken: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("Aktuelle Anzahl der Einträge in der Datenbank: %d\n", count)
|
log.Printf("Aktuelle Anzahl der Einträge in der Datenbank: %d\n", count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
164
main.go.old
164
main.go.old
|
@ -1,164 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue