Skip to content

Commit

Permalink
refactor: move LocationContext logic out of Stop
Browse files Browse the repository at this point in the history
  • Loading branch information
digitalcora committed Nov 5, 2024
1 parent f04f570 commit b6953a7
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 140 deletions.
93 changes: 0 additions & 93 deletions lib/screens/stops/stop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@ defmodule Screens.Stops.Stop do

require Logger

alias Screens.LocationContext
alias Screens.RoutePatterns.RoutePattern
alias Screens.Routes.Route
alias Screens.RouteType
alias Screens.Stops
alias Screens.Util
alias Screens.V3Api
alias ScreensConfig.V2.{BusEink, BusShelter, Dup, GlEink, PreFare}

defstruct ~w[id name location_type platform_code platform_name]a

Expand All @@ -24,8 +18,6 @@ defmodule Screens.Stops.Stop do
platform_name: String.t() | nil
}

@type screen_type :: BusEink | BusShelter | GlEink | PreFare | Dup

def fetch_parent_station_name_map(get_json_fn \\ &V3Api.get_json/2) do
case get_json_fn.("stops", %{
"filter[location_type]" => 1
Expand Down Expand Up @@ -116,89 +108,4 @@ defmodule Screens.Stops.Stop do
{:error, error}
end
end

@doc """
Fetches all the location context for a screen given its app type, stop id, and time
"""
@spec fetch_location_context(
screen_type(),
id(),
DateTime.t()
) :: {:ok, LocationContext.t()} | :error
def fetch_location_context(app, stop_id, now) do
Screens.Telemetry.span(
~w[screens stops stop fetch_location_context]a,
%{app: app, stop_id: stop_id},
fn ->
with alert_route_types <- get_route_type_filter(app, stop_id),
{:ok, routes_at_stop} <-
Route.serving_stop_with_active(stop_id, now, alert_route_types),
{:ok, tagged_stop_sequences} <-
fetch_tagged_stop_sequences_by_app(app, stop_id, routes_at_stop) do
stop_name = fetch_stop_name(stop_id)
stop_sequences = RoutePattern.untag_stop_sequences(tagged_stop_sequences)

{:ok,
%LocationContext{
home_stop: stop_id,
home_stop_name: stop_name,
tagged_stop_sequences: tagged_stop_sequences,
upstream_stops: upstream_stop_id_set(stop_id, stop_sequences),
downstream_stops: downstream_stop_id_set(stop_id, stop_sequences),
routes: routes_at_stop,
alert_route_types: alert_route_types
}}
else
:error ->
Logger.error(
"[fetch_location_context fetch error] Failed to get location context for an alert: stop_id=#{stop_id}"
)

:error
end
end
)
end

# Returns the route types we care about for the alerts of this screen type / place
@spec get_route_type_filter(screen_type(), String.t()) :: list(RouteType.t())
def get_route_type_filter(app, _) when app in [BusEink, BusShelter], do: [:bus]
def get_route_type_filter(GlEink, _), do: [:light_rail]
# Ashmont should not show Mattapan alerts for PreFare or Dup
def get_route_type_filter(app, "place-asmnl") when app in [PreFare, Dup], do: [:subway]
def get_route_type_filter(PreFare, _), do: [:light_rail, :subway]
# WTC is a special bus-only case
def get_route_type_filter(Dup, "place-wtcst"), do: [:bus]
def get_route_type_filter(Dup, _), do: [:light_rail, :subway]

@spec upstream_stop_id_set(String.t(), list(list(id()))) :: MapSet.t(id())
def upstream_stop_id_set(stop_id, stop_sequences) do
stop_sequences
|> Enum.flat_map(fn stop_sequence -> Util.slice_before(stop_sequence, stop_id) end)
|> MapSet.new()
end

@spec downstream_stop_id_set(String.t(), list(list(id()))) :: MapSet.t(id())
def downstream_stop_id_set(stop_id, stop_sequences) do
stop_sequences
|> Enum.flat_map(fn stop_sequence -> Util.slice_after(stop_sequence, stop_id) end)
|> MapSet.new()
end

defp fetch_tagged_stop_sequences_by_app(app, stop_id, _routes_at_stop)
when app in [BusEink, BusShelter, GlEink] do
RoutePattern.fetch_tagged_stop_sequences_through_stop(stop_id)
end

defp fetch_tagged_stop_sequences_by_app(Dup, stop_id, routes_at_stop) do
route_ids = Route.route_ids(routes_at_stop)
RoutePattern.fetch_tagged_parent_station_sequences_through_stop(stop_id, route_ids)
end

defp fetch_tagged_stop_sequences_by_app(PreFare, stop_id, routes_at_stop) do
route_ids = Route.route_ids(routes_at_stop)

# We limit results to canonical route patterns only--no stop sequences for nonstandard patterns.
RoutePattern.fetch_tagged_parent_station_sequences_through_stop(stop_id, route_ids, true)
end
end
3 changes: 2 additions & 1 deletion lib/screens/telemetry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ defmodule Screens.Telemetry do
log_span(~w[screens v3_api get_json]a, metadata: ~w[url cached]a),
# Stops
log_span(~w[screens stops stop fetch_stop_name]a, metadata: ~w[stop_id]),
log_span(~w[screens stops stop fetch_location_context]a, metadata: ~w[app stop_id]),
# Location Context
log_span(~w[screens location_context fetch]a, metadata: ~w[app stop_id]),
# Alerts
log_span(~w[screens alerts alert fetch]a),
# Routes
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/v2/candidate_generator/dup/alerts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Screens.V2.CandidateGenerator.Dup.Alerts do
now \\ DateTime.utc_now(),
fetch_stop_name_fn \\ &Stop.fetch_stop_name/1,
fetch_alerts_fn \\ &Alert.fetch/1,
fetch_location_context_fn \\ &Stop.fetch_location_context/3
fetch_location_context_fn \\ &LocationContext.fetch/3
) do
# In this function:
# - Fetch relevant alerts for all SUBWAY/LIGHT RAIL routes serving this stop
Expand Down
3 changes: 1 addition & 2 deletions lib/screens/v2/candidate_generator/widgets/alerts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule Screens.V2.CandidateGenerator.Widgets.Alerts do
alias Screens.Alerts.Alert
alias Screens.LocationContext
alias Screens.Routes.Route
alias Screens.Stops.Stop
alias Screens.Util
alias Screens.V2.WidgetInstance.Alert, as: AlertWidget
alias ScreensConfig.Screen
Expand All @@ -21,7 +20,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.Alerts do
%Screen{app_params: %app{alerts: %Alerts{stop_id: stop_id}}} = config,
now \\ DateTime.utc_now(),
fetch_alerts_by_stop_and_route_fn \\ &Alert.fetch_by_stop_and_route/2,
fetch_location_context_fn \\ &Stop.fetch_location_context/3
fetch_location_context_fn \\ &LocationContext.fetch/3
)
when app in @alert_supporting_screen_types do
with {:ok, location_context} <- fetch_location_context_fn.(app, stop_id, now),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ElevatorClosures do
@moduledoc false

