Skip to content

fix(api): partition and sort schedule-for-stop by direction ID#1126

Open
ARCoder181105 wants to merge 4 commits into
OneBusAway:mainfrom
ARCoder181105:fix/schedule-for-stop-direction-partitioning
Open

fix(api): partition and sort schedule-for-stop by direction ID#1126
ARCoder181105 wants to merge 4 commits into
OneBusAway:mainfrom
ARCoder181105:fix/schedule-for-stop-direction-partitioning

Conversation

@ARCoder181105

@ARCoder181105 ARCoder181105 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Description

Partitions the schedule-for-stop API response by direction_id and sorts direction groups alphabetically by tripHeadsign

Spec Requirement

Stop times for each route must be grouped into stopRouteDirectionSchedules by direction ID. When a route serves multiple directions at a stop, the direction groups must be sorted alphabetically by tripHeadsign.

Implementation Gap

  • Database queries did not select or order by direction_id.
  • The handler returned a flat list under a single route entry rather than separating stop times into direction sub-groups.
  • Direction groups were not sorted by headsign.

Acceptance Criteria

  • Stop times are partitioned into stopRouteDirectionSchedules by direction_id.
  • Direction groups within each route are sorted alphabetically by tripHeadsign.
  • Unit and integration test suite (make test) passes with full verification.

closes #1027
closes #1028

Summary by CodeRabbit

  • New Features

    • Schedule results now show separate entries for each route direction.
    • Direction-specific trip headsigns are now surfaced and sorted alphabetically.
  • Bug Fixes

    • Schedule ordering is more consistent, grouping results by route, direction, and departure time.
    • Stop schedules now better reflect direction-based service patterns.

…by headsign

- Add direction_id to SQL queries and order schedule stop times by route, direction, and departure time
- Refactor schedule-for-stop handler to group stop times into stopRouteDirectionSchedules objects
- Sort direction schedules alphabetically by tripHeadsign using modern Go slices sorting
- Add comprehensive unit tests verifying direction grouping and alphabetical sorting across routes
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ARCoder181105, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a9c942b6-8de4-4fd1-8b54-71763405f2fa

📥 Commits

Reviewing files that changed from the base of the PR and between d60a967 and 15c3d7c.

📒 Files selected for processing (1)
  • internal/restapi/schedule_for_stop_handler.go
📝 Walkthrough

Walkthrough

The GetScheduleForStopOnDate SQL query and its generated Go bindings are updated to select direction_id instead of the duplicate route_id and to order results by route, direction, then departure time. The schedule-for-stop handler now groups stop times by route and direction, computing per-direction headsigns and sorting direction schedules alphabetically. Tests are updated accordingly.

Changes

Direction-based schedule partitioning

Layer / File(s) Summary
Query and generated bindings for direction_id
gtfsdb/query.sql, gtfsdb/query.sql.go
GetScheduleForStopOnDate now selects t.direction_id instead of duplicate t.route_id, orders by r.id, COALESCE(t.direction_id, 0), st.departure_time, and GetScheduleForStopOnDateRow gains DirectionID sql.NullInt64 in place of RouteID_2, with scan logic updated accordingly.
Handler grouping and sorting by direction
internal/restapi/schedule_for_stop_handler.go
Replaces route-only grouping with route→direction maps, derives directionID per row (defaulting to "0"), computes per-direction headsign counts, builds one StopRouteDirectionSchedule per direction, and sorts direction schedules alphabetically by TripHeadsign using slices.SortFunc/cmp.Compare.
Test updates for direction partitioning
internal/restapi/schedule_for_stop_handler_test.go
Updates existing assertions to use renamed keys stopRouteDirectionSchedules/scheduleStopTimes, and adds TestScheduleForStopHandlerDirectionPartitioning to verify alphabetical ordering of direction groups per route.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ScheduleForStopHandler
  participant GetScheduleForStopOnDate

  Client->>ScheduleForStopHandler: request schedule for stop
  ScheduleForStopHandler->>GetScheduleForStopOnDate: query rows with direction_id
  GetScheduleForStopOnDate-->>ScheduleForStopHandler: rows with DirectionID
  ScheduleForStopHandler->>ScheduleForStopHandler: group stopTimes by route+direction
  ScheduleForStopHandler->>ScheduleForStopHandler: compute headsign per direction & sort
  ScheduleForStopHandler-->>Client: response with stopRouteDirectionSchedules
Loading

Possibly related PRs

  • OneBusAway/maglev#1037: Modifies the same GetScheduleForStopOnDate ORDER BY clause involving st.departure_time.

Suggested reviewers: burma-shave, aaronbrethorst

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: partitioning and sorting schedule-for-stop results by direction ID.
Linked Issues check ✅ Passed The SQL now fetches direction_id, the handler groups by route and direction with a "0" fallback, and direction groups are alphabetically sorted.
Out of Scope Changes check ✅ Passed The changes stay focused on the schedule-for-stop query, grouping logic, and tests needed for the linked issues.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/restapi/schedule_for_stop_handler_test.go (1)

628-669: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a regression case for NULL + 0 direction_id on the same route.

