Skip to content

feat: api continuous coverage endpoints - #1816

Merged
davidgamez merged 9 commits into
mainfrom
test/continuous-coverage-seal
Aug 28, 2026
Merged

feat: api continuous coverage endpoints#1816
davidgamez merged 9 commits into
mainfrom
test/continuous-coverage-seal

Conversation

@cka-y

@cka-y cka-y commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary:

Closes MobilityData/product-tasks#215

Adds GET /v1/gtfs_feeds/{id}/continuous_coverage, which returns a GTFS feed's dataset history (newest to oldest, paginated) for the "continuous coverage" criterion of the Seal of Reliability.

Expected behavior:

  • GET /v1/gtfs_feeds/{id}/continuous_coverage returns 200 with paginated items[] plus the latest_* summary fields for the feed's current dataset.
  • downloaded_after/downloaded_before that are malformed or out of order return 400.
  • An unknown feed id returns 404.
  • A feed with no datasets still returns a valid response: latest_files lists all three tracked files as absent, and every other latest_* field is null.

Testing tips:

  • ./scripts/api-tests.sh --test_file api/tests/unittest/models/test_continuous_coverage.py
  • ./scripts/api-tests.sh --test_file api/tests/unittest/models/test_gtfs_feed_continuous_coverage_impl.py
  • ./scripts/api-tests.sh --test_file api/tests/unittest/test_feeds.py
  • Locally: scripts/api-start.sh, then hit GET localhost:8080/v1/gtfs_feeds/<id>/continuous_coverage for a feed with several datasets; vary limit/offset/downloaded_after/downloaded_before and confirm latest_* stays constant across pages while items[] changes.

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Run the unit tests with ./scripts/api-tests.sh to make sure you didn't break anything
  • Add or update any needed documentation to the repo
  • Format the title like "feat: [new feature short description]". Title must follow the Conventional Commit Specification(https://www.conventionalcommits.org/en/v1.0.0/).
  • Linked all relevant issues
  • Include screenshot(s) showing how this pull request works and fixes the issue(s)

@davidgamez davidgamez changed the title poc: api continuous coverage endpoints feat: api continuous coverage endpoints Aug 26, 2026
src/feeds_gen/models/source_info.py
src/feeds_gen/models/validation_report.py
src/feeds_gen/security_api.py
src/user_service/impl/__init__.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These rows are not related to the current PR. However, were deleted in a previous commit by mistake

raise_http_error(404, f"FeedOrm with id {gtfs_feed_id} not found")

# Replace Z with +00:00 to make the datetime object timezone aware
# Due to https://github.com/python/cpython/issues/80010, once migrate to Python 3.11, we can use fromisoformat

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We are already in Python 3.11.

Comment thread docs/OperationsAPI.yaml
nullable: true
description: Machine-readable error category when the check failed.
example: timeout
GtfsFeedContinuousCoverageResponse:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Changes to the operations API are related to the schema update. This PR doesn't implement any endpoint that uses the newly generated models.

@davidgamez
davidgamez marked this pull request as ready for review August 27, 2026 18:57
Comment thread api/src/feeds/impl/feeds_api_impl.py Outdated
if downloaded_before and not valid_iso_date(downloaded_before):
raise_http_validation_error(invalid_date_message.format("downloaded_before"))

after_dt = datetime.fromisoformat(downloaded_after) if downloaded_after else None

@jcpitre jcpitre Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Apparently if you have 2 dates, one naive (no timezone) and the other aware (with timezone), then comparing the two like in the following if results in a TypeError.
Since the dates are provided as parameters to the endpoint, it could happen.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I was relying on the generated code to validate, but after further testing, there is no enforcement of the TZ. Thanks, Fixed.

Comment thread api/src/feeds/impl/feeds_api_impl.py Outdated
# page that is simply the next item, but the oldest item's neighbour lies outside the page,
# so it is fetched from the feed's unfiltered datasets - otherwise every page would report a
# missing overlap at its bottom edge and look like a gap.
predecessors = page[1:] + [self._previous_dataset(feed_datasets, page[-1]) if page else None]

@jcpitre jcpitre Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is it possible that the item inserted at the end in predecessors be one of the NULLed downloaded_at that we pushed to the end of the page in self._continuous_coverage_order()?
I don't know if it would cause a problem.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch. An undated dataset could get a positional predecessor, and a dated one right before an undated row could get that row reported as its predecessor. Fixed.

@jcpitre jcpitre left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just 2 comments

@davidgamez
davidgamez merged commit 187d2b1 into main Aug 28, 2026
23 of 24 checks passed
@davidgamez
davidgamez deleted the test/continuous-coverage-seal branch August 28, 2026 14:49
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.

3 participants