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

test(Predictions): Added the mox for predictions #2036

Merged
merged 21 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ config :dotcom, :cms_api_module, CMS.Api
config :dotcom, :httpoison, HTTPoison

config :dotcom, :mbta_api_module, MBTA.Api
config :dotcom, :repo_modules, route_patterns: RoutePatterns.Repo

config :dotcom, :repo_modules,
predictions: Predictions.Repo,
route_patterns: RoutePatterns.Repo

config :dotcom, :redis, Dotcom.Cache.Multilevel.Redis
config :dotcom, :redix, Redix
Expand Down
5 changes: 4 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ config :dotcom, :httpoison, HTTPoison.Mock

config :dotcom, :cms_api_module, CMS.Api.Static
config :dotcom, :mbta_api_module, MBTA.Api.Mock
config :dotcom, :repo_modules, route_patterns: RoutePatterns.Repo.Mock

config :dotcom, :repo_modules,
predictions: Predictions.Repo.Mock,
route_patterns: RoutePatterns.Repo.Mock

config :dotcom, :redis, Dotcom.Redis.Mock
config :dotcom, :redix, Dotcom.Redix.Mock
Expand Down
19 changes: 9 additions & 10 deletions lib/dotcom/realtime_schedule.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ defmodule Dotcom.RealtimeSchedule do
alias Dotcom.JsonHelpers
alias Dotcom.TransitNearMe
alias Predictions.Prediction
alias Predictions.Repo, as: PredictionsRepo
alias RoutePatterns.RoutePattern
alias Routes.Repo, as: RoutesRepo
alias Routes.Route
Expand All @@ -25,10 +24,11 @@ defmodule Dotcom.RealtimeSchedule do

@predicted_schedules_per_stop 2

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@default_opts [
stops_fn: &StopsRepo.get/1,
routes_fn: &RoutesRepo.by_stop_with_route_pattern/1,
predictions_fn: &PredictionsRepo.all_no_cache/1,
schedules_fn: &SchedulesRepo.by_route_ids/2,
alerts_fn: &Alerts.Repo.by_route_ids/2
]
Expand All @@ -52,7 +52,6 @@ defmodule Dotcom.RealtimeSchedule do
opts = Keyword.merge(@default_opts, opts)
stops_fn = Keyword.fetch!(opts, :stops_fn)
routes_fn = Keyword.fetch!(opts, :routes_fn)
predictions_fn = Keyword.fetch!(opts, :predictions_fn)
schedules_fn = Keyword.fetch!(opts, :schedules_fn)
alerts_fn = Keyword.fetch!(opts, :alerts_fn)

Expand All @@ -62,7 +61,7 @@ defmodule Dotcom.RealtimeSchedule do

# stage 2, get stops, predictions, schedules, and alerts
stops_task = Task.async(fn -> get_stops(stop_ids, stops_fn) end)
predictions_task = Task.async(fn -> get_predictions(route_with_patterns, predictions_fn) end)
predictions_task = Task.async(fn -> get_predictions(route_with_patterns) end)
schedules_task = Task.async(fn -> get_schedules(route_with_patterns, now, schedules_fn) end)

alerts_task = Task.async(fn -> get_alerts(route_with_patterns, now, alerts_fn) end)
Expand Down Expand Up @@ -128,12 +127,12 @@ defmodule Dotcom.RealtimeSchedule do
|> json_safe_alerts(now)
end

@spec get_predictions([route_with_patterns_t], fun()) :: map
defp get_predictions(route_with_patterns, predictions_fn) do
@spec get_predictions([route_with_patterns_t]) :: map
defp get_predictions(route_with_patterns) do
route_with_patterns
|> Enum.map(fn {stop_id, _route, route_patterns} ->
Task.async(fn ->
do_get_predictions(stop_id, route_patterns, predictions_fn)
do_get_predictions(stop_id, route_patterns)
end)
end)
|> Enum.flat_map(&Task.await(&1, @long_timeout))
Expand Down Expand Up @@ -161,13 +160,13 @@ defmodule Dotcom.RealtimeSchedule do
end
end

