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

refactor: Convert itinerary legs to segments for easier rendering #2262

Merged
merged 10 commits into from
Dec 11, 2024
1 change: 1 addition & 0 deletions lib/dotcom/trip_plan/input_form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ defmodule Dotcom.TripPlan.InputForm do
date: PlanParams.to_date_param(datetime),
time: PlanParams.to_time_param(datetime),
transportModes: __MODULE__.Modes.selected_mode_keys(modes) |> PlanParams.to_modes_param(),
numItineraries: 40,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I kinda wanna let this slide, but 40 seems kinda excessive?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh oops - didn't mean to include this change. I've been using that to get more itineraries so that the one I'm looking for is more likely to actually show up.

wheelchair: wheelchair
}
|> PlanParams.new()
Expand Down
42 changes: 42 additions & 0 deletions lib/dotcom/trip_plan/leg_to_segment_helper.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule Dotcom.TripPlan.LegToSegmentHelper do
@moduledoc """
A simple algorithm to convert legs as returned by Open Trip Planner
into segments to be displayed by our trip planner tool.
"""

alias Dotcom.TripPlan.{PersonalDetail, TransitDetail}

def legs_to_segments(legs) do
legs
|> raw_convert_to_segments()
|> prepend_start_location()
|> append_end_location()
end

defp raw_convert_to_segments(legs) do
Enum.map(legs, &to_segment/1)
end

defp to_segment(%{mode: %PersonalDetail{}} = leg) do
{:walking_segment, leg}
end

defp to_segment(%{mode: %TransitDetail{}} = leg) do
{:transit_segment, leg}
end

defp prepend_start_location([{_, leg} | _] = segments) do
[
{:location_segment, %{time: leg.start, place: leg.from}}
| segments
]
end

defp append_end_location([{_, leg} = last_segment]) do
[last_segment, {:location_segment, %{time: leg.stop, place: leg.to}}]
end

defp append_end_location([first_segment | rest_of_segments]) do
[first_segment | append_end_location(rest_of_segments)]
end
end
52 changes: 35 additions & 17 deletions lib/dotcom_web/components/trip_planner/itinerary_detail.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule DotcomWeb.Components.TripPlanner.ItineraryDetail do
import DotcomWeb.Components.TripPlanner.TransitLeg, only: [transit_leg: 1]
import DotcomWeb.Components.TripPlanner.WalkingLeg, only: [walking_leg: 1]

alias Dotcom.TripPlan.{PersonalDetail, TransitDetail}
alias Dotcom.TripPlan.LegToSegmentHelper

def itinerary_detail(
%{
Expand Down Expand Up @@ -77,27 +77,45 @@ defmodule DotcomWeb.Components.TripPlanner.ItineraryDetail do

defp specific_itinerary_detail(assigns) do
assigns =
assigns
|> assign(:start_place, List.first(assigns.itinerary.legs).from)
|> assign(:start_time, List.first(assigns.itinerary.legs).start)
|> assign(:end_place, List.last(assigns.itinerary.legs).to)
|> assign(:end_time, List.last(assigns.itinerary.legs).stop)
assign(
assigns,
:segments,
LegToSegmentHelper.legs_to_segments(assigns.itinerary.legs)
)

~H"""
<div class="mt-4">
<.place place={@start_place} time={@start_time} />
<div
:for={leg <- @itinerary.legs}
class={"#{if(match?(%TransitDetail{}, leg.mode), do: "bg-gray-bordered-background")}"}
>
<%= if match?(%PersonalDetail{}, leg.mode) do %>
<.walking_leg leg={leg} />
<% else %>
<.transit_leg leg={leg} />
<% end %>
<div :for={segment <- @segments}>
<.segment segment={segment} />
</div>
<.place place={@end_place} time={@end_time} />
</div>
"""
end

defp segment(%{segment: {:location_segment, %{time: time, place: place}}} = assigns) do
assigns =
assigns
|> assign(:time, time)
|> assign(:place, place)

~H"""
<.place place={@place} time={@time} />
"""
end

defp segment(%{segment: {:walking_segment, leg}} = assigns) do
assigns = assign(assigns, :leg, leg)

~H"""
<.walking_leg leg={@leg} />
"""
end

defp segment(%{segment: {:transit_segment, leg}} = assigns) do
assigns = assign(assigns, :leg, leg)

~H"""
<.transit_leg leg={@leg} />
"""
end
end
2 changes: 1 addition & 1 deletion lib/dotcom_web/components/trip_planner/transit_leg.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule DotcomWeb.Components.TripPlanner.TransitLeg do

def transit_leg(assigns) do
~H"""
<div>
<div class="bg-gray-bordered-background">
<.place
place={@leg.from}
time={@leg.start}
Expand Down
23 changes: 23 additions & 0 deletions test/dotcom/trip_plan/leg_to_segment_helper_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Dotcom.TripPlan.LegToSegmentHelperTest do
@moduledoc false

use ExUnit.Case, async: true

alias Dotcom.TripPlan.LegToSegmentHelper
alias Dotcom.TripPlan.{Leg, PersonalDetail, TransitDetail}

test "works for a typical walking-transit-walking itinerary and puts a location on either end" do
assert [
{:location_segment, _},
{:walking_segment, _},
{:transit_segment, _},
{:walking_segment, _},
{:location_segment, _}
] =
LegToSegmentHelper.legs_to_segments([
%Leg{mode: %PersonalDetail{}},
%Leg{mode: %TransitDetail{}},
%Leg{mode: %PersonalDetail{}}
])
end
end
Loading