Skip to content

Commit

Permalink
feat: initial widgets for new DUP logic
Browse files Browse the repository at this point in the history
Rather than being entirely placeholders, the `new_departures` DUP
variant now generates header and evergreen content widgets that should
work identically to the base variant. The "departures" area is stubbed
to always render a "no data" message.
  • Loading branch information
digitalcora committed Sep 16, 2024
1 parent e920468 commit f0a1591
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 11 deletions.
2 changes: 2 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,5 @@ config :screens, :screens_by_alert,
config :screens, Screens.V2.ScreenData,
config_cache_module: Screens.Config.MockCache,
parameters_module: Screens.V2.ScreenData.MockParameters

config :screens, Screens.V2.CandidateGenerator.DupNew, stop_module: Screens.Stops.MockStop
1 change: 1 addition & 0 deletions lib/screens/stops/stop.ex
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ defmodule Screens.Stops.Stop do
end
end

@callback fetch_stop_name(id()) :: String.t() | nil
def fetch_stop_name(stop_id) do
Screens.Telemetry.span(~w[screens stops stop fetch_stop_name]a, %{stop_id: stop_id}, fn ->
case Screens.V3Api.get_json("stops", %{"filter[id]" => stop_id}) do
Expand Down
6 changes: 6 additions & 0 deletions lib/screens/telemetry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ defmodule Screens.Telemetry do
metadata: ~w[stop_id type_filters]a
),
# DUP Candidate Generator
log_span(~w[screens v2 candidate_generator dup]a),
log_span(~w[screens v2 candidate_generator dup departures_instances]a),
log_span(~w[screens v2 candidate_generator dup departures get_section_data]a),
log_span(~w[screens v2 candidate_generator dup departures get_sections_data]a),
log_span(~w[screens v2 candidate_generator dup header_instances]a),
log_span(~w[screens v2 candidate_generator dup alerts_instances]a),
log_span(~w[screens v2 candidate_generator dup evergreen_content_instances]a),
# New DUP Candidate Generator
log_span(~w[screens v2 candidate_generator dup_new]a),
log_span(~w[screens v2 candidate_generator dup_new departures_instances]a),
log_span(~w[screens v2 candidate_generator dup_new evergreen_instances]a),
log_span(~w[screens v2 candidate_generator dup_new header_instances]a),

