Skip to content

Commit

Permalink
refactor: add "representative headsigns"
Browse files Browse the repository at this point in the history
This is in support of the new "real-time destinations" logic.

* RoutePatterns now have a `headsign`, which is the headsign of their
  representative trip.

* Trips now have a `pattern_headsign`, which is the headsign of their
  route pattern (if they have one), and a `representative_headsign`
  accessor, which returns the pattern headsign if there is one and the
  regular headsign if not.

* Departures now have a `representative_headsign` accessor, which
  delegates to the same-named accessor on the Trip of either their
  Prediction or Schedule, as appropriate.
  • Loading branch information
digitalcora committed Oct 11, 2024
1 parent 14d43df commit 68b3470
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 30 deletions.
2 changes: 1 addition & 1 deletion lib/screens/predictions/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ defmodule Screens.Predictions.Parser do
},
included
) do
trip = included |> Map.fetch!({trip_id, "trip"}) |> Trips.Parser.parse_trip()
trip = included |> Map.fetch!({trip_id, "trip"}) |> Trips.Parser.parse_trip(included)
stop = included |> Map.fetch!({stop_id, "stop"}) |> Stops.Parser.parse_stop()
route = included |> Map.fetch!({route_id, "route"}) |> Routes.Parser.parse_route(included)

Expand Down
5 changes: 4 additions & 1 deletion lib/screens/predictions/prediction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ defmodule Screens.Predictions.Prediction do
query_params,
"predictions",
Screens.Predictions.Parser,
%{include: ~w[alerts route.line stop trip.stops vehicle]}
%{
include:
~w[alerts route.line stop trip.route_pattern.representative_trip trip.stops vehicle]
}
)

case predictions do
Expand Down
25 changes: 12 additions & 13 deletions lib/screens/route_patterns/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,24 @@ defmodule Screens.RoutePatterns.Parser do
},
included
) do
%{
"attributes" => %{"headsign" => headsign},
"relationships" => %{"stops" => %{"data" => stop_references}}
} = Map.fetch!(included, {representative_trip_id, "trip"})

stops =
Enum.map(stop_references, fn %{"id" => stop_id} ->
included |> Map.fetch!({stop_id, "stop"}) |> Stops.Parser.parse_stop()
end)

%RoutePattern{
id: id,
canonical?: canonical?,
direction_id: direction_id,
typicality: typicality,
route: included |> Map.fetch!({route_id, "route"}) |> Routes.Parser.parse_route(included),
stops:
included
|> Map.fetch!({representative_trip_id, "trip"})
|> parse_representative_stops(included)
headsign: headsign,
stops: stops
}
end

defp parse_representative_stops(
%{"relationships" => %{"stops" => %{"data" => stop_references}}},
included
) do
Enum.map(stop_references, fn %{"id" => stop_id} ->
included |> Map.fetch!({stop_id, "stop"}) |> Stops.Parser.parse_stop()
end)
end
end
3 changes: 2 additions & 1 deletion lib/screens/route_patterns/route_pattern.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule Screens.RoutePatterns.RoutePattern do
alias Screens.Trips.Trip
alias Screens.V3Api

defstruct ~w[id canonical? direction_id typicality route stops]a
defstruct ~w[id canonical? direction_id typicality route headsign stops]a

@type id :: String.t()
@type typicality :: 1 | 2 | 3 | 4 | 5
Expand All @@ -19,6 +19,7 @@ defmodule Screens.RoutePatterns.RoutePattern do
direction_id: Trip.direction(),
typicality: typicality(),
route: Route.t(),
headsign: String.t(),
stops: [Stop.t()]
}

Expand Down
2 changes: 1 addition & 1 deletion lib/screens/schedules/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ defmodule Screens.Schedules.Parser do
},
included
) do
trip = included |> Map.fetch!({trip_id, "trip"}) |> Trips.Parser.parse_trip()
trip = included |> Map.fetch!({trip_id, "trip"}) |> Trips.Parser.parse_trip(included)
stop = included |> Map.fetch!({stop_id, "stop"}) |> Stops.Parser.parse_stop()
route = included |> Map.fetch!({route_id, "route"}) |> Routes.Parser.parse_route(included)

Expand Down
6 changes: 5 additions & 1 deletion lib/screens/schedules/schedule.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ defmodule Screens.Schedules.Schedule do
query_params,
"schedules",
Screens.Schedules.Parser,
Map.put(extra_params, :include, ~w[route.line stop trip.stops])
Map.put(
extra_params,
:include,
~w[route.line stop trip.route_pattern.representative_trip trip.stops]
)
)

case schedules do
Expand Down
48 changes: 42 additions & 6 deletions lib/screens/trips/parser.ex
Original file line number Diff line number Diff line change
@@ -1,23 +1,59 @@
defmodule Screens.Trips.Parser do
@moduledoc false

def parse_trip(%{"id" => id, "attributes" => attributes, "relationships" => relationships}) do
%{"headsign" => headsign, "direction_id" => direction_id} = attributes
%{"route" => %{"data" => %{"id" => route_id}}} = relationships
alias Screens.Trips.Trip

