Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a82bed7
feat(Additional feed summary params):
br648 Sep 24, 2025
af7ec62
improvement(Update to include gtfs plus validation): Gen gtfs plus va…
br648 Oct 9, 2025
075cdc7
improvement(Store gtfs plus validation in feed version): Update to fe…
br648 Oct 10, 2025
a9fb58d
improvement(Updates param name): Name is more inline with usage
br648 Oct 10, 2025
a1a056f
improvement(Updated feed source summary): Added published version id
br648 Oct 15, 2025
7a5ce48
improvement(GtfsPlusController): Update to save validation to related…
br648 Oct 16, 2025
43756e8
improvement(maven.yml): Bumped node version from 20.x to 22.x
br648 Oct 17, 2025
dde3bc4
Merge branch 'dev' into feature/DT-530-additional-feed-source-summary…
br648 Oct 17, 2025
fcdbd93
improvement(Added new params): Now includes published feed version an…
br648 Oct 23, 2025
21c3b53
Merge branch 'dev' into feature/DT-530-additional-feed-source-summary…
br648 Nov 6, 2025
fed3371
improvement(Update to include error counts): Feed source summary now …
br648 Nov 11, 2025
a437f5d
improvement(Code refactor): Move feed version extraction into FeedVer…
br648 Nov 12, 2025
50455b9
improvement(FeedSourceSummary): Update to mongo query to return resul…
br648 Nov 13, 2025
e02a53f
improvement(Addressed bug with extracting feed source summaries): Upd…
br648 Nov 18, 2025
70eef64
improvement(FeedSourceSummary): Updated query to get latest feed vers…
br648 Nov 18, 2025
2b6cc83
improvement(Feed source error count retrieval): New separate class to…
br648 Dec 2, 2025
308de2f
improvement(Fixed merge conflicts): Addressed conflicts in class Feed…
br648 Dec 10, 2025
ec3a329
feat(Exclude editor schemas from deletion): Data sanitizer update to …
br648 Dec 11, 2025
0572b6a
improvement(Restored to previous commit to remove data sanitizer upda…
br648 Dec 11, 2025
3ea77c1
Merge branch 'dev' into feature/DT-530-additional-feed-source-summary…
br648 Dec 11, 2025
4310c1b
improvement(Refactor to provide publish state and address PR feedback):
br648 Dec 15, 2025
cb06d5f
improvement(pom.xml): Restored gtfs-lib library reference
br648 Dec 15, 2025
259d4c3
Merge branch 'dev' into feature/DT-530-additional-feed-source-summary…
br648 Dec 17, 2025
ac2f2c7
improvement(Addressed PR feedback):
br648 Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.conveyal.datatools.manager.controllers;

import com.conveyal.datatools.common.status.MonitorableJob;
import com.conveyal.datatools.manager.auth.Auth0UserProfile;
import com.conveyal.datatools.manager.jobs.ProcessSingleFeedJob;
import com.conveyal.datatools.manager.jobs.ValidateFeedJob;
import com.conveyal.datatools.manager.jobs.ValidateGtfsPlusFeedJob;
import com.conveyal.datatools.manager.jobs.ValidateMobilityDataFeedJob;
import com.conveyal.datatools.manager.models.Deployment;
import com.conveyal.datatools.manager.models.ExternalFeedSourceProperty;
Expand Down Expand Up @@ -365,6 +365,7 @@ public static boolean validateAll (boolean load, boolean force, String filterFee
} else {
JobUtils.heavyExecutor.execute(new ValidateFeedJob(version, systemUser, false));
JobUtils.heavyExecutor.execute(new ValidateMobilityDataFeedJob(version, systemUser, false));
JobUtils.heavyExecutor.execute(new ValidateGtfsPlusFeedJob(version, systemUser, false));
}
}
// ValidateAllFeedsJob validateAllFeedsJob = new ValidateAllFeedsJob("system", force, load);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,17 +249,17 @@ private static String publishGtfsPlusFile(Request req, Response res) {
}

