diff --git a/config/test.exs b/config/test.exs index 5c00e0dd2..4eb4cb2f6 100644 --- a/config/test.exs +++ b/config/test.exs @@ -131,7 +131,9 @@ config :screens, headway_headsign: "Test" } ] - } + }, + elevator_redundancy_data_path: + Path.join(~w[#{File.cwd!()} test fixtures elevator_redundancy_data.json]) config :screens, ScreensWeb.AuthManager, secret_key: "test key" diff --git a/lib/screens/alerts/alert.ex b/lib/screens/alerts/alert.ex index 19dc4ac31..d459fddf1 100644 --- a/lib/screens/alerts/alert.ex +++ b/lib/screens/alerts/alert.ex @@ -374,4 +374,9 @@ defmodule Screens.Alerts.Alert do end def partial_station_closure?(_, _), do: false + + @spec informs_stop_id?(Alert.t(), Stop.id()) :: boolean() + def informs_stop_id?(%__MODULE__{informed_entities: informed_entities}, stop_id) do + Enum.any?(informed_entities, &(&1.stop == stop_id)) + end end diff --git a/lib/screens/v2/candidate_generator/elevator/closures.ex b/lib/screens/v2/candidate_generator/elevator/closures.ex index a3b28a5e7..20c02e85e 100644 --- a/lib/screens/v2/candidate_generator/elevator/closures.ex +++ b/lib/screens/v2/candidate_generator/elevator/closures.ex @@ -27,6 +27,16 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do @route injected(Route) @stop injected(Stop) + @elevator_redundancy_data :screens + |> Application.compile_env( + :elevator_redundancy_data_path, + :screens + |> :code.priv_dir() + |> Path.join("elevators/elevator_redundancy_data.json") + ) + |> File.read!() + |> Jason.decode!() + @spec elevator_status_instances(Screen.t(), NormalHeader.t(), Footer.t()) :: list(WidgetInstance.t()) def elevator_status_instances( @@ -37,7 +47,7 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do with {:ok, %Stop{id: stop_id}} <- @facility.fetch_stop_for_facility(elevator_id), {:ok, parent_station_map} <- @stop.fetch_parent_station_name_map(), {:ok, alerts} <- @alert.fetch_elevator_alerts_with_facilities() do - elevator_alerts = Enum.filter(alerts, &relevant_alert?/1) + elevator_alerts = Enum.filter(alerts, &relevant_alert?(&1, stop_id)) routes_map = get_routes_map(elevator_alerts, stop_id) {in_station_alerts, outside_alerts} = @@ -75,8 +85,11 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do end end - defp relevant_alert?(alert) do - relevant_effect?(alert) and informs_one_facility?(alert) + # All alerts at home station are relevant but only elevators without redundancies are + # relevant at other stations. + defp relevant_alert?(alert, home_stop_id) do + relevant_effect?(alert) and informs_one_facility?(alert) and + (Alert.informs_stop_id?(alert, home_stop_id) or not has_nearby_redundancy?(alert)) end defp relevant_effect?(alert), do: alert.effect == :elevator_closure @@ -169,4 +182,19 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do ie -> if InformedEntity.parent_station?(ie), do: ie.stop end) end + + defp has_nearby_redundancy?(%Alert{ + informed_entities: [%{facility: %{id: informed_facility_id}} | _] + }) do + if data = @elevator_redundancy_data[informed_facility_id] do + data["nearby_redundancy?"] + else + _ = + Sentry.capture_message( + "Elevator #{informed_facility_id} does not exist in redundancy data" + ) + + false + end + end end diff --git a/priv/elevators/elevator_redundancy_data.json b/priv/elevators/elevator_redundancy_data.json new file mode 100644 index 000000000..3620f441e --- /dev/null +++ b/priv/elevators/elevator_redundancy_data.json @@ -0,0 +1,635 @@ +{ + "918": { + "nearby_redundancy?": false + }, + "925": { + "nearby_redundancy?": false + }, + "804": { + "nearby_redundancy?": false + }, + "958": { + "nearby_redundancy?": false + }, + "780": { + "nearby_redundancy?": false + }, + "986": { + "nearby_redundancy?": false + }, + "921": { + "nearby_redundancy?": false + }, + "725": { + "nearby_redundancy?": false + }, + "816": { + "nearby_redundancy?": false + }, + "866": { + "nearby_redundancy?": false + }, + "996": { + "nearby_redundancy?": true + }, + "744": { + "nearby_redundancy?": false + }, + "710": { + "nearby_redundancy?": false + }, + "842": { + "nearby_redundancy?": false + }, + "899": { + "nearby_redundancy?": false + }, + "856": { + "nearby_redundancy?": false + }, + "879": { + "nearby_redundancy?": false + }, + "901": { + "nearby_redundancy?": false + }, + "922": { + "nearby_redundancy?": false + }, + "945": { + "nearby_redundancy?": false + }, + "701": { + "nearby_redundancy?": true + }, + "771": { + "nearby_redundancy?": false + }, + "734": { + "nearby_redundancy?": false + }, + "979": { + "nearby_redundancy?": false + }, + "936": { + "nearby_redundancy?": false + }, + "957": { + "nearby_redundancy?": false + }, + "998": { + "nearby_redundancy?": true + }, + "971": { + "nearby_redundancy?": false + }, + "913": { + "nearby_redundancy?": false + }, + "855": { + "nearby_redundancy?": false + }, + "731": { + "nearby_redundancy?": false + }, + "892": { + "nearby_redundancy?": false + }, + "854": { + "nearby_redundancy?": false + }, + "815": { + "nearby_redundancy?": true + }, + "712": { + "nearby_redundancy?": false + }, + "938": { + "nearby_redundancy?": false + }, + "896": { + "nearby_redundancy?": false + }, + "886": { + "nearby_redundancy?": false + }, + "994": { + "nearby_redundancy?": true + }, + "713": { + "nearby_redundancy?": false + }, + "933": { + "nearby_redundancy?": false + }, + "897": { + "nearby_redundancy?": false + }, + "769": { + "nearby_redundancy?": false + }, + "869": { + "nearby_redundancy?": false + }, + "908": { + "nearby_redundancy?": false + }, + "910": { + "nearby_redundancy?": false + }, + "813": { + "nearby_redundancy?": false + }, + "891": { + "nearby_redundancy?": false + }, + "947": { + "nearby_redundancy?": false + }, + "724": { + "nearby_redundancy?": false + }, + "711": { + "nearby_redundancy?": false + }, + "782": { + "nearby_redundancy?": false + }, + "972": { + "nearby_redundancy?": false + }, + "803": { + "nearby_redundancy?": false + }, + "978": { + "nearby_redundancy?": false + }, + "992": { + "nearby_redundancy?": false + }, + "859": { + "nearby_redundancy?": false + }, + "702": { + "nearby_redundancy?": true + }, + "757": { + "nearby_redundancy?": true + }, + "965": { + "nearby_redundancy?": false + }, + "722": { + "nearby_redundancy?": true + }, + "898": { + "nearby_redundancy?": false + }, + "717": { + "nearby_redundancy?": false + }, + "915": { + "nearby_redundancy?": false + }, + "807": { + "nearby_redundancy?": true + }, + "852": { + "nearby_redundancy?": false + }, + "858": { + "nearby_redundancy?": false + }, + "944": { + "nearby_redundancy?": false + }, + "850": { + "nearby_redundancy?": false + }, + "967": { + "nearby_redundancy?": true + }, + "768": { + "nearby_redundancy?": true + }, + "909": { + "nearby_redundancy?": false + }, + "970": { + "nearby_redundancy?": false + }, + "868": { + "nearby_redundancy?": false + }, + "52": { + "nearby_redundancy?": false + }, + "920": { + "nearby_redundancy?": false + }, + "949": { + "nearby_redundancy?": false + }, + "746": { + "nearby_redundancy?": true + }, + "805": { + "nearby_redundancy?": true + }, + "930": { + "nearby_redundancy?": true + }, + "719": { + "nearby_redundancy?": false + }, + "937": { + "nearby_redundancy?": false + }, + "966": { + "nearby_redundancy?": false + }, + "946": { + "nearby_redundancy?": false + }, + "814": { + "nearby_redundancy?": true + }, + "709": { + "nearby_redundancy?": false + }, + "721": { + "nearby_redundancy?": true + }, + "762": { + "nearby_redundancy?": true + }, + "846": { + "nearby_redundancy?": false + }, + "802": { + "nearby_redundancy?": true + }, + "880": { + "nearby_redundancy?": false + }, + "781": { + "nearby_redundancy?": false + }, + "876": { + "nearby_redundancy?": false + }, + "715": { + "nearby_redundancy?": false + }, + "864": { + "nearby_redundancy?": false + }, + "969": { + "nearby_redundancy?": false + }, + "890": { + "nearby_redundancy?": false + }, + "927": { + "nearby_redundancy?": false + }, + "889": { + "nearby_redundancy?": false + }, + "973": { + "nearby_redundancy?": false + }, + "904": { + "nearby_redundancy?": false + }, + "870": { + "nearby_redundancy?": false + }, + "975": { + "nearby_redundancy?": false + }, + "964": { + "nearby_redundancy?": false + }, + "982": { + "nearby_redundancy?": true + }, + "847": { + "nearby_redundancy?": false + }, + "923": { + "nearby_redundancy?": false + }, + "857": { + "nearby_redundancy?": false + }, + "808": { + "nearby_redundancy?": false + }, + "732": { + "nearby_redundancy?": false + }, + "851": { + "nearby_redundancy?": false + }, + "905": { + "nearby_redundancy?": false + }, + "806": { + "nearby_redundancy?": true + }, + "849": { + "nearby_redundancy?": false + }, + "885": { + "nearby_redundancy?": false + }, + "912": { + "nearby_redundancy?": false + }, + "845": { + "nearby_redundancy?": false + }, + "811": { + "nearby_redundancy?": false + }, + "900": { + "nearby_redundancy?": false + }, + "763": { + "nearby_redundancy?": true + }, + "735": { + "nearby_redundancy?": false + }, + "823": { + "nearby_redundancy?": false + }, + "841": { + "nearby_redundancy?": false + }, + "999": { + "nearby_redundancy?": true + }, + "906": { + "nearby_redundancy?": false + }, + "954": { + "nearby_redundancy?": false + }, + "953": { + "nearby_redundancy?": false + }, + "772": { + "nearby_redundancy?": true + }, + "861": { + "nearby_redundancy?": false + }, + "952": { + "nearby_redundancy?": false + }, + "843": { + "nearby_redundancy?": false + }, + "962": { + "nearby_redundancy?": false + }, + "884": { + "nearby_redundancy?": false + }, + "831": { + "nearby_redundancy?": false + }, + "723": { + "nearby_redundancy?": true + }, + "50": { + "nearby_redundancy?": false + }, + "935": { + "nearby_redundancy?": false + }, + "728": { + "nearby_redundancy?": false + }, + "745": { + "nearby_redundancy?": false + }, + "765": { + "nearby_redundancy?": false + }, + "919": { + "nearby_redundancy?": false + }, + "963": { + "nearby_redundancy?": false + }, + "991": { + "nearby_redundancy?": false + }, + "985": { + "nearby_redundancy?": false + }, + "53": { + "nearby_redundancy?": false + }, + "736": { + "nearby_redundancy?": true + }, + "867": { + "nearby_redundancy?": false + }, + "756": { + "nearby_redundancy?": true + }, + "903": { + "nearby_redundancy?": false + }, + "940": { + "nearby_redundancy?": false + }, + "800": { + "nearby_redundancy?": false + }, + "726": { + "nearby_redundancy?": false + }, + "764": { + "nearby_redundancy?": false + }, + "703": { + "nearby_redundancy?": true + }, + "887": { + "nearby_redundancy?": false + }, + "872": { + "nearby_redundancy?": false + }, + "733": { + "nearby_redundancy?": false + }, + "830": { + "nearby_redundancy?": false + }, + "817": { + "nearby_redundancy?": false + }, + "853": { + "nearby_redundancy?": false + }, + "955": { + "nearby_redundancy?": false + }, + "968": { + "nearby_redundancy?": false + }, + "821": { + "nearby_redundancy?": false + }, + "928": { + "nearby_redundancy?": false + }, + "931": { + "nearby_redundancy?": false + }, + "766": { + "nearby_redundancy?": false + }, + "961": { + "nearby_redundancy?": false + }, + "993": { + "nearby_redundancy?": false + }, + "997": { + "nearby_redundancy?": true + }, + "812": { + "nearby_redundancy?": false + }, + "990": { + "nearby_redundancy?": false + }, + "926": { + "nearby_redundancy?": false + }, + "956": { + "nearby_redundancy?": false + }, + "976": { + "nearby_redundancy?": false + }, + "727": { + "nearby_redundancy?": false + }, + "848": { + "nearby_redundancy?": false + }, + "983": { + "nearby_redundancy?": true + }, + "743": { + "nearby_redundancy?": true + }, + "810": { + "nearby_redundancy?": false + }, + "924": { + "nearby_redundancy?": false + }, + "987": { + "nearby_redundancy?": true + }, + "720": { + "nearby_redundancy?": true + }, + "718": { + "nearby_redundancy?": false + }, + "865": { + "nearby_redundancy?": false + }, + "911": { + "nearby_redundancy?": false + }, + "981": { + "nearby_redundancy?": false + }, + "932": { + "nearby_redundancy?": false + }, + "716": { + "nearby_redundancy?": false + }, + "980": { + "nearby_redundancy?": false + }, + "948": { + "nearby_redundancy?": false + }, + "929": { + "nearby_redundancy?": true + }, + "840": { + "nearby_redundancy?": false + }, + "907": { + "nearby_redundancy?": false + }, + "844": { + "nearby_redundancy?": false + }, + "714": { + "nearby_redundancy?": false + }, + "974": { + "nearby_redundancy?": false + }, + "917": { + "nearby_redundancy?": false + }, + "770": { + "nearby_redundancy?": true + }, + "860": { + "nearby_redundancy?": false + }, + "951": { + "nearby_redundancy?": false + }, + "939": { + "nearby_redundancy?": false + }, + "934": { + "nearby_redundancy?": false + }, + "51": { + "nearby_redundancy?": false + }, + "708": { + "nearby_redundancy?": false + }, + "881": { + "nearby_redundancy?": false + }, + "977": { + "nearby_redundancy?": false + }, + "6476": { + "nearby_redundancy?": false + }, + "801": { + "nearby_redundancy?": false + }, + "767": { + "nearby_redundancy?": true + }, + "914": { + "nearby_redundancy?": false + }, + "995": { + "nearby_redundancy?": true + } +} diff --git a/scripts/format_elevator_data.exs b/scripts/format_elevator_data.exs new file mode 100644 index 000000000..d963af9e3 --- /dev/null +++ b/scripts/format_elevator_data.exs @@ -0,0 +1,33 @@ +#!/usr/bin/env -S ERL_FLAGS=+B elixir + +# Script used to format the spreadsheet found at +# https://docs.google.com/spreadsheets/d/1lHogme-2SuDSgjrRK52k7yVgSFK-v_LMmUfkYgBJjIU/edit?gid=179933470#gid=179933470 + +# To use this script: +# 1. Download the spreadsheet above to a temporary directory +# 2. Run `elixir scripts/format_elevator_data.exs --path ` + +Mix.install([{:jason, "~> 1.4"}, {:csv, "~> 3.2"}]) + +{[path: path], _, _} = + System.argv() + |> OptionParser.parse(strict: [path: :string]) + +formatted_data = + path + |> File.stream!() + |> CSV.decode(headers: true) + |> Enum.map(fn + {:ok, %{"elevator_id" => ""}} -> + nil + + {:ok, %{"elevator_id" => id, "Exiting System Categorization" => category}} -> + {id, %{nearby_redundancy?: category == "1 - Nearby"}} + end) + |> Enum.reject(&is_nil/1) + |> Map.new() + +File.write( + "priv/elevators/elevator_redundancy_data.json", + Jason.encode!(formatted_data, pretty: true) +) diff --git a/test/fixtures/elevator_redundancy_data.json b/test/fixtures/elevator_redundancy_data.json new file mode 100644 index 000000000..469d07afb --- /dev/null +++ b/test/fixtures/elevator_redundancy_data.json @@ -0,0 +1,11 @@ +{ + "112": { + "nearby_redundancy?": true + }, + "222": { + "nearby_redundancy?": true + }, + "333": { + "nearby_redundancy?": false + } +} diff --git a/test/screens/v2/candidate_generator/elevator/closures_test.exs b/test/screens/v2/candidate_generator/elevator/closures_test.exs index 31aa6f5dc..eced7377b 100644 --- a/test/screens/v2/candidate_generator/elevator/closures_test.exs +++ b/test/screens/v2/candidate_generator/elevator/closures_test.exs @@ -234,6 +234,104 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do ) end + test "Filters out alerts at other stations with nearby redundancy", %{ + config: config, + header_instance: header_instance, + footer_instance: footer_instance + } do + expect(@facility, :fetch_stop_for_facility, fn "111" -> {:ok, %Stop{id: "place-test"}} end) + + expect(@stop, :fetch_parent_station_name_map, fn -> + {:ok, + %{ + "place-test" => "This Station", + "place-test-redundancy" => "Other With Redundancy", + "place-test-no-redundancy" => "Other No Redundancy" + }} + end) + + expect(@route, :fetch, 2, fn _ -> {:ok, [%Route{id: "Red", type: :subway}]} end) + + expect(@alert, :fetch_elevator_alerts_with_facilities, fn -> + alerts = [ + struct(Alert, + id: "1", + effect: :elevator_closure, + informed_entities: [ + %{stop: "place-test", facility: %{name: "In Station Elevator", id: "112"}} + ] + ), + struct(Alert, + id: "2", + effect: :elevator_closure, + informed_entities: [ + %{ + stop: "place-test-redundancy", + facility: %{name: "Other With Redundancy", id: "222"} + } + ] + ), + struct(Alert, + id: "3", + effect: :elevator_closure, + informed_entities: [ + %{ + stop: "place-test-no-redundancy", + facility: %{name: "Other Without Redundancy", id: "333"} + } + ] + ) + ] + + {:ok, alerts} + end) + + [ + ^header_instance, + %Screens.V2.WidgetInstance.OutsideElevatorClosures{ + app_params: %ScreensConfig.V2.Elevator{ + elevator_id: "111", + alternate_direction_text: "Test", + accessible_path_direction_arrow: :n, + evergreen_content: [], + accessible_path_image_url: nil, + accessible_path_image_here_coordinates: %{y: 0, x: 0} + }, + in_station_closures: [ + %Screens.V2.WidgetInstance.Elevator.Closure{ + id: "1", + elevator_name: "In Station Elevator", + elevator_id: "112", + description: nil, + header_text: nil + } + ], + other_stations_with_closures: [ + %Screens.V2.WidgetInstance.OutsideElevatorClosures.Station{ + id: "place-test-no-redundancy", + name: "Other No Redundancy", + route_icons: [%{type: :text, text: "RL", color: :red}], + closures: [ + %Screens.V2.WidgetInstance.Elevator.Closure{ + id: "3", + elevator_name: "Other Without Redundancy", + elevator_id: "333", + description: nil, + header_text: nil + } + ] + } + ] + }, + ^footer_instance + ] = + ElevatorClosures.elevator_status_instances( + struct(Screen, app_id: :elevator_v2, app_params: config), + header_instance, + footer_instance + ) + end + test "Returns CurrentElevatorClosed if configured elevator is closed", %{ config: config, header_instance: header_instance,