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
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add start and end to schema
optionsome committed Aug 8, 2024
commit b142bdf06d08eebb40f76d9ef043424bf24fe46e
15 changes: 15 additions & 0 deletions src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java
Original file line number Diff line number Diff line change
@@ -2,13 +2,15 @@

import java.time.Instant;
import java.util.Locale;
import javax.annotation.Nullable;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFilterPlaceType;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFormFactor;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLWheelchairBoarding;
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.model.StopTime;
import org.opentripplanner.routing.api.response.InputField;
import org.opentripplanner.routing.api.response.RoutingErrorCode;
import org.opentripplanner.routing.graphfinder.PlaceType;
@@ -109,4 +111,17 @@ public static boolean startsWith(String str, String name, Locale locale) {
public static boolean startsWith(I18NString str, String name, Locale locale) {
return str != null && str.toString(locale).toLowerCase(locale).startsWith(name);
}

/**
* Generally the missing values are removed during the graph build. However, for flex trips they
* are not and have to be converted to null here.
*/
@Nullable
public static Integer stopTimeToInt(int value) {
if (value == StopTime.MISSING_VALUE) {
return null;
} else {
return value;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package org.opentripplanner.apis.gtfs.datafetchers;

import static org.opentripplanner.apis.gtfs.GraphQLUtils.stopTimeToInt;

import graphql.relay.Relay;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.List;
import javax.annotation.Nullable;
import org.opentripplanner.apis.gtfs.GraphQLRequestContext;
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
import org.opentripplanner.apis.gtfs.model.DatedTripTime;
import org.opentripplanner.ext.restapi.mapping.LocalDateMapper;
import org.opentripplanner.framework.time.ServiceDateUtils;
import org.opentripplanner.model.Timetable;
@@ -16,6 +21,7 @@
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.timetable.DatedTrip;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.transit.service.TransitService;

public class DatedTripImpl implements GraphQLDataFetchers.GraphQLDatedTrip {
@@ -25,6 +31,24 @@ public DataFetcher<LocalDate> date() {
return env -> getSource(env).serviceDate();
}

@Override
public DataFetcher<DatedTripTime> end() {
return env -> {
var tripTimes = getTripTimes(env);
if (tripTimes == null) {
return null;
}
var stopIndex = tripTimes.getNumStops() - 1;
var scheduledTime = getZonedDateTime(env, tripTimes.getScheduledArrivalTime(stopIndex));
if (scheduledTime == null) {
return null;
}
return tripTimes.isRealtimeUpdated(stopIndex)
? DatedTripTime.of(scheduledTime, tripTimes.getArrivalDelay(stopIndex))
: DatedTripTime.ofStatic(scheduledTime);
};
}

@Override
public DataFetcher<Relay.ResolvedGlobalId> id() {
return env ->
@@ -46,6 +70,23 @@ public DataFetcher<Route> route() {
return environment -> getSource(environment).trip().getRoute();
}

@Override
public DataFetcher<DatedTripTime> start() {
return env -> {
var tripTimes = getTripTimes(env);
if (tripTimes == null) {
return null;
}
var scheduledTime = getZonedDateTime(env, tripTimes.getScheduledDepartureTime(0));
if (scheduledTime == null) {
return null;
}
return tripTimes.isRealtimeUpdated(0)
? DatedTripTime.of(scheduledTime, tripTimes.getDepartureDelay(0))
: DatedTripTime.ofStatic(scheduledTime);
};
}

@Override
public DataFetcher<Iterable<Object>> stops() {
return this::getStops;
@@ -56,18 +97,12 @@ public DataFetcher<Iterable<TripTimeOnDate>> stoptimes() {
return environment -> {
TransitService transitService = getTransitService(environment);
Trip trip = getSource(environment).trip();

var serviceDate = getSource(environment).serviceDate();
TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate);
// no matching pattern found
if (tripPattern == null) {
return List.of();
}

Instant midnight = ServiceDateUtils
.asStartOfService(serviceDate, transitService.getTimeZone())
.toInstant();
Timetable timetable = transitService.getTimetableForTripPattern(tripPattern, serviceDate);
Timetable timetable = getTimetable(environment, trip, serviceDate);
if (timetable == null) {
return List.of();
}
@@ -101,6 +136,43 @@ private TripPattern getTripPattern(DataFetchingEnvironment environment) {
return getTransitService(environment).getPatternForTrip(getSource(environment).trip());
}

@Nullable
private Timetable getTimetable(
DataFetchingEnvironment environment,
Trip trip,
LocalDate serviceDate
) {
TransitService transitService = getTransitService(environment);
TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate);
// no matching pattern found
if (tripPattern == null) {
return null;
}

return transitService.getTimetableForTripPattern(tripPattern, serviceDate);
}

@Nullable
private TripTimes getTripTimes(DataFetchingEnvironment environment) {
Trip trip = getSource(environment).trip();
var serviceDate = getSource(environment).serviceDate();
var timetable = getTimetable(environment, trip, serviceDate);
if (timetable == null) {
return null;
}
return timetable.getTripTimes(trip);
}

private ZonedDateTime getZonedDateTime(DataFetchingEnvironment environment, int time) {
var fixedTime = stopTimeToInt(time);
if (fixedTime == null) {
return null;
}
var serviceDate = getSource(environment).serviceDate();
TransitService transitService = getTransitService(environment);
return ServiceDateUtils.toZonedDateTime(serviceDate, transitService.getTimeZone(), fixedTime);
}

private TransitService getTransitService(DataFetchingEnvironment environment) {
return environment.<GraphQLRequestContext>getContext().transitService();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package org.opentripplanner.apis.gtfs.datafetchers;

import static org.opentripplanner.apis.gtfs.GraphQLUtils.stopTimeToInt;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers;
import org.opentripplanner.framework.graphql.GraphQLUtils;
import org.opentripplanner.model.StopTime;
import org.opentripplanner.model.TripTimeOnDate;
import org.opentripplanner.transit.model.timetable.RealTimeState;
import org.opentripplanner.transit.model.timetable.Trip;
@@ -13,7 +14,7 @@ public class StoptimeImpl implements GraphQLDataFetchers.GraphQLStoptime {

@Override
public DataFetcher<Integer> arrivalDelay() {
return environment -> missingValueToNull(getSource(environment).getArrivalDelay());
return environment -> stopTimeToInt(getSource(environment).getArrivalDelay());
}

@Override
@@ -58,12 +59,12 @@ public DataFetcher<Boolean> realtime() {

@Override
public DataFetcher<Integer> realtimeArrival() {
return environment -> missingValueToNull(getSource(environment).getRealtimeArrival());
return environment -> stopTimeToInt(getSource(environment).getRealtimeArrival());
}

@Override
public DataFetcher<Integer> realtimeDeparture() {
return environment -> missingValueToNull(getSource(environment).getRealtimeDeparture());
return environment -> stopTimeToInt(getSource(environment).getRealtimeDeparture());
}

@Override
@@ -76,12 +77,12 @@ public DataFetcher<String> realtimeState() {

@Override
public DataFetcher<Integer> scheduledArrival() {
return environment -> missingValueToNull(getSource(environment).getScheduledArrival());
return environment -> stopTimeToInt(getSource(environment).getScheduledArrival());
}

@Override
public DataFetcher<Integer> scheduledDeparture() {
return environment -> missingValueToNull(getSource(environment).getScheduledDeparture());
return environment -> stopTimeToInt(getSource(environment).getScheduledDeparture());
}

@Override
@@ -112,16 +113,4 @@ public DataFetcher<Trip> trip() {
private TripTimeOnDate getSource(DataFetchingEnvironment environment) {
return environment.getSource();
}

/**
* Generally the missing values are removed during the graph build. However, for flex trips they
* are not and have to be converted to null here.
*/
private Integer missingValueToNull(int value) {
if (value == StopTime.MISSING_VALUE) {
return null;
} else {
return value;
}
}
}
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode;
import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode;
import org.opentripplanner.apis.gtfs.model.DatedTripTime;
import org.opentripplanner.apis.gtfs.model.FeedPublisher;
import org.opentripplanner.apis.gtfs.model.PlanPageInfo;
import org.opentripplanner.apis.gtfs.model.RideHailingProvider;
@@ -319,12 +320,16 @@ public interface GraphQLCurrency {
public interface GraphQLDatedTrip {
public DataFetcher<java.time.LocalDate> date();

public DataFetcher<DatedTripTime> end();

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

public DataFetcher<TripPattern> pattern();

public DataFetcher<Route> route();

public DataFetcher<DatedTripTime> start();

public DataFetcher<Iterable<Object>> stops();

public DataFetcher<Iterable<TripTimeOnDate>> stoptimes();
@@ -354,6 +359,16 @@ public interface GraphQLDatedTripEdge {
public DataFetcher<DatedTrip> node();
}

/**
* Information about a dated trip's start or end times. May contain real-time information if
* available.
*/
public interface GraphQLDatedTripTime {
public DataFetcher<Object> estimated();

public DataFetcher<java.time.OffsetDateTime> scheduledTime();
}

/**
* The standard case of a fare product: it only has a single price to be paid by the passenger
* and no discounts are applied.
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@ config:
DatedTrip: org.opentripplanner.transit.model.timetable.DatedTrip#DatedTrip
DatedTripConnection: graphql.relay.Connection#Connection<DatedTrip>
DatedTripEdge: graphql.relay.Edge#Edge<DatedTrip>
DatedTripTime: org.opentripplanner.apis.gtfs.model.DatedTripTime#DatedTripTime
debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput
DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop
elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.opentripplanner.apis.gtfs.model;

import java.time.Duration;
import java.time.ZonedDateTime;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
* A scheduled time of a trip's start or end with an optional realtime information.
*/
public record DatedTripTime(
@Nonnull ZonedDateTime scheduledTime,
@Nullable RealTimeEstimate estimated
) {
@Nonnull
public static DatedTripTime of(ZonedDateTime realtime, int delaySecs) {
var delay = Duration.ofSeconds(delaySecs);
return new DatedTripTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay));
}

@Nonnull
public static DatedTripTime ofStatic(ZonedDateTime staticTime) {
return new DatedTripTime(staticTime, null);
}

/**
* Realtime information about a vehicle at a certain place.
* @param delay Delay or "earliness" of a vehicle. Earliness is expressed as a negative number.
*/
record RealTimeEstimate(ZonedDateTime time, Duration delay) {}
}
Original file line number Diff line number Diff line change
@@ -219,11 +219,8 @@ public int getArrivalDelay() {
@Override
public boolean getRealTime() {
return (
!tripTimes.isScheduled() &&
(
!tripTimes.isNoDataStop(boardStopPosInPattern) ||
!tripTimes.isNoDataStop(alightStopPosInPattern)
)
tripTimes.isRealtimeUpdated(boardStopPosInPattern) ||
tripTimes.isRealtimeUpdated(alightStopPosInPattern)
);
}

Original file line number Diff line number Diff line change
@@ -198,6 +198,13 @@ public boolean isPredictionInaccurate(int stop) {
return isStopRealTimeStates(stop, StopRealTimeState.INACCURATE_PREDICTIONS);
}

public boolean isRealtimeUpdated(int stop) {
return (
realTimeState != RealTimeState.SCHEDULED &&
!isStopRealTimeStates(stop, StopRealTimeState.NO_DATA)
);
}

public void setOccupancyStatus(int stop, OccupancyStatus occupancyStatus) {
prepareForRealTimeUpdates();
this.occupancyStatus[stop] = occupancyStatus;
Original file line number Diff line number Diff line change
@@ -234,6 +234,11 @@ public boolean isPredictionInaccurate(int stop) {
return false;
}

@Override
public boolean isRealtimeUpdated(int stop) {
return false;
}

@Override
public I18NString getTripHeadsign() {
return trip.getHeadsign();
Original file line number Diff line number Diff line change
@@ -123,6 +123,11 @@ default int compareTo(TripTimes other) {

boolean isPredictionInaccurate(int stop);

/**
* Return if trip has been updated and stop has not been given a NO_DATA update.
*/
boolean isRealtimeUpdated(int stop);

/**
* @return the whole trip's headsign. Individual stops can have different headsigns.
*/
Loading