From 50a4392ebcf9f2f8a7eea3104fcc39bd05e252b1 Mon Sep 17 00:00:00 2001 From: "Ole B. Rosentreter" Date: Fri, 27 Sep 2024 20:58:15 +0200 Subject: [PATCH 1/1] Initial project version --- .gitignore | 5 + pom.xml | 284 ++++++++++++++++++ .../calculators/AbstractCalculator.java | 44 +++ .../service/calculators/Calculator.java | 13 + .../calculators/ServletInitializer.java | 13 + .../calculators/run/RunPrediction.java | 125 ++++++++ .../service/calculators/util/Converter.java | 27 ++ .../service/calculators/util/Time.java | 75 +++++ src/main/resources/application.properties | 3 + src/main/resources/log4j.xml | 17 ++ src/main/webapp/index.html | 10 + 11 files changed, 616 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/de/laktatnebel/service/calculators/AbstractCalculator.java create mode 100644 src/main/java/de/laktatnebel/service/calculators/Calculator.java create mode 100644 src/main/java/de/laktatnebel/service/calculators/ServletInitializer.java create mode 100644 src/main/java/de/laktatnebel/service/calculators/run/RunPrediction.java create mode 100644 src/main/java/de/laktatnebel/service/calculators/util/Converter.java create mode 100644 src/main/java/de/laktatnebel/service/calculators/util/Time.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/log4j.xml create mode 100644 src/main/webapp/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9aac7d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.classpath +.project +.settings +bin +target diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cdc902d --- /dev/null +++ b/pom.xml @@ -0,0 +1,284 @@ + + 4.0.0 + de.laktatnebel.service + runningpredictor + 1.0.1-SNAPSHOT + war + + runningpredictor + Pedict Run Times + + + org.springframework.boot + spring-boot-starter-parent + 2.4.3 + + + + + 15 + 2.3.1 + 20230227 + 2.1.0 + + UTF-8 + UTF-8 + + ${java.version} + ${java.version} + + 3.3.0 + + 3.2.1 + 3.11.0 + 3.0.0 + + 3.3.0 + 3.3.2 + 3.3.0 + 3.5.0 + 3.5.0 + + 3.0.0 + 3.1.1 + 3.5.0 + 4.0.0-M6 + + 4.13.2 + + 2.2 + + -Xdoclint:none + + + + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + de.laktatnebel.libs + utillib + ${utillib.version} + + + + org.json + json + ${json.version} + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + + javax.xml.bind + jaxb-api + + + + de.laktatnebel.libs + utillib + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + + + + org.json + json + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + develop + false + true + + + + + + org.apache.maven.plugins + maven-site-plugin + ${maven-site-plugin.version} + + + + site + + + + + maven-javadoc-plugin + + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + ${javadoc.opts} + none + + + + attach-javadocs + + jar + + + ${javadoc.opts} + none + + + + + + org.apache.maven.plugins + maven-site-plugin + ${maven-site-plugin.version} + + + maven-compiler-plugin + + ${java.version} + + + + maven-enforcer-plugin + + + enforce-versions + + enforce + + + + + + + + [3.0,) + + + ${java.version} + + + + + + org.apache.tomcat.maven + tomcat7-maven-plugin + 2.0 + + localtomcat + http://localhost:8080/manager/text + + true + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + scm:git:ssh://laktatnebel.de/srv/git/runningpredictor.git + scm:git:ssh://laktatnebel.de/srv/git/runningpredictor.git + HEAD + + + + + laktatnebel.release + Release Repository + file:///home/oleb/.m2/distribution + + + laktatnebel.snapshots + false + Snapshot Repository + file:///home/oleb/.m2/distribution + + + laktatnebel.site + Sites + file:///home/oleb/.m2/site + + + diff --git a/src/main/java/de/laktatnebel/service/calculators/AbstractCalculator.java b/src/main/java/de/laktatnebel/service/calculators/AbstractCalculator.java new file mode 100644 index 0000000..379bf92 --- /dev/null +++ b/src/main/java/de/laktatnebel/service/calculators/AbstractCalculator.java @@ -0,0 +1,44 @@ +package de.laktatnebel.service.calculators; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractCalculator { + + /** Logger logger field */ + private static Logger logger = LoggerFactory.getLogger(AbstractCalculator.class); + + { + logger = null; + } + + /** + * @param clazz Class + * @return Logger + */ + public static Logger getLogger(Class clazz) { + if (AbstractCalculator.logger == null) { + AbstractCalculator.logger = LoggerFactory.getLogger(clazz); + } + return AbstractCalculator.logger; + } + /** + * + * @param logger Logger + * @param string String + */ + public static void log(Logger logger, String string) { + if (logger.isTraceEnabled()) { + logger.trace(string); + } else if (logger.isDebugEnabled()) { + logger.debug(string); + } else if (logger.isInfoEnabled()) { + logger.info(string); + } else if (logger.isWarnEnabled()) { + logger.warn(string); + } else if (logger.isErrorEnabled()) { + logger.error(string); + } + } + +} diff --git a/src/main/java/de/laktatnebel/service/calculators/Calculator.java b/src/main/java/de/laktatnebel/service/calculators/Calculator.java new file mode 100644 index 0000000..ae3db28 --- /dev/null +++ b/src/main/java/de/laktatnebel/service/calculators/Calculator.java @@ -0,0 +1,13 @@ +package de.laktatnebel.service.calculators; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Calculator { + + public static void main(String[] args) { + SpringApplication.run(Calculator.class, args); + } + +} diff --git a/src/main/java/de/laktatnebel/service/calculators/ServletInitializer.java b/src/main/java/de/laktatnebel/service/calculators/ServletInitializer.java new file mode 100644 index 0000000..39fa12b --- /dev/null +++ b/src/main/java/de/laktatnebel/service/calculators/ServletInitializer.java @@ -0,0 +1,13 @@ +package de.laktatnebel.service.calculators; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(Calculator.class); + } + +} diff --git a/src/main/java/de/laktatnebel/service/calculators/run/RunPrediction.java b/src/main/java/de/laktatnebel/service/calculators/run/RunPrediction.java new file mode 100644 index 0000000..2b46d19 --- /dev/null +++ b/src/main/java/de/laktatnebel/service/calculators/run/RunPrediction.java @@ -0,0 +1,125 @@ +package de.laktatnebel.service.calculators.run; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import de.laktatnebel.service.calculators.AbstractCalculator; +import de.laktatnebel.service.calculators.util.Converter; +import de.laktatnebel.service.calculators.util.Time; + +@RestController +public class RunPrediction extends AbstractCalculator { + private static final double VDOT_PRECISION = 0.000125; + + @SuppressWarnings("unused") + + /** + * Default constructor. + */ + public RunPrediction() { + // TODO Auto-generated constructor stub + } + + @GetMapping(value = "/predict/{dist}/{hour}/{min}/{sec}/{disttopredict}", produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + Map getPace(@PathVariable(name = "dist") Double distance, + @PathVariable(name = "hour") Integer hours, @PathVariable(name = "min") Integer minutes, + @PathVariable(name = "sec") Integer seconds, @PathVariable(name = "disttopredict") Double distancetopredict) { + + Time originTime = new Time(hours, minutes, seconds); + getLogger(RunPrediction.class).debug("originTime\t" + originTime.toString()); + getLogger(RunPrediction.class).debug("originTime\t" + originTime.toFormattedString()); + + JackDaniels originJD = new JackDaniels (originTime, distance); + getLogger(RunPrediction.class).debug("originJD\t" + originJD.toString()); + + String originPace = Converter.getPace(originTime, distance); + + Time estimatedTime = new Time(); + estimatedTime.setTimeMinutes(originTime.getMinutes() * Math.pow(distancetopredict / distance, 1.06)); + getLogger(RunPrediction.class).debug("estimatedTime\t" + estimatedTime.toString()); + getLogger(RunPrediction.class).debug("estimatedTime\t" + estimatedTime.toFormattedString()); + + JackDaniels estimatedJD = new JackDaniels(estimatedTime, distancetopredict); + getLogger(RunPrediction.class).debug("estimatedJD\t" + estimatedJD.toString()); + + String estimatedPace = Converter.getPace(estimatedTime, distancetopredict); + + int i=0; + Time predictedTime = new Time(); + predictedTime.setTimeHours(estimatedTime.getHours()); + JackDaniels predictedJD = new JackDaniels(estimatedTime, distancetopredict); + while (Math.abs(predictedJD.getVdot() / originJD.getVdot() - 1) > VDOT_PRECISION) { + if (estimatedJD.getVdot() > originJD.getVdot()) { + predictedTime.addSeconds(1); + } else { + predictedTime.addSeconds(-1); + } + + getLogger(RunPrediction.class).debug("predictedTime\t" + predictedTime.toString()); + getLogger(RunPrediction.class).debug("predictedTime\t" + predictedTime.toFormattedString()); + + predictedJD = new JackDaniels(predictedTime, distancetopredict); + getLogger(RunPrediction.class).debug("predictedJD\t" + predictedJD.toString()); + + if (++i >= 100 ) break; + } + + String predictedPace = Converter.getPace(predictedTime, distancetopredict); + + Double kmPerHours = Converter.getKmPerHours(distance, originTime); + Double meterPerMin = Converter.getMeterPerMin(distance, originTime); + + Map predictionMap = new HashMap(); + + predictionMap.put("pace_min-km", originPace); + predictionMap.put("speed_km-h", Double.toString(kmPerHours)); + predictionMap.put("meter-per-min", Double.toString(meterPerMin)); + predictionMap.put("vO2", Double.toString(originJD.getVo2())); + predictionMap.put("fvO2", Double.toString(originJD.getFvo2())); + predictionMap.put("vdot", Double.toString(originJD.vdot)); + predictionMap.put("distancetopredict", Double.toString(distancetopredict)); + predictionMap.put("estimatedTime", estimatedTime.toFormattedString()); + predictionMap.put("estimatedPace_min-km", estimatedPace); + predictionMap.put("predictedTime", predictedTime.toFormattedString()); + predictionMap.put("predictedPace_min-km", predictedPace); + + return predictionMap; + + } + + private class JackDaniels { + private Double vo2 = 0.0; + private Double fvo2 = 0.0; + private Double vdot = 0.0; + + public JackDaniels(Time originTime, Double distance) { + this.vo2 = 0.000104 * Math.pow(Converter.getMeterPerMin(distance, originTime), 2) + 0.182258 * Converter.getMeterPerMin(distance, originTime) - 4.60; + this.fvo2 = 0.80 + 0.298956 * Math.pow(Math.E, (-0.193261*originTime.getMinutes())) + 0.189438 * Math.pow(Math.E, (-0.012778*originTime.getMinutes())); + this.vdot = getVo2() / getFvo2(); + } + + public Double getVo2() { + return vo2; + } + + public Double getFvo2() { + return fvo2; + } + + public Double getVdot() { + return vdot; + } + + @Override + public String toString() { + return "JackDaniels [vo2=" + vo2 + ", fvo2=" + fvo2 + ", vdot=" + vdot + "]"; + } + } +} diff --git a/src/main/java/de/laktatnebel/service/calculators/util/Converter.java b/src/main/java/de/laktatnebel/service/calculators/util/Converter.java new file mode 100644 index 0000000..dba64c4 --- /dev/null +++ b/src/main/java/de/laktatnebel/service/calculators/util/Converter.java @@ -0,0 +1,27 @@ +package de.laktatnebel.service.calculators.util; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class Converter { + + public static String getPace(Time time, Double distance) { + Date dateFromTimeInSeconds = new Date(Math.round(60L * 60L * 1000L * time.getHours() / distance)); + + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss,SS"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + + return sdf.format(dateFromTimeInSeconds); + } + + public static Double getKmPerHours(Double distance, Time time) { + return distance / time.getHours(); + } + + public static Double getMeterPerMin(Double distance, Time time) { + return distance * 1000 / time.getMinutes(); + } + + +} diff --git a/src/main/java/de/laktatnebel/service/calculators/util/Time.java b/src/main/java/de/laktatnebel/service/calculators/util/Time.java new file mode 100644 index 0000000..1f77739 --- /dev/null +++ b/src/main/java/de/laktatnebel/service/calculators/util/Time.java @@ -0,0 +1,75 @@ +package de.laktatnebel.service.calculators.util; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class Time { + + private Double seconds = 0.0; + private Double minutes = 0.0; + private Double hours = 0.0; + + public Time() { + super(); + } + + public Time(Integer hours, Integer minutes, Integer seconds) { + super(); + setTime(hours, minutes, seconds); + } + + public void addSeconds(int secondsToAdd) { + setTimeSeconds(this.seconds + (double) secondsToAdd/10); + } + + public String toFormattedString() { + Date dateFromTimeInSeconds = new Date(Math.round(1000L * seconds)); + + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss,SS"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + + return sdf.format(dateFromTimeInSeconds); + } + + public Double getSeconds() { + return seconds; + } + + public Double getMinutes() { + return minutes; + } + + public Double getHours() { + return hours; + } + + public void setTimeSeconds(Double seconds) { + this.seconds = seconds; + this.minutes = seconds / 60; + this.hours = seconds / 3600; + } + + public void setTimeMinutes(Double minutes) { + this.seconds = minutes * 60; + this.minutes = minutes; + this.hours = minutes / 60; + } + + public void setTimeHours(Double hours) { + this.seconds = hours * 3600; + this.minutes = hours * 60; + this.hours = hours; + } + + public void setTime(Integer hours, Integer minutes, Integer seconds) { + this.seconds = (double) hours * 3600 + (double) minutes * 60 + (double) seconds; + this.minutes = (double) hours * 60 + (double) minutes + (double) seconds / 60; + this.hours = (double) hours + (double) minutes / 60 + (double) seconds / 3600; + } + + @Override + public String toString() { + return "Time [seconds=" + seconds + ", minutes=" + minutes + ", hours=" + hours + "]"; + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..dc7c72e --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,3 @@ +logging.level.root=TRACE +logging.level.org.springframework.web=DEBUG +logging.level.org.hibernate=ERROR diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml new file mode 100644 index 0000000..f04bbed --- /dev/null +++ b/src/main/resources/log4j.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 0000000..adf7f11 --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,10 @@ + + + + +Insert title here + + +something ... + + \ No newline at end of file -- 2.39.5