@spec do_get_predictions(Stop.id_t(), [RoutePattern.t()], fun()) :: [
@spec do_get_predictions(Stop.id_t(), [RoutePattern.t()]) :: [
{
route_pattern_name_t,
[Prediction.t()]
}
]
defp do_get_predictions(stop_id, route_patterns, predictions_fn) do
defp do_get_predictions(stop_id, route_patterns) do
route_patterns
|> Enum.map(fn route_pattern ->
key = route_pattern_key(route_pattern, stop_id)
Expand All @@ -180,7 +179,7 @@ defmodule Dotcom.RealtimeSchedule do
sort: "time",
"page[limit]": @predicted_schedules_per_stop
]
|> predictions_fn.()
|> @predictions_repo.all_no_cache()
|> Enum.filter(& &1.time)

{key, next_two_predictions}
Expand Down
5 changes: 3 additions & 2 deletions lib/dotcom/transit_near_me.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ defmodule Dotcom.TransitNearMe do
"place-hsmnl"
]

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@spec build(Address.t(), Keyword.t()) :: stops_with_distances
def build(%Address{} = location, opts) do
opts = Keyword.merge(@default_opts, opts)
Expand Down Expand Up @@ -382,10 +384,9 @@ defmodule Dotcom.TransitNearMe do
PredictedSchedule.t()
]
defp get_predicted_schedules(schedules, params, opts) do
predictions_fn = Keyword.get(opts, :predictions_fn, &Predictions.Repo.all/1)
now = Keyword.fetch!(opts, :now)

predictions = predictions_fn.(params)
predictions = @predictions_repo.all(params)

if predictions == [] do
Logger.warning("#{__MODULE__} no.predictions.for.schedule #{inspect(params)}")
Expand Down
4 changes: 3 additions & 1 deletion lib/dotcom_web/channels/vehicle_map_marker_channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ defmodule DotcomWeb.VehicleMapMarkerChannel do
alias Leaflet.MapData.Marker
alias Vehicles.Vehicle

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

intercept(["reset", "add", "update", "remove"])

@impl Phoenix.Channel
Expand Down Expand Up @@ -46,7 +48,7 @@ defmodule DotcomWeb.VehicleMapMarkerChannel do
trip = Schedules.Repo.trip(vehicle.trip_id)

prediction =
Predictions.Repo.all(
@predictions_repo.all(
route: vehicle.route_id,
direction_id: vehicle.direction_id
)
Expand Down
5 changes: 2 additions & 3 deletions lib/dotcom_web/controllers/schedule/finder_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule DotcomWeb.ScheduleController.FinderApi do
require Logger

@route_patterns_repo Application.compile_env!(:dotcom, :repo_modules)[:route_patterns]
@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@type react_keys :: :date | :direction | :is_current
@type react_strings :: [{react_keys, String.t()}]
Expand Down Expand Up @@ -217,11 +218,9 @@ defmodule DotcomWeb.ScheduleController.FinderApi do
direction_id: direction_id
]

predictions_fn = Map.get(conn.assigns, :predictions_fn, &Predictions.Repo.all/1)

predictions =
if current_service?,
do: predictions_fn.(prediction_opts) |> Enum.filter(&(&1.stop.id == stop_id)),
do: @predictions_repo.all(prediction_opts) |> Enum.filter(&(&1.stop.id == stop_id)),
else: []

{schedules, predictions}
Expand Down
8 changes: 3 additions & 5 deletions lib/dotcom_web/controllers/schedule/green.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,24 +72,22 @@ defmodule DotcomWeb.ScheduleController.Green do
assign(conn, :stops_on_routes, GreenLine.stops_on_routes(direction_id, date))
end

def predictions(conn, opts) do
def predictions(conn, _opts) do
{predictions, vehicle_predictions} =
if DotcomWeb.ScheduleController.Predictions.should_fetch_predictions?(conn) do
predictions_fn = opts[:predictions_fn] || (&Predictions.Repo.all/1)

predictions_stream =
conn
|> conn_with_branches
|> Task.async_stream(
fn conn ->
DotcomWeb.ScheduleController.Predictions.predictions(conn, predictions_fn)
DotcomWeb.ScheduleController.Predictions.predictions(conn)
end,
timeout: @task_timeout,
on_timeout: :kill_task
)

