Skip to content

Commit c003d8d

Browse files
committed
Temporal precedence thoughts
1 parent 7d8bb39 commit c003d8d

File tree

4 files changed

+571
-4
lines changed

4 files changed

+571
-4
lines changed

.github/workflows/release.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ jobs:
176176
with:
177177
go-version: ${{ env.GO_VERSION }}
178178

179+
- name: Verify clean directory
180+
run: git diff --exit-code
181+
shell: bash
182+
179183
- name: Build wheels
180184
if: matrix.platform != 'macos-arm64'
181185
uses: pypa/[email protected]
@@ -193,10 +197,6 @@ jobs:
193197
CIBW_REPAIR_WHEEL_COMMAND: ""
194198
MACOSX_DEPLOYMENT_TARGET: 14.0
195199

196-
- name: Verify clean directory
197-
run: git diff --exit-code
198-
shell: bash
199-
200200
- name: Upload wheels
201201
uses: actions/upload-artifact@v4
202202
with:

factory/precedence_temporal.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// © 2019-present nextmv.io inc
2+
3+
package factory
4+
5+
import (
6+
"fmt"
7+
"reflect"
8+
9+
"github.com/nextmv-io/nextroute"
10+
nmerror "github.com/nextmv-io/nextroute/common/errors"
11+
"github.com/nextmv-io/nextroute/schema"
12+
)
13+
14+
// addPrecedenceTemporallyInformation adds information to the Model data, when a
15+
// stop's "precedes_temporally" or "succeeds_temporally" field is not nil.
16+
func addPrecedenceTemporallyInformation(
17+
input schema.Input,
18+
model nextroute.Model,
19+
_ Options,
20+
) (nextroute.Model, error) {
21+
present := false
22+
var sequences []sequence
23+
stopIDToIndex := map[string]int{}
24+
for s, stop := range input.Stops {
25+
stopIDToIndex[stop.ID] = s
26+
if stop.Precedes == nil && stop.Succeeds == nil {
27+
continue
28+
}
29+
30+
stopSequences, err := getSequences(stop)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
sequences = append(sequences, stopSequences...)
36+
present = true
37+
}
38+
39+
if !present {
40+
return model, nil
41+
}
42+
43+
data, err := getModelData(model)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
data.sequences = sequences
49+
50+
model.SetData(data)
51+
52+
return model, nil
53+
}
54+
55+
// precedence processes the "Precedes" or "Succeeds" field of a stop. It return
56+
// the precedence (succeeds or precedes) as a slice of strings, even for a
57+
// single string.
58+
func precedenceTemporal(stop schema.Stop, name string) ([]precedenceData, error) {
59+
field := reflect.ValueOf(stop).FieldByName(name).Interface()
60+
var precedence []precedenceData
61+
if field == nil {
62+
return precedence, nil
63+
}
64+
65+
// field can be a string, a slice of strings or a slice of structs of type
66+
// precedenceData.
67+
switch field := field.(type) {
68+
case string:
69+
precedence = append(precedence, precedenceData{id: field})
70+
return precedence, nil
71+
case []any:
72+
for i, element := range field {
73+
switch element := element.(type) {
74+
case string:
75+
precedence = append(precedence, precedenceData{id: element})
76+
case map[string]any:
77+
if id, ok := element["id"].(string); ok {
78+
direct, _ := element["direct"].(bool)
79+
precedence = append(precedence, precedenceData{id: id, direct: direct})
80+
} else {
81+
return nil,
82+
nmerror.NewInputDataError(fmt.Errorf(
83+
"could not obtain %s from stop %s, "+
84+
"element %v in slice is missing field id",
85+
name,
86+
stop.ID,
87+
i,
88+
))
89+
}
90+
default:
91+
return nil,
92+
nmerror.NewInputDataError(fmt.Errorf(
93+
"could not obtain %s from stop %s, "+
94+
"element %v in slice is neither string nor struct, got %v",
95+
name,
96+
stop.ID,
97+
i,
98+
element,
99+
))
100+
}
101+
}
102+
return precedence, nil
103+
default:
104+
return nil,
105+
fmt.Errorf(
106+
"could not obtain %s from stop %s, "+
107+
"it is not of type string or slice of string or slice of structs with fields id and direct, got %v",
108+
name,
109+
stop.ID,
110+
field,
111+
)
112+
}
113+
}
114+
115+
type precedenceData struct {
116+
id string
117+
direct bool
118+
}
119+
120+
// getSequences returns all the sequences for a stop, based on the "precedes"
121+
// and "succeeds" fields.
122+
func getSequences(stop schema.Stop) ([]sequence, error) {
123+
var sequences []sequence
124+
if stop.Precedes != nil {
125+
precedes, err := precedence(stop, "Precedes")
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
predecessorSequences := make([]sequence, len(precedes))
131+
for i, p := range precedes {
132+
predecessorSequences[i] = sequence{
133+
predecessor: stop.ID,
134+
successor: p.id,
135+
direct: p.direct,
136+
}
137+
}
138+
sequences = append(sequences, predecessorSequences...)
139+
}
140+
141+
if stop.Succeeds != nil {
142+
succeeds, err := precedence(stop, "Succeeds")
143+
if err != nil {
144+
return nil, err
145+
}
146+
147+
successorSequences := make([]sequence, len(succeeds))
148+
for i, s := range succeeds {
149+
successorSequences[i] = sequence{
150+
predecessor: s.id,
151+
successor: stop.ID,
152+
direct: s.direct,
153+
}
154+
}
155+
156+
sequences = append(sequences, successorSequences...)
157+
}
158+
159+
return sequences, nil
160+
}

0 commit comments

Comments
 (0)