diff --git a/configurations/test/server.yml.tmp b/configurations/test/server.yml.tmp index 677eee64c..c24f035fa 100644 --- a/configurations/test/server.yml.tmp +++ b/configurations/test/server.yml.tmp @@ -33,7 +33,7 @@ modules: gtfsapi: enabled: true load_on_fetch: false - # use_extension: mtc + use_extension: mtc # update_frequency: 30 # in seconds manager: normalizeFieldTransformation: @@ -74,8 +74,12 @@ extensions: enabled: true rtd_api: http://localhost:9876/ s3_bucket: bucket-name + ### Uncomment and edit if the S3 region of the bucket above is different than application.data.s3_region. + # s3_region: us-west-2 s3_prefix: waiting/ s3_download_prefix: waiting/ + ### Uncomment and edit if separate credentials are needed to access the bucket above. + # s3_credentials_file: ~/.aws/credentials transitland: enabled: true api: https://transit.land/api/v1/feeds diff --git a/pom.xml b/pom.xml index 113e49c11..aa23c463a 100644 --- a/pom.xml +++ b/pom.xml @@ -272,7 +272,7 @@ com.github.ibi-group gtfs-lib - dbbf3d166cf31fc9dae490ef4b8db42ec436f743 + 926b910e098fa8aba38ada01ce2f5bb7dd38e917 diff --git a/src/main/java/com/conveyal/datatools/common/utils/aws/S3Utils.java b/src/main/java/com/conveyal/datatools/common/utils/aws/S3Utils.java index 078781132..ada92fbbf 100644 --- a/src/main/java/com/conveyal/datatools/common/utils/aws/S3Utils.java +++ b/src/main/java/com/conveyal/datatools/common/utils/aws/S3Utils.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.net.URL; import java.util.Date; +import java.util.List; import java.util.UUID; import static com.conveyal.datatools.common.utils.SparkUtils.logMessageAndHalt; @@ -40,41 +41,21 @@ public class S3Utils { public static final String DEFAULT_BUCKET; public static final String DEFAULT_BUCKET_GTFS_FOLDER = "gtfs/"; + public static final String APP_DATA_S3_REGION = "application.data.s3_region"; + + public static final String APP_DATA_CREDS_FILE = "application.data.s3_credentials_file"; + static { // Placeholder variables need to be used before setting the final variable to make sure initialization occurs - AmazonS3 tempS3Client = null; AWSCredentialsProvider tempS3CredentialsProvider = null; String tempGtfsS3Bucket = null; S3ClientManagerImpl tempS3ClientManager = null; // Only configure s3 if the config requires doing so if (DataManager.useS3 || hasConfigProperty("modules.gtfsapi.use_extension")) { - try { - AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard(); - String credentialsFile = DataManager.getConfigPropertyAsText("application.data.s3_credentials_file"); - tempS3CredentialsProvider = credentialsFile != null ? - new ProfileCredentialsProvider(credentialsFile, "default") : - new DefaultAWSCredentialsProviderChain(); // default credentials providers, e.g. IAM role - builder.withCredentials(tempS3CredentialsProvider); - - // If region configuration string is provided, use that. - // Otherwise defaults to value provided in ~/.aws/config - String region = DataManager.getConfigPropertyAsText("application.data.s3_region"); - if (region != null) { - builder.withRegion(region); - } - tempS3Client = builder.build(); - } catch (Exception e) { - LOG.error( - "S3 client not initialized correctly. Must provide config property application.data.s3_region or specify region in ~/.aws/config", - e - ); - } - - if (tempS3Client == null) { - throw new IllegalArgumentException("Fatal error initializing the default s3Client"); - } - tempS3ClientManager = new S3ClientManagerImpl(tempS3Client); + S3Wrapper build = buildS3Wrapper(List.of(APP_DATA_CREDS_FILE), List.of(APP_DATA_S3_REGION)); + tempS3CredentialsProvider = build.credentials; + tempS3ClientManager = new S3ClientManagerImpl(build.s3Client); // s3 storage tempGtfsS3Bucket = DataManager.getConfigPropertyAsText("application.data.gtfs_s3_bucket"); @@ -89,6 +70,76 @@ public class S3Utils { DEFAULT_BUCKET = tempGtfsS3Bucket; } + public static class S3Wrapper { + public final AWSCredentialsProvider credentials; + public final AmazonS3 s3Client; + + public S3Wrapper(AWSCredentialsProvider credentials, AmazonS3 s3Client) { + this.credentials = credentials; + this.s3Client = s3Client; + } + } + + /** + * Builds an S3 client from the specified credentials and region, or fallback to defaults + * using the ambient IAM role. + * @param configCredentials The credentials file to use for the client. + * @param configRegions The configuration entries in the order to try to get the AWS region to apply. + * @return An S3 client for the provided credentials and region. + */ + public static S3Wrapper buildS3Wrapper( + List configCredentials, + List configRegions + ) throws IllegalArgumentException { + AmazonS3 s3client = null; + AWSCredentialsProvider credentialsProvider = null; + try { + AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard(); + // Iterate through the credentials and stop at the first non-null returned. + // Default to the ambient IAM credentials, if any. + for (String configCred : configCredentials) { + String credentialsFile = DataManager.getConfigPropertyAsText(configCred); + if (credentialsFile != null) { + try { + credentialsProvider = new ProfileCredentialsProvider(credentialsFile, "default"); + } catch (IllegalArgumentException e) { + LOG.error("Invalid credentials from {}. Trying the next one.", configCred, e); + } + if (credentialsProvider != null) break; + } + } + if (credentialsProvider == null) { + // default credentials providers, e.g. IAM role + credentialsProvider = new DefaultAWSCredentialsProviderChain(); + } + builder.withCredentials(credentialsProvider); + + // Iterate through the regions and stop at the first non-null returned. + // Otherwise defaults to value (typically provided in ~/.aws/config) + for (String configRegion : configRegions) { + String region = DataManager.getConfigPropertyAsText(configRegion); + if (region != null) { + builder.withRegion(region); + break; + } + } + + s3client = builder.build(); + } catch (Exception e) { + LOG.error( + "S3 client not initialized correctly. Must provide config property {} or specify region in ~/.aws/config", + configRegions, + e + ); + } + + if (s3client == null) { + throw new IllegalArgumentException("Fatal error initializing the default s3Client"); + } + + return new S3Wrapper(credentialsProvider, s3client); + } + /** * Makes a key for an object id that is assumed to be in the default bucket's GTFS folder */ @@ -105,7 +156,7 @@ public static String getDefaultBucketUriForKey(String key) { } public static String getDefaultBucketUrlForKey(String key) { - return String.format("https://s3.amazonaws.com/%s/%s", DEFAULT_BUCKET, key); + return String.format("https://%s.s3.amazonaws.com/%s", DEFAULT_BUCKET, key); } /** diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/EditorController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/EditorController.java index 671466860..5eec768a6 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/EditorController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/EditorController.java @@ -313,7 +313,7 @@ private String cascadeDeleteStop(Request req, Response res) { int stopSequence = patternStop.getValue(); // Begin with the stop prior to the one deleted, unless at the beginning. int beginWithSequence = (stopSequence != 0) ? stopSequence - 1 : stopSequence; - tableWriter.normalizeStopTimesForPattern(patternStop.getKey(), beginWithSequence, false); + tableWriter.normalizeStopTimesForPattern(patternStop.getKey(), beginWithSequence, false, false); } } } @@ -407,8 +407,9 @@ private String updateStopTimesFromPatternStops (Request req, Response res) { try { int beginStopSequence = Integer.parseInt(req.queryParams("stopSequence")); boolean interpolateStopTimes = Boolean.parseBoolean(req.queryParams("interpolateStopTimes")); + boolean ignoreNonBlankStopTimes = Boolean.parseBoolean(req.queryParams("ignoreNonBlankStopTimes")); JdbcTableWriter tableWriter = new JdbcTableWriter(table, datasource, namespace); - int stopTimesUpdated = tableWriter.normalizeStopTimesForPattern(patternId, beginStopSequence, interpolateStopTimes); + int stopTimesUpdated = tableWriter.normalizeStopTimesForPattern(patternId, beginStopSequence, interpolateStopTimes, ignoreNonBlankStopTimes); return SparkUtils.formatJSON("updateResult", stopTimesUpdated + " stop times updated."); } catch (Exception e) { logMessageAndHalt(req, 400, "Error normalizing stop times", e); diff --git a/src/main/java/com/conveyal/datatools/manager/DataManager.java b/src/main/java/com/conveyal/datatools/manager/DataManager.java index 5d4dc0edc..04629444f 100644 --- a/src/main/java/com/conveyal/datatools/manager/DataManager.java +++ b/src/main/java/com/conveyal/datatools/manager/DataManager.java @@ -209,25 +209,36 @@ static void registerRoutes() throws IOException { SnapshotController.register(EDITOR_API_PREFIX); EditorLockController.register(EDITOR_API_PREFIX); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.AREAS, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.AGENCY, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.ATTRIBUTIONS, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.CALENDAR, DataManager.GTFS_DATA_SOURCE); // NOTE: fare_attributes controller handles updates to nested table fare_rules. new EditorControllerImpl(EDITOR_API_PREFIX, Table.FARE_ATTRIBUTES, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.FARE_LEG_JOIN_RULES, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.FARE_LEG_RULES, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.FARE_MEDIAS, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.FARE_PRODUCTS, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.FARE_TRANSFER_RULES, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.FEED_INFO, DataManager.GTFS_DATA_SOURCE); + // NOTE: Booking rules, locations and location shapes are GTFS Flex additions. new EditorControllerImpl(EDITOR_API_PREFIX, Table.BOOKING_RULES, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.LOCATIONS, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.LOCATION_GROUP, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.LOCATION_GROUP_STOPS, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.LOCATION_SHAPES, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.NETWORKS, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.RIDER_CATEGORIES, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.ROUTES, DataManager.GTFS_DATA_SOURCE); // NOTE: Patterns controller handles updates to nested tables shapes, pattern stops, and frequencies. new EditorControllerImpl(EDITOR_API_PREFIX, Table.PATTERNS, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.SCHEDULE_EXCEPTIONS, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.STOPS, DataManager.GTFS_DATA_SOURCE); + new EditorControllerImpl(EDITOR_API_PREFIX, Table.TIME_FRAMES, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.TRANSLATIONS, DataManager.GTFS_DATA_SOURCE); new EditorControllerImpl(EDITOR_API_PREFIX, Table.TRIPS, DataManager.GTFS_DATA_SOURCE); + // TODO: Add transfers.txt controller? } @@ -248,7 +259,7 @@ static void registerRoutes() throws IOException { String extensionFeedBucket = getExtensionPropertyAsText(extensionType, "s3_bucket"); String extensionBucketFolder = getExtensionPropertyAsText(extensionType, "s3_download_prefix"); int updateFrequency = getConfigProperty("modules.gtfsapi.update_frequency").asInt(); - if (S3Utils.DEFAULT_BUCKET != null && extensionBucketFolder != null) { + if (extensionFeedBucket != null && extensionBucketFolder != null) { FeedUpdater.schedule(updateFrequency, extensionFeedBucket, extensionBucketFolder); } else LOG.warn("FeedUpdater not initialized. S3 bucket and folder not provided."); diff --git a/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java b/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java index 4e441fc01..5da20eaed 100644 --- a/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java +++ b/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.manager.extensions.mtc; import com.amazonaws.AmazonServiceException; +import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.PutObjectRequest; import com.conveyal.datatools.common.utils.CloseableHttpURLConnection; import com.conveyal.datatools.common.utils.aws.CheckedAWSException; @@ -31,6 +32,8 @@ import java.util.List; import java.util.Map; +import static com.conveyal.datatools.common.utils.aws.S3Utils.APP_DATA_CREDS_FILE; +import static com.conveyal.datatools.common.utils.aws.S3Utils.APP_DATA_S3_REGION; import static com.conveyal.datatools.manager.models.ExternalFeedSourceProperty.constructId; import static com.mongodb.client.model.Filters.eq; @@ -54,6 +57,9 @@ public class MtcFeedResource implements ExternalFeedResource { public static final String RESOURCE_TYPE = "MTC"; public static final String STOP_CODE_PRIMARY_PREFIX_FIELD_NAME = "PrimaryPrefix"; public static final String STOP_CODE_SECONDARY_PREFIXES_FIELD_NAME = "SecondaryPrefixes"; + public static final String CONFIG_MTC_CREDENTIALS = "extensions.mtc.s3_credentials_file"; + public static final String CONFIG_MTC_REGION = "extensions.mtc.s3_region"; + private static S3Utils.S3Wrapper s3Wrapper; private String rtdApi, s3Bucket, s3Prefix; @@ -212,7 +218,7 @@ public void feedVersionCreated( LOG.info("Pushing to MTC S3 Bucket: s3://{}/{}", s3Bucket, keyName); File file = feedVersion.retrieveGtfsFile(); try { - S3Utils.getDefaultS3Client().putObject(new PutObjectRequest(s3Bucket, keyName, file)); + getS3Client().putObject(new PutObjectRequest(s3Bucket, keyName, file)); } catch (Exception e) { LOG.error("Could not upload feed version to s3."); e.printStackTrace(); @@ -354,4 +360,26 @@ public static List getSecondaryStopCodePrefixes(Map configRegions = List.of(CONFIG_MTC_REGION, APP_DATA_S3_REGION); + List configCredentials = List.of(CONFIG_MTC_CREDENTIALS, APP_DATA_CREDS_FILE); + return S3Utils.buildS3Wrapper(configCredentials, configRegions); + } + + /** + * Get the S3 client from a cached wrapper to access the MTC feed processing bucket. + * Note: In S3Utils, an AWSClientManager is used, however in practice, only the default client is invoked + * (i.e. with role=null, region=null), so the logical path returns the original s3Client, which we are doing here. + */ + public static AmazonS3 getS3Client() { + if (s3Wrapper == null) { + s3Wrapper = getS3Wrapper(); + } + return s3Wrapper.s3Client; + } } diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java b/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java index 3800df702..b49eff6fa 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java @@ -5,7 +5,7 @@ import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectSummary; import com.conveyal.datatools.common.utils.aws.CheckedAWSException; -import com.conveyal.datatools.common.utils.aws.S3Utils; +import com.conveyal.datatools.manager.extensions.mtc.MtcFeedResource; import com.conveyal.datatools.manager.models.ExternalFeedSourceProperty; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; @@ -406,7 +406,7 @@ private FeedVersion findMatchingFeedVersion( ) throws AmazonServiceException, IOException, CheckedAWSException { String filename = keyName.split("/")[1]; String feedId = filename.replace(".zip", ""); - S3Object object = S3Utils.getDefaultS3Client().getObject(feedBucket, keyName); + S3Object object = MtcFeedResource.getS3Client().getObject(feedBucket, keyName); File file = new File(FeedStore.basePath, filename); try ( InputStream in = object.getObjectContent(); @@ -447,17 +447,14 @@ public interface CompletedFeedRetriever { /** * Implements the default behavior for above interface. + * This method uses extensions.mtc.s3_credentials_file and s3_region, if populated, + * and falls back to the application's S3 credentials and region otherwise. */ public class DefaultCompletedFeedRetriever implements CompletedFeedRetriever { @Override public List retrieveCompletedFeeds() { - try { - ObjectListing gtfsList = S3Utils.getDefaultS3Client().listObjects(feedBucket, bucketFolder); - return gtfsList.getObjectSummaries(); - } catch (CheckedAWSException e) { - LOG.error("Failed to list S3 Objects", e); - return null; - } + ObjectListing gtfsList = MtcFeedResource.getS3Client().listObjects(feedBucket, bucketFolder); + return gtfsList.getObjectSummaries(); } } } diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/PublishProjectFeedsJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/PublishProjectFeedsJob.java index 214790baa..9cf75c8fb 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/PublishProjectFeedsJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/PublishProjectFeedsJob.java @@ -1,14 +1,13 @@ package com.conveyal.datatools.manager.jobs; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.CannedAccessControlList; import com.conveyal.datatools.common.status.MonitorableJob; import com.conveyal.datatools.common.utils.aws.S3Utils; import com.conveyal.datatools.manager.auth.Auth0UserProfile; -import com.conveyal.datatools.manager.models.FeedVersion; +import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.Project; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.io.FileUtils; +import org.apache.commons.math3.util.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,53 +47,59 @@ public void jobLogic () { r.append("\n"); r.append("\n"); r.append("\n"); - r.append("" + title + "\n"); - r.append(""); + r.append("").append(title).append("\n"); + r.append(""); r.append("\n"); r.append("\n"); - r.append("