/**
* HTTP endpoint that validates GTFS+ tables for a specific feed version (or its saved/edited GTFS+).
* HTTP endpoint that validates GTFS+ tables for a specific feed version (or its saved/edited GTFS+). If the feed
* version already has GTFS+ validation results, those will be returned instead of re-validating.
*/
private static GtfsPlusValidation getGtfsPlusValidation(Request req, Response res) {
String feedVersionId = req.params("versionid");
GtfsPlusValidation gtfsPlusValidation = null;
try {
gtfsPlusValidation = GtfsPlusValidation.validate(feedVersionId);
String feedVersionId = req.params("versionid");
return GtfsPlusValidation.validate(feedVersionId);
} catch(Exception e) {
logMessageAndHalt(req, 500, "Could not read GTFS+ zip file", e);
}
return gtfsPlusValidation;
return null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,47 @@ private GtfsPlusValidation (String feedVersionId) {
this.feedVersionId = feedVersionId;
}

public GtfsPlusValidation(String feedVersionId, boolean published, List<ValidationIssue> issues) {
this(feedVersionId);
this.published = published;
this.issues = issues;
}

public GtfsPlusValidation() {
this(null);
// Empty constructor for serialization
}

/**
* Validate a GTFS+ feed and return a list of issues encountered.
* FIXME: For now this uses the MapDB-backed GTFSFeed class. Which actually suggests that this might
* should be contained within a MonitorableJob.
* Overload method to retrieve the feed version.
*/
public static GtfsPlusValidation validate(String feedVersionId) throws Exception {
GtfsPlusValidation validation = new GtfsPlusValidation(feedVersionId);
FeedVersion feedVersion = Persistence.feedVersions.getById(feedVersionId);
if (feedVersion != null) {
if (feedVersion.gtfsPlusValidation != null) {
return feedVersion.gtfsPlusValidation;
}
GtfsPlusValidation gtfsPlusValidation = validate(feedVersion);
feedVersion.gtfsPlusValidation = gtfsPlusValidation;
Persistence.feedVersions.replace(feedVersion.id, feedVersion);
return gtfsPlusValidation;
}
return null;
}

/**
* Validate a GTFS+ feed and return a list of issues encountered.
*/
public static GtfsPlusValidation validate(FeedVersion feedVersion) throws Exception {
if (!DataManager.isModuleEnabled("gtfsplus")) {
throw new IllegalStateException("GTFS+ module must be enabled in server.yml to run GTFS+ validation.");
}
LOG.info("Validating GTFS+ for {}", feedVersionId);
GtfsPlusValidation validation = new GtfsPlusValidation(feedVersion.id);
LOG.info("Validating GTFS+ for {}", feedVersion.id);

FeedVersion feedVersion = Persistence.feedVersions.getById(feedVersionId);
// Load the main GTFS file.
// FIXME: Swap MapDB-backed GTFSFeed for use of SQL data?
File gtfsFeedDbFile = gtfsPlusStore.getFeedFile(feedVersionId + ".db");
File gtfsFeedDbFile = gtfsPlusStore.getFeedFile(feedVersion.id + ".db");
String gtfsFeedDbFilePath = gtfsFeedDbFile.getAbsolutePath();
GTFSFeed gtfsFeed;
try {
Expand All @@ -87,7 +112,7 @@ public static GtfsPlusValidation validate(String feedVersionId) throws Exception
}

// check for saved GTFS+ data
File file = gtfsPlusStore.getFeed(feedVersionId);
File file = gtfsPlusStore.getFeed(feedVersion.id);
if (file == null) {
validation.published = true;
LOG.warn("GTFS+ Validation -- Modified GTFS+ file not found, loading from main version GTFS.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public class ValidationIssue implements Serializable {
public int rowIndex;
public String description;

public ValidationIssue() {
// Empty constructor for serialization
}

public ValidationIssue(String tableId, String fieldName, int rowIndex, String description) {
this.tableId = tableId;
this.fieldName = fieldName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class ProcessSingleFeedJob extends FeedVersionJob {
public static boolean ENABLE_MTC_TRANSFORMATIONS = true;

// Used in testing to skip validation and speed up response times.
public static boolean VALIDATE_MOBILITY_DATA = true;
public static boolean ENABLE_ADDITIONAL_VALIDATION = true;

/**
* Create a job for the given feed version.
Expand Down Expand Up @@ -131,8 +131,9 @@ public void jobLogic() {

// Next, validate the feed.
addNextJob(new ValidateFeedJob(feedVersion, owner, isNewVersion));
if (VALIDATE_MOBILITY_DATA) {
if (ENABLE_ADDITIONAL_VALIDATION) {
addNextJob(new ValidateMobilityDataFeedJob(feedVersion, owner, isNewVersion));
addNextJob(new ValidateGtfsPlusFeedJob(feedVersion, owner, isNewVersion));
}

// We only need to snapshot the feed if there are transformations at the database level. In the case that there
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.conveyal.datatools.manager.jobs;

import com.conveyal.datatools.common.status.FeedVersionJob;
import com.conveyal.datatools.manager.auth.Auth0UserProfile;
import com.conveyal.datatools.manager.models.FeedVersion;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This job handles the GTFS+ validation of a given feed version. If the version is not new, it will simply
* replace the existing version with the version object that has updated validation info.
*/
public class ValidateGtfsPlusFeedJob extends FeedVersionJob {
public static final Logger LOG = LoggerFactory.getLogger(ValidateGtfsPlusFeedJob.class);

private final FeedVersion feedVersion;
private final boolean isNewVersion;

public ValidateGtfsPlusFeedJob(FeedVersion version, Auth0UserProfile owner, boolean isNewVersion) {
super(owner, "Validating GTFS+", JobType.VALIDATE_FEED);
feedVersion = version;
this.isNewVersion = isNewVersion;
status.update("Waiting to begin GTFS+ validation...", 0);
}

@Override
public void jobLogic () {
LOG.info("Running ValidateGtfsPlusFeedJob for {}", feedVersion.id);
feedVersion.validateGtfsPlus(status);
}

@Override
public void jobFinished () {
if (!status.error) {
if (parentJobId != null && JobType.PROCESS_FEED.equals(parentJobType)) {
// Validate stage is happening as part of an overall process feed job.
// At this point all GTFS data has been loaded and validated, so we record
// the FeedVersion into mongo.
// This happens here because otherwise we would have to wait for other jobs,
// such as BuildTransportNetwork, to finish. If those subsequent jobs fail,
// the version won't get loaded into MongoDB (even though it exists in postgres).
feedVersion.persistFeedVersionAfterValidation(isNewVersion);
}
status.completeSuccessfully("GTFS+ validation finished!");
} else {
// If the version was not stored successfully, call FeedVersion#delete to reset things to before the version
// was uploaded/fetched. Note: delete calls made to MongoDB on the version ID will not succeed, but that is
// expected.
feedVersion.delete();
}
}

/**
* Getter that allows a client to know the ID of the feed version that will be created as soon as the upload is
* initiated; however, we will not store the FeedVersion in the mongo application database until the upload and
* processing is completed. This prevents clients from manipulating GTFS data before it is entirely imported.
*/
@JsonProperty
public String getFeedVersionId () {
return feedVersion.id;
}

@JsonProperty
public String getFeedSourceId () {
return feedVersion.parentFeedSource().id;
}


}
Loading
Loading