This test only checks alphabetical tripHeadsign ordering, so it would still pass if rows with NULL and explicit 0 direction_id values were merged into one "0" bucket with scheduleStopTimes no longer sorted by departure time. A fixture that exercises that acceptance criterion would lock down the new defaulting behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/restapi/schedule_for_stop_handler_test.go` around lines 628 - 669,
Add a regression test in TestScheduleForStopHandlerDirectionPartitioning that
uses a route fixture containing both NULL and explicit 0 direction_id values on
the same route, then assert they remain split into distinct
stopRouteDirectionSchedules buckets instead of merging into one "0" group. Also
verify each bucket’s scheduleStopTimes stays sorted by departure time, so the
test covers the defaulting behavior as well as the existing tripHeadsign
ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gtfsdb/query.sql`:
- Line 562: The ORDER BY in the query that feeds schedule stop times is using
raw t.direction_id while the handler normalizes missing direction values to "0";
update the sort key in the scheduleStopTimes query to use the same normalization
via COALESCE so NULL and 0 trips are grouped together. Adjust the ordering near
the r.id, t.direction_id, st.departure_time sort logic so the SQL sort matches
the later direction bucketing and preserves departure order within the merged
"0" bucket.

---

Nitpick comments:
In `@internal/restapi/schedule_for_stop_handler_test.go`:
- Around line 628-669: Add a regression test in
TestScheduleForStopHandlerDirectionPartitioning that uses a route fixture
containing both NULL and explicit 0 direction_id values on the same route, then
assert they remain split into distinct stopRouteDirectionSchedules buckets
instead of merging into one "0" group. Also verify each bucket’s
scheduleStopTimes stays sorted by departure time, so the test covers the
defaulting behavior as well as the existing tripHeadsign ordering.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d9874139-8f85-4618-8440-730d1220ef22

📥 Commits

Reviewing files that changed from the base of the PR and between d7f5728 and bb2620f.

📒 Files selected for processing (4)
  • gtfsdb/query.sql
  • gtfsdb/query.sql.go
  • internal/restapi/schedule_for_stop_handler.go
  • internal/restapi/schedule_for_stop_handler_test.go

Comment thread gtfsdb/query.sql Outdated
ARCoder181105 and others added 2 commits July 1, 2026 22:29
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@ARCoder181105 ARCoder181105 requested a review from burma-shave July 1, 2026 17:24
@ARCoder181105

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@ARCoder181105

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/restapi/schedule_for_stop_handler_test.go (1)

629-669: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test validates sorting but not actual direction partitioning.

This test only checks alphabetical ordering of tripHeadsign across stopRouteDirectionSchedules; it never asserts that stop times are actually split by direction_id (e.g., a route with multiple direction IDs should yield >1 direction group, and each group's stop times should share consistent trip directions). A regression that collapses all directions back into a single group would still pass this test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/restapi/schedule_for_stop_handler_test.go` around lines 629 - 669,
The test in TestScheduleForStopHandlerDirectionPartitioning only verifies
alphabetical ordering of tripHeadsign and does not confirm that stop times are
actually partitioned by direction_id. Update the assertions so each
stopRouteDirectionSchedules group is validated for consistent trip direction
membership, and add a check that routes with multiple direction IDs produce
multiple direction groups rather than collapsing into one. Use the existing
identifiers stopRouteDirectionSchedules, tripHeadsign, and
TestScheduleForStopHandlerDirectionPartitioning to locate the test logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/restapi/schedule_for_stop_handler.go`:
- Around line 318-328: The headsign selection in the route/direction loop is
non-deterministic because `routeDirectionHeadsignCounts[routeID][dirID]` is a
map and ties on `count` currently depend on random iteration order. Update the
representative headsign choice in the `schedule_for_stop_handler` logic so ties
are broken deterministically, for example by comparing headsign strings after
counts match, while keeping the existing max-count behavior in the
`dirHeadsigns` / `tripHeadsign` selection.

---

Nitpick comments:
In `@internal/restapi/schedule_for_stop_handler_test.go`:
- Around line 629-669: The test in
TestScheduleForStopHandlerDirectionPartitioning only verifies alphabetical
ordering of tripHeadsign and does not confirm that stop times are actually
partitioned by direction_id. Update the assertions so each
stopRouteDirectionSchedules group is validated for consistent trip direction
membership, and add a check that routes with multiple direction IDs produce
multiple direction groups rather than collapsing into one. Use the existing
identifiers stopRouteDirectionSchedules, tripHeadsign, and
TestScheduleForStopHandlerDirectionPartitioning to locate the test logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7472a520-5517-43c1-81f8-29a34ac671f6

📥 Commits

Reviewing files that changed from the base of the PR and between d7f5728 and d60a967.

📒 Files selected for processing (4)
  • gtfsdb/query.sql
  • gtfsdb/query.sql.go
  • internal/restapi/schedule_for_stop_handler.go
  • internal/restapi/schedule_for_stop_handler_test.go

Comment thread internal/restapi/schedule_for_stop_handler.go
@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

schedule-for-stop: Direction groups are not sorted alphabetically schedule-for-stop: Trips are not partitioned by direction_id

1 participant