Skip to content
Draft
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
89 changes: 65 additions & 24 deletions lib/dotcom/system_status/subway.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,25 @@ defmodule Dotcom.SystemStatus.Subway do
import Dotcom.Alerts, only: [route_alert?: 2, systemwide_mode_alert?: 2]
alias Alerts.Alert

@affected_stops Application.compile_env!(:dotcom, :affected_stops_module)
@endpoint_stops Application.compile_env!(:dotcom, :endpoint_stops_module)

@type status_time() :: :current | {:future, DateTime.t()}

@type status_t() :: :normal | Dotcom.Alerts.service_effect_t()

@type subheading_data_t() ::
{:affected_stops, [map()]}
| {:endpoint_stops, [tuple()]}
| {:delay}
| nil

@type status_entry() :: %{
alerts: [Alert.t()],
status: status_t(),
multiple: boolean(),
time: status_time()
time: status_time(),
subheading_data: subheading_data_t()
}

@type status_entry_group() :: %{
Expand Down Expand Up @@ -47,17 +57,17 @@ defmodule Dotcom.SystemStatus.Subway do
...> ]
iex> Dotcom.SystemStatus.Subway.subway_status(alerts, Timex.now())
%{
"Blue" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}],
"Blue" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}],
"Orange" => [
%{
branch_ids: [],
status_entries: [
%{time: :current, status: :shuttle, multiple: false, alerts: alerts}
%{time: :current, status: :shuttle, multiple: false, alerts: alerts, subheading_data: {:endpoint_stops, []}}
]
}
],
"Red" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}],
"Green" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}]
"Red" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}],
"Green" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}]
}

