Skip to content

Commit

Permalink
feat: transfers.txt trip=>route reference validation. (#1267)
Browse files Browse the repository at this point in the history
* Initial transfers.txt trip=>route reference validation.

* Use a FileValidator to support injection.

* Add trip=>stop reference checks.

* More transfers.txt validation.

* Adding comments, unit-tests, and documentation for new validators.

* Table formatting.
  • Loading branch information
bdferris-v2 authored Oct 17, 2022
1 parent 23366db commit ba7a83e
Show file tree
Hide file tree
Showing 5 changed files with 589 additions and 0 deletions.
70 changes: 70 additions & 0 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ Each Notice is associated with a severity: `INFO`, `WARNING`, `ERROR`.
| [`stop_time_with_arrival_before_previous_departure_time`](#stop_time_with_arrival_before_previous_departure_time) | Backwards time travel between stops in `stop_times.txt` |
| [`stop_time_with_only_arrival_or_departure_time`](#stop_time_with_only_arrival_or_departure_time) | Missing `stop_times.arrival_time` or `stop_times.departure_time`. |
| [`stop_without_zone_id`](#stop_without_zone_id) | Stop without value for `stops.zone_id`. |
| [`transfer_with_invalid_stop_location_type`](#transfer_with_invalid_stop_location_type) | A stop id field from GTFS file `transfers.txt` references a stop that has a `location_type` other than 0 or 1 (aka Stop/Platform or Station). |
| [`transfer_with_invalid_trip_and_route`](#transfer_with_invalid_trip_and_route) | A trip id field from GTFS file `transfers.txt` references a route that does not match its `trips.txt` `route_id`. |
| [`transfer_with_invalid_trip_and_stop`](#transfer_with_invalid_trip_and_stop) | A trip id field from GTFS file `transfers.txt` references a stop that is not included in the referenced trip's stop-times. |
| [`translation_foreign_key_violation`](#translation_foreign_key_violation) | An entity with the given `record_id` and `record_sub_id` cannot be found in the referenced table. |
| [`translation_unexpected_value`](#translation_unexpected_value) | A field in a translations row has value but must be empty. |
| [`wrong_parent_location_type`](#wrong_parent_location_type) | Incorrect type of the parent location. |
Expand Down Expand Up @@ -1455,6 +1458,73 @@ If `fare_rules.txt` is provided, and `fare_rules.txt` uses at least one column a

</details>

<a name="TransferWithInvalidStopLocationTypeNotice"/>

### transfer_with_invalid_stop_location_type

A `from_stop_id` or `to_stop_id` field from GTFS file `transfers.txt` references a stop that has a `location_type` other than 0 or 1 (aka Stop/Platform or Station).

#### References
* [transfers.txt specification](http://gtfs.org/reference/static/#transferstxt)

<details>

#### Notice fields description
| Field name | Description | Type |
|---------------------|---------------------------------------------------------------------------|--------|
| `csvRowNumber` | The row number from `transfers.txt` for the faulty entry. | long |
| `stopIdFieldName` | The name of the stop id field (e.g. `from_stop_id`) referencing the stop. | String |
| `stopId` | The referenced stop id. | String |
| `locationTypeValue` | The numeric value of the invalid location type. | int |
| `locationTypeName` | The name of the invalid location type. | String |

</details>

<a name="TransferWithInvalidTripAndRouteNotice"/>

### transfer_with_invalid_trip_and_route

A `from_trip_id` or `to_trip_id` field from GTFS file `transfers.txt` references a route that does not match its `trips.txt` `route_id`.

#### References
* [transfers.txt specification](http://gtfs.org/reference/static/#transferstxt)

<details>

#### Notice fields description
| Field name | Description | Type |
|-------------------|------------------------------------------------------------------------------|--------|
| `csvRowNumber` | The row number from `transfers.txt` for the faulty entry. | long |
| `tripFieldName` | The name of the trip id field (e.g. `from_trip_id`) referencing a trip. | String |
| `tripId` | The referenced trip id. | String |
| `routeFieldName` | The name of the route id field (e.g. `from_route_id`) referencing the route. | String |
| `routeId` | The referenced route id. | String |
| `expectedRouteId` | The expected route id from `trips.txt`. | String |

</details>

<a name="TransferWithInvalidTripAndStopNotice"/>

### transfer_with_invalid_trip_and_stop

A `from_trip_id` or `to_trip_id` field from GTFS file `transfers.txt` references a stop that is not included in the referenced trip's stop-times.

#### References
* [transfers.txt specification](http://gtfs.org/reference/static/#transferstxt)

<details>

#### Notice fields description
| Field name | Description | Type |
|-----------------|----------------------------------------------------------------------------|--------|
| `csvRowNumber` | The row number from `transfers.txt` for the faulty entry. | long |
| `tripFieldName` | The name of the trip id field (e.g. `from_trip_id`) referencing a trip. | String |
| `tripId` | The referenced trip id. | String |
| `stopFieldName` | The name of the stop id field (e.g. `stop_route_id`) referencing the stop. | String |
| `stopId` | The referenced stop id. | String |

</details>

<a name="TranslationForeignKeyViolationNotice"/>

### translation_foreign_key_violation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.mobilitydata.gtfsvalidator.validator;

import java.util.Optional;
import javax.inject.Inject;
import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator;
import org.mobilitydata.gtfsvalidator.notice.NoticeContainer;
import org.mobilitydata.gtfsvalidator.notice.SeverityLevel;
import org.mobilitydata.gtfsvalidator.notice.ValidationNotice;
import org.mobilitydata.gtfsvalidator.table.GtfsLocationType;
import org.mobilitydata.gtfsvalidator.table.GtfsStop;
import org.mobilitydata.gtfsvalidator.table.GtfsStopTableContainer;
import org.mobilitydata.gtfsvalidator.table.GtfsTransfer;
import org.mobilitydata.gtfsvalidator.table.GtfsTransferTableContainer;
import org.mobilitydata.gtfsvalidator.table.GtfsTransferTableLoader;

/**
* Validates that {@code transfers.from_stop_id} and {@code to_stop_id} reference stops or stations.
*/
@GtfsValidator
public class TransfersStopTypeValidator extends FileValidator {
private final GtfsTransferTableContainer transfersContainer;
private final GtfsStopTableContainer stopsContainer;

@Inject
public TransfersStopTypeValidator(
GtfsTransferTableContainer transfersContainer, GtfsStopTableContainer stopsContainer) {
this.transfersContainer = transfersContainer;
this.stopsContainer = stopsContainer;
}

@Override
public void validate(NoticeContainer noticeContainer) {
for (GtfsTransfer entity : transfersContainer.getEntities()) {
validateEntity(entity, noticeContainer);
}
}

public void validateEntity(GtfsTransfer entity, NoticeContainer noticeContainer) {
validateStopType(
entity,
GtfsTransferTableLoader.FROM_STOP_ID_FIELD_NAME,
entity.fromStopId(),
noticeContainer);
validateStopType(
entity, GtfsTransferTableLoader.TO_STOP_ID_FIELD_NAME, entity.toStopId(), noticeContainer);
}

private void validateStopType(
GtfsTransfer entity, String stopIdFieldName, String stopId, NoticeContainer noticeContainer) {
Optional<GtfsStop> optStop = stopsContainer.byStopId(stopId);
if (optStop.isEmpty()) {
// Foreign key reference is validated elsewhere.
return;
}

GtfsLocationType locationType = optStop.get().locationType();
if (!isValidTransferStopType(locationType)) {
noticeContainer.addValidationNotice(
new TransferWithInvalidStopLocationTypeNotice(
entity.csvRowNumber(), stopIdFieldName, stopId, locationType));
}
}

private static boolean isValidTransferStopType(GtfsLocationType locationType) {
switch (locationType) {
case STOP:
case STATION:
return true;
default:
return false;
}
}

/**
* A `from_stop_id` or `to_stop_id` field from GTFS file `transfers.txt` references a stop that
* has a `location_type` other than 0 or 1 (aka Stop/Platform or Station).
*
* <p>Severity: {@code SeverityLevel.ERROR}
*/
public static final class TransferWithInvalidStopLocationTypeNotice extends ValidationNotice {
// The row number from `transfers.txt` for the faulty entry.
private final long csvRowNumber;
// The name of the stop id field (e.g. `from_stop_id`) referencing the stop.
private final String stopIdFieldName;
// The referenced stop id.
private final String stopId;
// The numeric value of the invalid location type.
private final int locationTypeValue;
// The name of the invalid location type.
private String locationTypeName;

public TransferWithInvalidStopLocationTypeNotice(
long csvRowNumber, String stopIdFieldName, String stopId, GtfsLocationType locationType) {
super(SeverityLevel.ERROR);
this.csvRowNumber = csvRowNumber;
this.stopIdFieldName = stopIdFieldName;
this.stopId = stopId;
this.locationTypeValue = locationType.getNumber();
this.locationTypeName = locationType.toString();
}
}
}
Loading

0 comments on commit ba7a83e

Please sign in to comment.