Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions api/src/scripts/populate_db_gtfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,31 @@ def process_entity_types(self, session: "Session", feed: Gtfsrealtimefeed, row,
self.logger.warning(f"Entity types array is empty for feed {stable_id}")
feed.entitytypes.clear()

def inherit_static_feed_locations(self, gtfs_rt_feed, matched_feeds):
"""
Inherit locations from referenced static GTFS feeds when the GTFS-RT
feed has no location metadata of its own.

Existing GTFS-RT locations are preserved. When multiple static feeds
are referenced, their locations are combined and deduplicated by
location ID.
"""
if gtfs_rt_feed.locations:
return

inherited_locations = []
seen_location_ids = set()

for gtfs_feed in matched_feeds:
for location in gtfs_feed.locations:
if location.id in seen_location_ids:
continue
seen_location_ids.add(location.id)
inherited_locations.append(location)

if inherited_locations:
gtfs_rt_feed.locations = inherited_locations

def process_feed_references(self, session: "Session"):
"""
Process the feed references
Expand Down Expand Up @@ -155,6 +180,7 @@ def process_feed_references(self, session: "Session"):
matched_feeds.append(gtfs_feed)

gtfs_rt_feed.gtfs_feeds = matched_feeds
self.inherit_static_feed_locations(gtfs_rt_feed, matched_feeds)
session.add(gtfs_rt_feed)
session.flush()
self.logger.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,10 @@ mdb_source_id,data_type,entity_type,location.country_code,location.subdivision_n
# Also change is_producer_url_unstable from base TRUE to FALSE: should change to False.
# Also change is_seasonal from base TRUE to FALSE: should change to False.
1563,gtfs-rt,tu,US,SomeState,SomeCity,SomeCity Bus,FALSE,RT,,,mdb-50,http://bar.com,0,,,,,,,,,,inactive,,10,,FALSE,FALSE

# Regression fixture for #1567: a new GTFS-RT feed with no location fields references an existing static feed that already has location metadata.
99991,gtfs-rt,tu,,,,Location Lifecycle Test,TRUE,Locationless RT,,,50,https://example.com/gtfs-rt,0,,,,,,,,,,active,,,,,


# Multi-parent regression fixture for #1567: inherit the union of locations from referenced static GTFS feeds.
99992,gtfs-rt,tu,,,,Location Lifecycle Test,TRUE,Multi-parent Locationless RT,,,40|50,https://example.com/gtfs-rt-multi,0,,,,,,,,,,active,,,,,
89 changes: 89 additions & 0 deletions api/tests/integration/populate_tests/test_populate.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,92 @@ def test_is_feed_reference_overwrite(client: TestClient):
)
json_response = response.json()
assert json_response["feed_references"] == ["mdb-50"]


def test_new_gtfs_rt_feed_inherits_existing_static_feed_location(client: TestClient):
"""
Regression test for mobility-database-catalogs #1567.

A newly imported GTFS-RT feed with no location fields of its own should not
remain locationless when its static_reference points to a GTFS feed that
already has location metadata.
"""
response = client.request(
"GET",
"/v1/gtfs_rt_feeds/mdb-99991",
headers=authHeaders,
)

assert response.status_code == 200
feed = response.json()

# First prove that static_reference was resolved successfully.
assert feed["feed_references"] == ["mdb-50"]

# mdb-50 already has CA / Ontario / Barrie in the layered populate fixture.
# The suspected bug is that this location is not synchronized when the
# GTFS-RT -> GTFS relationship is subsequently established.
assert len(feed["locations"]) == 1
assert feed["locations"][0]["country_code"] == "CA"
assert feed["locations"][0]["subdivision_name"] == "Ontario"
assert feed["locations"][0]["municipality"] == "Barrie"


def test_gtfs_rt_feed_with_existing_location_is_not_overwritten(client: TestClient):
"""
A GTFS-RT location explicitly present in the catalogue must not be replaced
by the location of its referenced static GTFS feed.
"""
expected_locations = {
"mdb-1562": ("CA", "BC", "Vancouver"),
"mdb-1563": ("US", "SomeState", "SomeCity"),
}

for feed_id, expected in expected_locations.items():
response = client.request(
"GET",
f"/v1/gtfs_rt_feeds/{feed_id}",
headers=authHeaders,
)

assert response.status_code == 200
feed = response.json()

assert feed["feed_references"] == ["mdb-50"]
assert len(feed["locations"]) == 1
assert feed["locations"][0]["country_code"] == expected[0]
assert feed["locations"][0]["subdivision_name"] == expected[1]
assert feed["locations"][0]["municipality"] == expected[2]


def test_locationless_gtfs_rt_feed_inherits_union_of_static_feed_locations(
client: TestClient,
):
"""
Multiple static references contribute a deduplicated union of locations to
a locationless GTFS-RT feed.
"""
response = client.request(
"GET",
"/v1/gtfs_rt_feeds/mdb-99992",
headers=authHeaders,
)

assert response.status_code == 200
feed = response.json()

assert set(feed["feed_references"]) == {"mdb-40", "mdb-50"}

locations = {
(
location["country_code"],
location["subdivision_name"],
location["municipality"],
)
for location in feed["locations"]
}

assert locations == {
("CA", "Ontario", "London"),
("CA", "Ontario", "Barrie"),
}
Loading