From 7f1dc9fd5b46c077d99e6cb58ea3fe821b5935fa Mon Sep 17 00:00:00 2001 From: Paul Kim Date: Thu, 31 Aug 2023 12:57:09 -0400 Subject: [PATCH 1/3] Revert "Determine emptier car location and crowding description (#677)" (#684) This reverts commit 740f0c1c618cbf3636c654a0f1053e098f3ba4fd. --- lib/content/audio/approaching.ex | 6 +- lib/content/audio/predictions.ex | 67 ++++++++++------ lib/content/audio/train_is_arriving.ex | 5 +- lib/content/message/predictions.ex | 103 +++---------------------- 4 files changed, 58 insertions(+), 123 deletions(-) diff --git a/lib/content/audio/approaching.ex b/lib/content/audio/approaching.ex index efbd5ac6c..c5e8fdbbe 100644 --- a/lib/content/audio/approaching.ex +++ b/lib/content/audio/approaching.ex @@ -7,16 +7,14 @@ defmodule Content.Audio.Approaching do alias PaEss.Utilities @enforce_keys [:destination] - defstruct @enforce_keys ++ - [:trip_id, :platform, :route_id, new_cars?: false, crowding_description: nil] + defstruct @enforce_keys ++ [:trip_id, :platform, :route_id, new_cars?: false] @type t :: %__MODULE__{ destination: PaEss.destination(), trip_id: Predictions.Prediction.trip_id() | nil, platform: Content.platform() | nil, route_id: String.t() | nil, - new_cars?: boolean, - crowding_description: {atom(), atom()} | nil + new_cars?: boolean } defimpl Content.Audio do diff --git a/lib/content/audio/predictions.ex b/lib/content/audio/predictions.ex index 4d6865ff7..11639cfd3 100644 --- a/lib/content/audio/predictions.ex +++ b/lib/content/audio/predictions.ex @@ -46,34 +46,51 @@ defmodule Content.Audio.Predictions do ] predictions.minutes == :arriving -> - [ - %TrainIsArriving{ - destination: predictions.destination, - trip_id: predictions.trip_id, - platform: predictions.platform, - route_id: predictions.route_id, - crowding_description: - if(predictions.crowding_data_confidence == :high, - do: predictions.crowding_description - ) - } - ] + if predictions.crowding_data_confidence == :high do + # TODO: Pass along crowding data classification once available + [ + %TrainIsArriving{ + destination: predictions.destination, + trip_id: predictions.trip_id, + platform: predictions.platform, + route_id: predictions.route_id + } + ] + else + [ + %TrainIsArriving{ + destination: predictions.destination, + trip_id: predictions.trip_id, + platform: predictions.platform, + route_id: predictions.route_id + } + ] + end predictions.minutes == :approaching and (line == :top or multi_source?) and predictions.route_id in @heavy_rail_routes -> - [ - %Approaching{ - destination: predictions.destination, - trip_id: predictions.trip_id, - platform: predictions.platform, - route_id: predictions.route_id, - new_cars?: predictions.new_cars?, - crowding_description: - if(predictions.crowding_data_confidence == :high, - do: predictions.crowding_description - ) - } - ] + if predictions.crowding_data_confidence == :high do + # TODO: Pass along crowding data classification once available + [ + %Approaching{ + destination: predictions.destination, + trip_id: predictions.trip_id, + platform: predictions.platform, + route_id: predictions.route_id, + new_cars?: predictions.new_cars? + } + ] + else + [ + %Approaching{ + destination: predictions.destination, + trip_id: predictions.trip_id, + platform: predictions.platform, + route_id: predictions.route_id, + new_cars?: predictions.new_cars? + } + ] + end predictions.minutes == :approaching -> [ diff --git a/lib/content/audio/train_is_arriving.ex b/lib/content/audio/train_is_arriving.ex index 4e1cdcdf2..4b96d1d15 100644 --- a/lib/content/audio/train_is_arriving.ex +++ b/lib/content/audio/train_is_arriving.ex @@ -7,14 +7,13 @@ defmodule Content.Audio.TrainIsArriving do alias PaEss.Utilities @enforce_keys [:destination] - defstruct @enforce_keys ++ [:trip_id, :platform, :route_id, crowding_description: nil] + defstruct @enforce_keys ++ [:trip_id, :platform, :route_id] @type t :: %__MODULE__{ destination: PaEss.destination(), trip_id: Predictions.Prediction.trip_id() | nil, platform: Content.platform() | nil, - route_id: String.t() | nil, - crowding_description: {atom(), atom()} | nil + route_id: String.t() | nil } defimpl Content.Audio do diff --git a/lib/content/message/predictions.ex b/lib/content/message/predictions.ex index a142b1ae6..648d267d5 100644 --- a/lib/content/message/predictions.ex +++ b/lib/content/message/predictions.ex @@ -32,8 +32,7 @@ defmodule Content.Message.Predictions do new_cars?: false, terminal?: false, certainty: nil, - crowding_data_confidence: nil, - crowding_description: nil + crowding_data_confidence: nil ] @type t :: %__MODULE__{ @@ -50,8 +49,7 @@ defmodule Content.Message.Predictions do platform: Content.platform() | nil, terminal?: boolean(), certainty: non_neg_integer() | nil, - crowding_data_confidence: :high | :low | nil, - crowding_description: {atom(), atom()} | nil + crowding_data_confidence: :high | :low | nil } @spec non_terminal( @@ -81,7 +79,13 @@ defmodule Content.Message.Predictions do true -> predicted_time |> Kernel./(60) |> round() end - {crowding_data_confidence, crowding_description} = do_crowding(prediction) + crowding_data_confidence = + calculate_crowding_data_confidence( + prediction, + Engine.Locations.for_vehicle(prediction.vehicle_id) + ) + + # TODO: Calculate crowding data classification and pass that along as well case Content.Utilities.destination_for_prediction( prediction.route_id, @@ -102,8 +106,7 @@ defmodule Content.Message.Predictions do zone: zone, platform: platform, certainty: certainty, - crowding_data_confidence: crowding_data_confidence, - crowding_description: crowding_description + crowding_data_confidence: crowding_data_confidence } {:error, _} -> @@ -151,21 +154,10 @@ defmodule Content.Message.Predictions do end end - defp do_crowding(prediction) when prediction.route_id in ["Orange"] do - case Engine.Locations.for_vehicle(prediction.vehicle_id) do - %Locations.Location{} = location -> - {calculate_crowding_data_confidence(prediction, location), - get_crowding_description(location.multi_carriage_details)} - - _ -> - {nil, nil} - end - end - - defp do_crowding(_), do: {nil, nil} + defp calculate_crowding_data_confidence(_prediction, nil), do: nil defp calculate_crowding_data_confidence(prediction, location) - when location.stop_id == prediction.stop_id do + when prediction.route_id in ["Orange"] and location.stop_id == prediction.stop_id do if location.status in [:incoming_at, :in_transit_to], do: :high, else: :low @@ -173,77 +165,6 @@ defmodule Content.Message.Predictions do defp calculate_crowding_data_confidence(_prediction, _location), do: nil - defp get_crowding_description([]), do: nil - - defp get_crowding_description(carriage_details) do - crowding_levels = - Enum.map(carriage_details, &occupancy_status_to_crowding_level(&1.occupancy_status)) - - min_crowding_level = Enum.min(crowding_levels) - - relative_crowding_levels = - for crowding_level <- crowding_levels do - if crowding_level == min_crowding_level, - do: :e, - else: :f - end - - {get_emptier_location( - {Enum.count(relative_crowding_levels, &Kernel.==(&1, :e)), relative_crowding_levels} - ), crowding_level_to_atom(min_crowding_level)} - end - - defp occupancy_status_to_crowding_level(occupancy_status) do - case occupancy_status do - :many_seats_available -> 1 - :few_seats_available -> 1 - :standing_room_only -> 2 - :crushed_standing_room_only -> 3 - :full -> 3 - _ -> 4 - end - end - - defp crowding_level_to_atom(crowding_level) do - case crowding_level do - 1 -> :not_crowded - 2 -> :some_crowding - 3 -> :crowded - 4 -> :unknown_croding - end - end - - defp get_emptier_location(car_crowding_levels) do - case car_crowding_levels do - {1, [_, _, :f, :f, :f, :f]} -> :front - {1, [:f, :f, :f, :f, _, _]} -> :back - {1, [:f, :f, _, _, :f, :f]} -> :middle - {2, [_, _, _, :f, :f, :f]} -> :front - {2, [_, :f, :f, _, :f, :f]} -> :front - {2, [:f, :f, :f, _, _, _]} -> :back - {2, [:f, :f, _, :f, :f, _]} -> :back - {2, [_, _, :f, :f, _, _]} -> :front_and_back - {2, _} -> :middle - {3, [:f, _, _, _, _, :f]} -> :middle - {3, [_, _, _, _, :f, :f]} -> :front - {3, [:f, :f, _, _, _, _]} -> :back - {3, [:f, _, :f, _, :f, _]} -> :train_level - {3, [_, :f, _, :f, _, :f]} -> :train_level - {3, _} -> :front_and_back - {4, [:f, _, _, _, _, :f]} -> :middle - {4, [_, _, _, _, _, :f]} -> :front - {4, [_, _, _, :f, :f, _]} -> :front - {4, [:f, _, _, _, _, _]} -> :back - {4, [_, :f, :f, _, _, _]} -> :back - {4, [_, _, :f, :f, _, _]} -> :front_and_back - {4, _} -> :train_level - {5, [_, _, _, _, _, :f]} -> :front - {5, [:f, _, _, _, _, _]} -> :back - {5, _} -> :train_level - {6, _} -> :train_level - end - end - defimpl Content.Message do require Logger From 097ea6c2eb187bda01ee288a8eece642529ec2ca Mon Sep 17 00:00:00 2001 From: Brett Heath-Wlaz Date: Thu, 31 Aug 2023 13:21:52 -0400 Subject: [PATCH 2/3] suppress terminal and reverse predictions for RL (#683) --- lib/predictions/predictions.ex | 13 ++--- test/predictions/predictions_test.exs | 68 +-------------------------- 2 files changed, 5 insertions(+), 76 deletions(-) diff --git a/lib/predictions/predictions.ex b/lib/predictions/predictions.ex index fb2377f34..4079109a7 100644 --- a/lib/predictions/predictions.ex +++ b/lib/predictions/predictions.ex @@ -151,16 +151,11 @@ defmodule Predictions.Predictions do end @spec sufficient_certainty?(map(), String.t()) :: boolean() - defp sufficient_certainty?(_stop_time_event, route_id) - when route_id in ["Mattapan", "Green-B", "Green-C", "Green-D", "Green-E"] do - true + defp sufficient_certainty?(stop_time_event, route_id) when route_id in ["Red"] do + is_nil(stop_time_event["uncertainty"]) or stop_time_event["uncertainty"] < 120 end - defp sufficient_certainty?(stop_time_event, _route_id) do - if Application.get_env(:realtime_signs, :filter_uncertain_predictions?) do - is_nil(stop_time_event["uncertainty"]) or stop_time_event["uncertainty"] <= 300 - else - true - end + defp sufficient_certainty?(_stop_time_event, _route_id) do + true end end diff --git a/test/predictions/predictions_test.exs b/test/predictions/predictions_test.exs index b66e5c4ca..1a573cf94 100644 --- a/test/predictions/predictions_test.exs +++ b/test/predictions/predictions_test.exs @@ -882,7 +882,7 @@ defmodule Predictions.PredictionsTest do "timestamp" => nil, "trip" => %{ "direction_id" => 0, - "route_id" => "Blue", + "route_id" => "Red", "schedule_relationship" => "SCHEDULED", "start_date" => "20170329", "start_time" => nil, @@ -909,72 +909,6 @@ defmodule Predictions.PredictionsTest do assert predictions_map == %{} end - test "doesn't filter predictions with high uncertainty when feature is off" do - reassign_env(:filter_uncertain_predictions?, false) - - feed_message = %{ - "entity" => [ - %{ - "alert" => nil, - "id" => "1490783458_32568935", - "is_deleted" => false, - "trip_update" => %{ - "delay" => nil, - "stop_time_update" => [ - %{ - "arrival" => %{ - "delay" => nil, - "time" => 1_491_570_110, - "uncertainty" => 360 - }, - "departure" => %{ - "delay" => nil, - "time" => 1_491_570_120, - "uncertainty" => 360 - }, - "schedule_relationship" => "SCHEDULED", - "stop_id" => "70063", - "stop_sequence" => 1, - "stops_away" => 1, - "stopped?" => true, - "boarding_status" => "Stopped 1 stop away" - } - ], - "timestamp" => nil, - "trip" => %{ - "direction_id" => 0, - "route_id" => "Red", - "schedule_relationship" => "SCHEDULED", - "start_date" => "20170329", - "start_time" => nil, - "trip_id" => "32568935" - }, - "vehicle" => %{ - "id" => "R-54639F6C", - "label" => "1631", - "license_plate" => nil - } - }, - "vehicle" => nil - } - ], - "header" => %{ - "gtfs_realtime_version" => "1.0", - "incrementality" => "FULL_DATASET", - "timestamp" => 1_490_783_458 - } - } - - assert {%{ - {"70063", 0} => [ - %Predictions.Prediction{ - seconds_until_arrival: 110, - seconds_until_departure: 120 - } - ] - }, _} = get_all(feed_message, @current_time) - end - test "doesn't filter out uncertain light rail predictions" do reassign_env(:filter_uncertain_predictions?, true) From 72220b400a23116d7e6348eeb47cfdfea8872f58 Mon Sep 17 00:00:00 2001 From: Paul Kim Date: Thu, 31 Aug 2023 14:08:33 -0400 Subject: [PATCH 3/3] Revert "Revert "Determine emptier car location and crowding description"" (#685) * Revert "Revert "Determine emptier car location and crowding description (#677)" (#684)" This reverts commit 7f1dc9fd5b46c077d99e6cb58ea3fe821b5935fa. * Add default empty list for multi_carriage_details * simplify parsing code --- lib/content/audio/approaching.ex | 6 +- lib/content/audio/predictions.ex | 67 ++++++---------- lib/content/audio/train_is_arriving.ex | 5 +- lib/content/message/predictions.ex | 103 ++++++++++++++++++++++--- lib/engine/locations.ex | 20 +++-- 5 files changed, 132 insertions(+), 69 deletions(-) diff --git a/lib/content/audio/approaching.ex b/lib/content/audio/approaching.ex index c5e8fdbbe..efbd5ac6c 100644 --- a/lib/content/audio/approaching.ex +++ b/lib/content/audio/approaching.ex @@ -7,14 +7,16 @@ defmodule Content.Audio.Approaching do alias PaEss.Utilities @enforce_keys [:destination] - defstruct @enforce_keys ++ [:trip_id, :platform, :route_id, new_cars?: false] + defstruct @enforce_keys ++ + [:trip_id, :platform, :route_id, new_cars?: false, crowding_description: nil] @type t :: %__MODULE__{ destination: PaEss.destination(), trip_id: Predictions.Prediction.trip_id() | nil, platform: Content.platform() | nil, route_id: String.t() | nil, - new_cars?: boolean + new_cars?: boolean, + crowding_description: {atom(), atom()} | nil } defimpl Content.Audio do diff --git a/lib/content/audio/predictions.ex b/lib/content/audio/predictions.ex index 11639cfd3..4d6865ff7 100644 --- a/lib/content/audio/predictions.ex +++ b/lib/content/audio/predictions.ex @@ -46,51 +46,34 @@ defmodule Content.Audio.Predictions do ] predictions.minutes == :arriving -> - if predictions.crowding_data_confidence == :high do - # TODO: Pass along crowding data classification once available - [ - %TrainIsArriving{ - destination: predictions.destination, - trip_id: predictions.trip_id, - platform: predictions.platform, - route_id: predictions.route_id - } - ] - else - [ - %TrainIsArriving{ - destination: predictions.destination, - trip_id: predictions.trip_id, - platform: predictions.platform, - route_id: predictions.route_id - } - ] - end + [ + %TrainIsArriving{ + destination: predictions.destination, + trip_id: predictions.trip_id, + platform: predictions.platform, + route_id: predictions.route_id, + crowding_description: + if(predictions.crowding_data_confidence == :high, + do: predictions.crowding_description + ) + } + ] predictions.minutes == :approaching and (line == :top or multi_source?) and predictions.route_id in @heavy_rail_routes -> - if predictions.crowding_data_confidence == :high do - # TODO: Pass along crowding data classification once available - [ - %Approaching{ - destination: predictions.destination, - trip_id: predictions.trip_id, - platform: predictions.platform, - route_id: predictions.route_id, - new_cars?: predictions.new_cars? - } - ] - else - [ - %Approaching{ - destination: predictions.destination, - trip_id: predictions.trip_id, - platform: predictions.platform, - route_id: predictions.route_id, - new_cars?: predictions.new_cars? - } - ] - end + [ + %Approaching{ + destination: predictions.destination, + trip_id: predictions.trip_id, + platform: predictions.platform, + route_id: predictions.route_id, + new_cars?: predictions.new_cars?, + crowding_description: + if(predictions.crowding_data_confidence == :high, + do: predictions.crowding_description + ) + } + ] predictions.minutes == :approaching -> [ diff --git a/lib/content/audio/train_is_arriving.ex b/lib/content/audio/train_is_arriving.ex index 4b96d1d15..4e1cdcdf2 100644 --- a/lib/content/audio/train_is_arriving.ex +++ b/lib/content/audio/train_is_arriving.ex @@ -7,13 +7,14 @@ defmodule Content.Audio.TrainIsArriving do alias PaEss.Utilities @enforce_keys [:destination] - defstruct @enforce_keys ++ [:trip_id, :platform, :route_id] + defstruct @enforce_keys ++ [:trip_id, :platform, :route_id, crowding_description: nil] @type t :: %__MODULE__{ destination: PaEss.destination(), trip_id: Predictions.Prediction.trip_id() | nil, platform: Content.platform() | nil, - route_id: String.t() | nil + route_id: String.t() | nil, + crowding_description: {atom(), atom()} | nil } defimpl Content.Audio do diff --git a/lib/content/message/predictions.ex b/lib/content/message/predictions.ex index 648d267d5..a142b1ae6 100644 --- a/lib/content/message/predictions.ex +++ b/lib/content/message/predictions.ex @@ -32,7 +32,8 @@ defmodule Content.Message.Predictions do new_cars?: false, terminal?: false, certainty: nil, - crowding_data_confidence: nil + crowding_data_confidence: nil, + crowding_description: nil ] @type t :: %__MODULE__{ @@ -49,7 +50,8 @@ defmodule Content.Message.Predictions do platform: Content.platform() | nil, terminal?: boolean(), certainty: non_neg_integer() | nil, - crowding_data_confidence: :high | :low | nil + crowding_data_confidence: :high | :low | nil, + crowding_description: {atom(), atom()} | nil } @spec non_terminal( @@ -79,13 +81,7 @@ defmodule Content.Message.Predictions do true -> predicted_time |> Kernel./(60) |> round() end - crowding_data_confidence = - calculate_crowding_data_confidence( - prediction, - Engine.Locations.for_vehicle(prediction.vehicle_id) - ) - - # TODO: Calculate crowding data classification and pass that along as well + {crowding_data_confidence, crowding_description} = do_crowding(prediction) case Content.Utilities.destination_for_prediction( prediction.route_id, @@ -106,7 +102,8 @@ defmodule Content.Message.Predictions do zone: zone, platform: platform, certainty: certainty, - crowding_data_confidence: crowding_data_confidence + crowding_data_confidence: crowding_data_confidence, + crowding_description: crowding_description } {:error, _} -> @@ -154,10 +151,21 @@ defmodule Content.Message.Predictions do end end - defp calculate_crowding_data_confidence(_prediction, nil), do: nil + defp do_crowding(prediction) when prediction.route_id in ["Orange"] do + case Engine.Locations.for_vehicle(prediction.vehicle_id) do + %Locations.Location{} = location -> + {calculate_crowding_data_confidence(prediction, location), + get_crowding_description(location.multi_carriage_details)} + + _ -> + {nil, nil} + end + end + + defp do_crowding(_), do: {nil, nil} defp calculate_crowding_data_confidence(prediction, location) - when prediction.route_id in ["Orange"] and location.stop_id == prediction.stop_id do + when location.stop_id == prediction.stop_id do if location.status in [:incoming_at, :in_transit_to], do: :high, else: :low @@ -165,6 +173,77 @@ defmodule Content.Message.Predictions do defp calculate_crowding_data_confidence(_prediction, _location), do: nil + defp get_crowding_description([]), do: nil + + defp get_crowding_description(carriage_details) do + crowding_levels = + Enum.map(carriage_details, &occupancy_status_to_crowding_level(&1.occupancy_status)) + + min_crowding_level = Enum.min(crowding_levels) + + relative_crowding_levels = + for crowding_level <- crowding_levels do + if crowding_level == min_crowding_level, + do: :e, + else: :f + end + + {get_emptier_location( + {Enum.count(relative_crowding_levels, &Kernel.==(&1, :e)), relative_crowding_levels} + ), crowding_level_to_atom(min_crowding_level)} + end + + defp occupancy_status_to_crowding_level(occupancy_status) do + case occupancy_status do + :many_seats_available -> 1 + :few_seats_available -> 1 + :standing_room_only -> 2 + :crushed_standing_room_only -> 3 + :full -> 3 + _ -> 4 + end + end + + defp crowding_level_to_atom(crowding_level) do + case crowding_level do + 1 -> :not_crowded + 2 -> :some_crowding + 3 -> :crowded + 4 -> :unknown_croding + end + end + + defp get_emptier_location(car_crowding_levels) do + case car_crowding_levels do + {1, [_, _, :f, :f, :f, :f]} -> :front + {1, [:f, :f, :f, :f, _, _]} -> :back + {1, [:f, :f, _, _, :f, :f]} -> :middle + {2, [_, _, _, :f, :f, :f]} -> :front + {2, [_, :f, :f, _, :f, :f]} -> :front + {2, [:f, :f, :f, _, _, _]} -> :back + {2, [:f, :f, _, :f, :f, _]} -> :back + {2, [_, _, :f, :f, _, _]} -> :front_and_back + {2, _} -> :middle + {3, [:f, _, _, _, _, :f]} -> :middle + {3, [_, _, _, _, :f, :f]} -> :front + {3, [:f, :f, _, _, _, _]} -> :back + {3, [:f, _, :f, _, :f, _]} -> :train_level + {3, [_, :f, _, :f, _, :f]} -> :train_level + {3, _} -> :front_and_back + {4, [:f, _, _, _, _, :f]} -> :middle + {4, [_, _, _, _, _, :f]} -> :front + {4, [_, _, _, :f, :f, _]} -> :front + {4, [:f, _, _, _, _, _]} -> :back + {4, [_, :f, :f, _, _, _]} -> :back + {4, [_, _, :f, :f, _, _]} -> :front_and_back + {4, _} -> :train_level + {5, [_, _, _, _, _, :f]} -> :front + {5, [:f, _, _, _, _, _]} -> :back + {5, _} -> :train_level + {6, _} -> :train_level + end + end + defimpl Content.Message do require Logger diff --git a/lib/engine/locations.ex b/lib/engine/locations.ex index b394d5eec..f2c635fbc 100644 --- a/lib/engine/locations.ex +++ b/lib/engine/locations.ex @@ -142,21 +142,19 @@ defmodule Engine.Locations do trip_id: location["vehicle"]["trip"]["trip_id"], consist: location["vehicle"]["vehicle"]["consist"], multi_carriage_details: - location["vehicle"]["multi_carriage_details"] && - Enum.map( - location["vehicle"]["multi_carriage_details"], - &parse_carriage_details/1 - ) + parse_carriage_details(location["vehicle"]["multi_carriage_details"] || []) } end defp parse_carriage_details(multi_carriage_details) do - %Locations.CarriageDetails{ - label: multi_carriage_details["label"], - occupancy_status: occupancy_status_to_atom(multi_carriage_details["occupancy_status"]), - occupancy_percentage: multi_carriage_details["occupancy_percentage"], - carriage_sequence: multi_carriage_details["carriage_sequence"] - } + Enum.map(multi_carriage_details, fn carriage_details -> + %Locations.CarriageDetails{ + label: carriage_details["label"], + occupancy_status: occupancy_status_to_atom(carriage_details["occupancy_status"]), + occupancy_percentage: carriage_details["occupancy_percentage"], + carriage_sequence: carriage_details["carriage_sequence"] + } + end) end defp occupancy_status_to_atom(status) do