From d6fa93e9c93c6ff6ce551ca52c1aff1c7bc6fa7c Mon Sep 17 00:00:00 2001 From: Gavin Morrow Date: Mon, 15 Jun 2026 16:28:25 -0400 Subject: [PATCH] Remove stop_routes from Schedule (it's redundant) --- gtfs/src/subway_gleam/gtfs/st.gleam | 21 ++++++++++++--- server/dev/gen_schedule.gleam | 27 ++++++++++--------- .../src/subway_gleam/server/route/stop.gleam | 10 +++---- .../server/route/stop/alerts.gleam | 5 +--- .../src/subway_gleam/server/route/stops.gleam | 17 +++++++----- .../src/subway_gleam/server/route/train.gleam | 4 +-- 6 files changed, 48 insertions(+), 36 deletions(-) diff --git a/gtfs/src/subway_gleam/gtfs/st.gleam b/gtfs/src/subway_gleam/gtfs/st.gleam index 7e300b0..64a6cc6 100644 --- a/gtfs/src/subway_gleam/gtfs/st.gleam +++ b/gtfs/src/subway_gleam/gtfs/st.gleam @@ -43,7 +43,6 @@ pub type Schedule { ), trips: Trips, services: dict.Dict(Route, Service), - stop_routes: dict.Dict(StopId, set.Set(Route)), transfers: dict.Dict(StopId, set.Set(Transfer)), routes: dict.Dict(Route, RouteData), ) @@ -111,7 +110,12 @@ pub fn parse(bits: BitArray) -> Result(Schedule, FetchError) { }) use stop_times <- result.try(parse_file("stop_times.txt", stop_time_decoder())) - use #(services, stop_routes) <- result.try( + // `_stop_routes` is all the trains that are ever scheduled to stop at a + // station. That does not always line up with the routes you'd expect to see + // on a map. (e.g. there is one R train that terminates at 96th st every day.) + // + // This may be useful later, so I'm keeping it for now. + use #(services, _stop_routes) <- result.try( parse_stop_times(trips, stop_times, dict.new(), dict.new()) |> result.replace_error(InvalidStopTimes), ) @@ -129,7 +133,7 @@ pub fn parse(bits: BitArray) -> Result(Schedule, FetchError) { dict.insert(route, into: routes, for: route.id) }) - Schedule(stops:, trips:, services:, stop_routes:, transfers:, routes:) |> Ok + Schedule(stops:, trips:, services:, transfers:, routes:) |> Ok } fn parse_stop_times( @@ -332,6 +336,17 @@ fn stop_decoder( )) } +pub fn daytime_routes( + in schedule: Schedule, + for stop_id: StopId, +) -> set.Set(Route) { + let stop = dict.get(schedule.stops, #(stop_id, option.None)) + case stop { + Ok(stop) -> stop.daytime_routes + Error(Nil) -> set.new() + } +} + pub type StopId { /// A route followed by a two-digit number (e.g. `A01`). StopId(String) diff --git a/server/dev/gen_schedule.gleam b/server/dev/gen_schedule.gleam index cb987af..728a2f9 100644 --- a/server/dev/gen_schedule.gleam +++ b/server/dev/gen_schedule.gleam @@ -141,19 +141,20 @@ pub fn main() -> Nil { to: path <> "/services.gleam", contents: services_prefix <> "pub fn services() {" <> services_str <> "}", ) - let stop_routes_str = - string.inspect(schedule.stop_routes) - // The ShapeId constructor is opaque, so there's a helper func - |> string.replace(each: "ShapeId(", with: "shape_id(") - |> string.replace(each: "Set(", with: "set(") - let assert Ok(Nil) = - simplifile.write( - to: path <> "/stop_routes.gleam", - contents: stop_routes_prefix - <> "pub fn stop_routes() {" - <> stop_routes_str - <> "}", - ) + // See comment about stop_routes in st.parse + // let stop_routes_str = + // string.inspect(schedule.stop_routes) + // // The ShapeId constructor is opaque, so there's a helper func + // |> string.replace(each: "ShapeId(", with: "shape_id(") + // |> string.replace(each: "Set(", with: "set(") + // let assert Ok(Nil) = + // simplifile.write( + // to: path <> "/stop_routes.gleam", + // contents: stop_routes_prefix + // <> "pub fn stop_routes() {" + // <> stop_routes_str + // <> "}", + // ) let transfers_str = string.inspect(schedule.transfers) // The ShapeId constructor is opaque, so there's a helper func diff --git a/server/src/subway_gleam/server/route/stop.gleam b/server/src/subway_gleam/server/route/stop.gleam index 3187a7d..a6aaf67 100644 --- a/server/src/subway_gleam/server/route/stop.gleam +++ b/server/src/subway_gleam/server/route/stop.gleam @@ -85,19 +85,15 @@ pub fn model( |> result.replace_error(UnknownStop(stop_id)), ) - let routes = - state.schedule.stop_routes - |> dict.get(stop_id) - |> result.unwrap(or: set.new()) + let routes = st.daytime_routes(in: state.schedule, for: stop_id) let transfers = state.schedule.transfers |> dict.get(stop.id) |> result.unwrap(or: set.new()) |> set.map(fn(transfer) { let routes = - state.schedule.stop_routes - |> dict.get(transfer.destination) - |> result.unwrap(set.new()) + state.schedule + |> st.daytime_routes(for: transfer.destination) |> set.map(fn(route_id) { let assert Ok(route) = state.schedule.routes |> dict.get(route_id) route diff --git a/server/src/subway_gleam/server/route/stop/alerts.gleam b/server/src/subway_gleam/server/route/stop/alerts.gleam index 6d06751..a603fc4 100644 --- a/server/src/subway_gleam/server/route/stop/alerts.gleam +++ b/server/src/subway_gleam/server/route/stop/alerts.gleam @@ -36,10 +36,7 @@ pub fn alerts( |> result.replace_error(stop.error_unknown_stop(stop_id)), ) - let all_routes = - state.schedule.stop_routes - |> dict.get(stop_id) - |> result.unwrap(or: set.new()) + let all_routes = st.daytime_routes(in: state.schedule, for: stop_id) let route = route_id |> option.to_result(Nil) |> result.try(route.from_long_id) diff --git a/server/src/subway_gleam/server/route/stops.gleam b/server/src/subway_gleam/server/route/stops.gleam index 884c149..99f8ac8 100644 --- a/server/src/subway_gleam/server/route/stops.gleam +++ b/server/src/subway_gleam/server/route/stops.gleam @@ -26,12 +26,17 @@ pub fn stops(req: wisp.Request, state: state.State) -> wisp.Response { } }) let stop_routes = - state.schedule.stop_routes - |> dict.map_values(fn(_stop_id, routes) { - set.map(routes, st.route_data(for: _, in: state.schedule)) - |> set.to_list - |> list.sort(by: st.route_compare) - |> list.map(route_bullet.from_route_data) + state.schedule.stops + // Transform into expected shape + |> dict.fold(from: dict.new(), with: fn(acc, stop_id, stop) { + let #(stop_id, _direction) = stop_id + let routes = + set.map(stop.daytime_routes, st.route_data(for: _, in: state.schedule)) + |> set.to_list + |> list.sort(by: st.route_compare) + |> list.map(route_bullet.from_route_data) + + dict.insert(routes, into: acc, for: stop_id) }) let model = diff --git a/server/src/subway_gleam/server/route/train.gleam b/server/src/subway_gleam/server/route/train.gleam index 08b74a7..c589c2c 100644 --- a/server/src/subway_gleam/server/route/train.gleam +++ b/server/src/subway_gleam/server/route/train.gleam @@ -166,9 +166,7 @@ fn stop_li( |> result.map(set.to_list) |> result.unwrap(or: []) |> list.flat_map(fn(transfer) { - dict.get(schedule.stop_routes, transfer.destination) - |> result.map(set.to_list) - |> result.unwrap(or: []) + st.daytime_routes(in: schedule, for: transfer.destination) |> set.to_list }) |> list.map(st.route_data(for: _, in: schedule)) |> list.sort(by: st.route_compare) -- 2.51.2