" + title + "

\n"); + r.append("

").append(title).append("

\n"); r.append("The following feeds, in GTFS format, are available for download and use.\n"); if (dataSupportEmail != null) { r.append(String.format("If you have inquiries, please contact us at: %1$s", dataSupportEmail)); } r.append("
    \n"); status.update("Ensuring public GTFS files are up-to-date.", 50); + + SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy"); project.retrieveProjectFeedSources().stream() - .filter(fs -> fs.isPublic && fs.retrieveLatest() != null) - .forEach(fs -> { - // generate list item for feed source - String url; - if (fs.url != null && !hasConfigProperty("modules.enterprise.prefer_s3_links")) { - url = fs.url.toString(); - } - else { - // ensure latest feed is written to the s3 public folder - try { - fs.makePublic(); - } catch (Exception e) { - status.fail("Failed to make GTFS files public on S3", e); - return; - } - url = S3Utils.getDefaultBucketUrlForKey(fs.toPublicKey()); - } - FeedVersion latest = fs.retrieveLatest(); - r.append("
  • "); - r.append(""); - r.append(fs.name); - r.append(""); - r.append(" ("); - if (fs.url != null && fs.lastFetched != null) { - r.append("last checked: " + new SimpleDateFormat("dd MMM yyyy").format(fs.lastFetched) + ", "); - } - if (fs.lastUpdated() != null) { - r.append("last updated: " + new SimpleDateFormat("dd MMM yyyy").format(fs.lastUpdated()) + ")"); + .filter(fs -> fs.isPublic) + // Store latest version id, so we don't have to fetch it again in the forEach stage. + .map(fs -> new Pair(fs, fs.latestVersionId()) {}) + .filter(p -> p.getValue() != null) + .forEach(p -> { + // generate list item for feed source + FeedSource fs = p.getKey(); + String latestVersionId = p.getValue(); + String url; + if (fs.url != null && !hasConfigProperty("modules.enterprise.prefer_s3_links")) { + url = fs.url.toString(); + } + else { + // ensure latest feed is written to the s3 public folder + try { + fs.makePublic(latestVersionId); + } catch (Exception e) { + status.fail("Failed to make GTFS files public on S3", e); + return; } - r.append("
  • "); - }); + url = S3Utils.getDefaultBucketUrlForKey(fs.toPublicKey()); + } + r.append("
  • "); + r.append(""); + r.append(fs.name); + r.append(""); + r.append(" ("); + if (fs.url != null && fs.lastFetched != null) { + r.append("last checked: ").append(dateFormat.format(fs.lastFetched)).append(", "); + } + if (fs.lastUpdated() != null) { + r.append("last updated: ").append(dateFormat.format(fs.lastUpdated())).append(")"); + } + r.append("
  • "); + }); r.append("
"); r.append(""); r.append(""); @@ -111,12 +116,9 @@ public void jobLogic () { e.printStackTrace(); } try { - AmazonS3 defaultS3Client = S3Utils.getDefaultS3Client(); - defaultS3Client.putObject(S3Utils.DEFAULT_BUCKET, folder + fileName, file); - defaultS3Client.setObjectAcl(S3Utils.DEFAULT_BUCKET, folder + fileName, CannedAccessControlList.PublicRead); + S3Utils.uploadObject(folder + fileName, file); } catch (Exception e) { status.fail("Failed to perform S3 actions", e); - return; } } diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java index fe1cc7948..c90baa53e 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java @@ -597,13 +597,22 @@ public String toPublicKey() { * Makes the feed source's latest version have public access on AWS S3. */ public void makePublic() throws CheckedAWSException { + makePublic(this.latestVersionId()); + } + + /** + * Makes the feed source's specified version have public access on AWS S3. + */ + public void makePublic(String versionId) throws CheckedAWSException { String sourceKey = S3Utils.DEFAULT_BUCKET_GTFS_FOLDER + this.id + ".zip"; String publicKey = toPublicKey(); - String versionId = this.latestVersionId(); String latestVersionKey = S3Utils.DEFAULT_BUCKET_GTFS_FOLDER + versionId; - // only deploy to public if storing feeds on s3 (no mechanism for downloading/publishing - // them otherwise) + // Copy, forever, the latest stored version to the public folder, regardless of + // whether S3 is used to store individual feed versions (useS3 = true) or not. + // + // Note: If modules.enterprise.prefer_s3_links is not set and the feed source has a fetch URL, + // this method will not be called, and the feed download link will point to the fetch URL instead. if (DataManager.useS3) { AmazonS3 defaultS3Client = S3Utils.getDefaultS3Client(); boolean sourceExists = defaultS3Client.doesObjectExist(S3Utils.DEFAULT_BUCKET, sourceKey); @@ -634,6 +643,9 @@ public void makePublic() throws CheckedAWSException { defaultS3Client.copyObject(S3Utils.DEFAULT_BUCKET, latestVersionKey, S3Utils.DEFAULT_BUCKET, sourceKey); } } + } else { + LOG.info("copying latest local feed {} to s3 public folder", this); + S3Utils.uploadObject(publicKey, retrieveLatest().retrieveGtfsFile()); } } diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedSourceSummary.java b/src/main/java/com/conveyal/datatools/manager/models/FeedSourceSummary.java index ab5883f2e..90d9943fb 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedSourceSummary.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedSourceSummary.java @@ -87,6 +87,8 @@ public class FeedSourceSummary { public FeedValidationResultSummary publishedValidationSummary; + public String publishedVersionId; + public PublishState publishState; @JsonInclude(JsonInclude.Include.NON_NULL) @@ -113,6 +115,7 @@ public FeedSourceSummary(String projectId, String organizationId, Document feedS // Convert to local date type for consistency. lastUpdated = getLocalDateFromDate(feedSourceDocument.getDate("lastUpdated")); url = feedSourceDocument.getString("url"); + publishedVersionId = feedSourceDocument.getString("publishedVersionId"); // Get optional filename. filename = feedSourceDocument.getString("filename"); // Optional external properties, if enabled by config. @@ -180,7 +183,8 @@ public static List getFeedSourceSummaries(String projectId, S "labelIds", "url", "filename", - "noteIds" + "noteIds", + "publishedVersionId" ) ), sort(Sorts.ascending("name")) @@ -219,7 +223,8 @@ public static Map getLatestFeedVersionForFeedSources ); // Define the variable passed into the lookup pipeline. - List> feedSourceId = List.of(new Variable<>("feedSourceId", "$_id")); + Variable feedSourceIdVariable = new Variable<>("feedSourceId", "$_id"); + List> feedSourceId = List.of(feedSourceIdVariable); // $lookup that uses the above pipeline to produce "latestFeedVersion" (an array with at most one element). Bson lookupLatestFeedVersion = lookup( @@ -231,7 +236,14 @@ public static Map getLatestFeedVersionForFeedSources // Pipeline to find the published FeedVersion by namespace (or identifier stored in publishedVersionId) List publishedFeedVersionPipeline = Arrays.asList( - // Match FeedVersion documents where namespace equals the outer document's publishedVersionId. + // Match FeedVersion documents where namespace equals the outer document's + // feedSourceId (important when dealing with many FeedVersions) + // and publishedVersionId. + match( + expr( + new Document("$eq", Arrays.asList("$feedSourceId", "$$feedSourceId")) + ) + ), match( expr( new Document("$eq", Arrays.asList("$namespace", "$$publishedVersionId")) @@ -242,13 +254,16 @@ public static Map getLatestFeedVersionForFeedSources project(include("validationResult")) ); - // Pass publishedVersionId from the local document into the lookup pipeline. - List> publishedVersionId = List.of(new Variable<>("publishedVersionId", "$publishedVersionId")); + // Pass feedSourceId and publishedVersionId from the local document into the lookup pipeline. + List> feedSourceIdAndPublishedVersionId = List.of( + feedSourceIdVariable, + new Variable<>("publishedVersionId", "$publishedVersionId") + ); // $lookup that uses the above pipeline to produce "publishedFeedVersion" (an array with at most one element). Bson lookupPublishedFeedVersion = lookup( "FeedVersion", - publishedVersionId, + feedSourceIdAndPublishedVersionId, publishedFeedVersionPipeline, "publishedFeedVersion" ); diff --git a/src/main/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformation.java b/src/main/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformation.java index 534ff54bd..f68feaf67 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformation.java +++ b/src/main/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformation.java @@ -101,21 +101,27 @@ public void transform(FeedTransformZipTarget zipTarget, MonitorableJob.Status st LOG.warn("No revenue trips! All trips and related stop times will be removed."); } - writeToFile( - zipTarget, - "stop_times.txt", - revenueStopTimes.getStopTimeRows().values().stream().flatMap(List::stream).collect(Collectors.toList()), - headersForStopTime, - revenueStopTimes.getDeletedRowCount() - ); + // Only update tables and report change if rows are removed. - writeToFile( - zipTarget, - "trips.txt", - revenueTrips.tripRows, - headersForTrips, - revenueTrips.getDeletedRowCount() - ); + if (revenueStopTimes.getDeletedRowCount() != 0) { + writeToFile( + zipTarget, + "stop_times.txt", + revenueStopTimes.getStopTimeRows().values().stream().flatMap(List::stream).collect(Collectors.toList()), + headersForStopTime, + revenueStopTimes.getDeletedRowCount() + ); + } + + if (revenueTrips.getDeletedRowCount() != 0) { + writeToFile( + zipTarget, + "trips.txt", + revenueTrips.tripRows, + headersForTrips, + revenueTrips.getDeletedRowCount() + ); + } } catch (Exception e) { LOG.error("Unknown error encountered while attempting to remove non revenue trip from zip file.", e); } @@ -237,7 +243,7 @@ private boolean isRevenueStopTime( private boolean isRevenuePickupDropOff(CsvReader csvReader, Map fieldIndexes) throws IOException { String pickup = csvReader.get(fieldIndexes.get(PICKUP_FIELD_NAME)); String dropOff = csvReader.get(fieldIndexes.get(DROP_OFF_FIELD_NAME)); - return !"1".equals(pickup) && !"1".equals(dropOff); + return !"1".equals(pickup) || !"1".equals(dropOff); } /** diff --git a/src/main/resources/mongo/getFeedSourceSummaries.js b/src/main/resources/mongo/getFeedSourceSummaries.js index 3e4db025d..45c6dffe5 100644 --- a/src/main/resources/mongo/getFeedSourceSummaries.js +++ b/src/main/resources/mongo/getFeedSourceSummaries.js @@ -15,7 +15,8 @@ db.getCollection('FeedSource').aggregate([ "labelIds": 1, "url": 1, "filename": 1, - "noteIds": 1 + "noteIds": 1, + "publishedVersionId": 1 } }, { diff --git a/src/main/resources/mongo/getLatestFeedVersionForFeedSources.js b/src/main/resources/mongo/getLatestFeedVersionForFeedSources.js index 21a563012..de63cc179 100644 --- a/src/main/resources/mongo/getLatestFeedVersionForFeedSources.js +++ b/src/main/resources/mongo/getLatestFeedVersionForFeedSources.js @@ -25,8 +25,10 @@ db.FeedSource.aggregate([ { $lookup: { from: "FeedVersion", - let: { publishedVersionId: "$publishedVersionId" }, + let: { feedSourceId: "$_id", publishedVersionId: "$publishedVersionId" }, pipeline: [ + // Filtering by feedSourceId helps by 100x when dealing with large number of feed versions. + { $match: { $expr: { $eq: ["$feedSourceId", "$$feedSourceId"] } } }, { $match: { $expr: { $eq: ["$namespace", "$$publishedVersionId"] } } }, { $limit: 1 }, { $project: { validationResult: 1 } } diff --git a/src/test/java/com/conveyal/datatools/common/utils/aws/S3UtilsTest.java b/src/test/java/com/conveyal/datatools/common/utils/aws/S3UtilsTest.java new file mode 100644 index 000000000..2064ee237 --- /dev/null +++ b/src/test/java/com/conveyal/datatools/common/utils/aws/S3UtilsTest.java @@ -0,0 +1,31 @@ +package com.conveyal.datatools.common.utils.aws; + +import com.conveyal.datatools.DatatoolsTest; +import com.conveyal.datatools.UnitTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class S3UtilsTest extends UnitTest { + /** + * Create feed version for GTFS+ validation test. + */ + @BeforeAll + static void setUp() throws IOException { + // Start server if it isn't already running. + DatatoolsTest.setUp(); + } + + @Test + void canGetDefaultBucketUrlForKey() { + final String FILENAME = "public/index.html"; + + assertEquals( + "https://bucket-name.s3.amazonaws.com/public/index.html", + S3Utils.getDefaultBucketUrlForKey(FILENAME) + ); + } +} diff --git a/src/test/java/com/conveyal/datatools/editor/controllers/api/EditorControllerTest.java b/src/test/java/com/conveyal/datatools/editor/controllers/api/EditorControllerTest.java index 45a658b3f..aa0761257 100644 --- a/src/test/java/com/conveyal/datatools/editor/controllers/api/EditorControllerTest.java +++ b/src/test/java/com/conveyal/datatools/editor/controllers/api/EditorControllerTest.java @@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import io.restassured.http.Method; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -27,10 +28,13 @@ import java.io.IOException; import java.sql.SQLException; import java.util.Date; +import java.util.Objects; import java.util.stream.Stream; import static com.conveyal.datatools.TestUtils.assertThatSqlCountQueryYieldsExpectedCount; +import static com.conveyal.datatools.TestUtils.createFeedVersion; import static com.conveyal.datatools.TestUtils.createFeedVersionFromGtfsZip; +import static com.conveyal.datatools.TestUtils.zipFolderFiles; import static com.conveyal.datatools.manager.auth.Auth0Users.USERS_API_PATH; import static com.conveyal.datatools.manager.controllers.api.UserController.TEST_AUTH0_DOMAIN; import static io.restassured.RestAssured.given; @@ -45,8 +49,10 @@ public class EditorControllerTest extends UnitTest { private static Project project; private static FeedSource feedSource; private static FeedSource feedSourceCascadeDelete; + private static FeedSource faresV2FeedSource; private static FeedVersion feedVersion; private static FeedVersion feedVersionCascadeDelete; + private static FeedVersion faresV2Version; private static final ObjectMapper mapper = new ObjectMapper(); /** @@ -72,12 +78,18 @@ public static void setUp() throws Exception { feedSourceCascadeDelete.projectId = project.id; Persistence.feedSources.create(feedSourceCascadeDelete); + faresV2FeedSource = new FeedSource("FaresV2"); + faresV2FeedSource.projectId = project.id; + Persistence.feedSources.create(faresV2FeedSource); + feedVersion = createFeedVersionFromGtfsZip(feedSource, "bart_old.zip"); feedVersionCascadeDelete = createFeedVersionFromGtfsZip(feedSourceCascadeDelete, "bart_old.zip"); + faresV2Version = createFeedVersion(faresV2FeedSource, zipFolderFiles("fake-agency-with-fares-v2")); // Create and run snapshot jobs - crateAndRunSnapshotJob(feedVersion.name, feedSource.id, feedVersion.namespace); - crateAndRunSnapshotJob(feedVersionCascadeDelete.name, feedSourceCascadeDelete.id, feedVersionCascadeDelete.namespace); + createAndRunSnapshotJob(feedVersion.name, feedSource.id, feedVersion.namespace); + createAndRunSnapshotJob(feedVersionCascadeDelete.name, feedSourceCascadeDelete.id, feedVersionCascadeDelete.namespace); + createAndRunSnapshotJob(faresV2Version.name, faresV2FeedSource.id, faresV2Version.namespace); LOG.info("{} setup completed in {} ms", EditorControllerTest.class.getSimpleName(), System.currentTimeMillis() - startTime); } @@ -86,15 +98,22 @@ public static void tearDown() { project.delete(); feedSource.delete(); feedSourceCascadeDelete.delete(); + faresV2FeedSource.delete(); } /** * Create and run a snapshot job in the current thread (so tests do not run until this is complete). */ - private static void crateAndRunSnapshotJob(String feedVersionName, String feedSourceId, String namespace) { + private static void createAndRunSnapshotJob(String feedVersionName, String feedSourceId, String namespace) { Snapshot snapshot = new Snapshot("Snapshot of " + feedVersionName, feedSourceId, namespace); CreateSnapshotJob createSnapshotJob = - new CreateSnapshotJob(Auth0UserProfile.createTestAdminUser(), snapshot, true, false, false); + new CreateSnapshotJob( + Auth0UserProfile.createTestAdminUser(), + snapshot, + true, + false, + false + ); createSnapshotJob.run(); } @@ -111,14 +130,7 @@ private static Stream createPatchTableTests() { */ @ParameterizedTest @MethodSource("createPatchTableTests") - public void canPatchTableTests( - String field, - String entity, - int expectedCount, - String graphQLQueryFile, - String table - ) throws IOException { - + void canPatchTableTests(String field, String entity, int expectedCount, String graphQLQueryFile, String table) throws IOException { LOG.info("Making patch {} request", table); String value = "NEW"; ObjectNode jsonBody = mapper.createObjectNode(); @@ -138,7 +150,7 @@ public void canPatchTableTests( * Make sure the patch table endpoint can patch stops conditionally with query. */ @Test - public void canPatchStopsConditionally() throws IOException { + void canPatchStopsConditionally() throws IOException { LOG.info("Making conditional patch stops request"); ObjectNode jsonBody = mapper.createObjectNode(); String field = "stop_desc"; @@ -187,13 +199,7 @@ void canCascadeDeleteStop() throws IOException, SQLException { stopId, feedVersionCascadeDelete.feedSourceId ); - String response = given() - .port(DataManager.PORT) - .delete(path) - .then() - .extract() - .response() - .asString(); + String response = makeRequest(Method.DELETE, path, ""); JsonNode json = mapper.readTree(response); assertEquals(OK_200, json.get("code").asInt()); @@ -204,20 +210,64 @@ void canCascadeDeleteStop() throws IOException, SQLException { } /** - * Perform patch table request on the feed source ID with the requested query and patch JSON. A null query will - * apply the patch JSON to the entire table. + * Confirm that a fare product can be created, updated and deleted. */ - private static int patchTableRequest(String entity, String feedId, String query, JsonNode oatchJSON) throws IOException { - String path = String.format("/api/editor/secure/%s?feedId=%s", entity, feedId); - if (query != null) path += "&" + query; - String response = given() + @Test + void canCreateUpdateAndDeleteFareProduct() throws IOException { + String fareProductId = "AERIAL_TRAM_ROUND_TRIP"; + String fareProductIdUpdated = "AERIAL_TRAM_ROUND_TRIP_UPDATED"; + String urlPrefix = "/api/editor/secure/fareproduct"; + String urlSuffix = String.format("?feedId=%s&sessionId=test", faresV2Version.feedSourceId); + String payload = + "{" + + "\"fare_product_id\":\"" + fareProductId + "\"," + + "\"fare_product_name\":\"Portland Aerial Tram Single Round Trip\"," + + "\"rider_category_id\":\"HONORED_CITIZEN\"," + + "\"fare_media_id\":\"1\"," + + "\"amount\":\"13.5\"," + + "\"currency\":\"USD\"" + + "}"; + + // Create. + String response = makeRequest(Method.POST, String.format("%s%s", urlPrefix, urlSuffix), payload); + JsonNode json = mapper.readTree(response); + String id = json.get("id").asText(); + assertEquals(fareProductId, json.get("fare_product_id").asText()); + + // Update. + payload = payload.replace(fareProductId, fareProductIdUpdated); + response = makeRequest(Method.PUT, String.format("%s/%s%s", urlPrefix, id, urlSuffix), payload); + json = mapper.readTree(response); + assertEquals(fareProductIdUpdated, json.get("fare_product_id").asText()); + + // Delete. + response = makeRequest(Method.DELETE, String.format("%s/%s%s", urlPrefix, id, urlSuffix), ""); + json = mapper.readTree(response); + assertEquals(200, json.get("code").asInt()); + } + + /** + * Make request and return the response. + */ + private static String makeRequest(Method method, String path, Object payload) { + return given() .port(DataManager.PORT) - .body(oatchJSON) - .patch(path) + .body(payload) + .request(method, path) .then() .extract() .response() .asString(); + } + + /** + * Perform patch table request on the feed source ID with the requested query and patch JSON. A null query will + * apply the patch JSON to the entire table. + */ + private static int patchTableRequest(String entity, String feedId, String query, JsonNode patchJSON) throws IOException { + String path = String.format("/api/editor/secure/%s?feedId=%s", entity, feedId); + if (query != null) path += "&" + query; + String response = makeRequest(Method.PATCH, path, patchJSON); JsonNode json = mapper.readTree(response); return json.get("count").asInt(); } @@ -230,16 +280,9 @@ private static JsonNode graphqlQuery (String namespace, String graphQLQueryFile) ObjectNode variables = mapper.createObjectNode(); variables.put("namespace", namespace); graphQLBody.set("variables", variables); - String query = IOUtils.toString(EditorControllerTest.class.getClassLoader().getResourceAsStream(graphQLQueryFile)); + String query = IOUtils.toString(Objects.requireNonNull(EditorControllerTest.class.getClassLoader().getResourceAsStream(graphQLQueryFile))); graphQLBody.put("query", query); - String graphQLString = given() - .port(DataManager.PORT) - .body(graphQLBody) - .post("api/manager/secure/gtfs/graphql") - .then() - .extract() - .response() - .asString(); + String graphQLString = makeRequest(Method.POST, "api/manager/secure/gtfs/graphql", graphQLBody); return mapper.readTree(graphQLString); } diff --git a/src/test/java/com/conveyal/datatools/manager/controllers/api/FeedSourceControllerTest.java b/src/test/java/com/conveyal/datatools/manager/controllers/api/FeedSourceControllerTest.java index b5a9585e3..f8ad6b0ae 100644 --- a/src/test/java/com/conveyal/datatools/manager/controllers/api/FeedSourceControllerTest.java +++ b/src/test/java/com/conveyal/datatools/manager/controllers/api/FeedSourceControllerTest.java @@ -11,6 +11,7 @@ import com.conveyal.datatools.manager.models.FeedRetrievalMethod; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedSourceSummary; +import com.conveyal.datatools.manager.models.FeedValidationResultSummary; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.datatools.manager.models.FeedVersionSummary; import com.conveyal.datatools.manager.models.FetchFrequency; @@ -148,8 +149,7 @@ private static void setUpFeedVersionFromLatestDeployment() throws MalformedURLEx feedVersionPublishedFromLatestDeployment = createFeedVersion( "published-feed-version-from-latest-deployment", - // Set to null so the relationship to feed source is via the published version id. - null, + feedSourceWithLatestDeploymentFeedVersion.id, LocalDate.of(2022, Month.NOVEMBER, 2), LocalDate.of(2022, Month.NOVEMBER, 3), feedSourceWithLatestDeploymentFeedVersion.publishedVersionId, @@ -375,25 +375,28 @@ void canRetrieveDeployedFeedVersionFromLatestDeployment() throws IOException { ); assertNotNull(feedSourceSummaries); - assertEquals(feedSourceWithLatestDeploymentFeedVersion.id, feedSourceSummaries.get(0).id); - assertEquals(feedSourceWithLatestDeploymentFeedVersion.projectId, feedSourceSummaries.get(0).projectId); - assertEquals(feedSourceWithLatestDeploymentFeedVersion.labelIds, feedSourceSummaries.get(0).labelIds); - assertEquals(feedSourceWithLatestDeploymentFeedVersion.url.toString(), feedSourceSummaries.get(0).url); - assertEquals(feedSourceWithLatestDeploymentFeedVersion.noteIds, feedSourceSummaries.get(0).noteIds); - assertEquals(feedSourceWithLatestDeploymentFeedVersion.organizationId(), feedSourceSummaries.get(0).organizationId); - assertEquals(feedVersionFromLatestDeploymentVersion2.id, feedSourceSummaries.get(0).deployedFeedVersionId); - assertEquals(feedVersionFromLatestDeploymentVersion2.validationSummary().startDate, feedSourceSummaries.get(0).deployedFeedVersionStartDate); - assertEquals(feedVersionFromLatestDeploymentVersion2.validationSummary().endDate, feedSourceSummaries.get(0).deployedFeedVersionEndDate); - assertEquals(feedVersionFromLatestDeploymentVersion2.validationSummary().errorCount, feedSourceSummaries.get(0).deployedFeedVersionIssues); - assertEquals(feedVersionFromLatestDeploymentVersion2.id, feedSourceSummaries.get(0).latestValidation.feedVersionId); - assertEquals(feedVersionFromLatestDeploymentVersion2.validationSummary().startDate, feedSourceSummaries.get(0).latestValidation.startDate); - assertEquals(feedVersionFromLatestDeploymentVersion2.validationSummary().endDate, feedSourceSummaries.get(0).latestValidation.endDate); - assertEquals(feedVersionFromLatestDeploymentVersion2.validationSummary().errorCount, feedSourceSummaries.get(0).latestValidation.errorCount); - assertEquals(feedVersionFromLatestDeploymentVersion2.sentToExternalPublisher, feedSourceSummaries.get(0).latestSentToExternalPublisher); - assertEquals(PublishState.PUBLISH_BLOCKED, feedSourceSummaries.get(0).publishState); - assertEquals(feedVersionPublishedFromLatestDeployment.validationResult.errorCount, feedSourceSummaries.get(0).publishedValidationSummary.errorCount); - assertEquals(feedVersionPublishedFromLatestDeployment.validationResult.firstCalendarDate, feedSourceSummaries.get(0).publishedValidationSummary.startDate); - assertEquals(feedVersionPublishedFromLatestDeployment.validationResult.lastCalendarDate, feedSourceSummaries.get(0).publishedValidationSummary.endDate); + FeedSourceSummary firstSummary = feedSourceSummaries.get(0); + FeedValidationResultSummary expectedValidationSummary = feedVersionFromLatestDeploymentVersion2.validationSummary(); + assertEquals(feedSourceWithLatestDeploymentFeedVersion.id, firstSummary.id); + assertEquals(feedSourceWithLatestDeploymentFeedVersion.projectId, firstSummary.projectId); + assertEquals(feedSourceWithLatestDeploymentFeedVersion.labelIds, firstSummary.labelIds); + assertEquals(feedSourceWithLatestDeploymentFeedVersion.url.toString(), firstSummary.url); + assertEquals(feedSourceWithLatestDeploymentFeedVersion.noteIds, firstSummary.noteIds); + assertEquals(feedSourceWithLatestDeploymentFeedVersion.organizationId(), firstSummary.organizationId); + assertEquals(feedVersionFromLatestDeploymentVersion2.id, firstSummary.deployedFeedVersionId); + assertEquals(expectedValidationSummary.startDate, firstSummary.deployedFeedVersionStartDate); + assertEquals(expectedValidationSummary.endDate, firstSummary.deployedFeedVersionEndDate); + assertEquals(expectedValidationSummary.errorCount, firstSummary.deployedFeedVersionIssues); + assertEquals(feedVersionFromLatestDeploymentVersion2.id, firstSummary.latestValidation.feedVersionId); + assertEquals(expectedValidationSummary.startDate, firstSummary.latestValidation.startDate); + assertEquals(expectedValidationSummary.endDate, firstSummary.latestValidation.endDate); + assertEquals(expectedValidationSummary.errorCount, firstSummary.latestValidation.errorCount); + assertEquals(feedVersionFromLatestDeploymentVersion2.sentToExternalPublisher, firstSummary.latestSentToExternalPublisher); + assertEquals(PublishState.PUBLISH_BLOCKED, firstSummary.publishState); + assertEquals(feedSourceWithLatestDeploymentFeedVersion.publishedVersionId, firstSummary.publishedVersionId); + assertEquals(feedVersionPublishedFromLatestDeployment.validationResult.errorCount, firstSummary.publishedValidationSummary.errorCount); + assertEquals(feedVersionPublishedFromLatestDeployment.validationResult.firstCalendarDate, firstSummary.publishedValidationSummary.startDate); + assertEquals(feedVersionPublishedFromLatestDeployment.validationResult.lastCalendarDate, firstSummary.publishedValidationSummary.endDate); } @Test @@ -414,22 +417,26 @@ void canRetrieveDeployedFeedVersionFromPinnedDeployment() throws IOException { FeedSourceSummary.class ); assertNotNull(feedSourceSummaries); - assertEquals(feedSourceWithPinnedDeploymentFeedVersion.id, feedSourceSummaries.get(0).id); - assertEquals(feedSourceWithPinnedDeploymentFeedVersion.projectId, feedSourceSummaries.get(0).projectId); - assertEquals(feedSourceWithPinnedDeploymentFeedVersion.labelIds, feedSourceSummaries.get(0).labelIds); - assertEquals(feedSourceWithPinnedDeploymentFeedVersion.url.toString(), feedSourceSummaries.get(0).url); - assertEquals(feedSourceWithPinnedDeploymentFeedVersion.noteIds, feedSourceSummaries.get(0).noteIds); - assertEquals(feedSourceWithPinnedDeploymentFeedVersion.organizationId(), feedSourceSummaries.get(0).organizationId); - assertEquals(feedVersionFromPinnedDeployment.id, feedSourceSummaries.get(0).deployedFeedVersionId); - assertEquals(feedVersionFromPinnedDeployment.validationSummary().startDate, feedSourceSummaries.get(0).deployedFeedVersionStartDate); - assertEquals(feedVersionFromPinnedDeployment.validationSummary().endDate, feedSourceSummaries.get(0).deployedFeedVersionEndDate); - assertEquals(feedVersionFromPinnedDeployment.validationSummary().errorCount, feedSourceSummaries.get(0).deployedFeedVersionIssues); - assertEquals(feedVersionFromPinnedDeployment.id, feedSourceSummaries.get(0).latestValidation.feedVersionId); - assertEquals(feedVersionFromPinnedDeployment.validationSummary().startDate, feedSourceSummaries.get(0).latestValidation.startDate); - assertEquals(feedVersionFromPinnedDeployment.validationSummary().endDate, feedSourceSummaries.get(0).latestValidation.endDate); - assertEquals(feedVersionFromPinnedDeployment.validationSummary().errorCount, feedSourceSummaries.get(0).latestValidation.errorCount); - assertEquals(feedVersionFromPinnedDeployment.sentToExternalPublisher, feedSourceSummaries.get(0).latestSentToExternalPublisher); - assertEquals(PublishState.PUBLISH_BLOCKED, feedSourceSummaries.get(0).publishState); + FeedSourceSummary firstSummary = feedSourceSummaries.get(0); + FeedValidationResultSummary expectedValidationSummary = feedVersionFromPinnedDeployment.validationSummary(); + + assertEquals(feedSourceWithPinnedDeploymentFeedVersion.id, firstSummary.id); + assertEquals(feedSourceWithPinnedDeploymentFeedVersion.projectId, firstSummary.projectId); + assertEquals(feedSourceWithPinnedDeploymentFeedVersion.labelIds, firstSummary.labelIds); + assertEquals(feedSourceWithPinnedDeploymentFeedVersion.url.toString(), firstSummary.url); + assertEquals(feedSourceWithPinnedDeploymentFeedVersion.noteIds, firstSummary.noteIds); + assertEquals(feedSourceWithPinnedDeploymentFeedVersion.organizationId(), firstSummary.organizationId); + assertEquals(feedVersionFromPinnedDeployment.id, firstSummary.deployedFeedVersionId); + assertEquals(expectedValidationSummary.startDate, firstSummary.deployedFeedVersionStartDate); + assertEquals(expectedValidationSummary.endDate, firstSummary.deployedFeedVersionEndDate); + assertEquals(expectedValidationSummary.errorCount, firstSummary.deployedFeedVersionIssues); + assertEquals(feedVersionFromPinnedDeployment.id, firstSummary.latestValidation.feedVersionId); + assertEquals(expectedValidationSummary.startDate, firstSummary.latestValidation.startDate); + assertEquals(expectedValidationSummary.endDate, firstSummary.latestValidation.endDate); + assertEquals(expectedValidationSummary.errorCount, firstSummary.latestValidation.errorCount); + assertEquals(feedVersionFromPinnedDeployment.sentToExternalPublisher, firstSummary.latestSentToExternalPublisher); + assertEquals(PublishState.PUBLISH_BLOCKED, firstSummary.publishState); + assertEquals(feedSourceWithPinnedDeploymentFeedVersion.publishedVersionId, firstSummary.publishedVersionId); } @ParameterizedTest diff --git a/src/test/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResourceTest.java b/src/test/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResourceTest.java index 0ab5113e7..edac92893 100644 --- a/src/test/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResourceTest.java +++ b/src/test/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResourceTest.java @@ -1,7 +1,12 @@ package com.conveyal.datatools.manager.extensions.mtc; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import com.conveyal.datatools.DatatoolsTest; +import com.conveyal.datatools.TestUtils; import com.conveyal.datatools.UnitTest; +import com.conveyal.datatools.common.utils.aws.S3Utils; +import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.auth.Auth0Connection; import com.conveyal.datatools.manager.jobs.ProcessSingleFeedJob; import com.conveyal.datatools.manager.models.ExternalFeedSourceProperty; @@ -14,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -26,6 +32,9 @@ import static com.conveyal.datatools.TestUtils.createFeedVersion; import static com.conveyal.datatools.TestUtils.parseJson; import static com.conveyal.datatools.TestUtils.zipFolderFiles; +import static com.conveyal.datatools.manager.DataManager.getConfigPropertyAsText; +import static com.conveyal.datatools.manager.extensions.mtc.MtcFeedResource.CONFIG_MTC_CREDENTIALS; +import static com.conveyal.datatools.manager.extensions.mtc.MtcFeedResource.CONFIG_MTC_REGION; import static com.conveyal.datatools.manager.extensions.mtc.MtcFeedResource.STOP_CODE_PRIMARY_PREFIX_FIELD_NAME; import static com.conveyal.datatools.manager.extensions.mtc.MtcFeedResource.STOP_CODE_SECONDARY_PREFIXES_FIELD_NAME; import static com.conveyal.gtfs.error.NewGTFSErrorType.MISSING_STOP_CODE_PREFIX; @@ -41,12 +50,16 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class MtcFeedResourceTest extends UnitTest { private static Project project; private static FeedSource feedSource; private static WireMockServer wireMockServer; + private static String defaultMtcAwsCredentials; + private static String defaultMtcAwsRegion; private static final String AGENCY_CODE = "DE"; @@ -61,6 +74,8 @@ static void setUp() throws IOException { // Enable MTC extension, but disable the transformations. ProcessSingleFeedJob.ENABLE_MTC_TRANSFORMATIONS = false; DatatoolsTest.enableMTCExtension(); + defaultMtcAwsCredentials = getConfigPropertyAsText(CONFIG_MTC_CREDENTIALS); + defaultMtcAwsRegion = getConfigPropertyAsText(CONFIG_MTC_REGION); Auth0Connection.setAuthDisabled(true); // Create a project, feed sources. @@ -91,6 +106,16 @@ static void tearDown() { ProcessSingleFeedJob.ENABLE_MTC_TRANSFORMATIONS = true; } + @AfterEach + void resetState() { + DataManager.overrideConfigProperty(CONFIG_MTC_CREDENTIALS, defaultMtcAwsCredentials); + + String currentMtcRegion = getConfigPropertyAsText(CONFIG_MTC_REGION); + if (currentMtcRegion != null && !currentMtcRegion.equals(defaultMtcAwsRegion)) { + DataManager.overrideConfigProperty(CONFIG_MTC_REGION, defaultMtcAwsRegion); + } + } + @Test void shouldConvertRtdNullToEmptyString() { assertThat(MtcFeedResource.convertRtdString("null"), equalTo("")); @@ -235,4 +260,29 @@ void shouldUpdateRtdCarrierProperty(String address) throws JsonProcessingExcepti carrier.toJson().contains(String.format(",\"AgencyAddress\":%s,", jsonAddress)) ); } + + @ParameterizedTest + @ValueSource(booleans = { false, true }) + void canUseCorrectMtcCredentials(boolean overwriteDefaults) { + if (overwriteDefaults) { + DataManager.overrideConfigProperty(CONFIG_MTC_REGION, "us-west-2"); + String credentialsFile = TestUtils.class.getResource("mock-aws-credentials").getFile(); + DataManager.overrideConfigProperty(CONFIG_MTC_CREDENTIALS, credentialsFile); + } + + S3Utils.S3Wrapper s3wrapper = MtcFeedResource.getS3Wrapper(); + assertNotNull(s3wrapper); + + // Either application.data.s3_region or extensions.mtc.s3_region from server.yml.tmp. + assertEquals(overwriteDefaults ? "us-west-2" : "us-east-1", s3wrapper.s3Client.getRegion().toAWSRegion().getName()); + + // The credentials should reflect the content in the mock-aws-credentials file referenced above. + if (overwriteDefaults) { + AWSCredentials credentials = s3wrapper.credentials.getCredentials(); + assertEquals("mock-id-123", credentials.getAWSAccessKeyId()); + assertEquals("mock-secret-123", credentials.getAWSSecretKey()); + } else { + assertEquals(DefaultAWSCredentialsProviderChain.class, s3wrapper.credentials.getClass()); + } + } } diff --git a/src/test/java/com/conveyal/datatools/manager/jobs/validation/FaresV2ValidationTest.java b/src/test/java/com/conveyal/datatools/manager/jobs/validation/FaresV2ValidationTest.java new file mode 100644 index 000000000..a5301d079 --- /dev/null +++ b/src/test/java/com/conveyal/datatools/manager/jobs/validation/FaresV2ValidationTest.java @@ -0,0 +1,100 @@ +package com.conveyal.datatools.manager.jobs.validation; + +import com.conveyal.datatools.DatatoolsTest; +import com.conveyal.datatools.manager.auth.Auth0Connection; +import com.conveyal.datatools.manager.models.FeedSource; +import com.conveyal.datatools.manager.models.FeedVersion; +import com.conveyal.datatools.manager.models.Project; +import com.conveyal.datatools.manager.persistence.Persistence; +import org.bson.Document; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import static com.conveyal.datatools.TestUtils.createFeedVersion; +import static com.conveyal.datatools.TestUtils.zipFolderFiles; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class FaresV2ValidationTest { + + private static Project project; + private static FeedVersion faresV2Version; + + @BeforeAll + public static void setUp() throws IOException { + // Start server if it isn't already running. + DatatoolsTest.setUp(); + Auth0Connection.setAuthDisabled(true); + // Create a project, feed sources, and feed versions to merge. + project = new Project(); + project.name = String.format("Test %s", new Date()); + Persistence.projects.create(project); + + FeedSource faresV2FeedSource = new FeedSource("fares-v2"); + faresV2FeedSource.projectId = project.id; + Persistence.feedSources.create(faresV2FeedSource); + faresV2Version = createFeedVersion( + faresV2FeedSource, + zipFolderFiles("fake-agency-with-fares-v2") + ); + Persistence.feedVersions.replace(faresV2Version.id, faresV2Version); + } + + @AfterAll + static void tearDown() { + Auth0Connection.setAuthDisabled(Auth0Connection.getDefaultAuthDisabled()); + project.delete(); + } + + @Test + void canValidateFareV2Files() { + Document summary = (Document) faresV2Version.mobilityDataResult.get("summary"); + List files = summary.getList("files", String.class); + assertTrue(files.contains("areas.txt")); + assertTrue(files.contains("fare_leg_rules.txt")); + assertTrue(files.contains("fare_leg_join_rules.txt")); + assertTrue(files.contains("fare_media.txt")); + assertTrue(files.contains("fare_products.txt")); + assertTrue(files.contains("fare_transfer_rules.txt")); + assertTrue(files.contains("networks.txt")); + assertTrue(files.contains("rider_categories.txt")); + assertTrue(files.contains("route_networks.txt")); + assertTrue(files.contains("stop_areas.txt")); + assertTrue(files.contains("timeframes.txt")); + } + + /** + * This is not an exhaustive test, more of a sanity check that MobilityData can detect errors in fares v2 data. + * https://gtfs-validator.mobilitydata.org/rules.html + */ + @ParameterizedTest + @MethodSource("createValidationErrorChecks") + void canDetectValidationErrors(Set codes, String expectedCode) { + assertTrue(codes.contains(expectedCode)); + } + + private static Stream createValidationErrorChecks() { + ArrayList notices = (ArrayList) faresV2Version.mobilityDataResult.get("notices"); + Set codes = new HashSet<>(); + for (Document notice : notices) { + codes.add(notice.getString("code")); + } + return Stream.of( + Arguments.of(codes, "fare_transfer_rule_duration_limit_type_without_duration_limit"), + Arguments.of(codes, "fare_transfer_rule_duration_limit_without_type"), + Arguments.of(codes, "fare_transfer_rule_missing_transfer_count"), + Arguments.of(codes, "fare_transfer_rule_with_forbidden_transfer_count") + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformationTest.java b/src/test/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformationTest.java index ab738dfb6..cae5496f0 100644 --- a/src/test/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformationTest.java +++ b/src/test/java/com/conveyal/datatools/manager/models/transform/RemoveNonRevenueTripsTransformationTest.java @@ -107,15 +107,16 @@ static Stream createNonRevenueCases() { return Stream.of( Arguments.of( List.of( - "non-revenue-trip1,1,", "non-revenue-trip1,1,1", "non-revenue-trip1,1,1", - "non-revenue-trip1,1," + "non-revenue-trip1,1,1", + "non-revenue-trip1,1,1" ), List.of( "revenue-trip,0,0", "revenue-trip,1,1", - "revenue-trip,1,1", + "revenue-trip,2,1", + "revenue-trip,1,2", "revenue-trip,0,0" ), fieldIndexes @@ -131,8 +132,8 @@ static Stream createNonRevenueCases() { List.of( "revenue-trip,0,0,2,2", "revenue-trip,0,0,1,1", - "revenue-trip,0,0,2,2", - "revenue-trip,0,0,2,2" + "revenue-trip,0,0,2,1", + "revenue-trip,0,0,1,2" ), allFieldIndexes ) diff --git a/src/test/java/com/conveyal/datatools/manager/utils/sql/SqlSchemaUpdaterTest.java b/src/test/java/com/conveyal/datatools/manager/utils/sql/SqlSchemaUpdaterTest.java index c374cc010..f40474ee3 100644 --- a/src/test/java/com/conveyal/datatools/manager/utils/sql/SqlSchemaUpdaterTest.java +++ b/src/test/java/com/conveyal/datatools/manager/utils/sql/SqlSchemaUpdaterTest.java @@ -88,7 +88,16 @@ void canCheckAndUpgradeTables(String namespaceType) throws Exception { // Some tables are missing from the feed and should be flagged. assertTrue(namespaceCheck.missingTables.containsAll(Lists.newArrayList( Table.SCHEDULE_EXCEPTIONS, + Table.TIME_FRAMES, + Table.FARE_MEDIAS, + Table.FARE_PRODUCTS, + Table.NETWORKS, + Table.FARE_LEG_RULES, + Table.FARE_TRANSFER_RULES, Table.FEED_INFO, + Table.AREAS, + Table.RIDER_CATEGORIES, + Table.FARE_LEG_JOIN_RULES, Table.TRANSFERS, Table.FREQUENCIES, Table.ATTRIBUTIONS, diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/agency.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/agency.txt new file mode 100644 index 000000000..5cb6afa42 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/agency.txt @@ -0,0 +1,2 @@ +agency_id,agency_name,agency_url,agency_lang,agency_phone,agency_email,agency_timezone,agency_fare_url,agency_branding_url +1,Fake Transit,http://www.fake-agency.com,,,,America/Los_Angeles,, diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/areas.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/areas.txt new file mode 100644 index 000000000..73b6734b6 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/areas.txt @@ -0,0 +1,40 @@ +area_id,area_name +area_bl,Blue Line +area_bl_airport,Blue Line - Airport Station +area_cf_zone_buzzards,CapeFLYER - Wareham/Buzzards Bay/Bourne +area_cf_zone_hyannis,CapeFLYER - Hyannis +area_commuter_rail_porter_zone_1a,Commuter Rail Zone 1A +area_commuter_rail_sumner_tunnel_zone_1a,Commuter Rail Zone 1A +area_commuter_rail_zone_1,Commuter Rail Zone 1 +area_commuter_rail_zone_10,Commuter Rail Zone 10 +area_commuter_rail_zone_1a,Commuter Rail Zone 1A +area_commuter_rail_zone_2,Commuter Rail Zone 2 +area_commuter_rail_zone_3,Commuter Rail Zone 3 +area_commuter_rail_zone_4,Commuter Rail Zone 4 +area_commuter_rail_zone_5,Commuter Rail Zone 5 +area_commuter_rail_zone_6,Commuter Rail Zone 6 +area_commuter_rail_zone_7,Commuter Rail Zone 7 +area_commuter_rail_zone_8,Commuter Rail Zone 8 +area_commuter_rail_zone_9,Commuter Rail Zone 9 +area_fairmount_line_zone_1a,Commuter Rail Zone 1A - Fairmount Line +area_gl_govt_ctr,Green Line - Government Center +area_green_b_west_of_kenmore,Green Line B - West of Kenmore +area_green_c_west_of_kenmore,Green Line C - West of Kenmore +area_green_e_west_of_symphony,Green Line E - West of Symphony +area_m_ashmont_mattapan,Mattapan Trolley - Ashmont and Mattapan +area_ol_state,Orange Line - State +area_red_south_station,Red Line - South Station +area_route_354_downtown,Route 354 - Downtown +area_route_354_outside_downtown,Route 354 - Outside Downtown +area_route_426_downtown,Route 426 - Downtown +area_route_426_outside_downtown,Route 426 - Outside Downtown +area_route_450_downtown,Route 450 - Downtown +area_route_450_outside_downtown,Route 450 - Outside Downtown +area_sl3_north_of_airport,Silver Line - North of Airport Station +area_sl_airport,Silver Line - Airport Station +area_sl_courthouse,Silver Line - Courthouse +area_sl_logan_terminal,Silver Line - Airport Terminals +area_sl_silver_line_way,Silver Line - Silver Line Way +area_sl_south_station,Silver Line - South Station +area_sl_world_trade_center,Silver Line - World Trade Center +area_ss_commuter_rail_zone_1a,Commuter Rail Zone 1A - South Station diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/calendar.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/calendar.txt new file mode 100644 index 000000000..1bf13678e --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/calendar.txt @@ -0,0 +1,2 @@ +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +04100312-8fe1-46a5-a9f2-556f39478f57,1,1,1,1,1,1,1,20170915,20170917 \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/calendar_dates.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/calendar_dates.txt new file mode 100644 index 000000000..5d0a31806 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/calendar_dates.txt @@ -0,0 +1,3 @@ +service_id,date,exception_type +04100312-8fe1-46a5-a9f2-556f39478f57,20170916,2 +calendar-date-service,20170917,1 \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_leg_join_rules.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_leg_join_rules.txt new file mode 100644 index 000000000..a05f4b14a --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_leg_join_rules.txt @@ -0,0 +1,2 @@ +from_network_id,to_network_id,from_stop_id,to_stop_id +1,2,3,4 \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_leg_rules.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_leg_rules.txt new file mode 100644 index 000000000..37b60d472 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_leg_rules.txt @@ -0,0 +1,697 @@ +leg_group_id,network_id,from_area_id,to_area_id,fare_product_id,from_timeframe_group_id,to_timeframe_group_id,transfer_only +leg_airport_rapid_transit_quick_subway,rapid_transit,area_bl_airport,,prod_rapid_transit_quick_subway,timeframe_regular,, +leg_cape_buzzards_hyannis_cash,cape_flyer,area_cf_zone_buzzards,area_cf_zone_hyannis,prod_cape_buzzards_hyannis_fare,,, +leg_cape_buzzards_hyannis_cash,cape_flyer,area_cf_zone_hyannis,area_cf_zone_buzzards,prod_cape_buzzards_hyannis_fare,,, +leg_cape_buzzards_hyannis_cash,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_8,prod_cape_buzzards_hyannis_fare,,, +leg_cape_buzzards_hyannis_cash,cape_flyer,area_commuter_rail_zone_8,area_cf_zone_hyannis,prod_cape_buzzards_hyannis_fare,,, +leg_cape_sbb_buzzards_cash,cape_flyer,area_cf_zone_buzzards,area_commuter_rail_zone_1a,prod_cape_sbb_buzzards_fare,,, +leg_cape_sbb_buzzards_cash,cape_flyer,area_cf_zone_buzzards,area_commuter_rail_zone_2,prod_cape_sbb_buzzards_fare,,, +leg_cape_sbb_buzzards_cash,cape_flyer,area_cf_zone_buzzards,area_commuter_rail_zone_4,prod_cape_sbb_buzzards_fare,,, +leg_cape_sbb_buzzards_cash,cape_flyer,area_commuter_rail_zone_1a,area_cf_zone_buzzards,prod_cape_sbb_buzzards_fare,,, +leg_cape_sbb_buzzards_cash,cape_flyer,area_commuter_rail_zone_2,area_cf_zone_buzzards,prod_cape_sbb_buzzards_fare,,, +leg_cape_sbb_buzzards_cash,cape_flyer,area_commuter_rail_zone_4,area_cf_zone_buzzards,prod_cape_sbb_buzzards_fare,,, +leg_cape_sbb_hyannis_cash,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_1a,prod_cape_sbb_hyannis_fare,,, +leg_cape_sbb_hyannis_cash,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_2,prod_cape_sbb_hyannis_fare,,, +leg_cape_sbb_hyannis_cash,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_4,prod_cape_sbb_hyannis_fare,,, +leg_cape_sbb_hyannis_cash,cape_flyer,area_commuter_rail_zone_1a,area_cf_zone_hyannis,prod_cape_sbb_hyannis_fare,,, +leg_cape_sbb_hyannis_cash,cape_flyer,area_commuter_rail_zone_2,area_cf_zone_hyannis,prod_cape_sbb_hyannis_fare,,, +leg_cape_sbb_hyannis_cash,cape_flyer,area_commuter_rail_zone_4,area_cf_zone_hyannis,prod_cape_sbb_hyannis_fare,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_sumner_tunnel_zone_1a,area_commuter_rail_sumner_tunnel_zone_1a,prod_cr_zone_1a,timeframe_sumner_tunnel_closure,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_sumner_tunnel_zone_1a,area_commuter_rail_zone_1a,prod_cr_zone_1a,timeframe_sumner_tunnel_closure,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_1,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_10,prod_cr_inter_10,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_1a,prod_cr_zone_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_2,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_3,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_4,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_5,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_6,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_7,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_8,prod_cr_inter_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_9,prod_cr_inter_9,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_fairmount_line_zone_1a,prod_cr_zone_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1,area_ss_commuter_rail_zone_1a,prod_cr_zone_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_1,prod_cr_inter_10,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_10,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_1a,prod_cr_zone_10,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_2,prod_cr_inter_9,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_3,prod_cr_inter_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_4,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_5,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_6,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_7,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_8,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_9,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_fairmount_line_zone_1a,prod_cr_zone_10,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_10,area_ss_commuter_rail_zone_1a,prod_cr_zone_10,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_sumner_tunnel_zone_1a,prod_cr_zone_1a,timeframe_sumner_tunnel_closure,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_1,prod_cr_zone_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_10,prod_cr_zone_10,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_1a,prod_cr_zone_1a,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_2,prod_cr_zone_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_3,prod_cr_zone_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_4,prod_cr_zone_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_5,prod_cr_zone_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_6,prod_cr_zone_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_7,prod_cr_zone_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_8,prod_cr_zone_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_9,prod_cr_zone_9,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_fairmount_line_zone_1a,prod_cr_zone_1a,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_1a,area_ss_commuter_rail_zone_1a,prod_cr_zone_1a,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_1,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_10,prod_cr_inter_9,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_1a,prod_cr_zone_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_2,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_3,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_4,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_5,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_6,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_7,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_8,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_9,prod_cr_inter_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_fairmount_line_zone_1a,prod_cr_zone_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_2,area_ss_commuter_rail_zone_1a,prod_cr_zone_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_1,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_10,prod_cr_inter_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_1a,prod_cr_zone_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_2,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_3,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_4,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_5,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_6,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_7,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_8,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_9,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_fairmount_line_zone_1a,prod_cr_zone_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_3,area_ss_commuter_rail_zone_1a,prod_cr_zone_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_1,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_10,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_1a,prod_cr_zone_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_2,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_3,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_4,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_5,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_6,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_7,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_8,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_9,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_fairmount_line_zone_1a,prod_cr_zone_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_4,area_ss_commuter_rail_zone_1a,prod_cr_zone_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_1,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_10,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_1a,prod_cr_zone_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_2,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_3,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_4,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_5,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_6,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_7,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_8,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_9,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_fairmount_line_zone_1a,prod_cr_zone_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_5,area_ss_commuter_rail_zone_1a,prod_cr_zone_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_1,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_10,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_1a,prod_cr_zone_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_2,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_3,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_4,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_5,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_6,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_7,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_8,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_9,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_fairmount_line_zone_1a,prod_cr_zone_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_6,area_ss_commuter_rail_zone_1a,prod_cr_zone_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_1,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_10,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_1a,prod_cr_zone_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_2,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_3,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_4,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_5,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_6,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_7,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_8,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_9,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_fairmount_line_zone_1a,prod_cr_zone_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_7,area_ss_commuter_rail_zone_1a,prod_cr_zone_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_1,prod_cr_inter_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_10,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_1a,prod_cr_zone_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_2,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_3,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_4,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_5,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_6,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_7,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_8,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_9,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_fairmount_line_zone_1a,prod_cr_zone_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_8,area_ss_commuter_rail_zone_1a,prod_cr_zone_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_1,prod_cr_inter_9,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_10,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_1a,prod_cr_zone_9,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_2,prod_cr_inter_8,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_3,prod_cr_inter_7,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_4,prod_cr_inter_6,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_5,prod_cr_inter_5,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_6,prod_cr_inter_4,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_7,prod_cr_inter_3,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_8,prod_cr_inter_2,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_9,prod_cr_inter_1,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_fairmount_line_zone_1a,prod_cr_zone_9,,, +leg_commuter_rail_cash,commuter_rail,area_commuter_rail_zone_9,area_ss_commuter_rail_zone_1a,prod_cr_zone_9,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_1,prod_cr_zone_1,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_10,prod_cr_zone_10,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_1a,prod_cr_zone_1a,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_2,prod_cr_zone_2,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_3,prod_cr_zone_3,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_4,prod_cr_zone_4,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_5,prod_cr_zone_5,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_6,prod_cr_zone_6,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_7,prod_cr_zone_7,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_8,prod_cr_zone_8,,, +leg_commuter_rail_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_9,prod_cr_zone_9,,, +leg_commuter_rail_free,commuter_rail,area_commuter_rail_porter_zone_1a,area_commuter_rail_zone_1a,prod_free_fare,timeframe_alewife_kendall_surge,, +leg_commuter_rail_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_porter_zone_1a,prod_free_fare,timeframe_alewife_kendall_surge,, +leg_express_bus_cash,express_bus,area_route_354_downtown,area_route_354_outside_downtown,prod_express_bus,,, +leg_express_bus_cash,express_bus,area_route_354_outside_downtown,area_route_354_downtown,prod_express_bus,,, +leg_express_bus_cash,express_bus,area_route_426_downtown,area_route_426_outside_downtown,prod_express_bus,,, +leg_express_bus_cash,express_bus,area_route_426_outside_downtown,area_route_426_downtown,prod_express_bus,,, +leg_express_bus_cash,express_bus,area_route_450_downtown,area_route_450_outside_downtown,prod_express_bus,,, +leg_express_bus_cash,express_bus,area_route_450_outside_downtown,area_route_450_downtown,prod_express_bus,,, +leg_express_bus_cash,express_bus_special,,,prod_express_bus,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_1,prod_cr_zone_1,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_10,prod_cr_zone_10,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_1a,prod_cr_zone_1a,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_2,prod_cr_zone_2,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_3,prod_cr_zone_3,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_4,prod_cr_zone_4,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_5,prod_cr_zone_5,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_6,prod_cr_zone_6,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_7,prod_cr_zone_7,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_8,prod_cr_zone_8,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_9,prod_cr_zone_9,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_fairmount_line_zone_1a,prod_cr_zone_1a,,, +leg_fairmount_line_cash,commuter_rail,area_fairmount_line_zone_1a,area_ss_commuter_rail_zone_1a,prod_cr_zone_1a,,, +leg_ferry_east_boston_free,ferry_east_boston,,,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_ferry_east_boston_free,ferry_east_boston,,,prod_boat_zone_1a,timeframe_regular,, +leg_ferry_f1_cash,ferry_f1,,,prod_ferry_f1,,, +leg_ferry_f4_cash,ferry_f4,,,prod_ferry_f4,,, +leg_ferry_f6_cash,ferry_f6,,,prod_boat_zone_1a,timeframe_sumner_tunnel_closure,, +leg_ferry_f6_cash,ferry_f6,,,prod_boat_zone_1,timeframe_regular,, +leg_ferry_lynn_cash,ferry_lynn,,,prod_boat_zone_1a,timeframe_sumner_tunnel_closure,, +leg_ferry_lynn_cash,ferry_lynn,,,prod_boat_zone_2,timeframe_regular,, +leg_foxboro_event_cash,cr_foxboro,,,prod_foxboro_event_fare,,, +leg_local_bus_cash,express_bus,area_route_354_downtown,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,express_bus,area_route_354_outside_downtown,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,express_bus,area_route_426_downtown,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,express_bus,area_route_426_outside_downtown,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,express_bus,area_route_450_downtown,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,express_bus,area_route_450_outside_downtown,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_downtown,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_downtown,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_downtown,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_downtown,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_downtown,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_downtown,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_downtown,,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_outside_downtown,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_outside_downtown,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_outside_downtown,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_outside_downtown,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_outside_downtown,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_outside_downtown,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_354_outside_downtown,,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_downtown,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_downtown,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_downtown,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_downtown,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_downtown,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_downtown,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_downtown,,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_outside_downtown,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_outside_downtown,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_outside_downtown,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_outside_downtown,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_outside_downtown,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_outside_downtown,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_426_outside_downtown,,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_downtown,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_downtown,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_downtown,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_downtown,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_downtown,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_downtown,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_downtown,,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_outside_downtown,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_outside_downtown,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_outside_downtown,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_outside_downtown,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_outside_downtown,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_outside_downtown,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,area_route_450_outside_downtown,,prod_local_bus,,, +leg_local_bus_cash,local_bus,,area_route_354_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,,area_route_354_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,,area_route_426_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,,area_route_426_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,,area_route_450_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,,area_route_450_outside_downtown,prod_local_bus,,, +leg_local_bus_cash,local_bus,,,prod_local_bus,,, +leg_local_bus_free,local_bus_free,area_route_354_downtown,area_route_354_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_downtown,area_route_426_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_downtown,area_route_450_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_downtown,,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_outside_downtown,area_route_354_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_outside_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_outside_downtown,area_route_426_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_outside_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_outside_downtown,area_route_450_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_outside_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_354_outside_downtown,,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_downtown,area_route_354_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_downtown,area_route_426_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_downtown,area_route_450_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_downtown,,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_outside_downtown,area_route_354_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_outside_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_outside_downtown,area_route_426_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_outside_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_outside_downtown,area_route_450_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_outside_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_426_outside_downtown,,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_downtown,area_route_354_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_downtown,area_route_426_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_downtown,area_route_450_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_downtown,,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_outside_downtown,area_route_354_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_outside_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_outside_downtown,area_route_426_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_outside_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_outside_downtown,area_route_450_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_outside_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,area_route_450_outside_downtown,,prod_free_fare,,, +leg_local_bus_free,local_bus_free,,area_route_354_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,,area_route_354_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,,area_route_426_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,,area_route_426_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,,area_route_450_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,,area_route_450_outside_downtown,prod_free_fare,,, +leg_local_bus_free,local_bus_free,,,prod_free_fare,,, +leg_local_bus_quick_subway,express_bus,area_route_354_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,express_bus,area_route_354_outside_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,express_bus,area_route_426_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,express_bus,area_route_426_outside_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,express_bus,area_route_450_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,express_bus,area_route_450_outside_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_downtown,,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_outside_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_outside_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_outside_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_outside_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_outside_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_outside_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_354_outside_downtown,,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_downtown,,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_outside_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_outside_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_outside_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_outside_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_outside_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_outside_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_426_outside_downtown,,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_downtown,,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_outside_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_outside_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_outside_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_outside_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_outside_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_outside_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,area_route_450_outside_downtown,,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,,area_route_354_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,,area_route_426_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,,area_route_450_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,, +leg_local_bus_quick_subway,local_bus,,,prod_rapid_transit_quick_subway,,, +leg_local_bus_restricted_cash,local_bus_restricted,,,prod_local_bus,,, +leg_mattapan_rapid_transit_cash,m_rapid_transit,,,prod_rapid_transit_cash,,, +leg_mattapan_rapid_transit_quick_subway,m_rapid_transit,area_m_ashmont_mattapan,,prod_rapid_transit_quick_subway,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_downtown,area_route_354_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_downtown,area_route_426_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_downtown,area_route_450_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_downtown,,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_outside_downtown,area_route_354_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_outside_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_outside_downtown,area_route_426_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_outside_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_outside_downtown,area_route_450_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_outside_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_354_outside_downtown,,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_downtown,area_route_354_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_downtown,area_route_426_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_downtown,area_route_450_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_downtown,,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_outside_downtown,area_route_354_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_outside_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_outside_downtown,area_route_426_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_outside_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_outside_downtown,area_route_450_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_outside_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_426_outside_downtown,,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_downtown,area_route_354_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_downtown,area_route_426_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_downtown,area_route_450_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_downtown,,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_outside_downtown,area_route_354_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_outside_downtown,area_route_354_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_outside_downtown,area_route_426_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_outside_downtown,area_route_426_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_outside_downtown,area_route_450_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_outside_downtown,area_route_450_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,area_route_450_outside_downtown,,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,,area_route_354_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,,area_route_354_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,,area_route_426_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,,area_route_426_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,,area_route_450_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,,area_route_450_outside_downtown,prod_free_fare,,, +leg_rail_replacement_free,rail_replacement_bus,,,prod_free_fare,,, +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_downtown,,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_outside_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_outside_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_outside_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_outside_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_outside_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_outside_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_354_outside_downtown,,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_downtown,,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_outside_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_outside_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_outside_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_outside_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_outside_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_outside_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_426_outside_downtown,,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_downtown,,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_outside_downtown,area_route_354_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_outside_downtown,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_outside_downtown,area_route_426_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_outside_downtown,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_outside_downtown,area_route_450_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_outside_downtown,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,area_route_450_outside_downtown,,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,,area_route_354_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,,area_route_354_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,,area_route_426_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,,area_route_426_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,,area_route_450_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,,area_route_450_outside_downtown,prod_rapid_transit_quick_subway,,,1 +leg_rail_replacement_quick_subway,rail_replacement_bus,,,prod_rapid_transit_quick_subway,,,1 +leg_rapid_transit_cash,rapid_transit,area_green_b_west_of_kenmore,,prod_rapid_transit_cash,,, +leg_rapid_transit_cash,rapid_transit,area_green_c_west_of_kenmore,,prod_rapid_transit_cash,,, +leg_rapid_transit_cash,rapid_transit,area_green_e_west_of_symphony,,prod_rapid_transit_cash,,, +leg_rapid_transit_cash,rapid_transit,,,prod_rapid_transit_cash,,,1 +leg_rapid_transit_free,rapid_transit,area_bl,,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_rapid_transit_free,rapid_transit,area_bl_airport,,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_rapid_transit_free,rapid_transit,area_gl_govt_ctr,,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_rapid_transit_free,rapid_transit,area_ol_state,,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_rapid_transit_free,rapid_transit,,,prod_free_fare,,,1 +leg_rapid_transit_quick_subway,rapid_transit,area_bl,,prod_rapid_transit_quick_subway,timeframe_regular,, +leg_rapid_transit_quick_subway,rapid_transit,area_gl_govt_ctr,,prod_rapid_transit_quick_subway,timeframe_regular,, +leg_rapid_transit_quick_subway,rapid_transit,area_ol_state,,prod_rapid_transit_quick_subway,timeframe_regular,, +leg_rapid_transit_quick_subway,rapid_transit,,,prod_rapid_transit_quick_subway,timeframe_regular,, +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl3_north_of_airport,area_sl3_north_of_airport,prod_rapid_transit_cash,timeframe_regular,, +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl3_north_of_airport,area_sl_airport,prod_rapid_transit_cash,timeframe_regular,, +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl3_north_of_airport,,prod_rapid_transit_cash,timeframe_regular,, +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl_silver_line_way,area_sl3_north_of_airport,prod_rapid_transit_cash,timeframe_regular,, +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl_silver_line_way,area_sl_airport,prod_rapid_transit_cash,timeframe_regular,, +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl_silver_line_way,,prod_rapid_transit_cash,,, +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl_south_station,area_sl3_north_of_airport,prod_rapid_transit_cash,,,1 +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl_south_station,area_sl_airport,prod_rapid_transit_cash,,,1 +leg_sl_rapid_transit_cash,sl_rapid_transit,area_sl_south_station,,prod_rapid_transit_cash,,,1 +leg_sl_rapid_transit_cash,sl_rapid_transit,,,prod_rapid_transit_cash,,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl3_north_of_airport,area_sl3_north_of_airport,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl3_north_of_airport,area_sl_airport,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl3_north_of_airport,,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl_airport,area_sl3_north_of_airport,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl_airport,,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl_logan_terminal,,prod_free_fare,,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl_silver_line_way,area_sl3_north_of_airport,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_sl_rapid_transit_free,sl_rapid_transit,area_sl_silver_line_way,area_sl_airport,prod_free_fare,timeframe_sumner_tunnel_closure,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_airport,area_sl3_north_of_airport,prod_rapid_transit_quick_subway,timeframe_regular,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_airport,,prod_rapid_transit_quick_subway,timeframe_regular,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_courthouse,area_sl3_north_of_airport,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_courthouse,area_sl_airport,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_courthouse,,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_south_station,area_sl3_north_of_airport,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_south_station,area_sl_airport,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_south_station,,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_world_trade_center,area_sl3_north_of_airport,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_world_trade_center,area_sl_airport,prod_rapid_transit_quick_subway,,, +leg_sl_rapid_transit_quick_subway,sl_rapid_transit,area_sl_world_trade_center,,prod_rapid_transit_quick_subway,,, +leg_ss_fairmount_line_zone_1a_cash,commuter_rail,area_ss_commuter_rail_zone_1a,area_fairmount_line_zone_1a,prod_cr_zone_1a,,, +leg_ss_rapid_transit_cash,rapid_transit,area_red_south_station,,prod_rapid_transit_cash,,,1 +leg_ss_rapid_transit_free,rapid_transit,area_red_south_station,,prod_free_fare,,,1 +leg_ss_rapid_transit_quick_subway,rapid_transit,area_red_south_station,,prod_rapid_transit_quick_subway,,, +leg_systemwide_free,cape_flyer,area_cf_zone_buzzards,area_cf_zone_hyannis,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_buzzards,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_buzzards,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_buzzards,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_hyannis,area_cf_zone_buzzards,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_cf_zone_hyannis,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_commuter_rail_zone_1a,area_cf_zone_buzzards,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_commuter_rail_zone_1a,area_cf_zone_hyannis,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_commuter_rail_zone_2,area_cf_zone_buzzards,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_commuter_rail_zone_2,area_cf_zone_hyannis,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_commuter_rail_zone_4,area_cf_zone_buzzards,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_commuter_rail_zone_4,area_cf_zone_hyannis,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cape_flyer,area_commuter_rail_zone_8,area_cf_zone_hyannis,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_10,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_1a,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_2,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_3,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_4,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_5,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_6,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_7,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_8,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_commuter_rail_zone_9,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_fairmount_line_zone_1a,area_ss_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_1,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_10,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_2,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_3,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_4,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_5,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_6,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_7,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_8,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_commuter_rail_zone_9,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,commuter_rail,area_ss_commuter_rail_zone_1a,area_fairmount_line_zone_1a,prod_free_fare,timeframe_systemwide_free,, +leg_systemwide_free,cr_foxboro,,,prod_free_fare,timeframe_systemwide_free,, diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_media.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_media.txt new file mode 100644 index 000000000..a695de634 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_media.txt @@ -0,0 +1,5 @@ +fare_media_id,fare_media_name,fare_media_type +cash,Cash,0 +credit_debit,Credit/debit card,0 +charlieticket,CharlieTicket,1 +mticket,mTicket app,4 diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_products.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_products.txt new file mode 100644 index 000000000..88812f3a4 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_products.txt @@ -0,0 +1,97 @@ +fare_product_id,fare_product_name,rider_category_id,fare_media_id,amount,currency +prod_boat_zone_1,Ferry Zone 1 one-way fare,HONORED_CITIZEN,cash,6.50,USD +prod_boat_zone_1,Ferry Zone 1 one-way fare,,credit_debit,6.50,USD +prod_boat_zone_1,Ferry Zone 1 one-way fare,,mticket,6.50,USD +prod_boat_zone_1a,Ferry Zone 1A one-way fare,,cash,2.40,USD +prod_boat_zone_1a,Ferry Zone 1A one-way fare,,credit_debit,2.40,USD +prod_boat_zone_1a,Ferry Zone 1A one-way fare,,mticket,2.40,USD +prod_boat_zone_2,Ferry Zone 2 one-way fare,,cash,7.00,USD +prod_boat_zone_2,Ferry Zone 2 one-way fare,,credit_debit,7.00,USD +prod_boat_zone_2,Ferry Zone 2 one-way fare,,mticket,7.00,USD +prod_cape_buzzards_hyannis_fare,CapeFLYER Middleborough/Lakeville one-way fare,,cash,5.00,USD +prod_cape_buzzards_hyannis_fare,CapeFLYER Middleborough/Lakeville one-way fare,,credit_debit,5.00,USD +prod_cape_buzzards_hyannis_fare,CapeFLYER Middleborough/Lakeville one-way fare,,mticket,5.00,USD +prod_cape_sbb_buzzards_fare,CapeFLYER Bourne one-way fare,,cash,20.00,USD +prod_cape_sbb_buzzards_fare,CapeFLYER Bourne one-way fare,,credit_debit,20.00,USD +prod_cape_sbb_buzzards_fare,CapeFLYER Bourne one-way fare,,mticket,20.00,USD +prod_cape_sbb_hyannis_fare,CapeFLYER Hyannis one-way fare,,cash,22.00,USD +prod_cape_sbb_hyannis_fare,CapeFLYER Hyannis one-way fare,,credit_debit,22.00,USD +prod_cape_sbb_hyannis_fare,CapeFLYER Hyannis one-way fare,,mticket,22.00,USD +prod_cr_inter_1,Commuter Rail Interzone 1 one-way fare,,cash,2.75,USD +prod_cr_inter_1,Commuter Rail Interzone 1 one-way fare,,credit_debit,2.75,USD +prod_cr_inter_1,Commuter Rail Interzone 1 one-way fare,,mticket,2.75,USD +prod_cr_inter_10,Commuter Rail Interzone 10 one-way fare,,cash,7.25,USD +prod_cr_inter_10,Commuter Rail Interzone 10 one-way fare,,credit_debit,7.25,USD +prod_cr_inter_10,Commuter Rail Interzone 10 one-way fare,,mticket,7.25,USD +prod_cr_inter_2,Commuter Rail Interzone 2 one-way fare,,cash,3.25,USD +prod_cr_inter_2,Commuter Rail Interzone 2 one-way fare,,credit_debit,3.25,USD +prod_cr_inter_2,Commuter Rail Interzone 2 one-way fare,,mticket,3.25,USD +prod_cr_inter_3,Commuter Rail Interzone 3 one-way fare,,cash,3.50,USD +prod_cr_inter_3,Commuter Rail Interzone 3 one-way fare,,credit_debit,3.50,USD +prod_cr_inter_3,Commuter Rail Interzone 3 one-way fare,,mticket,3.50,USD +prod_cr_inter_4,Commuter Rail Interzone 4 one-way fare,,cash,4.25,USD +prod_cr_inter_4,Commuter Rail Interzone 4 one-way fare,,credit_debit,4.25,USD +prod_cr_inter_4,Commuter Rail Interzone 4 one-way fare,,mticket,4.25,USD +prod_cr_inter_5,Commuter Rail Interzone 5 one-way fare,,cash,4.75,USD +prod_cr_inter_5,Commuter Rail Interzone 5 one-way fare,,credit_debit,4.75,USD +prod_cr_inter_5,Commuter Rail Interzone 5 one-way fare,,mticket,4.75,USD +prod_cr_inter_6,Commuter Rail Interzone 6 one-way fare,,cash,5.25,USD +prod_cr_inter_6,Commuter Rail Interzone 6 one-way fare,,credit_debit,5.25,USD +prod_cr_inter_6,Commuter Rail Interzone 6 one-way fare,,mticket,5.25,USD +prod_cr_inter_7,Commuter Rail Interzone 7 one-way fare,,cash,5.75,USD +prod_cr_inter_7,Commuter Rail Interzone 7 one-way fare,,credit_debit,5.75,USD +prod_cr_inter_7,Commuter Rail Interzone 7 one-way fare,,mticket,5.75,USD +prod_cr_inter_8,Commuter Rail Interzone 8 one-way fare,,cash,6.25,USD +prod_cr_inter_8,Commuter Rail Interzone 8 one-way fare,,credit_debit,6.25,USD +prod_cr_inter_8,Commuter Rail Interzone 8 one-way fare,,mticket,6.25,USD +prod_cr_inter_9,Commuter Rail Interzone 9 one-way fare,,cash,6.75,USD +prod_cr_inter_9,Commuter Rail Interzone 9 one-way fare,,credit_debit,6.75,USD +prod_cr_inter_9,Commuter Rail Interzone 9 one-way fare,,mticket,6.75,USD +prod_cr_zone_1,Commuter Rail Zone 1 one-way fare,,cash,6.50,USD +prod_cr_zone_1,Commuter Rail Zone 1 one-way fare,,credit_debit,6.50,USD +prod_cr_zone_1,Commuter Rail Zone 1 one-way fare,,mticket,6.50,USD +prod_cr_zone_10,Commuter Rail Zone 10 one-way fare,,cash,13.25,USD +prod_cr_zone_10,Commuter Rail Zone 10 one-way fare,,credit_debit,13.25,USD +prod_cr_zone_10,Commuter Rail Zone 10 one-way fare,,mticket,13.25,USD +prod_cr_zone_1a,Commuter Rail Zone 1A one-way fare,,cash,2.40,USD +prod_cr_zone_1a,Commuter Rail Zone 1A one-way fare,,credit_debit,2.40,USD +prod_cr_zone_1a,Commuter Rail Zone 1A one-way fare,,mticket,2.40,USD +prod_cr_zone_2,Commuter Rail Zone 2 one-way fare,,cash,7.00,USD +prod_cr_zone_2,Commuter Rail Zone 2 one-way fare,,credit_debit,7.00,USD +prod_cr_zone_2,Commuter Rail Zone 2 one-way fare,,mticket,7.00,USD +prod_cr_zone_3,Commuter Rail Zone 3 one-way fare,,cash,8.00,USD +prod_cr_zone_3,Commuter Rail Zone 3 one-way fare,,credit_debit,8.00,USD +prod_cr_zone_3,Commuter Rail Zone 3 one-way fare,,mticket,8.00,USD +prod_cr_zone_4,Commuter Rail Zone 4 one-way fare,,cash,8.75,USD +prod_cr_zone_4,Commuter Rail Zone 4 one-way fare,,credit_debit,8.75,USD +prod_cr_zone_4,Commuter Rail Zone 4 one-way fare,,mticket,8.75,USD +prod_cr_zone_5,Commuter Rail Zone 5 one-way fare,,cash,9.75,USD +prod_cr_zone_5,Commuter Rail Zone 5 one-way fare,,credit_debit,9.75,USD +prod_cr_zone_5,Commuter Rail Zone 5 one-way fare,,mticket,9.75,USD +prod_cr_zone_6,Commuter Rail Zone 6 one-way fare,,cash,10.50,USD +prod_cr_zone_6,Commuter Rail Zone 6 one-way fare,,credit_debit,10.50,USD +prod_cr_zone_6,Commuter Rail Zone 6 one-way fare,,mticket,10.50,USD +prod_cr_zone_7,Commuter Rail Zone 7 one-way fare,,cash,11.00,USD +prod_cr_zone_7,Commuter Rail Zone 7 one-way fare,,credit_debit,11.00,USD +prod_cr_zone_7,Commuter Rail Zone 7 one-way fare,,mticket,11.00,USD +prod_cr_zone_8,Commuter Rail Zone 8 one-way fare,,cash,12.25,USD +prod_cr_zone_8,Commuter Rail Zone 8 one-way fare,,credit_debit,12.25,USD +prod_cr_zone_8,Commuter Rail Zone 8 one-way fare,,mticket,12.25,USD +prod_cr_zone_9,Commuter Rail Zone 9 one-way fare,,cash,12.75,USD +prod_cr_zone_9,Commuter Rail Zone 9 one-way fare,,credit_debit,12.75,USD +prod_cr_zone_9,Commuter Rail Zone 9 one-way fare,,mticket,12.75,USD +prod_express_bus,Express Bus cash fare,,cash,4.25,USD +prod_ferry_east_boston,East Boston Ferry one-way fare,,mticket,2.40,USD +prod_ferry_f1,Hingham/Hull Ferry one-way fare,,cash,9.75,USD +prod_ferry_f1,Hingham/Hull Ferry one-way fare,,credit_debit,9.75,USD +prod_ferry_f1,Hingham/Hull Ferry one-way fare,,mticket,9.75,USD +prod_ferry_f4,Charlestown Ferry one-way fare,,cash,3.70,USD +prod_ferry_f4,Charlestown Ferry one-way fare,,credit_debit,3.70,USD +prod_ferry_f4,Charlestown Ferry one-way fare,,mticket,3.70,USD +prod_foxboro_event_fare,Foxboro Event Service round-trip fare,,cash,20.00,USD +prod_foxboro_event_fare,Foxboro Event Service round-trip fare,,credit_debit,20.00,USD +prod_foxboro_event_fare,Foxboro Event Service round-trip fare,,mticket,20.00,USD +prod_free_fare,Free fare,,,0.00,USD +prod_local_bus,Local Bus cash fare,,cash,1.70,USD +prod_rapid_transit_cash,Subway cash fare,,cash,2.40,USD +prod_rapid_transit_quick_subway,Subway Quick Ticket,,charlieticket,2.40,USD diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_transfer_rules.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_transfer_rules.txt new file mode 100644 index 000000000..bccb48b0f --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/fare_transfer_rules.txt @@ -0,0 +1,38 @@ +from_leg_group_id,to_leg_group_id,transfer_count,duration_limit,duration_limit_type,fare_transfer_type,fare_product_id,filter_fare_product_id,fare_media_behavior,fare_product_behavior +leg_airport_rapid_transit_quick_subway,leg_local_bus_quick_subway,99,7200,1,0,prod_rapid_transit_quick_subway,prod_rapid_transit_quick_subway,0,1 +leg_airport_rapid_transit_quick_subway,leg_rapid_transit_quick_subway,,,1,0,prod_rapid_transit_quick_subway,prod_rapid_transit_quick_subway,0,1 +leg_mattapan_rapid_transit_quick_subway,leg_local_bus_quick_subway,,7200,1,0,prod_rapid_transit_quick_subway,prod_rapid_transit_quick_subway,0,1 +leg_mattapan_rapid_transit_quick_subway,leg_rail_replacement_quick_subway,,99,,0,,prod_rapid_transit_quick_subway,0,1 +leg_mattapan_rapid_transit_quick_subway,leg_rapid_transit_quick_subway,99999,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_mattapan_rapid_transit_quick_subway,leg_mattapan_rapid_transit_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_rail_replacement_quick_subway,leg_airport_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rail_replacement_quick_subway,leg_local_bus_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_rail_replacement_quick_subway,leg_mattapan_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rail_replacement_quick_subway,leg_rail_replacement_quick_subway,-1,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rail_replacement_quick_subway,leg_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rail_replacement_quick_subway,leg_sl_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rail_replacement_quick_subway,leg_ss_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rapid_transit_cash,leg_rapid_transit_cash,-1,,,0,,prod_rapid_transit_cash,0,1 +leg_rapid_transit_cash,leg_sl_rapid_transit_cash,,,,0,,prod_rapid_transit_cash,0,1 +leg_rapid_transit_free,leg_rapid_transit_free,-1,,,0,,prod_free_fare,0,1 +leg_rapid_transit_free,leg_sl_rapid_transit_free,,,,0,,prod_free_fare,0,1 +leg_rapid_transit_quick_subway,leg_airport_rapid_transit_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_rapid_transit_quick_subway,leg_local_bus_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_rapid_transit_quick_subway,leg_mattapan_rapid_transit_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_rapid_transit_quick_subway,leg_rail_replacement_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rapid_transit_quick_subway,leg_rapid_transit_quick_subway,-1,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rapid_transit_quick_subway,leg_sl_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_rapid_transit_quick_subway,leg_ss_rapid_transit_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_sl_rapid_transit_cash,leg_ss_rapid_transit_cash,,,,0,,prod_rapid_transit_cash,0,1 +leg_sl_rapid_transit_free,leg_ss_rapid_transit_free,,,,0,,prod_free_fare,0,1 +leg_sl_rapid_transit_quick_subway,leg_airport_rapid_transit_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_sl_rapid_transit_quick_subway,leg_local_bus_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_sl_rapid_transit_quick_subway,leg_mattapan_rapid_transit_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_sl_rapid_transit_quick_subway,leg_rail_replacement_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_sl_rapid_transit_quick_subway,leg_ss_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_ss_rapid_transit_cash,leg_rapid_transit_cash,,,,0,,prod_rapid_transit_cash,0,1 +leg_ss_rapid_transit_free,leg_rapid_transit_free,,,,0,,prod_free_fare,0,1 +leg_ss_rapid_transit_quick_subway,leg_local_bus_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_ss_rapid_transit_quick_subway,leg_mattapan_rapid_transit_quick_subway,,7200,1,0,,prod_rapid_transit_quick_subway,0,1 +leg_ss_rapid_transit_quick_subway,leg_rail_replacement_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 +leg_ss_rapid_transit_quick_subway,leg_rapid_transit_quick_subway,,,,0,,prod_rapid_transit_quick_subway,0,1 diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/feed_info.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/feed_info.txt new file mode 100644 index 000000000..ceac60810 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/feed_info.txt @@ -0,0 +1,2 @@ +feed_id,feed_publisher_name,feed_publisher_url,feed_lang,feed_version +fake_transit,Conveyal,http://www.conveyal.com,en,1.0 \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/networks.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/networks.txt new file mode 100644 index 000000000..47a67eaad --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/networks.txt @@ -0,0 +1,2 @@ +network_id,network_name +1,Forbidden because network id is defined in routes \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/rider_categories.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/rider_categories.txt new file mode 100644 index 000000000..d2bb3107a --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/rider_categories.txt @@ -0,0 +1,4 @@ +rider_category_id,rider_category_name,min_age,max_age,eligibility_url,is_default_fare_category +HONORED_CITIZEN,Honored Citizen,,,https://trimet.org/fares/honoredcitizen.htm,0 +ADULT,Adult,,,,1 +YOUTH,Youth,7,17,https://trimet.org/fares/youth.htm,0 diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/route_networks.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/route_networks.txt new file mode 100644 index 000000000..d2f8f9a9d --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/route_networks.txt @@ -0,0 +1,2 @@ +network_id,route_id +1,1 \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/routes.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/routes.txt new file mode 100644 index 000000000..dd6a30eaf --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/routes.txt @@ -0,0 +1,2 @@ +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type,route_url,route_color,route_text_color,route_sort_order,route_fare_class,line_id,listed_route,network_id +1,1,,RL,RT,1,https://www.mbta.com/schedules/Red,DA291C,FFFFFF,10010,Rapid Transit,line-Red,,rapid_transit \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/shapes.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/shapes.txt new file mode 100644 index 000000000..3f2e3fd13 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/shapes.txt @@ -0,0 +1,8 @@ +shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence,shape_dist_traveled +5820f377-f947-4728-ac29-ac0102cbc34e,37.0612132,-122.0074332,1,0.0000000 +5820f377-f947-4728-ac29-ac0102cbc34e,37.0611720,-122.0075000,2,7.4997067 +5820f377-f947-4728-ac29-ac0102cbc34e,37.0613590,-122.0076830,3,33.8739075 +5820f377-f947-4728-ac29-ac0102cbc34e,37.0608780,-122.0082780,4,109.0402932 +5820f377-f947-4728-ac29-ac0102cbc34e,37.0603590,-122.0088280,5,184.6078298 +5820f377-f947-4728-ac29-ac0102cbc34e,37.0597610,-122.0093540,6,265.8053023 +5820f377-f947-4728-ac29-ac0102cbc34e,37.0590660,-122.0099190,7,357.8617018 diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stop_areas.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stop_areas.txt new file mode 100644 index 000000000..85cbda304 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stop_areas.txt @@ -0,0 +1,848 @@ +stop_id,area_id +4u6g,area_route_426_downtown +4u6g,area_route_450_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_sl_logan_terminal +4u6g,area_sl_logan_terminal +4u6g,area_sl_logan_terminal +4u6g,area_sl_logan_terminal +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_sl_logan_terminal +4u6g,area_route_426_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_downtown +4u6g,area_route_354_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_ol_state +4u6g,area_ol_state +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl_airport +4u6g,area_bl_airport +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_bl +4u6g,area_red_south_station +4u6g,area_red_south_station +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_green_b_west_of_kenmore +4u6g,area_gl_govt_ctr +4u6g,area_gl_govt_ctr +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_c_west_of_kenmore +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_green_e_west_of_symphony +4u6g,area_m_ashmont_mattapan +4u6g,area_m_ashmont_mattapan +4u6g,area_m_ashmont_mattapan +4u6g,area_bl +4u6g,area_sl_airport +4u6g,area_sl_airport +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_450_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_route_426_outside_downtown +4u6g,area_sl_south_station +4u6g,area_sl_courthouse +4u6g,area_sl_world_trade_center +4u6g,area_sl_silver_line_way +4u6g,area_sl_world_trade_center +4u6g,area_sl_courthouse +4u6g,area_sl_south_station +4u6g,area_sl_silver_line_way +4u6g,area_sl3_north_of_airport +4u6g,area_sl3_north_of_airport +4u6g,area_sl3_north_of_airport +4u6g,area_sl3_north_of_airport +4u6g,area_sl3_north_of_airport +4u6g,area_sl3_north_of_airport +4u6g,area_sl3_north_of_airport +4u6g,area_sl3_north_of_airport +4u6g,area_route_426_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_route_426_downtown +4u6g,area_route_450_downtown +4u6g,area_route_354_outside_downtown +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_cf_zone_buzzards +4u6g,area_cf_zone_buzzards +4u6g,area_cf_zone_buzzards +4u6g,area_cf_zone_hyannis +4u6g,area_commuter_rail_zone_2 +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_fairmount_line_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_porter_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_porter_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_porter_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_sumner_tunnel_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_10 +4u6g,area_commuter_rail_zone_9 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_ss_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_8 +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1a +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_1 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_2 +4u6g,area_commuter_rail_zone_3 +4u6g,area_commuter_rail_zone_4 +4u6g,area_commuter_rail_zone_5 +4u6g,area_commuter_rail_zone_6 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 +4u6g,area_commuter_rail_zone_7 \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stop_times.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stop_times.txt new file mode 100644 index 000000000..1cfefaa49 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stop_times.txt @@ -0,0 +1,7 @@ +trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,shape_dist_traveled,timepoint +a30277f8-e50a-4a85-9141-b1e0da9d429d,07:00:00,07:00:00,4u6g,1,Test stop headsign,0,0,0.0000000, +a30277f8-e50a-4a85-9141-b1e0da9d429d,07:01:00,07:01:00,johv,2,Test stop headsign 2,0,0,341.4491961, +frequency-trip,08:00:00,08:00:00,4u6g,1,Test stop headsign frequency trip,0,0,0.0000000, +frequency-trip,08:29:00,08:29:00,1234,2,Test stop headsign frequency trip 2,0,0,341.4491961, +calendar-date-trip,08:00:00,08:00:00,4u6g,1,Test stop headsign calendar date trip,0,0,0.0000000, +calendar-date-trip,08:29:00,08:29:00,1234,2,Test stop headsign calendar date trip 2,0,0,341.4491961, \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stops.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stops.txt new file mode 100644 index 000000000..ba368047e --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/stops.txt @@ -0,0 +1,5 @@ +stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station,stop_timezone,wheelchair_boarding +4u6g,,Butler Ln,,37.0612132,-122.0074332,,,0,,, +johv,,Scotts Valley Dr & Victor Sq,,37.0590172,-122.0096058,,,0,,, +123,,Parent Station,,37.0666,-122.0777,,,1,,, +1234,,Child Stop,,37.06662,-122.07772,,,0,123,, \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/timeframes.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/timeframes.txt new file mode 100644 index 000000000..52667abe1 --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/timeframes.txt @@ -0,0 +1,8 @@ +timeframe_group_id,start_time,end_time,service_id +timeframe_regular,,,04100312-8fe1-46a5-a9f2-556f39478f57 +timeframe_systemwide_free,,,04100312-8fe1-46a5-a9f2-556f39478f57 +timeframe_sumner_tunnel_closure,,,04100312-8fe1-46a5-a9f2-556f39478f57 +timeframe_sumner_tunnel_closure,00:00:00,02:30:00,04100312-8fe1-46a5-a9f2-556f39478f57 +timeframe_regular,02:30:00,24:00:00,04100312-8fe1-46a5-a9f2-556f39478f57 +timeframe_alewife_kendall_surge,,,04100312-8fe1-46a5-a9f2-556f39478f57 +timeframe_alewife_kendall_surge,00:00:00,02:30:00,04100312-8fe1-46a5-a9f2-556f39478f57 diff --git a/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/trips.txt b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/trips.txt new file mode 100644 index 000000000..982c01e0f --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/gtfs/fake-agency-with-fares-v2/trips.txt @@ -0,0 +1,4 @@ +route_id,trip_id,trip_headsign,trip_short_name,direction_id,block_id,shape_id,bikes_allowed,wheelchair_accessible,service_id +1,a30277f8-e50a-4a85-9141-b1e0da9d429d,,,0,,5820f377-f947-4728-ac29-ac0102cbc34e,0,0,04100312-8fe1-46a5-a9f2-556f39478f57 +1,frequency-trip,,,0,,5820f377-f947-4728-ac29-ac0102cbc34e,0,0,04100312-8fe1-46a5-a9f2-556f39478f57 +1,calendar-date-trip,,,0,,5820f377-f947-4728-ac29-ac0102cbc34e,0,0,calendar-date-service \ No newline at end of file diff --git a/src/test/resources/com/conveyal/datatools/mock-aws-credentials b/src/test/resources/com/conveyal/datatools/mock-aws-credentials new file mode 100644 index 000000000..6835e25cc --- /dev/null +++ b/src/test/resources/com/conveyal/datatools/mock-aws-credentials @@ -0,0 +1,3 @@ +[default] +aws_access_key_id = mock-id-123 +aws_secret_access_key = mock-secret-123