252 lines
5.1 KiB
Go
252 lines
5.1 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"html/template"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
var stopIDs map[int] int;
|
||
|
||
type Favourite struct {
|
||
Name string `json:"name"`
|
||
Code string `json:"code"`
|
||
ID string `json:"id"`
|
||
}
|
||
|
||
type CarFlags struct {
|
||
Disabled bool `json:"disabled"`
|
||
AirConditioner bool `json:"klimatik"`
|
||
BicycleRack bool `json:"bicycle_rack"`
|
||
DoubleDecker bool `json:"doubledecker"`
|
||
ByTimeTable bool `json:"by_timetable"`
|
||
}
|
||
|
||
type Car struct {
|
||
DepartureTime string `json:"departure_time"`
|
||
Flags CarFlags `json:"flags"`
|
||
Destination int `json:"destination"`
|
||
CourseID int `json:"course_id"`
|
||
DestinationName string `json:"destination_name"`
|
||
CalculatedTime string `json:"calc_time"`
|
||
DirectionFlag int `json:"direction_flag"`
|
||
TTCourseStopID int `json:"ttcoursestop_id"`
|
||
}
|
||
|
||
type Line struct {
|
||
Transport string `json:"transport"`
|
||
ID int `json:"id"`
|
||
Name string `json:"name"`
|
||
Cars []Car `json:"cars"`
|
||
Color string
|
||
}
|
||
|
||
type VirtualPanelData struct {
|
||
ID int `json:"id"`
|
||
Lines []Line `json:"lines"`
|
||
}
|
||
|
||
type SofiaTraffic struct {
|
||
Data VirtualPanelData `json:"virtual_panel_data"`
|
||
}
|
||
|
||
type OutputData struct {
|
||
ID string
|
||
Lines []Line
|
||
}
|
||
|
||
type CacheData struct {
|
||
TargetID int `json:"target_id"`
|
||
DataID int `json:"data_id"`
|
||
}
|
||
|
||
func lessLine(left Line, right Line) bool {
|
||
val := strings.Compare(left.Transport, right.Transport)
|
||
if val != 0 {
|
||
return val < 0
|
||
} else {
|
||
l, lerr := strconv.Atoi(left.Name)
|
||
r, rerr := strconv.Atoi(right.Name)
|
||
|
||
switch {
|
||
case lerr == nil && rerr == nil:
|
||
return l < r
|
||
case lerr == nil && rerr != nil:
|
||
return true
|
||
case lerr != nil && rerr == nil:
|
||
return false
|
||
case lerr != nil && rerr != nil:
|
||
val = strings.Compare(left.Name, right.Name)
|
||
return val < 0
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func handle(resp http.ResponseWriter, req *http.Request) {
|
||
id := strings.TrimPrefix(req.URL.Path, "/")
|
||
find_id := req.URL.Query().Get("find")
|
||
if len(find_id) != 0 {
|
||
find_int, err := strconv.Atoi(find_id)
|
||
if err != nil {
|
||
log.Println(err)
|
||
show500(resp)
|
||
return
|
||
}
|
||
target_id := stopIDs[find_int]
|
||
http.Redirect(resp, req, fmt.Sprintf("/%d", target_id), http.StatusFound)
|
||
} else if len(id) == 0 {
|
||
showFavourites(resp)
|
||
} else {
|
||
showTable(resp, id)
|
||
}
|
||
}
|
||
|
||
func show500(resp http.ResponseWriter) {
|
||
resp.WriteHeader(http.StatusInternalServerError)
|
||
fmt.Fprintf(resp, "<h1 style=\"text-align:center;\">500 Internal Server Error</h1>")
|
||
}
|
||
|
||
func setLineColor(line *Line) {
|
||
switch {
|
||
case line.Transport == "A":
|
||
line.Color = "FF0000"
|
||
case line.Transport == "ТБ":
|
||
line.Color = "0000FF"
|
||
case line.Transport == "ТМ":
|
||
line.Color = "FF8000"
|
||
}
|
||
}
|
||
|
||
func getSofiaTrafficData(id string) (SofiaTraffic, error) {
|
||
var data SofiaTraffic
|
||
url := fmt.Sprintf("https://www.sofiatraffic.bg/interactivecard/virtual_panel?stop_id=%s", id)
|
||
|
||
respAPI, err := http.Get(url)
|
||
if err != nil {
|
||
log.Println(err)
|
||
return data, errors.New("get failed")
|
||
}
|
||
if respAPI.StatusCode != 200 {
|
||
log.Printf("Status code for ID %v is %v", id, respAPI.StatusCode)
|
||
return data, errors.New("bad status code")
|
||
}
|
||
if respAPI.Body != nil {
|
||
defer respAPI.Body.Close()
|
||
}
|
||
|
||
body, err := io.ReadAll(respAPI.Body)
|
||
if err != nil {
|
||
log.Println(err)
|
||
return data, errors.New("failed to read body")
|
||
}
|
||
|
||
err = json.Unmarshal(body, &data)
|
||
if err != nil {
|
||
log.Println(err)
|
||
return data, errors.New("failed to unmarshal json")
|
||
}
|
||
|
||
return data, nil
|
||
}
|
||
|
||
func showTable(resp http.ResponseWriter, id string) {
|
||
data, err := getSofiaTrafficData(id)
|
||
if err != nil {
|
||
show500(resp)
|
||
return
|
||
}
|
||
|
||
// Sort Lines
|
||
sort.Slice(data.Data.Lines, func (i int, j int) bool {
|
||
return lessLine(data.Data.Lines[i], data.Data.Lines[j])
|
||
})
|
||
|
||
// Set Line Colors
|
||
for idx := range(data.Data.Lines) {
|
||
setLineColor(&(data.Data.Lines[idx]))
|
||
}
|
||
|
||
var dataOut OutputData
|
||
dataOut.ID = fmt.Sprintf("%04d", data.Data.ID)
|
||
dataOut.Lines = data.Data.Lines
|
||
|
||
templ := template.Must(template.ParseFiles("stop.html"))
|
||
templ.Execute(resp, dataOut)
|
||
}
|
||
|
||
func readFavourites() []Favourite {
|
||
var favourites []Favourite
|
||
jsonFile, err := os.Open("favourites.json")
|
||
if err != nil {
|
||
log.Println(err)
|
||
return favourites
|
||
}
|
||
defer jsonFile.Close()
|
||
|
||
bytes, err := io.ReadAll(jsonFile)
|
||
if err != nil {
|
||
log.Println(err)
|
||
return favourites
|
||
}
|
||
|
||
err = json.Unmarshal(bytes, &favourites)
|
||
if err != nil {
|
||
log.Println(err)
|
||
}
|
||
return favourites
|
||
}
|
||
|
||
func showFavourites(resp http.ResponseWriter) {
|
||
// Read And Parse JSON
|
||
favourites := readFavourites()
|
||
data := make(map[string] []Favourite)
|
||
data["Favourites"] = favourites
|
||
|
||
// Read And Execute Template
|
||
templ := template.Must(template.ParseFiles("index.html"))
|
||
templ.Execute(resp, data)
|
||
}
|
||
|
||
func loadIDs() {
|
||
stopIDs = make(map[int]int)
|
||
var cache []CacheData
|
||
jsonFile, err := os.Open("cache.json")
|
||
if err != nil {
|
||
log.Println(err)
|
||
return
|
||
}
|
||
defer jsonFile.Close()
|
||
|
||
bytes, err := io.ReadAll(jsonFile)
|
||
if err != nil {
|
||
log.Println(err)
|
||
return
|
||
}
|
||
|
||
err = json.Unmarshal(bytes, &cache)
|
||
if err != nil {
|
||
log.Println(err)
|
||
return
|
||
}
|
||
|
||
for _, val := range(cache) {
|
||
stopIDs[val.TargetID] = val.DataID
|
||
}
|
||
}
|
||
|
||
func main() {
|
||
loadIDs()
|
||
http.HandleFunc("/", handle)
|
||
log.Println("Server Running")
|
||
http.ListenAndServe(":8000", nil)
|
||
}
|