Skip to content

Commit

Permalink
Itinerary Grouping (#2135)
Browse files Browse the repository at this point in the history
* itinerary group start

* first official pass at producing groups

* correct grouping

* empty line
  • Loading branch information
anthonyshull authored Aug 9, 2024
1 parent 07edd26 commit 7941622
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 0 deletions.
155 changes: 155 additions & 0 deletions livebooks/itinerary-groups.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Itinerary Groups

## Section

```elixir
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(%TripPlan.Leg{mode: %TripPlan.PersonalDetail{}} = leg) do
unique_leg_to_tuple(leg)
end

defp combined_leg_to_tuple(%TripPlan.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(%TripPlan.Leg{mode: %TripPlan.PersonalDetail{}} = leg) do
"WALK #{leg.distance} MILES FROM #{leg.from.name} TO #{leg.to.name}"
end

defp leg_to_string(%TripPlan.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(%TripPlan.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

%TripPlan.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?(%TripPlan.Leg{mode: %TripPlan.PersonalDetail{}} = leg) do
leg.distance <= 0.2
end

def short_walking_leg?(_), do: false

defp unique_leg_to_tuple(%TripPlan.Leg{mode: %TripPlan.PersonalDetail{}} = leg) do
{"WALK", leg.from.name, leg.to.name}
end

defp unique_leg_to_tuple(%TripPlan.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
```

```elixir
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)
```
41 changes: 41 additions & 0 deletions livebooks/trips.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
- check: No mode grouping
name: Symphony to Fenwood Rd
from: place-symcl
to: place-fenwd
- check: Group same parent stop, different child stops
name: Forest Hills to Roslindale Village
from: place-forhl
to: place-NB-0064
- check: Group same parent stop, different child stops
name: Harvard Square to Concord Ave @ Corporal Burns Rd
from: place-harsq
to: '2179'
- check: Group same parent stop, different child stops
name: Silver Line Way to South Station
from: place-conrd
to: place-sstat
- check: Group same parent stop, different child stops
name: Sullivan Square to Somerville Ave @ Stone Ave
from: place-sull
to: '2612'
- check: Group with same origin/destination
name: Davis to Lechmere
from: place-davis
to: place-lech
- check: Green Line and Orange Line overlap
name: North Station to Haymarket
from: place-north
to: place-haecl
- check: Subway grouping; Green Line trunk
name: Government Center to Copley
from: place-gover
to: place-coecl
- check: Subway grouping; Green Line branches
name: Government Center to Kenmore
from: place-gover
to: place-kencl
- check: Bus or Subway grouping
name: Chelsea to Government Center
from: place-chels
to: place-gover

0 comments on commit 7941622

Please sign in to comment.