Alerts for individual Green line branches are grouped together and
Expand All @@ -75,20 +85,20 @@ defmodule Dotcom.SystemStatus.Subway do
...> ]
iex> Dotcom.SystemStatus.Subway.subway_status(alerts, Timex.now())
%{
"Blue" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}],
"Orange" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}],
"Red" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}],
"Blue" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}],
"Orange" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}],
"Red" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}],
"Green" => [
%{
branch_ids: ["Green-E"],
status_entries: [
%{time: :current, status: :delay, multiple: false, alerts: alerts}
%{time: :current, status: :delay, multiple: false, alerts: alerts, subheading_data: {:delay}}
]
},
%{
branch_ids: ["Green-B", "Green-C", "Green-D"],
status_entries: [
%{time: :current, status: :normal, multiple: false, alerts: []}
%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}
]
}
]
Expand All @@ -108,23 +118,23 @@ defmodule Dotcom.SystemStatus.Subway do
...> ]
iex> Dotcom.SystemStatus.Subway.subway_status(alerts, Timex.now())
%{
"Blue" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}],
"Orange" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}],
"Blue" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}],
"Orange" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}],
"Red" => [
%{
branch_ids: [],
status_entries: [
%{time: :current, status: :normal, multiple: false, alerts: []}
%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil }
]
},
%{
branch_ids: ["Mattapan"],
status_entries: [
%{time: :current, status: :suspension, multiple: false, alerts: alerts}
%{time: :current, status: :suspension, multiple: false, alerts: alerts, subheading_data: {:endpoint_stops, []}}
]
}
],
"Green" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: []}]}]
"Green" => [%{branch_ids: [], status_entries: [%{time: :current, status: :normal, multiple: false, alerts: [], subheading_data: nil}]}]
}
"""
@spec subway_status([Alert.t()], DateTime.t()) :: %{Routes.Route.id_t() => status_entry_group()}
Expand Down Expand Up @@ -185,7 +195,10 @@ defmodule Dotcom.SystemStatus.Subway do
|> alerts_for_routes(alerts)
|> Enum.group_by(&affected_green_line_branch_ids/1)
|> Enum.map(fn {branch_ids, alerts} ->
%{branch_ids: branch_ids, status_entries: alerts_to_statuses(alerts, time)}
%{
branch_ids: branch_ids,
status_entries: alerts |> alerts_to_statuses(time, branch_ids)
}
end)
|> maybe_add_normal_entry()
|> Enum.map(&maybe_collapse_branch_ids/1)
Expand Down Expand Up @@ -302,8 +315,7 @@ defmodule Dotcom.SystemStatus.Subway do
[]

mattapan_alerts ->
mattapan_statuses = mattapan_alerts |> alerts_to_statuses(time)

mattapan_statuses = mattapan_alerts |> alerts_to_statuses(time, ["Mattapan"])
[status_entry_group(mattapan_statuses, ["Mattapan"])]
end
end
Expand All @@ -314,7 +326,7 @@ defmodule Dotcom.SystemStatus.Subway do
defp statuses_for_route(route_id, alerts, time) do
route_id
|> alerts_for_route(alerts)
|> alerts_to_statuses(time)
|> alerts_to_statuses(time, [route_id])
end

# Returns a status_entry_group, to be used in the
Expand Down Expand Up @@ -357,12 +369,40 @@ defmodule Dotcom.SystemStatus.Subway do
# - Identical alerts are grouped together and pluralized.
# - Times are given as a kitchen-formatted string, nil, or "Now".
# - Statuses are sorted alphabetically.
@spec alerts_to_statuses([Alert.t()], DateTime.t()) :: [status_entry()]
defp alerts_to_statuses(alerts, time) do
# - Subheading information is added
@spec alerts_to_statuses([Alert.t()], DateTime.t(), [Routes.Route.id_t()]) :: [status_entry()]
defp alerts_to_statuses(alerts, time, route_ids) do
alerts
|> alerts_to_statuses_naive(time)
|> consolidate_duplicates()
|> sort_statuses()
|> Enum.map(&add_subheading_data(&1, route_ids))
end

@spec add_subheading_data(status_entry(), [Routes.Route.id_t()]) :: status_entry()
defp add_subheading_data(%{status: :station_closure, alerts: alerts} = entry, route_ids)
when route_ids != [] do
%{
entry
| subheading_data: {:affected_stops, @affected_stops.affected_stops(alerts, route_ids)}
}
end

defp add_subheading_data(%{status: :delay} = entry, _route_ids) do
%{entry | subheading_data: {:delay}}
end

defp add_subheading_data(%{status: status, alerts: alerts} = entry, route_ids)
when status in [:service_change, :shuttle, :single_tracking, :suspension] and
route_ids != [] do
%{
entry
| subheading_data: {:endpoint_stops, @endpoint_stops.endpoint_stops(alerts, route_ids)}
}
end

defp add_subheading_data(entry, _route_ids) do
%{entry | subheading_data: nil}
end

# Naively maps a list of alerts to a list of statuses, where a
Expand All @@ -389,7 +429,7 @@ defmodule Dotcom.SystemStatus.Subway do

@spec normal_status() :: status_entry()
defp normal_status() do
%{multiple: false, status: :normal, time: :current, alerts: []}
%{multiple: false, status: :normal, time: :current, alerts: [], subheading_data: nil}
end

# Translates an alert to a status:
Expand All @@ -400,7 +440,7 @@ defmodule Dotcom.SystemStatus.Subway do
@spec alert_to_status(Alert.t(), DateTime.t()) :: status_entry()
defp alert_to_status(alert, time) do
time = future_start_time(alert.active_period, time)
%{alerts: [alert], multiple: false, status: alert.effect, time: time}
%{alerts: [alert], multiple: false, status: alert.effect, time: time, subheading_data: nil}
end

# - If the active period is in the future, returns its start_time.
Expand Down Expand Up @@ -441,7 +481,8 @@ defmodule Dotcom.SystemStatus.Subway do
time: time,
status: effect,
multiple: length(grouped_statuses) > 1,
alerts: Enum.flat_map(grouped_statuses, & &1.alerts) |> Enum.uniq()
alerts: Enum.flat_map(grouped_statuses, & &1.alerts) |> Enum.uniq(),
subheading_data: nil
}
end)
end
Expand Down
21 changes: 11 additions & 10 deletions lib/dotcom_web/components/system_status/status_row_heading.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ defmodule DotcomWeb.Components.SystemStatus.StatusRowHeading do

alias Alerts.Alert

@affected_stops Application.compile_env!(:dotcom, :affected_stops_module)
@endpoint_stops Application.compile_env!(:dotcom, :endpoint_stops_module)

attr :alerts, :list, default: []
attr :future, :boolean, default: false
attr :hide_route_pill, :boolean, default: false
Expand All @@ -23,6 +20,7 @@ defmodule DotcomWeb.Components.SystemStatus.StatusRowHeading do
attr :prefix, :string, default: nil
attr :route_ids, :list, required: true
attr :status, :atom, required: true
attr :subheading_data, :any, default: nil

def status_row_heading(assigns) do
%{
Expand Down Expand Up @@ -112,16 +110,21 @@ defmodule DotcomWeb.Components.SystemStatus.StatusRowHeading do
"""
end

defp decorations(%{status: :station_closure, alerts: alerts, route_ids: route_ids}) do
affected_stops = @affected_stops.affected_stops(alerts, route_ids)

defp decorations(%{
status: :station_closure,
subheading_data: {:affected_stops, affected_stops}
}) do
%{
plural: affected_stops |> Enum.count() > 1,
subheading_text: affected_stops |> humanize_affected_stops()
}
end

defp decorations(%{status: :delay, alerts: alerts}) do
defp decorations(%{
status: :delay,
alerts: alerts,
subheading_data: {:delay}
}) do
all_single_tracking? = alerts |> Enum.all?(&(&1.cause == :single_tracking))

subheading_text = if all_single_tracking?, do: ~t"Due to Single Tracking"
Expand All @@ -131,10 +134,8 @@ defmodule DotcomWeb.Components.SystemStatus.StatusRowHeading do
}
end

