Skip to content

Commit c32be07

Browse files
committed
Add validation rules
1 parent 70b3593 commit c32be07

File tree

4 files changed

+92
-5
lines changed

4 files changed

+92
-5
lines changed

application/src/main/java/org/opentripplanner/updater/trip/siri/CallWrapper.java

+11
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ static List<CallWrapper> of(EstimatedVehicleJourney estimatedVehicleJourney) {
4646
String getStopPointRef();
4747
Boolean isCancellation();
4848
Boolean isPredictionInaccurate();
49+
boolean isExtraCall();
4950
OccupancyEnumeration getOccupancy();
5051
List<NaturalLanguageStringStructure> getDestinationDisplaies();
5152
ZonedDateTime getAimedArrivalTime();
@@ -82,6 +83,11 @@ public Boolean isPredictionInaccurate() {
8283
return call.isPredictionInaccurate();
8384
}
8485

86+
@Override
87+
public boolean isExtraCall() {
88+
return Boolean.TRUE.equals(call.isExtraCall());
89+
}
90+
8591
@Override
8692
public OccupancyEnumeration getOccupancy() {
8793
return call.getOccupancy();
@@ -179,6 +185,11 @@ public Boolean isPredictionInaccurate() {
179185
return call.isPredictionInaccurate();
180186
}
181187

188+
@Override
189+
public boolean isExtraCall() {
190+
return Boolean.TRUE.equals(call.isExtraCall());
191+
}
192+
182193
@Override
183194
public OccupancyEnumeration getOccupancy() {
184195
return call.getOccupancy();

application/src/main/java/org/opentripplanner/updater/trip/siri/ExtraCallTripBuilder.java

+29-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static java.lang.Boolean.TRUE;
44
import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_STOP_SEQUENCE;
55
import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_START_DATE;
6+
import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.STOP_MISMATCH;
67
import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.UNKNOWN_STOP;
78

89
import java.time.LocalDate;
@@ -18,6 +19,7 @@
1819
import org.opentripplanner.transit.model.framework.Result;
1920
import org.opentripplanner.transit.model.network.StopPattern;
2021
import org.opentripplanner.transit.model.network.TripPattern;
22+
import org.opentripplanner.transit.model.site.StopLocation;
2123
import org.opentripplanner.transit.model.timetable.RealTimeState;
2224
import org.opentripplanner.transit.model.timetable.RealTimeTripTimes;
2325
import org.opentripplanner.transit.model.timetable.Trip;
@@ -73,8 +75,11 @@ class ExtraCallTripBuilder {
7375
}
7476

7577
Result<TripUpdate, UpdateError> build() {
76-
if (calls.size() <= transitService.findPattern(trip).numberOfStops()) {
77-
// An extra call trip update is expected to have at least one more stop than the scheduled trip
78+
TripPattern originalPattern = transitService.findPattern(trip);
79+
long numExtraCalls = calls.stream().filter(CallWrapper::isExtraCall).count();
80+
if (calls.size() - numExtraCalls != originalPattern.numberOfStops()) {
81+
// A trip update with extra calls is expected to have the same number of non-extra calls as
82+
// the number of stops in the original scheduled trip
7883
return UpdateError.result(trip.getId(), INVALID_STOP_SEQUENCE, dataSource);
7984
}
8085

@@ -90,13 +95,17 @@ Result<TripUpdate, UpdateError> build() {
9095
ZonedDateTime departureDate = serviceDate.atStartOfDay(timeZone);
9196

9297
// Create the "scheduled version" of the trip
98+
// We do not reuse the trip times of the original scheduled trip
99+
// since adding extra stops change the timings of later stops in the trip pattern.
93100
var aimedStopTimes = new ArrayList<StopTime>();
101+
int extraCallCounter = 0;
94102
for (int stopSequence = 0; stopSequence < calls.size(); stopSequence++) {
103+
CallWrapper call = calls.get(stopSequence);
95104
StopTime stopTime = stopTimesMapper.createAimedStopTime(
96105
trip,
97106
departureDate,
98107
stopSequence,
99-
calls.get(stopSequence),
108+
call,
100109
stopSequence == 0,
101110
stopSequence == (calls.size() - 1)
102111
);
@@ -106,6 +115,23 @@ Result<TripUpdate, UpdateError> build() {
106115
return UpdateError.result(trip.getId(), UNKNOWN_STOP, dataSource);
107116
}
108117

118+
// Drop this update if it replaces scheduled stops from the original pattern.
119+
// Only changes within the same parent station are allowed.
120+
if (call.isExtraCall()) {
121+
extraCallCounter++;
122+
} else {
123+
StopLocation stopInOriginalPattern = originalPattern.getStop(
124+
stopSequence - extraCallCounter
125+
);
126+
StopLocation stopInNewPattern = stopTime.getStop();
127+
if (
128+
!stopInNewPattern.equals(stopInOriginalPattern) &&
129+
!stopInNewPattern.isPartOfSameStationAs(stopInOriginalPattern)
130+
) {
131+
return UpdateError.result(trip.getId(), STOP_MISMATCH, dataSource);
132+
}
133+
}
134+
109135
aimedStopTimes.add(stopTime);
110136
}
111137

application/src/test/java/org/opentripplanner/updater/trip/siri/TestCall.java

+5
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ public Boolean isPredictionInaccurate() {
7979
return predictionInaccurate;
8080
}
8181

82+
@Override
83+
public boolean isExtraCall() {
84+
return false;
85+
}
86+
8287
@Override
8388
public OccupancyEnumeration getOccupancy() {
8489
return occupancy;

application/src/test/java/org/opentripplanner/updater/trip/siri/moduletests/extracall/ExtraCallTest.java

+47-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure;
55

66
import java.util.List;
7-
import org.junit.jupiter.api.Disabled;
87
import org.junit.jupiter.api.Test;
98
import org.opentripplanner.updater.spi.UpdateError;
109
import org.opentripplanner.updater.trip.RealtimeTestConstants;
@@ -75,7 +74,6 @@ void testExtraCallAndCancellation() {
7574
}
7675

7776
@Test
78-
@Disabled("Not supported yet")
7977
void testExtraUnknownStop() {
8078
var env = RealtimeTestEnvironment.of().addTrip(TRIP_1_INPUT).build();
8179

@@ -96,9 +94,56 @@ void testExtraUnknownStop() {
9694

9795
var result = env.applyEstimatedTimetable(updates);
9896

97+
assertFailure(UpdateError.UpdateErrorType.TOO_MANY_STOPS, result);
98+
}
99+
100+
@Test
101+
void testExtraCallSameNumberOfStops() {
102+
var env = RealtimeTestEnvironment.of().addTrip(TRIP_1_INPUT).build();
103+
104+
var updates = new SiriEtBuilder(env.getDateTimeHelper())
105+
.withDatedVehicleJourneyRef(TRIP_1_ID)
106+
.withEstimatedCalls(builder ->
107+
builder
108+
.call(STOP_A1)
109+
.departAimedExpected("00:00:11", "00:00:15")
110+
// Unexpected ExtraCall flag on a scheduled stop
111+
.call(STOP_B1)
112+
.withIsExtraCall(true)
113+
.arriveAimedExpected("00:00:30", "00:00:33")
114+
)
115+
.buildEstimatedTimetableDeliveries();
116+
117+
var result = env.applyEstimatedTimetable(updates);
118+
99119
assertFailure(UpdateError.UpdateErrorType.INVALID_STOP_SEQUENCE, result);
100120
}
101121

122+
@Test
123+
void testExtraCallAndIllegalChangeOfOtherStops() {
124+
var env = RealtimeTestEnvironment.of().addTrip(TRIP_1_INPUT).build();
125+
126+
var updates = new SiriEtBuilder(env.getDateTimeHelper())
127+
.withDatedVehicleJourneyRef(TRIP_1_ID)
128+
.withEstimatedCalls(builder ->
129+
builder
130+
.call(STOP_A1)
131+
.departAimedExpected("00:00:11", "00:00:15")
132+
.call(STOP_D1)
133+
.withIsExtraCall(true)
134+
.arriveAimedExpected("00:00:19", "00:00:20")
135+
.departAimedExpected("00:00:24", "00:00:25")
136+
// this scheduled stop should not be changed
137+
.call(STOP_C1)
138+
.arriveAimedExpected("00:00:30", "00:00:33")
139+
)
140+
.buildEstimatedTimetableDeliveries();
141+
142+
var result = env.applyEstimatedTimetable(updates);
143+
144+
assertFailure(UpdateError.UpdateErrorType.STOP_MISMATCH, result);
145+
}
146+
102147
private static List<EstimatedTimetableDeliveryStructure> updateWithExtraCall(
103148
RealtimeTestEnvironment env
104149
) {

0 commit comments

Comments
 (0)