sofiatraffic/main.go

236 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"errors"
"fmt"
"html/template"
"io"
"log"
"net/http"
"os"
"sort"
"strconv"
"strings"
)
type Favourite struct {
Name string `json:"name"`
Code string `json:"code"`
}
type Car struct {
Time string `json:"time"`
}
type MaybeString string
func (ms *MaybeString) UnmarshalJSON(b []byte) error {
var i any
err := json.Unmarshal(b, &i)
if err != nil {
return err
}
switch val := i.(type) {
case int, float32, float64:
*ms = MaybeString(fmt.Sprint(val))
return nil
case string:
*ms = MaybeString(val)
return nil
default:
return fmt.Errorf("unknown type %T", val)
}
}
type Line struct {
Type string `json:"vehicle_type"`
Name MaybeString `json:"name"`
Cars []Car `json:"arrivals"`
Color string
}
type SofiaTraffic struct {
Name string `json:"name"`
Code string `json:"code"`
Lines []Line `json:"lines"`
}
func lessLine(left Line, right Line) bool {
val := strings.Compare(left.Type, right.Type)
if val != 0 {
return val < 0
} else {
l, lerr := strconv.Atoi(string(left.Name))
r, rerr := strconv.Atoi(string(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(string(left.Name), string(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
}
http.Redirect(resp, req, fmt.Sprintf("/%04d", find_int), 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 fixLine(line *Line) {
switch line.Type {
case "bus":
line.Type = "А"
case "trolley":
line.Type = "ТБ"
case "tram":
line.Type = "ТМ"
}
switch line.Name[0] {
case 'E':
line.Type = "А"
line.Name = line.Name[1:]
}
}
func setLineColor(line *Line) {
switch {
case line.Type == "А":
line.Color = "BD202E"
case line.Type == "ТБ":
line.Color = "2AA9E0"
case line.Type == "ТМ":
line.Color = "F7941F"
default:
line.Color = "000000"
}
}
func getSofiaTrafficData(code string) (SofiaTraffic, error) {
var data SofiaTraffic
url := fmt.Sprintf("https://api-arrivals.sofiatraffic.bg/api/v1/arrivals/%s/", code)
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 Code %v is %v", code, 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, code string) {
data, err := getSofiaTrafficData(code)
if err != nil {
fmt.Println("POOTIS")
show500(resp)
return
}
for idx := range(data.Lines) {
// Fix Line
fixLine(&(data.Lines[idx]))
// Set Line Colors
setLineColor(&(data.Lines[idx]))
// Do not show seconds
for car_idx := range(data.Lines[idx].Cars) {
data.Lines[idx].Cars[car_idx].Time = data.Lines[idx].Cars[car_idx].Time[:5]
}
}
// Sort Lines
sort.Slice(data.Lines, func (i int, j int) bool {
return lessLine(data.Lines[i], data.Lines[j])
})
templ := template.Must(template.ParseFiles("stop.html"))
templ.Execute(resp, data)
}
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 main() {
http.HandleFunc("/", handle)
log.Println("Server Running")
http.ListenAndServe(":8000", nil)
}