diff --git a/assets/src/components/propertiesPanel/minischedule.tsx b/assets/src/components/propertiesPanel/minischedule.tsx index 86548d019..c9803d396 100644 --- a/assets/src/components/propertiesPanel/minischedule.tsx +++ b/assets/src/components/propertiesPanel/minischedule.tsx @@ -544,29 +544,27 @@ const TripSchedule = ({ activeStatus={layoverActiveStatus} /> ) : null} - {isTrip(trip) ? ( - isDeadhead(trip) ? ( - - ) : ( - - ) - ) : ( + {isAsDirected(trip) ? ( + ) : isDeadhead(trip) ? ( + + ) : ( + )} ) @@ -839,7 +837,10 @@ const isBreak = (activity: Piece | Break): activity is Break => Object.prototype.hasOwnProperty.call(activity, "breakType") const isTrip = (trip: Trip | AsDirected): trip is Trip => - Object.prototype.hasOwnProperty.call(trip, "id") + !Object.prototype.hasOwnProperty.call(trip, "kind") + +const isAsDirected = (trip: Trip | AsDirected): trip is AsDirected => + Object.prototype.hasOwnProperty.call(trip, "kind") const isDeadhead = (trip: Trip | AsDirected): boolean => isTrip(trip) && trip.routeId == null diff --git a/assets/src/minischedule.d.ts b/assets/src/minischedule.d.ts index 4bec92489..af1ff5d94 100644 --- a/assets/src/minischedule.d.ts +++ b/assets/src/minischedule.d.ts @@ -21,6 +21,7 @@ export interface Break { } export interface AsDirected { + id: TripId | null kind: "wad" | "rad" startTime: Time endTime: Time diff --git a/assets/src/models/minischeduleData.ts b/assets/src/models/minischeduleData.ts index 95415d252..ff86c082b 100644 --- a/assets/src/models/minischeduleData.ts +++ b/assets/src/models/minischeduleData.ts @@ -23,6 +23,7 @@ interface BlockData { } interface AsDirectedData { + id: TripId | null kind: "wad" | "rad" start_time: Time end_time: Time @@ -93,7 +94,7 @@ const pieceFromData = (pieceData: PieceData): Piece => ({ startTime: pieceData.start_time, startPlace: pieceData.start_place, trips: pieceData.trips.map((data) => - isTripData(data) ? tripFromData(data) : asDirectedFromData(data) + isAsDirectedData(data) ? asDirectedFromData(data) : tripFromData(data) ), endTime: pieceData.end_time, endPlace: pieceData.end_place, @@ -122,10 +123,12 @@ const tripFromData = (tripData: TripData): Trip => ({ }) const asDirectedFromData = (asDirectedData: AsDirectedData): AsDirected => ({ + id: asDirectedData.id, kind: asDirectedData.kind, startTime: asDirectedData.start_time, endTime: asDirectedData.end_time, }) -const isTripData = (data: TripData | AsDirectedData): data is TripData => - Object.prototype.hasOwnProperty.call(data, "id") +const isAsDirectedData = ( + data: TripData | AsDirectedData +): data is AsDirectedData => Object.prototype.hasOwnProperty.call(data, "kind") diff --git a/assets/tests/api.test.ts b/assets/tests/api.test.ts index 8acb5723c..369540c2e 100644 --- a/assets/tests/api.test.ts +++ b/assets/tests/api.test.ts @@ -885,6 +885,7 @@ describe("fetchScheduleBlock", () => { end_place: "end place", }, { + id: "as-directed", kind: "rad", start_time: 567, end_time: 1000, @@ -923,6 +924,7 @@ describe("fetchScheduleBlock", () => { endPlace: "end place", }, { + id: "as-directed", kind: "rad", startTime: 567, endTime: 1000, diff --git a/assets/tests/components/propertiesPanel/minischedule.test.tsx b/assets/tests/components/propertiesPanel/minischedule.test.tsx index 58b883cbf..c241f77e0 100644 --- a/assets/tests/components/propertiesPanel/minischedule.test.tsx +++ b/assets/tests/components/propertiesPanel/minischedule.test.tsx @@ -116,6 +116,7 @@ const asDirectedPiece: Piece = { startPlace: "place", trips: [ { + id: "asDirected1", kind: "rad", startTime: 16200, endTime: 44400, diff --git a/lib/schedule/as_directed.ex b/lib/schedule/as_directed.ex index 876fa0247..46f3b8c82 100644 --- a/lib/schedule/as_directed.ex +++ b/lib/schedule/as_directed.ex @@ -1,9 +1,11 @@ defmodule Schedule.AsDirected do @moduledoc false + alias Schedule.Trip alias Schedule.Hastus.Place @type t :: %__MODULE__{ + id: Trip.id() | nil, kind: :wad | :rad, start_time: Util.Time.time_of_day(), end_time: Util.Time.time_of_day(), @@ -12,6 +14,7 @@ defmodule Schedule.AsDirected do } @enforce_keys [ + :id, :kind, :start_time, :end_time, @@ -22,6 +25,7 @@ defmodule Schedule.AsDirected do @derive Jason.Encoder defstruct [ + :id, :kind, :start_time, :end_time, diff --git a/lib/schedule/hastus/activity.ex b/lib/schedule/hastus/activity.ex index e83df8af8..247c836f6 100644 --- a/lib/schedule/hastus/activity.ex +++ b/lib/schedule/hastus/activity.ex @@ -151,20 +151,13 @@ defmodule Schedule.Hastus.Activity do end) end - as_directeds_and_schedule_trips = - if operator_is_as_directed?(activity) do - [as_directed_from_trips(trips_in_piece)] - else - Enum.map(trips_in_piece, &Map.fetch!(schedule_trips_by_id, &1.trip_id)) - end - %Piece{ schedule_id: activity.schedule_id, run_id: activity.run_id, block_id: block_id_from_trips(trips_in_piece), start_time: activity.start_time, start_place: activity.start_place, - trips: as_directeds_and_schedule_trips, + trips: as_directeds_and_schedule_trips_from_trips(trips_in_piece, schedule_trips_by_id), end_time: activity.end_time, end_place: activity.end_place, start_mid_route?: @@ -175,29 +168,79 @@ defmodule Schedule.Hastus.Activity do defp operator_activity_to_piece(activity, _, _, _), do: activity - @spec as_directed_from_trips([Hastus.Trip.t()]) :: AsDirected.t() - defp as_directed_from_trips(trips_in_piece) do - [ - %Hastus.Trip{route_id: nil} = _pullout, - as_directed_trip, - %Hastus.Trip{route_id: nil} = _pull_back - ] = trips_in_piece - - kind = - case as_directed_trip.route_id do - "rad" -> :rad - "wad" -> :wad - end + defp as_directeds_and_schedule_trips_from_trips(trips, schedule_trips_by_id) do + trips + |> Enum.map(&convert_as_directed/1) + |> strip_surrounding_pulls_from_as_directeds() + |> lookup_schedule_trips(schedule_trips_by_id) + end + + defp convert_as_directed(%Hastus.Trip{route_id: "wad"} = trip) do + as_directed(trip, :wad) + end + + defp convert_as_directed(%Hastus.Trip{route_id: "rad"} = trip) do + as_directed(trip, :rad) + end + + defp convert_as_directed(trip) do + trip + end + defp as_directed(trip, kind) do %AsDirected{ + id: trip.trip_id, kind: kind, - start_time: as_directed_trip.start_time, - end_time: as_directed_trip.end_time, - start_place: as_directed_trip.start_place, - end_place: as_directed_trip.end_place + start_time: trip.start_time, + end_time: trip.end_time, + start_place: trip.start_place, + end_place: trip.end_place } end + defp strip_surrounding_pulls_from_as_directeds([]) do + [] + end + + defp strip_surrounding_pulls_from_as_directeds([ + %Hastus.Trip{route_id: nil} = _pullout, + %Schedule.AsDirected{} = as_directed_trip, + %Hastus.Trip{route_id: nil} = _pull_back + | rest + ]) do + [ + as_directed_trip | strip_surrounding_pulls_from_as_directeds(rest) + ] + end + + defp strip_surrounding_pulls_from_as_directeds([trip | rest]) do + [ + trip + | strip_surrounding_pulls_from_as_directeds(rest) + ] + end + + defp lookup_schedule_trips( + [], + _schedule_trips_by_id + ) do + [] + end + + defp lookup_schedule_trips( + [%Schedule.AsDirected{} = as_directed_trip | rest], + schedule_trips_by_id + ) do + [as_directed_trip | lookup_schedule_trips(rest, schedule_trips_by_id)] + end + + defp lookup_schedule_trips([%Hastus.Trip{} = trip | rest], schedule_trips_by_id) do + [ + Map.get(schedule_trips_by_id, trip.trip_id, trip) + | lookup_schedule_trips(rest, schedule_trips_by_id) + ] + end + @spec as_directed_activities_to_pieces([__MODULE__.t() | Piece.t()]) :: [ __MODULE__.t() | Piece.t() ] @@ -230,6 +273,7 @@ defmodule Schedule.Hastus.Activity do start_place: activity.start_place, trips: [ %AsDirected{ + id: nil, kind: case activity.activity_type do "wad" -> :wad @@ -327,11 +371,6 @@ defmodule Schedule.Hastus.Activity do trip.start_time <= activity.end_time end - @spec operator_is_as_directed?(__MODULE__.t()) :: boolean() - defp operator_is_as_directed?(%__MODULE__{activity_type: "Operator"} = activity) do - activity.partial_block_id =~ ~r/^[r|w]ad/i - end - @spec start_mid_route?( __MODULE__.t(), [Hastus.Trip.t()], diff --git a/test/schedule/block_test.exs b/test/schedule/block_test.exs index 52563c6ab..9be7e89f8 100644 --- a/test/schedule/block_test.exs +++ b/test/schedule/block_test.exs @@ -4,7 +4,7 @@ defmodule Schedule.BlockTest do import Skate.Factory alias Schedule.Block - alias Schedule.{Block, Trip} + alias Schedule.{Block, Trip, AsDirected} alias Schedule.Gtfs.StopTime @pullout %Trip{ @@ -80,6 +80,31 @@ defmodule Schedule.BlockTest do pieces: [@piece] ) + @as_directed %AsDirected{ + id: "ad1", + kind: :wad, + start_time: 7, + end_time: 8, + start_place: "garage", + end_place: "route" + } + + @piece_with_as_directed build(:piece, + block_id: @pullout.block_id, + start_time: 1, + end_time: 19, + trips: [@pullout, @trip1, @as_directed, @deadhead, @trip2, @pullback] + ) + + @block_with_as_directed build( + :block, + id: "b", + schedule_id: "schedule", + start_time: 1, + end_time: 19, + pieces: [@piece_with_as_directed] + ) + describe "blocks_from_trips/ and get/3 " do test "can create blocks and then get them" do by_id = Block.blocks_from_pieces([@piece]) @@ -133,6 +158,12 @@ defmodule Schedule.BlockTest do test "returns :err if the given trip isn't found" do assert Block.next_revenue_trip(@block, "t3") == :err end + + test "works if there are as-directed trips in the block" do + assert Block.next_revenue_trip(@block_with_as_directed, @trip1.id) == {:trip, @as_directed} + assert Block.next_revenue_trip(@block_with_as_directed, @as_directed.id) == {:trip, @trip2} + assert Block.next_revenue_trip(@block_with_as_directed, @trip2.id) == :last + end end describe "active?" do diff --git a/test/schedule/data_test.exs b/test/schedule/data_test.exs index 42624cc99..4648e4591 100644 --- a/test/schedule/data_test.exs +++ b/test/schedule/data_test.exs @@ -1762,6 +1762,130 @@ defmodule Schedule.DataTest do } = Data.run_from_hastus(run_key, activities, trips, %{}, %{}, %{}) end + test "allows as directed trips and regular trips in the same piece" do + run_key = {"abc20011", "123-9073"} + + activities = [ + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 68_220, + start_place: "cabot", + end_place: "cabot", + activity_type: "Sign-on" + }, + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + activity_type: "Operator", + partial_block_id: "wad-294" + } + ] + + trips = [ + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 68_220, + end_time: 68_820, + start_place: "cabot", + end_place: "ctypt", + route_id: nil, + trip_id: "64466045" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 68_820, + end_time: 70_860, + start_place: "ctypt", + end_place: "copst", + route_id: "09", + trip_id: "64464840" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 71_280, + end_time: 73_020, + start_place: "copst", + end_place: "ctypt", + route_id: "09", + trip_id: "64463084" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 73_020, + end_time: 73_560, + start_place: "ctypt", + end_place: "cabot", + route_id: nil, + trip_id: "64465819" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 73_800, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: "wad", + trip_id: "64463012" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 84_600, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: nil, + trip_id: "64466123" + } + ] + + schedule_trips_by_id = %{ + "64466045" => build(:trip, id: "64466045"), + "64464840" => build(:trip, id: "64464840"), + "64463084" => build(:trip, id: "64463084"), + "64465819" => build(:trip, id: "64465819"), + "64463012" => build(:trip, id: "64463012"), + "64466123" => build(:trip, id: "64466123") + } + + assert %Run{ + activities: [ + %Piece{ + block_id: "Cwad-294", + start_time: 68_220, + trips: [ + %Schedule.Trip{id: "64466045"}, + %Schedule.Trip{id: "64464840"}, + %Schedule.Trip{id: "64463084"}, + %Schedule.AsDirected{ + kind: :wad, + start_time: 73_800, + end_time: 84_600 + } + ], + end_time: 84_600 + } + ] + } = Data.run_from_hastus(run_key, activities, trips, %{}, schedule_trips_by_id, %{}) + end + test "makes breaks" do run_key = {"schedule", "run"} diff --git a/test/support/factory.ex b/test/support/factory.ex index 36bf21887..e6cca86ec 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -157,6 +157,7 @@ defmodule Skate.Factory do def as_directed_factory do %Schedule.AsDirected{ + id: sequence("Schedule.AsDirected.id:"), kind: :wad, start_time: 1, end_time: 2,