neiam
/apollos_crib
It's Apollo's Crib
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
Let the source page say whether service-day filtering is working
8c2ccfc · Gabriel Morell · 2026-09-01 20:14
Message
{commit_body(@commit)}
Files changed
modified
apps/room_gtfs/lib/room_gtfs/gtfs_worker.ex
+30
−0
@@ -589,6 +589,17 @@ defmodule RoomGtfs.Worker do
| 589 | 589 | def source_stats(id) do |
| 590 | 590 | %{ |
| 591 | 591 | calendars: Storage.count_calendars(id), |
| 592 | + # The three numbers that say whether service-day filtering is working for | |
| 593 | + # this source, which a count of rows on its own does not. | |
| 594 | + # | |
| 595 | + # calendar_dates -- did calendar_dates.txt import at all | |
| 596 | + # services_today -- zero means the filter stands down entirely and | |
| 597 | + # the board shows every pattern, running or not | |
| 598 | + # trips_no_service -- trips kept because neither file mentions their | |
| 599 | + # service; a large share means the same thing | |
| 600 | + calendar_dates: Storage.count_calendar_dates(id), | |
| 601 | + services_today: services_today(id), | |
| 602 | + trips_no_service: Storage.count_trips_without_service(id), | |
| 592 | 603 | directions: Storage.count_directions(id), |
| 593 | 604 | routes: Storage.count_routes(id), |
| 594 | 605 | shapes: Storage.count_shapes(id), |
@@ -598,6 +609,25 @@ defmodule RoomGtfs.Worker do
| 598 | 609 | rt: RoomGtfs.Worker.RT.stats(id), |
| 599 | 610 | } |
| 600 | 611 | end |
| 612 | + | |
| 613 | + # In the source's own timezone: a board in Boston rolls over to the next | |
| 614 | + # service day at midnight there, not at midnight UTC, and for part of every | |
| 615 | + # day the two disagree about which services are running. | |
| 616 | + defp services_today(id) do | |
| 617 | + tz = | |
| 618 | + case Configuration.get_source!(:bare, id) do | |
| 619 | + %{config: %{tz: tz}} when is_binary(tz) -> tz | |
| 620 | + _otherwise -> "Etc/UTC" | |
| 621 | + end | |
| 622 | + | |
| 623 | + tz | |
| 624 | + |> DateTime.now!() | |
| 625 | + |> DateTime.to_date() | |
| 626 | + |> then(&Storage.services_on(id, &1)) | |
| 627 | + |> MapSet.size() | |
| 628 | + rescue | |
| 629 | + _ -> 0 | |
| 630 | + end | |
| 601 | 631 | end |
| 602 | 632 | |
| 603 | 633 | defmodule RoomGtfs.Worker.RT do |
modified
apps/room_sanctum/lib/room_sanctum/storage.ex
+38
−0
@@ -273,6 +273,44 @@ defmodule RoomSanctum.Storage do
| 273 | 273 | CalendarDate.changeset(calendar_date, attrs) |
| 274 | 274 | end |
| 275 | 275 | |
| 276 | + @doc """ | |
| 277 | + How many of a source's trips run on a service neither calendar file mentions. | |
| 278 | + | |
| 279 | + These are the trips the arrival filter keeps rather than hides, on the | |
| 280 | + grounds that not knowing when something runs is not the same as knowing it | |
| 281 | + does not. A large number here means the filter is mostly not filtering, which | |
| 282 | + is worth being able to see rather than infer from a board that looks wrong. | |
| 283 | + """ | |
| 284 | + def count_trips_without_service(source_id) do | |
| 285 | + # Fully qualified: the Trip alias is introduced further down the file, and | |
| 286 | + # `from` does not resolve the module until this runs -- so the short name | |
| 287 | + # compiles here and fails at the first call. | |
| 288 | + from(t in RoomSanctum.Storage.GTFS.Trip, | |
| 289 | + as: :trip, | |
| 290 | + where: t.source_id == ^source_id, | |
| 291 | + where: | |
| 292 | + not exists( | |
| 293 | + from(c in Calendar, | |
| 294 | + where: | |
| 295 | + c.source_id == parent_as(:trip).source_id and | |
| 296 | + c.service_id == parent_as(:trip).service_id, | |
| 297 | + select: 1 | |
| 298 | + ) | |
| 299 | + ), | |
| 300 | + where: | |
| 301 | + not exists( | |
| 302 | + from(cd in CalendarDate, | |
| 303 | + where: | |
| 304 | + cd.source_id == parent_as(:trip).source_id and | |
| 305 | + cd.service_id == parent_as(:trip).service_id, | |
| 306 | + select: 1 | |
| 307 | + ) | |
| 308 | + ), | |
| 309 | + select: count(t.id) | |
| 310 | + ) | |
| 311 | + |> Repo.one() | |
| 312 | + end | |
| 313 | + | |
| 276 | 314 | @doc """ |
| 277 | 315 | The service ids running on a date, as a MapSet. |
| 278 | 316 |
modified
apps/room_sanctum/test/room_sanctum/calendar_dates_test.exs
+47
−0
@@ -74,6 +74,53 @@ defmodule RoomSanctum.CalendarDatesTest do
| 74 | 74 | end |
| 75 | 75 | end |
| 76 | 76 | |
| 77 | + describe "count_trips_without_service/1" do | |
| 78 | + alias RoomSanctum.Storage.GTFS.Trip | |
| 79 | + | |
| 80 | + defp trip(source, service_id) do | |
| 81 | + Repo.insert!(%Trip{ | |
| 82 | + source_id: source.id, | |
| 83 | + trip_id: "t-#{service_id}-#{System.unique_integer([:positive])}", | |
| 84 | + route_id: "r1", | |
| 85 | + service_id: service_id | |
| 86 | + }) | |
| 87 | + end | |
| 88 | + | |
| 89 | + test "counts the trips the arrival filter has to keep on faith", %{source: source} do | |
| 90 | + calendar(source, "known", [:tuesday], ~D[2026-08-31], ~D[2026-09-05]) | |
| 91 | + trip(source, "known") | |
| 92 | + trip(source, "orphan-a") | |
| 93 | + trip(source, "orphan-b") | |
| 94 | + | |
| 95 | + assert Storage.count_trips_without_service(source.id) == 2 | |
| 96 | + end | |
| 97 | + | |
| 98 | + test "an exception counts as knowing, even with no calendar row", %{source: source} do | |
| 99 | + exception(source, "by-exception", ~D[2026-09-01], 1) | |
| 100 | + trip(source, "by-exception") | |
| 101 | + | |
| 102 | + assert Storage.count_trips_without_service(source.id) == 0 | |
| 103 | + end | |
| 104 | + | |
| 105 | + test "another source's trips are not counted", %{source: source} do | |
| 106 | + {:ok, other_user} = | |
| 107 | + Accounts.register_user(%{ | |
| 108 | + email: "cnt#{System.unique_integer([:positive])}@example.com", | |
| 109 | + password: "hello world!hello world!" | |
| 110 | + }) | |
| 111 | + | |
| 112 | + {:ok, other} = | |
| 113 | + Configuration.create_source(%{ | |
| 114 | + name: "U", notes: "", type: :gtfs, enabled: true, user_id: other_user.id, | |
| 115 | + config: %{"__type__" => "gtfs", "url" => "https://e.test/u.zip", "tz" => "UTC"} | |
| 116 | + }) | |
| 117 | + | |
| 118 | + trip(other, "theirs") | |
| 119 | + | |
| 120 | + assert Storage.count_trips_without_service(source.id) == 0 | |
| 121 | + end | |
| 122 | + end | |
| 123 | + | |
| 77 | 124 | describe "services_on/2" do |
| 78 | 125 | test "a service whose weekday flag is set, inside its date range", %{source: source} do |
| 79 | 126 | # 2026-09-01 is a Tuesday. |
Parents: af6ed7e