szczanieckiej.git

commit a0633a4eee7fd36c7e2491b6fd2ecaa2a137979b

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 {