Itinerary Groups
Section
defmodule ItineraryGroups.Helpers do
@moduledoc false
def attach_groups_to_plan(%{"from" => from, "to" => to} = trip) do
plan(from, to)
|> Kernel.elem(1)
|> Enum.reject(&Kernel.is_binary/1)
|> group()
|> Kernel.then(&Map.put(trip, "groups", &1))
end
defp combined_leg_to_tuple(%Leg{mode: %PersonalDetail{}} = leg) do
unique_leg_to_tuple(leg)
end
defp combined_leg_to_tuple(%Leg{mode: %{route: route}} = leg) do
{route.id, leg.from.name, leg.to.name}
end
defp departures(group) do
group
|> Enum.take(5)
|> Enum.map(&itinerary_to_departure/1)
end
defp format_datetime(datetime) do
Timex.format!(datetime, "%H:%M %p", :strftime)
end
defp group(itineraries) do
itineraries
|> Enum.group_by(&unique_legs_to_hash/1)
|> groups_to_strings()
end
defp groups_to_strings(groups) do
Enum.map(groups, fn {_, group} ->
%{
departures: departures(group),
legs:
group
|> Enum.uniq_by(&itinerary_to_hash/1)
|> Enum.map(fn itinerary ->
itinerary |> Map.get(:legs) |> legs_to_string()
end)
}
end)
|> Enum.reject(fn group -> Kernel.length(group.legs) == 0 end)
end
defp itinerary_to_departure(itinerary) do
format_datetime(itinerary.start)
end
defp itinerary_to_hash(itinerary) do
itinerary
|> Map.get(:legs)
|> Enum.reject(&short_walking_leg?/1)
|> Enum.map(&combined_leg_to_tuple/1)
|> :erlang.phash2()
end
defp leg_to_string(%Leg{mode: %PersonalDetail{}} = leg) do
"WALK #{leg.distance} MILES FROM #{leg.from.name} TO #{leg.to.name}"
end
defp leg_to_string(%Leg{mode: %{mode: "BUS"} = mode} = leg) do
"TAKE THE #{mode.route.id} BUS FROM #{leg.from.name} TO #{leg.to.name}"
end
defp leg_to_string(%Leg{mode: %{mode: _} = mode} = leg) do
"TAKE THE #{mode.route.long_name} #{mode.mode} FROM #{leg.from.name} TO #{leg.to.name}"
end
defp legs_to_string(legs) do
legs
|> Enum.reject(&short_walking_leg?/1)
|> Enum.map(&leg_to_string/1)
end
defp named_position(stop) do
stop = Stops.Repo.get(stop)
parent_stop =
if stop.child? do
Stops.Repo.get(stop.parent_id)
else
nil
end
%NamedPosition{
stop: parent_stop || stop
}
end
defp plan(from, to) do
TripPlanner.OpenTripPlanner.plan(
named_position(from),
named_position(to),
[]
# [depart_at: DateTime.from_naive!(~N[2024-07-31T08:30:00], "America/New_York")]
)
end
def short_walking_leg?(%Leg{mode: %PersonalDetail{}} = leg) do
leg.distance <= 0.2
end
def short_walking_leg?(_), do: false
defp unique_leg_to_tuple(%Leg{mode: %PersonalDetail{}} = leg) do
{"WALK", leg.from.name, leg.to.name}
end
defp unique_leg_to_tuple(%Leg{mode: %{route: route}} = leg) do
{Routes.Route.type_atom(route.type), leg.from.name, leg.to.name}
end
defp unique_legs_to_hash(legs) do
legs
|> Enum.reject(&short_walking_leg?/1)
|> Enum.map(&unique_leg_to_tuple/1)
|> :erlang.phash2()
end
end
alias ItineraryGroups.Helpers
Application.start(:yamerl)
write_path =
System.tmp_dir!()
|> Path.join("itinerary-groups.yml")
|> IO.inspect()
plans =
File.cwd!()
|> Path.join("/livebooks/trips.yml")
|> YamlElixir.read_from_file!()
|> Enum.map(fn plan ->
Helpers.attach_groups_to_plan(plan)
end)
|> List.flatten()
yaml = Ymlr.document!(plans)
File.write!(write_path, yaml)