commit bd6fc3e7bd90ccc819e846b95abe1dfc0d579c0c Author: bvn13 Date: Fri Apr 3 02:05:42 2020 +0300 initial commit = working code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10ea907 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.ideaDataSources +dataSources + +*.iml +*.ipr +*.iws \ No newline at end of file diff --git a/.gradle/5.2.1/executionHistory/executionHistory.bin b/.gradle/5.2.1/executionHistory/executionHistory.bin new file mode 100644 index 0000000..b66c9a4 Binary files /dev/null and b/.gradle/5.2.1/executionHistory/executionHistory.bin differ diff --git a/.gradle/5.2.1/executionHistory/executionHistory.lock b/.gradle/5.2.1/executionHistory/executionHistory.lock new file mode 100644 index 0000000..f89109b Binary files /dev/null and b/.gradle/5.2.1/executionHistory/executionHistory.lock differ diff --git a/.gradle/5.2.1/fileChanges/last-build.bin b/.gradle/5.2.1/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/.gradle/5.2.1/fileChanges/last-build.bin differ diff --git a/.gradle/5.2.1/fileContent/fileContent.lock b/.gradle/5.2.1/fileContent/fileContent.lock new file mode 100644 index 0000000..4c249dd Binary files /dev/null and b/.gradle/5.2.1/fileContent/fileContent.lock differ diff --git a/.gradle/5.2.1/fileHashes/fileHashes.bin b/.gradle/5.2.1/fileHashes/fileHashes.bin new file mode 100644 index 0000000..5667d3c Binary files /dev/null and b/.gradle/5.2.1/fileHashes/fileHashes.bin differ diff --git a/.gradle/5.2.1/fileHashes/fileHashes.lock b/.gradle/5.2.1/fileHashes/fileHashes.lock new file mode 100644 index 0000000..cf5d084 Binary files /dev/null and b/.gradle/5.2.1/fileHashes/fileHashes.lock differ diff --git a/.gradle/5.2.1/fileHashes/resourceHashesCache.bin b/.gradle/5.2.1/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..2b3331a Binary files /dev/null and b/.gradle/5.2.1/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/5.2.1/gc.properties b/.gradle/5.2.1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/5.2.1/javaCompile/classAnalysis.bin b/.gradle/5.2.1/javaCompile/classAnalysis.bin new file mode 100644 index 0000000..e37730a Binary files /dev/null and b/.gradle/5.2.1/javaCompile/classAnalysis.bin differ diff --git a/.gradle/5.2.1/javaCompile/jarAnalysis.bin b/.gradle/5.2.1/javaCompile/jarAnalysis.bin new file mode 100644 index 0000000..35bb87e Binary files /dev/null and b/.gradle/5.2.1/javaCompile/jarAnalysis.bin differ diff --git a/.gradle/5.2.1/javaCompile/javaCompile.lock b/.gradle/5.2.1/javaCompile/javaCompile.lock new file mode 100644 index 0000000..b13e7ff Binary files /dev/null and b/.gradle/5.2.1/javaCompile/javaCompile.lock differ diff --git a/.gradle/5.2.1/javaCompile/taskHistory.bin b/.gradle/5.2.1/javaCompile/taskHistory.bin new file mode 100644 index 0000000..f073f8b Binary files /dev/null and b/.gradle/5.2.1/javaCompile/taskHistory.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..4aa05ec Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..697e3dc --- /dev/null +++ b/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Thu Apr 02 12:41:23 MSK 2020 +gradle.version=5.2.1 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000..e37cfe4 Binary files /dev/null and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4f98d88 --- /dev/null +++ b/build.gradle @@ -0,0 +1,98 @@ +buildscript { + ext { + springBootVersion = '2.2.6.RELEASE' + springBootDependenciesVersion = '1.0.9.RELEASE' + } + + repositories { + maven { url 'https://repo.spring.io/milestone' } + mavenCentral() + } + + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" + classpath "io.spring.gradle:dependency-management-plugin:${springBootDependenciesVersion}" + } +} + + +plugins { + id 'org.springframework.boot' version '2.2.6.RELEASE' + id 'io.spring.dependency-management' version '1.0.9.RELEASE' + id 'java' +} + +allprojects { + repositories { + mavenCentral() + } +} + +subprojects { + project.ext { + camelVersion = '3.1.0' + springBootDependenciesVersion = '2.2.2.RELEASE' + fasterxmlJacksonVersion = '2.10.0' + apacheCommonsCollectionsVersion = '4.4' + apacheCommonsLangVersion = '3.9' + } + + apply plugin: 'java' + apply plugin: 'idea' + apply plugin: 'org.springframework.boot' + apply plugin: 'io.spring.dependency-management' + + group = 'com.bvn13.covid19' + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + + repositories { + maven { url 'https://repo.spring.io/milestone' } + mavenCentral() + } + + tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + } + + dependencyManagement { + imports { + mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") + } + } + + configurations { + compileOnly { + extendsFrom annotationProcessor + } + } + + dependencies { + annotationProcessor 'org.projectlombok:lombok' + compileOnly 'org.projectlombok:lombok' + + testImplementation('org.springframework.boot:spring-boot-starter-test') { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } + + // https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 + implementation "org.apache.commons:commons-collections4:${apacheCommonsCollectionsVersion}" + + // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 + implementation "org.apache.commons:commons-lang3:${apacheCommonsLangVersion}" + + } + + test { + useJUnitPlatform() + + testLogging { + events 'started', 'passed', 'failed', 'skipped', 'standard_out', 'standard_error' + } + } + +} + +defaultTasks('clean', 'compileTestJava', 'jar') + diff --git a/bvn13.develop.covid19.main.iml b/bvn13.develop.covid19.main.iml new file mode 100644 index 0000000..f6e9e70 --- /dev/null +++ b/bvn13.develop.covid19.main.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/bvn13.develop.covid19.test.iml b/bvn13.develop.covid19.test.iml new file mode 100644 index 0000000..523ea6a --- /dev/null +++ b/bvn13.develop.covid19.test.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/covid19-api/.gitignore b/covid19-api/.gitignore new file mode 100644 index 0000000..6c01878 --- /dev/null +++ b/covid19-api/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/covid19-api/build.gradle b/covid19-api/build.gradle new file mode 100644 index 0000000..3172df4 --- /dev/null +++ b/covid19-api/build.gradle @@ -0,0 +1,15 @@ +version = '0.0.1' + +dependencies { + compile(project(':covid19-model')) + + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-cache' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + implementation 'com.github.ben-manes.caffeine:caffeine:2.8.1' + + runtimeOnly 'org.postgresql:postgresql' + +} + diff --git a/covid19-api/gradle/wrapper/gradle-wrapper.jar b/covid19-api/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f3d88b1 Binary files /dev/null and b/covid19-api/gradle/wrapper/gradle-wrapper.jar differ diff --git a/covid19-api/gradle/wrapper/gradle-wrapper.properties b/covid19-api/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a4b4429 --- /dev/null +++ b/covid19-api/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/covid19-api/gradlew b/covid19-api/gradlew new file mode 100755 index 0000000..536f027 --- /dev/null +++ b/covid19-api/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG=$(dirname "$PRG")"/$link" + fi +done +SAVED="$(pwd)" +cd "$(dirname \"$PRG\")/" >/dev/null +APP_HOME="$(pwd -P)" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=$(basename "$0") + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn() { + echo "$*" +} + +die() { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$(uname)" in +CYGWIN*) + cygwin=true + ;; +Darwin*) + darwin=true + ;; +MINGW*) + msys=true + ;; +NONSTOP*) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ]; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then + MAX_FD_LIMIT=$(ulimit -H -n) + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +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 + ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) + SEP="" + for dir in $ROOTDIRSRAW; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ]; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@"; do + CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) + CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition + eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") + else + eval $(echo args$i)="\"$arg\"" + fi + i=$(expr $i + 1) + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save() { + for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/covid19-api/gradlew.bat b/covid19-api/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/covid19-api/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +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% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/Covid19ApiApplication.java b/covid19-api/src/main/java/com/bvn13/covid19/api/Covid19ApiApplication.java new file mode 100644 index 0000000..1d621cd --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/Covid19ApiApplication.java @@ -0,0 +1,20 @@ +package com.bvn13.covid19.api; + +import com.bvn13.covid19.model.Covid19ModelConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Import; + +@EnableCaching +@SpringBootApplication +@Import({ + Covid19ModelConfig.class +}) +public class Covid19ApiApplication { + + public static void main(String[] args) { + SpringApplication.run(Covid19ApiApplication.class, args); + } + +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/controllers/MainController.java b/covid19-api/src/main/java/com/bvn13/covid19/api/controllers/MainController.java new file mode 100644 index 0000000..e7133eb --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/controllers/MainController.java @@ -0,0 +1,57 @@ +package com.bvn13.covid19.api.controllers; + +import com.bvn13.covid19.api.model.CovidStatsInfo; +import com.bvn13.covid19.api.model.CovidStatsResponse; +import com.bvn13.covid19.api.service.CovidStatsMaker; +import com.bvn13.covid19.model.entities.CovidStat; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.PostConstruct; +import java.time.ZoneId; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/") +public class MainController { + + private final CovidStatsMaker covidStatsMaker; + + @Value("${app.zone-id}") + private String zoneIdStr; + private ZoneId zoneId; + + @PostConstruct + public void init() { + zoneId = ZoneId.of(zoneIdStr); + } + + @GetMapping + public CovidStatsResponse getStatistics() { + return covidStatsMaker.findLastUpdateInfo() + .map(updateInfo -> CovidStatsResponse.builder() + .datetime(updateInfo.getDatetime()) + .updatedOn(updateInfo.getCreatedOn().atZone(zoneId)) + .stats(convertStats(covidStatsMaker.findCovidStatsByUpdateInfoId(updateInfo.getId()))) + .build()) + .orElse(CovidStatsResponse.builder().build()); + } + + private List convertStats(Collection stats) { + return stats.stream() + .map(stat -> CovidStatsInfo.builder() + .region(stat.getRegion().getName()) + .sick(stat.getSick()) + .healed(stat.getHealed()) + .died(stat.getDied()) + .build()) + .collect(Collectors.toList()); + } + +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/converters/ZonedDateTimeToStringConverter.java b/covid19-api/src/main/java/com/bvn13/covid19/api/converters/ZonedDateTimeToStringConverter.java new file mode 100644 index 0000000..a74d7c6 --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/converters/ZonedDateTimeToStringConverter.java @@ -0,0 +1,15 @@ +package com.bvn13.covid19.api.converters; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Component +public class ZonedDateTimeToStringConverter implements Converter { + @Override + public String convert(ZonedDateTime source) { + return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(source); + } +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/dtos/CovidUpdateInfoDto.java b/covid19-api/src/main/java/com/bvn13/covid19/api/dtos/CovidUpdateInfoDto.java new file mode 100644 index 0000000..5b7bd41 --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/dtos/CovidUpdateInfoDto.java @@ -0,0 +1,10 @@ +package com.bvn13.covid19.api.dtos; + +import lombok.Value; + +import java.time.LocalDateTime; + +@Value +public class CovidUpdateInfoDto { + LocalDateTime createdOn; +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/model/CovidStatsInfo.java b/covid19-api/src/main/java/com/bvn13/covid19/api/model/CovidStatsInfo.java new file mode 100644 index 0000000..9349f45 --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/model/CovidStatsInfo.java @@ -0,0 +1,25 @@ +package com.bvn13.covid19.api.model; + +import lombok.Builder; +import lombok.Value; + +import java.time.ZonedDateTime; + +@Builder +@Value +public class CovidStatsInfo { + + String region; + long sick; + long healed; + long died; + + @Builder + @Value + public static class Delta { + long sick; + long healed; + long died; + } + +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/model/CovidStatsResponse.java b/covid19-api/src/main/java/com/bvn13/covid19/api/model/CovidStatsResponse.java new file mode 100644 index 0000000..9e35152 --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/model/CovidStatsResponse.java @@ -0,0 +1,19 @@ +package com.bvn13.covid19.api.model; + +import lombok.Builder; +import lombok.Singular; +import lombok.Value; + +import java.time.ZonedDateTime; +import java.util.List; + +@Builder +@Value +public class CovidStatsResponse { + + ZonedDateTime updatedOn; + ZonedDateTime datetime; + + @Singular(value = "stats") + List stats; +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/repositories/CovidStatsRepository.java b/covid19-api/src/main/java/com/bvn13/covid19/api/repositories/CovidStatsRepository.java new file mode 100644 index 0000000..2e48452 --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/repositories/CovidStatsRepository.java @@ -0,0 +1,16 @@ +package com.bvn13.covid19.api.repositories; + +import com.bvn13.covid19.model.entities.CovidStat; +import com.bvn13.covid19.model.entities.CovidUpdateInfo; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Collection; + +@Repository +public interface CovidStatsRepository extends JpaRepository { + + Collection findAllByUpdateInfo(CovidUpdateInfo updateInfo); + Collection findAllByUpdateInfo_Id(long updateInfoId); + +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/repositories/CovidUpdateInfosRepository.java b/covid19-api/src/main/java/com/bvn13/covid19/api/repositories/CovidUpdateInfosRepository.java new file mode 100644 index 0000000..0eee747 --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/repositories/CovidUpdateInfosRepository.java @@ -0,0 +1,20 @@ +package com.bvn13.covid19.api.repositories; + +import com.bvn13.covid19.api.dtos.CovidUpdateInfoDto; +import com.bvn13.covid19.model.entities.CovidUpdateInfo; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.Optional; + +@Repository +public interface CovidUpdateInfosRepository extends JpaRepository { + + @Query("select new com.bvn13.covid19.api.dtos.CovidUpdateInfoDto(max(U.createdOn)) from CovidUpdateInfo U") + Optional findLastUpdateInfo(); + + Optional findFirstByCreatedOn(LocalDateTime createdOn); + +} diff --git a/covid19-api/src/main/java/com/bvn13/covid19/api/service/CovidStatsMaker.java b/covid19-api/src/main/java/com/bvn13/covid19/api/service/CovidStatsMaker.java new file mode 100644 index 0000000..9e7cc3b --- /dev/null +++ b/covid19-api/src/main/java/com/bvn13/covid19/api/service/CovidStatsMaker.java @@ -0,0 +1,50 @@ +package com.bvn13.covid19.api.service; + +import com.bvn13.covid19.api.repositories.CovidStatsRepository; +import com.bvn13.covid19.api.repositories.CovidUpdateInfosRepository; +import com.bvn13.covid19.model.entities.CovidStat; +import com.bvn13.covid19.model.entities.CovidUpdateInfo; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Component; + +import javax.transaction.Transactional; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +@RequiredArgsConstructor +@Component +public class CovidStatsMaker { + + private final CovidStatsRepository covidRepository; + private final CovidUpdateInfosRepository updatesRepository; + + @Transactional + public Collection getLastCovidStats() { + return updatesRepository.findLastUpdateInfo() + .flatMap(updateInfo -> updatesRepository.findFirstByCreatedOn(updateInfo.getCreatedOn())) + .map(covidRepository::findAllByUpdateInfo) + .orElse(Collections.emptyList()); + } + + @Cacheable( + cacheNames = "covid-last-update-info", + unless = "#result == null" + ) + @Transactional + public Optional findLastUpdateInfo() { + return updatesRepository.findLastUpdateInfo() + .flatMap(updateInfo -> updatesRepository.findFirstByCreatedOn(updateInfo.getCreatedOn())); + } + + @Cacheable( + cacheNames = "covid-stats-by-update-info-id", + condition = "#updateInfoId > 0", + unless = "#result == null || #result.size() <= 0" + ) + public Collection findCovidStatsByUpdateInfoId(long updateInfoId) { + return covidRepository.findAllByUpdateInfo_Id(updateInfoId); + } + +} diff --git a/covid19-api/src/main/resources/application.yaml b/covid19-api/src/main/resources/application.yaml new file mode 100644 index 0000000..e0ac796 --- /dev/null +++ b/covid19-api/src/main/resources/application.yaml @@ -0,0 +1,49 @@ +app: + zone-id: Europe/Moscow + +spring: + application: + name: covid19-api + + cache: + type: caffeine + caffeine: + spec: expireAfterWrite=15m + cache-names: covid-last-update-info, covid-stats-by-update-info-id + + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/covid19 + username: + password: + + jpa: + show-sql: true + hibernate: + dialect: org.hibernate.dialect.PostgreSQL + ddl-auto: update + + properties: + hibernate: + use_sql_comments: true + format_sql: true + show_sql: true + jdbc: + lob: + non_contextual_creation: true +logging: + level: + root: info + com: + bvn13: + covid19: debug + org: + springframework: warn + hibernate: + SQL: debug + type: + descriptor: + sql: + BasicBinder: trace + pattern: + console: "%d{dd-MM-yyyy HH:mm:ss.SSS} %highlight(%-5level) %magenta([%thread]) %yellow(%logger.%M) - %msg%n" \ No newline at end of file diff --git a/covid19-api/src/test/java/com/bvn13/covid19/api/Covid19ApiApplicationTests.java b/covid19-api/src/test/java/com/bvn13/covid19/api/Covid19ApiApplicationTests.java new file mode 100644 index 0000000..e6398cc --- /dev/null +++ b/covid19-api/src/test/java/com/bvn13/covid19/api/Covid19ApiApplicationTests.java @@ -0,0 +1,13 @@ +package com.bvn13.covid19.covid19api; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Covid19ApiApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/covid19-model/.gitignore b/covid19-model/.gitignore new file mode 100644 index 0000000..6c01878 --- /dev/null +++ b/covid19-model/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/covid19-model/build.gradle b/covid19-model/build.gradle new file mode 100644 index 0000000..eb009cc --- /dev/null +++ b/covid19-model/build.gradle @@ -0,0 +1,11 @@ +//apply plugin: 'org.springframework.boot.gradle.plugin.SpringBootPlugin' + +version = '0.0.1' + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' +} + +bootJar { enabled = false } +jar { enabled = true } \ No newline at end of file diff --git a/covid19-model/gradle/wrapper/gradle-wrapper.jar b/covid19-model/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f3d88b1 Binary files /dev/null and b/covid19-model/gradle/wrapper/gradle-wrapper.jar differ diff --git a/covid19-model/gradle/wrapper/gradle-wrapper.properties b/covid19-model/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a4b4429 --- /dev/null +++ b/covid19-model/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/covid19-model/gradlew b/covid19-model/gradlew new file mode 100755 index 0000000..536f027 --- /dev/null +++ b/covid19-model/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG=$(dirname "$PRG")"/$link" + fi +done +SAVED="$(pwd)" +cd "$(dirname \"$PRG\")/" >/dev/null +APP_HOME="$(pwd -P)" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=$(basename "$0") + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn() { + echo "$*" +} + +die() { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$(uname)" in +CYGWIN*) + cygwin=true + ;; +Darwin*) + darwin=true + ;; +MINGW*) + msys=true + ;; +NONSTOP*) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ]; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then + MAX_FD_LIMIT=$(ulimit -H -n) + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +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 + ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) + SEP="" + for dir in $ROOTDIRSRAW; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ]; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@"; do + CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) + CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition + eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") + else + eval $(echo args$i)="\"$arg\"" + fi + i=$(expr $i + 1) + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save() { + for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/covid19-model/gradlew.bat b/covid19-model/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/covid19-model/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +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% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/covid19-model/src/main/java/com/bvn13/covid19/model/Covid19ModelConfig.java b/covid19-model/src/main/java/com/bvn13/covid19/model/Covid19ModelConfig.java new file mode 100644 index 0000000..2db48a3 --- /dev/null +++ b/covid19-model/src/main/java/com/bvn13/covid19/model/Covid19ModelConfig.java @@ -0,0 +1,12 @@ +package com.bvn13.covid19.model; + +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@Configuration +//@ComponentScan("com.bvn13.covid19.model") +@EntityScan("com.bvn13.covid19.model.entities") +public class Covid19ModelConfig { +} diff --git a/covid19-model/src/main/java/com/bvn13/covid19/model/entities/CovidStat.java b/covid19-model/src/main/java/com/bvn13/covid19/model/entities/CovidStat.java new file mode 100644 index 0000000..08e7c8e --- /dev/null +++ b/covid19-model/src/main/java/com/bvn13/covid19/model/entities/CovidStat.java @@ -0,0 +1,36 @@ +package com.bvn13.covid19.model.entities; + +import lombok.Data; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "covid_statistics") +public class CovidStat { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private long id; + + private LocalDateTime createdOn; + + @ManyToOne + @JoinColumn(name = "region_id") + private Region region; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "update_info_id") + private CovidUpdateInfo updateInfo; + + private long sick; + private long healed; + private long died; + + @PrePersist + public void prePersist() { + this.createdOn = LocalDateTime.now(); + } + +} diff --git a/covid19-model/src/main/java/com/bvn13/covid19/model/entities/CovidUpdateInfo.java b/covid19-model/src/main/java/com/bvn13/covid19/model/entities/CovidUpdateInfo.java new file mode 100644 index 0000000..50c15a0 --- /dev/null +++ b/covid19-model/src/main/java/com/bvn13/covid19/model/entities/CovidUpdateInfo.java @@ -0,0 +1,23 @@ +package com.bvn13.covid19.model.entities; + +import lombok.Data; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; + +@Data +@Entity +@Table(name = "update_info", uniqueConstraints = { + @UniqueConstraint(columnNames = {"createdOn"}) +}) +public class CovidUpdateInfo { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private long id; + + private LocalDateTime createdOn; + private ZonedDateTime datetime; + +} diff --git a/covid19-model/src/main/java/com/bvn13/covid19/model/entities/Region.java b/covid19-model/src/main/java/com/bvn13/covid19/model/entities/Region.java new file mode 100644 index 0000000..2d050ca --- /dev/null +++ b/covid19-model/src/main/java/com/bvn13/covid19/model/entities/Region.java @@ -0,0 +1,20 @@ +package com.bvn13.covid19.model.entities; + +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +@Data +@Entity +@Table(name = "regions", uniqueConstraints = { + @UniqueConstraint(columnNames = "name") +}) +public class Region { + + @Id + private String name; + +} diff --git a/covid19-model/src/main/resources/application.yaml b/covid19-model/src/main/resources/application.yaml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/covid19-model/src/main/resources/application.yaml @@ -0,0 +1 @@ + diff --git a/covid19-model/src/test/java/com/bvn13/covid19/model/Covid19ModelApplicationTests.java b/covid19-model/src/test/java/com/bvn13/covid19/model/Covid19ModelApplicationTests.java new file mode 100644 index 0000000..3e20d34 --- /dev/null +++ b/covid19-model/src/test/java/com/bvn13/covid19/model/Covid19ModelApplicationTests.java @@ -0,0 +1,13 @@ +package com.bvn13.covid19.model; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Covid19ModelApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/covid19-scheduler/.gitignore b/covid19-scheduler/.gitignore new file mode 100644 index 0000000..6c01878 --- /dev/null +++ b/covid19-scheduler/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/covid19-scheduler/build.gradle b/covid19-scheduler/build.gradle new file mode 100644 index 0000000..c621c0d --- /dev/null +++ b/covid19-scheduler/build.gradle @@ -0,0 +1,18 @@ +version = '0.0.1' + +dependencies { + compile(project(':covid19-model')) + + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.apache.camel.springboot:camel-spring-boot-starter:3.1.0' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + implementation 'org.jsoup:jsoup:1.13.1' + + runtimeOnly 'org.postgresql:postgresql' + +} + +springBoot { + mainClassName = 'com.bvn13.covid19.scheduler.Covid19SchedulerApplication' +} \ No newline at end of file diff --git a/covid19-scheduler/gradle/wrapper/gradle-wrapper.jar b/covid19-scheduler/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f3d88b1 Binary files /dev/null and b/covid19-scheduler/gradle/wrapper/gradle-wrapper.jar differ diff --git a/covid19-scheduler/gradle/wrapper/gradle-wrapper.properties b/covid19-scheduler/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a4b4429 --- /dev/null +++ b/covid19-scheduler/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/covid19-scheduler/gradlew b/covid19-scheduler/gradlew new file mode 100755 index 0000000..536f027 --- /dev/null +++ b/covid19-scheduler/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG=$(dirname "$PRG")"/$link" + fi +done +SAVED="$(pwd)" +cd "$(dirname \"$PRG\")/" >/dev/null +APP_HOME="$(pwd -P)" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=$(basename "$0") + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn() { + echo "$*" +} + +die() { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$(uname)" in +CYGWIN*) + cygwin=true + ;; +Darwin*) + darwin=true + ;; +MINGW*) + msys=true + ;; +NONSTOP*) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ]; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then + MAX_FD_LIMIT=$(ulimit -H -n) + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +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 + ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) + SEP="" + for dir in $ROOTDIRSRAW; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ]; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@"; do + CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) + CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition + eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") + else + eval $(echo args$i)="\"$arg\"" + fi + i=$(expr $i + 1) + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save() { + for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/covid19-scheduler/gradlew.bat b/covid19-scheduler/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/covid19-scheduler/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +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% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/Covid19SchedulerApplication.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/Covid19SchedulerApplication.java new file mode 100644 index 0000000..7fd0fe1 --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/Covid19SchedulerApplication.java @@ -0,0 +1,20 @@ +package com.bvn13.covid19.scheduler; + +import com.bvn13.covid19.model.Covid19ModelConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@EnableJpaRepositories("com.bvn13.covid19.scheduler") +@SpringBootApplication +@Import({ + Covid19ModelConfig.class +}) +public class Covid19SchedulerApplication { + + public static void main(String[] args) { + SpringApplication.run(Covid19SchedulerApplication.class, args); + } + +} diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/CovidStatsRepository.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/CovidStatsRepository.java new file mode 100644 index 0000000..e3cda1f --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/CovidStatsRepository.java @@ -0,0 +1,10 @@ +package com.bvn13.covid19.scheduler; + +import com.bvn13.covid19.model.entities.CovidStat; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CovidStatsRepository extends JpaRepository { + +} diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/CovidUpdateInfosRepository.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/CovidUpdateInfosRepository.java new file mode 100644 index 0000000..ab140e3 --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/CovidUpdateInfosRepository.java @@ -0,0 +1,9 @@ +package com.bvn13.covid19.scheduler; + +import com.bvn13.covid19.model.entities.CovidUpdateInfo; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CovidUpdateInfosRepository extends JpaRepository { +} diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/RegionsRepository.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/RegionsRepository.java new file mode 100644 index 0000000..e1f8fb4 --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/RegionsRepository.java @@ -0,0 +1,14 @@ +package com.bvn13.covid19.scheduler; + +import com.bvn13.covid19.model.entities.Region; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface RegionsRepository extends JpaRepository { + + Optional findByName(String name); + +} diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfDataRetriever.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfDataRetriever.java new file mode 100644 index 0000000..88229e9 --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfDataRetriever.java @@ -0,0 +1,59 @@ +package com.bvn13.covid19.scheduler.updater.stopcoronovirusrf; + +import com.bvn13.covid19.scheduler.updater.stopcoronovirusrf.model.RowData; +import org.apache.camel.Exchange; +import org.apache.camel.Handler; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class StopcoronovirusRfDataRetriever { + + //private static final String URL = "https://стопкоронавирус.рф/"; + private static final String HOST = "xn--80aesfpebagmfblc0a.xn--p1ai"; + private static final String URL = "https://xn--80aesfpebagmfblc0a.xn--p1ai/"; + + @Value("${app.user-agent}") + private String userAgent; + + @Handler + public void retrieveData(Exchange exchange) throws Exception { + Document doc = Jsoup.connect(URL) + .userAgent(userAgent) + .timeout(30*1000) + //.referrer("http://google.com") +// .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") +// .header("Accept-Encoding", "gzip, deflate, br") +// .header("Accept-Language", "ru-RU,ru;q=0.5") +// .header("Cache-Control", "no-cache") +// .header("Connection", "keep-alive") +// .header("Pragma", "no-cache") +// .header("Host", HOST) + .get(); + + Elements tableData = doc.select("div.d-map__list > table > tbody > tr"); + + List rows = new ArrayList<>(tableData.size()); + + for (Element row : tableData) { + rows.add(RowData.builder() + .region(row.selectFirst("th").text()) + .sick(row.selectFirst("td > span.d-map__indicator_sick").parent().ownText()) + .healed(row.selectFirst("td > span.d-map__indicator_healed").parent().ownText()) + .died(row.selectFirst("td > span.d-map__indicator_die").parent().ownText()) + .build()); + } + + exchange.getIn().setHeader(StopcoronovirusRfUpdater.HEADER_DATE_OF_DATA, doc.selectFirst(".d-map__title > span").ownText()); + exchange.getIn().setBody(rows); + + } + +} diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfService.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfService.java new file mode 100644 index 0000000..b276ee6 --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfService.java @@ -0,0 +1,133 @@ +package com.bvn13.covid19.scheduler.updater.stopcoronovirusrf; + +import com.bvn13.covid19.model.entities.CovidStat; +import com.bvn13.covid19.model.entities.Region; +import com.bvn13.covid19.model.entities.CovidUpdateInfo; +import com.bvn13.covid19.scheduler.CovidStatsRepository; +import com.bvn13.covid19.scheduler.RegionsRepository; +import com.bvn13.covid19.scheduler.CovidUpdateInfosRepository; +import com.bvn13.covid19.scheduler.updater.stopcoronovirusrf.model.RowData; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.transaction.Transactional; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Collection; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +@RequiredArgsConstructor +public class StopcoronovirusRfService { + + private static final Pattern PATTERN_DATE = Pattern.compile("[а-яА-Я\\s]*(\\d{1,2})\\s([а-яА-Я]+)\\s(\\d{1,2})\\:(\\d{1,2})", Pattern.CASE_INSENSITIVE); + + private final CovidStatsRepository repository; + private final RegionsRepository regionsRepository; + private final CovidUpdateInfosRepository updatesRepository; + + @Value("${app.zone-id}") + private String zoneIdStr; + private ZoneId zoneId; + + @PostConstruct + public void init() { + zoneId = ZoneId.of(zoneIdStr); + } + + @Transactional + public void saveRawData(String date, Collection rawData) { + + ZonedDateTime zdt = parseZonedDateTime(date); + + CovidUpdateInfo updateInfo = new CovidUpdateInfo(); + updateInfo.setCreatedOn(LocalDateTime.now()); + updateInfo.setDatetime(zdt); + updateInfo = updatesRepository.save(updateInfo); + + Iterator iter = rawData.iterator(); + while (iter.hasNext()) { + RowData row = iter.next(); + + Region region = regionsRepository.findByName(row.getRegion()).orElseGet(() -> { + Region r = new Region(); + r.setName(row.getRegion()); + return regionsRepository.save(r); + }); + + CovidStat covidStat = new CovidStat(); + covidStat.setUpdateInfo(updateInfo); + covidStat.setRegion(region); + covidStat.setSick(Long.parseLong(row.getSick())); + covidStat.setHealed(Long.parseLong(row.getHealed())); + covidStat.setDied(Long.parseLong(row.getDied())); + + repository.save(covidStat); + } + + } + + private ZonedDateTime parseZonedDateTime(String date) { + Matcher matcher = PATTERN_DATE.matcher(date); + if (matcher.find()) { + return ZonedDateTime.of( + 2020, + detectMonth(matcher.group(2)), + Integer.parseInt(matcher.group(1)), + Integer.parseInt(matcher.group(3)), + Integer.parseInt(matcher.group(4)), + 0, 0, zoneId + ); + } else { + throw new IllegalArgumentException("Could not parse date: "+date); + } + } + + private int detectMonth(String m) { + switch (m.trim().toLowerCase()) { + case "январь": + case "января": + return 1; + case "февраль": + case "февраля": + return 2; + case "март": + case "марта": + return 3; + case "апрель": + case "апреля": + return 4; + case "май": + case "мая": + return 5; + case "июнь": + case "июня": + return 6; + case "июль": + case "июля": + return 7; + case "август": + case "августа": + return 8; + case "сентябрь": + case "сентября": + return 9; + case "октябрь": + case "октября": + return 10; + case "ноябрь": + case "ноября": + return 11; + case "декабрь": + case "декабря": + return 12; + default: throw new IllegalArgumentException("Could not detect month: "+m); + } + } + +} diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfUpdater.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfUpdater.java new file mode 100644 index 0000000..3114349 --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/StopcoronovirusRfUpdater.java @@ -0,0 +1,51 @@ +package com.bvn13.covid19.scheduler.updater.stopcoronovirusrf; + +import com.bvn13.covid19.scheduler.updater.stopcoronovirusrf.model.RowData; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.camel.*; +import org.apache.camel.builder.RouteBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.Collection; + +@Slf4j +@Component +@RequiredArgsConstructor +public class StopcoronovirusRfUpdater extends RouteBuilder { + + public static final String HEADER_DATE_OF_DATA = "covid19-date-of-data"; + + private final StopcoronovirusRfDataRetriever dataRetriever; + private final StopcoronovirusRfService service; + + @Value("${app.timer.stopcoronovirusrf}") + private int stopcoronovirusrfTimerSecons; + private long stopcoronovirusrfTimerMillis; + + + @PostConstruct + public void init() { + stopcoronovirusrfTimerMillis = stopcoronovirusrfTimerSecons * 1000; + } + + @Override + public void configure() throws Exception { + + from("timer:stopcoronovirusrf?delay=1000&period="+stopcoronovirusrfTimerMillis) + .log(LoggingLevel.DEBUG, log, "Start retrieving data from stopcoronovirus.rf") + .process(dataRetriever::retrieveData) + .bean(this, "saveData") + .log(LoggingLevel.DEBUG, log, "Processed data count: ${body.size}") + ; + + } + + @Handler + public void saveData(@Header(HEADER_DATE_OF_DATA) String dataDate, @Body Collection rawData) { + service.saveRawData(dataDate, rawData); + } + +} diff --git a/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/model/RowData.java b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/model/RowData.java new file mode 100644 index 0000000..772171c --- /dev/null +++ b/covid19-scheduler/src/main/java/com/bvn13/covid19/scheduler/updater/stopcoronovirusrf/model/RowData.java @@ -0,0 +1,15 @@ +package com.bvn13.covid19.scheduler.updater.stopcoronovirusrf.model; + +import lombok.Builder; +import lombok.Value; + +@Builder +@Value +public class RowData { + + String region; + String sick; + String healed; + String died; + +} diff --git a/covid19-scheduler/src/main/resources/application.yaml b/covid19-scheduler/src/main/resources/application.yaml new file mode 100644 index 0000000..dcb6b66 --- /dev/null +++ b/covid19-scheduler/src/main/resources/application.yaml @@ -0,0 +1,49 @@ +app: + user-agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0 + zone-id: Europe/Moscow + timer: + # IN SECONDS + # 1 hour + stopcoronovirusrf: 3600 + +spring: + application: + name: covid19-scheduler + + + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/covid19 + username: + password: + + jpa: + show-sql: true + hibernate: + dialect: org.hibernate.dialect.PostgreSQL + ddl-auto: update + + properties: + hibernate: + use_sql_comments: true + format_sql: true + show_sql: true + jdbc: + lob: + non_contextual_creation: true +logging: + level: + root: info + com: + bvn13: + covid19: debug + org: + springframework: warn + hibernate: + SQL: debug + type: + descriptor: + sql: + BasicBinder: trace + pattern: + console: "%d{dd-MM-yyyy HH:mm:ss.SSS} %highlight(%-5level) %magenta([%thread]) %yellow(%logger.%M) - %msg%n" diff --git a/covid19-scheduler/src/test/java/com/bvn13/covid19/scheduler/Covid19SchedulerApplicationTests.java b/covid19-scheduler/src/test/java/com/bvn13/covid19/scheduler/Covid19SchedulerApplicationTests.java new file mode 100644 index 0000000..bb5abd4 --- /dev/null +++ b/covid19-scheduler/src/test/java/com/bvn13/covid19/scheduler/Covid19SchedulerApplicationTests.java @@ -0,0 +1,13 @@ +package com.bvn13.covid19.scheduler; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Covid19SchedulerApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/covid19.iml b/covid19.iml new file mode 100644 index 0000000..d04fa17 --- /dev/null +++ b/covid19.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/covid19.ipr b/covid19.ipr new file mode 100644 index 0000000..08254dc --- /dev/null +++ b/covid19.ipr @@ -0,0 +1,1655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/covid19 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + " + + + master_key + covid19 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/covid19.iws b/covid19.iws new file mode 100644 index 0000000..cbad7d6 --- /dev/null +++ b/covid19.iws @@ -0,0 +1,513 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + true + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + +