Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add query for cancelled trips to GTFS GraphQL API #5393

Merged
merged 57 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
4b052d5
Draft upcoming cancelled trips query
vesameskanen Oct 2, 2023
2e6529e
Remove duplicate imports
vesameskanen Oct 3, 2023
627d251
Node resolver for DatedTrip
vesameskanen Oct 3, 2023
6c26434
Hook in DatedTrip data fetcher
vesameskanen Oct 4, 2023
fb71d74
Feed filtering parameter for cancelled trips query
vesameskanen Oct 4, 2023
184e7e2
More efficient iteration over all trips
vesameskanen Oct 4, 2023
2787b7e
Sort cancelled trips by ascending time
vesameskanen Oct 4, 2023
58f0387
Move filtering by feed into transit service
vesameskanen Oct 4, 2023
3388c15
Merge remote-tracking branch 'otp/dev-2.x' into gtfsgraphql-cancelled…
vesameskanen Oct 17, 2023
45aacde
Merge remote-tracking branch 'otp/dev-2.x' into gtfsgraphql-cancelled…
vesameskanen Dec 28, 2023
a1fe6e3
Merge remote-tracking branch 'upstream/dev-2.x' into gtfsgraphql-canc…
optionsome Aug 6, 2024
20df02b
Use LocalDate for date and include trip's fields instead of trip
optionsome Aug 7, 2024
2a9df2a
Improve documentation and mark cancelledTripTimes as deprecated
optionsome Aug 8, 2024
b142bdf
Add start and end to schema
optionsome Aug 8, 2024
8e12d3a
Create DatedStopTime type and use it in DatedTrip
optionsome Aug 9, 2024
53ff5d5
cancelled -> canceled
optionsome Aug 9, 2024
0469361
Add graphql test
optionsome Aug 9, 2024
c0648b7
Rename method
optionsome Aug 9, 2024
6437bd7
Add small tests
optionsome Aug 9, 2024
6e143d9
Realtime -> RealTime
optionsome Aug 9, 2024
1de24b2
Add more tests
optionsome Aug 9, 2024
7251391
Refactor canceled trips fetching to be more efficient
optionsome Sep 9, 2024
bedee96
Update and fix tests
optionsome Sep 9, 2024
c0768e0
Create an union for dated stoptimes
optionsome Sep 9, 2024
66100b9
Use TripOnServiceDate and redesign schema
optionsome Oct 18, 2024
284c483
Merge remote-tracking branch 'upstream/dev-2.x' into gtfsgraphql-canc…
optionsome Oct 18, 2024
079485e
Remove stuff from new stoptime type
optionsome Oct 25, 2024
c15a29e
Mark stopTimes as non-null
optionsome Oct 25, 2024
65927c6
Do less inside timetable snapshot provider initialization
optionsome Oct 25, 2024
9c124df
Minor fixes
optionsome Oct 25, 2024
9e5eb14
Rename FixedDatedStopTime -> FixedStopTimeOnServiceDate
optionsome Oct 25, 2024
c8924fc
Merge remote-tracking branch 'upstream/dev-2.x' into gtfsgraphql-canc…
optionsome Oct 25, 2024
7f1948e
Run codegen
optionsome Oct 25, 2024
7ed48c3
Improve schema doc and mark some fields as non-null
optionsome Oct 31, 2024
2797fa5
Slightly refactor methods to fetch cancelled trips
optionsome Nov 1, 2024
a2b30f8
Improve schema doc
optionsome Nov 1, 2024
6eaef7a
Rename date -> serviceDate
optionsome Nov 1, 2024
396c43c
Rename ArrivalDepartureTime -> FixedArrivalDepartureTime and LegTime …
optionsome Nov 1, 2024
be25fe3
Merge remote-tracking branch 'upstream/dev-2.x' into gtfsgraphql-canc…
optionsome Nov 2, 2024
672a968
Make TripOnServiceDate an interface and rename some types
optionsome Nov 13, 2024
ec59d99
Merge remote-tracking branch 'upstream/dev-2.x' into gtfsgraphql-canc…
optionsome Nov 13, 2024
8885c19
Rename RegularArrivalDepartureTime -> LegCallTime
optionsome Nov 17, 2024
c7d6d43
Refactor LegCallTime
optionsome Nov 17, 2024
178c150
Rename StopTime -> StopCall etc. and create own model for its times
optionsome Nov 18, 2024
349efb6
Create implementations for call times
optionsome Nov 18, 2024
405f580
Remove support for feeds argument
optionsome Nov 19, 2024
ac21d8a
Improve doc
optionsome Nov 21, 2024
ca53100
Redesign schema
optionsome Nov 22, 2024
45a01af
Merge remote-tracking branch 'upstream/dev-2.x' into gtfsgraphql-canc…
optionsome Nov 22, 2024
de5758b
Add module test for listing canceled trips
leonardehrenfried Nov 25, 2024
e6c0036
Remove duplication
leonardehrenfried Nov 25, 2024
bee85de
Renaming or ordering
optionsome Nov 25, 2024
dba65b0
Update application/src/main/resources/org/opentripplanner/apis/gtfs/s…
optionsome Nov 26, 2024
b3bad26
Implement new schema design
optionsome Dec 2, 2024
ff426dc
Merge remote-tracking branch 'upstream/dev-2.x' into gtfsgraphql-canc…
optionsome Dec 2, 2024
6a485a3
Minor schema doc fixes
optionsome Dec 2, 2024
5217ce5
Formatting
optionsome Dec 2, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl;
import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl;
import org.opentripplanner.apis.gtfs.datafetchers.CurrencyImpl;
import org.opentripplanner.apis.gtfs.datafetchers.DatedTripImpl;
import org.opentripplanner.apis.gtfs.datafetchers.DefaultFareProductImpl;
import org.opentripplanner.apis.gtfs.datafetchers.DepartureRowImpl;
import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver;
Expand Down Expand Up @@ -170,6 +171,7 @@ protected static GraphQLSchema buildSchema() {
.type(typeWiring.build(CurrencyImpl.class))
.type(typeWiring.build(FareProductUseImpl.class))
.type(typeWiring.build(DefaultFareProductImpl.class))
.type(typeWiring.build(DatedTripImpl.class))
.build();
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.opentripplanner.apis.gtfs.datafetchers;

import graphql.relay.Relay;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import org.opentripplanner.api.mapping.LocalDateMapper;
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
import org.opentripplanner.transit.model.timetable.DatedTrip;
import org.opentripplanner.transit.model.timetable.Trip;

public class DatedTripImpl implements GraphQLDataFetchers.GraphQLDatedTrip {

@Override
public DataFetcher<String> date() {
return env -> LocalDateMapper.mapToApi(getSource(env).serviceDate());
}

@Override
public DataFetcher<Trip> trip() {
return env -> getSource(env).trip();
}

@Override
public DataFetcher<Relay.ResolvedGlobalId> id() {
return env ->
new Relay.ResolvedGlobalId(
"DatedTrip",
getSource(env).trip().getId().toString() +
';' +
LocalDateMapper.mapToApi(getSource(env).serviceDate())
);
}

private DatedTrip getSource(DataFetchingEnvironment environment) {
return environment.getSource();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.opentripplanner.transit.model.organization.Agency;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.Station;
import org.opentripplanner.transit.model.timetable.DatedTrip;
import org.opentripplanner.transit.model.timetable.Trip;

public class NodeTypeResolver implements TypeResolver {
Expand Down Expand Up @@ -85,6 +86,9 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) {
if (o instanceof Trip) {
return schema.getObjectType("Trip");
}
if (o instanceof DatedTrip) {
return schema.getObjectType("DatedTrip");
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.DataFetchingEnvironmentImpl;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -63,6 +64,7 @@
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.Station;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.DatedTrip;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.service.TransitService;
import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher;
Expand Down Expand Up @@ -422,6 +424,12 @@ public DataFetcher<Object> node() {

return new PlaceAtDistance(place, Double.parseDouble(parts[0]));
}
case "DatedTrip":
{
String[] parts = id.split(";");
var trip = transitService.getTripForId(FeedScopedId.parse(parts[0]));
return new DatedTrip(trip, LocalDate.parse(parts[1]));
}
case "Route":
return transitService.getRouteForId(FeedScopedId.parse(id));
case "Stop":
Expand Down Expand Up @@ -787,6 +795,18 @@ public DataFetcher<Iterable<Trip>> trips() {
};
}

@Override
public DataFetcher<Connection<DatedTrip>> cancelledTrips() {
return environment -> {
var args = new GraphQLTypes.GraphQLQueryTypeCancelledTripsArgs(environment.getArguments());
var datedTrips = getTransitService(environment)
.getCancelledTrips(args.getGraphQLFeeds())
.stream()
.collect(Collectors.toList());
return new SimpleListConnection<>(datedTrips).get(environment);
};
}

@Override
public DataFetcher<VehicleParking> vehicleParking() {
return environment -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode;
import org.opentripplanner.apis.gtfs.model.RideHailingProvider;
import org.opentripplanner.apis.gtfs.model.RouteTypeModel;
import org.opentripplanner.apis.gtfs.model.StopOnRouteModel;
import org.opentripplanner.apis.gtfs.model.StopOnTripModel;
import org.opentripplanner.apis.gtfs.model.StopPosition;
import org.opentripplanner.apis.gtfs.model.UnknownModel;
import org.opentripplanner.ext.fares.model.FareRuleSet;
import org.opentripplanner.ext.ridehailing.model.RideEstimate;
import org.opentripplanner.model.StopTimesInPattern;
Expand Down Expand Up @@ -55,6 +59,7 @@
import org.opentripplanner.transit.model.network.Route;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.organization.Agency;
import org.opentripplanner.transit.model.timetable.DatedTrip;
import org.opentripplanner.transit.model.timetable.Trip;

public class GraphQLDataFetchers {
Expand Down Expand Up @@ -293,6 +298,29 @@ public interface GraphQLCurrency {
public DataFetcher<Integer> digits();
}

/** Trip on a specific date */
public interface GraphQLDatedTrip {
public DataFetcher<String> date();

public DataFetcher<graphql.relay.Relay.ResolvedGlobalId> id();

public DataFetcher<Trip> trip();
}

/** A connection to a list of dated trips */
public interface GraphQLDatedTripConnection {
public DataFetcher<Iterable<Edge<DatedTrip>>> edges();

public DataFetcher<Object> pageInfo();
}

/** An edge for DatedTrip connection */
public interface GraphQLDatedTripEdge {
public DataFetcher<String> cursor();

public DataFetcher<DatedTrip> node();
}

/**
* The standard case of a fare product: it only has a single price to be paid by the passenger
* and no discounts are applied.
Expand Down Expand Up @@ -665,6 +693,8 @@ public interface GraphQLQueryType {

public DataFetcher<Iterable<TripTimeOnDate>> cancelledTripTimes();

public DataFetcher<Connection<DatedTrip>> cancelledTrips();

public DataFetcher<VehicleParking> carPark();

public DataFetcher<Iterable<VehicleParking>> carParks();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
package org.opentripplanner.apis.gtfs.generated;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -1205,6 +1206,65 @@ public void setGraphQLTrips(List<String> trips) {
}
}

public static class GraphQLQueryTypeCancelledTripsArgs {

private String after;
private String before;
private List<String> feeds;
private Integer first;
private Integer last;

public GraphQLQueryTypeCancelledTripsArgs(Map<String, Object> args) {
if (args != null) {
this.after = (String) args.get("after");
this.before = (String) args.get("before");
this.feeds = (List<String>) args.get("feeds");
this.first = (Integer) args.get("first");
this.last = (Integer) args.get("last");
}
}

public String getGraphQLAfter() {
return this.after;
}

public String getGraphQLBefore() {
return this.before;
}

public List<String> getGraphQLFeeds() {
return this.feeds;
}

public Integer getGraphQLFirst() {
return this.first;
}

public Integer getGraphQLLast() {
return this.last;
}

public void setGraphQLAfter(String after) {
this.after = after;
}

public void setGraphQLBefore(String before) {
this.before = before;
}

public void setGraphQLFeeds(List<String> feeds) {
this.feeds = feeds;
}

public void setGraphQLFirst(Integer first) {
this.first = first;
}

public void setGraphQLLast(Integer last) {
this.last = last;
}
}

public static class GraphQLQueryTypeCarParkArgs {

private String id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ config:
ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo
Cluster: Object
Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate
DatedTrip: org.opentripplanner.transit.model.timetable.DatedTrip#DatedTrip
DatedTripConnection: graphql.relay.Connection#Connection<DatedTrip>
DatedTripEdge: graphql.relay.Edge#Edge<DatedTrip>
debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput
DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop
elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.opentripplanner.transit.model.timetable;

import java.time.LocalDate;

/**
* Class which represents a trin on a specific date
*/
public record DatedTrip(Trip trip, LocalDate serviceDate) {}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -46,6 +48,8 @@
import org.opentripplanner.transit.model.site.Station;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.site.StopLocationsGroup;
import org.opentripplanner.transit.model.timetable.DatedTrip;
import org.opentripplanner.transit.model.timetable.RealTimeState;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate;
import org.opentripplanner.transit.model.timetable.TripOnServiceDate;
Expand Down Expand Up @@ -247,6 +251,59 @@ public Trip getTripForId(FeedScopedId id) {
return this.transitModelIndex.getTripForId().get(id);
}

@Override
public Collection<DatedTrip> getCancelledTrips(List<String> feeds) {
optionsome marked this conversation as resolved.
Show resolved Hide resolved
OTPRequestTimeoutException.checkForTimeout();
List<DatedTrip> cancelledTrips = new ArrayList<>();
Map<Trip, Integer> departures = new HashMap<>();

var timetableSnapshot = lazyGetTimeTableSnapShot();
if (timetableSnapshot == null) {
return cancelledTrips;
}
var calendarService = getCalendarService();
var patternMap = transitModelIndex.getPatternForTrip();
var trips = transitModelIndex.getTripForId();

for (Map.Entry<FeedScopedId, Trip> entry : trips.entrySet()) {
var trip = entry.getValue();
if (feeds != null && !feeds.contains(trip.getId().getFeedId())) {
continue;
}
Set<LocalDate> serviceDates = calendarService.getServiceDatesForServiceId(
trip.getServiceId()
);
var pattern = patternMap.get(trip);
for (LocalDate date : serviceDates) {
var timetable = timetableSnapshot.resolve(pattern, date);
var tripTimes = timetable.getTripTimes(trip);
if (tripTimes.getRealTimeState() == RealTimeState.CANCELED) { // use UPDATED for faked testing
cancelledTrips.add(new DatedTrip(trip, date));
// store departure time from first stop
departures.put(trip, tripTimes.sortIndex());
}
}
}
cancelledTrips.sort((t1, t2) -> {
if (t1.serviceDate().isBefore(t2.serviceDate())) {
return -1;
} else if (t2.serviceDate().isBefore(t1.serviceDate())) {
return 1;
}
var departure1 = departures.get(t1.trip());
var departure2 = departures.get(t2.trip());
if (departure1 < departure2) {
return -1;
} else if (departure1 > departure2) {
return 1;
} else {
// identical departure day and time, so sort by unique feedscope id
return t1.trip().getId().compareTo(t2.trip().getId());
}
});
return cancelledTrips;
}

@Override
public Collection<Trip> getAllTrips() {
OTPRequestTimeoutException.checkForTimeout();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.opentripplanner.transit.model.site.Station;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.site.StopLocationsGroup;
import org.opentripplanner.transit.model.timetable.DatedTrip;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate;
import org.opentripplanner.transit.model.timetable.TripOnServiceDate;
Expand Down Expand Up @@ -108,6 +109,8 @@ public interface TransitService {

Collection<Trip> getAllTrips();

Collection<DatedTrip> getCancelledTrips(List<String> feeds);

Collection<Route> getAllRoutes();

TripPattern getPatternForTrip(Trip trip);
Expand Down
Loading