feat(odpt): import Toei bus and train feeds absent from the portal API - #1814
feat(odpt): import Toei bus and train feeds absent from the portal API#1814ianktc wants to merge 1 commit into
Conversation
The ODPT members-portal resources API does not list Toei under any of its licence values, and Toei's file paths (/api/v4/files/Toei/data/<file>.zip) cannot be produced by PUBLIC_GTFS_ENDPOINT, which hardcodes an "odpt" path segment and a date parameter. Declare the two feeds as static feed items carrying a literal gtfs_endpoint, merged into the fetched list so they flow through the normal per-item processing and reach processed_stable_ids. Gate the stale-feed sweep on the portal response rather than on the merged list: the static entries make the latter permanently non-empty, which would otherwise defeat the guard protecting the catalog from an empty-but-successful portal response. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| # These are declared as ordinary feed items, rather than seeded straight into the DB, | ||
| # so they flow through the same per-item processing as portal feeds -- in particular | ||
| # so their stable_ids reach processed_stable_ids and survive the stale sweep. | ||
| STATIC_FEEDS: Final[List[dict]] = [ |
There was a problem hiding this comment.
[question] from my understand these feeds are independent of the api imports (i.e. they need to be imported only once) - any reason they have to apart of the monthly import here?
There was a problem hiding this comment.
hmm yes its true they are imported only once, this approach is only necessary if the endpoint exception
https://api-public.odpt.org/api/v4/files/{org_label}/data/{dataset_label}.zip comes up again with other organizations so we can track them moving forward. It also depends on whether we want to manually import an odpt series feed in the catalogs repo then?
In that case we are deciding to wait until more than 1 organization besides Toei occurs before we separately add this endpoint in this monthly import?
easier (now): just manually import the Toei feeds in the catalogs repo as odpt series feeds
plan for future: include in the monthly import as done in this PR in case we have future organizations that fall under this new namespace
Summary:
This PR was created due to the two Toei (Bus and Train) feeds that were absent in the ODPT Integration. This is due to the Toei organization not being included in the ODPT Metadata API, and instead hosted at its own namespace. Both feeds are CC BY 4.0 and should be included in the ODPT integration by statically declaring them.
I referred back to this particular file shared with us by ODPT, and it calls out Toei Bus as an exception, I later found Toei Subway to also follow this exception case:
odpt_metadata_info.txt
Discovered after trying to deprecate mdb feeds with no odpt counterparts: MobilityData/mobility-database-catalogs#1633
Investigated with the help of Claude:
Toei is not in the portal response at all. Querying with
format=gtfsand license (odpt|ccby4|cc0|other_auth|other_noauth) returns zero mentions of Toei:odptccby4cc0other_authother_noauthThe URL template cannot express Toei's path even if it were listed.
_fetch_feedsdiscards the API's owndataresource[].urlvalues and rebuilds each URL fromPUBLIC_GTFS_ENDPOINT:https://api-public.odpt.org/api/v4/files/ odpt/{org_label}/{dataset_label}.zip?date=current
All 528
dataresourceURLs across all 75 orgs match that shape, so the template is correct for portal feeds. Toei's is a different namespace:https://api-public.odpt.org/api/v4/files/ Toei/data/Toei-Train-GTFS.zip
with no
odptsegment, a literaldata/element, a filename that is not a dataset label, and nodateparameter. No(org_label, dataset_label)pair produces it.Changes:
STATIC_FEEDSconstant holding the two Toei entries as ordinary feed items with a literalgtfs_endpoint, bypassingPUBLIC_GTFS_ENDPOINT_fetch_feeds's result is bound toportal_feedsand merged in_import_odptportal_feedsrather thanfeeds_list. This is the one genuine hazard in the change:STATIC_FEEDSmakes the merged list permanently non-empty, which would have silently disabled the existing empty-response guard and allowed a single empty-but-successful portal response to deprecate the entireodpt-catalog except the static feedsExpected behavior:
An import run creates 2 schedule feeds and 4 GTFS-RT sub-feeds:
odpt-Toei-ToeiBus.../files/Toei/data/ToeiBus-GTFS.zipodpt-Toei-ToeiTrain.../files/Toei/data/Toei-Train-GTFS.zipodpt-Toei-ToeiBus-vp.../gtfs/realtime/ToeiBusodpt-Toei-ToeiTrain-vp.../gtfs/realtime/toei_odpt_train_vehicleodpt-Toei-ToeiTrain-tu.../gtfs/realtime/toei_odpt_train_trip_updateodpt-Toei-ToeiTrain-sa.../gtfs/realtime/toei_odpt_train_alertSubsequent runs are no-ops for these feeds — the existing fingerprint diffing applies to them unchanged. They are never deprecated by the sweep despite being permanently absent from the portal.
Testing tips:
Result: all tests pass, branch coverage 84% (threshold 80%). The ODPT file alone is 12 passing tests.
Four new cases in
TestStaticFeeds:test_static_feeds_are_well_formed— literalgtfs_endpoint, resolvablelicense_type, unique org/dataset pairs (that pair becomes the stable id), and any declared RT url is a real URL.test_static_feeds_are_imported_with_their_literal_urls— driven with an empty portal response so the only feeds processed are static ones; asserts the URLs survive untouched, plus RT rows' producer_url, entity type, and back-link to the schedule feed.test_static_feeds_are_marked_as_seen_for_the_stale_sweep— schedule and RT stable ids reachprocessed_stable_ids; RT sub-feeds match the sameodpt-prefix and would otherwise be swept.test_static_feeds_do_not_defeat_the_empty_fetch_guard— regression guard for the sweep-gating hazard above: seeds a live feed, runs with an empty portal response, asserts it survives anddeprecated == 0../scripts/api-tests.shto make sure you didn't break anythingodpt_metadata_info.txt