defp decorations(%{status: status, alerts: alerts, route_ids: route_ids})
defp decorations(%{status: status, subheading_data: {:endpoint_stops, endpoints}})
when status in [:service_change, :shuttle, :single_tracking, :suspension] do
endpoints = @endpoint_stops.endpoint_stops(alerts, route_ids)

%{
subheading_text: endpoints |> humanize_endpoint_list(),
subheading_aria_label: endpoints |> humanize_endpoint_list_a11y()
Expand Down
37 changes: 32 additions & 5 deletions lib/dotcom_web/components/system_status/subway_status.ex
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatus do
end

defp heading(assigns) do
subheading_data = get_in(assigns.row, [:status_entry, :subheading_data])
assigns = assigns |> assign(:subheading_data, subheading_data)

~H"""
<.status_row_heading
alerts={@row |> Map.get(:alerts)}
Expand All @@ -101,6 +104,7 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatus do
prefix={@row.status_entry.prefix}
plural={@row.status_entry.plural}
future={@row.status_entry.future}
subheading_data={@subheading_data}
route_ids={[@row.route_info.route_id | @row.route_info.branch_ids]}
/>
"""
Expand Down Expand Up @@ -192,14 +196,35 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatus do
end

defp combine_status_entries(
%{status: status1, prefix: prefix1, future: future1},
%{status: status2, prefix: prefix2, future: future2}
%{status: status1, prefix: prefix1, future: future1} = status_entry1,
%{status: status2, prefix: prefix2, future: future2} = status_entry2
)
when status1 == status2 and prefix1 == prefix2 and future1 == future2,
do: %{status: status1, prefix: prefix1, plural: true, future: future1}
do: %{
status: status1,
prefix: prefix1,
plural: true,
future: future1,
subheading_data:
combine_subheading_data(
Map.get(status_entry1, :subheading_data),
Map.get(status_entry2, :subheading_data)
)
}

defp combine_status_entries(_status_entry1, _status_entry2), do: see_alerts_status()

defp combine_subheading_data({:affected_stops, stops1}, {:affected_stops, stops2}),
do: {:affected_stops, stops1 ++ stops2}

defp combine_subheading_data({:endpoint_stops, endpoints1}, {:endpoint_stops, endpoints2}),
do: {:endpoint_stops, endpoints1 ++ endpoints2}

defp combine_subheading_data({:delay}, {:delay}), do: {:delay}
defp combine_subheading_data(data1, nil), do: data1
defp combine_subheading_data(nil, data2), do: data2
defp combine_subheading_data(data1, _data2), do: data1

defp add_url(row) do
route_id = route_id_from_route_info(row.route_info)
sub_page = if normal?(row.status_entry), do: "line", else: "alerts"
Expand Down Expand Up @@ -267,7 +292,8 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatus do
status: alert.effect,
plural: false,
prefix: prefix,
future: future?(status_entry)
future: future?(status_entry),
subheading_data: status_entry.subheading_data
},
style: %{
hide_route_pill: true
Expand All @@ -287,7 +313,8 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatus do
status: status,
plural: multiple,
prefix: prefix,
future: future?(status_entry)
future: future?(status_entry),
subheading_data: status_entry.subheading_data
},
style: %{
hide_route_pill: true
Expand Down
2 changes: 2 additions & 0 deletions test/dotcom/system_status/subway_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ defmodule Dotcom.SystemStatus.SubwayTest do
@lines_without_branches List.delete(Subway.lines(), "Green")

setup _ do
Mox.stub(Dotcom.Alerts.AffectedStops.Mock, :affected_stops, fn _, _ -> [] end)
Mox.stub(Dotcom.Alerts.EndpointStops.Mock, :endpoint_stops, fn _, _ -> [] end)
Mox.stub_with(Dotcom.Utils.DateTime.Mock, Dotcom.Utils.DateTime)
:ok
end
Expand Down
2 changes: 2 additions & 0 deletions test/dotcom/system_status_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ defmodule Dotcom.SystemStatusTest do
setup :verify_on_exit!

setup _ do
stub(Dotcom.Alerts.AffectedStops.Mock, :affected_stops, fn _, _ -> [] end)
stub(Dotcom.Alerts.EndpointStops.Mock, :endpoint_stops, fn _, _ -> [] end)
stub_with(Dotcom.Utils.DateTime.Mock, Dotcom.Utils.DateTime)
:ok
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -774,10 +774,15 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatusTest do

stops = Factories.Stops.Stop.build_list(2, :stop)

expect(Dotcom.Alerts.AffectedStops.Mock, :affected_stops, fn
[_], _ ->
[affected_branch1 | affected_branch2] = affected_branches

expect(Dotcom.Alerts.AffectedStops.Mock, :affected_stops, 2, fn
[_], [^affected_branch1] ->
stops |> Enum.take(1) |> Enum.map(&%{stop: &1, direction: :all})

[_], ^affected_branch2 ->
stops |> Enum.at(1) |> then(&[%{stop: &1, direction: :all}])

[_, _], _ ->
stops |> Enum.map(&%{stop: &1, direction: :all})
end)
Expand Down
Loading