WIP: страницы для правил #62
@ -7,15 +7,18 @@ import org.springframework.context.event.EventListener;
|
|||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import ru.ulstu.extractor.gitrepository.service.IndexService;
|
import ru.ulstu.extractor.gitrepository.service.IndexService;
|
||||||
|
import ru.ulstu.extractor.rule.service.DbRuleService;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
public class GitExtractorApplication {
|
public class GitExtractorApplication {
|
||||||
private final IndexService indexService;
|
private final IndexService indexService;
|
||||||
|
private final DbRuleService dbRuleService;
|
||||||
|
|
||||||
public GitExtractorApplication(IndexService indexService) {
|
public GitExtractorApplication(IndexService indexService, DbRuleService dbRuleService) {
|
||||||
this.indexService = indexService;
|
this.indexService = indexService;
|
||||||
|
this.dbRuleService = dbRuleService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@ -25,6 +28,6 @@ public class GitExtractorApplication {
|
|||||||
@EventListener(ApplicationReadyEvent.class)
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
public void doSomethingAfterStartup() {
|
public void doSomethingAfterStartup() {
|
||||||
indexService.indexFailedBranchesOnStart();
|
indexService.indexFailedBranchesOnStart();
|
||||||
|
//dbRuleService.generateRulesFromMarkup();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ public class AssessmentController {
|
|||||||
public String getAssessments(Model model, @RequestParam Optional<Integer> branchId) {
|
public String getAssessments(Model model, @RequestParam Optional<Integer> branchId) {
|
||||||
model.addAttribute("branches", branchService.findAllValid());
|
model.addAttribute("branches", branchService.findAllValid());
|
||||||
if (branchId.isPresent()) {
|
if (branchId.isPresent()) {
|
||||||
model.addAttribute("assessments", assessmentService.getAssessmentsByForecastTendencies(branchId.get()));
|
model.addAttribute("assessments", assessmentService.getAssessments(branchId.get()));
|
||||||
model.addAttribute("filterBranchForm", new FilterBranchForm(branchId.get()));
|
model.addAttribute("filterBranchForm", new FilterBranchForm(branchId.get()));
|
||||||
} else {
|
} else {
|
||||||
model.addAttribute("filterBranchForm", new FilterBranchForm());
|
model.addAttribute("filterBranchForm", new FilterBranchForm());
|
||||||
|
@ -2,21 +2,16 @@ package ru.ulstu.extractor.assessment.service;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import ru.ulstu.extractor.assessment.model.Assessment;
|
import ru.ulstu.extractor.assessment.model.Assessment;
|
||||||
import ru.ulstu.extractor.rule.model.AssessmentException;
|
|
||||||
import ru.ulstu.extractor.rule.model.DbRule;
|
import ru.ulstu.extractor.rule.model.DbRule;
|
||||||
import ru.ulstu.extractor.rule.service.AntecedentValueService;
|
import ru.ulstu.extractor.rule.service.AntecedentValueService;
|
||||||
import ru.ulstu.extractor.rule.service.DbRuleService;
|
import ru.ulstu.extractor.rule.service.DbRuleService;
|
||||||
import ru.ulstu.extractor.rule.service.FuzzyInferenceService;
|
import ru.ulstu.extractor.rule.service.FuzzyInferenceService;
|
||||||
import ru.ulstu.extractor.ts.model.TimeSeries;
|
import ru.ulstu.extractor.ts.model.TimeSeries;
|
||||||
import ru.ulstu.extractor.ts.model.TimeSeriesValue;
|
|
||||||
import ru.ulstu.extractor.ts.service.TimeSeriesService;
|
import ru.ulstu.extractor.ts.service.TimeSeriesService;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AssessmentService {
|
public class AssessmentService {
|
||||||
@ -35,97 +30,13 @@ public class AssessmentService {
|
|||||||
this.fuzzyInferenceService = fuzzyInferenceService;
|
this.fuzzyInferenceService = fuzzyInferenceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Assessment> getAssessmentsByForecastTendencies(Integer branchId) {
|
public List<Assessment> getAssessments(Integer branchId) {
|
||||||
List<TimeSeries> timeSeries = timeSeriesService.getByBranch(branchId);
|
List<TimeSeries> timeSeries = timeSeriesService.getByBranch(branchId);
|
||||||
List<DbRule> dbRules = ruleService.getList();
|
List<DbRule> dbRules = ruleService.getList();
|
||||||
try {
|
|
||||||
return getAssessmentsByTimeSeriesTendencies(dbRules, timeSeries);
|
|
||||||
} catch (AssessmentException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Assessment> getAssessmentsByLastValues(Integer branchId) {
|
|
||||||
List<TimeSeries> timeSeries = timeSeriesService.getByBranch(branchId);
|
|
||||||
List<DbRule> dbRules = ruleService.getList();
|
|
||||||
return getAssessmentsByLastValues(dbRules, timeSeries);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Assessment> getSingleAssessmentByTimeSeriesTendencies(List<DbRule> dbRules, List<TimeSeries> timeSeries) throws AssessmentException {
|
|
||||||
Map<String, Double> variableValues = new HashMap<>();
|
Map<String, Double> variableValues = new HashMap<>();
|
||||||
timeSeries.forEach(ts -> variableValues.put(ts.getTimeSeriesType().name(), timeSeriesService.getLastTimeSeriesTendency(ts)));
|
timeSeries.forEach(ts -> variableValues.put(ts.getTimeSeriesType().name(), timeSeriesService.getLastTimeSeriesTendency(ts)));
|
||||||
return fuzzyInferenceService.getFuzzyInference(dbRules,
|
return fuzzyInferenceService.getFuzzyInference(dbRules,
|
||||||
antecedentValueService.getList(),
|
antecedentValueService.getList(),
|
||||||
variableValues,
|
variableValues);
|
||||||
getTSsMin(timeSeries),
|
|
||||||
getTSsMax(timeSeries));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Assessment> getAssessmentsByTimeSeriesTendencies(List<DbRule> dbRules, List<TimeSeries> timeSeries) {
|
|
||||||
return dbRules
|
|
||||||
.stream()
|
|
||||||
.flatMap(dbRule -> {
|
|
||||||
Map<String, Double> variableValues = new HashMap<>();
|
|
||||||
timeSeries
|
|
||||||
.stream()
|
|
||||||
.filter(ts -> ts.getTimeSeriesType() == dbRule.getFirstAntecedent()
|
|
||||||
|| ts.getTimeSeriesType() == dbRule.getSecondAntecedent())
|
|
||||||
.forEach(ts -> variableValues.put(ts.getTimeSeriesType().name(), timeSeriesService
|
|
||||||
.getLastTimeSeriesTendency(ts)));
|
|
||||||
return fuzzyInferenceService.getFuzzyInference(List.of(dbRule),
|
|
||||||
antecedentValueService.getList(),
|
|
||||||
variableValues,
|
|
||||||
getTSsMin(timeSeries),
|
|
||||||
getTSsMax(timeSeries)).stream();
|
|
||||||
})
|
|
||||||
.sorted(Comparator.comparing(Assessment::getDegree))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Assessment> getAssessmentsByLastValues(List<DbRule> dbRules, List<TimeSeries> timeSeries) {
|
|
||||||
Map<String, Double> variableValues = new HashMap<>();
|
|
||||||
timeSeries.forEach(ts -> variableValues.put(ts.getTimeSeriesType().name(), ts.getValues().get(ts.getValues().size() - 1).getValue()));
|
|
||||||
return fuzzyInferenceService.getFuzzyInference(dbRules,
|
|
||||||
antecedentValueService.getList(),
|
|
||||||
variableValues,
|
|
||||||
getTSsMin(timeSeries),
|
|
||||||
getTSsMax(timeSeries));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Double getMin(List<Double> values) {
|
|
||||||
return values.stream().mapToDouble(v -> v).min().getAsDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map.Entry<String, Double> getTSMin(TimeSeries ts) {
|
|
||||||
return Map.entry(ts.getTimeSeriesType().name(),
|
|
||||||
getMin(ts.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Double> getTSsMin(List<TimeSeries> tss) {
|
|
||||||
Map<String, Double> res = new HashMap<>();
|
|
||||||
tss.forEach(ts -> {
|
|
||||||
Map.Entry<String, Double> entry = getTSMin(ts);
|
|
||||||
res.put(entry.getKey(), entry.getValue());
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Double getMax(List<Double> values) {
|
|
||||||
return values.stream().mapToDouble(v -> v).max().getAsDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map.Entry<String, Double> getTSMax(TimeSeries ts) {
|
|
||||||
return Map.entry(ts.getTimeSeriesType().name(),
|
|
||||||
getMax(ts.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Double> getTSsMax(List<TimeSeries> tss) {
|
|
||||||
Map<String, Double> res = new HashMap<>();
|
|
||||||
tss.forEach(ts -> {
|
|
||||||
Map.Entry<String, Double> entry = getTSMax(ts);
|
|
||||||
res.put(entry.getKey(), entry.getValue());
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,7 @@ import static ru.ulstu.extractor.core.Route.LIST_INDEXED_REPOSITORIES;
|
|||||||
public class MvcConfiguration implements WebMvcConfigurer {
|
public class MvcConfiguration implements WebMvcConfigurer {
|
||||||
@Override
|
@Override
|
||||||
public void addViewControllers(ViewControllerRegistry registry) {
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
registry.addViewController("/{articlename:\\w+}");
|
|
||||||
registry.addRedirectViewController("/", LIST_INDEXED_REPOSITORIES);
|
registry.addRedirectViewController("/", LIST_INDEXED_REPOSITORIES);
|
||||||
registry.addRedirectViewController("/default", "/home");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -21,6 +21,7 @@ public class Route {
|
|||||||
public static final String ADD_RULE = "addRule";
|
public static final String ADD_RULE = "addRule";
|
||||||
public static final String ASSESSMENTS = "assessments";
|
public static final String ASSESSMENTS = "assessments";
|
||||||
public static final String DELETE_RULE = "deleteRule";
|
public static final String DELETE_RULE = "deleteRule";
|
||||||
|
public static final String ADD_MARKUP = "addMarkup";
|
||||||
|
|
||||||
public static String getLIST_INDEXED_REPOSITORIES() {
|
public static String getLIST_INDEXED_REPOSITORIES() {
|
||||||
return LIST_INDEXED_REPOSITORIES;
|
return LIST_INDEXED_REPOSITORIES;
|
||||||
@ -45,4 +46,8 @@ public class Route {
|
|||||||
public static String getASSESSMENTS() {
|
public static String getASSESSMENTS() {
|
||||||
return ASSESSMENTS;
|
return ASSESSMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getADD_MARKUP() {
|
||||||
|
return ADD_MARKUP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ru.ulstu.extractor.gitrepository.service;
|
package ru.ulstu.extractor.gitrepository.service;
|
||||||
|
|
||||||
import com.sun.istack.NotNull;
|
import com.sun.istack.NotNull;
|
||||||
@ -18,6 +13,7 @@ import ru.ulstu.extractor.branch.service.BranchService;
|
|||||||
import ru.ulstu.extractor.commit.model.Commit;
|
import ru.ulstu.extractor.commit.model.Commit;
|
||||||
import ru.ulstu.extractor.gitrepository.model.GitRepository;
|
import ru.ulstu.extractor.gitrepository.model.GitRepository;
|
||||||
import ru.ulstu.extractor.ts.creator.db.DBTimeSeriesCreator;
|
import ru.ulstu.extractor.ts.creator.db.DBTimeSeriesCreator;
|
||||||
|
import ru.ulstu.extractor.ts.service.ScheduledTimeSeriesService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -30,13 +26,16 @@ public class IndexService {
|
|||||||
private final GitRepositoryService gitRepositoryService;
|
private final GitRepositoryService gitRepositoryService;
|
||||||
private final BranchService branchService;
|
private final BranchService branchService;
|
||||||
private final List<DBTimeSeriesCreator> timeSeriesCreators;
|
private final List<DBTimeSeriesCreator> timeSeriesCreators;
|
||||||
|
private final ScheduledTimeSeriesService scheduledTimeSeriesService;
|
||||||
|
|
||||||
public IndexService(GitRepositoryService gitRepositoryService,
|
public IndexService(GitRepositoryService gitRepositoryService,
|
||||||
BranchService branchService,
|
BranchService branchService,
|
||||||
List<DBTimeSeriesCreator> timeSeriesCreators) {
|
List<DBTimeSeriesCreator> timeSeriesCreators,
|
||||||
|
ScheduledTimeSeriesService scheduledTimeSeriesService) {
|
||||||
this.gitRepositoryService = gitRepositoryService;
|
this.gitRepositoryService = gitRepositoryService;
|
||||||
this.branchService = branchService;
|
this.branchService = branchService;
|
||||||
this.timeSeriesCreators = timeSeriesCreators;
|
this.timeSeriesCreators = timeSeriesCreators;
|
||||||
|
this.scheduledTimeSeriesService = scheduledTimeSeriesService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -74,6 +73,7 @@ public class IndexService {
|
|||||||
branch = branchService.findByBranchId(branch.getId()).orElseThrow(() -> new RuntimeException("Branch not found by id"));
|
branch = branchService.findByBranchId(branch.getId()).orElseThrow(() -> new RuntimeException("Branch not found by id"));
|
||||||
final Branch branchForSave = branchService.updateStatus(branch, IndexingStatus.FINISHED);
|
final Branch branchForSave = branchService.updateStatus(branch, IndexingStatus.FINISHED);
|
||||||
timeSeriesCreators.forEach(tsCreator -> tsCreator.addTimeSeries(branchForSave));
|
timeSeriesCreators.forEach(tsCreator -> tsCreator.addTimeSeries(branchForSave));
|
||||||
|
scheduledTimeSeriesService.addTimeSeriesPoints();
|
||||||
LOG.debug("Complete indexing {} branch", branch.getName());
|
LOG.debug("Complete indexing {} branch", branch.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,26 @@ public class HttpService {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JSONArray post(String url, JSONArray postData) {
|
||||||
|
log.debug("Service call: {}", url);
|
||||||
|
JSONArray response = null;
|
||||||
|
try {
|
||||||
|
response = new JSONArray(Optional.ofNullable(client
|
||||||
|
.post()
|
||||||
|
.uri(url)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.body(BodyInserters.fromValue(postData.toString()))
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(String.class)
|
||||||
|
.toFuture().get()).orElse("[{response:\"empty\"}]"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new JSONArray("[{response:\"empty\"}]");
|
||||||
|
}
|
||||||
|
log.debug("Service response: {}", response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
public JSONArray get(String url) {
|
public JSONArray get(String url) {
|
||||||
log.debug("Service call: {}", url);
|
log.debug("Service call: {}", url);
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package ru.ulstu.extractor.markup.controller;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import ru.ulstu.extractor.branch.service.BranchService;
|
||||||
|
import ru.ulstu.extractor.core.Route;
|
||||||
|
import ru.ulstu.extractor.markup.model.MarkupForm;
|
||||||
|
import ru.ulstu.extractor.markup.model.TimeSeriesForMarkup;
|
||||||
|
import ru.ulstu.extractor.markup.service.MarkupService;
|
||||||
|
import ru.ulstu.extractor.ts.service.TimeSeriesService;
|
||||||
|
import springfox.documentation.annotations.ApiIgnore;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@ApiIgnore
|
||||||
|
public class TimeSeriesMarkupController {
|
||||||
|
private final TimeSeriesService timeSeriesService;
|
||||||
|
private final BranchService branchService;
|
||||||
|
private final MarkupService markupService;
|
||||||
|
|
||||||
|
public TimeSeriesMarkupController(TimeSeriesService timeSeriesService,
|
||||||
|
BranchService branchService,
|
||||||
|
MarkupService markupService) {
|
||||||
|
this.timeSeriesService = timeSeriesService;
|
||||||
|
this.branchService = branchService;
|
||||||
|
this.markupService = markupService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("time-series-markup")
|
||||||
|
public String markupTs(Model model) {
|
||||||
|
model.addAttribute("branches", branchService.findAllValid());
|
||||||
|
model.addAttribute("markupForm", new MarkupForm());
|
||||||
|
return "markup";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("time-series-markup")
|
||||||
|
public String filter(Model model, @ModelAttribute MarkupForm markupForm) {
|
||||||
|
model.addAttribute("branches", branchService.findAllValid());
|
||||||
|
if (markupForm != null && markupForm.getBranchId() != null) {
|
||||||
|
List<TimeSeriesForMarkup> tss = markupService.getTimeSeriesForMarkup(
|
||||||
|
timeSeriesService.getGroupedTendencies(
|
||||||
|
timeSeriesService.getByBranch(markupForm.getBranchId())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
MarkupForm markupFormWithTs = new MarkupForm(markupForm.getBranchId(), tss);
|
||||||
|
model.addAttribute("markupForm", markupFormWithTs);
|
||||||
|
} else {
|
||||||
|
model.addAttribute("markupForm", new MarkupForm());
|
||||||
|
}
|
||||||
|
return "markup";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(Route.ADD_MARKUP)
|
||||||
|
public String addMarkups(Model model, @ModelAttribute MarkupForm markupForm) {
|
||||||
|
model.addAttribute("branches", branchService.findAllValid());
|
||||||
|
if (markupForm != null && markupForm.getBranchId() != null) {
|
||||||
|
List<TimeSeriesForMarkup> tss = markupService.getTimeSeriesForMarkup(
|
||||||
|
timeSeriesService.getGroupedTendencies(
|
||||||
|
timeSeriesService.getByBranch(markupForm.getBranchId())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
MarkupForm markupFormWithTs = new MarkupForm(markupForm.getBranchId(), tss);
|
||||||
|
model.addAttribute("markupForm", markupFormWithTs);
|
||||||
|
if (markupForm.getTimeSeriesForMarkupList() != null
|
||||||
|
&& !markupForm.getTimeSeriesForMarkupList().isEmpty()) {
|
||||||
|
markupService.generateRules(markupForm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "markup";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package ru.ulstu.extractor.markup.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MarkupForm {
|
||||||
|
private List<TimeSeriesForMarkup> timeSeriesForMarkupList = new ArrayList<>();
|
||||||
|
private Integer branchId;
|
||||||
|
|
||||||
|
public MarkupForm() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MarkupForm(Integer branchId, List<TimeSeriesForMarkup> tss) {
|
||||||
|
this.timeSeriesForMarkupList = tss;
|
||||||
|
this.branchId = branchId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TimeSeriesForMarkup> getTimeSeriesForMarkupList() {
|
||||||
|
return timeSeriesForMarkupList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeSeriesForMarkupList(List<TimeSeriesForMarkup> timeSeriesForMarkupList) {
|
||||||
|
this.timeSeriesForMarkupList = timeSeriesForMarkupList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getBranchId() {
|
||||||
|
return branchId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBranchId(Integer branchId) {
|
||||||
|
this.branchId = branchId;
|
||||||
|
}
|
||||||
|
}
|
31
src/main/java/ru/ulstu/extractor/markup/model/MarkupRow.java
Normal file
31
src/main/java/ru/ulstu/extractor/markup/model/MarkupRow.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package ru.ulstu.extractor.markup.model;
|
||||||
|
|
||||||
|
import ru.ulstu.extractor.ts.model.TimeSeries;
|
||||||
|
|
||||||
|
public class MarkupRow {
|
||||||
|
private TimeSeries timeSeries;
|
||||||
|
private String markup;
|
||||||
|
|
||||||
|
public MarkupRow() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MarkupRow(TimeSeries ts) {
|
||||||
|
this.timeSeries = ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSeries getTimeSeries() {
|
||||||
|
return timeSeries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeSeries(TimeSeries timeSeries) {
|
||||||
|
this.timeSeries = timeSeries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMarkup() {
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarkup(String markup) {
|
||||||
|
this.markup = markup;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package ru.ulstu.extractor.markup.model;
|
||||||
|
|
||||||
|
import ru.ulstu.extractor.rule.model.AntecedentValue;
|
||||||
|
import ru.ulstu.extractor.ts.model.TimeSeries;
|
||||||
|
import ru.ulstu.extractor.ts.model.TimeSeriesType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class TimeSeriesForMarkup {
|
||||||
|
//output
|
||||||
|
private Map<TimeSeries, AntecedentValue> timeSeriesTendencyMap = new HashMap<>();
|
||||||
|
private Date dateFrom;
|
||||||
|
private Date dateTo;
|
||||||
|
|
||||||
|
//input
|
||||||
|
private List<TimeSeriesType> timeSeriesTypes;
|
||||||
|
private List<AntecedentValue> antecedentValues;
|
||||||
|
private String markup;
|
||||||
|
|
||||||
|
public TimeSeriesForMarkup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSeriesForMarkup(TimeSeries ts1, AntecedentValue tendency1, TimeSeries ts2, AntecedentValue tendency2, Date dateFrom, Date dateTo) {
|
||||||
|
this.dateFrom = dateFrom;
|
||||||
|
this.dateTo = dateTo;
|
||||||
|
this.timeSeriesTendencyMap.put(ts1, tendency1);
|
||||||
|
this.timeSeriesTendencyMap.put(ts2, tendency2);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<TimeSeries, AntecedentValue> getTimeSeriesTendencyMap() {
|
||||||
|
return timeSeriesTendencyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDateFrom() {
|
||||||
|
return dateFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDateFrom(Date dateFrom) {
|
||||||
|
this.dateFrom = dateFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDateTo() {
|
||||||
|
return dateTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDateTo(Date dateTo) {
|
||||||
|
this.dateTo = dateTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMarkup() {
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeSeriesTendencyMap(Map<TimeSeries, AntecedentValue> timeSeriesTendencyMap) {
|
||||||
|
this.timeSeriesTendencyMap = timeSeriesTendencyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarkup(String markup) {
|
||||||
|
this.markup = markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TimeSeriesType> getTimeSeriesTypes() {
|
||||||
|
return timeSeriesTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeSeriesTypes(List<TimeSeriesType> timeSeriesTypes) {
|
||||||
|
this.timeSeriesTypes = timeSeriesTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AntecedentValue> getAntecedentValues() {
|
||||||
|
return antecedentValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAntecedentValues(List<AntecedentValue> antecedentValues) {
|
||||||
|
this.antecedentValues = antecedentValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
this.antecedentValues = new ArrayList<>(timeSeriesTendencyMap.values());
|
||||||
|
this.timeSeriesTypes = timeSeriesTendencyMap.keySet().stream().map(TimeSeries::getTimeSeriesType).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package ru.ulstu.extractor.markup.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.extractor.markup.model.MarkupForm;
|
||||||
|
import ru.ulstu.extractor.markup.model.TimeSeriesForMarkup;
|
||||||
|
import ru.ulstu.extractor.rule.model.AntecedentValue;
|
||||||
|
import ru.ulstu.extractor.rule.service.AntecedentValueService;
|
||||||
|
import ru.ulstu.extractor.rule.service.DbRuleService;
|
||||||
|
import ru.ulstu.extractor.ts.model.TimeSeries;
|
||||||
|
import ru.ulstu.extractor.ts.model.TimeSeriesValue;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MarkupService {
|
||||||
|
private final DbRuleService dbRuleService;
|
||||||
|
private final AntecedentValueService antecedentValueService;
|
||||||
|
|
||||||
|
public MarkupService(DbRuleService dbRuleService,
|
||||||
|
AntecedentValueService antecedentValueService) {
|
||||||
|
this.dbRuleService = dbRuleService;
|
||||||
|
this.antecedentValueService = antecedentValueService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void generateRules(MarkupForm markupForm) {
|
||||||
|
markupForm.getTimeSeriesForMarkupList().forEach(markupRow -> {
|
||||||
|
for (int i = 0; i < markupRow.getTimeSeriesTypes().size(); i++) {
|
||||||
|
for (int j = i+1; j < markupRow.getTimeSeriesTypes().size(); j++) {
|
||||||
|
if (i != j) {
|
||||||
|
dbRuleService.saveRule(markupRow.getTimeSeriesTypes().get(i), markupRow.getAntecedentValues().get(i),
|
||||||
|
markupRow.getTimeSeriesTypes().get(j), markupRow.getAntecedentValues().get(j),
|
||||||
|
markupRow.getMarkup());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private AntecedentValue getAntecedent(Double diff) {
|
||||||
|
String antecedentValue;
|
||||||
|
if (diff < 0) {
|
||||||
|
antecedentValue = "спад";
|
||||||
|
} else if (diff > 0) {
|
||||||
|
antecedentValue = "рост";
|
||||||
|
} else {
|
||||||
|
antecedentValue = "стабильно";
|
||||||
|
}
|
||||||
|
return antecedentValueService.getByValue(antecedentValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<TimeSeriesForMarkup> getTimeSeriesForMarkup(List<TimeSeries> timeSeriesList) {
|
||||||
|
List<TimeSeriesForMarkup> tsForMarkup = new ArrayList<>();
|
||||||
|
for (int i = 0; i < timeSeriesList.size(); i++) {
|
||||||
|
TimeSeries ts1 = timeSeriesList.get(i);
|
||||||
|
for (int j = i + 1; j < timeSeriesList.size(); j++) {
|
||||||
|
TimeSeries ts2 = timeSeriesList.get(j);
|
||||||
|
List<TimeSeriesValue> values1 = ts1.getValues();
|
||||||
|
for (int k = 0; k < values1.size() - 1; k++) {
|
||||||
|
List<TimeSeriesValue> values2 = ts2.getValues();
|
||||||
|
for (int l = 0; l < values2.size() - 1; l++) {
|
||||||
|
if (values1.get(k).getDate().equals(values2.get(l).getDate())
|
||||||
|
&& values1.get(k + 1).getDate().equals(values2.get(l + 1).getDate())) {
|
||||||
|
final int index = k;
|
||||||
|
TimeSeriesForMarkup found = tsForMarkup
|
||||||
|
.stream()
|
||||||
|
.filter(m -> m.getDateFrom().equals(values1.get(index).getDate()) && m.getDateTo().equals(values1.get(index + 1).getDate()))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
AntecedentValue antecedentValue1 = getAntecedent(values1.get(index + 1).getValue() - values1.get(index).getValue());
|
||||||
|
AntecedentValue antecedentValue2 = getAntecedent(values2.get(l + 1).getValue() - values2.get(l).getValue());
|
||||||
|
if (found == null) {
|
||||||
|
tsForMarkup.add(new TimeSeriesForMarkup(ts1, antecedentValue1, ts2, antecedentValue2, values1.get(index).getDate(), values1.get(index + 1).getDate()));
|
||||||
|
} else {
|
||||||
|
found.getTimeSeriesTendencyMap().put(ts1, antecedentValue1);
|
||||||
|
found.getTimeSeriesTendencyMap().put(ts2, antecedentValue2);
|
||||||
|
found.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tsForMarkup
|
||||||
|
.stream()
|
||||||
|
.sorted(Comparator.comparing(TimeSeriesForMarkup::getDateFrom))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -54,7 +54,7 @@ public class RuleController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(DELETE_RULE)
|
@GetMapping(DELETE_RULE)
|
||||||
public String deleteRule(Model model, @RequestParam Integer id) {
|
public String deleteRule(@RequestParam Integer id) {
|
||||||
ruleService.deleteById(id);
|
ruleService.deleteById(id);
|
||||||
return "redirect:/" + LIST_RULE;
|
return "redirect:/" + LIST_RULE;
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,7 @@ public class AntecedentValue extends BaseEntity {
|
|||||||
public AntecedentValue() {
|
public AntecedentValue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AntecedentValue(Integer id, String antecedentValue) {
|
public AntecedentValue(String antecedentValue) {
|
||||||
this.setId(id);
|
|
||||||
this.antecedentValue = antecedentValue;
|
this.antecedentValue = antecedentValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,5 +4,8 @@ package ru.ulstu.extractor.rule.repository;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import ru.ulstu.extractor.rule.model.AntecedentValue;
|
import ru.ulstu.extractor.rule.model.AntecedentValue;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface AntecedentValueRepository extends JpaRepository<AntecedentValue, Integer> {
|
public interface AntecedentValueRepository extends JpaRepository<AntecedentValue, Integer> {
|
||||||
|
Optional<AntecedentValue> findByAntecedentValue(String antecedentValue);
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,9 @@ public class AntecedentValueService {
|
|||||||
return antecedentValueRepository.findById(antecedentValueId)
|
return antecedentValueRepository.findById(antecedentValueId)
|
||||||
.orElseThrow(() -> new RuntimeException("Antecedent value not found by id " + antecedentValueId));
|
.orElseThrow(() -> new RuntimeException("Antecedent value not found by id " + antecedentValueId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AntecedentValue getByValue(String antecedentValue) {
|
||||||
|
return antecedentValueRepository.findByAntecedentValue(antecedentValue)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Antecedent value not found by value " + antecedentValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ package ru.ulstu.extractor.rule.service;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import ru.ulstu.extractor.rule.model.AddRuleForm;
|
import ru.ulstu.extractor.rule.model.AddRuleForm;
|
||||||
|
import ru.ulstu.extractor.rule.model.AntecedentValue;
|
||||||
import ru.ulstu.extractor.rule.model.DbRule;
|
import ru.ulstu.extractor.rule.model.DbRule;
|
||||||
import ru.ulstu.extractor.rule.repository.RuleRepository;
|
import ru.ulstu.extractor.rule.repository.RuleRepository;
|
||||||
import ru.ulstu.extractor.ts.model.TimeSeriesType;
|
import ru.ulstu.extractor.ts.model.TimeSeriesType;
|
||||||
import ru.ulstu.extractor.ts.service.TimeSeriesService;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -13,14 +13,11 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
public class DbRuleService {
|
public class DbRuleService {
|
||||||
private final RuleRepository ruleRepository;
|
private final RuleRepository ruleRepository;
|
||||||
private final TimeSeriesService timeSeriesService;
|
|
||||||
private final AntecedentValueService antecedentValueService;
|
private final AntecedentValueService antecedentValueService;
|
||||||
|
|
||||||
public DbRuleService(RuleRepository ruleRepository,
|
public DbRuleService(RuleRepository ruleRepository,
|
||||||
TimeSeriesService timeSeriesService,
|
|
||||||
AntecedentValueService antecedentValueService) {
|
AntecedentValueService antecedentValueService) {
|
||||||
this.ruleRepository = ruleRepository;
|
this.ruleRepository = ruleRepository;
|
||||||
this.timeSeriesService = timeSeriesService;
|
|
||||||
this.antecedentValueService = antecedentValueService;
|
this.antecedentValueService = antecedentValueService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +43,17 @@ public class DbRuleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveRule(TimeSeriesType timeSeriesType1, AntecedentValue antecedentValue1,
|
||||||
|
TimeSeriesType timeSeriesType2, AntecedentValue antecedentValue2,
|
||||||
|
String consequent) {
|
||||||
|
ruleRepository.save(new DbRule(antecedentValueService.getByValue(antecedentValue1.getAntecedentValue()),
|
||||||
|
timeSeriesType1,
|
||||||
|
antecedentValueService.getByValue(antecedentValue2.getAntecedentValue()),
|
||||||
|
timeSeriesType2,
|
||||||
|
consequent));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public DbRule findById(Integer id) {
|
public DbRule findById(Integer id) {
|
||||||
return ruleRepository.getOne(id);
|
return ruleRepository.getOne(id);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import com.fuzzylite.norm.t.AlgebraicProduct;
|
|||||||
import com.fuzzylite.norm.t.Minimum;
|
import com.fuzzylite.norm.t.Minimum;
|
||||||
import com.fuzzylite.rule.Rule;
|
import com.fuzzylite.rule.Rule;
|
||||||
import com.fuzzylite.rule.RuleBlock;
|
import com.fuzzylite.rule.RuleBlock;
|
||||||
|
import com.fuzzylite.term.Activated;
|
||||||
import com.fuzzylite.term.Triangle;
|
import com.fuzzylite.term.Triangle;
|
||||||
import com.fuzzylite.variable.InputVariable;
|
import com.fuzzylite.variable.InputVariable;
|
||||||
import com.fuzzylite.variable.OutputVariable;
|
import com.fuzzylite.variable.OutputVariable;
|
||||||
@ -34,8 +35,7 @@ public class FuzzyInferenceService {
|
|||||||
+ " is %s";
|
+ " is %s";
|
||||||
private final static String NO_RESULT = "Нет результата";
|
private final static String NO_RESULT = "Нет результата";
|
||||||
|
|
||||||
private List<String> getRulesFromDb(List<DbRule> dbRules, Map<String, Double> variableValues) {
|
private List<String> mapRulesToString(List<DbRule> dbRules) {
|
||||||
validateVariables(variableValues, dbRules);
|
|
||||||
return dbRules.stream().map(this::getFuzzyRule).collect(Collectors.toList());
|
return dbRules.stream().map(this::getFuzzyRule).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +51,6 @@ public class FuzzyInferenceService {
|
|||||||
private RuleBlock getRuleBlock(Engine engine,
|
private RuleBlock getRuleBlock(Engine engine,
|
||||||
List<DbRule> dbRules,
|
List<DbRule> dbRules,
|
||||||
Map<String, Double> variableValues,
|
Map<String, Double> variableValues,
|
||||||
Map<String, Double> min,
|
|
||||||
Map<String, Double> max,
|
|
||||||
List<AntecedentValue> antecedentValues,
|
List<AntecedentValue> antecedentValues,
|
||||||
List<Integer> consequentValues) {
|
List<Integer> consequentValues) {
|
||||||
variableValues.forEach((key, value) -> {
|
variableValues.forEach((key, value) -> {
|
||||||
@ -60,20 +58,11 @@ public class FuzzyInferenceService {
|
|||||||
input.setName(key);
|
input.setName(key);
|
||||||
input.setDescription("");
|
input.setDescription("");
|
||||||
input.setEnabled(true);
|
input.setEnabled(true);
|
||||||
double delta = antecedentValues.size() > 1
|
input.setRange(-1, 1);
|
||||||
? (max.get(key) - min.get(key)) / (antecedentValues.size() - 1)
|
|
||||||
: (max.get(key) - min.get(key));
|
|
||||||
input.setRange(min.get(key), max.get(key));
|
|
||||||
input.setLockValueInRange(false);
|
input.setLockValueInRange(false);
|
||||||
for (int i = 0; i < antecedentValues.size(); i++) {
|
input.addTerm(new Triangle("спад", -1, 0));
|
||||||
input.addTerm(
|
input.addTerm(new Triangle("стабильно", -0.1, 0.1));
|
||||||
new Triangle(
|
input.addTerm(new Triangle("рост", 0, 1));
|
||||||
antecedentValues.get(i).getAntecedentValue(),
|
|
||||||
min.get(key) + i * delta - 0.5 * delta,
|
|
||||||
min.get(key) + i * delta + delta + 0.5 * delta
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
engine.addInputVariable(input);
|
engine.addInputVariable(input);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -87,7 +76,7 @@ public class FuzzyInferenceService {
|
|||||||
output.setDefaultValue(Double.NaN);
|
output.setDefaultValue(Double.NaN);
|
||||||
output.setLockValueInRange(false);
|
output.setLockValueInRange(false);
|
||||||
for (int i = 0; i < consequentValues.size(); i++) {
|
for (int i = 0; i < consequentValues.size(); i++) {
|
||||||
output.addTerm(new Triangle(consequentValues.get(i).toString(), i, i + 2.1));
|
output.addTerm(new Triangle(consequentValues.get(i).toString(), i, i + 1));
|
||||||
}
|
}
|
||||||
engine.addOutputVariable(output);
|
engine.addOutputVariable(output);
|
||||||
|
|
||||||
@ -99,7 +88,7 @@ public class FuzzyInferenceService {
|
|||||||
//mamdani.setDisjunction(null);
|
//mamdani.setDisjunction(null);
|
||||||
mamdani.setImplication(new AlgebraicProduct());
|
mamdani.setImplication(new AlgebraicProduct());
|
||||||
mamdani.setActivation(new General());
|
mamdani.setActivation(new General());
|
||||||
getRulesFromDb(dbRules, variableValues).forEach(r -> {
|
mapRulesToString(dbRules).forEach(r -> {
|
||||||
LOG.info(r);
|
LOG.info(r);
|
||||||
mamdani.addRule(Rule.parse(r, engine));
|
mamdani.addRule(Rule.parse(r, engine));
|
||||||
});
|
});
|
||||||
@ -115,21 +104,25 @@ public class FuzzyInferenceService {
|
|||||||
|
|
||||||
public List<Assessment> getFuzzyInference(List<DbRule> dbRules,
|
public List<Assessment> getFuzzyInference(List<DbRule> dbRules,
|
||||||
List<AntecedentValue> antecedentValues,
|
List<AntecedentValue> antecedentValues,
|
||||||
Map<String, Double> variableValues,
|
Map<String, Double> variableValues) {
|
||||||
Map<String, Double> min,
|
validateVariables(variableValues, dbRules);
|
||||||
Map<String, Double> max) {
|
variableValues.entrySet().forEach(e -> System.out.println(e.getKey() + " " + e.getValue()));
|
||||||
Engine engine = getFuzzyEngine();
|
Engine engine = getFuzzyEngine();
|
||||||
List<Integer> consequentValues = dbRules.stream().map(DbRule::getId).collect(Collectors.toList());
|
List<Integer> consequentValues = dbRules.stream().map(DbRule::getId).collect(Collectors.toList());
|
||||||
engine.addRuleBlock(getRuleBlock(engine, dbRules, variableValues, min, max, antecedentValues, consequentValues));
|
engine.addRuleBlock(getRuleBlock(engine, dbRules, variableValues, antecedentValues, consequentValues));
|
||||||
Map.Entry<String, Double> consequent = getConsequent(engine, variableValues);
|
Map<String, Double> consequents = getConsequent(engine, variableValues);
|
||||||
if (consequent.getKey().equals(NO_RESULT)) {
|
if (consequents.containsKey(NO_RESULT)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return dbRules
|
List<Assessment> assessments = new ArrayList<>();
|
||||||
.stream()
|
for (Map.Entry<String, Double> consequent : consequents.entrySet()) {
|
||||||
.filter(r -> r.getId().equals(Integer.valueOf(consequent.getKey())))
|
for (DbRule dbRule : dbRules) {
|
||||||
.map(r -> new Assessment(r, consequent.getValue()))
|
if (dbRule.getId().equals(Integer.valueOf(consequent.getKey()))) {
|
||||||
.collect(Collectors.toList());
|
assessments.add(new Assessment(dbRule, consequent.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return assessments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -146,7 +139,7 @@ public class FuzzyInferenceService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map.Entry<String, Double> getConsequent(Engine engine, Map<String, Double> variableValues) {
|
private Map<String, Double> getConsequent(Engine engine, Map<String, Double> variableValues) {
|
||||||
OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME);
|
OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME);
|
||||||
for (Map.Entry<String, Double> variableValue : variableValues.entrySet()) {
|
for (Map.Entry<String, Double> variableValue : variableValues.entrySet()) {
|
||||||
InputVariable inputVariable = engine.getInputVariable(variableValue.getKey());
|
InputVariable inputVariable = engine.getInputVariable(variableValue.getKey());
|
||||||
@ -156,8 +149,8 @@ public class FuzzyInferenceService {
|
|||||||
if (outputVariable != null) {
|
if (outputVariable != null) {
|
||||||
LOG.info("Output: {}", outputVariable.getValue());
|
LOG.info("Output: {}", outputVariable.getValue());
|
||||||
}
|
}
|
||||||
return (outputVariable == null || Double.isNaN(outputVariable.getValue()))
|
return Double.isNaN(outputVariable.getValue())
|
||||||
? Map.entry(NO_RESULT, 0.0)
|
? Map.of(NO_RESULT, 0.0)
|
||||||
: Map.entry(outputVariable.highestMembershipTerm(outputVariable.getValue()).getName(), outputVariable.getValue());
|
: outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package ru.ulstu.extractor.ts.controller;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import ru.ulstu.extractor.ts.model.TimeSeries;
|
|
||||||
import ru.ulstu.extractor.ts.service.TimeSeriesService;
|
|
||||||
import springfox.documentation.annotations.ApiIgnore;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@ApiIgnore
|
|
||||||
public class TimeSeriesMarkupController {
|
|
||||||
private final TimeSeriesService timeSeriesService;
|
|
||||||
|
|
||||||
public TimeSeriesMarkupController(TimeSeriesService timeSeriesService) {
|
|
||||||
this.timeSeriesService = timeSeriesService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("time-series-markup")
|
|
||||||
public String markupTs(Model model) {
|
|
||||||
List<TimeSeries> ts = timeSeriesService.getAllTimeSeries();
|
|
||||||
model.addAttribute("ts", ts);
|
|
||||||
return "markup";
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package ru.ulstu.extractor.ts.model;
|
|||||||
|
|
||||||
import org.hibernate.annotations.Fetch;
|
import org.hibernate.annotations.Fetch;
|
||||||
import org.hibernate.annotations.FetchMode;
|
import org.hibernate.annotations.FetchMode;
|
||||||
|
import org.json.JSONObject;
|
||||||
import ru.ulstu.extractor.branch.model.Branch;
|
import ru.ulstu.extractor.branch.model.Branch;
|
||||||
import ru.ulstu.extractor.core.BaseEntity;
|
import ru.ulstu.extractor.core.BaseEntity;
|
||||||
|
|
||||||
@ -15,6 +16,10 @@ import javax.persistence.ManyToOne;
|
|||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ru.ulstu.extractor.util.JsonUtils.getListOfObjects;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public class TimeSeries extends BaseEntity {
|
public class TimeSeries extends BaseEntity {
|
||||||
@ -51,6 +56,16 @@ public class TimeSeries extends BaseEntity {
|
|||||||
this.branch = branch;
|
this.branch = branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TimeSeries(JSONObject timeSeries, Optional<TimeSeries> maybeTimeSeries) {
|
||||||
|
this.name = timeSeries.getString("name");
|
||||||
|
this.timeSeriesType = maybeTimeSeries.map(TimeSeries::getTimeSeriesType).orElse(null);
|
||||||
|
this.setId(maybeTimeSeries.map(TimeSeries::getId).orElse(null));
|
||||||
|
this.values = getListOfObjects(timeSeries.getJSONArray("values"))
|
||||||
|
.stream()
|
||||||
|
.map(TimeSeriesValue::new)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,6 @@ package ru.ulstu.extractor.ts.model;
|
|||||||
public enum TimeSeriesType {
|
public enum TimeSeriesType {
|
||||||
COMMITS("Временной ряд коммитов"),
|
COMMITS("Временной ряд коммитов"),
|
||||||
AUTHOR_COMMITS("Временной ряд коммитов авторов"),
|
AUTHOR_COMMITS("Временной ряд коммитов авторов"),
|
||||||
AUTHOR_ISSUES("Временной ряд задач авторов"),
|
|
||||||
AUTHOR_COMPLETED_ISSUES("Временной ряд завершенных задач авторов"),
|
|
||||||
AUTHORS("Временной ряд авторов"),
|
|
||||||
BRANCHES("Временной ряд веток"),
|
BRANCHES("Временной ряд веток"),
|
||||||
CLASSES("Временной ряд классов"),
|
CLASSES("Временной ряд классов"),
|
||||||
DEPENDENCIES("Временной ряд зависимостей"),
|
DEPENDENCIES("Временной ряд зависимостей"),
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package ru.ulstu.extractor.ts.model;
|
package ru.ulstu.extractor.ts.model;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
import ru.ulstu.extractor.core.BaseEntity;
|
import ru.ulstu.extractor.core.BaseEntity;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -28,6 +31,16 @@ public class TimeSeriesValue extends BaseEntity {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TimeSeriesValue(JSONObject jsonValue) {
|
||||||
|
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss");
|
||||||
|
try {
|
||||||
|
this.date = formatter.parse(jsonValue.getString("date"));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
this.value = jsonValue.getDouble("value");
|
||||||
|
}
|
||||||
|
|
||||||
public Date getDate() {
|
public Date getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ru.ulstu.extractor.ts.service;
|
package ru.ulstu.extractor.ts.service;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
@ -13,19 +8,20 @@ import org.springframework.stereotype.Service;
|
|||||||
import ru.ulstu.extractor.branch.model.Branch;
|
import ru.ulstu.extractor.branch.model.Branch;
|
||||||
import ru.ulstu.extractor.http.HttpService;
|
import ru.ulstu.extractor.http.HttpService;
|
||||||
import ru.ulstu.extractor.http.JsonTimeSeries;
|
import ru.ulstu.extractor.http.JsonTimeSeries;
|
||||||
import ru.ulstu.extractor.http.SmoothingTimeSeries;
|
|
||||||
import ru.ulstu.extractor.ts.model.TimeSeries;
|
import ru.ulstu.extractor.ts.model.TimeSeries;
|
||||||
import ru.ulstu.extractor.ts.model.TimeSeriesType;
|
import ru.ulstu.extractor.ts.model.TimeSeriesType;
|
||||||
import ru.ulstu.extractor.ts.model.TimeSeriesValue;
|
import ru.ulstu.extractor.ts.model.TimeSeriesValue;
|
||||||
import ru.ulstu.extractor.ts.repository.TimeSeriesRepository;
|
import ru.ulstu.extractor.ts.repository.TimeSeriesRepository;
|
||||||
import ru.ulstu.extractor.ts.repository.TimeSeriesValueRepository;
|
import ru.ulstu.extractor.ts.repository.TimeSeriesValueRepository;
|
||||||
import ru.ulstu.extractor.ts.util.TimeSeriesDateMapper;
|
import ru.ulstu.extractor.ts.util.TimeSeriesDateMapper;
|
||||||
|
import ru.ulstu.extractor.util.JsonUtils;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ -38,7 +34,8 @@ public class TimeSeriesService {
|
|||||||
private final TimeSeriesDateMapper.TimeSeriesInterval timeSeriesInterval = TimeSeriesDateMapper.TimeSeriesInterval.HOUR;
|
private final TimeSeriesDateMapper.TimeSeriesInterval timeSeriesInterval = TimeSeriesDateMapper.TimeSeriesInterval.HOUR;
|
||||||
private final HttpService httpService;
|
private final HttpService httpService;
|
||||||
private final static String TIME_SERIES_SAVE_SERVICE_URL = "http://time-series.athene.tech/api/1.0/add-time-series?setKey=git-extractor";
|
private final static String TIME_SERIES_SAVE_SERVICE_URL = "http://time-series.athene.tech/api/1.0/add-time-series?setKey=git-extractor";
|
||||||
private final static String TIME_SERIES_TENDENCY_URL = "http://time-series.athene.tech/api/1.0/getSpecificMethodSmoothed";
|
private final static String TIME_SERIES_TENDENCY_URL = "http://time-series.athene.tech/api/1.0/getMaxSmoothing";
|
||||||
|
private final static String TIME_SERIES_GROUPED_TENDENCIES_URL = "http://time-series.athene.tech/api/1.0/getGroupedTendencies";
|
||||||
|
|
||||||
public TimeSeriesService(TimeSeriesRepository timeSeriesRepository,
|
public TimeSeriesService(TimeSeriesRepository timeSeriesRepository,
|
||||||
TimeSeriesValueRepository timeSeriesValueRepository,
|
TimeSeriesValueRepository timeSeriesValueRepository,
|
||||||
@ -129,21 +126,42 @@ public class TimeSeriesService {
|
|||||||
|
|
||||||
public double getLastTimeSeriesTendency(TimeSeries ts) {
|
public double getLastTimeSeriesTendency(TimeSeries ts) {
|
||||||
if (ts != null && ts.getValues().size() > MIN_TIME_SERIES_LENGTH) {
|
if (ts != null && ts.getValues().size() > MIN_TIME_SERIES_LENGTH) {
|
||||||
JSONObject response = httpService.post(TIME_SERIES_TENDENCY_URL, new JSONObject(new SmoothingTimeSeries(ts)));
|
JSONObject response = httpService.post(TIME_SERIES_TENDENCY_URL, new JSONObject(new JsonTimeSeries(normalizeTimeSeries(ts))));
|
||||||
LOG.debug("Успешно отправлен на сервис сглаживания");
|
LOG.debug("Успешно отправлен на сервис сглаживания");
|
||||||
if (response.has("response") && response.getString("response").equals("empty")) {
|
if (response.has("response") && response.getString("response").equals("empty")) {
|
||||||
return DEFAULT_TIME_SERIES_TENDENCY;
|
return DEFAULT_TIME_SERIES_TENDENCY;
|
||||||
}
|
}
|
||||||
JSONArray jsonArray = response.getJSONObject("timeSeries").getJSONArray("values");
|
JSONArray jsonArray = response.getJSONObject("timeSeries").getJSONArray("values");
|
||||||
return jsonArray.getJSONObject(jsonArray.length() - 1).getDouble("value");
|
return jsonArray.getJSONObject(jsonArray.length() - 1).getDouble("value") -
|
||||||
|
jsonArray.getJSONObject(jsonArray.length() - 2).getDouble("value");
|
||||||
}
|
}
|
||||||
return DEFAULT_TIME_SERIES_TENDENCY;
|
return DEFAULT_TIME_SERIES_TENDENCY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TimeSeries normalizeTimeSeries(TimeSeries ts) {
|
||||||
|
double sum = ts.getValues().stream().mapToDouble(TimeSeriesValue::getValue).sum();
|
||||||
|
|
||||||
|
if (sum > 0.0d) {
|
||||||
|
for (int i = 0; i < ts.getValues().size(); i++) {
|
||||||
|
ts.getValues().get(i).setValue(ts.getValues().get(i).getValue() / sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isBranchContainsAllTimeSeries(Branch b) {
|
public boolean isBranchContainsAllTimeSeries(Branch b) {
|
||||||
List<TimeSeries> timeSeries = getByBranch(b.getId());
|
List<TimeSeries> timeSeries = getByBranch(b.getId());
|
||||||
return Stream.of(TimeSeriesType.values()).allMatch(type -> timeSeries
|
return Stream.of(TimeSeriesType.values()).allMatch(type -> timeSeries
|
||||||
.stream()
|
.stream()
|
||||||
.anyMatch(ts -> type == ts.getTimeSeriesType()));
|
.anyMatch(ts -> type == ts.getTimeSeriesType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TimeSeries> getGroupedTendencies(List<TimeSeries> tsList) {
|
||||||
|
JSONArray response = httpService.post(TIME_SERIES_GROUPED_TENDENCIES_URL, new JSONArray(tsList.stream().map(JsonTimeSeries::new).collect(Collectors.toList())));
|
||||||
|
LOG.debug("Send to group time series tendencies");
|
||||||
|
return JsonUtils.getListOfObjects(response)
|
||||||
|
.stream()
|
||||||
|
.map(jsonTs -> new TimeSeries(jsonTs, tsList.stream().filter(ts -> jsonTs.get("name").equals("Model of " + ts.getName())).findAny()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
src/main/java/ru/ulstu/extractor/util/JsonUtils.java
Normal file
18
src/main/java/ru/ulstu/extractor/util/JsonUtils.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package ru.ulstu.extractor.util;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JsonUtils {
|
||||||
|
|
||||||
|
public static List<JSONObject> getListOfObjects(JSONArray jsonArray) {
|
||||||
|
List<JSONObject> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
|
result.add(jsonArray.getJSONObject(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
49
src/main/resources/db/changelog-20230302_210000-schema.xml
Normal file
49
src/main/resources/db/changelog-20230302_210000-schema.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
<changeSet author="orion" id="20230302-210000-1">
|
||||||
|
<createTable tableName="markup">
|
||||||
|
<column name="id" type="integer">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="version" type="integer"/>
|
||||||
|
<column name="time_series_id" type="integer">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="markup" type="varchar(500)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<addPrimaryKey columnNames="id" constraintName="pk_markup" tableName="markup"/>
|
||||||
|
<addForeignKeyConstraint baseTableName="markup" baseColumnNames="time_series_id"
|
||||||
|
constraintName="fk_markup_time_series"
|
||||||
|
referencedTableName="time_series"
|
||||||
|
referencedColumnNames="id"/>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="orion" id="20230422-210000-1">
|
||||||
|
<sql>
|
||||||
|
delete
|
||||||
|
from time_series
|
||||||
|
where time_series_type = 'AUTHORS';
|
||||||
|
delete
|
||||||
|
from rule
|
||||||
|
where first_antecedent = 'AUTHORS'
|
||||||
|
OR second_antecedent = 'AUTHORS';
|
||||||
|
delete
|
||||||
|
from time_series
|
||||||
|
where time_series_type = 'AUTHOR_COMPLETED_ISSUES';
|
||||||
|
delete
|
||||||
|
from rule
|
||||||
|
where first_antecedent = 'AUTHOR_COMPLETED_ISSUES'
|
||||||
|
OR second_antecedent = 'AUTHOR_COMPLETED_ISSUES';
|
||||||
|
delete
|
||||||
|
from time_series
|
||||||
|
where time_series_type = 'AUTHOR_ISSUES';
|
||||||
|
delete
|
||||||
|
from rule
|
||||||
|
where first_antecedent = 'AUTHOR_ISSUES'
|
||||||
|
OR second_antecedent = 'AUTHOR_ISSUES';
|
||||||
|
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -15,4 +15,5 @@
|
|||||||
<include file="db/changelog-20220422_120000-schema.xml"/>
|
<include file="db/changelog-20220422_120000-schema.xml"/>
|
||||||
<include file="db/changelog-20220621_120000-schema.xml"/>
|
<include file="db/changelog-20220621_120000-schema.xml"/>
|
||||||
<include file="db/changelog-20221012_170000-schema.xml"/>
|
<include file="db/changelog-20221012_170000-schema.xml"/>
|
||||||
|
<include file="db/changelog-20230302_210000-schema.xml"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
@ -31,11 +31,9 @@
|
|||||||
<input type="submit" class="btn btn-outline-success w-100" value="Применить фильтр"/>
|
<input type="submit" class="btn btn-outline-success w-100" value="Применить фильтр"/>
|
||||||
</div>
|
</div>
|
||||||
<div th:if="*{branchId == null}">Выбрерите ветку для получения оценки репозитория</div>
|
<div th:if="*{branchId == null}">Выбрерите ветку для получения оценки репозитория</div>
|
||||||
|
|
||||||
<input type="hidden" th:field="*{branchId}">
|
|
||||||
</form>
|
</form>
|
||||||
<div th:if="${assessments != null && #lists.size(assessments) > 0}">
|
<div th:if="${assessments != null && #lists.size(assessments) > 0}">
|
||||||
<h5>Состояние репозитория описывается следующими выражениями:</h5>
|
<h5>Состояние репозитория по нескольким правилам описывается следующими выражениями:</h5>
|
||||||
<div th:each="assessment: ${assessments}">
|
<div th:each="assessment: ${assessments}">
|
||||||
<span th:text="${assessment.consequent}"></span>
|
<span th:text="${assessment.consequent}"></span>
|
||||||
вследствие тенденции '<span th:text="${assessment.firstAntecedentTendency}"></span>' показателя '<span
|
вследствие тенденции '<span th:text="${assessment.firstAntecedentTendency}"></span>' показателя '<span
|
||||||
|
@ -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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!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">
|
||||||
@ -38,10 +33,13 @@
|
|||||||
<a class="nav-link" href="/statistic" th:text="#{messages.menu.statistic}">Link</a>
|
<a class="nav-link" href="/statistic" th:text="#{messages.menu.statistic}">Link</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/listRules" th:text="Правила">Link</a>
|
<a class="nav-link" href="/time-series-markup">Разметка данных</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/assessments" th:text="Рекомендации">Link</a>
|
<a class="nav-link" href="/listRules">Правила</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/assessments">Рекомендации</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
th:text="${branch.name}"></a>
|
th:text="${branch.name}"></a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
<div class="pull-right">
|
||||||
<a role="button" class="btn btn-primary" title="Повторить индексацию ветки"
|
<a role="button" class="btn btn-primary" title="Повторить индексацию ветки"
|
||||||
th:if="${branch.indexingStatus.name() != 'INDEXING'}"
|
th:if="${branch.indexingStatus.name() != 'INDEXING'}"
|
||||||
th:href="@{'reindexBranch?branchId=' + ${branch.id}}">
|
th:href="@{'reindexBranch?branchId=' + ${branch.id}}">
|
||||||
@ -30,11 +31,13 @@
|
|||||||
<a role="button" class="btn btn-dark disabled"
|
<a role="button" class="btn btn-dark disabled"
|
||||||
th:if="${branch.indexingStatus.name() == 'INDEXING'}">Индексируется...
|
th:if="${branch.indexingStatus.name() == 'INDEXING'}">Индексируется...
|
||||||
</a>
|
</a>
|
||||||
<a role="button" class="btn btn-danger" th:if="${branch.indexingStatus.name() != 'INDEXING'}"
|
<a role="button" class="btn btn-danger"
|
||||||
|
th:if="${branch.indexingStatus.name() != 'INDEXING'}"
|
||||||
th:href="@{'deleteBranch?id=' + ${branch.id} + '&repositoryId='+${repository.id}}"
|
th:href="@{'deleteBranch?id=' + ${branch.id} + '&repositoryId='+${repository.id}}"
|
||||||
onclick="return confirm('Удалить ветку?')">
|
onclick="return confirm('Удалить ветку?')">
|
||||||
<i class="fa fa-times" aria-hidden="true"></i>
|
<i class="fa fa-times" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr th:each="repo: ${repositories}">
|
<tr th:each="repo: ${repositories}">
|
||||||
<td><a th:href="@{${@route.LIST_REPOSITORY_BRANCHES} + '?repositoryId=' + ${repo.id}}"
|
<td><a th:href="@{${@route.LIST_REPOSITORY_BRANCHES} + '?repositoryId=' + ${repo.id}}"
|
||||||
th:text="${repo.url}"></a></td>
|
th:text="${repo.name}" th:title="${repo.url}"></a></td>
|
||||||
<td>
|
<td>
|
||||||
<a role="button" class="btn btn-danger" th:href="@{'deleteRepository?id=' + ${repo.id}}"
|
<a role="button" class="btn btn-danger pull-right" th:href="@{'deleteRepository?id=' + ${repo.id}}"
|
||||||
onclick="return confirm('Удалить репозиторий?')">
|
onclick="return confirm('Удалить репозиторий?')">
|
||||||
<i class="fa fa-times" aria-hidden="true"></i>
|
<i class="fa fa-times" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
|
@ -7,18 +7,44 @@
|
|||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
</head>
|
</head>
|
||||||
<div class="container" layout:fragment="content">
|
<div class="container" layout:fragment="content">
|
||||||
<table class="table table-striped">
|
<form action="#" th:action="${@route.ADD_MARKUP}" method="post" th:object="${markupForm}">
|
||||||
<thead class="thead-dark">
|
<div class="row">
|
||||||
<tr>
|
<div class="col-md-2 col-sm-12">
|
||||||
<th scope="col" colspan="10">Разметка временных рядов</th>
|
Репозиторий-ветка
|
||||||
</tr>
|
</div>
|
||||||
</thead>
|
<div class="col-md-6 col-sm-12">
|
||||||
<tbody>
|
<select id="select-branch" class="selectpicker" data-live-search="true" th:field="*{branchId}"
|
||||||
<tr th:each="t: ${ts}">
|
data-width="90%">
|
||||||
<td><span class="badge badge-success" th:text="${t.name}"></span></td>
|
<option value="">Все ветки</option>
|
||||||
<td><span class="badge badge-success" th:text="${#lists.size(t.values)}"></span></td>
|
<option th:each="branch : ${branches}"
|
||||||
</tr>
|
th:value="${branch.id}"
|
||||||
</tbody>
|
th:utext="${branch.gitRepository.url} + ' - '+ ${branch.name}">
|
||||||
</table>
|
</option>
|
||||||
|
</select>
|
||||||
|
<script th:inline="javascript">
|
||||||
|
$('#select-branch').val([[*{branchId}]]);
|
||||||
|
$('#select-branch').selectpicker('refresh');
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 col-sm-12">
|
||||||
|
<input type="submit" class="btn btn-outline-success w-100" th:formaction="@{/time-series-markup}"
|
||||||
|
value="Применить"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h5 class="mb-3" th:if="*{timeSeriesForMarkupList != null && #lists.size(timeSeriesForMarkupList) > 0}">
|
||||||
|
Охарактеризуйте периоды вашего проекта</h5>
|
||||||
|
<div class="form-group" th:each="m, itemStat: *{timeSeriesForMarkupList}">
|
||||||
|
<div th:each="m2, itemStat2: *{timeSeriesForMarkupList[__${itemStat.index}__].timeSeriesTypes}">
|
||||||
|
<input type="hidden" th:field="*{timeSeriesForMarkupList[__${itemStat.index}__].timeSeriesTypes[__${itemStat2.index}__]}">
|
||||||
|
</div>
|
||||||
|
<div th:each="m3, itemStat3: *{timeSeriesForMarkupList[__${itemStat.index}__].antecedentValues}">
|
||||||
|
<input type="hidden" th:field="*{timeSeriesForMarkupList[__${itemStat.index}__].antecedentValues[__${itemStat3.index}__].antecedentValue}">
|
||||||
|
</div>
|
||||||
|
<label th:text="${#dates.format(m.dateFrom, 'dd.MM.yyyy HH:mm')} + ' - ' + ${#dates.format(m.dateTo, 'dd.MM.yyyy HH:mm')}"></label>
|
||||||
|
<textarea th:field="*{timeSeriesForMarkupList[__${itemStat.index}__].markup}" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
<input type="submit" th:if="*{timeSeriesForMarkupList != null && #lists.size(timeSeriesForMarkupList) > 0}"
|
||||||
|
class="btn btn-outline-success w-100" value="Сгенерировать правила"/>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user