# events
log_event(~w[hackney_pool]a,
Expand Down
33 changes: 22 additions & 11 deletions lib/screens/v2/candidate_generator/dup_new.ex
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
defmodule Screens.V2.CandidateGenerator.DupNew do
@moduledoc false

alias Screens.Telemetry
alias Screens.V2.CandidateGenerator
alias Screens.V2.CandidateGenerator.Dup, as: DupBase
alias Screens.V2.WidgetInstance.Placeholder
alias Screens.V2.CandidateGenerator.Widgets.Evergreen
alias __MODULE__.{Departures, Header}

@behaviour CandidateGenerator

@telemetry_name ~w[screens v2 candidate_generator dup_new]a
@instance_generators [
header_instances: &Header.instances/2,
departures_instances: &Departures.instances/2,
evergreen_instances: &Evergreen.evergreen_content_instances/2
]
|> Enum.map(fn {name, func} -> {@telemetry_name ++ [name], func} end)

@impl CandidateGenerator
defdelegate screen_template(), to: DupBase
defdelegate screen_template(), to: Screens.V2.CandidateGenerator.Dup

@impl CandidateGenerator
def candidate_instances(_config) do
List.duplicate(
%Placeholder{
color: :gray,
slot_names: [:full_rotation_zero, :full_rotation_one, :full_rotation_two]
},
3
)
def candidate_instances(config, now \\ DateTime.utc_now()) do
Telemetry.span(@telemetry_name, fn ->
context = Telemetry.context()

@instance_generators
|> Task.async_stream(fn {name, func} ->
Telemetry.span(name, context, fn -> func.(config, now) end)
end)
|> Enum.flat_map(fn {:ok, instances} -> instances end)
end)
end

@impl CandidateGenerator
Expand Down
22 changes: 22 additions & 0 deletions lib/screens/v2/candidate_generator/dup_new/departures.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule Screens.V2.CandidateGenerator.DupNew.Departures do
@moduledoc false

alias Screens.V2.WidgetInstance.Departures, as: DeparturesWidget
alias Screens.V2.WidgetInstance.{DeparturesNoData, OvernightDepartures}
alias ScreensConfig.Screen

@type widget :: DeparturesNoData.t() | DeparturesWidget.t() | OvernightDepartures.t()

@spec instances(Screen.t(), DateTime.t()) :: [widget()]
def instances(config, _now) do
~w[
main_content_zero
main_content_one
main_content_two
main_content_reduced_zero
main_content_reduced_one
main_content_reduced_two
]a
|> Enum.map(&%DeparturesNoData{screen: config, slot_name: &1})
end
end
24 changes: 24 additions & 0 deletions lib/screens/v2/candidate_generator/dup_new/header.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule Screens.V2.CandidateGenerator.DupNew.Header do
@moduledoc false

alias Screens.V2.WidgetInstance.NormalHeader
alias ScreensConfig.Screen
alias ScreensConfig.V2.Dup
alias ScreensConfig.V2.Header.{CurrentStopId, CurrentStopName}

@stop Application.compile_env(
:screens,
[Screens.V2.CandidateGenerator.DupNew, :stop_module],
Screens.Stops.Stop
)

@spec instances(Screen.t(), DateTime.t()) :: [NormalHeader.t()]
def instances(%Screen{app_params: %Dup{header: header_config}} = config, now) do
# Generate one header for each of the 3 rotations.
%NormalHeader{screen: config, icon: :logo, text: stop_name(header_config), time: now}
|> List.duplicate(3)
end

defp stop_name(%CurrentStopName{stop_name: name}), do: name
defp stop_name(%CurrentStopId{stop_id: stop_id}), do: @stop.fetch_stop_name(stop_id)
end
80 changes: 80 additions & 0 deletions test/screens/v2/candidate_generator/dup_new_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
defmodule Screens.V2.CandidateGenerator.DupNewTest do
use ExUnit.Case, async: true

alias ScreensConfig.Screen
alias ScreensConfig.V2.{Alerts, Departures, EvergreenContentItem, Header, Schedule}
alias ScreensConfig.V2.Dup, as: DupConfig
alias Screens.Stops.MockStop
alias Screens.Util.Assets
alias Screens.V2.CandidateGenerator.DupNew
alias Screens.V2.WidgetInstance.{DeparturesNoData, EvergreenContent, NormalHeader}

import Mox
setup :verify_on_exit!

describe "candidate_instances/2" do
@config %Screen{
app_id: :dup_v2,
app_params: %DupConfig{
alerts: %Alerts{stop_id: "place-abcde"},
header: %Header.CurrentStopName{stop_name: "Test Stop"},
primary_departures: %Departures{sections: []},
secondary_departures: %Departures{sections: []}
},
vendor: :outfront,
device_id: "TEST",
name: "TEST"
}
@now ~U[2024-01-15 11:45:30Z]

test "returns expected header instances" do
expected_header = %NormalHeader{screen: @config, icon: :logo, text: "Test Stop", time: @now}

instances = DupNew.candidate_instances(@config, @now)

assert Enum.filter(instances, &is_struct(&1, NormalHeader)) ==
List.duplicate(expected_header, 3)
end

test "returns header with stop name determined from stop ID" do
config = put_in(@config.app_params.header, %Header.CurrentStopId{stop_id: "test_id"})
expect(MockStop, :fetch_stop_name, fn "test_id" -> "Test Name" end)

instances = DupNew.candidate_instances(config, @now)

assert %NormalHeader{text: "Test Name"} = Enum.find(instances, &is_struct(&1, NormalHeader))
end

test "returns evergreen content when scheduled" do
schedule = %Schedule{start_dt: ~U[2024-01-01 00:00:00Z], end_dt: ~U[2024-02-01 00:00:00Z]}

item = %EvergreenContentItem{
asset_path: "test.png",
priority: [1],
schedule: [schedule],
slot_names: ["bottom_pane_zero"]
}

config = put_in(@config.app_params.evergreen_content, [item])
now_active = ~U[2024-01-10 00:00:00Z]
now_inactive = ~U[2024-02-02 00:00:00Z]

expected_instance = %EvergreenContent{
screen: config,
asset_url: Assets.s3_asset_url("test.png"),
now: now_active,
priority: [1],
schedule: [schedule],
slot_names: [:bottom_pane_zero]
}

assert expected_instance in DupNew.candidate_instances(config, now_active)
assert expected_instance not in DupNew.candidate_instances(config, now_inactive)
end

test "stub: always returns no-data state for departures" do
expected_instance = %DeparturesNoData{screen: @config, slot_name: :main_content_zero}
assert expected_instance in DupNew.candidate_instances(@config, @now)
end
end
end
1 change: 1 addition & 0 deletions test/support/mocks.ex
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Mox.defmock(Screens.Config.MockCache, for: Screens.Config.Cache)
Mox.defmock(Screens.Stops.MockStop, for: Screens.Stops.Stop)
Mox.defmock(Screens.V2.ScreenData.MockParameters, for: Screens.V2.ScreenData.Parameters)

0 comments on commit f0a1591

Please sign in to comment.