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

Replace feed_expiration_date with rules for 7 and 30 days #1245

39 changes: 32 additions & 7 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,14 @@ Each Notice is associated with a severity: `INFO`, `WARNING`, `ERROR`.
| [`equal_shape_distance_same_coordinates`](#equal_shape_distance_same_coordinates) | Two consecutive points have equal `shape_dist_traveled` and the same lat/lon coordinates in `shapes.txt`. |
| [`fast_travel_between_consecutive_stops`](#fast_travel_between_consecutive_stops) | A transit vehicle moves too fast between two consecutive stops. |
| [`fast_travel_between_far_stops`](#fast_travel_between_far_stops) | A transit vehicle moves too fast between two far stops. |
| [`feed_expiration_date`](#feed_expiration_date) | Dataset should be valid for at least the next 7 days. Dataset should cover at least the next 30 days of service. |
| [`feed_expiration_date_7_days`](#feed_expiration_date_7_days) | Dataset should be valid for at least the next 7 days. |
| [`feed_expiration_date_30_days`](#feed_expiration_date_30_days) | Dataset should cover at least the next 30 days of service. |
| [`feed_info_lang_and_agency_mismatch`](#feed_info_lang_and_agency_mismatch) | Mismatching feed and agency language fields. |
| [`inconsistent_agency_lang`](#inconsistent_agency_lang) | Inconsistent language among agencies. |
| [`leading_or_trailing_whitespaces`](#leading_or_trailing_whitespaces) | The value in CSV file has leading or trailing whitespaces. |
| [`missing_feed_info_date`](#missing_feed_info_date) | `feed_end_date` should be provided if `feed_start_date` is provided. `feed_start_date` should be provided if `feed_end_date` is provided. |
| [`missing_recommended_file`](#missing_recommended_file) | A recommended file is missing. |
| [`missing_recommended_field`](#missing_recommended_field) | A recommended field is missing. |
| [`missing_recommended_file`](#missing_recommended_file) | A recommended file is missing. |
| [`missing_recommended_field`](#missing_recommended_field) | A recommended field is missing. |
| [`missing_timepoint_column`](#missing_timepoint_column) | `timepoint` column is missing for a dataset. |
| [`missing_timepoint_value`](#missing_timepoint_value) | `stop_times.timepoint` value is missing for a record. |
| [`more_than_one_entity`](#more_than_one_entity) | More than one row in CSV. |
Expand All @@ -127,6 +128,7 @@ Each Notice is associated with a severity: `INFO`, `WARNING`, `ERROR`.
| [`unusable_trip`](#unusable_trip) | Trips must have more than one stop to be usable. |
| [`unused_shape`](#unused_shape) | Shape is not used in GTFS file `trips.txt`. |
| [`unused_trip`](#unused_trip) | Trip is not be used in `stop_times.txt` |
| | |

<a name="INFOS"/>

Expand Down Expand Up @@ -1603,12 +1605,35 @@ Same as for [`FastTravelBetweenConsecutiveStopsNotice`](#FastTravelBetweenConsec

</details>

<a name="FeedExpirationDateNotice"/>
<a name="FeedExpirationDate7DaysNotice"/>

### feed_expiration_date
### feed_expiration_date_7_days

At any time, the published GTFS dataset should be valid for at least the next 7 days, and ideally for as long as the operator is confident that the schedule will continue to be operated.
If possible, the GTFS dataset should cover at least the next 30 days of service.
The dataset expiration date defined in `feed_info.txt` is in seven days or less. At any time, the published GTFS dataset should be valid for at least the next 7 days.

### References
* [General Publishing & General Practices](https://gtfs.org/best-practices/#dataset-publishing--general-practices)

<details>

#### Notice fields description
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this table doesn't render, I think we need a line break after "Notice fields description".

| Field name | Description | Type |
|------------------------- |---------------------------------------------- |-------- |
| `csvRowNumber` | The row number of the faulty record. | Long |
| `currentDate` | Current date (YYYYMMDD format). | String |
| `feedEndDate` | Feed end date (YYYYMMDD format). | String |
| `suggestedExpirationDate` | Suggested expiration date (YYYYMMDD format). | String |

#### Affected files
* [`feed_info.txt`](http://gtfs.org/reference/static#feed_infotxt)
</details>


<a name="FeedExpirationDate30DaysNotice"/>

### feed_expiration_date_30_days

At any time, the GTFS dataset should cover at least the next 30 days of service, and ideally for as long as the operator is confident that the schedule will continue to be operated.

#### References
* [General Publishing & General Practices](https://gtfs.org/best-practices/#dataset-publishing--general-practices)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
* <p>Generated notice:
*
* <ul>
* <li>{@link FeedExpirationDateNotice}
* <li>{@link FeedExpirationDate7DaysNotice}
* <li>{@link FeedExpirationDate30DaysNotice}
* </ul>
*/
@GtfsValidator
Expand All @@ -53,9 +54,9 @@ public void validate(GtfsFeedInfo entity, NoticeContainer noticeContainer) {
GtfsDate currentDate = GtfsDate.fromLocalDate(now);
GtfsDate currentDatePlusSevenDays = GtfsDate.fromLocalDate(now.plusDays(7));
GtfsDate currentDatePlusThirtyDays = GtfsDate.fromLocalDate(now.plusDays(30));
if (entity.feedEndDate().isBefore(currentDatePlusSevenDays)) {
if (entity.feedEndDate().compareTo(currentDatePlusSevenDays) <= 0) {
noticeContainer.addValidationNotice(
new FeedExpirationDateNotice(
new FeedExpirationDate7DaysNotice(
entity.csvRowNumber(),
currentDate,
entity.feedEndDate(),
Expand All @@ -64,7 +65,7 @@ public void validate(GtfsFeedInfo entity, NoticeContainer noticeContainer) {
}
if (entity.feedEndDate().compareTo(currentDatePlusThirtyDays) <= 0) {
noticeContainer.addValidationNotice(
new FeedExpirationDateNotice(
new FeedExpirationDate30DaysNotice(
entity.csvRowNumber(),
currentDate,
entity.feedEndDate(),
Expand All @@ -73,13 +74,32 @@ public void validate(GtfsFeedInfo entity, NoticeContainer noticeContainer) {
}
}

static class FeedExpirationDateNotice extends ValidationNotice {
static class FeedExpirationDate7DaysNotice extends ValidationNotice {
private final long csvRowNumber;
private final GtfsDate currentDate;
private final GtfsDate feedEndDate;
private final GtfsDate suggestedExpirationDate;

FeedExpirationDateNotice(
FeedExpirationDate7DaysNotice(
long csvRowNumber,
GtfsDate currentDate,
GtfsDate feedEndDate,
GtfsDate suggestedExpirationDate) {
super(SeverityLevel.WARNING);
this.csvRowNumber = csvRowNumber;
this.currentDate = currentDate;
this.feedEndDate = feedEndDate;
this.suggestedExpirationDate = suggestedExpirationDate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to specify a suggested expiration date here? The real suggestion is for as long as the operator is confident that the schedule will continue to be operated, as per the Best Practices.
I see a risk of sending the wrong message, and not much added value.
@bdferris, thoughts?

Copy link
Contributor

@maximearmstrong maximearmstrong Sep 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we don't need it, but if you look at the original FeedExpirationDateNotice, the field was already included. If we decide to remove it, maybe it we should open another PR to make the refactor.

}
}

static class FeedExpirationDate30DaysNotice extends ValidationNotice {
private final long csvRowNumber;
private final GtfsDate currentDate;
private final GtfsDate feedEndDate;
private final GtfsDate suggestedExpirationDate;

FeedExpirationDate30DaysNotice(
long csvRowNumber,
GtfsDate currentDate,
GtfsDate feedEndDate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
import org.mobilitydata.gtfsvalidator.notice.ValidationNotice;
import org.mobilitydata.gtfsvalidator.table.GtfsFeedInfo;
import org.mobilitydata.gtfsvalidator.type.GtfsDate;
import org.mobilitydata.gtfsvalidator.validator.FeedExpirationDateValidator.FeedExpirationDateNotice;
import org.mobilitydata.gtfsvalidator.validator.FeedExpirationDateValidator.FeedExpirationDate30DaysNotice;
import org.mobilitydata.gtfsvalidator.validator.FeedExpirationDateValidator.FeedExpirationDate7DaysNotice;

public class FeedExpirationDateValidatorTest {
private static final ZonedDateTime TEST_NOW =
Expand Down Expand Up @@ -56,7 +57,7 @@ public void feedExpiringInFiveDaysFromNowShouldGenerateNotice() {
validateFeedInfo(
createFeedInfo(GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(3)))))
.containsExactly(
new FeedExpirationDateNotice(
new FeedExpirationDate7DaysNotice(
1,
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate()),
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(3)),
Expand All @@ -69,11 +70,11 @@ public void feedExpiringInSevenDaysFromNowShouldGenerateNotice() {
validateFeedInfo(
createFeedInfo(GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(7)))))
.containsExactly(
new FeedExpirationDateNotice(
new FeedExpirationDate7DaysNotice(
1,
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate()),
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(7)),
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(30))));
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(7))));
}

@Test
Expand All @@ -82,7 +83,7 @@ public void feedExpiring7to30DaysFromNowShouldGenerateNotice() {
validateFeedInfo(
createFeedInfo(GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(23)))))
.containsExactly(
new FeedExpirationDateNotice(
new FeedExpirationDate30DaysNotice(
1,
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate()),
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(23)),
Expand All @@ -95,7 +96,7 @@ public void feedExpiring30DaysFromNowShouldGenerateNotice() {
validateFeedInfo(
createFeedInfo(GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(30)))))
.containsExactly(
new FeedExpirationDateNotice(
new FeedExpirationDate30DaysNotice(
1,
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate()),
GtfsDate.fromLocalDate(TEST_NOW.toLocalDate().plusDays(30)),
Expand Down