Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions lib/dotcom/alerts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,84 @@ defmodule Dotcom.Alerts do
%{} -> false
end)
end

def routes_with_high_priority_alerts_by_mode(alerts) do
Comment thread
thecristen marked this conversation as resolved.
modes = [:subway, :bus, :commuter_rail, :ferry]
empty_by_mode = Map.new(modes, fn mode -> {mode, MapSet.new()} end)

route_ids_by_mode =
alerts
|> Enum.filter(&(Alerts.Priority.priority(&1) == :high))
|> Enum.reduce(empty_by_mode, fn alert, acc ->
route_ids = Alert.get_entity(alert, :route) |> MapSet.delete(nil)

alert
|> alert_route_type()
|> Enum.map(&Route.type_atom/1)
|> Enum.reduce(acc, fn mode, acc2 ->
Map.update!(acc2, mode, &MapSet.union(&1, route_ids))
end)
end)

Enum.map(modes, fn mode_key ->
route_ids =
route_ids_by_mode
|> Map.fetch!(mode_key)
|> MapSet.to_list()

{mode_key,
get_many(route_ids, &@routes_repo_module.get/1)
|> Stream.filter(&match?({:ok, %Route{}}, &1))
|> Stream.map(fn {:ok, route} -> route end)
|> Enum.sort_by(& &1.sort_order)}
end)
end

defp get_many([], _), do: []

defp get_many(ids, func) do
Task.async_stream(ids, func, max_concurrency: 8, on_timeout: :kill_task, ordered: false)
end

def stops_with_access_alerts_by_effect(alerts) do
access_effects = Alerts.Accessibility.effect_types()
empty_by_effect = Map.new(access_effects, &{&1, MapSet.new()})

stop_ids_by_effect =
alerts
|> Enum.reduce(empty_by_effect, fn alert, acc ->
if Map.has_key?(acc, alert.effect) do
stop_id = alert_stop_ids(alert) |> List.last()
Map.update!(acc, alert.effect, &MapSet.put(&1, stop_id))
else
acc
end
end)

Enum.map(access_effects, fn effect ->
stops =
stop_ids_by_effect
|> Map.fetch!(effect)
|> get_many(&@stops_repo_module.get_parent/1)
|> Stream.filter(&match?({:ok, %Stop{}}, &1))
|> Stream.map(fn {:ok, stop} -> stop end)
|> Enum.sort_by(& &1.name)

{effect, stops}
end)
end

def alert_route_type(alert) do
alert
|> Alert.get_entity(:route_type)
|> MapSet.delete(nil)
|> MapSet.to_list()
end

def alert_stop_ids(alert) do
alert
|> Alert.get_entity(:stop)
|> MapSet.delete(nil)
|> MapSet.to_list()
end
end
72 changes: 9 additions & 63 deletions lib/dotcom_web/views/page_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,73 +11,19 @@ defmodule DotcomWeb.PageView do
alias CMS.Partial.Banner
alias DotcomWeb.PartialView

@stops_repo Application.compile_env!(:dotcom, :repo_modules)[:stops]
@spec get_route(Routes.Route.id_t()) :: Routes.Route.t() | nil
def get_route(id) do
case DotcomWeb.ScheduleController.Line.Helpers.get_route(id) do
{:ok, route} -> route
_ -> nil
end
end

@spec sort_routes({Routes.Route.gtfs_route_type(), [Routes.Route.t()]}) ::
{Routes.Route.gtfs_route_type(), [Routes.Route.t()]}
defp sort_routes({mode, routes}) do
{mode, Enum.sort_by(routes, & &1.sort_order)}
end

@spec get_mode_order({Routes.Route.gtfs_route_type(), [Routes.Route.t()]}) :: integer()
defp get_mode_order({:subway, _}), do: 0
defp get_mode_order({:bus, _}), do: 1
defp get_mode_order({:commuter_rail, _}), do: 2
defp get_mode_order({:ferry, _}), do: 3

@spec get_access_issue_order({Alerts.Accessibility.effect_type(), [Stops.Stop.t()]}) ::
integer()
defp get_access_issue_order({:elevator_closure, _}), do: 0
defp get_access_issue_order({:escalator_closure, _}), do: 1
defp get_access_issue_order({:access_issue, _}), do: 2