alias Screens.Alerts.Alert
alias Screens.LocationContext
alias Screens.Routes.Route
alias Screens.Stops.Stop
alias Screens.V2.WidgetInstance.ElevatorStatus, as: ElevatorStatusWidget
Expand All @@ -15,7 +16,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ElevatorClosures do
}
} = config,
now \\ DateTime.utc_now(),
fetch_location_context_fn \\ &Stop.fetch_location_context/3,
fetch_location_context_fn \\ &LocationContext.fetch/3,
fetch_elevator_alerts_with_facilities_fn \\ &Alert.fetch_elevator_alerts_with_facilities/0
) do
with {:ok, location_context} <- fetch_location_context_fn.(PreFare, parent_station_id, now),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do
@moduledoc false

alias Screens.Alerts.Alert
alias Screens.LocationContext, as: LC
alias Screens.LocationContext
alias Screens.Routes.Route
alias Screens.Stops.Stop
alias Screens.V2.LocalizedAlert
Expand Down Expand Up @@ -53,7 +53,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do
now \\ DateTime.utc_now(),
fetch_alerts_fn \\ &Alert.fetch/1,
fetch_stop_name_fn \\ &Stop.fetch_stop_name/1,
fetch_location_context_fn \\ &Stop.fetch_location_context/3,
fetch_location_context_fn \\ &LocationContext.fetch/3,
fetch_subway_platforms_for_stop_fn \\ &Stop.fetch_subway_platforms_for_stop/1
) do
%PreFare{
Expand All @@ -65,7 +65,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do
route_ids <- Route.route_ids(location_context.routes),
{:ok, alerts} <- fetch_alerts_fn.(route_ids: route_ids) do
relevant_alerts = relevant_alerts(alerts, location_context, now)
is_terminal_station = terminal?(stop_id, LC.stop_sequences(location_context))
is_terminal_station = terminal?(stop_id, LocationContext.stop_sequences(location_context))

immediate_disruptions = get_immediate_disruptions(relevant_alerts, location_context)
downstream_disruptions = get_downstream_disruptions(relevant_alerts, location_context)
Expand Down Expand Up @@ -95,7 +95,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do
find_closest_downstream_alerts(
downstream_disruptions,
stop_id,
LC.stop_sequences(location_context)
LocationContext.stop_sequences(location_context)
)

flex_zone_alerts = downstream_disruptions -- fullscreen_alerts
Expand Down Expand Up @@ -300,7 +300,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do
# If the current station's stop_id is the first or last entry in all stop_sequences,
# it is a terminal station. Delay alerts heading in the direction of the station are not relevant.
defp relevant_direction?(%{alert: alert, location_context: location_context}) do
stop_sequences = LC.stop_sequences(location_context)
stop_sequences = LocationContext.stop_sequences(location_context)
informed_entities = Alert.informed_entities(alert)

direction_id =
Expand Down
90 changes: 90 additions & 0 deletions lib/screens/v2/location_context.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
defmodule Screens.LocationContext do
@moduledoc false

require Logger

alias Screens.RoutePatterns.RoutePattern
alias Screens.Routes.Route
alias Screens.RouteType
alias Screens.Stops.Stop
alias Screens.Util

alias ScreensConfig.V2.{BusEink, BusShelter, Dup, GlEink, PreFare}

@enforce_keys [:home_stop]
defstruct home_stop: "",
Expand All @@ -28,6 +33,74 @@ defmodule Screens.LocationContext do
alert_route_types: list(RouteType.t())
}

