From ee798a1f824676b1023bf9d603f0f427b8e171ca Mon Sep 17 00:00:00 2001 From: Jingsi Lu Date: Wed, 15 Jan 2025 09:59:50 -0500 Subject: [PATCH] feat: 1822 flex forbidden pickup type & forbidden drop off type (#1936) PickupDropOffTypeValidator, ForbiddenDropOffTypeNotice, PickupDropOffTypeValidatorTest --- .../validator/PickupDropOffTypeValidator.java | 95 +++++++++++++++++++ .../validator/NoticeFieldsTest.java | 2 + .../PickupDropOffTypeValidatorTest.java | 76 +++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 main/src/main/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidator.java create mode 100644 main/src/test/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidatorTest.java diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidator.java new file mode 100644 index 0000000000..233c31bb42 --- /dev/null +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidator.java @@ -0,0 +1,95 @@ +package org.mobilitydata.gtfsvalidator.validator; + +import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.ERROR; + +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; +import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; +import org.mobilitydata.gtfsvalidator.table.GtfsPickupDropOff; +import org.mobilitydata.gtfsvalidator.table.GtfsStopTime; +import org.mobilitydata.gtfsvalidator.type.GtfsTime; + +/** + * Validates that the `start_pickup_drop_off_window` or `end_pickup_drop_off_window` fields are not + * set when the `pickup_type` is regularly scheduled (0) or must be coordinated with the driver (3), + * and that these fields are not set when the `drop_off_type` is regularly scheduled (0). + * + *

Generated notices include: - {@link ForbiddenPickupTypeNotice} - {@link + * ForbiddenDropOffTypeNotice} if the `drop_off_type` is invalid. + */ +@GtfsValidator +public class PickupDropOffTypeValidator extends SingleEntityValidator { + @Override + public void validate(GtfsStopTime entity, NoticeContainer noticeContainer) { + if ((entity.hasStartPickupDropOffWindow() || entity.hasEndPickupDropOffWindow()) + && (entity.pickupType().equals(GtfsPickupDropOff.ALLOWED) + || entity.pickupType().equals(GtfsPickupDropOff.ON_REQUEST_TO_DRIVER))) { + noticeContainer.addValidationNotice( + new ForbiddenPickupTypeNotice( + entity.csvRowNumber(), + entity.startPickupDropOffWindow(), + entity.endPickupDropOffWindow())); + } + + if ((entity.hasStartPickupDropOffWindow() || entity.hasEndPickupDropOffWindow()) + && entity.dropOffType().equals(GtfsPickupDropOff.ALLOWED)) { + noticeContainer.addValidationNotice( + new ForbiddenDropOffTypeNotice( + entity.csvRowNumber(), + entity.startPickupDropOffWindow(), + entity.endPickupDropOffWindow())); + } + } + + @Override + public boolean shouldCallValidate(ColumnInspector header) { + return header.hasColumn(GtfsStopTime.START_PICKUP_DROP_OFF_WINDOW_FIELD_NAME) + || header.hasColumn(GtfsStopTime.END_PICKUP_DROP_OFF_WINDOW_FIELD_NAME); + } + + /** + * pickup_drop_off_window fields are forbidden when the pickup_type is regularly scheduled (0) or + * must be coordinated with the driver (3). + */ + @GtfsValidationNotice(severity = ERROR) + public static class ForbiddenPickupTypeNotice extends ValidationNotice { + /** The row of the faulty record. */ + private final int csvRowNumber; + + /** The start pickup drop off window of the faulty record. */ + private final GtfsTime startPickupDropOffWindow; + + /** The end pickup drop off window of the faulty record. */ + private final GtfsTime endPickupDropOffWindow; + + public ForbiddenPickupTypeNotice( + int csvRowNumber, GtfsTime startPickupDropOffWindow, GtfsTime endPickupDropOffWindow) { + this.csvRowNumber = csvRowNumber; + this.startPickupDropOffWindow = startPickupDropOffWindow; + this.endPickupDropOffWindow = endPickupDropOffWindow; + } + } + + /** + * pickup_drop_off_window fields are forbidden when the drop_off_type is regularly scheduled (0). + */ + @GtfsValidationNotice(severity = ERROR) + public static class ForbiddenDropOffTypeNotice extends ValidationNotice { + /** The row of the faulty record. */ + private final int csvRowNumber; + + /** The start pickup drop off window of the faulty record. */ + private final GtfsTime startPickupDropOffWindow; + + /** The end pickup drop off window of the faulty record. */ + private final GtfsTime endPickupDropOffWindow; + + public ForbiddenDropOffTypeNotice( + int csvRowNumber, GtfsTime startPickupDropOffWindow, GtfsTime endPickupDropOffWindow) { + this.csvRowNumber = csvRowNumber; + this.startPickupDropOffWindow = startPickupDropOffWindow; + this.endPickupDropOffWindow = endPickupDropOffWindow; + } + } +} diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeFieldsTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeFieldsTest.java index 6a1a55ae91..0507ec5147 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeFieldsTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeFieldsTest.java @@ -80,6 +80,7 @@ public void testNoticeClassFieldNames() { "departureTime1", "distanceKm", "endFieldName", + "endPickupDropOffWindow", "endValue", "entityCount", "entityId", @@ -182,6 +183,7 @@ public void testNoticeClassFieldNames() { "specifiedField", "speedKph", "startFieldName", + "startPickupDropOffWindow", "startValue", "stopCsvRowNumber", "stopDesc", diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidatorTest.java new file mode 100644 index 0000000000..23537fb844 --- /dev/null +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/PickupDropOffTypeValidatorTest.java @@ -0,0 +1,76 @@ +package org.mobilitydata.gtfsvalidator.validator; + +import static com.google.common.truth.Truth.assertThat; + +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; +import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; +import org.mobilitydata.gtfsvalidator.table.GtfsPickupDropOff; +import org.mobilitydata.gtfsvalidator.table.GtfsStopTime; +import org.mobilitydata.gtfsvalidator.type.GtfsTime; + +@RunWith(JUnit4.class) +public class PickupDropOffTypeValidatorTest { + static PickupDropOffTypeValidator validator = new PickupDropOffTypeValidator(); + + private static List generateNotices(GtfsStopTime stopTime) { + NoticeContainer noticeContainer = new NoticeContainer(); + validator.validate(stopTime, noticeContainer); + return noticeContainer.getValidationNotices(); + } + + @Test + public void forbiddenDropOffTypeShouldGenerateNotice() { + GtfsStopTime stopTime = + new GtfsStopTime.Builder() + .setCsvRowNumber(1) + .setPickupType(GtfsPickupDropOff.NOT_AVAILABLE) + .setDropOffType(GtfsPickupDropOff.ALLOWED) + .setStartPickupDropOffWindow(GtfsTime.fromString("00:00:02")) + .setEndPickupDropOffWindow(GtfsTime.fromString("00:00:03")) + .build(); + assertThat(generateNotices(stopTime)) + .containsExactly( + new PickupDropOffTypeValidator.ForbiddenDropOffTypeNotice( + 1, GtfsTime.fromString("00:00:02"), GtfsTime.fromString("00:00:03"))); + } + + @Test + public void allowedDropOffTypeShouldNotGenerateNotice() { + GtfsStopTime stopTime = + new GtfsStopTime.Builder() + .setCsvRowNumber(2) + .setDropOffType(GtfsPickupDropOff.NOT_AVAILABLE) + .build(); + assertThat(generateNotices(stopTime)).isEmpty(); + } + + @Test + public void forbiddenPickupTypeShouldGenerateNotice() { + GtfsStopTime stopTime = + new GtfsStopTime.Builder() + .setCsvRowNumber(3) + .setPickupType(GtfsPickupDropOff.ALLOWED) + .setDropOffType(GtfsPickupDropOff.NOT_AVAILABLE) + .setStartPickupDropOffWindow(GtfsTime.fromString("08:00:00")) + .setEndPickupDropOffWindow(GtfsTime.fromString("09:00:00")) + .build(); + assertThat(generateNotices(stopTime)) + .containsExactly( + new PickupDropOffTypeValidator.ForbiddenPickupTypeNotice( + 3, GtfsTime.fromString("08:00:00"), GtfsTime.fromString("09:00:00"))); + } + + @Test + public void allowedPickupTypeShouldNotGenerateNotice() { + GtfsStopTime stopTime = + new GtfsStopTime.Builder() + .setCsvRowNumber(4) + .setPickupType(GtfsPickupDropOff.NOT_AVAILABLE) + .build(); + assertThat(generateNotices(stopTime)).isEmpty(); + } +}