@spec alerts([Alerts.Alert.t()]) :: Phoenix.HTML.Safe.t()
def alerts(alerts) do
routes_with_high_priority_alerts_by_mode =
alerts
|> Enum.filter(&(Alerts.Priority.priority(&1) == :high))
|> Enum.reduce(MapSet.new(), fn alert, routes ->
MapSet.union(routes, Alerts.Alert.get_entity(alert, :route))
end)
|> Enum.filter(& &1)
|> Enum.map(&get_route/1)
|> Enum.filter(& &1)
|> Enum.group_by(&Routes.Route.type_atom(&1.type))
|> (&Map.merge(%{bus: [], subway: [], ferry: [], commuter_rail: []}, &1)).()
|> Enum.map(&sort_routes/1)
|> Enum.sort_by(&get_mode_order/1)

stops_with_accessibility_alerts_by_issue =
alerts
|> Enum.filter(&Alerts.Accessibility.accessibility?/1)
|> Enum.reduce(
Map.new(Alerts.Accessibility.effect_types(), fn t -> {t, MapSet.new()} end),
fn alert, types ->
stops = Alerts.Alert.get_entity(alert, :stop)
type = alert.effect

Map.put(types, type, MapSet.union(Map.get(types, type), stops))
end
)
|> Enum.map(fn {type, stops} ->
{type,
Enum.map(stops, &@stops_repo.get_parent/1)
|> Enum.filter(& &1)
|> Enum.uniq_by(& &1.id)
|> Enum.sort_by(& &1.name)}
end)
|> Enum.sort_by(&get_access_issue_order/1)
[routes, stops] =
[
&Dotcom.Alerts.routes_with_high_priority_alerts_by_mode/1,
&Dotcom.Alerts.stops_with_access_alerts_by_effect/1
]
|> Task.async_stream(& &1.(alerts), timeout: 10_000)
|> Enum.map(fn {:ok, result} -> result end)

render("_alerts.html",
routes_with_high_priority_alerts_by_mode: routes_with_high_priority_alerts_by_mode,
stops_with_accessibility_alerts_by_issue: stops_with_accessibility_alerts_by_issue
routes_with_high_priority_alerts_by_mode: routes,
stops_with_accessibility_alerts_by_issue: stops
)
end

Expand Down
57 changes: 57 additions & 0 deletions test/dotcom/alerts_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -491,4 +491,61 @@ defmodule Dotcom.AlertsTest do

assert systemwide_mode_alert?(alert_without_route, mode)
end

describe "routes_with_high_priority_alerts_by_mode/1" do
setup do
{:ok, %{alerts: Factories.Alerts.Alert.build_list(50, :alert)}}
end

test "builds list of routes by mode", %{alerts: alerts} do
stub(Routes.Repo.Mock, :get, fn _ -> Factories.Routes.Route.build(:route) end)

for {mode, routes} <- routes_with_high_priority_alerts_by_mode(alerts) do
# valid mode
assert Routes.Route.types_for_mode(mode)

if routes != [] do
assert [%Routes.Route{} | _] = routes
end
end
end

test "doesn't error if nil routes", %{alerts: alerts} do
stub(Routes.Repo.Mock, :get, fn _ -> nil end)

assert routes_with_high_priority_alerts_by_mode(alerts) == [
subway: [],
bus: [],
commuter_rail: [],
ferry: []
]
end
end

describe "stops_with_access_alerts_by_effect/1" do
setup do
{:ok, %{alerts: Factories.Alerts.Alert.build_list(50, :alert)}}
end

test "builds list of stops by accessibility effect", %{alerts: alerts} do
stub(Stops.Repo.Mock, :get_parent, fn _ -> Factories.Stops.Stop.build(:stop) end)

stops_by_effect = stops_with_access_alerts_by_effect(alerts)

for {effect, stops} <- stops_by_effect do
assert effect in Alerts.Accessibility.effect_types()

if stops != [] do
assert [%Stops.Stop{} | _] = stops
end
end
end

test "doesn't error if nil stops", %{alerts: alerts} do
stub(Stops.Repo.Mock, :get_parent, fn _ -> nil end)

stops_by_effect = stops_with_access_alerts_by_effect(alerts)
assert stops_by_effect == [elevator_closure: [], escalator_closure: [], access_issue: []]
end
end
end
Loading