@type screen_type :: BusEink | BusShelter | Dup | GlEink | PreFare

@doc """
Fetches all the location context for a screen given its app type, stop id, and time
"""
@spec fetch(screen_type(), Stop.id(), DateTime.t()) :: {:ok, t()} | :error
def fetch(app, stop_id, now) do
Screens.Telemetry.span(
~w[screens location_context fetch]a,
%{app: app, stop_id: stop_id},
fn ->
with alert_route_types <- route_type_filter(app, stop_id),
{:ok, routes_at_stop} <-
Route.serving_stop_with_active(stop_id, now, alert_route_types),
{:ok, tagged_stop_sequences} <-
fetch_tagged_stop_sequences_by_app(app, stop_id, routes_at_stop) do
stop_name = Stop.fetch_stop_name(stop_id)
stop_sequences = RoutePattern.untag_stop_sequences(tagged_stop_sequences)

{
:ok,
%__MODULE__{
home_stop: stop_id,
home_stop_name: stop_name,
tagged_stop_sequences: tagged_stop_sequences,
upstream_stops: upstream_stop_id_set(stop_id, stop_sequences),
downstream_stops: downstream_stop_id_set(stop_id, stop_sequences),
routes: routes_at_stop,
alert_route_types: alert_route_types
}
}
else
:error ->
Logger.error(
"[location_context fetch error] Failed to get location context for an alert: stop_id=#{stop_id}"
)

:error
end
end
)
end

# Returns the route types we care about for the alerts of this screen type / place
@spec route_type_filter(screen_type(), String.t()) :: list(RouteType.t())
def route_type_filter(app, _) when app in [BusEink, BusShelter], do: [:bus]
def route_type_filter(GlEink, _), do: [:light_rail]
# Ashmont should not show Mattapan alerts for PreFare or Dup
def route_type_filter(app, "place-asmnl") when app in [PreFare, Dup], do: [:subway]
def route_type_filter(PreFare, _), do: [:light_rail, :subway]
# WTC is a special bus-only case
def route_type_filter(Dup, "place-wtcst"), do: [:bus]
def route_type_filter(Dup, _), do: [:light_rail, :subway]

@spec upstream_stop_id_set(String.t(), list(list(Stop.id()))) :: MapSet.t(Stop.id())
def upstream_stop_id_set(stop_id, stop_sequences) do
stop_sequences
|> Enum.flat_map(fn stop_sequence -> Util.slice_before(stop_sequence, stop_id) end)
|> MapSet.new()
end

@spec downstream_stop_id_set(String.t(), list(list(Stop.id()))) :: MapSet.t(Stop.id())
def downstream_stop_id_set(stop_id, stop_sequences) do
stop_sequences
|> Enum.flat_map(fn stop_sequence -> Util.slice_after(stop_sequence, stop_id) end)
|> MapSet.new()
end

@doc """
Returns IDs of routes that serve this location.
"""
Expand All @@ -45,4 +118,21 @@ defmodule Screens.LocationContext do
def stop_sequences(%__MODULE__{} = t) do
RoutePattern.untag_stop_sequences(t.tagged_stop_sequences)
end

