diff --git a/cli/src/main/java/org/mobilitydata/gtfsvalidator/cli/Arguments.java b/cli/src/main/java/org/mobilitydata/gtfsvalidator/cli/Arguments.java index d7dda065c0..d79bfbfeeb 100644 --- a/cli/src/main/java/org/mobilitydata/gtfsvalidator/cli/Arguments.java +++ b/cli/src/main/java/org/mobilitydata/gtfsvalidator/cli/Arguments.java @@ -21,6 +21,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import org.mobilitydata.gtfsvalidator.input.CountryCode; import org.mobilitydata.gtfsvalidator.runner.ValidationRunnerConfig; @@ -52,6 +54,14 @@ public class Arguments { + "It must be a two-letter country code (ISO 3166-1 alpha-2)") private String countryCode; + @Parameter( + names = {"-d", "--date"}, + description = + "Date to simulate when validating, in ISO_LOCAL_DATE format like " + + "'2001-01-30'. By default, the current date is used. " + + "This option enables debugging rules like feed expiration.") + private String dateString; + @Parameter( names = {"-u", "--url"}, description = "Fully qualified URL to download GTFS archive") @@ -111,6 +121,9 @@ ValidationRunnerConfig toConfig() throws URISyntaxException { if (countryCode != null) { builder.setCountryCode(CountryCode.forStringOrUnknown(countryCode)); } + if (dateString != null) { + builder.setDateForValidation(LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE)); + } if (validationReportName != null) { builder.setValidationReportFileName(validationReportName); } diff --git a/cli/src/test/java/org/mobilitydata/gtfsvalidator/cli/ArgumentsTest.java b/cli/src/test/java/org/mobilitydata/gtfsvalidator/cli/ArgumentsTest.java index 795215c72a..4cba280185 100644 --- a/cli/src/test/java/org/mobilitydata/gtfsvalidator/cli/ArgumentsTest.java +++ b/cli/src/test/java/org/mobilitydata/gtfsvalidator/cli/ArgumentsTest.java @@ -87,7 +87,8 @@ public void longNameShouldInitializeArguments() throws URISyntaxException { "--input", "/tmp/gtfs.zip", "--output_base", "/tmp/output", "--country_code", "ca", - "--threads", "4" + "--threads", "4", + "--date", "2020-01-02" }; Arguments underTest = new Arguments(); @@ -100,6 +101,7 @@ public void longNameShouldInitializeArguments() throws URISyntaxException { assertThat(config.validationReportFileName()).matches("report.json"); assertThat(config.htmlReportFileName()).matches("report.html"); assertThat(config.systemErrorsReportFileName()).matches("system_errors.json"); + assertThat(config.dateForValidation().toString()).matches("2020-01-02"); } @Test diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/HtmlReportGenerator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/HtmlReportGenerator.java index 0cdef7acf4..3d72409b8a 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/HtmlReportGenerator.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/HtmlReportGenerator.java @@ -39,7 +39,8 @@ public void generateReport( ValidationRunnerConfig config, VersionInfo versionInfo, Path reportPath, - String date) + String date, + boolean is_different_date) throws IOException { TemplateEngine templateEngine = new TemplateEngine(); ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); @@ -54,6 +55,7 @@ public void generateReport( context.setVariable("summary", summary); context.setVariable("config", config); context.setVariable("date", date); + context.setVariable("is_different_date", is_different_date); try (FileWriter writer = new FileWriter(reportPath.toFile())) { templateEngine.process("report.html", context, writer); diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/JsonReportSummary.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/JsonReportSummary.java index 8ee69ae308..08470a1ca3 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/JsonReportSummary.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/JsonReportSummary.java @@ -29,6 +29,7 @@ public class JsonReportSummary { private String validationReportName; private String htmlReportName; private String countryCode; + private String dateForValidation; private JsonReportFeedInfo feedInfo; private List agencies; private Set files; @@ -55,6 +56,7 @@ public JsonReportSummary( this.validationReportName = config.validationReportFileName(); this.htmlReportName = config.htmlReportFileName(); this.countryCode = config.countryCode().getCountryCode(); + this.dateForValidation = config.dateForValidation().toString(); } else { logger.atSevere().log( "No validation configuration for JSON report, there will be missing data in the report."); diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunner.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunner.java index ad5fae0960..51a7f61734 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunner.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunner.java @@ -25,10 +25,9 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; -import java.text.SimpleDateFormat; import java.time.Duration; -import java.time.LocalDate; -import java.util.Date; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.stream.Collectors; import org.mobilitydata.gtfsvalidator.input.DateForValidation; @@ -120,7 +119,7 @@ public Status run(ValidationRunnerConfig config) { ValidationContext validationContext = ValidationContext.builder() .setCountryCode(config.countryCode()) - .setDateForValidation(new DateForValidation(LocalDate.now())) + .setDateForValidation(new DateForValidation(config.dateForValidation())) .build(); try { feedContainer = @@ -267,9 +266,9 @@ public static void exportReport( "Error creating output directory: %s", config.outputDirectory()); } } - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); - Date now = new Date(System.currentTimeMillis()); - String date = formatter.format(now); + ZonedDateTime now = ZonedDateTime.now(); + String date = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd 'at' HH:mm:ss z")); + boolean is_different_date = !now.toLocalDate().equals(config.dateForValidation()); Gson gson = createGson(config.prettyJson()); HtmlReportGenerator htmlGenerator = new HtmlReportGenerator(); @@ -291,7 +290,8 @@ public static void exportReport( config, versionInfo, config.outputDirectory().resolve(config.htmlReportFileName()), - date); + date, + is_different_date); Files.write( config.outputDirectory().resolve(config.systemErrorsReportFileName()), gson.toJson(noticeContainer.exportSystemErrors()).getBytes(StandardCharsets.UTF_8)); diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunnerConfig.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunnerConfig.java index 2fc9d52c67..9eea19bb65 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunnerConfig.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/runner/ValidationRunnerConfig.java @@ -18,6 +18,7 @@ import com.google.auto.value.AutoValue; import java.net.URI; import java.nio.file.Path; +import java.time.LocalDate; import java.util.Optional; import org.mobilitydata.gtfsvalidator.input.CountryCode; @@ -56,6 +57,9 @@ public Path systemErrorsReportPath() { // validated. public abstract CountryCode countryCode(); + // The date to use for validation. + public abstract LocalDate dateForValidation(); + // If true, any output json will be pretty-printed. public abstract boolean prettyJson(); @@ -67,7 +71,8 @@ public static Builder builder() { .setSystemErrorsReportFileName("system_errors.json") .setNumThreads(1) .setPrettyJson(false) - .setCountryCode(CountryCode.forStringOrUnknown(CountryCode.ZZ)); + .setCountryCode(CountryCode.forStringOrUnknown(CountryCode.ZZ)) + .setDateForValidation(LocalDate.now()); } @AutoValue.Builder @@ -88,6 +93,8 @@ public abstract static class Builder { public abstract Builder setCountryCode(CountryCode countryCode); + public abstract Builder setDateForValidation(LocalDate dateForValidation); + public abstract Builder setPrettyJson(boolean prettyJson); public abstract ValidationRunnerConfig build(); diff --git a/main/src/main/resources/report.html b/main/src/main/resources/report.html index 6c1d1256c8..f95a71d3b0 100644 --- a/main/src/main/resources/report.html +++ b/main/src/main/resources/report.html @@ -229,6 +229,7 @@

GTFS Schedule Validation Report


+

Use this report alongside our documentation.

diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/JsonReportSummaryTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/JsonReportSummaryTest.java index c9587c34d7..0c6fedea7d 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/JsonReportSummaryTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/JsonReportSummaryTest.java @@ -9,6 +9,7 @@ import com.google.gson.JsonParser; import java.net.URI; import java.nio.file.Path; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; @@ -41,6 +42,7 @@ private static ValidationRunnerConfig generateValidationRunnerConfig() throws Ex builder.setPrettyJson(true); builder.setSystemErrorsReportFileName("some_error_filename"); builder.setValidationReportFileName("some_report_filename"); + builder.setDateForValidation(LocalDate.parse("2020-01-02")); return builder.build(); } @@ -91,7 +93,8 @@ public void noFeedMetadataWithConfigTest() throws Exception { + "\"systemErrorsReportName\":\"some_error_filename\"," + "\"validationReportName\":\"some_report_filename\"," + "\"htmlReportName\":\"some_html_filename\"," - + "\"countryCode\":\"GB\"}"; + + "\"countryCode\":\"GB\"," + + "\"dateForValidation\":\"2020-01-02\"}"; assertEquals(JsonParser.parseString(expected), gson.toJsonTree(reportSummary)); } @@ -113,6 +116,7 @@ public void withFeedMetadataWithConfigTest() throws Exception { + "\"validationReportName\":\"some_report_filename\"," + "\"htmlReportName\":\"some_html_filename\"," + "\"countryCode\":\"GB\"," + + "\"dateForValidation\":\"2020-01-02\"," + "\"feedInfo\":{\"publisherName\":\"value1\",\"publisherUrl\":\"value2\"}," + "\"agencies\":[" + "{\"name\":\"agency1\",\"url\":\"some URL 1\",\"phone\":\"phone1\",\"email\":\"email1\"},"