Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GTFS dataset files get zipped and validated #24

Open
wants to merge 57 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9b085ae
new property for GTFS zip file name and path
kat-ka Dec 23, 2020
89656f8
new method for packing GTFS txt files in one GTFS zip file
kat-ka Dec 23, 2020
6ef2d18
pack GTFS zip file at the end
kat-ka Dec 23, 2020
d440ed3
add dependencies and repositories for GTFS validation with conveyal
kat-ka Jan 5, 2021
c3baedf
validate GTFS and write results in a file
kat-ka Jan 5, 2021
8794e54
new validator class for properties defined in the application.propert…
kat-ka Jan 5, 2021
e642a01
define GTFS output directories and files
kat-ka Jan 5, 2021
0d63810
run with GTFS validation, add also properties validation for output d…
kat-ka Jan 5, 2021
48bf797
value annotation with default, null check
kat-ka Jan 5, 2021
dc002be
new properties for sending notification mail in case of GTFS validati…
kat-ka Jan 12, 2021
59c04c6
add spring boot mail dependency
kat-ka Jan 12, 2021
ea0507c
format
kat-ka Jan 12, 2021
8cf011a
alert implementation for sending notifications
kat-ka Jan 12, 2021
0b39897
JSON converter with new methods for pretty print and convertion from …
kat-ka Jan 12, 2021
099cedf
GTFS feed validation not only saves results to file but also sends al…
kat-ka Jan 12, 2021
883485c
format
kat-ka Jan 12, 2021
d7092f9
Exception with getMessage() or printStackTrace(), but not both
kat-ka Jan 12, 2021
8620fdb
property with default for better property validation logging
kat-ka Jan 14, 2021
04925f2
additional e-mail address validation for better logging
kat-ka Jan 14, 2021
9dbfdf3
call mail properties validation
kat-ka Jan 14, 2021
c06b444
email notification not only for invalid GTFS feeds but also for any p…
kat-ka Jan 19, 2021
57b3726
change log level
kat-ka Jan 19, 2021
8f7ce7f
change log message
kat-ka Jan 19, 2021
1067e0a
add log message
kat-ka Jan 19, 2021
78cc622
use commons-lang for removing an element from an array
kat-ka Feb 4, 2021
a44c88f
change ridetogtfsconverter log level, add more comments to explain pa…
kat-ka Feb 4, 2021
e4181b4
will be used to handle OneBusAway errors
kat-ka Feb 4, 2021
45bc21b
helper for closing OBA objects
kat-ka Feb 4, 2021
8c01612
convert mail recipient addresses to normal form
kat-ka Feb 4, 2021
d631e62
move zip method since it has nothing to do with OBA
kat-ka Feb 4, 2021
6da8293
empty default for better debugging
kat-ka Feb 4, 2021
f91fb47
empty default for better debugging
kat-ka Feb 4, 2021
6c58246
more detailed OneBusAway GTFS reader and writer exception handling an…
kat-ka Feb 4, 2021
3dc6686
take out zip method since it moved
kat-ka Feb 4, 2021
07638ff
more logging
kat-ka Feb 4, 2021
f9055a3
routing service classes with better exception handling and logging an…
kat-ka Feb 4, 2021
87e24fb
unused
kat-ka Feb 4, 2021
682b662
set default
kat-ka Feb 4, 2021
63b64ba
more detailed log and mail messages, better wording
kat-ka Feb 4, 2021
3696165
also add the method to the interface
kat-ka Feb 4, 2021
960f832
move area validation to validator class, move e-mail normalization to…
kat-ka Feb 4, 2021
e7c62af
update, readability
kat-ka Feb 4, 2021
e5483a9
gradle wrapper update
kat-ka Feb 9, 2021
5a32777
dependency version updates
kat-ka Feb 9, 2021
f8a6883
update git ignore files and directories
kat-ka Feb 9, 2021
da2c4ba
version match needed for OSMNodeService
kat-ka Feb 9, 2021
5af9ce7
update tests
kat-ka Feb 9, 2021
6a563d9
routing engine keys as application properties
kat-ka Feb 10, 2021
208d095
try to fix HibernateException on Jenkins for running the tests
kat-ka Feb 10, 2021
86bba73
GTFS validation method returns success or failure
kat-ka Feb 10, 2021
3e19c82
new method for serving GTFS file; wording
kat-ka Feb 10, 2021
0866481
make GTFS file public
kat-ka Feb 10, 2021
29b0374
update
kat-ka Feb 10, 2021
c84c3c0
format
kat-ka Feb 10, 2021
f5878cf
expect less because of failure
kat-ka Feb 16, 2021
3108cb5
avoid NPE
kat-ka Mar 10, 2021
047c3d9
use area Baden-Wuerttemberg, or remove it if not needed
kat-ka Mar 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ build/
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
Expand All @@ -33,8 +36,8 @@ out/
### VS Code ###
.vscode/

### Eclipse ###
.classpath
.project
.settings/
bin/
### project-specific ###

