From ddfe9ab4b4a0dc793d13d2e37f516f4c4e2c3fe4 Mon Sep 17 00:00:00 2001 From: Christian Maddox Date: Thu, 18 Jul 2024 14:47:42 -0400 Subject: [PATCH] fix: Handle platform closures (#2107) * Added field to struct. * Added fetch for all platform IDs at a station. * Added edge cases so widgets can handle single platform closures. * Added PreFare alert tests. * Added SubwayStatus tests. * Credo. * Fixed check so function can be used by WidgetInstances. * Fixed platform closure logic for SubwayStatus. * Name changes. * Refactor to pattern match Co-authored-by: sloane <1699281+sloanelybutsurely@users.noreply.github.com> * Changed reject to filter. * Formatting. * Fixed route selection for station closures at JFK. * Added cldr_messages to help with pluralizing strings. * Pluralized strings. * Added more tests. * Fixed platform names. * Changed how we determine if ie is for a subway platform. * Fixed tests. * Added platform support for GL. * Use existing function. * No single pipes. * Exclude partial closures from dual_screen_alert?. * Added typespecs. * Added a struct to describe alerts WidgetInstance.SubwayStatus expects. * Added provider so we don't see warnings. * Moved partial closure logic to CG. * Added a comment. * Added better type. * Fixed default. --------- Co-authored-by: sloane <1699281+sloanelybutsurely@users.noreply.github.com> --- config/config.exs | 5 + lib/screens/alerts/alert.ex | 29 + lib/screens/cldr.ex | 13 + lib/screens/stops/parser.ex | 9 +- lib/screens/stops/stop.ex | 24 +- .../widgets/reconstructed_alert.ex | 31 +- .../widgets/subway_status.ex | 38 +- .../v2/widget_instance/reconstructed_alert.ex | 118 ++- .../v2/widget_instance/subway_status.ex | 100 +- mix.exs | 3 +- mix.lock | 8 + .../widgets/reconstructed_alert_test.exs | 32 +- .../reconstructed_alert_test.exs | 186 +++- .../v2/widget_instance/subway_status_test.exs | 949 ++++++++++-------- 14 files changed, 1073 insertions(+), 472 deletions(-) create mode 100644 lib/screens/cldr.ex diff --git a/config/config.exs b/config/config.exs index 1dc4b7d27..23bc7ab57 100644 --- a/config/config.exs +++ b/config/config.exs @@ -56,6 +56,11 @@ config :ueberauth, Ueberauth, keycloak: nil ] +config :ex_cldr, + default_locale: "en", + default_backend: Screens.Cldr, + json_library: Jason + config :screens, gds_dms_username: "mbtadata@gmail.com", config_fetcher: Screens.Config.Fetch.S3, diff --git a/lib/screens/alerts/alert.ex b/lib/screens/alerts/alert.ex index b14b05879..6480905ea 100644 --- a/lib/screens/alerts/alert.ex +++ b/lib/screens/alerts/alert.ex @@ -1,6 +1,7 @@ defmodule Screens.Alerts.Alert do @moduledoc false + alias Screens.Alerts.InformedEntity alias Screens.Routes.Route alias Screens.RouteType alias Screens.Stops.Stop @@ -592,4 +593,32 @@ defmodule Screens.Alerts.Alert do def direction_id(%__MODULE__{informed_entities: informed_entities}), do: List.first(informed_entities).direction_id + + def informed_parent_stations(%__MODULE__{ + informed_entities: informed_entities + }) do + Enum.filter(informed_entities, &InformedEntity.parent_station?/1) + end + + # Although Alerts UI allows you to create partial closures affecting multiple stations, + # we are assuming that will never happen. + @spec is_partial_station_closure?(__MODULE__.t(), list(Stop.t())) :: boolean() + def is_partial_station_closure?( + %__MODULE__{effect: :station_closure, informed_entities: informed_entities} = alert, + all_platforms_at_informed_station + ) do + informed_parent_stations = informed_parent_stations(alert) + + case informed_parent_stations do + [_] -> + platform_ids = Enum.map(all_platforms_at_informed_station, & &1.id) + informed_platforms = Enum.filter(informed_entities, &(&1.stop in platform_ids)) + length(informed_platforms) != length(all_platforms_at_informed_station) + + _ -> + false + end + end + + def is_partial_station_closure?(_, _), do: false end diff --git a/lib/screens/cldr.ex b/lib/screens/cldr.ex new file mode 100644 index 000000000..de942548a --- /dev/null +++ b/lib/screens/cldr.ex @@ -0,0 +1,13 @@ +defmodule Screens.Cldr do + @moduledoc """ + Define a backend module that will host our + Cldr configuration and public API. + + Most function calls in Cldr will be calls + to functions on this module. + """ + use Cldr, + locales: ["en"], + default_locale: "en", + providers: [Cldr.Number] +end diff --git a/lib/screens/stops/parser.ex b/lib/screens/stops/parser.ex index 00bec837d..0821ce17a 100644 --- a/lib/screens/stops/parser.ex +++ b/lib/screens/stops/parser.ex @@ -3,12 +3,17 @@ defmodule Screens.Stops.Parser do def parse_stop(%{ "id" => id, - "attributes" => %{"name" => name, "platform_code" => platform_code} + "attributes" => %{ + "name" => name, + "platform_code" => platform_code, + "platform_name" => platform_name + } }) do %Screens.Stops.Stop{ id: id, name: name, - platform_code: platform_code + platform_code: platform_code, + platform_name: platform_name } end end diff --git a/lib/screens/stops/stop.ex b/lib/screens/stops/stop.ex index bda4d6b9e..75bb2faea 100644 --- a/lib/screens/stops/stop.ex +++ b/lib/screens/stops/stop.ex @@ -12,7 +12,7 @@ defmodule Screens.Stops.Stop do alias Screens.LocationContext alias Screens.RoutePatterns.RoutePattern - alias Screens.Routes + alias Screens.{Routes, Stops} alias Screens.Routes.Route alias Screens.RouteType alias Screens.Stops.StationsWithRoutesAgent @@ -22,14 +22,16 @@ defmodule Screens.Stops.Stop do defstruct id: nil, name: nil, - platform_code: nil + platform_code: nil, + platform_name: nil @type id :: String.t() @type t :: %__MODULE__{ id: id, name: String.t(), - platform_code: String.t() | nil + platform_code: String.t() | nil, + platform_name: String.t() | nil } @type screen_type :: BusEink | BusShelter | GlEink | PreFare | Dup | Triptych @@ -361,6 +363,22 @@ defmodule Screens.Stops.Stop do end end + def fetch_subway_platforms_for_stop(stop_id) do + case Screens.V3Api.get_json("stops/" <> stop_id, %{"include" => "child_stops"}) do + {:ok, %{"included" => child_stop_data}} -> + child_stop_data + |> Enum.filter(fn %{ + "attributes" => %{ + "location_type" => location_type, + "vehicle_type" => vehicle_type + } + } -> + location_type == 0 and vehicle_type in [0, 1] + end) + |> Enum.map(&Stops.Parser.parse_stop/1) + end + end + # --- END API functions --- def stop_on_route?(stop_id, stop_sequence) when not is_nil(stop_id) do diff --git a/lib/screens/v2/candidate_generator/widgets/reconstructed_alert.ex b/lib/screens/v2/candidate_generator/widgets/reconstructed_alert.ex index ff4382d0f..6f78d94ca 100644 --- a/lib/screens/v2/candidate_generator/widgets/reconstructed_alert.ex +++ b/lib/screens/v2/candidate_generator/widgets/reconstructed_alert.ex @@ -53,7 +53,8 @@ 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 \\ &Stop.fetch_location_context/3, + fetch_subway_platforms_for_stop_fn \\ &Stop.fetch_subway_platforms_for_stop/1 ) do %PreFare{ reconstructed_alert_widget: %CurrentStopId{stop_id: stop_id} @@ -75,7 +76,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do location_context: location_context, fetch_stop_name_fn: fetch_stop_name_fn, is_terminal_station: is_terminal_station, - now: now + now: now, + fetch_subway_platforms_for_stop_fn: fetch_subway_platforms_for_stop_fn ] cond do @@ -157,9 +159,13 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do location_context: location_context, fetch_stop_name_fn: fetch_stop_name_fn, is_terminal_station: is_terminal_station, - now: now + now: now, + fetch_subway_platforms_for_stop_fn: fetch_subway_platforms_for_stop_fn ) do Enum.map(alerts, fn alert -> + all_platforms_names_at_informed_station = + get_platform_names_at_informed_station(alert, fetch_subway_platforms_for_stop_fn) + %ReconstructedAlert{ screen: config, alert: alert, @@ -167,11 +173,28 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlert do location_context: location_context, informed_stations: get_stations(alert, fetch_stop_name_fn), is_terminal_station: is_terminal_station, - is_full_screen: is_full_screen + is_full_screen: is_full_screen, + partial_closure_platform_names: all_platforms_names_at_informed_station } end) end + defp get_platform_names_at_informed_station( + %Alert{effect: :station_closure, informed_entities: informed_entities} = alert, + fetch_subway_platforms_for_stop_fn + ) do + with [informed_parent_station] <- Alert.informed_parent_stations(alert), + platforms <- fetch_subway_platforms_for_stop_fn.(informed_parent_station.stop), + true <- Alert.is_partial_station_closure?(alert, platforms) do + informed_stop_ids = Enum.map(informed_entities, & &1.stop) + platforms |> Enum.filter(&(&1.id in informed_stop_ids)) |> Enum.map(& &1.platform_name) + else + _ -> [] + end + end + + defp get_platform_names_at_informed_station(_, _), do: [] + defp find_closest_downstream_alerts(alerts, stop_id, stop_sequences) do home_stop_distance_map = build_distance_map(stop_id, stop_sequences) # Map each alert with its distance from home. diff --git a/lib/screens/v2/candidate_generator/widgets/subway_status.ex b/lib/screens/v2/candidate_generator/widgets/subway_status.ex index e703c99d9..cafadea45 100644 --- a/lib/screens/v2/candidate_generator/widgets/subway_status.ex +++ b/lib/screens/v2/candidate_generator/widgets/subway_status.ex @@ -2,14 +2,23 @@ defmodule Screens.V2.CandidateGenerator.Widgets.SubwayStatus do @moduledoc false alias Screens.Alerts.Alert + alias Screens.Stops.Stop alias Screens.V2.WidgetInstance.SubwayStatus - def subway_status_instances(config, now \\ DateTime.utc_now()) do + def subway_status_instances( + config, + now \\ DateTime.utc_now(), + fetch_subway_platforms_for_stop_fn \\ &Stop.fetch_subway_platforms_for_stop/1 + ) do route_ids = ["Blue", "Orange", "Red", "Green-B", "Green-C", "Green-D", "Green-E"] case Screens.Alerts.Alert.fetch(route_ids: route_ids) do {:ok, alerts} -> - relevant_alerts = Enum.filter(alerts, &relevant?(&1, now)) + relevant_alerts = + alerts + |> Enum.filter(&relevant?(&1, now)) + |> Enum.map(&append_context(&1, fetch_subway_platforms_for_stop_fn)) + [%SubwayStatus{screen: config, subway_alerts: relevant_alerts}] :error -> @@ -27,5 +36,28 @@ defmodule Screens.V2.CandidateGenerator.Widgets.SubwayStatus do defp relevant_effect?(%Alert{effect: effect}), do: effect in [:suspension, :shuttle, :station_closure] - defp suppressed?(alert), do: alert.id == "529291" + defp suppressed?(_alert), do: false + + defp append_context( + %Alert{effect: :station_closure} = alert, + fetch_subway_platforms_for_stop_fn + ) do + informed_parent_stations = Alert.informed_parent_stations(alert) + + all_platforms_at_informed_station = + case informed_parent_stations do + [informed_parent_station] -> + fetch_subway_platforms_for_stop_fn.(informed_parent_station.stop) + + _ -> + [] + end + + %SubwayStatus.SubwayStatusAlert{ + alert: alert, + context: %{all_platforms_at_informed_station: all_platforms_at_informed_station} + } + end + + defp append_context(alert, _), do: struct(SubwayStatus.SubwayStatusAlert, alert: alert) end diff --git a/lib/screens/v2/widget_instance/reconstructed_alert.ex b/lib/screens/v2/widget_instance/reconstructed_alert.ex index 444aeb61e..6ec4a9657 100644 --- a/lib/screens/v2/widget_instance/reconstructed_alert.ex +++ b/lib/screens/v2/widget_instance/reconstructed_alert.ex @@ -23,7 +23,8 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do informed_stations: nil, is_terminal_station: false, # Full screen alert, whether that's a single or dual screen alert - is_full_screen: false + is_full_screen: false, + partial_closure_platform_names: [] @type stop_id :: String.t() @@ -36,7 +37,8 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do location_context: LocationContext.t(), informed_stations: list(String.t()), is_terminal_station: boolean(), - is_full_screen: boolean() + is_full_screen: boolean(), + partial_closure_platform_names: list(String.t()) } @type serialized_response :: @@ -143,8 +145,8 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do # and their affiliated route id list_of_directions_and_routes = informed_entities - |> Enum.map(fn entity -> get_direction_and_route_from_entity(entity, location) end) - |> Enum.reject(&is_nil/1) + |> Enum.map(&get_direction_and_route_from_entity(&1, alert.effect, location)) + |> Enum.filter(& &1) |> Enum.uniq() {direction_id, route_id} = select_direction_and_route(list_of_directions_and_routes) @@ -170,14 +172,19 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do # Given an entity and the directionality of the alert from the home stop, # return a tuple with the affected direction_id and route_id + defp get_direction_and_route_from_entity(%{stop: "place-jfk"}, :station_closure, _location), + do: {nil, "Red"} + # Skip processing JFK, because it is a branching node station. The other stations in the alert # will determine the destination needed for this alert - defp get_direction_and_route_from_entity(%{stop: "place-jfk"}, _location), do: nil + defp get_direction_and_route_from_entity(%{stop: "place-jfk"}, _, _location), + do: nil # If the route is red and the alert is downstream, we have to figure out whether the alert # only affects one branch or both defp get_direction_and_route_from_entity( %{direction_id: nil, route: "Red", stop: stop_id}, + _, location ) when stop_id != nil and location in [:downstream, :boundary_downstream] do @@ -196,6 +203,7 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do # Same with RL upstream alerts defp get_direction_and_route_from_entity( %{direction_id: nil, route: "Red", stop: stop_id}, + _, location ) when stop_id != nil and location in [:upstream, :boundary_upstream] do @@ -211,15 +219,15 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do end end - defp get_direction_and_route_from_entity(%{direction_id: nil, route: route}, location) + defp get_direction_and_route_from_entity(%{direction_id: nil, route: route}, _, location) when location in [:downstream, :boundary_downstream], do: {0, route} - defp get_direction_and_route_from_entity(%{direction_id: nil, route: route}, location) + defp get_direction_and_route_from_entity(%{direction_id: nil, route: route}, _, location) when location in [:upstream, :boundary_upstream], do: {1, route} - defp get_direction_and_route_from_entity(%{direction_id: direction_id, route: route}, _), + defp get_direction_and_route_from_entity(%{direction_id: direction_id, route: route}, _, _), do: {direction_id, route} # Select 1 direction + route from this list of directions + routes for multiple branches @@ -373,7 +381,19 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do }), do: false - def dual_screen_alert?(%__MODULE__{is_terminal_station: is_terminal_station, alert: alert} = t) do + def dual_screen_alert?(%__MODULE__{ + alert: %Alert{effect: :station_closure}, + partial_closure_platform_names: partial_closure_platform_names + }) + when partial_closure_platform_names != [], + do: false + + def dual_screen_alert?( + %__MODULE__{ + is_terminal_station: is_terminal_station, + alert: alert + } = t + ) do Alert.effect(alert) in [:station_closure, :suspension, :shuttle] and LocalizedAlert.location(t, is_terminal_station) == :inside and LocalizedAlert.informs_all_active_routes_at_home_stop?(t) and @@ -1012,8 +1032,45 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do end end + # Partial closure defp serialize_outside_alert( - %__MODULE__{alert: %Alert{effect: :station_closure}} = t, + %__MODULE__{ + informed_stations: [informed_station], + partial_closure_platform_names: partial_closure_platform_names + } = t, + _location + ) + when partial_closure_platform_names != [] do + issue = + case partial_closure_platform_names do + [informed_platform_name] -> + "Bypassing #{informed_platform_name} platform at #{informed_station}" + + informed_subway_platforms -> + Cldr.Message.format!("Bypassing {num_platforms, plural, + =1 {1 platform} + other {# platforms}} at {informed_station}", + num_platforms: length(informed_subway_platforms), + informed_station: informed_station + ) + end + + %{ + issue: issue, + remedy: nil, + location: "", + cause: nil, + routes: get_route_pills(t), + effect: :station_closure, + urgent: false + } + end + + # Full closure + defp serialize_outside_alert( + %__MODULE__{ + alert: %Alert{effect: :station_closure} + } = t, _location ) do %{alert: %{cause: cause}, informed_stations: informed_stations} = t @@ -1153,11 +1210,29 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do def serialize(widget, log_fn \\ &Logger.warning/1) - def serialize(%__MODULE__{is_full_screen: true, alert: %Alert{effect: effect}} = t, log_fn) do - diagram_data = serialize_diagram(t, log_fn) - - main_data = pick_layout_serializer(t, diagram_data, effect, dual_screen_alert?(t)) + def serialize( + %__MODULE__{ + is_full_screen: true, + alert: %Alert{effect: :station_closure}, + partial_closure_platform_names: partial_closure_platform_names + } = t, + _log_fn + ) + when partial_closure_platform_names != [] do + location = LocalizedAlert.location(t) + serialize_single_screen_fallback_alert(t, location) + end + def serialize( + %__MODULE__{ + is_full_screen: true, + alert: %Alert{effect: effect} + } = t, + log_fn + ) do + location = LocalizedAlert.location(t) + diagram_data = serialize_diagram(t, log_fn) + main_data = pick_layout_serializer(t, diagram_data, effect, location, dual_screen_alert?(t)) Map.merge(main_data, diagram_data) end @@ -1190,19 +1265,20 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlert do end end - def pick_layout_serializer(t, diagram, effect, true) when diagram == %{} and effect != :delay, - do: serialize_dual_screen_fallback_alert(t) + def pick_layout_serializer(t, diagram, effect, location, is_dual_screen_alert) + + def pick_layout_serializer(t, diagram, effect, _, true) + when diagram == %{} and effect != :delay, + do: serialize_dual_screen_fallback_alert(t) - def pick_layout_serializer(t, diagram, effect, false) + def pick_layout_serializer(t, diagram, effect, location, false) when diagram == %{} and effect != :delay do - location = LocalizedAlert.location(t) serialize_single_screen_fallback_alert(t, location) end - def pick_layout_serializer(t, _, _, true), do: serialize_dual_screen_alert(t) + def pick_layout_serializer(t, _, _, _, true), do: serialize_dual_screen_alert(t) - def pick_layout_serializer(t, _, _, _) do - location = LocalizedAlert.location(t) + def pick_layout_serializer(t, _, _, location, _) do serialize_single_screen_alert(t, location) end diff --git a/lib/screens/v2/widget_instance/subway_status.ex b/lib/screens/v2/widget_instance/subway_status.ex index f78e76487..fe2d3aaed 100644 --- a/lib/screens/v2/widget_instance/subway_status.ex +++ b/lib/screens/v2/widget_instance/subway_status.ex @@ -1,19 +1,38 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do - @moduledoc false + @moduledoc """ + A flex-zone widget that displays a brief status of each subway line. + """ alias Screens.Alerts.Alert alias Screens.Alerts.InformedEntity + alias Screens.Routes.Route alias Screens.Stops.Stop alias Screens.V2.WidgetInstance.SubwayStatus alias ScreensConfig.Screen alias ScreensConfig.V2.{Footer, GlEink, PreFare} + defmodule SubwayStatusAlert do + @moduledoc false + + @type t :: %__MODULE__{ + alert: Alert.t(), + context: context() + } + + @enforce_keys [:alert] + defstruct @enforce_keys ++ [context: %{}] + + @type context :: %{ + optional(:all_platforms_at_informed_station) => list(String.t()) + } + end + defstruct screen: nil, subway_alerts: nil @type t :: %__MODULE__{ screen: Screen.t(), - subway_alerts: list(Alert.t()) + subway_alerts: list(SubwayStatusAlert.t()) } @type serialized_response :: %{ @@ -153,7 +172,7 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do ) end - defp alert_routes(%Alert{informed_entities: entities}) do + defp alert_routes(%{alert: %Alert{informed_entities: entities}}) do entities |> Enum.map(fn e -> Map.get(e, :route) end) |> Enum.reject(&is_nil/1) @@ -360,16 +379,22 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do Map.merge(%{route_pill: serialize_route_pill(route_id)}, serialize_alert(alert, route_id)) end + @spec serialize_alert(Alert.t() | nil, Route.id()) :: alert() + defp serialize_alert(alert, route_id) + defp serialize_alert(nil, _route_id) do %{status: "Normal Service"} end - defp serialize_alert(%Alert{effect: :shuttle, informed_entities: informed_entities}, route_id) do + defp serialize_alert( + %{alert: %Alert{effect: :shuttle, informed_entities: informed_entities}}, + route_id + ) do %{status: "Shuttle Bus", location: get_location(informed_entities, route_id)} end defp serialize_alert( - %Alert{effect: :suspension, informed_entities: informed_entities}, + %{alert: %Alert{effect: :suspension, informed_entities: informed_entities}}, route_id ) do location = get_location(informed_entities, route_id) @@ -380,22 +405,42 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do end defp serialize_alert( - %Alert{effect: :station_closure, informed_entities: informed_entities}, + %{ + alert: %Alert{effect: :station_closure, informed_entities: informed_entities} = alert, + context: %{all_platforms_at_informed_station: all_platforms_at_informed_station} + }, route_id ) do - # Get closed station names from informed entities - stop_names = get_stop_names_from_informed_entities(informed_entities, route_id) + if Alert.is_partial_station_closure?(alert, all_platforms_at_informed_station) do + platform_ids = Enum.map(all_platforms_at_informed_station, & &1.id) + informed_platforms = Enum.filter(informed_entities, &(&1.stop in platform_ids)) - {status, location} = format_station_closure(stop_names) + %{ + status: + Cldr.Message.format!("Bypassing {num_informed_platforms, plural, + =1 {1 stop} + other {# stops}}", + num_informed_platforms: length(informed_platforms) + ), + location: %{full: "mbta.com/alerts", abbrev: "mbta.com/alerts"} + } + else + # Get closed station names from informed entities + stop_names = get_stop_names_from_informed_entities(informed_entities, route_id) - %{status: status, location: location, station_count: length(stop_names)} + {status, location} = format_station_closure(stop_names) + + %{status: status, location: location, station_count: length(stop_names)} + end end defp serialize_alert( - %Alert{ - effect: :delay, - severity: severity, - informed_entities: informed_entities + %{ + alert: %Alert{ + effect: :delay, + severity: severity, + informed_entities: informed_entities + } }, route_id ) do @@ -420,13 +465,19 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do } end - def serialize_green_line_branch_alert( - %Alert{ - effect: :station_closure, - informed_entities: informed_entities - }, - route_ids - ) do + @spec serialize_green_line_branch_alert(%{alert: Alert.t(), context: %{}}, list(String.t())) :: + alert() + defp serialize_green_line_branch_alert(alert, route_ids) + + defp serialize_green_line_branch_alert( + %{ + alert: %Alert{ + effect: :station_closure, + informed_entities: informed_entities + } + }, + route_ids + ) do stop_names = Enum.flat_map(route_ids, &get_stop_names_from_informed_entities(informed_entities, &1)) @@ -442,7 +493,7 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do # If only one branch is affected, we can still determine a stop # range to show, for applicable alert types - def serialize_green_line_branch_alert(alert, [route_id]) do + defp serialize_green_line_branch_alert(alert, [route_id]) do Map.merge( %{route_pill: serialize_gl_pill_with_branches([route_id])}, serialize_alert(alert, route_id) @@ -450,7 +501,7 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do end # Otherwise, give up on determining a stop range. - def serialize_green_line_branch_alert(alert, route_ids) do + defp serialize_green_line_branch_alert(alert, route_ids) do Map.merge( %{route_pill: serialize_gl_pill_with_branches(route_ids)}, serialize_alert(alert, "Green") @@ -506,7 +557,7 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do end end - defp alert_affects_gl_trunk_or_whole_line?(alert, gl_stop_sets) do + defp alert_affects_gl_trunk_or_whole_line?(%{alert: alert}, gl_stop_sets) do alert_affects_gl_trunk?(alert, gl_stop_sets) or alert_affects_whole_green_line?(alert) end @@ -724,6 +775,7 @@ defmodule Screens.V2.WidgetInstance.SubwayStatus do def get_total_alerts(alerts) do total_affected_routes = alerts + |> Enum.map(& &1.alert) |> Enum.uniq_by(& &1.id) |> Enum.map(&get_total_affected_routes_for_alert/1) |> Enum.sum() diff --git a/mix.exs b/mix.exs index cd9b34c68..9d2073047 100644 --- a/mix.exs +++ b/mix.exs @@ -89,7 +89,8 @@ defmodule Screens.MixProject do ref: "25fb47c58fc0b485c8c6df78fe94914292856903"}, {:nebulex, "~> 2.6"}, {:remote_ip, "~> 1.2"}, - {:hackney_telemetry, "~> 0.2.0"} + {:hackney_telemetry, "~> 0.2.0"}, + {:ex_cldr_messages, "~> 1.0"} ] end end diff --git a/mix.lock b/mix.lock index 964ef8d96..ff4fae5a7 100644 --- a/mix.lock +++ b/mix.lock @@ -3,6 +3,7 @@ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, + "cldr_utils": {:hex, :cldr_utils, "2.27.0", "a75d5cdaaf6b7432eb10f547e6abe635c94746985c5b78e35bbbd08b16473b6c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "516f601e28da10b8f1f3af565321c4e3da3b898a0b50a5e5be425eff76d587e1"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "corsica": {:hex, :corsica, "2.1.3", "dccd094ffce38178acead9ae743180cdaffa388f35f0461ba1e8151d32e190e6", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "616c08f61a345780c2cf662ff226816f04d8868e12054e68963e95285b5be8bc"}, @@ -10,13 +11,19 @@ "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"}, + "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, + "digital_token": {:hex, :digital_token, "0.6.0", "13e6de581f0b1f6c686f7c7d12ab11a84a7b22fa79adeb4b50eec1a2d278d258", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2455d626e7c61a128b02a4a8caddb092548c3eb613ac6f6a85e4cbb6caddc4d1"}, "ehmon": {:git, "https://github.com/mbta/ehmon.git", "1fb603262bd02d74a16183bd8f344dcace9d7561", []}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_aws": {:hex, :ex_aws, "2.5.4", "86c5bb870a49e0ab6f5aa5dd58cf505f09d2624ebe17530db3c1b61c88a673af", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e82bd0091bb9a5bb190139599f922ff3fc7aebcca4374d65c99c4e23aa6d1625"}, "ex_aws_polly": {:hex, :ex_aws_polly, "0.5.0", "277662ce3f4203eef352ae0ea37df1feb54c291d8717cc6c241575e25c861c4c", [:mix], [{:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: false]}], "hexpm", "a021192995409c103b927c75d1d1d2e892f2bb23a0c0063bc85355907b600ca3"}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.5.3", "422468e5c3e1a4da5298e66c3468b465cfd354b842e512cb1f6fbbe4e2f5bdaf", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "4f09dd372cc386550e484808c5ac5027766c8d0cd8271ccc578b82ee6ef4f3b8"}, "ex_aws_secretsmanager": {:hex, :ex_aws_secretsmanager, "2.0.0", "deff8c12335f0160882afeb9687e55a97fddcd7d9a82fc3a6fbb270797374773", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}], "hexpm", "8b2838af536c32263ff797012b29e87bad73ef34f43cfa60ebca8e84576f6d45"}, + "ex_cldr": {:hex, :ex_cldr, "2.39.2", "4a3a77797da8f900369822ea9353adfa035a5bbbbfff09b2d3d1b6fa461768e3", [:mix], [{:cldr_utils, "~> 2.25", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "02fd8913ef28d1b2a4190fd8016c2dec1f2291c9ce56c17d7649848c0261a6eb"}, + "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.1", "29317f533cb5ec046d04523256cca4090291e9157028f28731395149b06ff8b2", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "095d5e973bf0ee066dd1153990d10cb6fa6d8ff0e028295bdce7a7821c70a0e4"}, + "ex_cldr_messages": {:hex, :ex_cldr_messages, "1.0.2", "9909829e8cdb4eeb6d5b4dbe76b8e07ae39d2d2254fb943ff74dd9ace55a9120", [:mix], [{:ex_cldr_dates_times, "~> 2.13", [hex: :ex_cldr_dates_times, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.28", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.12", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.20", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:ex_money, "~> 5.9", [hex: :ex_money, repo: "hexpm", optional: true]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "3805a92f5ff64ee951f3a0174a85221af0117276ac794638df25b845edf431fa"}, + "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.1", "49dc6e77e6d9ad22660aaa2480a7408ad3aedfbe517e4e83e5fe3a7bf5345770", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:digital_token, "~> 0.3 or ~> 1.0", [hex: :digital_token, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "c003bfaa3fdee6bab5195f128b94038c2ce1cf4879a759eef431dd075d9a5dac"}, "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "gettext": {:hex, :gettext, "0.22.3", "c8273e78db4a0bb6fba7e9f0fd881112f349a3117f7f7c598fa18c66c888e524", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "935f23447713954a6866f1bb28c3a878c4c011e802bcd68a726f5e558e4b64bd"}, @@ -36,6 +43,7 @@ "nebulex": {:hex, :nebulex, "2.6.2", "0874989db4e382362884662d2ee9f31b4c4862595f4ec300bd279068729dd2d0", [:mix], [{:decorator, "~> 1.4", [hex: :decorator, repo: "hexpm", optional: true]}, {:shards, "~> 1.1", [hex: :shards, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "002a1774d5a187eb631ae4006db13df4bb6b325fe2a3c14cb14a1f3e989042b4"}, "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "oidcc": {:hex, :oidcc, "3.1.2", "54dd00ebfb3f92aaa410535a725058e43205f01cf7a07b500163d714426300c2", [:mix, :rebar3], [{:jose, "~> 1.11", [hex: :jose, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.3.1", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "50b8d8e2c1c3ef7f593a4d465c2fc64302c01c940d4a3d7f588ad5260190bedc"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"}, diff --git a/test/screens/v2/candidate_generator/widgets/reconstructed_alert_test.exs b/test/screens/v2/candidate_generator/widgets/reconstructed_alert_test.exs index dbf083a05..df78c7f14 100644 --- a/test/screens/v2/candidate_generator/widgets/reconstructed_alert_test.exs +++ b/test/screens/v2/candidate_generator/widgets/reconstructed_alert_test.exs @@ -125,6 +125,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do fetch_directional_alerts_fn: fn _ -> {:ok, directional_alerts} end, fetch_stop_name_fn: fetch_stop_name_fn, fetch_location_context_fn: fn _, _, _ -> {:ok, location_context} end, + fetch_subway_platforms_for_stop_fn: fn _ -> [] end, x_fetch_alerts_fn: fn _ -> :error end, x_fetch_stop_name_fn: fn _ -> :error end, x_fetch_location_context_fn: fn _, _, _ -> :error end @@ -138,7 +139,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do now: now, happening_now_active_period: happening_now_active_period, fetch_stop_name_fn: fetch_stop_name_fn, - fetch_location_context_fn: fetch_location_context_fn + fetch_location_context_fn: fetch_location_context_fn, + fetch_subway_platforms_for_stop_fn: fetch_subway_platforms_for_stop_fn } = context alerts = [ @@ -168,7 +170,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do screen: config, location_context: location_context, now: now, - is_terminal_station: true + is_terminal_station: true, + all_platforms_at_informed_station: [] } expected_widgets = [ @@ -218,7 +221,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do now, fetch_alerts_fn, fetch_stop_name_fn, - fetch_location_context_fn + fetch_location_context_fn, + fetch_subway_platforms_for_stop_fn ) end @@ -230,7 +234,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do now: now, happening_now_active_period: happening_now_active_period, fetch_stop_name_fn: fetch_stop_name_fn, - fetch_location_context_fn: fetch_location_context_fn + fetch_location_context_fn: fetch_location_context_fn, + fetch_subway_platforms_for_stop_fn: fetch_subway_platforms_for_stop_fn } = context alerts = [ @@ -260,7 +265,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do screen: config, location_context: location_context, now: now, - is_terminal_station: true + is_terminal_station: true, + all_platforms_at_informed_station: [] } expected_widgets = [ @@ -310,7 +316,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do now, fetch_alerts_fn, fetch_stop_name_fn, - fetch_location_context_fn + fetch_location_context_fn, + fetch_subway_platforms_for_stop_fn ) end @@ -341,7 +348,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do screen: config, location_context: location_context, now: now, - is_terminal_station: true + is_terminal_station: true, + all_platforms_at_informed_station: [] } expected_widgets = [ @@ -428,7 +436,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do now: now, fetch_location_context_fn: fetch_location_context_fn, fetch_alerts_fn: fetch_alerts_fn, - x_fetch_stop_name_fn: x_fetch_stop_name_fn + x_fetch_stop_name_fn: x_fetch_stop_name_fn, + fetch_subway_platforms_for_stop_fn: fetch_subway_platforms_for_stop_fn } = context expected_common_data = %{ @@ -436,6 +445,7 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do location_context: location_context, now: now, informed_stations: [], + all_platforms_at_informed_station: [], is_terminal_station: true } @@ -483,7 +493,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do now, fetch_alerts_fn, x_fetch_stop_name_fn, - fetch_location_context_fn + fetch_location_context_fn, + fetch_subway_platforms_for_stop_fn ) end @@ -502,7 +513,8 @@ defmodule Screens.V2.CandidateGenerator.Widgets.ReconstructedAlertTest do location_context: location_context, now: now, is_terminal_station: true, - is_full_screen: true + is_full_screen: true, + all_platforms_at_informed_station: [] } expected_widgets = [ diff --git a/test/screens/v2/widget_instance/reconstructed_alert_test.exs b/test/screens/v2/widget_instance/reconstructed_alert_test.exs index b37bcbf6d..f4ce0727e 100644 --- a/test/screens/v2/widget_instance/reconstructed_alert_test.exs +++ b/test/screens/v2/widget_instance/reconstructed_alert_test.exs @@ -28,7 +28,8 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do downstream_stops: nil, routes: nil, alert_route_types: nil - } + }, + partial_closure_platform_names: [] } } end @@ -129,6 +130,10 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do } end + defp put_partial_closure_platform_names(widget, partial_closure_platform_names) do + %{widget | partial_closure_platform_names: partial_closure_platform_names} + end + defp ie(opts) do %{ stop: opts[:stop], @@ -373,8 +378,8 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do test "returns takeover for a suspension that affects all station trips", %{widget: widget} do widget = put_informed_entities(widget, [ - ie(route: "Red", route_type: 1), - ie(route: "Orange", route_type: 1) + ie(route: "Red", route_type: 1, stop: "place-dwnxg"), + ie(route: "Orange", route_type: 1, stop: "place-dwnxg") ]) |> put_is_full_screen(true) @@ -519,6 +524,20 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do assert [:large] == WidgetInstance.slot_names(widget) assert :reconstructed_large_alert == WidgetInstance.widget_type(widget) end + + test "returns :paged_main_content_left for full screen single platform closures", %{ + widget: widget + } do + widget = + widget + |> put_home_stop(PreFare, "place-forhl") + |> put_effect(:station_closure) + |> put_is_full_screen(true) + + assert [1] == WidgetInstance.priority(widget) + assert [:paged_main_content_left] == WidgetInstance.slot_names(widget) + assert :single_screen_alert == WidgetInstance.widget_type(widget) + end end describe "serialize_dual_screen_alert/1 single line station" do @@ -1042,6 +1061,90 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do assert expected == ReconstructedAlert.serialize(widget, &fake_log/1) end + + test "handles platform closure at home station", %{widget: widget} do + widget = + widget + |> put_home_stop(PreFare, "place-portr") + |> put_effect(:station_closure) + |> put_informed_entities([ + ie(stop: "place-portr", route: "Red", route_type: 1), + ie(stop: "70065", route: "Red", route_type: 1) + ]) + |> put_tagged_stop_sequences(%{ + "Red" => [["place-portr", "place-asmnl"]] + }) + |> put_cause(:unknown) + |> put_is_full_screen(true) + |> put_alert_header("Test Alert") + |> put_routes_at_stop([ + %{ + route_id: "Red", + active?: true, + direction_destinations: nil, + long_name: nil, + short_name: nil, + type: :subway + } + ]) + |> put_partial_closure_platform_names(["Ashmont/Braintree", "Alewife"]) + + expected = %{ + issue: nil, + remedy: nil, + remedy_bold: "Test Alert", + location: nil, + cause: nil, + routes: [%{route_id: "Red", svg_name: "rl"}], + effect: :station_closure, + updated_at: "Friday, 5:00 am", + region: :here + } + + assert expected == ReconstructedAlert.serialize(widget, &fake_log/1) + end + + test "handles downstream platform closure", %{widget: widget} do + widget = + widget + |> put_home_stop(PreFare, "place-asmnl") + |> put_effect(:station_closure) + |> put_informed_entities([ + ie(stop: "place-portr", route: "Red", route_type: 1), + ie(stop: "70065", route: "Red", route_type: 1) + ]) + |> put_tagged_stop_sequences(%{ + "Red" => [["place-portr", "place-asmnl"]] + }) + |> put_cause(:unknown) + |> put_is_full_screen(true) + |> put_alert_header("Test Alert") + |> put_routes_at_stop([ + %{ + route_id: "Red", + active?: true, + direction_destinations: nil, + long_name: nil, + short_name: nil, + type: :subway + } + ]) + |> put_partial_closure_platform_names(["Ashmont/Braintree", "Alewife"]) + + expected = %{ + issue: nil, + remedy: nil, + remedy_bold: "Test Alert", + location: nil, + cause: nil, + routes: [%{route_id: "Red", svg_name: "rl-alewife", headsign: "Alewife"}], + effect: :station_closure, + updated_at: "Friday, 5:00 am", + region: :outside + } + + assert expected == ReconstructedAlert.serialize(widget, &fake_log/1) + end end describe "serialize_fullscreen_alert/1 transfer station" do @@ -1473,7 +1576,7 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do assert expected == ReconstructedAlert.serialize(widget, &fake_log/1) end - test "handles station closure", %{widget: widget} do + test "handles full station closure", %{widget: widget} do widget = widget |> put_effect(:station_closure) @@ -1499,6 +1602,71 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do assert expected == ReconstructedAlert.serialize(widget, &fake_log/1) end + test "handles platform closure", %{widget: widget} do + widget = + widget + |> put_home_stop(PreFare, "place-asmnl") + |> put_effect(:station_closure) + |> put_informed_entities([ + ie(stop: "place-portr", route: "Red", route_type: 1), + ie(stop: "70065", route: "Red", route_type: 1) + ]) + |> put_tagged_stop_sequences(%{ + "Red" => [["place-portr", "place-asmnl"]] + }) + |> put_cause(:unknown) + |> put_informed_stations(["Porter"]) + |> put_partial_closure_platform_names(["Ashmont/Braintree"]) + + expected = %{ + issue: "Bypassing Ashmont/Braintree platform at Porter", + location: "", + cause: nil, + routes: [ + %{color: :red, text: "RED LINE", type: :text} + ], + effect: :station_closure, + urgent: false, + region: :outside, + remedy: nil + } + + assert expected == ReconstructedAlert.serialize(widget, &fake_log/1) + end + + test "handles multiple platform closures at same station", %{widget: widget} do + widget = + widget + |> put_home_stop(PreFare, "place-andrw") + |> put_effect(:station_closure) + |> put_informed_entities([ + ie(stop: "place-jfk", route: "Red", route_type: 1), + ie(stop: "70085", route: "Red", route_type: 1), + ie(stop: "70095", route: "Red", route_type: 1) + ]) + |> put_tagged_stop_sequences(%{ + "Red" => [["place-jfk", "place-andrw"]] + }) + |> put_cause(:unknown) + |> put_informed_stations(["JFK/UMass"]) + |> put_partial_closure_platform_names(["Ashmont", "Braintree"]) + + expected = %{ + issue: "Bypassing 2 platforms at JFK/UMass", + location: "", + cause: nil, + routes: [ + %{color: :red, text: "RED LINE", type: :text} + ], + effect: :station_closure, + urgent: false, + region: :outside, + remedy: nil + } + + assert expected == ReconstructedAlert.serialize(widget, &fake_log/1) + end + test "handles delay", %{widget: widget} do widget = widget @@ -2539,13 +2707,16 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do }} end + fetch_subway_platforms_for_stop_fn = fn _ -> [] end + alert_widget = config |> CandidateGenerator.Widgets.ReconstructedAlert.reconstructed_alert_instances( now, fetch_alerts_fn, fetch_stop_name_fn, - fetch_location_context_fn + fetch_location_context_fn, + fetch_subway_platforms_for_stop_fn ) |> List.first() @@ -2953,13 +3124,16 @@ defmodule Screens.V2.WidgetInstance.ReconstructedAlertTest do }} end + fetch_subway_platforms_for_stop_fn = fn _ -> [] end + alert_widget = config |> CandidateGenerator.Widgets.ReconstructedAlert.reconstructed_alert_instances( now, fetch_alerts_fn, fetch_stop_name_fn, - fetch_location_context_fn + fetch_location_context_fn, + fetch_subway_platforms_for_stop_fn ) |> List.first() diff --git a/test/screens/v2/widget_instance/subway_status_test.exs b/test/screens/v2/widget_instance/subway_status_test.exs index 6bd35b8a7..61eaf9961 100644 --- a/test/screens/v2/widget_instance/subway_status_test.exs +++ b/test/screens/v2/widget_instance/subway_status_test.exs @@ -7,6 +7,9 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do alias Screens.V2.WidgetInstance alias Screens.V2.WidgetInstance.SubwayStatus + defp subway_alerts(alerts), + do: Enum.map(alerts, &%{alert: &1, context: %{all_platforms_at_informed_station: []}}) + describe "priority/1" do test "returns high priority for a flex zone widget" do instance = %SubwayStatus{subway_alerts: []} @@ -64,17 +67,18 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles station closure alert with 4+ stops" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Blue", stop: "place-aport"}, - %{route: "Blue", stop: "place-mvbcl"}, - %{route: "Blue", stop: "place-aqucl"}, - %{route: "Blue", stop: "place-state"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Blue", stop: "place-aport"}, + %{route: "Blue", stop: "place-mvbcl"}, + %{route: "Blue", stop: "place-aqucl"}, + %{route: "Blue", stop: "place-state"} + ] + } + ]) } expected = %{ @@ -124,16 +128,17 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles station closure alert with 3 stops" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Blue", stop: "place-aport"}, - %{route: "Blue", stop: "place-mvbcl"}, - %{route: "Blue", stop: "place-aqucl"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Blue", stop: "place-aport"}, + %{route: "Blue", stop: "place-mvbcl"}, + %{route: "Blue", stop: "place-aqucl"} + ] + } + ]) } expected = %{ @@ -183,15 +188,16 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 1 alert" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Blue", stop: "place-aport"}, - %{route: "Blue", stop: "place-mvbcl"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Blue", stop: "place-aport"}, + %{route: "Blue", stop: "place-mvbcl"} + ] + } + ]) } expected = %{ @@ -238,26 +244,27 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 2 alerts, 2 routes" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Blue", stop: "place-aport"}, - %{route: "Blue", stop: "place-mvbcl"}, - %{route: "Blue", stop: "place-aqucl"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil}, - %{route: "Green-C", stop: nil}, - %{route: "Green-D", stop: nil}, - %{route: "Green-E", stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Blue", stop: "place-aport"}, + %{route: "Blue", stop: "place-mvbcl"}, + %{route: "Blue", stop: "place-aqucl"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil}, + %{route: "Green-C", stop: nil}, + %{route: "Green-D", stop: nil}, + %{route: "Green-E", stop: nil} + ] + } + ]) } expected = %{ @@ -302,23 +309,24 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 2 alerts, 1 non-GL route" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Blue", stop: "place-aport"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Blue", stop: "place-aport"}, - %{route: "Blue", stop: "place-mvbcl"}, - %{route: "Blue", stop: "place-aqucl"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Blue", stop: "place-aport"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Blue", stop: "place-aport"}, + %{route: "Blue", stop: "place-mvbcl"}, + %{route: "Blue", stop: "place-aqucl"} + ] + } + ]) } expected = %{ @@ -370,42 +378,43 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 4 alerts, 2 non-GL routes" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Blue", stop: "place-aport"}, - %{route: "Blue", stop: "place-mvbcl"}, - %{route: "Blue", stop: "place-aqucl"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Blue", stop: "place-aport"}, - %{route: "Blue", stop: "place-mvbcl"}, - %{route: "Blue", stop: "place-aqucl"} - ] - }, - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"}, - %{route: "Orange", stop: "place-mlmnl"}, - %{route: "Orange", stop: "place-welln"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"}, - %{route: "Orange", stop: "place-mlmnl"}, - %{route: "Orange", stop: "place-welln"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Blue", stop: "place-aport"}, + %{route: "Blue", stop: "place-mvbcl"}, + %{route: "Blue", stop: "place-aqucl"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Blue", stop: "place-aport"}, + %{route: "Blue", stop: "place-mvbcl"}, + %{route: "Blue", stop: "place-aqucl"} + ] + }, + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"}, + %{route: "Orange", stop: "place-mlmnl"}, + %{route: "Orange", stop: "place-welln"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"}, + %{route: "Orange", stop: "place-mlmnl"}, + %{route: "Orange", stop: "place-welln"} + ] + } + ]) } expected = %{ @@ -454,31 +463,32 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 3 alerts, 2 non-GL routes" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Blue", stop: "place-aport"} - ] - }, - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"}, - %{route: "Orange", stop: "place-mlmnl"}, - %{route: "Orange", stop: "place-welln"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"}, - %{route: "Orange", stop: "place-mlmnl"}, - %{route: "Orange", stop: "place-welln"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Blue", stop: "place-aport"} + ] + }, + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"}, + %{route: "Orange", stop: "place-mlmnl"}, + %{route: "Orange", stop: "place-welln"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"}, + %{route: "Orange", stop: "place-mlmnl"}, + %{route: "Orange", stop: "place-welln"} + ] + } + ]) } expected = %{ @@ -532,23 +542,24 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 1 alert on GL trunk and 1 alert on GL branch" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Green-C", stop: "place-hwsst"}, - %{route: "Green-C", stop: "place-kntst"}, - %{route: "Green-C", stop: "place-stpul"} - ] - }, - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Green-D", stop: "place-gover"}, - %{route: "Green-D", stop: "place-river"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Green-C", stop: "place-hwsst"}, + %{route: "Green-C", stop: "place-kntst"}, + %{route: "Green-C", stop: "place-stpul"} + ] + }, + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Green-D", stop: "place-gover"}, + %{route: "Green-D", stop: "place-river"} + ] + } + ]) } expected = %{ @@ -608,33 +619,34 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 1 alert on GL trunk and 2 alerts on GL branch" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Green-C", stop: "place-hwsst"}, - %{route: "Green-C", stop: "place-kntst"}, - %{route: "Green-C", stop: "place-stpul"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil} - ] - }, - %Alert{ - effect: :delay, - severity: 6, - informed_entities: [ - %{route: "Green-B", stop: nil}, - %{route: "Green-C", stop: nil}, - %{route: "Green-D", stop: nil}, - %{route: "Green-E", stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Green-C", stop: "place-hwsst"}, + %{route: "Green-C", stop: "place-kntst"}, + %{route: "Green-C", stop: "place-stpul"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil} + ] + }, + %Alert{ + effect: :delay, + severity: 6, + informed_entities: [ + %{route: "Green-B", stop: nil}, + %{route: "Green-C", stop: nil}, + %{route: "Green-D", stop: nil}, + %{route: "Green-E", stop: nil} + ] + } + ]) } expected = %{ @@ -687,25 +699,26 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 2 alerts on GL trunk" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Green-C", stop: "place-gover"}, - %{route: "Green-C", stop: "place-pktrm"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil}, - %{route: "Green-C", stop: nil}, - %{route: "Green-D", stop: nil}, - %{route: "Green-E", stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Green-C", stop: "place-gover"}, + %{route: "Green-C", stop: "place-pktrm"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil}, + %{route: "Green-C", stop: nil}, + %{route: "Green-D", stop: nil}, + %{route: "Green-E", stop: nil} + ] + } + ]) } expected = %{ @@ -761,22 +774,23 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 2 alerts on GL branches" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Green-C", stop: nil} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Green-C", stop: nil} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil} + ] + } + ]) } expected = %{ @@ -829,29 +843,30 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 3+ alerts on GL branches" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Green-C", stop: nil} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil} - ] - }, - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Green-E", stop: "place-symcl"}, - %{route: "Green-E", stop: "place-nuniv"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Green-C", stop: nil} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil} + ] + }, + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Green-E", stop: "place-symcl"}, + %{route: "Green-E", stop: "place-nuniv"} + ] + } + ]) } expected = %{ @@ -899,28 +914,29 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 2 alerts on GL branches and 1 alert on non-GL route" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Green-C", stop: nil} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil} - ] - }, - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Green-C", stop: nil} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil} + ] + }, + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"} + ] + } + ]) } expected = %{ @@ -975,31 +991,32 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 2 alerts on GL trunk and 1 alert on GL branch" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Green-C", stop: nil} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil}, - %{route: "Green-C", stop: nil}, - %{route: "Green-D", stop: nil}, - %{route: "Green-E", stop: nil} - ] - }, - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Green-D", stop: "place-kencl"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Green-C", stop: nil} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil}, + %{route: "Green-C", stop: nil}, + %{route: "Green-D", stop: nil}, + %{route: "Green-E", stop: nil} + ] + }, + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Green-D", stop: "place-kencl"} + ] + } + ]) } expected = %{ @@ -1047,35 +1064,36 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 2 alerts on GL branches and 2 alerts on non-GL route" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Green-C", stop: nil} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Green-B", stop: nil} - ] - }, - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"} - ] - }, - %Alert{ - effect: :delay, - severity: 5, - informed_entities: [ - %{route: "Orange", stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Green-C", stop: nil} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Green-B", stop: nil} + ] + }, + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"} + ] + }, + %Alert{ + effect: :delay, + severity: 5, + informed_entities: [ + %{route: "Orange", stop: nil} + ] + } + ]) } expected = %{ @@ -1129,27 +1147,28 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 1 alert on GL trunk and 2 alerts on non-GL route" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Green-D", stop: "place-lech"}, - %{route: "Green-E", stop: "place-lech"} - ] - }, - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"} - ] - }, - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Blue", stop: "place-bmmnl"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Green-D", stop: "place-lech"}, + %{route: "Green-E", stop: "place-lech"} + ] + }, + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"} + ] + }, + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Blue", stop: "place-bmmnl"} + ] + } + ]) } expected = %{ @@ -1201,21 +1220,22 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 1 alert on GL branch and 1 alert on non-GL route" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Green-C", stop: nil} - ] - }, - %Alert{ - effect: :station_closure, - informed_entities: [ - %{route: "Orange", stop: "place-ogmnl"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Green-C", stop: nil} + ] + }, + %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Orange", stop: "place-ogmnl"} + ] + } + ]) } expected = %{ @@ -1261,17 +1281,18 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "handles 1 alert affecting 3 routes" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Green-C", stop: nil}, - %{route: "Blue", stop: nil}, - %{route: "Orange", stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Green-C", stop: nil}, + %{route: "Blue", stop: nil}, + %{route: "Orange", stop: nil} + ] + } + ]) } expected = %{ @@ -1319,18 +1340,146 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do assert expected == WidgetInstance.serialize(instance) end - test "uses 'Entire line' location text for whole-line shuttles" do + test "handles 1 platform closure alert" do instance = %SubwayStatus{ subway_alerts: [ - %Alert{ - effect: :shuttle, - informed_entities: [ - %{route: "Blue", route_type: 1, direction_id: nil, stop: nil} - ] + %{ + alert: %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Red", stop: "place-portr", route_type: 1}, + %{route: "Red", stop: "70065", route_type: 1} + ] + }, + context: %{ + all_platforms_at_informed_station: [ + %{id: "70065", platform_name: "Ashmont/Braintree"}, + %{id: "70066", platform_name: "Alewife"} + ] + } } ] } + expected = %{ + blue: %{ + type: :contracted, + alerts: [ + %{ + route_pill: %{type: :text, text: "BL", color: :blue}, + status: "Normal Service" + } + ] + }, + orange: %{ + type: :contracted, + alerts: [ + %{ + route_pill: %{type: :text, text: "OL", color: :orange}, + status: "Normal Service" + } + ] + }, + red: %{ + type: :extended, + alert: %{ + status: "Bypassing 1 stop", + location: %{full: "mbta.com/alerts", abbrev: "mbta.com/alerts"}, + route_pill: %{type: :text, text: "RL", color: :red} + } + }, + green: %{ + type: :contracted, + alerts: [ + %{ + route_pill: %{type: :text, text: "GL", color: :green}, + status: "Normal Service" + } + ] + } + } + + assert expected == WidgetInstance.serialize(instance) + end + + test "handles alert closing multiple platforms at one station" do + instance = %SubwayStatus{ + subway_alerts: [ + %{ + alert: %Alert{ + effect: :station_closure, + informed_entities: [ + %{route: "Red", stop: "place-jfk", route_type: 1}, + %{route: "Red", stop: "70085", route_type: 1}, + %{route: "Red", stop: "70095", route_type: 1} + ] + }, + context: %{ + all_platforms_at_informed_station: [ + %{id: "70085", platform_name: "Ashmont"}, + %{id: "70086", platform_name: "Alewife (from Ashmont)"}, + %{id: "70095", platform_name: "Braintree"}, + %{id: "70096", platform_name: "Alewife (from Braintree)"} + ] + } + } + ] + } + + expected = %{ + blue: %{ + type: :contracted, + alerts: [ + %{ + route_pill: %{type: :text, text: "BL", color: :blue}, + status: "Normal Service" + } + ] + }, + orange: %{ + type: :contracted, + alerts: [ + %{ + route_pill: %{type: :text, text: "OL", color: :orange}, + status: "Normal Service" + } + ] + }, + red: %{ + type: :extended, + alert: %{ + status: "Bypassing 2 stops", + location: %{full: "mbta.com/alerts", abbrev: "mbta.com/alerts"}, + route_pill: %{type: :text, text: "RL", color: :red} + } + }, + green: %{ + type: :contracted, + alerts: [ + %{ + route_pill: %{type: :text, text: "GL", color: :green}, + status: "Normal Service" + } + ] + } + } + + assert expected == WidgetInstance.serialize(instance) + end + + test "uses 'Entire line' location text for whole-line shuttles" do + instance = %SubwayStatus{ + subway_alerts: + subway_alerts([ + %Alert{ + effect: :shuttle, + informed_entities: [ + %{route: "Blue", route_type: 1, direction_id: nil, stop: nil} + ] + } + ]) + } + expected = %{ blue: %{ type: :extended, @@ -1365,14 +1514,15 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "uses 'Entire line' location text for whole-line suspensions" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Blue", route_type: 1, direction_id: nil, stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Blue", route_type: 1, direction_id: nil, stop: nil} + ] + } + ]) } expected = %{ @@ -1409,17 +1559,18 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "uses 'Entire line' location text for whole-Green Line suspensions" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :suspension, - informed_entities: [ - %{route: "Green-B", route_type: 0, direction_id: nil, stop: nil}, - %{route: "Green-C", route_type: 0, direction_id: nil, stop: nil}, - %{route: "Green-D", route_type: 0, direction_id: nil, stop: nil}, - %{route: "Green-E", route_type: 0, direction_id: nil, stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :suspension, + informed_entities: [ + %{route: "Green-B", route_type: 0, direction_id: nil, stop: nil}, + %{route: "Green-C", route_type: 0, direction_id: nil, stop: nil}, + %{route: "Green-D", route_type: 0, direction_id: nil, stop: nil}, + %{route: "Green-E", route_type: 0, direction_id: nil, stop: nil} + ] + } + ]) } expected = %{ @@ -1456,15 +1607,16 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "does _not_ use 'Entire line' location text for whole-line delays" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :delay, - severity: 9, - informed_entities: [ - %{route: "Blue", route_type: 1, direction_id: nil, stop: nil} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :delay, + severity: 9, + informed_entities: [ + %{route: "Blue", route_type: 1, direction_id: nil, stop: nil} + ] + } + ]) } expected = %{ @@ -1501,17 +1653,18 @@ defmodule Screens.V2.WidgetInstance.SubwayStatusTest do test "finds correct endpoints if shuttle starts on trunk" do instance = %SubwayStatus{ - subway_alerts: [ - %Alert{ - effect: :shuttle, - informed_entities: [ - %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-kencl"}, - %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-smary"}, - %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-hwsst"}, - %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-kntst"} - ] - } - ] + subway_alerts: + subway_alerts([ + %Alert{ + effect: :shuttle, + informed_entities: [ + %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-kencl"}, + %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-smary"}, + %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-hwsst"}, + %{direction_id: nil, route: "Green-C", route_type: 0, stop: "place-kntst"} + ] + } + ]) } expected = %{