Skip to content

Commit 08a92da

Browse files
lemaldjzimbel-mbta
andauthored
Better handle shuttle alert directionality (#440)
* refactor: add direction_id argument to stop_shuttling_on_route/4 * test: test case analogous to Lowell Line shuttle * feat: parse and handle direction_id from shuttle alerts * chore(ci): bump version of asdf-vm/actions/install * chore(ci): bump cache key to not use old version of ASDF deps * refactor: only evaluate shuttle_type/1 once * refactor: better name boolean values Co-authored-by: Jon Zimbel <63608771+jzimbel-mbta@users.noreply.github.com> --------- Co-authored-by: Jon Zimbel <63608771+jzimbel-mbta@users.noreply.github.com>
1 parent e3043f1 commit 08a92da

6 files changed

Lines changed: 107 additions & 32 deletions

File tree

.github/workflows/elixir.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ jobs:
3232
uses: actions/cache@v4
3333
with:
3434
path: ~/.asdf
35-
key: ${{ runner.os }}-asdf-v2-${{ hashFiles('.tool-versions') }}
35+
key: ${{ runner.os }}-asdf-v4-${{ hashFiles('.tool-versions') }}
3636
id: asdf-cache
3737
# only run `asdf install` if we didn't hit the cache
38-
- uses: asdf-vm/actions/install@v1
38+
- uses: asdf-vm/actions/install@v4
3939
if: steps.asdf-cache.outputs.cache-hit != 'true'
4040
# if we did hit the cache, set up the environment
4141
- name: Setup ASDF environment

lib/concentrate/filter/alert/shuttles.ex

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,16 @@ defmodule Concentrate.Filter.Alert.Shuttles do
3636
@spec stop_shuttling_on_route(
3737
route_id :: String.t(),
3838
stop_id :: String.t(),
39+
direction_id :: 0 | 1,
3940
:calendar.date() | integer
4041
) :: :start | :stop | :through | nil
41-
def stop_shuttling_on_route(route_id, stop_id, date_or_timestamp)
42+
def stop_shuttling_on_route(route_id, stop_id, direction_id, date_or_timestamp)
4243
when is_binary(route_id) and is_binary(stop_id) do
43-
case TimeTable.date_overlaps(@table, {:route_stop, route_id, stop_id}, date_or_timestamp) do
44+
case TimeTable.date_overlaps(
45+
@table,
46+
{:route_stop, route_id, stop_id, direction_id},
47+
date_or_timestamp
48+
) do
4449
[atom | _] -> atom
4550
[] -> nil
4651
end
@@ -80,9 +85,11 @@ defmodule Concentrate.Filter.Alert.Shuttles do
8085

8186
route_stops =
8287
if is_binary(stop_id) and is_binary(route_id) do
83-
[
84-
{{:route_stop, route_id, stop_id}, shuttle_type(InformedEntity.activities(entity))}
85-
]
88+
shuttle_type = shuttle_type(InformedEntity.activities(entity))
89+
90+
for direction_id <- direction_ids(InformedEntity.direction_id(entity)) do
91+
{{:route_stop, route_id, stop_id, direction_id}, shuttle_type}
92+
end
8693
else
8794
[]
8895
end

lib/concentrate/group_filter/shuttle.ex

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule Concentrate.GroupFilter.Shuttle do
2121
stus =
2222
if is_tuple(date) and is_binary(route_id) and
2323
module.trip_shuttling?(trip_id, route_id, direction_id, date) do
24-
shuttle_updates(route_id, stus, module)
24+
shuttle_updates(route_id, direction_id, stus, module)
2525
else
2626
stus
2727
end
@@ -31,27 +31,27 @@ defmodule Concentrate.GroupFilter.Shuttle do
3131

3232
def filter(%TripGroup{} = other, _module), do: other
3333

34-
defp shuttle_updates(route_id, stus, module) do
35-
initial_state = {false, false}
34+
defp shuttle_updates(route_id, direction_id, stus, module) do
35+
initial_state = {_has_started? = false, _has_shuttled? = false}
3636

3737
stus
38-
|> Enum.flat_map_reduce(initial_state, &shuttle_stop(route_id, module, &1, &2))
38+
|> Enum.flat_map_reduce(initial_state, &shuttle_stop(route_id, direction_id, module, &1, &2))
3939
|> elem(0)
4040
end
4141

42-
defp shuttle_stop(route_id, shuttle_module, stop_time_updates, state)
42+
defp shuttle_stop(route_id, direction_id, shuttle_module, stop_time_updates, state)
4343

44-
defp shuttle_stop(_route_id, _module, stu, {true, true} = state) do
44+
defp shuttle_stop(_route_id, _direction_id, _module, stu, {true, true} = state) do
4545
{[StopTimeUpdate.skip(stu)], state}
4646
end
4747

48-
defp shuttle_stop(route_id, module, stu, {has_started?, has_shuttled?} = state) do
48+
defp shuttle_stop(route_id, direction_id, module, stu, {has_started?, has_shuttled?} = state) do
4949
time = StopTimeUpdate.time(stu)
5050

5151
if is_integer(time) do
5252
stop_id = StopTimeUpdate.stop_id(stu)
5353

54-
case module.stop_shuttling_on_route(route_id, stop_id, time) do
54+
case module.stop_shuttling_on_route(route_id, stop_id, direction_id, time) do
5555
nil ->
5656
drop_arrival_time_if_after_shuttle(stu, has_shuttled?)
5757

test/concentrate/filter/alert/shuttles_test.exs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ defmodule Concentrate.Filter.Alert.ShuttlesTest do
6969
end
7070
end
7171

72-
describe "stop_shuttling_on_route?/1" do
72+
describe "stop_shuttling_on_route/4" do
7373
setup :supervised
7474

7575
test "returns a atom indicating whether the route is shuttling a particular stop" do
@@ -88,6 +88,13 @@ defmodule Concentrate.Filter.Alert.ShuttlesTest do
8888
stop_id: "start",
8989
activities: ["BOARD", "RIDE"]
9090
),
91+
InformedEntity.new(
92+
route_type: 2,
93+
direction_id: 0,
94+
route_id: "route",
95+
stop_id: "start_unidirectional",
96+
activities: ["BOARD", "RIDE"]
97+
),
9198
InformedEntity.new(
9299
route_type: 2,
93100
route_id: "route",
@@ -99,11 +106,14 @@ defmodule Concentrate.Filter.Alert.ShuttlesTest do
99106

100107
handle_events([[alert]], :from, :state)
101108

102-
assert stop_shuttling_on_route("route", "through", 5) == :through
103-
assert stop_shuttling_on_route("route", "through", 21) == nil
104-
assert stop_shuttling_on_route("route", "through", {1970, 1, 1}) == :through
105-
assert stop_shuttling_on_route("route", "start", 5) == :start
106-
assert stop_shuttling_on_route("route", "stop", 5) == :stop
109+
assert stop_shuttling_on_route("route", "through", 0, 5) == :through
110+
assert stop_shuttling_on_route("route", "through", 0, 21) == nil
111+
assert stop_shuttling_on_route("route", "through", 0, {1970, 1, 1}) == :through
112+
assert stop_shuttling_on_route("route", "start", 0, 5) == :start
113+
assert stop_shuttling_on_route("route", "start", 1, 5) == :start
114+
assert stop_shuttling_on_route("route", "start_unidirectional", 0, 5) == :start
115+
assert stop_shuttling_on_route("route", "start_unidirectional", 1, 5) == nil
116+
assert stop_shuttling_on_route("route", "stop", 0, 5) == :stop
107117
end
108118
end
109119

@@ -112,8 +122,8 @@ defmodule Concentrate.Filter.Alert.ShuttlesTest do
112122
refute trip_shuttling?("trip", "route", 0, 0)
113123
end
114124

115-
test "stop_shuttling_on_route/3 returns nil" do
116-
assert stop_shuttling_on_route("route", "stop", 0) == nil
125+
test "stop_shuttling_on_route/4 returns nil" do
126+
assert stop_shuttling_on_route("route", "stop", 0, 0) == nil
117127
end
118128
end
119129

test/concentrate/group_filter/shuttle_test.exs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
77

88
@trip_id "trip"
99
@route_id "route"
10+
@direction_id 0
1011
@valid_date {1970, 1, 1}
1112
@valid_date_time 8
1213

@@ -28,6 +29,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
2829
TripDescriptor.new(
2930
trip_id: @trip_id,
3031
route_id: @route_id,
32+
direction_id: @direction_id,
3133
start_date: @valid_date
3234
)
3335

@@ -59,6 +61,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
5961
TripDescriptor.new(
6062
trip_id: @trip_id,
6163
route_id: @route_id,
64+
direction_id: @direction_id,
6265
start_date: {1970, 1, 1}
6366
),
6467
stus: [
@@ -112,6 +115,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
112115
TripDescriptor.new(
113116
trip_id: @trip_id,
114117
route_id: @route_id,
118+
direction_id: @direction_id,
115119
start_date: {1970, 1, 1}
116120
),
117121
stus: [
@@ -149,6 +153,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
149153
TripDescriptor.new(
150154
trip_id: @trip_id,
151155
route_id: @route_id,
156+
direction_id: @direction_id,
152157
start_date: {1970, 1, 1}
153158
),
154159
stus: [
@@ -187,6 +192,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
187192
TripDescriptor.new(
188193
trip_id: @trip_id,
189194
route_id: @route_id,
195+
direction_id: @direction_id,
190196
start_date: {1970, 1, 1}
191197
),
192198
stus: [
@@ -210,6 +216,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
210216
TripDescriptor.new(
211217
trip_id: @trip_id,
212218
route_id: @route_id,
219+
direction_id: @direction_id,
213220
start_date: {1970, 1, 1}
214221
),
215222
stus: [
@@ -230,7 +237,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
230237
TripDescriptor.new(
231238
trip_id: @trip_id,
232239
route_id: "single_direction",
233-
direction_id: 0,
240+
direction_id: @direction_id,
234241
start_date: {1970, 1, 1}
235242
),
236243
stus: [
@@ -274,7 +281,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
274281
TripDescriptor.new(
275282
trip_id: "other_trip",
276283
route_id: @route_id,
277-
direction_id: 0,
284+
direction_id: @direction_id,
278285
start_date: {1970, 1, 1}
279286
),
280287
stus: [
@@ -296,6 +303,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
296303
TripDescriptor.new(
297304
trip_id: @trip_id,
298305
route_id: @route_id,
306+
direction_id: @direction_id,
299307
start_date: {1970, 1, 1}
300308
),
301309
stus: [
@@ -349,6 +357,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
349357
TripDescriptor.new(
350358
trip_id: @trip_id,
351359
route_id: @route_id,
360+
direction_id: @direction_id,
352361
start_date: {1970, 1, 1}
353362
),
354363
stus: [
@@ -389,6 +398,7 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
389398
TripDescriptor.new(
390399
trip_id: @trip_id,
391400
route_id: @route_id,
401+
direction_id: @direction_id,
392402
start_date: {1970, 1, 1}
393403
),
394404
stus: [
@@ -414,10 +424,57 @@ defmodule Concentrate.GroupFilter.ShuttleTest do
414424
assert StopTimeUpdate.departure_time(stop)
415425
end
416426

427+
test "start of a shuttle in one direction doesn't affect STUs in the opposite direction" do
428+
group =
429+
%TripGroup{
430+
td:
431+
TripDescriptor.new(
432+
trip_id: @trip_id,
433+
route_id: @route_id,
434+
direction_id: @direction_id,
435+
start_date: {1970, 1, 1}
436+
),
437+
stus: [
438+
StopTimeUpdate.new(
439+
trip_id: @trip_id,
440+
stop_id: "shuttle_start_unidirectional",
441+
departure_time: @valid_date_time
442+
),
443+
StopTimeUpdate.new(
444+
trip_id: @trip_id,
445+
stop_id: "after_shuttle",
446+
arrival_time: @valid_date_time,
447+
departure_time: @valid_date_time
448+
),
449+
StopTimeUpdate.new(
450+
trip_id: @trip_id,
451+
stop_id: "after_shuttle_2",
452+
arrival_time: @valid_date_time
453+
)
454+
]
455+
}
456+
457+
%TripGroup{stus: reduced} = filter(group, @module)
458+
459+
assert [start_unidirectional, after_shuttle, after_shuttle_2] = reduced
460+
assert StopTimeUpdate.schedule_relationship(start_unidirectional) == :SCHEDULED
461+
assert StopTimeUpdate.departure_time(start_unidirectional)
462+
assert StopTimeUpdate.schedule_relationship(after_shuttle) == :SCHEDULED
463+
assert StopTimeUpdate.arrival_time(after_shuttle)
464+
assert StopTimeUpdate.departure_time(after_shuttle)
465+
assert StopTimeUpdate.schedule_relationship(after_shuttle_2) == :SCHEDULED
466+
assert StopTimeUpdate.arrival_time(after_shuttle_2)
467+
end
468+
417469
test "updates on non-shuttle trips are not modified" do
418470
group =
419471
%TripGroup{
420-
td: TripDescriptor.new(trip_id: "other_trip", start_date: @valid_date),
472+
td:
473+
TripDescriptor.new(
474+
trip_id: "other_trip",
475+
direction_id: @direction_id,
476+
start_date: @valid_date
477+
),
421478
stus: [
422479
StopTimeUpdate.new(
423480
trip_id: "other_trip",

test/support/filter/fakes.ex

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,11 @@ defmodule Concentrate.Filter.FakeShuttles do
108108
def trip_shuttling?("trip", "single_direction", 0, {1970, 1, 1}), do: true
109109
def trip_shuttling?(_trip_id, _route_id, _direction_id, {_, _, _}), do: false
110110

111-
def stop_shuttling_on_route("route", "shuttle_1", 8), do: :through
112-
def stop_shuttling_on_route("route", "shuttle_2", 8), do: :through
113-
def stop_shuttling_on_route("route", "shuttle_start", 8), do: :start
114-
def stop_shuttling_on_route("route", "shuttle_stop", 8), do: :stop
115-
def stop_shuttling_on_route("single_direction", "shuttle_1", 8), do: :through
116-
def stop_shuttling_on_route(_, _, dt) when is_integer(dt), do: nil
111+
def stop_shuttling_on_route("route", "shuttle_1", _direction_id, 8), do: :through
112+
def stop_shuttling_on_route("route", "shuttle_2", _direction_id, 8), do: :through
113+
def stop_shuttling_on_route("route", "shuttle_start", _direction_id, 8), do: :start
114+
def stop_shuttling_on_route("route", "shuttle_start_unidirectional", 1, 8), do: :start
115+
def stop_shuttling_on_route("route", "shuttle_stop", _direction_id, 8), do: :stop
116+
def stop_shuttling_on_route("single_direction", "shuttle_1", 0, 8), do: :through
117+
def stop_shuttling_on_route(_, _, _, dt) when is_integer(dt), do: nil
117118
end

0 commit comments

Comments
 (0)