def parse(%{"data" => data} = response) do
included =
response
|> Map.get("included", [])
|> Map.new(fn %{"id" => id, "type" => type} = resource -> {{id, type}, resource} end)

Enum.map(data, &parse_trip(&1, included))
end

def parse_trip(
%{
"id" => id,
"attributes" => %{"headsign" => headsign, "direction_id" => direction_id},
"relationships" => %{"route" => %{"data" => %{"id" => route_id}}} = relationships
},
included
) do
# We do not fully parse the trip => route_pattern => representative_trip chain, as this would
# recurse infinitely. Instead we hoist up only the attributes we're interested in.
pattern_headsign =
case Map.get(relationships, "route_pattern") do
%{
"data" => %{
"relationships" => %{
"representative_trip" => %{"data" => %{"id" => representative_trip_id}}
}
}
} ->
included
|> Map.fetch!({representative_trip_id, "trip"})
|> Map.fetch!("attributes")
|> Map.fetch!("headsign")

_ ->
nil
end

stops =
case Map.get(relationships, "stops") do
%{"data" => stop_list} ->
Enum.map(stop_list, fn %{"id" => stop_id, "type" => "stop"} -> stop_id end)
%{"data" => stops_data} ->
Enum.map(stops_data, fn %{"id" => stop_id, "type" => "stop"} -> stop_id end)

_ ->
nil
end

%Screens.Trips.Trip{
%Trip{
id: id,
direction_id: direction_id,
headsign: headsign,
pattern_headsign: pattern_headsign,
route_id: route_id,
stops: stops
}
Expand Down
15 changes: 9 additions & 6 deletions lib/screens/trips/trip.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ defmodule Screens.Trips.Trip do

alias Screens.Stops.Stop

defstruct id: "",
direction_id: nil,
headsign: nil,
route_id: nil,
stops: nil
defstruct ~w[id direction_id headsign pattern_headsign route_id stops]a

@type id :: String.t()
@type direction :: 0 | 1

@type t :: %__MODULE__{
id: id,
direction_id: direction(),
headsign: String.t() | nil,
headsign: String.t(),
pattern_headsign: String.t() | nil,
route_id: String.t() | nil,
stops: list(Stop.id()) | nil
}

@spec representative_headsign(t()) :: String.t()
def representative_headsign(%__MODULE__{pattern_headsign: headsign}) when not is_nil(headsign),
do: headsign

def representative_headsign(%__MODULE__{headsign: headsign}), do: headsign
end
7 changes: 7 additions & 0 deletions lib/screens/v2/departure.ex
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ defmodule Screens.V2.Departure do
def id(%__MODULE__{prediction: %Prediction{id: prediction_id}}), do: prediction_id
def id(%__MODULE__{schedule: %Schedule{id: schedule_id}}), do: schedule_id

@spec representative_headsign(t()) :: String.t() | nil
def representative_headsign(%__MODULE__{prediction: %Prediction{trip: trip}}),
do: Trip.representative_headsign(trip)

def representative_headsign(%__MODULE__{schedule: %Schedule{trip: trip}}),
do: Trip.representative_headsign(trip)

@spec route(t()) :: Route.t()
def route(%__MODULE__{prediction: %Prediction{route: route}}), do: route
def route(%__MODULE__{prediction: nil, schedule: %Schedule{route: route}}), do: route
Expand Down
6 changes: 6 additions & 0 deletions test/screens/route_patterns/route_pattern_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ defmodule Screens.RoutePatterns.RoutePatternTest do
%{
"id" => "canonical-Blue-C1-0",
"type" => "trip",
"attributes" => %{"headsign" => "Bowdoin"},
"relationships" => %{
"stops" => %{
"data" => [
Expand All @@ -81,6 +82,7 @@ defmodule Screens.RoutePatterns.RoutePatternTest do
%{
"id" => "65198154",
"type" => "trip",
"attributes" => %{"headsign" => "Bowdoin"},
"relationships" => %{
"stops" => %{
"data" => [
Expand Down Expand Up @@ -172,6 +174,7 @@ defmodule Screens.RoutePatterns.RoutePatternTest do
direction_id: 0,
typicality: 1,
route: blue_route,
headsign: "Bowdoin",
stops: [wonderland, bowdoin]
},
%RoutePattern{
Expand All @@ -180,6 +183,7 @@ defmodule Screens.RoutePatterns.RoutePatternTest do
direction_id: 0,
typicality: 3,
route: blue_route,
headsign: "Bowdoin",
stops: [orient_heights, bowdoin]
}
]
Expand Down Expand Up @@ -271,11 +275,13 @@ defmodule Screens.RoutePatterns.RoutePatternTest do
%{
"id" => "trip-blue",
"type" => "trip",
"attributes" => %{"headsign" => "Bowdoin"},
"relationships" => %{"stops" => %{"data" => []}}
},
%{
"id" => "trip-bus-1",
"type" => "trip",
"attributes" => %{"headsign" => "Nubian"},
"relationships" => %{"stops" => %{"data" => []}}
}
]
Expand Down

0 comments on commit 68b3470

Please sign in to comment.