1-method-choosing #2

Merged
romanov73 merged 6 commits from 1-method-choosing into master 2022-04-18 13:11:46 +04:00
59 changed files with 374 additions and 404 deletions

View File

@ -1,14 +1,7 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
plugins { plugins {
id 'java' id 'java'
id 'io.spring.dependency-management' version '1.0.9.RELEASE' id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'org.springframework.boot' version '2.3.3.RELEASE' id 'org.springframework.boot' version '2.6.4'
id "org.sonarqube" version "2.7"
} }
jar { jar {
@ -16,26 +9,29 @@ jar {
} }
repositories { repositories {
mavenLocal() maven {
mavenCentral() url "https://repo.athene.tech/repository/maven-central/"
}
maven {
url "https://repo.athene.tech/repository/maven-releases/"
}
} }
sourceCompatibility = 11 sourceCompatibility = '11'
targetCompatibility = 11 targetCompatibility = '11'
dependencies { dependencies {
ext { ext {
versionSLF4J = '1.7.24' versionSLF4J = '1.7.24'
versionJetty = '9.3.16.v20170120' versionJetty = '9.3.16.v20170120'
versionJackson = '2.9.4' versionJackson = '2.9.4'
versionSwagger = '2.5.0'
} }
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
implementation group: 'org.slf4j', name: 'slf4j-api', version: versionSLF4J implementation group: 'org.slf4j', name: 'slf4j-api', version: versionSLF4J
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect' implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.1.0'
implementation group: 'org.javassist', name: 'javassist', version: '3.25.0-GA' implementation group: 'org.javassist', name: 'javassist', version: '3.25.0-GA'
implementation group: 'org.eclipse.jetty', name: 'jetty-servlet', version: versionJetty implementation group: 'org.eclipse.jetty', name: 'jetty-servlet', version: versionJetty
@ -43,8 +39,7 @@ dependencies {
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: versionJackson implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: versionJackson
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: versionJackson implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: versionJackson
implementation group: 'io.springfox', name: 'springfox-swagger2', version: versionSwagger implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: versionSwagger
implementation group: 'org.webjars', name: 'jquery', version: '3.6.0' implementation group: 'org.webjars', name: 'jquery', version: '3.6.0'
implementation group: 'org.webjars', name: 'bootstrap', version: '4.6.0' implementation group: 'org.webjars', name: 'bootstrap', version: '4.6.0'

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu; package ru.ulstu;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu; package ru.ulstu;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.configuration; package ru.ulstu.configuration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@ -1,24 +1,21 @@
package ru.ulstu.configuration; package ru.ulstu.configuration;
import com.google.common.base.Predicates; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration @Configuration
@EnableSwagger2
public class SwaggerConfiguration { public class SwaggerConfiguration {
private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class);
@Bean @Bean
public Docket swaggerApi() { public GroupedOpenApi swaggerApi() {
return new Docket(DocumentationType.SWAGGER_2) log.info("Creating Swagger API configuration bean");
.select() return GroupedOpenApi.builder()
.apis(RequestHandlerSelectors.any()) .group("timetable-api")
.paths(Predicates.not(PathSelectors.regex("/error"))) .pathsToMatch(ApiConfiguration.API_1_0 + "**")
.build(); .build();
} }
} }

View File

@ -1,12 +1,6 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.configuration; package ru.ulstu.configuration;
import nz.net.ultraq.thymeleaf.LayoutDialect; import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.controller; package ru.ulstu.controller;
import org.slf4j.Logger; import org.slf4j.Logger;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.controller; package ru.ulstu.controller;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;

View File

@ -1,11 +1,6 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.controller; package ru.ulstu.controller;
import io.swagger.v3.oas.annotations.Hidden;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -17,7 +12,6 @@ import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.datamodel.ts.TimeSeriesValue; import ru.ulstu.datamodel.ts.TimeSeriesValue;
import ru.ulstu.db.DbService; import ru.ulstu.db.DbService;
import ru.ulstu.service.TimeSeriesService; import ru.ulstu.service.TimeSeriesService;
import springfox.documentation.annotations.ApiIgnore;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -29,7 +23,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@Controller @Controller
@ApiIgnore @Hidden
public class IndexController { public class IndexController {
private final TimeSeriesService timeSeriesService; private final TimeSeriesService timeSeriesService;
private final DbService dbService; private final DbService dbService;
@ -56,15 +50,45 @@ public class IndexController {
if (chartForm.getTimeSeriesMeta() != null if (chartForm.getTimeSeriesMeta() != null
&& chartForm.getTimeSeriesMeta().getKey() != null && chartForm.getTimeSeriesMeta().getKey() != null
&& !chartForm.getTimeSeriesMeta().getKey().isEmpty()) { && !chartForm.getTimeSeriesMeta().getKey().isEmpty()) {
addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()), model); addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()), null, model);
} }
return "index"; return "index";
} }
private void addChartToModel(TimeSeries timeSeries, Model model) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException { @GetMapping("/method")
public String method(Model model) throws IOException {
model.addAttribute("sets", dbService.getSets());
model.addAttribute("methods", timeSeriesService.getAvailableMethods());
model.addAttribute("chartForm", new ChartForm());
return "method";
}
@GetMapping("chartMethod")
public String chartMethod(@ModelAttribute ChartForm chartForm, Model model) throws IOException, ModelingException, ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
model.addAttribute("sets", dbService.getSets());
model.addAttribute("methods", timeSeriesService.getAvailableMethods());
if (chartForm.getSet() != null && !chartForm.getSet().getKey().equals("")) {
model.addAttribute("listTimeSeries", dbService.getTimeSeriesMeta(chartForm.getSet()));
}
if (chartForm.getTimeSeriesMeta() != null
&& chartForm.getTimeSeriesMeta().getKey() != null
&& !chartForm.getTimeSeriesMeta().getKey().isEmpty()
&& chartForm.getMethodClassName() != null
&& !chartForm.getMethodClassName().equals("")) {
addChartToModel(dbService.getTimeSeries(chartForm.getSet(), chartForm.getTimeSeriesMeta().getKey()), chartForm.getMethodClassName(), model);
}
return "method";
}
private void addChartToModel(TimeSeries timeSeries, String method, Model model) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
int countForecastPoints = timeSeries.getLength() > 20 ? 10 : timeSeries.getLength() / 3; int countForecastPoints = timeSeries.getLength() > 20 ? 10 : timeSeries.getLength() / 3;
TimeSeries timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries); TimeSeries timeSeriesModel = timeSeriesService.smoothTimeSeries(timeSeries).getTimeSeries();
ModelingResult modelingResult = timeSeriesService.getForecast(timeSeries, countForecastPoints); ModelingResult modelingResult;
if (method == null) {
modelingResult = timeSeriesService.getForecast(timeSeries, countForecastPoints);
} else {
modelingResult = timeSeriesService.getForecast(timeSeries, method, countForecastPoints);
}
TimeSeries forecast = modelingResult.getTimeSeries(); TimeSeries forecast = modelingResult.getTimeSeries();
TimeSeries testForecast = modelingResult.getTestForecast(); TimeSeries testForecast = modelingResult.getTestForecast();
model.addAttribute("dates", getDatesForChart(timeSeries, forecast)); model.addAttribute("dates", getDatesForChart(timeSeries, forecast));