vehicle_predictions =
DotcomWeb.ScheduleController.Predictions.vehicle_predictions(conn, predictions_fn)
DotcomWeb.ScheduleController.Predictions.vehicle_predictions(conn)

{flat_map_results(predictions_stream), vehicle_predictions}
else
Expand Down
2 changes: 1 addition & 1 deletion lib/dotcom_web/controllers/schedule/line_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ defmodule DotcomWeb.ScheduleController.LineApi do
conn
|> DateInRating.call(opts)
|> Green.vehicle_locations(VehicleLocations.init(opts))
|> Green.predictions(Predictions.init(opts))
|> Green.predictions(opts)
|> VehicleTooltips.call(opts)
end

Expand Down
53 changes: 22 additions & 31 deletions lib/dotcom_web/controllers/schedule/predictions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,20 @@ defmodule DotcomWeb.ScheduleController.Predictions do
alias Predictions.Prediction
alias Util.AsyncAssign

@default_opts [
predictions_fn: &Predictions.Repo.all/1
]

@typep predictions_fn :: (Keyword.t() -> [Prediction.t()] | {:error, any})
@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@impl true
def init(opts) do
Keyword.merge(@default_opts, opts)
end
def init(opts \\ []), do: opts

@impl true
def call(conn, opts) do
Util.log_duration(__MODULE__, :do_call, [conn, opts])
def call(conn, _opts \\ []) do
Util.log_duration(__MODULE__, :do_call, [conn])
end

def do_call(conn, opts) do
def do_call(conn) do
if should_fetch_predictions?(conn) do
predictions_task = fn -> predictions(conn, opts[:predictions_fn]) end
vehicle_predictions_task = fn -> vehicle_predictions(conn, opts[:predictions_fn]) end
predictions_task = fn -> predictions(conn) end
vehicle_predictions_task = fn -> vehicle_predictions(conn) end

conn
|> AsyncAssign.async_assign_default(:predictions, predictions_task, [])
Expand All @@ -54,18 +48,15 @@ defmodule DotcomWeb.ScheduleController.Predictions do
Date.compare(assigns.date, Util.service_date(assigns.date_time)) == :eq
end

@spec predictions(Plug.Conn.t(), predictions_fn) :: [Prediction.t()]
def predictions(
%{
assigns: %{
origin: origin,
destination: destination,
route: %{id: route_id},
direction_id: direction_id
}
},
predictions_fn
)
@spec predictions(Plug.Conn.t()) :: [Prediction.t()]
def predictions(%{
assigns: %{
origin: origin,
destination: destination,
route: %{id: route_id},
direction_id: direction_id
}
})
when not is_nil(origin) do
destination_id = if destination, do: Map.get(destination, :id)

Expand All @@ -76,7 +67,7 @@ defmodule DotcomWeb.ScheduleController.Predictions do
[route: route_id, direction_id: direction_id]
end

predictions_fn.(opts)
@predictions_repo.all(opts)
|> case do
{:error, error} ->
Logger.error("predictions for opts #{inspect(opts)}: #{inspect(error)}")
Expand All @@ -93,7 +84,7 @@ defmodule DotcomWeb.ScheduleController.Predictions do
end
end

def predictions(_conn, _) do
def predictions(_conn) do
[]
end

Expand All @@ -114,16 +105,16 @@ defmodule DotcomWeb.ScheduleController.Predictions do
end)
end

@spec vehicle_predictions(Plug.Conn.t(), predictions_fn) :: [Prediction.t()]
def vehicle_predictions(%{assigns: %{vehicle_locations: vehicle_locations}}, predictions_fn) do
@spec vehicle_predictions(Plug.Conn.t()) :: [Prediction.t()]
def vehicle_predictions(%{assigns: %{vehicle_locations: vehicle_locations}}) do
{trip_ids, stop_ids} =
vehicle_locations
|> Map.keys()
|> Enum.unzip()

trip_ids = trip_ids |> Enum.reject(&is_nil/1) |> Enum.join(",")

