diff --git a/lib/dotcom/trip_plan/alerts.ex b/lib/dotcom/trip_plan/alerts.ex index fb165a41b3..b80dab34a0 100644 --- a/lib/dotcom/trip_plan/alerts.ex +++ b/lib/dotcom/trip_plan/alerts.ex @@ -8,14 +8,40 @@ defmodule Dotcom.TripPlan.Alerts do * at the times they'll be travelling """ - alias Alerts.Alert - alias Alerts.InformedEntity, as: IE + alias Alerts.{Alert, InformedEntity} alias Dotcom.TripPlan.{Itinerary, Leg, TransitDetail} + def by_mode_and_stops(alerts, leg) do + {route_alerts, stop_alerts} = + alerts + |> Enum.split_with(fn alert -> + alert.informed_entity.entities + |> Enum.all?(fn + %{stop: nil} -> true + _ -> false + end) + end) + + %{from: from_stop_ids, to: to_stop_ids} = Leg.stop_ids(leg) + + entities_from = mode_entities_with_stop_ids(leg.mode, from_stop_ids) + from = Alerts.Match.match(stop_alerts, entities_from) + + entities_to = mode_entities_with_stop_ids(leg.mode, to_stop_ids) + to = Alerts.Match.match(stop_alerts, entities_to) + + %{ + route: route_alerts, + from: from, + to: to + } + end + @spec from_itinerary(Itinerary.t()) :: [Alerts.Alert.t()] def from_itinerary(itinerary) do itinerary.start |> Alerts.Repo.all() + |> Enum.reject(&reject_irrelevant_alert(&1, itinerary.accessible?)) |> Alerts.Match.match( entities(itinerary), itinerary.start @@ -25,8 +51,9 @@ defmodule Dotcom.TripPlan.Alerts do @doc "Filters a list of Alerts to those relevant to the Itinerary" @spec filter_for_itinerary([Alert.t()], Itinerary.t()) :: [Alert.t()] def filter_for_itinerary(alerts, itinerary) do - Alerts.Match.match( - alerts, + alerts + |> Enum.reject(&reject_irrelevant_alert(&1, itinerary.accessible?)) + |> Alerts.Match.match( Enum.concat(intermediate_entities(itinerary), entities(itinerary)), itinerary.start ) @@ -42,13 +69,31 @@ defmodule Dotcom.TripPlan.Alerts do ) end + # Reject an alert that is not relevant to a trip plan: + # - accessibility issue (see `reject_accessibility_alert`) + # - bike issue + # - facility issue + # - parking closure + # - parking issue + defp reject_irrelevant_alert(alert, accessible?) do + reject_accessibility_alert(alert, accessible?) || + Enum.member?(~w[bike_issue facility_issue parking_closure parking_issue]a, alert.effect) + end + + # Reject an alert that is not relevant to a trip plan *unless* we want an accessible trip: + # - elevator closure + # - escalator closure + defp reject_accessibility_alert(alert, accessible?) do + not accessible? && (alert.effect == :elevator_closure || alert.effect == :escalator_closure) + end + defp intermediate_entities(itinerary) do itinerary |> Itinerary.intermediate_stop_ids() - |> Enum.map(&%IE{stop: &1}) + |> Enum.map(&%InformedEntity{stop: &1}) end - @spec entities(Itinerary.t()) :: [IE.t()] + @spec entities(Itinerary.t()) :: [InformedEntity.t()] defp entities(itinerary) do itinerary |> Enum.flat_map(&leg_entities(&1)) @@ -84,36 +129,17 @@ defmodule Dotcom.TripPlan.Alerts do trip.direction_id end - [%IE{route_type: route_type, route: route.id, trip: trip_id, direction_id: direction_id}] + [ + %InformedEntity{ + route_type: route_type, + route: route.id, + trip: trip_id, + direction_id: direction_id + } + ] end defp mode_entities(_) do [] end - - def by_mode_and_stops(alerts, leg) do - {route_alerts, stop_alerts} = - alerts - |> Enum.split_with(fn alert -> - alert.informed_entity.entities - |> Enum.all?(fn - %{stop: nil} -> true - _ -> false - end) - end) - - %{from: from_stop_ids, to: to_stop_ids} = Leg.stop_ids(leg) - - entities_from = mode_entities_with_stop_ids(leg.mode, from_stop_ids) - from = Alerts.Match.match(stop_alerts, entities_from) - - entities_to = mode_entities_with_stop_ids(leg.mode, to_stop_ids) - to = Alerts.Match.match(stop_alerts, entities_to) - - %{ - route: route_alerts, - from: from, - to: to - } - end end diff --git a/test/dotcom/trip_plan/alerts_test.exs b/test/dotcom/trip_plan/alerts_test.exs index e5855fc3c2..770e41fa9e 100644 --- a/test/dotcom/trip_plan/alerts_test.exs +++ b/test/dotcom/trip_plan/alerts_test.exs @@ -163,6 +163,56 @@ defmodule Dotcom.TripPlan.AlertsTest do bad_alert = Alert.update(good_alert, active_period: [invalid_active_period(itinerary)]) assert_only_good_alert(good_alert, bad_alert, itinerary) end + + test "rejects an accessibility alert", %{itinerary: itinerary, route_id: route_id} do + expect(MBTA.Api.Mock, :get_json, fn "/trips/" <> id, [] -> + %JsonApi{ + data: [ + Api.build(:trip_item, %{id: id}) + ] + } + end) + + good_alert = + Alert.new( + active_period: [valid_active_period(itinerary)], + effect: :no_service, + informed_entity: [%InformedEntity{route: route_id}] + ) + + bad_alert = Alert.update(good_alert, effect: :elevator_closure) + + assert_only_good_alert(good_alert, bad_alert, itinerary) + end + + test "keeps an accesibility alert if the itinerary is accessible", %{ + itinerary: itinerary, + route_id: route_id + } do + expect(MBTA.Api.Mock, :get_json, fn "/trips/" <> id, [] -> + %JsonApi{ + data: [ + Api.build(:trip_item, %{id: id}) + ] + } + end) + + first_alert = + Alert.new( + active_period: [valid_active_period(itinerary)], + effect: :no_service, + informed_entity: [%InformedEntity{route: route_id}] + ) + + second_alert = Alert.update(first_alert, effect: :elevator_closure) + + accessible_itinerary = Map.put(itinerary, :accessible?, true) + + assert filter_for_itinerary([first_alert, second_alert], accessible_itinerary) == [ + first_alert, + second_alert + ] + end end describe "by_mode_and_stops/2" do