View File

@ -1,16 +1,11 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.controller; package ru.ulstu.controller;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -21,11 +16,13 @@ import ru.ulstu.datamodel.ForecastParams;
import ru.ulstu.datamodel.ModelingResult; import ru.ulstu.datamodel.ModelingResult;
import ru.ulstu.datamodel.exception.ModelingException; import ru.ulstu.datamodel.exception.ModelingException;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.service.MethodParamBruteForce; import ru.ulstu.method.Method;
import ru.ulstu.service.TimeSeriesService; import ru.ulstu.service.TimeSeriesService;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@RestController @RestController
@ -34,17 +31,14 @@ public class TimeSeriesController {
private final static Logger LOGGER = LoggerFactory.getLogger(TimeSeriesController.class); private final static Logger LOGGER = LoggerFactory.getLogger(TimeSeriesController.class);
private final TimeSeriesService timeSeriesService; private final TimeSeriesService timeSeriesService;
private final MethodParamBruteForce methodParamBruteForce;
public TimeSeriesController(TimeSeriesService timeSeriesService, public TimeSeriesController(TimeSeriesService timeSeriesService) {
MethodParamBruteForce methodParamBruteForce) {
this.timeSeriesService = timeSeriesService; this.timeSeriesService = timeSeriesService;
this.methodParamBruteForce = methodParamBruteForce;
} }
@PostMapping("getForecast") @PostMapping("getForecast")
@ApiOperation("Получить прогноз временного ряда") @Operation(description = "Получить прогноз временного ряда любым методом")
public ResponseEntity<ModelingResult> getForecastTimeSeries(@RequestBody ForecastParams forecastParams, HttpServletRequest request) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException { public ResponseEntity<ModelingResult> getForecastTimeSeries(@RequestBody @Valid ForecastParams forecastParams, HttpServletRequest request) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
LOGGER.info("User ip: " + HttpUtils.getUserIp(request)); LOGGER.info("User ip: " + HttpUtils.getUserIp(request));
LOGGER.info("Forecast: " + forecastParams); LOGGER.info("Forecast: " + forecastParams);
ResponseEntity<ModelingResult> result = new ResponseEntity<>(timeSeriesService.getForecast(forecastParams.getOriginalTimeSeries(), ResponseEntity<ModelingResult> result = new ResponseEntity<>(timeSeriesService.getForecast(forecastParams.getOriginalTimeSeries(),
@ -54,12 +48,30 @@ public class TimeSeriesController {
} }
@PostMapping("getSmoothed") @PostMapping("getSmoothed")
@ApiOperation("Получить сглаженный временной ряд") @Operation(description = "Получить сглаженный временной ряд любым методом")
public ResponseEntity<ModelingResult> getSmoothedTimeSeries(@RequestBody TimeSeries timeSeries, HttpServletRequest request) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { public ResponseEntity<ModelingResult> getSmoothedTimeSeries(@RequestBody TimeSeries timeSeries, HttpServletRequest request) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
LOGGER.info("User ip: " + HttpUtils.getUserIp(request)); LOGGER.info("User ip: " + HttpUtils.getUserIp(request));
LOGGER.info("Time series for smoothing: " + timeSeries); LOGGER.info("Time series for smoothing: " + timeSeries);
ResponseEntity<ModelingResult> result = new ResponseEntity<>(methodParamBruteForce.getSmoothedTimeSeries(timeSeries), HttpStatus.OK); ResponseEntity<ModelingResult> result = new ResponseEntity<>(timeSeriesService.smoothTimeSeries(timeSeries), HttpStatus.OK);
LOGGER.info("Smoothing complete"); LOGGER.info("Smoothing complete");
return result; return result;
} }
@PostMapping("getSpecificMethodForecast")
@Operation(description = "Получить прогноз временного ряда указанным методом")
public ResponseEntity<ModelingResult> getForecastTimeSeriesSpecificMethod(@RequestBody @Valid ForecastParams forecastParams, HttpServletRequest request) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
LOGGER.info("User ip: " + HttpUtils.getUserIp(request));
LOGGER.info("Forecast: " + forecastParams);
ResponseEntity<ModelingResult> result = new ResponseEntity<>(timeSeriesService.getForecast(forecastParams.getOriginalTimeSeries(),
forecastParams.getMethodClassName(),
forecastParams.getCountForecast()), HttpStatus.OK);
LOGGER.info("Forecast result complete");
return result;
}
@GetMapping("availableMethods")
@Operation(description = "Получить список доступных методов моделирования")
public ResponseEntity<List<Method>> getAvailableMethods() {
return new ResponseEntity<>(timeSeriesService.getAvailableMethods(), HttpStatus.OK);
}
} }

View File

@ -1,12 +1,6 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.controller; package ru.ulstu.controller;
import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -30,25 +24,25 @@ public class UtilController {
} }
@GetMapping("isAlive") @GetMapping("isAlive")
@ApiOperation("Проверка сервиса") @Operation(description = "Проверка сервиса")
public ResponseEntity<Boolean> isAlive() { public ResponseEntity<Boolean> isAlive() {
return new ResponseEntity<>(true, HttpStatus.OK); return new ResponseEntity<>(true, HttpStatus.OK);
} }
@GetMapping("getRandom") @GetMapping("getRandom")
@ApiOperation("Получить временной ряд рандомной длины") @Operation(description = "Получить временной ряд рандомной длины")
public ResponseEntity<TimeSeries> getRandomTimeSeries(@RequestParam("length") int length) { public ResponseEntity<TimeSeries> getRandomTimeSeries(@RequestParam("length") int length) {
return new ResponseEntity<>(utilService.getRandomTimeSeries(length), HttpStatus.OK); return new ResponseEntity<>(utilService.getRandomTimeSeries(length), HttpStatus.OK);
} }
@GetMapping("getFromString") @GetMapping("getFromString")
@ApiOperation("Преобразовать строку с разделителями во временной ряд") @Operation(description = "Преобразовать строку с разделителями во временной ряд")
public ResponseEntity<TimeSeries> getTimeSeriesFromString(@RequestParam("tsString") String tsString) { public ResponseEntity<TimeSeries> getTimeSeriesFromString(@RequestParam("tsString") String tsString) {
return new ResponseEntity<>(utilService.getTimeSeriesFromString(tsString), HttpStatus.OK); return new ResponseEntity<>(utilService.getTimeSeriesFromString(tsString), HttpStatus.OK);
} }
@PostMapping("timeSeriesToString") @PostMapping("timeSeriesToString")
@ApiOperation("Преобразовать временной ряд в строку с разделителями") @Operation(description = "Преобразовать временной ряд в строку с разделителями")
public ResponseEntity<String> getTimeSeriesToString(@RequestBody TimeSeries timeSeries) { public ResponseEntity<String> getTimeSeriesToString(@RequestBody TimeSeries timeSeries) {
return new ResponseEntity<>(utilService.getTimeSeriesToString(timeSeries), HttpStatus.OK); return new ResponseEntity<>(utilService.getTimeSeriesToString(timeSeries), HttpStatus.OK);
} }

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel; package ru.ulstu.datamodel;
import ru.ulstu.db.model.TimeSeriesMeta; import ru.ulstu.db.model.TimeSeriesMeta;
@ -12,6 +6,7 @@ import ru.ulstu.db.model.TimeSeriesSet;
public class ChartForm { public class ChartForm {
private TimeSeriesSet set; private TimeSeriesSet set;
private TimeSeriesMeta timeSeriesMeta; private TimeSeriesMeta timeSeriesMeta;
private String methodClassName = null;
public TimeSeriesSet getSet() { public TimeSeriesSet getSet() {
return set; return set;
@ -28,4 +23,12 @@ public class ChartForm {
public void setTimeSeriesMeta(TimeSeriesMeta timeSeriesMeta) { public void setTimeSeriesMeta(TimeSeriesMeta timeSeriesMeta) {
this.timeSeriesMeta = timeSeriesMeta; this.timeSeriesMeta = timeSeriesMeta;
} }
public String getMethodClassName() {
return methodClassName;
}
public void setMethodClassName(String methodClassName) {
this.methodClassName = methodClassName;
}
} }

View File

@ -1,16 +1,15 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel; package ru.ulstu.datamodel;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;
import javax.validation.constraints.NotNull;
public class ForecastParams { public class ForecastParams {
@NotNull
private TimeSeries originalTimeSeries; private TimeSeries originalTimeSeries;
@NotNull
private int countForecast; private int countForecast;
private String methodClassName;
public TimeSeries getOriginalTimeSeries() { public TimeSeries getOriginalTimeSeries() {
return originalTimeSeries; return originalTimeSeries;
@ -28,6 +27,14 @@ public class ForecastParams {
this.countForecast = countForecast; this.countForecast = countForecast;
} }
public String getMethodClassName() {
return methodClassName;
}
public void setMethodClassName(String methodClassName) {
this.methodClassName = methodClassName;
}
@Override @Override
public String toString() { public String toString() {
return "ForecastParams{" + return "ForecastParams{" +

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel; package ru.ulstu.datamodel;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel; package ru.ulstu.datamodel;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel; package ru.ulstu.datamodel;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.exception; package ru.ulstu.datamodel.exception;
public class ForecastValidateException extends ModelingException { public class ForecastValidateException extends ModelingException {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.exception; package ru.ulstu.datamodel.exception;
public class ModelingException extends Exception { public class ModelingException extends Exception {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.exception; package ru.ulstu.datamodel.exception;
public class TimeSeriesValidateException extends ModelingException { public class TimeSeriesValidateException extends ModelingException {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.response; package ru.ulstu.datamodel.response;
class ControllerResponse<D, E> { class ControllerResponse<D, E> {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.response; package ru.ulstu.datamodel.response;
class ControllerResponseError<D> { class ControllerResponseError<D> {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.response; package ru.ulstu.datamodel.response;
public enum ErrorConstants { public enum ErrorConstants {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.response; package ru.ulstu.datamodel.response;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.response; package ru.ulstu.datamodel.response;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.ts; package ru.ulstu.datamodel.ts;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.datamodel.ts; package ru.ulstu.datamodel.ts;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.db; package ru.ulstu.db;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.db; package ru.ulstu.db;
import ru.ulstu.datamodel.exception.ModelingException; import ru.ulstu.datamodel.exception.ModelingException;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.db.model; package ru.ulstu.db.model;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;

View File

@ -1,15 +1,9 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.db.model; package ru.ulstu.db.model;
import java.io.File; import java.io.File;
public class TimeSeriesSet { public class TimeSeriesSet {
private String key; private final String key;
public TimeSeriesSet(File dir) { public TimeSeriesSet(File dir) {
this.key = dir.getName(); this.key = dir.getName();

View File

@ -1,13 +1,6 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method; package ru.ulstu.method;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import ru.ulstu.TimeSeriesUtils; import ru.ulstu.TimeSeriesUtils;
import ru.ulstu.datamodel.Model; import ru.ulstu.datamodel.Model;
import ru.ulstu.datamodel.exception.ForecastValidateException; import ru.ulstu.datamodel.exception.ForecastValidateException;
@ -20,7 +13,7 @@ import java.time.temporal.ChronoUnit;
import java.util.List; import java.util.List;
/** /**
* Наиболее общая логика моделировании и прогнозирования временных рядов * Наиболее общая логика моделирования и прогнозирования временных рядов
*/ */
public abstract class Method { public abstract class Method {
@JsonIgnore @JsonIgnore
@ -93,7 +86,9 @@ public abstract class Method {
Model model = getModel(timeSeries, parameters); Model model = getModel(timeSeries, parameters);
TimeSeries forecast = generateEmptyForecastPoints(model.getTimeSeriesModel(), countPoints); TimeSeries forecast = generateEmptyForecastPoints(model.getTimeSeriesModel(), countPoints);
forecast.getFirstValue().setValue(model.getTimeSeriesModel().getLastValue().getValue()); forecast.getFirstValue().setValue(model.getTimeSeriesModel().getLastValue().getValue());
return getForecastWithValidParams(model, forecast); forecast = getForecastWithValidParams(model, forecast);
forecast.getFirstValue().setValue(timeSeries.getLastValue().getValue());
return forecast;
} }
protected TimeSeries generateEmptyForecastPoints(TimeSeries model, int countPointForecast) { protected TimeSeries generateEmptyForecastPoints(TimeSeries model, int countPointForecast) {
@ -117,8 +112,13 @@ public abstract class Method {
} }
@Override @Override
@JsonProperty("name")
public String toString() { public String toString() {
return getName();
}
public String getKey() {
return getClass().getSimpleName(); return getClass().getSimpleName();
} }
public abstract String getName();
} }

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method; package ru.ulstu.method;
public class MethodParamValue { public class MethodParamValue {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method; package ru.ulstu.method;
import java.util.List; import java.util.List;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.addtrendaddseason; package ru.ulstu.method.exponential.addtrendaddseason;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -92,4 +86,9 @@ public class AddTrendAddSeason extends Method {
public List<MethodParameter> getAvailableParameters() { public List<MethodParameter> getAvailableParameters() {
return AddTrendAddSeasonModel.getAvailableParameters(); return AddTrendAddSeasonModel.getAvailableParameters();
} }
@Override
public String getName() {
return "Экспоненциальный метод с аддитивным трендом и аддитивной сезонностью (метод Хольта-Уинтерса)";
}
} }

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.addtrendaddseason; package ru.ulstu.method.exponential.addtrendaddseason;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.addtrendnoseason; package ru.ulstu.method.exponential.addtrendnoseason;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -58,4 +52,9 @@ public class AddTrendNoSeason extends Method {
public List<MethodParameter> getAvailableParameters() { public List<MethodParameter> getAvailableParameters() {
return AddTrendNoSeasonModel.getAvailableParameters(); return AddTrendNoSeasonModel.getAvailableParameters();
} }
@Override
public String getName() {
return "Экспоненциальный метод с аддитивным трендом без сезонной компоненты (метод Хольта)";
}
} }

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.addtrendnoseason; package ru.ulstu.method.exponential.addtrendnoseason;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.notrendnoseason; package ru.ulstu.method.exponential.notrendnoseason;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -48,4 +42,9 @@ public class NoTrendNoSeason extends Method {
public List<MethodParameter> getAvailableParameters() { public List<MethodParameter> getAvailableParameters() {
return NoTrendNoSeasonModel.getAvailableParameters(); return NoTrendNoSeasonModel.getAvailableParameters();
} }
@Override
public String getName() {
return "Экспоненциальный метод без трендовой и сезонной компоненты";
}
} }

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.notrendnoseason; package ru.ulstu.method.exponential.notrendnoseason;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.parameter; package ru.ulstu.method.exponential.parameter;
public class Alpha extends ExponentialMethodParameter { public class Alpha extends ExponentialMethodParameter {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.parameter; package ru.ulstu.method.exponential.parameter;
public class Beta extends ExponentialMethodParameter { public class Beta extends ExponentialMethodParameter {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.parameter; package ru.ulstu.method.exponential.parameter;
public class ExponentialMethodParamValue<T extends ExponentialMethodParameter> { public class ExponentialMethodParamValue<T extends ExponentialMethodParameter> {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.parameter; package ru.ulstu.method.exponential.parameter;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.parameter; package ru.ulstu.method.exponential.parameter;
public class Gamma extends ExponentialMethodParameter { public class Gamma extends ExponentialMethodParameter {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.method.exponential.parameter; package ru.ulstu.method.exponential.parameter;
public class Season extends ExponentialMethodParameter { public class Season extends ExponentialMethodParameter {

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.score; package ru.ulstu.score;
import ru.ulstu.datamodel.Score; import ru.ulstu.datamodel.Score;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.score; package ru.ulstu.score;
import ru.ulstu.datamodel.exception.ModelingException; import ru.ulstu.datamodel.exception.ModelingException;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.service; package ru.ulstu.service;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -33,7 +27,7 @@ import java.util.concurrent.Future;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
public class MethodParamBruteForce { class MethodParamBruteForce {
private final int DEFAULT_THREAD_COUNT = 50; private final int DEFAULT_THREAD_COUNT = 50;
private final List<Method> methods; private final List<Method> methods;
private final ScoreMethod scoreMethod = new Smape(); private final ScoreMethod scoreMethod = new Smape();
@ -43,9 +37,9 @@ public class MethodParamBruteForce {
this.methods = methods; this.methods = methods;
} }
public ModelingResult getForecast(TimeSeries timeSeries, int countPointsForecast) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ModelingException { private ModelingResult getForecastByMethods(TimeSeries timeSeries, List<Method> methods, int countPointsForecast) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ModelingException {
List<Future<ModelingResult>> results = new ArrayList<>(); List<Future<ModelingResult>> futureModelingResults = new ArrayList<>();
List<ModelingResult> results2 = new CopyOnWriteArrayList<>(); List<ModelingResult> modelingResults = new CopyOnWriteArrayList<>();
final int countPoints = (countPointsForecast > timeSeries.getLength()) ? timeSeries.getLength() / 3 : countPointsForecast; final int countPoints = (countPointsForecast > timeSeries.getLength()) ? timeSeries.getLength() / 3 : countPointsForecast;
TimeSeries reducedTimeSeries = new TimeSeries(timeSeries.getValues().stream().limit(timeSeries.getLength() - countPoints).collect(Collectors.toList()), TimeSeries reducedTimeSeries = new TimeSeries(timeSeries.getValues().stream().limit(timeSeries.getLength() - countPoints).collect(Collectors.toList()),
"test part of " + timeSeries.getKey()); "test part of " + timeSeries.getKey());
@ -58,7 +52,7 @@ public class MethodParamBruteForce {
for (List<MethodParamValue> parametersValues : availableParametersValues) { for (List<MethodParamValue> parametersValues : availableParametersValues) {
Method methodInstance = method.getClass().getDeclaredConstructor().newInstance(); Method methodInstance = method.getClass().getDeclaredConstructor().newInstance();
if (methodInstance.canMakeForecast(reducedTimeSeries, parametersValues, countPoints)) { if (methodInstance.canMakeForecast(reducedTimeSeries, parametersValues, countPoints)) {
results.add(executors.submit(() -> { futureModelingResults.add(executors.submit(() -> {
TimeSeries forecast = syncDates(methodInstance.getForecast(reducedTimeSeries, parametersValues, countPoints), timeSeries); TimeSeries forecast = syncDates(methodInstance.getForecast(reducedTimeSeries, parametersValues, countPoints), timeSeries);
return new ModelingResult(forecast, null, return new ModelingResult(forecast, null,
parametersValues, parametersValues,
@ -68,17 +62,39 @@ public class MethodParamBruteForce {
} }
} }
} }
for (Future<ModelingResult> futureModelingResult : results) { for (Future<ModelingResult> futureModelingResult : futureModelingResults) {
results2.add(futureModelingResult.get()); modelingResults.add(futureModelingResult.get());
} }
ModelingResult bestResult = results2.stream()
return getBestResultForecast(modelingResults, timeSeries, countPoints);
}
public ModelingResult getForecast(TimeSeries timeSeries, String methodClassName, int countPointsForecast) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ModelingException {
Method method = methods.stream()
.filter(m -> m.getClass().getSimpleName().equals(methodClassName))
.findAny()
.orElseThrow(() -> new ModelingException("Неизвестный метод прогнозирования"));
return getForecastByMethods(timeSeries, List.of(method), countPointsForecast);
}
public ModelingResult getForecast(TimeSeries timeSeries, Method method, int countPointsForecast) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ModelingException {
return getForecastByMethods(timeSeries, List.of(method), countPointsForecast);
}
public ModelingResult getForecast(TimeSeries timeSeries, int countPointsForecast) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ModelingException {
return getForecastByMethods(timeSeries, methods, countPointsForecast);
}
private ModelingResult getBestResultForecast(List<ModelingResult> modelingResults,
TimeSeries timeSeries,
int countPoints) throws ModelingException {
ModelingResult bestResult = modelingResults.stream()
.min(Comparator.comparing(modelingResult -> modelingResult.getScore().getDoubleValue())) .min(Comparator.comparing(modelingResult -> modelingResult.getScore().getDoubleValue()))
.orElseThrow(() -> new ModelingException("Лучший метод не найден")); .orElseThrow(() -> new ModelingException("Лучший метод не найден"));
TimeSeries forecast = bestResult.getTimeSeriesMethod().getForecast(timeSeries, TimeSeries forecast = bestResult.getTimeSeriesMethod().getForecast(timeSeries,
bestResult.getParamValues(), bestResult.getParamValues(),
countPoints); countPoints);
forecast.getValue(0).setValue(timeSeries.getNumericValue(timeSeries.getLength() - 1));
return new ModelingResult(forecast, return new ModelingResult(forecast,
bestResult.getTimeSeries(), bestResult.getTimeSeries(),
@ -96,9 +112,12 @@ public class MethodParamBruteForce {
return forecast; return forecast;
} }
/*
TODO:
public TimeSeries getForecastWithOptimalLength(TimeSeries timeSeries) { public TimeSeries getForecastWithOptimalLength(TimeSeries timeSeries) {
throw new RuntimeException("Not implemented"); throw new RuntimeException("Not implemented");
} }
*/
public ModelingResult getSmoothedTimeSeries(TimeSeries timeSeries) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { public ModelingResult getSmoothedTimeSeries(TimeSeries timeSeries) throws ExecutionException, InterruptedException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
List<Future<ModelingResult>> results = new ArrayList<>(); List<Future<ModelingResult>> results = new ArrayList<>();
@ -139,7 +158,7 @@ public class MethodParamBruteForce {
parameterOffset.put(methodParameter, 0); parameterOffset.put(methodParameter, 0);
parameterValues.put(methodParameter, methodParameter.getAvailableValues()); parameterValues.put(methodParameter, methodParameter.getAvailableValues());
} }
while (!isAllValuesUsed(parameterOffset, parameterValues)) { while (isNotAllParameterValuesUsed(parameterOffset, parameterValues)) {
List<MethodParamValue> resultRow = new ArrayList<>(); List<MethodParamValue> resultRow = new ArrayList<>();
for (MethodParameter methodParameter : parameterOffset.keySet()) { for (MethodParameter methodParameter : parameterOffset.keySet()) {
resultRow.add(new MethodParamValue(methodParameter, resultRow.add(new MethodParamValue(methodParameter,
@ -155,7 +174,7 @@ public class MethodParamBruteForce {
Map<MethodParameter, List<Number>> parameterValues) { Map<MethodParameter, List<Number>> parameterValues) {
List<MethodParameter> parameters = new ArrayList<>(parameterOffset.keySet()); List<MethodParameter> parameters = new ArrayList<>(parameterOffset.keySet());
int i = 0; int i = 0;
while (i < parameters.size() && !isAllValuesUsed(parameterOffset, parameterValues)) { while (i < parameters.size() && isNotAllParameterValuesUsed(parameterOffset, parameterValues)) {
if (parameterOffset.get(parameters.get(i)) == parameterValues.get(parameters.get(i)).size() - 1) { if (parameterOffset.get(parameters.get(i)) == parameterValues.get(parameters.get(i)).size() - 1) {
parameterOffset.put(parameters.get(i), 0); parameterOffset.put(parameters.get(i), 0);
i++; i++;
@ -169,13 +188,17 @@ public class MethodParamBruteForce {
} }
} }
private boolean isAllValuesUsed(Map<MethodParameter, Integer> parameterOffset, private boolean isNotAllParameterValuesUsed(Map<MethodParameter, Integer> parameterOffset,
Map<MethodParameter, List<Number>> parameterValues) { Map<MethodParameter, List<Number>> parameterValues) {
for (MethodParameter methodParameter : parameterOffset.keySet()) { for (MethodParameter methodParameter : parameterOffset.keySet()) {
if (parameterOffset.get(methodParameter) != parameterValues.get(methodParameter).size() - 1) { if (parameterOffset.get(methodParameter) != parameterValues.get(methodParameter).size() - 1) {
return false; return true;
} }
} }
return true; return false;
}
public List<Method> getAvailableMethods() {
return methods;
} }
} }

View File

@ -1,33 +1,41 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.service; package ru.ulstu.service;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.ulstu.datamodel.ModelingResult; import ru.ulstu.datamodel.ModelingResult;
import ru.ulstu.datamodel.exception.ModelingException; import ru.ulstu.datamodel.exception.ModelingException;
import ru.ulstu.datamodel.ts.TimeSeries; import ru.ulstu.datamodel.ts.TimeSeries;
import ru.ulstu.method.Method;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@Service @Service
public class TimeSeriesService { public class TimeSeriesService {
private final MethodParamBruteForce methodParamBruteForce; private final MethodParamBruteForce methodParamBruteForce;
private final ApplicationContext applicationContext;
public TimeSeriesService(MethodParamBruteForce methodParamBruteForce) { public TimeSeriesService(MethodParamBruteForce methodParamBruteForce,
ApplicationContext applicationContext) {
this.methodParamBruteForce = methodParamBruteForce; this.methodParamBruteForce = methodParamBruteForce;
this.applicationContext = applicationContext;
} }
public ModelingResult getForecast(TimeSeries timeSeries, int countPoints) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException { public ModelingResult getForecast(TimeSeries timeSeries, int countPoints) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
return methodParamBruteForce.getForecast(timeSeries, countPoints); return methodParamBruteForce.getForecast(timeSeries, countPoints);
} }
public TimeSeries smoothTimeSeries(TimeSeries timeSeries) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { public ModelingResult getForecast(TimeSeries timeSeries, String methodClassName, int countPoints) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ModelingException {
return methodParamBruteForce.getSmoothedTimeSeries(timeSeries).getTimeSeries(); return methodParamBruteForce.getForecast(timeSeries, methodClassName, countPoints);
}
public ModelingResult smoothTimeSeries(TimeSeries timeSeries) throws ExecutionException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
return methodParamBruteForce.getSmoothedTimeSeries(timeSeries);
}
public List<Method> getAvailableMethods() {
return methodParamBruteForce.getAvailableMethods();
} }
} }

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.service; package ru.ulstu.service;
import org.slf4j.Logger; import org.slf4j.Logger;

View File

@ -1,9 +1,3 @@
/*
* Copyright (C) 2021 Anton Romanov - All Rights Reserved
* You may use, distribute and modify this code, please write to: romanov73@gmail.com.
*
*/
package ru.ulstu.service; package ru.ulstu.service;
import ru.ulstu.datamodel.exception.ModelingException; import ru.ulstu.datamodel.exception.ModelingException;

View File

@ -1,13 +1,8 @@
#
# Copyright (C) 2021 Anton Romanov - All Rights Reserved
# You may use, distribute and modify this code, please write to: romanov73@gmail.com.
#
#
spring.main.banner-mode=off spring.main.banner-mode=off
logging.level.tech.athene=DEBUG logging.level.tech.athene=DEBUG
server.port=8080 server.port=8080
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
joinfaces.primefaces.theme=afterwork joinfaces.primefaces.theme=afterwork
joinfaces.primefaces.font-awesome=true joinfaces.primefaces.font-awesome=true
time-series.db-path=time-series-db time-series.db-path=time-series-db
messages.basename.path=messages_en.properties

View File

@ -1,8 +1,3 @@
#
# Copyright (C) 2021 Anton Romanov - All Rights Reserved
# You may use, distribute and modify this code, please write to: romanov73@gmail.com.
#
#
messages.app-name=Time Series Smoothing messages.app-name=Time Series Smoothing
messages.menu.home=Main messages.menu.home=Main
messages.time_series_forecast=Time series forecast messages.time_series_forecast=Time series forecast

View File

@ -1,8 +1,3 @@
#
# Copyright (C) 2021 Anton Romanov - All Rights Reserved
# You may use, distribute and modify this code, please write to: romanov73@gmail.com.
#
#
messages.app-name=Time Series Smoothing messages.app-name=Time Series Smoothing
messages.menu.home=Main messages.menu.home=Main
messages.time_series_forecast=Time series forecast messages.time_series_forecast=Time series forecast

View File

@ -1,8 +1,3 @@
#
# Copyright (C) 2021 Anton Romanov - All Rights Reserved
# You may use, distribute and modify this code, please write to: romanov73@gmail.com.
#
#
messages.app-name=Time Series Smoothing messages.app-name=Time Series Smoothing
messages.menu.home=На главную messages.menu.home=На главную
messages.time_series_forecast=Прогноз временного ряда messages.time_series_forecast=Прогноз временного ряда

View File

@ -1,9 +1,3 @@
<!--
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
~
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="ru" <html lang="ru"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml">
@ -19,10 +13,24 @@
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/> <link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/>
</head> </head>
<body> <body>
<nav class="navbar navbar-light bg-light"> <nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="img/logo.png" width="180" height="60" alt=""> <img src="img/logo.png" width="180" height="60" alt="">
</a> </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="/">Главная <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/method">Выбрать метод</a>
</li>
</ul>
</div>
</nav> </nav>
<div class="wrapper"> <div class="wrapper">
<div id="sidebar" class="collapse navbar-collapse sidebar-mobile"> <div id="sidebar" class="collapse navbar-collapse sidebar-mobile">

View File

@ -1,9 +1,3 @@
<!--
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
~
-->
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd"> <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html <html
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"

View File

@ -0,0 +1,156 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
layout:decorate="~{default}">
<head>
<title>Time series smoothing</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<div class="container" layout:fragment="content">
<script src="/webjars/highcharts/7.0.0/highcharts.js"></script>
<script th:inline="javascript" th:if="${timeSeries != null}">
$(document).ready(function () {
var chart = {
renderTo: 'container',
style: {
fontFamily: 'arial'
}
};
var title = {
text: [[#{messages.time_series_forecast}]]
};
var xAxis = {
categories: [[${dates}]],
title: {
enabled: true,
text: [[#{messages.date}]]
},
startOnTick: true,
endOnTick: true,
showLastLabel: true
};
var yAxis = {
title: {
text: [[#{messages.value}]]
}
};
var plotOptions = {
scatter: {
marker: {
radius: 5,
states: {
hover: {
enabled: true,
lineColor: 'rgb(100,100,100)'
}
}
},
states: {
hover: {
marker: {
enabled: true
}
}
},
tooltip: {
headerFormat: '<b>{series.name}</b><br>',
pointFormat: '{point.y}'
}
}
};
var series = [
{
name: [[#{messages.time_series}]],
color: 'rgba(119,152,191,0.5)',
data: [[${timeSeries}]]
},
{
name: [[#{messages.time_series_model}]],
color: 'rgba(255,200,0,0.5)',
data: [[${model}]]
},
{
name: [[#{messages.time_series_test_forecast}]],
color: 'rgba(255,0,0,0.5)',
data: [[${testForecast}]]
},
{
name: [[#{messages.time_series_forecast_short}]],
color: 'rgba(255,0,0,0.5)',
data: [[${forecast}]]
}
];
var json = {};
json.chart = chart;
json.title = title;
json.xAxis = xAxis;
json.yAxis = yAxis;
json.series = series;
json.plotOptions = plotOptions;
$('#chart').highcharts(json);
});
</script>
<form action="#" th:action="chartMethod" th:object="${chartForm}">
<div class="row">
<div class="col-3">
<select id="select-set" class="selectpicker" data-live-search="true" th:field="*{set}"
data-width="90%" onchange="$('#select-ts').val(''); form.submit();">
<option value="">Набор временных рядов</option>
<option th:each="set : ${sets}"
th:value="${set.key}"
th:utext="${set.key}">
</option>
</select>
<script th:inline="javascript" th:if="*{set != null}">
$('#select-set').val([[*{set.key}]]);
$('#select-set').selectpicker('refresh');
</script>
<select id="select-ts" class="selectpicker" data-live-search="true" th:field="*{timeSeriesMeta}"
data-width="90%" onchange="form.submit();">
<option value="">Временной ряд</option>
<option th:each="ts : ${listTimeSeries}"
th:value="${ts.key}"
th:utext="${ts.key}">
</option>
</select>
<script th:inline="javascript" th:if="*{timeSeriesMeta != null}">
$('#select-ts').val([[*{timeSeriesMeta.key}]]);
$('#select-ts').selectpicker('refresh');
</script>
<select id="select-method" class="selectpicker" data-live-search="true" th:field="*{methodClassName}"
data-width="90%" onchange="form.submit();">
<option value="">Метод прогнозирования</option>
<option th:each="method : ${methods}"
th:value="${method.key}"
th:utext="${method.name}">
</option>
</select>
<script th:inline="javascript" th:if="*{methodClassName != null && methodClassName != ''}">
$('#select-method').val([[*{methodClassName}]]);
$('#select-method').selectpicker('refresh');
</script>
<div th:if="${forecastDescription != null && forecastDescription.timeSeriesMethod != null}">
<p> Метод прогнозирования: <span th:text="${forecastDescription.timeSeriesMethod}"> </span>
<p> Оценка: <span th:text="${forecastDescription.score.value}"> </span>
</div>
</div>
<div class="col-9">
<div id="chart" style="width: 550px; height: 400px; margin: 0 auto"></div>
</div>
</div>
</form>
</div>
</html>