Skip to content

Commit

Permalink
feat: new booking_rules.txt validation notices (#1866)
Browse files Browse the repository at this point in the history
  • Loading branch information
cka-y authored Oct 3, 2024
1 parent c3714ff commit e3b9f3d
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.mobilitydata.gtfsvalidator.table.GtfsBookingRules;
import org.mobilitydata.gtfsvalidator.table.GtfsBookingRulesSchema;
import org.mobilitydata.gtfsvalidator.table.GtfsBookingType;
import org.mobilitydata.gtfsvalidator.type.GtfsTime;

@GtfsValidator
public class BookingRulesEntityValidator extends SingleEntityValidator<GtfsBookingRules> {
Expand Down Expand Up @@ -39,25 +40,46 @@ public void validate(GtfsBookingRules entity, NoticeContainer noticeContainer) {
validatePriorNoticeDurationMin(entity, noticeContainer);
validatePriorNoticeStartDay(entity, noticeContainer);
validatePriorNoticeDayRange(entity, noticeContainer);
validateMissingPriorDayBookingFields(entity, noticeContainer);
validatePriorNoticeStartTime(entity, noticeContainer);
}

private static void validatePriorNoticeDurationMin(
GtfsBookingRules entity, NoticeContainer noticeContainer) {
// Check if prior_notice_duration_min is set for booking_type SAMEDAY
if (entity.bookingType() == GtfsBookingType.SAMEDAY && !entity.hasPriorNoticeDurationMin()) {
noticeContainer.addValidationNotice(new MissingPriorNoticeDurationMinNotice(entity));
}
// Check if prior_notice_duration_min is less than prior_notice_duration_max
if (entity.hasPriorNoticeDurationMin() && entity.hasPriorNoticeDurationMax()) {
if (entity.priorNoticeDurationMax() < entity.priorNoticeDurationMin()) {
noticeContainer.addValidationNotice(
new InvalidPriorNoticeDurationMinNotice(
entity, entity.priorNoticeDurationMin(), entity.priorNoticeDurationMax()));
noticeContainer.addValidationNotice(new InvalidPriorNoticeDurationMinNotice(entity));
}
}
}

private static void validateMissingPriorDayBookingFields(
GtfsBookingRules entity, NoticeContainer noticeContainer) {
if (entity.bookingType() == GtfsBookingType.PRIORDAY
&& (!entity.hasPriorNoticeLastDay() || !entity.hasPriorNoticeLastTime())) {
noticeContainer.addValidationNotice(new MissingPriorDayBookingFieldValueNotice(entity));
}
}

private static void validatePriorNoticeStartTime(
GtfsBookingRules entity, NoticeContainer noticeContainer) {
if (entity.hasPriorNoticeStartTime() && !entity.hasPriorNoticeStartDay()) {
noticeContainer.addValidationNotice(new ForbiddenPriorNoticeStartTimeNotice(entity));
}
if (!entity.hasPriorNoticeStartTime() && entity.hasPriorNoticeStartDay()) {
noticeContainer.addValidationNotice(new MissingPriorNoticeStartTimeNotice(entity));
}
}

private static void validatePriorNoticeStartDay(
GtfsBookingRules entity, NoticeContainer noticeContainer) {
if (entity.hasPriorNoticeDurationMax() && entity.hasPriorNoticeStartDay()) {
noticeContainer.addValidationNotice(
new ForbiddenPriorNoticeStartDayNotice(
entity, entity.priorNoticeStartDay(), entity.priorNoticeDurationMax()));
noticeContainer.addValidationNotice(new ForbiddenPriorNoticeStartDayNotice(entity));
}
}

Expand Down Expand Up @@ -229,12 +251,30 @@ static class InvalidPriorNoticeDurationMinNotice extends ValidationNotice {
/** The value of the `prior_notice_duration_max` field. */
private final int priorNoticeDurationMax;

InvalidPriorNoticeDurationMinNotice(
GtfsBookingRules bookingRule, int priorNoticeDurationMin, int priorNoticeDurationMax) {
InvalidPriorNoticeDurationMinNotice(GtfsBookingRules bookingRule) {
this.csvRowNumber = bookingRule.csvRowNumber();
this.bookingRuleId = bookingRule.bookingRuleId();
this.priorNoticeDurationMin = bookingRule.priorNoticeDurationMin();
this.priorNoticeDurationMax = bookingRule.priorNoticeDurationMax();
}
}

/**
* `prior_notice_duration_min` value is required for same day `booking_type` in booking_rules.txt.
*/
@GtfsValidationNotice(
severity = SeverityLevel.ERROR,
files = @FileRefs(GtfsBookingRulesSchema.class))
static class MissingPriorNoticeDurationMinNotice extends ValidationNotice {
/** The row number of the faulty record. */
private final int csvRowNumber;

/** The `booking_rules.booking_rule_id` of the faulty record. */
private final String bookingRuleId;

MissingPriorNoticeDurationMinNotice(GtfsBookingRules bookingRule) {
this.csvRowNumber = bookingRule.csvRowNumber();
this.bookingRuleId = bookingRule.bookingRuleId();
this.priorNoticeDurationMin = priorNoticeDurationMin;
this.priorNoticeDurationMax = priorNoticeDurationMax;
}
}

Expand All @@ -255,12 +295,11 @@ static class ForbiddenPriorNoticeStartDayNotice extends ValidationNotice {
/** The value of the `prior_notice_duration_max` field. */
private final int priorNoticeDurationMax;

ForbiddenPriorNoticeStartDayNotice(
GtfsBookingRules bookingRule, int priorNoticeStartDay, int priorNoticeDurationMax) {
ForbiddenPriorNoticeStartDayNotice(GtfsBookingRules bookingRule) {
this.csvRowNumber = bookingRule.csvRowNumber();
this.bookingRuleId = bookingRule.bookingRuleId();
this.priorNoticeStartDay = priorNoticeStartDay;
this.priorNoticeDurationMax = priorNoticeDurationMax;
this.priorNoticeStartDay = bookingRule.priorNoticeStartDay();
this.priorNoticeDurationMax = bookingRule.priorNoticeDurationMax();
}
}

Expand Down Expand Up @@ -293,4 +332,72 @@ static class PriorNoticeLastDayAfterStartDayNotice extends ValidationNotice {
this.priorNoticeStartDay = bookingRule.priorNoticeStartDay();
}
}

/**
* `prior_notice_last_day` and `prior_notice_last_time` values are required for prior day
* `booking_type` in booking_rules.txt.
*/
@GtfsValidationNotice(
severity = SeverityLevel.ERROR,
files = @FileRefs(GtfsBookingRulesSchema.class))
static class MissingPriorDayBookingFieldValueNotice extends ValidationNotice {
/** The row number of the faulty record. */
private final int csvRowNumber;

/** The `booking_rules.booking_rule_id` of the faulty record. */
private final String bookingRuleId;

MissingPriorDayBookingFieldValueNotice(GtfsBookingRules bookingRule) {
this.csvRowNumber = bookingRule.csvRowNumber();
this.bookingRuleId = bookingRule.bookingRuleId();
}
}

/**
* `prior_notice_start_time` value is forbidden when `prior_notice_start_day` value is not set in
* booking_rules.txt.
*/
@GtfsValidationNotice(
severity = SeverityLevel.ERROR,
files = @FileRefs(GtfsBookingRulesSchema.class))
static class ForbiddenPriorNoticeStartTimeNotice extends ValidationNotice {
/** The row number of the faulty record. */
private final int csvRowNumber;

/** The `booking_rules.booking_rule_id` of the faulty record. */
private final String bookingRuleId;

/** The value of the `prior_notice_start_time` field. */
private final GtfsTime priorNoticeStartTime;

ForbiddenPriorNoticeStartTimeNotice(GtfsBookingRules bookingRule) {
this.csvRowNumber = bookingRule.csvRowNumber();
this.bookingRuleId = bookingRule.bookingRuleId();
this.priorNoticeStartTime = bookingRule.priorNoticeStartTime();
}
}

/**
* `prior_notice_start_time` value is required when `prior_notice_start_day` value is set in
* booking_rules.txt.
*/
@GtfsValidationNotice(
severity = SeverityLevel.ERROR,
files = @FileRefs(GtfsBookingRulesSchema.class))
static class MissingPriorNoticeStartTimeNotice extends ValidationNotice {
/** The row number of the faulty record. */
private final int csvRowNumber;

/** The `booking_rules.booking_rule_id` of the faulty record. */
private final String bookingRuleId;

/** The value of the `prior_notice_start_day` field. */
private final int priorNoticeStartDay;

MissingPriorNoticeStartTimeNotice(GtfsBookingRules bookingRule) {
this.csvRowNumber = bookingRule.csvRowNumber();
this.bookingRuleId = bookingRule.bookingRuleId();
this.priorNoticeStartDay = bookingRule.priorNoticeStartDay();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public void scheduledBookingShouldNotGenerateNotice() {
GtfsBookingRules bookingRule =
new GtfsBookingRules.Builder()
.setCsvRowNumber(1)
.setPriorNoticeDurationMin(1)
.setBookingRuleId("rule-3")
.setBookingType(GtfsBookingType.SAMEDAY)
.build();
Expand All @@ -76,7 +77,7 @@ public void realTimeBookingWithMultipleForbiddenFieldsShouldGenerateNotice() {
.setCsvRowNumber(1)
.setBookingRuleId("rule-4")
.setBookingType(REALTIME)
.setPriorNoticeDurationMax(60) // Forbidden field
.setPriorNoticeStartDay(5) // Forbidden field
.setPriorNoticeStartTime(GtfsTime.fromSecondsSinceMidnight(2)) // Forbidden field
.setPriorNoticeServiceId("service-1") // Forbidden field
.build();
Expand All @@ -86,7 +87,7 @@ public void realTimeBookingWithMultipleForbiddenFieldsShouldGenerateNotice() {
new ForbiddenRealTimeBookingFieldValueNotice(
bookingRule,
List.of(
GtfsBookingRules.PRIOR_NOTICE_DURATION_MAX_FIELD_NAME,
GtfsBookingRules.PRIOR_NOTICE_START_DAY_FIELD_NAME,
GtfsBookingRules.PRIOR_NOTICE_START_TIME_FIELD_NAME,
GtfsBookingRules.PRIOR_NOTICE_SERVICE_ID_FIELD_NAME)));
}
Expand All @@ -99,7 +100,7 @@ public void sameDayBookingWithForbiddenFieldsShouldGenerateNotice() {
.setBookingRuleId("rule-5")
.setBookingType(GtfsBookingType.SAMEDAY)
.setPriorNoticeLastDay(2) // Forbidden field
.setPriorNoticeStartTime(GtfsTime.fromSecondsSinceMidnight(5000))
.setPriorNoticeDurationMin(1)
.build();

assertThat(generateNotices(bookingRule))
Expand All @@ -113,6 +114,7 @@ public void sameDayBookingWithoutForbiddenFieldsShouldNotGenerateNotice() {
GtfsBookingRules bookingRule =
new GtfsBookingRules.Builder()
.setCsvRowNumber(1)
.setPriorNoticeDurationMin(1)
.setBookingRuleId("rule-6")
.setBookingType(GtfsBookingType.SAMEDAY)
.build();
Expand All @@ -127,6 +129,8 @@ public void priorDayBookingWithForbiddenFieldsShouldGenerateNotice() {
.setCsvRowNumber(1)
.setBookingRuleId("rule-7")
.setBookingType(GtfsBookingType.PRIORDAY)
.setPriorNoticeLastDay(1)
.setPriorNoticeLastTime(GtfsTime.fromSecondsSinceMidnight(5000))
.setPriorNoticeDurationMin(30) // Forbidden field
.setPriorNoticeDurationMax(60) // Forbidden field
.build();
Expand All @@ -153,24 +157,25 @@ public void invalidPriorNoticeDurationMinShouldGenerateNotice() {

assertThat(generateNotices(bookingRule))
.containsExactly(
new BookingRulesEntityValidator.InvalidPriorNoticeDurationMinNotice(
bookingRule, 60, 30));
new BookingRulesEntityValidator.InvalidPriorNoticeDurationMinNotice(bookingRule));
}

@Test
public void forbiddenPriorNoticeStartDayShouldGenerateNotice() {
GtfsBookingRules bookingRule =
new GtfsBookingRules.Builder()
.setCsvRowNumber(1)
.setPriorNoticeDurationMin(1)
.setBookingRuleId("rule-9")
.setBookingType(GtfsBookingType.SAMEDAY)
.setPriorNoticeStartTime(GtfsTime.fromSecondsSinceMidnight(5000))
.setPriorNoticeDurationMax(30) // Duration max is set
.setPriorNoticeStartDay(5) // Forbidden when duration max is set
.build();

assertThat(generateNotices(bookingRule))
.containsExactly(
new BookingRulesEntityValidator.ForbiddenPriorNoticeStartDayNotice(bookingRule, 5, 30));
new BookingRulesEntityValidator.ForbiddenPriorNoticeStartDayNotice(bookingRule));
}

@Test
Expand All @@ -185,4 +190,96 @@ public void priorNoticeLastDayAfterStartDayShouldGenerateNotice() {
assertThat(generateNotices(bookingRule))
.contains(new PriorNoticeLastDayAfterStartDayNotice(bookingRule));
}

@Test
public void missingPriorNoticeDurationMinShouldGenerateNotice() {
GtfsBookingRules bookingRule =
new GtfsBookingRules.Builder()
.setCsvRowNumber(1)
.setBookingRuleId("rule-10")
.setBookingType(GtfsBookingType.SAMEDAY) // SAMEDAY booking type
.build(); // No prior_notice_duration_min set

assertThat(generateNotices(bookingRule))
.containsExactly(
new BookingRulesEntityValidator.MissingPriorNoticeDurationMinNotice(bookingRule));
}

@Test
public void missingPriorDayBookingFieldValueShouldGenerateNotice() {
// Case 1: Missing both prior_notice_last_day and prior_notice_last_time
GtfsBookingRules bookingRule =
new GtfsBookingRules.Builder()
.setCsvRowNumber(1)
.setBookingRuleId("rule-11")
.setBookingType(GtfsBookingType.PRIORDAY) // PRIORDAY booking type
.build(); // No prior_notice_last_day or prior_notice_last_time set

assertThat(generateNotices(bookingRule))
.containsExactly(
new BookingRulesEntityValidator.MissingPriorDayBookingFieldValueNotice(bookingRule));

// Case 2: Missing prior_notice_last_time only
GtfsBookingRules bookingRuleMissingTime =
new GtfsBookingRules.Builder()
.setCsvRowNumber(2)
.setBookingRuleId("rule-12")
.setBookingType(GtfsBookingType.PRIORDAY) // PRIORDAY booking type
.setPriorNoticeLastDay(2) // Setting prior_notice_last_day
.build(); // No prior_notice_last_time set

assertThat(generateNotices(bookingRuleMissingTime))
.containsExactly(
new BookingRulesEntityValidator.MissingPriorDayBookingFieldValueNotice(
bookingRuleMissingTime));

// Case 3: Missing prior_notice_last_day only
GtfsBookingRules bookingRuleMissingDay =
new GtfsBookingRules.Builder()
.setCsvRowNumber(3)
.setBookingRuleId("rule-13")
.setBookingType(GtfsBookingType.PRIORDAY) // PRIORDAY booking type
.setPriorNoticeLastTime(
GtfsTime.fromSecondsSinceMidnight(5000)) // Setting prior_notice_last_time
.build(); // No prior_notice_last_day set

assertThat(generateNotices(bookingRuleMissingDay))
.containsExactly(
new BookingRulesEntityValidator.MissingPriorDayBookingFieldValueNotice(
bookingRuleMissingDay));
}

@Test
public void forbiddenPriorNoticeStartTimeShouldGenerateNotice() {
GtfsBookingRules bookingRule =
new GtfsBookingRules.Builder()
.setCsvRowNumber(2)
.setPriorNoticeLastTime(GtfsTime.fromSecondsSinceMidnight(5000))
.setPriorNoticeLastDay(2)
.setBookingRuleId("rule-14")
.setBookingType(GtfsBookingType.PRIORDAY)
.setPriorNoticeStartTime(GtfsTime.fromSecondsSinceMidnight(5000)) // Set start time
.build(); // No prior_notice_start_day set

assertThat(generateNotices(bookingRule))
.containsExactly(
new BookingRulesEntityValidator.ForbiddenPriorNoticeStartTimeNotice(bookingRule));
}

@Test
public void missingPriorNoticeStartTimeShouldGenerateNotice() {
GtfsBookingRules bookingRule =
new GtfsBookingRules.Builder()
.setCsvRowNumber(2)
.setPriorNoticeLastTime(GtfsTime.fromSecondsSinceMidnight(5000))
.setPriorNoticeLastDay(2)
.setBookingRuleId("rule-14")
.setBookingType(GtfsBookingType.PRIORDAY)
.setPriorNoticeStartDay(3) // Set start day
.build(); // No prior_notice_start_time set

assertThat(generateNotices(bookingRule))
.containsExactly(
new BookingRulesEntityValidator.MissingPriorNoticeStartTimeNotice(bookingRule));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ public void testNoticeClassFieldNames() {
"priorNoticeDurationMin",
"priorNoticeLastDay",
"priorNoticeStartDay",
"priorNoticeStartTime",
"recordId",
"recordSubId",
"routeColor",
Expand Down

0 comments on commit e3b9f3d

Please sign in to comment.