defp fetch_tagged_stop_sequences_by_app(app, stop_id, _routes_at_stop)
when app in [BusEink, BusShelter, GlEink] do
RoutePattern.fetch_tagged_stop_sequences_through_stop(stop_id)
end

defp fetch_tagged_stop_sequences_by_app(Dup, stop_id, routes_at_stop) do
route_ids = Route.route_ids(routes_at_stop)
RoutePattern.fetch_tagged_parent_station_sequences_through_stop(stop_id, route_ids)
end

defp fetch_tagged_stop_sequences_by_app(PreFare, stop_id, routes_at_stop) do
route_ids = Route.route_ids(routes_at_stop)

# We limit results to canonical route patterns only--no stop sequences for nonstandard patterns.
RoutePattern.fetch_tagged_parent_station_sequences_through_stop(stop_id, route_ids, true)
end
end
7 changes: 3 additions & 4 deletions test/screens/v2/candidate_generator/widgets/alerts_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ defmodule Screens.V2.CandidateGenerator.Widgets.AlertsTest do
alias ScreensConfig.V2.{Alerts, BusShelter, Busway}
alias Screens.LocationContext
alias Screens.RoutePatterns.RoutePattern
alias Screens.Stops.Stop
alias Screens.V2.WidgetInstance.Alert, as: AlertWidget

defp ie(opts \\ []) do
Expand Down Expand Up @@ -70,10 +69,10 @@ defmodule Screens.V2.CandidateGenerator.Widgets.AlertsTest do
location_context = %LocationContext{
home_stop: stop_id,
tagged_stop_sequences: tagged_stop_sequences,
upstream_stops: Stop.upstream_stop_id_set(stop_id, stop_sequences),
downstream_stops: Stop.downstream_stop_id_set(stop_id, stop_sequences),
upstream_stops: LocationContext.upstream_stop_id_set(stop_id, stop_sequences),
downstream_stops: LocationContext.downstream_stop_id_set(stop_id, stop_sequences),
routes: routes_at_stop,
alert_route_types: Stop.get_route_type_filter(app, stop_id)
alert_route_types: LocationContext.route_type_filter(app, stop_id)
}

%{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do
alias ScreensConfig.V2.{Busway, PreFare}
alias Screens.LocationContext
alias Screens.RoutePatterns.RoutePattern
alias Screens.Stops.Stop
alias Screens.V2.WidgetInstance.ReconstructedAlert, as: ReconstructedAlertWidget

defp ie(opts) do
Expand Down Expand Up @@ -109,10 +108,10 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do
location_context = %LocationContext{
home_stop: stop_id,
tagged_stop_sequences: tagged_stop_sequences,
upstream_stops: Stop.upstream_stop_id_set(stop_id, stop_sequences),
downstream_stops: Stop.downstream_stop_id_set(stop_id, stop_sequences),
upstream_stops: LocationContext.upstream_stop_id_set(stop_id, stop_sequences),
downstream_stops: LocationContext.downstream_stop_id_set(stop_id, stop_sequences),
routes: routes_at_stop,
alert_route_types: Stop.get_route_type_filter(app, stop_id)
alert_route_types: LocationContext.route_type_filter(app, stop_id)
}

%{
Expand Down
7 changes: 3 additions & 4 deletions test/screens/v2/localized_alert_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ defmodule Screens.V2.LocalizedAlertTest do
alias Screens.LocationContext
alias Screens.RoutePatterns.RoutePattern
alias Screens.RouteType
alias Screens.Stops.Stop
alias Screens.V2.LocalizedAlert, as: LocalizedAlert
alias Screens.V2.WidgetInstance.Alert, as: AlertWidget

Expand Down Expand Up @@ -35,7 +34,7 @@ defmodule Screens.V2.LocalizedAlertTest do
widget
| location_context: %{
widget.location_context
| alert_route_types: Stop.get_route_type_filter(app_config_module, stop_id),
| alert_route_types: LocationContext.route_type_filter(app_config_module, stop_id),
home_stop: stop_id
}
}
Expand All @@ -54,9 +53,9 @@ defmodule Screens.V2.LocalizedAlertTest do
widget.location_context
| tagged_stop_sequences: tagged_sequences,
upstream_stops:
Stop.upstream_stop_id_set(widget.location_context.home_stop, sequences),
LocationContext.upstream_stop_id_set(widget.location_context.home_stop, sequences),
downstream_stops:
Stop.downstream_stop_id_set(widget.location_context.home_stop, sequences)
LocationContext.downstream_stop_id_set(widget.location_context.home_stop, sequences)
}
}
end
Expand Down
Loading

0 comments on commit b6953a7

Please sign in to comment.