/data*/
/src/main/resources/public/
/tmp/
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ Define database connection and GTFS option properties in the **application.prope

* spring.datasource.url
* spring.datasource.username
* spring.datasource.password
* spring.datasource.password
...

or use environment variables:

* DB_URL
* DB_USERNAME
* DB_PASSWORD
* DB_PASSWORD
...

`./gradlew clean bootRun --args 'runOnce'`
Expand All @@ -26,4 +26,4 @@ to start the application and generate the GTFS once.

to start the application and generate the GTFS by the given cron schedule.

Default GTFS output directory is `data/`.
Default GTFS output directory is `data/output/`. When using scheduling the GTFS file will also be published to http://localhost:8080/gtfs.zip
30 changes: 22 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'org.springframework.boot' version '2.4.0'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'org.springframework.boot' version '2.4.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'groovy'
}
Expand All @@ -14,13 +14,19 @@ configurations {
}
repositories {
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.osgeo.org/repository/release/' }
mavenCentral()
maven {
url = 'http://nexus.onebusaway.org/content/repositories/releases/'
allowInsecureProtocol = true
}
}
ext {
set('springCloudVersion', "2020.0.0-M5")
set('springCloudVersion', "2020.0.1")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j'
// routing with GraphHopper
Expand All @@ -30,20 +36,28 @@ dependencies {
// for XML WebClient response in OpenStreetMap node service
implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'javax.activation:activation:1.1.1'
implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.3'
implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.1'
// write GTFS
implementation 'org.onebusaway:onebusaway-gtfs:1.3.4'
// delete directory
implementation 'org.onebusaway:onebusaway-gtfs:1.3.87'
// GTFS validation
implementation ('com.conveyal:gtfs-validation-lib:0.1.8') {
exclude group: 'org.slf4j', module: 'slf4j-simple'
}
implementation ('com.conveyal:gtfs-validator-json:0.1.8') {
exclude group: 'org.slf4j', module: 'slf4j-simple'
}
// for util handling
implementation 'commons-io:commons-io:2.8.0'
implementation 'org.apache.commons:commons-lang3:3.11'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.codehaus.groovy:groovy:3.0.6'
testImplementation 'org.codehaus.groovy:groovy:3.0.7'
testImplementation 'org.codehaus.groovy:groovy-all:3.0.6'
testImplementation 'org.spockframework:spock-spring:2.0-M4-groovy-3.0'
testImplementation 'org.spockframework:spock-spring:2.0-M4-groovy-3.0'
}
dependencyManagement {
imports {
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
Expand Down
21 changes: 3 additions & 18 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Expand All @@ -54,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
Expand All @@ -64,29 +64,14 @@ echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
Expand Down
119 changes: 55 additions & 64 deletions src/main/java/com/ride2go/ridetogtfsconverter/RunService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@

import static com.ride2go.ridetogtfsconverter.configuration.SystemConfiguration.AMOUNT_OF_THREADS;
import static com.ride2go.ridetogtfsconverter.util.DateAndTimeHandler.TIME_ZONE_ID_BERLIN;
import static com.ride2go.ridetogtfsconverter.validation.Constraints.AREA_BADEN_WUERTTEMBERG;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -24,92 +21,96 @@
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;

import com.ride2go.ridetogtfsconverter.exception.OBAException;
import com.ride2go.ridetogtfsconverter.gtfs.OBAWriterParameter;
import com.ride2go.ridetogtfsconverter.gtfs.WriterService;
import com.ride2go.ridetogtfsconverter.model.item.Offer;
import com.ride2go.ridetogtfsconverter.normalization.ApplicationPropertiesNormalizer;
import com.ride2go.ridetogtfsconverter.ridesdata.ReaderService;
import com.ride2go.ridetogtfsconverter.routing.RoutingHandler;
import com.ride2go.ridetogtfsconverter.util.DateAndTimeHandler;
import com.ride2go.ridetogtfsconverter.util.FileHandler;
import com.ride2go.ridetogtfsconverter.validation.ApplicationPropertiesValidator;
import com.ride2go.ridetogtfsconverter.validation.GtfsValidator;

@Service
public class RunService {

private static final Logger LOG = LoggerFactory.getLogger(RunService.class);

private static final String DEFAULT_GTFS_OUTPUT_DIRECTORY = "data/";
private static final String PUBLIC_GTFS_FILE_LOCATION = "src/main/resources/public/gtfs.zip";

private static final int PAGE_SIZE = 5000;

private static final Sort TRIP_SORT = Sort.by(Order.desc("tripId"));

@Autowired
private WriterService writerService;
private ApplicationPropertiesNormalizer applicationPropertiesNormalizer;

@Autowired
private ApplicationPropertiesValidator applicationPropertiesValidator;

@Autowired
private ReaderService readerService;

@Autowired
private RoutingHandler routingHandler;

@Value("${custom.gtfs.output.directory}")
private String gtfsOutputDirectory;
@Autowired
private WriterService writerService;

@Value("${custom.trips.by-user}")
private String tripsByUser;
@Autowired
private GtfsValidator gtfsValidator;

@Value("${custom.gtfs.dataset.directory:data/dataset/}")
private String gtfsDatasetDirectory;

@Value("${custom.gtfs.file:data/output/gtfs.zip}")
private String gtfsFile;

@Value("${custom.gtfs.trips.area}")
private String area;
@Value("${custom.gtfs.validation.file:data/validation/results.json}")
private String gtfsValidationFile;

@Value("${custom.trips.by-user:#{null}}")
private String tripsByUser;

protected void run() throws Exception {
updateDateAndTimeParameter();
if (applicationPropertiesValidator.validDirectoriesAndFiles(
gtfsDatasetDirectory, gtfsFile, gtfsValidationFile)) {
applicationPropertiesValidator.validArea();
final File directory = new File(gtfsDatasetDirectory);
try {
writerService.writeProviderInfoAsGTFS(directory);
if (tripsByUser != null && !tripsByUser.trim().isEmpty()) {
LOG.info("Get all trips from User " + tripsByUser);
processTripsByUser(directory, tripsByUser);
} else {
LOG.info("UserId not specified. Get trips from all users.");
processAllTrips(directory);
}
} catch (OBAException e) {
LOG.error("Problem while writing GTFS data with OneBusAway: " + e.getMessage());
}
FileHandler.zipGtfsDatasetFiles(directory, gtfsFile);
gtfsValidator.setRecipients(
applicationPropertiesNormalizer.normalizeMailRecipientAddressArray(
gtfsValidator.getRecipients()));
applicationPropertiesValidator.validMailAddresses(
gtfsValidator.getRecipients());
if (gtfsValidator.check(gtfsFile, gtfsValidationFile)) {
FileHandler.exposeGtfsFile(gtfsFile, PUBLIC_GTFS_FILE_LOCATION);
}
}
}

private void updateDateAndTimeParameter() {
DateAndTimeHandler.today = LocalDate.now(TIME_ZONE_ID_BERLIN);
DateAndTimeHandler.oneMonthFromToday = DateAndTimeHandler.today.plusMonths(1);
DateAndTimeHandler.oneYearFromToday = DateAndTimeHandler.today.plusYears(1);
OBAWriterParameter.feedStartDate = DateAndTimeHandler.today;
OBAWriterParameter.feedEndDate = DateAndTimeHandler.oneMonthFromToday;
OBAWriterParameter.feedTimePeriodWeekDays = OBAWriterParameter.getFeedTimePeriodWeekDays();

final File directory = getDirectory();
LOG.info("Use directory " + directory);
final String userId = getUserId();
area();
writerService.writeProviderInfoAsGTFS(directory);
if (userId != null) {
LOG.info("Get all trips from User " + userId);
processTripsByUser(directory, userId);
} else {
LOG.info("UserId not specified. Get trips from all users.");
processAllTrips(directory);
}
}

private File getDirectory() throws IOException {
final String directoryString = getValueOrDefault(gtfsOutputDirectory, DEFAULT_GTFS_OUTPUT_DIRECTORY);
final File directory = new File(directoryString);
try {
if (directory.exists() && directory.isDirectory()) {
FileUtils.deleteDirectory(directory);
}
} catch (IOException e) {
LOG.error("Problem with given directory");
throw e;
}
return directory;
}

private String getUserId() {
return getValueOrDefault(tripsByUser, null);
}

private void area() {
if (area.isEmpty()) {
LOG.info("No area restriction");
} else {
if (area.equals(AREA_BADEN_WUERTTEMBERG)) {
LOG.info("Trips only from the area: " + area);
} else {
LOG.info("Area {} not recognized", area);
}
}
}

private void processTripsByUser(final File directory, String userId) {
Expand Down Expand Up @@ -141,14 +142,4 @@ private void processAllTrips(final File directory)
index += AMOUNT_OF_THREADS;
}
}

private String getValueOrDefault(String value, String defaultValue) {
if (value != null) {
value = value.trim();
if (!value.isEmpty()) {
return value;
}
}
return defaultValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ public class ScheduledService {

private boolean finished = true;

@Scheduled(cron = "${custom.scheduling.job.cron}", zone = TIME_ZONE_BERLIN)
@Scheduled(cron = "${custom.scheduling.job.cron:}", zone = TIME_ZONE_BERLIN)
public void run() throws Exception {
if (enable) {
if (finished) {
finished = false;
runService.run();
finished = true;
} else {
LOG.info("Can't do a new run if the last run is not finished");
LOG.warn("Can't do a new run if the last run is not finished");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer()

@Bean
@Qualifier("configured")
public RoutingService getRoutingService(@Value("${custom.routing.service}") final String routingServiceChoice) {
public RoutingService getRoutingService(@Value("${custom.routing.service:}") final String routingServiceChoice) {
if (routingServiceChoice.equals("GH")) {
LOG.info("Use GraphHopper as routing engine");
return new GHRoutingService();
Expand Down
Loading