Author: Adam Evyčędo <git@apiote.xyz>
make line graphs and headsigns array
api/api.go | 88 +++++++++++--------------------------- api/lineResponse.go | 44 +++++++++++++++++++ server/handler_vars.go | 64 ++++++++++++++++++++++++++- server/route_feeds.go | 11 ++-- server/route_line.go | 101 ++++++++++++++++++++++++++++++++++++++++++++ server/router.go | 60 -------------------------- traffic/convert.go | 37 +++++++-------- traffic/line.go | 1 traffic/structs_gen.go | 17 +++----
diff --git a/api/api.go b/api/api.go index 2931d8d64c2c244d598c2f70f734d2a66d486439..b99e1b920ca2496474c0cf0af7b2b977decab01d 100644 --- a/api/api.go +++ b/api/api.go @@ -77,66 +77,42 @@ return s } func convertTrafficLineGraphs(trafficLine traffic.Line, line LineV1, context traffic.Context, t *traffic.Traffic) (LineV1, error) { - if len(trafficLine.GraphThere.StopCodes) != 0 { - graph := LineGraphV1{ - Stops: make([]StopStubV1, len(trafficLine.GraphThere.StopCodes)), - NextNodes: trafficLine.GraphThere.NextNodes, - } - for i, code := range trafficLine.GraphThere.StopCodes { - stopStub, err := traffic.GetStopStub(code, line.Name, context, t) - if err != nil { - return line, fmt.Errorf("while getting stopStub for %s: %w", code, err) + for _, graph := range trafficLine.Graphs { + if len(graph.StopCodes) != 0 { + lineGraph := LineGraphV1{ + Stops: make([]StopStubV1, len(graph.StopCodes)), + NextNodes: graph.NextNodes, } - graph.Stops[i] = convertTrafficStopStub(stopStub) - } - line.Graphs = append(line.Graphs, graph) - } - if len(trafficLine.GraphBack.StopCodes) != 0 { - graph := LineGraphV1{ - Stops: make([]StopStubV1, len(trafficLine.GraphBack.StopCodes)), - NextNodes: trafficLine.GraphBack.NextNodes, - } - for i, code := range trafficLine.GraphBack.StopCodes { - stopStub, err := traffic.GetStopStub(code, line.Name, context, t) - if err != nil { - return line, fmt.Errorf("while getting stopStub for %s: %w", code, err) + for i, code := range graph.StopCodes { + stopStub, err := traffic.GetStopStub(code, line.Name, context, t) + if err != nil { + return line, fmt.Errorf("while getting stopStub for %s: %w", code, err) + } + lineGraph.Stops[i] = convertTrafficStopStub(stopStub) } - graph.Stops[i] = convertTrafficStopStub(stopStub) + line.Graphs = append(line.Graphs, lineGraph) } - line.Graphs = append(line.Graphs, graph) } return line, nil } func convertTrafficLineGraphsV1forLineV2(trafficLine traffic.Line, line LineV2, context traffic.Context, t *traffic.Traffic) (LineV2, error) { - if len(trafficLine.GraphThere.StopCodes) != 0 { - graph := LineGraphV1{ - Stops: make([]StopStubV1, len(trafficLine.GraphThere.StopCodes)), - NextNodes: trafficLine.GraphThere.NextNodes, - } - for i, code := range trafficLine.GraphThere.StopCodes { - stopStub, err := traffic.GetStopStub(code, line.Name, context, t) - if err != nil { - return line, fmt.Errorf("while getting stopStub for %s: %w", code, err) + for _, graph := range trafficLine.Graphs { + if len(graph.StopCodes) != 0 { + lineGraph := LineGraphV1{ + Stops: make([]StopStubV1, len(graph.StopCodes)), + NextNodes: graph.NextNodes, } - graph.Stops[i] = convertTrafficStopStub(stopStub) - } - line.Graphs = append(line.Graphs, graph) - } - if len(trafficLine.GraphBack.StopCodes) != 0 { - graph := LineGraphV1{ - Stops: make([]StopStubV1, len(trafficLine.GraphBack.StopCodes)), - NextNodes: trafficLine.GraphBack.NextNodes, - } - for i, code := range trafficLine.GraphBack.StopCodes { - stopStub, err := traffic.GetStopStub(code, line.Name, context, t) - if err != nil { - return line, fmt.Errorf("while getting stopStub for %s: %w", code, err) + for i, code := range graph.StopCodes { + stopStub, err := traffic.GetStopStub(code, line.Name, context, t) + if err != nil { + return line, fmt.Errorf("while getting stopStub for %s: %w", code, err) + } + lineGraph.Stops[i] = convertTrafficStopStub(stopStub) } - graph.Stops[i] = convertTrafficStopStub(stopStub) + line.Graphs = append(line.Graphs, lineGraph) } - line.Graphs = append(line.Graphs, graph) } return line, nil @@ -148,15 +124,9 @@ Name: line.Name, Colour: fromColor(line.Colour), Kind: makeLineTypeV2(line), FeedID: feedID, - Headsigns: [][]string{}, + Headsigns: line.Headsigns, } - if len(line.HeadsignsThere) != 0 { - l.Headsigns = append(l.Headsigns, line.HeadsignsThere) - } - if len(line.HeadsignsBack) != 0 { - l.Headsigns = append(l.Headsigns, line.HeadsignsBack) - } return l } @@ -166,15 +136,9 @@ Name: line.Name, Colour: fromColor(line.Colour), Kind: makeLineTypeV3(line), FeedID: feedID, - Headsigns: [][]string{}, + Headsigns: line.Headsigns, } - if len(line.HeadsignsThere) != 0 { - l.Headsigns = append(l.Headsigns, line.HeadsignsThere) - } - if len(line.HeadsignsBack) != 0 { - l.Headsigns = append(l.Headsigns, line.HeadsignsBack) - } return l } diff --git a/api/lineResponse.go b/api/lineResponse.go new file mode 100644 index 0000000000000000000000000000000000000000..1fda5e76233deed002c02e15e432010dfac279c9 --- /dev/null +++ b/api/lineResponse.go @@ -0,0 +1,44 @@ +package api + +import "apiote.xyz/p/szczanieckiej/traffic" + +func MakeLineResponse(line traffic.Line, context traffic.Context, t *traffic.Traffic, accept uint) (LineResponse, error) { + switch accept { + case 0: + response := LineResponseDev{ + Line: LineV2{}, + } + line, err := makeLineV2(line, context, t) + response.Line = line + return response, err + case 1: + response := LineResponseV1{ + Line: LineV1{}, + } + line, err := makeLineV1(line, context, t) + response.Line = line + return response, err + default: + return LineResponseDev{}, AcceptError + } +} + +func makeLineV2(line traffic.Line, context traffic.Context, t *traffic.Traffic) (LineV2, error) { + response, err := CreateSuccessLineV2(line, context, t) + if err != nil { + return LineV2{}, err + } + return response.(LineResponseDev).Line, nil + + // TODO +} + +func makeLineV1(line traffic.Line, context traffic.Context, t *traffic.Traffic) (LineV1, error) { + response, err := CreateSuccessLine(line, context, t) + if err != nil { + return LineV1{}, err + } + return response.(LineResponseV1).Line, nil + + // TODO +} diff --git a/server/handler_vars.go b/server/handler_vars.go index 5217d2e23aced3b92b3a81ceec7feb303ce17870..2d04b58d6209de2404c1a0fb82f81389191770f3 100644 --- a/server/handler_vars.go +++ b/server/handler_vars.go @@ -5,6 +5,7 @@ "apiote.xyz/p/szczanieckiej/config" "apiote.xyz/p/szczanieckiej/traffic" "net/http" + "strings" "git.sr.ht/~sircmpwn/go-bare" "golang.org/x/text/language" @@ -16,16 +17,23 @@ getRequest() *http.Request getTraffic() *traffic.Traffic getConfig() config.Config getAccept() uint + getFeedName() string + getPath() []string + setPath([]string) + getVersionCode() traffic.Validity + setVersionCode(traffic.Validity) + getContext() traffic.Context + setContext(traffic.Context) getAcceptLanguage() string - setAcceptLanguage(l string) + setAcceptLanguage(string) getPreferredLanguages() []language.Tag - setPreferredLanguages(t []language.Tag) + setPreferredLanguages([]language.Tag) getResponse() any - setResponse(r any) + setResponse(any) getResponseBytes() []byte - setResponseBytes(b []byte) + setResponseBytes([]byte) } type HandlerVars struct { @@ -34,7 +42,11 @@ r *http.Request t *traffic.Traffic c config.Config a uint + f string + path []string + versionCode traffic.Validity + context traffic.Context acceptLanguage string preferredLanguages []language.Tag @@ -57,6 +69,27 @@ } func (v HandlerVars) getAccept() uint { return v.a } +func (v HandlerVars) getFeedName() string { + return v.f +} +func (v HandlerVars) getPath() []string { + return v.path +} +func (v *HandlerVars) setPath(p []string) { + v.path = p +} +func (v HandlerVars) getVersionCode() traffic.Validity { + return v.versionCode +} +func (v *HandlerVars) setVersionCode(c traffic.Validity) { + v.versionCode = c +} +func (v HandlerVars) getContext() traffic.Context { + return v.context +} +func (v *HandlerVars) setContext(c traffic.Context) { + v.context = c +} func (v HandlerVars) getAcceptLanguage() string { return v.acceptLanguage } @@ -80,6 +113,29 @@ return v.responseBytes } func (v *HandlerVars) setResponseBytes(b []byte) { v.responseBytes = b +} + +func splitPath(v AbstractHandlerVars) AbstractHandlerVars { + v.setPath(strings.Split(v.getRequest().URL.Path[1:], "/")) + return v +} + +func getVersionCode(v AbstractHandlerVars) (AbstractHandlerVars, error) { + dateString := v.getRequest().Form.Get("date") + versionCode, _, err := parseDate(dateString, v.getFeedName(), v.getTraffic()) + v.setVersionCode(versionCode) + return v, err +} + +func createContext(v AbstractHandlerVars) AbstractHandlerVars { + v.setContext( + traffic.Context{ + DataHome: v.getConfig().FeedsPath, + FeedName: v.getFeedName(), + Version: v.getVersionCode(), + }, + ) + return v } func getAcceptLanguage(v AbstractHandlerVars) AbstractHandlerVars { diff --git a/server/route_feeds.go b/server/route_feeds.go index 0ee314d2f7d79762e79edfd2aa379ed1609239a9..4a9dc6d26b8a1bc8f20507c737be5740012e7e76 100644 --- a/server/route_feeds.go +++ b/server/route_feeds.go @@ -24,12 +24,6 @@ vv := v.(*FeedsHandlerVars) feedInfos, lastUpdates, err := traffic.GetFeedInfos(v.getTraffic(), v.getConfig()) - if err != nil && errors.Is(err, api.AcceptError) { - err = ServerError{ - code: http.StatusNotAcceptable, - } - } - vv.feedInfos = feedInfos vv.lastUpdates = lastUpdates return vv, err @@ -40,6 +34,11 @@ vv := v.(*FeedsHandlerVars) response, err := api.MakeFeedsResponse(vv.feedInfos, vv.lastUpdates, v.getAccept(), v.getPreferredLanguages()) v.setResponse(response) + if err != nil && errors.Is(err, api.AcceptError) { + err = ServerError{ + code: http.StatusNotAcceptable, + } + } return vv, err } diff --git a/server/route_line.go b/server/route_line.go new file mode 100644 index 0000000000000000000000000000000000000000..0a9ad73e5d7078fd3dd552363e4610e6d8e606b5 --- /dev/null +++ b/server/route_line.go @@ -0,0 +1,101 @@ +package server + +import ( + "errors" + + "apiote.xyz/p/szczanieckiej/api" + "apiote.xyz/p/szczanieckiej/config" + "apiote.xyz/p/szczanieckiej/traffic" + + "net/http" + + "apiote.xyz/p/gott/v2" + "git.sr.ht/~sircmpwn/go-bare" +) + +type LineHandlerVars struct { + HandlerVars + + context traffic.Context + + line traffic.Line +} + +func checkLinePath(v AbstractHandlerVars) error { + if len(v.getPath()) != 3 { + return ServerError{ + code: http.StatusNotFound, + field: "line", + value: "EMPTY", + } + } + + return nil +} + +func getLine(v AbstractHandlerVars) (AbstractHandlerVars, error) { + vv := v.(*LineHandlerVars) + line, err := traffic.GetLine(v.getPath()[2], v.getContext(), v.getTraffic()) + vv.line = line + return vv, err +} + +func checkEmptyLine(v AbstractHandlerVars) error { + vv := v.(*LineHandlerVars) + if vv.line.Name == "" { + return ServerError{ + code: http.StatusNotFound, + field: "line", + value: v.getPath()[2], + } + } + + return nil +} + +func makeLineResponse(v AbstractHandlerVars) (AbstractHandlerVars, error) { + vv := v.(*LineHandlerVars) + + response, err := api.MakeLineResponse(vv.line, v.getContext(), v.getTraffic(), v.getAccept()) + v.setResponse(response) + if err != nil && errors.Is(err, api.AcceptError) { + err = ServerError{ + code: http.StatusNotAcceptable, + } + } + + return vv, err +} + +func marshalLineResponse(v AbstractHandlerVars) (AbstractHandlerVars, error) { + r := v.getResponse().(api.LineResponse) + bytes, err := bare.Marshal(&r) + v.setResponseBytes(bytes) + return v, err +} + +func handleLine(w http.ResponseWriter, r *http.Request, feedName string, cfg config.Config, t *traffic.Traffic, accept uint) error { + handlerVars := &LineHandlerVars{ + HandlerVars: HandlerVars{ + w: w, + r: r, + t: t, + c: cfg, + a: accept, + }, + } + result := gott.R[AbstractHandlerVars]{ + S: handlerVars, + } + result. + Map(splitPath). + Tee(checkLinePath). + Bind(getVersionCode). + Map(createContext). + Bind(getLine). + Tee(checkEmptyLine). + Bind(makeLineResponse). + Bind(marshalLineResponse) + + return result.E +} diff --git a/server/router.go b/server/router.go index 3ed7415b0f6e113dcac42db9e008c5f8c1ddf69d..f7675d44ba113e8279dcf8376211422dea8809eb 100644 --- a/server/router.go +++ b/server/router.go @@ -499,66 +499,6 @@ } return nil } -func handleLine(w http.ResponseWriter, r *http.Request, feedName string, cfg config.Config, t *traffic.Traffic, accept uint) error { - if accept > 1 { - return ServerError{ - code: http.StatusNotAcceptable, - } - } - path := strings.Split(r.URL.Path[1:], "/") - if len(path) == 3 { - dateString := r.Form.Get("date") - versionCode, _, err := parseDate(dateString, feedName, t) - if err != nil { - return err - } - name := path[2] - - context := traffic.Context{ - DataHome: cfg.FeedsPath, - FeedName: feedName, - Version: versionCode, - } - - line, err := traffic.GetLine(name, context, t) - if err != nil { - return fmt.Errorf("while getting line: %w", err) - } - if line.Name == "" { - return ServerError{ - code: http.StatusNotFound, - field: "line", - value: name, - } - } - var success api.LineResponse - switch accept { - case 0: - success, err = api.CreateSuccessLineV2(line, context, t) - case 1: - success, err = api.CreateSuccessLine(line, context, t) - } - if err != nil { - return fmt.Errorf("while creating response: %w", err) - } - bytes, err := bare.Marshal(&success) - if err != nil { - return fmt.Errorf("while marshaling line: %w", err) - } - _, err = w.Write(bytes) - if err != nil { - return fmt.Errorf("while writing: %w", err) - } - } else { - return ServerError{ - code: http.StatusNotFound, - field: "line", - value: "EMPTY", - } - } - return nil -} - func sendError(w http.ResponseWriter, r *http.Request, err error) { var ( se ServerError diff --git a/traffic/convert.go b/traffic/convert.go index a3c9d3c4fb3e220496bfc8a7ac2314321bfcd226..a49e3b03356915983b357d580834a3bc90b1e9b0 100644 --- a/traffic/convert.go +++ b/traffic/convert.go @@ -909,11 +909,9 @@ if _, ok := graphs[trip.LineName]; !ok { graphs[trip.LineName] = map[uint]LineGraph{} graphs[trip.LineName][0] = LineGraph{ NextNodes: map[int][]int{}, - PrevNodes: map[int][]int{}, } graphs[trip.LineName][1] = LineGraph{ NextNodes: map[int][]int{}, - PrevNodes: map[int][]int{}, } } @@ -948,15 +946,7 @@ graph.StopCodes = append(graph.StopCodes, stop.Code) } if previousTripID != tripID { // first of current trip - connectionDone := false - for _, n := range graph.PrevNodes[current] { - if n == -1 { - connectionDone = true - } - } - if !connectionDone { - graph.PrevNodes[current] = append(graph.PrevNodes[current], -1) - } + graph.NextNodes[-1] = append(graph.NextNodes[-1], current) } else { // second <- first to last <- penultimate of current trip connectionDone := false @@ -967,7 +957,6 @@ } } if !connectionDone { graph.NextNodes[previous] = append(graph.NextNodes[previous], current) - graph.PrevNodes[current] = append(graph.PrevNodes[current], previous) } } @@ -1055,15 +1044,23 @@ if colour == "" { colour = "ffffff" } + headsigns := [][]string{} + for _, headsign := range c.lineHeadsigns[lineName] { + headsigns = append(headsigns, headsign) + } + + graphs := []LineGraph{} + for _, graph := range c.LineGraphs[lineName] { + graphs = append(graphs, graph) + } + line := Line{ - Id: routeID, - Name: lineName, - Colour: hex2colour(colour), - Kind: LineType(kind), - GraphThere: c.LineGraphs[lineName][0], - GraphBack: c.LineGraphs[lineName][1], - HeadsignsThere: c.lineHeadsigns[lineName][0], - HeadsignsBack: c.lineHeadsigns[lineName][1], + Id: routeID, + Name: lineName, + Colour: hex2colour(colour), + Kind: LineType(kind), + Graphs: graphs, + Headsigns: headsigns, } if field, present := fields["agency_id"]; present { diff --git a/traffic/line.go b/traffic/line.go new file mode 100644 index 0000000000000000000000000000000000000000..afb546e8275f9eba7a4b1ffaed28da19058735f2 --- /dev/null +++ b/traffic/line.go @@ -0,0 +1 @@ +package traffic diff --git a/traffic/structs_gen.go b/traffic/structs_gen.go index b58b794530595797adaac0a8aa7339e799add583..9bd9ff49d7174901352a521fdee6cb3beefa02d5 100644 --- a/traffic/structs_gen.go +++ b/traffic/structs_gen.go @@ -103,7 +103,6 @@ type LineGraph struct { StopCodes []string `bare:"stopCodes"` NextNodes map[int][]int `bare:"nextNodes"` - PrevNodes map[int][]int `bare:"prevNodes"` } func (t *LineGraph) Decode(data []byte) error { @@ -143,15 +142,13 @@ return bare.Marshal(t) } type Line struct { - Id string `bare:"id"` - Name string `bare:"name"` - Colour Colour `bare:"colour"` - Kind LineType `bare:"kind"` - AgencyID string `bare:"agencyID"` - HeadsignsThere []string `bare:"headsignsThere"` - HeadsignsBack []string `bare:"headsignsBack"` - GraphThere LineGraph `bare:"graphThere"` - GraphBack LineGraph `bare:"graphBack"` + Id string `bare:"id"` + Name string `bare:"name"` + Colour Colour `bare:"colour"` + Kind LineType `bare:"kind"` + AgencyID string `bare:"agencyID"` + Headsigns [][]string `bare:"headsigns"` + Graphs []LineGraph `bare:"graphs"` } func (t *Line) Decode(data []byte) error {