case predictions_fn.(trip: trip_ids) do
case @predictions_repo.all(trip: trip_ids) do
{:error, error} ->
Logger.error("predictions for trips #{inspect(trip_ids)}: #{inspect(error)}")

Expand All @@ -135,7 +126,7 @@ defmodule DotcomWeb.ScheduleController.Predictions do
end
end

def vehicle_predictions(_conn, _) do
def vehicle_predictions(_conn) do
[]
end
end
17 changes: 9 additions & 8 deletions lib/dotcom_web/controllers/schedule/trip_info.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ defmodule DotcomWeb.ScheduleController.TripInfo do
alias Routes.Route
alias DotcomWeb.ScheduleController.VehicleLocations

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@default_opts [
trip_fn: &Schedules.Repo.schedule_for_trip/2,
vehicle_fn: &Vehicles.Repo.trip/1,
prediction_fn: &Predictions.Repo.all/1
vehicle_fn: &Vehicles.Repo.trip/1
]

@impl true
Expand Down Expand Up @@ -96,7 +97,7 @@ defmodule DotcomWeb.ScheduleController.TripInfo do
case opts[:trip_fn].(trip_id, date: conn.assigns.date) do
trips when is_list(trips) ->
trips
|> build_trip_times(conn.assigns, trip_id, opts[:prediction_fn])
|> build_trip_times(conn.assigns, trip_id)
|> TripInfo.from_list(
vehicle: opts[:vehicle_fn].(trip_id),
vehicle_stop_name: active_stop,
Expand Down Expand Up @@ -148,19 +149,19 @@ defmodule DotcomWeb.ScheduleController.TripInfo do
NaiveDateTime.compare(a, b)
end

defp build_trip_times(schedules, %{date_time: date_time} = assigns, trip_id, prediction_fn) do
defp build_trip_times(schedules, %{date_time: date_time} = assigns, trip_id) do
assigns
|> get_trip_predictions(Util.service_date(date_time), trip_id, prediction_fn)
|> get_trip_predictions(Util.service_date(date_time), trip_id)
|> PredictedSchedule.group(schedules)
end

defp get_trip_predictions(%{date: date}, service_date, _, _prediction_fn)
defp get_trip_predictions(%{date: date}, service_date, _trip_id)
when date != service_date do
[]
end

defp get_trip_predictions(_, _, trip_id, prediction_fn) do
prediction_fn.(trip: trip_id)
defp get_trip_predictions(_, _, trip_id) do
@predictions_repo.all(trip: trip_id)
end

@spec show_trips?(DateTime.t(), DateTime.t(), integer, String.t()) :: boolean
Expand Down
6 changes: 4 additions & 2 deletions lib/predicted_schedule.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ defmodule PredictedSchedule do
prediction: Prediction.t() | nil
}

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

def get(route_id, stop_id, opts \\ []) do
schedules_fn = Keyword.get(opts, :schedules_fn, &Schedules.Repo.by_route_ids/2)
predictions_fn = Keyword.get(opts, :predictions_fn, &Predictions.Repo.all/1)

now = Keyword.get(opts, :now, Util.now())
direction_id = Keyword.get(opts, :direction_id)
sort_fn = Keyword.get(opts, :sort_fn, &sort_predicted_schedules/1)
Expand Down Expand Up @@ -53,7 +55,7 @@ defmodule PredictedSchedule do

predicted_schedules =
[route: route_id, direction_id: direction_id]
|> predictions_fn.()
|> @predictions_repo.all()
|> Enum.filter(&(&1.stop.id == stop_id))
|> PredictedSchedule.group(schedules, sort_fn: sort_fn)
|> filter_predicted_schedules(now)
Expand Down
15 changes: 15 additions & 0 deletions lib/predictions/repo/behaviour.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Predictions.Repo.Behaviour do
@moduledoc """
Behavior for an API client for fetching prediction data.
"""

@doc """
Return predictions for given params
"""
@callback all(Keyword.t()) :: [Predictions.Prediction.t()] | []

@doc """
Return predictions for give prarams ignoring the cache
"""
@callback all_no_cache(Keyword.t()) :: [Predictions.Prediction.t()] | []
end