Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🍎🚫💰 Surface prediction 'uncertainty' values through v3 API #698

Merged
merged 6 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 4 additions & 0 deletions apps/api_web/lib/api_web/views/prediction_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ defmodule ApiWeb.PredictionView do

attributes([
:arrival_time,
:arrival_uncertainty,
:departure_time,
:departure_uncertainty,
:direction_id,
:schedule_relationship,
:status,
Expand Down Expand Up @@ -39,7 +41,9 @@ defmodule ApiWeb.PredictionView do
def attributes(%Model.Prediction{} = p, conn) do
attributes = %{
arrival_time: format_time(p.arrival_time),
arrival_uncertainty: p.arrival_uncertainty,
departure_time: format_time(p.departure_time),
departure_uncertainty: p.departure_uncertainty,
direction_id: p.direction_id,
schedule_relationship: schedule_relationship(p),
status: p.status,
Expand Down
4 changes: 4 additions & 0 deletions apps/api_web/test/api_web/views/prediction_view_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ defmodule ApiWeb.PredictionViewTest do
vehicle_id: "vehicle",
direction_id: 0,
arrival_time: nil,
arrival_uncertainty: nil,
departure_time: @datetime,
departure_uncertainty: 60,
schedule_relationship: :added,
status: "All Aboard",
stop_sequence: 5
Expand Down Expand Up @@ -76,7 +78,9 @@ defmodule ApiWeb.PredictionViewTest do
assert rendered["data"]["attributes"] == %{
"direction_id" => 0,
"arrival_time" => nil,
"arrival_uncertainty" => nil,
"departure_time" => "2016-06-07T00:00:00-04:00",
"departure_uncertainty" => 60,
"status" => "All Aboard",
"schedule_relationship" => "ADDED",
"stop_sequence" => 5
Expand Down
31 changes: 31 additions & 0 deletions apps/model/lib/model/prediction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ defmodule Model.Prediction do
:direction_id,
:route_pattern_id,
:arrival_time,
:arrival_uncertainty,
:departure_time,
:departure_uncertainty,
:stop_sequence,
:schedule_relationship,
:status,
Expand All @@ -39,13 +41,40 @@ defmodule Model.Prediction do
"""
@type schedule_relationship :: :added | :cancelled | :no_data | :skipped | :unscheduled | nil

@typedoc """
Uncertainty value for the arrival time prediction.
Bus and Commuter Rail
See [entities tripUpdate stop_time_updates arrival uncertainty](https://swiftly-inc.stoplight.io/docs/realtime-standalone/613d1d7f1eae3-gtfs-rt-trip-updates)
| Value | Description |
|------------------|-------------|
| < 300 or omitted | Valid real-time prediction |
| 300 | Real-time prediction not available. This code is primarily used when a vehicle has not yet been assigned to the trip, (i.e. because the block has not started yet). It is a schedule-based prediction, but Swiftly adjusts the schedule-based prediction time using observed historical travel times to make predictions more accurate than the schedule |
| 301 | Valid real-time prediction, though the bus appears to be stalled or significantly delayed and predictions are not as accurate |
| > 301 | Likely invalid prediction, recommend not showing anything (and not showing scheduled time), very rare situation |

Subway
See [GTFS `Realtime` `FeedMessage` `FeedEntity` `TripUpdate` `StopTimeUpdate` `arrival`](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-stoptimeupdate).
| Value | Description |
|--------|-------------|
| 60 | A trip that has already started |
| 120 | A terminal/reverse trip departure for a trip that has NOT started and a train is awaiting departure at the origin |
| 360 | A terminal/reverse trip for a trip that has NOT started and a train is completing a previous trip |
"""
@type uncertainty_values :: non_neg_integer | nil

@typedoc """
* `:arrival_time` - When the vehicle is now predicted to arrive. `nil` if the first stop (`stop_id`) on the the trip
(`trip_id`). See
[GTFS `Realtime` `FeedMessage` `FeedEntity` `TripUpdate` `StopTimeUpdate` `arrival`](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-stoptimeupdate).
* `:arrival_uncertainty` - Value representing the uncertainty of arrival prediction (`arrival_time`). See
[entities tripUpdate stop_time_updates arrival uncertainty](https://swiftly-inc.stoplight.io/docs/realtime-standalone/613d1d7f1eae3-gtfs-rt-trip-updates)
[GTFS `Realtime` `FeedMessage` `FeedEntity` `TripUpdate` `StopTimeUpdate` `arrival`](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-stoptimeupdate).
* `:departure_time` - When the vehicle is now predicted to depart. `nil` if the last stop (`stop_id`) on the trip
(`trip_id`). See
[GTFS `Realtime` `FeedMessage` `FeedEntity` `TripUpdate` `StopTimeUpdate` `departure`](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-stoptimeupdate).
* `:departure_uncertainty` - Value representing the uncertainty of departure prediction (`departure_time`). See
[entities tripUpdate stop_time_updates departure uncertainty](https://swiftly-inc.stoplight.io/docs/realtime-standalone/613d1d7f1eae3-gtfs-rt-trip-updates)
[GTFS `Realtime` `FeedMessage` `FeedEntity` `TripUpdate` `StopTimeUpdate` `departure`](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-stoptimeupdate).
* `:direction_id` - Which direction along `route_id` the `trip_id` is going. See
[GTFS `trips.txt` `direction_id`](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#tripstxt).
* `:route_id` - The route `trip_id` is on doing in `direction_id`. See
Expand All @@ -65,7 +94,9 @@ defmodule Model.Prediction do
"""
@type t :: %__MODULE__{
arrival_time: DateTime.t() | nil,
arrival_uncertainty: uncertainty_values,
departure_time: DateTime.t() | nil,
departure_uncertainty: uncertainty_values,
direction_id: Model.Direction.id(),
route_id: Model.Route.id(),
route_pattern_id: Model.RoutePattern.id(),
Expand Down
8 changes: 8 additions & 0 deletions apps/parse/lib/parse/trip_updates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ defmodule Parse.TripUpdates do
| stop_id: copy(update.stop_id),
stop_sequence: update.stop_sequence,
arrival_time: parse_stop_time_event(update.arrival),
arrival_uncertainty: parse_uncertainty(update.arrival),
departure_time: parse_stop_time_event(update.departure),
departure_uncertainty: parse_uncertainty(update.departure),
schedule_relationship:
stop_time_relationship(update.schedule_relationship, base.schedule_relationship)
}
Expand All @@ -54,6 +56,12 @@ defmodule Parse.TripUpdates do
nil
end

defp parse_uncertainty(%{uncertainty: uncertainty}) when is_integer(uncertainty) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since your documentation (nice!) only allows three values, would it make sense to only allow those three values through? I think you can use a guard like when is_integer(uncertainty) and uncertainty in [60, 120, 360] but I haven't tested it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this incorporate the uncertainty values that Swiftly uses?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be mistaken, but I think those values are converted into the 60, 120, 360 versions before they make it here. I'll look into that to verify.

Copy link
Contributor Author

@bfauble bfauble Nov 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like these values are only rail, the swiftly values, which are different, cover bus/CR. Will update docs/types to address.

uncertainty
end

defp parse_uncertainty(_), do: nil

defp vehicle_id(%{id: id}), do: id
defp vehicle_id(_), do: nil

Expand Down
14 changes: 11 additions & 3 deletions apps/parse/test/parse/trip_updates_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ defmodule Parse.TripUpdatesTest do
update = %{
"stop_id" => "place-north",
"stop_sequence" => 6,
"arrival" => %{
"time" => 1_502_290_000
},
"departure" => %{
"time" => 1_502_290_500
"time" => 1_502_290_500,
"uncertainty" => 60
}
}

Expand Down Expand Up @@ -52,7 +56,8 @@ defmodule Parse.TripUpdatesTest do
stop_id: "stop",
stop_sequence: 5,
arrival: %{
time: 1
time: 1,
uncertainty: 60
},
departure: nil,
schedule_relationship: :SCHEDULED
Expand All @@ -71,7 +76,10 @@ defmodule Parse.TripUpdatesTest do
stop_id: "stop",
vehicle_id: "vehicle",
stop_sequence: 5,
arrival_time: %DateTime{}
arrival_time: %DateTime{},
arrival_uncertainty: 60,
departure_time: nil,
departure_uncertainty: nil
} = actual
end

Expand Down