diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java index 45df2f3152..38f9d78f8b 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java @@ -114,6 +114,7 @@ public static FeedMetadata from(GtfsFeedContainer feedContainer, ImmutableSet) feedContainer.getTableForFilename(GtfsCalendarDate.FILENAME).get()); } + feedMetadata.loadSpecFeatures(feedContainer); return feedMetadata; } @@ -195,25 +196,41 @@ private void loadDeviatedFixedRouteFeature(GtfsFeedContainer feedContainer) { } private boolean hasAtLeastOneTripWithAllFields(GtfsFeedContainer feedContainer) { - var optionalStopTimeTable = feedContainer.getTableForFilename(GtfsStopTime.FILENAME); - if (optionalStopTimeTable.isPresent()) { - for (GtfsEntity entity : optionalStopTimeTable.get().getEntities()) { - if (entity instanceof GtfsStopTime) { - GtfsStopTime stopTime = (GtfsStopTime) entity; - return stopTime.hasTripId() - && stopTime.tripId() != null - && stopTime.hasLocationId() - && stopTime.locationId() != null - && stopTime.hasStopId() - && stopTime.stopId() != null - && stopTime.hasArrivalTime() - && stopTime.arrivalTime() != null - && stopTime.hasDepartureTime() - && stopTime.departureTime() != null; - } - } - } - return false; + return feedContainer + .getTableForFilename(GtfsStopTime.FILENAME) + .map(table -> (GtfsStopTimeTableContainer) table) + .map(GtfsStopTimeTableContainer::byTripIdMap) + .map( + byTripIdMap -> + byTripIdMap.asMap().values().stream() + .anyMatch( + gtfsStopTimes -> { + boolean hasTripId = false, + hasLocationId = false, + hasStopId = false, + hasArrivalTime = false, + hasDepartureTime = false; + + for (GtfsStopTime stopTime : gtfsStopTimes) { + hasTripId |= stopTime.hasTripId(); + hasLocationId |= stopTime.hasLocationId(); + hasStopId |= stopTime.hasStopId(); + hasArrivalTime |= stopTime.hasArrivalTime(); + hasDepartureTime |= stopTime.hasDepartureTime(); + + // Early return if all fields are found for this trip + if (hasTripId + && hasLocationId + && hasStopId + && hasArrivalTime + && hasDepartureTime) { + return true; + } + } + // Continue checking other trips + return false; + })) + .orElse(false); } private void loadZoneBasedDemandResponsiveTransitFeature(GtfsFeedContainer feedContainer) { @@ -228,11 +245,7 @@ private boolean hasAtLeastOneTripWithOnlyLocationId(GtfsFeedContainer feedContai for (GtfsEntity entity : optionalStopTimeTable.get().getEntities()) { if (entity instanceof GtfsStopTime) { GtfsStopTime stopTime = (GtfsStopTime) entity; - if (stopTime.hasTripId() - && stopTime.tripId() != null - && stopTime.hasLocationId() - && stopTime.locationId() != null - && (!stopTime.hasStopId() || stopTime.stopId() == null)) { + if (stopTime.hasTripId() && stopTime.hasLocationId() && (!stopTime.hasStopId())) { return true; } } diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java index b1ba194643..254b2bc3f5 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java @@ -513,4 +513,25 @@ public void containsWheelchairAccessibilityFeature() throws IOException, Interru true, ImmutableList.of(GtfsAgencyTableDescriptor.class, GtfsTripTableDescriptor.class)); } + + @Test + public void containsDeviatedFixedRouteFeatureTest() throws IOException, InterruptedException { + // Create stop times with various field combinations for the same trip + String stopTimesContent = + "trip_id, stop_sequence, arrival_time, departure_time, stop_id, location_id\n" + + "trip1,1,01:00:00,01:30:00,stop1,location1\n" + + "trip1,2,,02:30:00,,location2\n" + + "trip1,3,02:00:00,,stop2,location2"; + + createDataFile(GtfsStopTime.FILENAME, stopTimesContent); + + // Validate that the feature is present + validateSpecFeature( + "Predefined Routes with Deviation", + true, + ImmutableList.of( + GtfsAgencyTableDescriptor.class, + GtfsStopTimeTableDescriptor.class, + GtfsTripTableDescriptor.class)); + } }