diff --git a/src/main/java/ru/ulstu/extractor/GitExtractorApplication.java b/src/main/java/ru/ulstu/extractor/GitExtractorApplication.java index 1cf7928..95e1008 100644 --- a/src/main/java/ru/ulstu/extractor/GitExtractorApplication.java +++ b/src/main/java/ru/ulstu/extractor/GitExtractorApplication.java @@ -7,15 +7,18 @@ import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import ru.ulstu.extractor.gitrepository.service.IndexService; +import ru.ulstu.extractor.rule.service.DbRuleService; @SpringBootApplication @EnableScheduling @EnableAsync public class GitExtractorApplication { private final IndexService indexService; + private final DbRuleService dbRuleService; - public GitExtractorApplication(IndexService indexService) { + public GitExtractorApplication(IndexService indexService, DbRuleService dbRuleService) { this.indexService = indexService; + this.dbRuleService = dbRuleService; } public static void main(String[] args) { @@ -25,6 +28,6 @@ public class GitExtractorApplication { @EventListener(ApplicationReadyEvent.class) public void doSomethingAfterStartup() { indexService.indexFailedBranchesOnStart(); + //dbRuleService.generateRulesFromMarkup(); } - } diff --git a/src/main/java/ru/ulstu/extractor/assessment/controller/AssessmentController.java b/src/main/java/ru/ulstu/extractor/assessment/controller/AssessmentController.java index e5721fa..9a7ffd8 100644 --- a/src/main/java/ru/ulstu/extractor/assessment/controller/AssessmentController.java +++ b/src/main/java/ru/ulstu/extractor/assessment/controller/AssessmentController.java @@ -29,7 +29,7 @@ public class AssessmentController { public String getAssessments(Model model, @RequestParam Optional branchId) { model.addAttribute("branches", branchService.findAllValid()); if (branchId.isPresent()) { - model.addAttribute("assessments", assessmentService.getAssessmentsByForecastTendencies(branchId.get())); + model.addAttribute("assessments", assessmentService.getAssessments(branchId.get())); model.addAttribute("filterBranchForm", new FilterBranchForm(branchId.get())); } else { model.addAttribute("filterBranchForm", new FilterBranchForm()); diff --git a/src/main/java/ru/ulstu/extractor/assessment/service/AssessmentService.java b/src/main/java/ru/ulstu/extractor/assessment/service/AssessmentService.java index e1a2020..0bae62a 100644 --- a/src/main/java/ru/ulstu/extractor/assessment/service/AssessmentService.java +++ b/src/main/java/ru/ulstu/extractor/assessment/service/AssessmentService.java @@ -2,21 +2,16 @@ package ru.ulstu.extractor.assessment.service; import org.springframework.stereotype.Service; 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.service.AntecedentValueService; import ru.ulstu.extractor.rule.service.DbRuleService; import ru.ulstu.extractor.rule.service.FuzzyInferenceService; import ru.ulstu.extractor.ts.model.TimeSeries; -import ru.ulstu.extractor.ts.model.TimeSeriesValue; import ru.ulstu.extractor.ts.service.TimeSeriesService; -import java.util.ArrayList; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @Service public class AssessmentService { @@ -35,97 +30,13 @@ public class AssessmentService { this.fuzzyInferenceService = fuzzyInferenceService; } - public List getAssessmentsByForecastTendencies(Integer branchId) { + public List getAssessments(Integer branchId) { List timeSeries = timeSeriesService.getByBranch(branchId); List dbRules = ruleService.getList(); - try { - return getAssessmentsByTimeSeriesTendencies(dbRules, timeSeries); - } catch (AssessmentException ex) { - ex.printStackTrace(); - return new ArrayList<>(); - } - } - - public List getAssessmentsByLastValues(Integer branchId) { - List timeSeries = timeSeriesService.getByBranch(branchId); - List dbRules = ruleService.getList(); - return getAssessmentsByLastValues(dbRules, timeSeries); - } - - private List getSingleAssessmentByTimeSeriesTendencies(List dbRules, List timeSeries) throws AssessmentException { Map variableValues = new HashMap<>(); timeSeries.forEach(ts -> variableValues.put(ts.getTimeSeriesType().name(), timeSeriesService.getLastTimeSeriesTendency(ts))); return fuzzyInferenceService.getFuzzyInference(dbRules, antecedentValueService.getList(), - variableValues, - getTSsMin(timeSeries), - getTSsMax(timeSeries)); - } - - private List getAssessmentsByTimeSeriesTendencies(List dbRules, List timeSeries) { - return dbRules - .stream() - .flatMap(dbRule -> { - Map 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 getAssessmentsByLastValues(List dbRules, List timeSeries) { - Map 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 values) { - return values.stream().mapToDouble(v -> v).min().getAsDouble(); - } - - private Map.Entry getTSMin(TimeSeries ts) { - return Map.entry(ts.getTimeSeriesType().name(), - getMin(ts.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList()))); - } - - private Map getTSsMin(List tss) { - Map res = new HashMap<>(); - tss.forEach(ts -> { - Map.Entry entry = getTSMin(ts); - res.put(entry.getKey(), entry.getValue()); - }); - return res; - } - - private Double getMax(List values) { - return values.stream().mapToDouble(v -> v).max().getAsDouble(); - } - - private Map.Entry getTSMax(TimeSeries ts) { - return Map.entry(ts.getTimeSeriesType().name(), - getMax(ts.getValues().stream().map(TimeSeriesValue::getValue).collect(Collectors.toList()))); - } - - private Map getTSsMax(List tss) { - Map res = new HashMap<>(); - tss.forEach(ts -> { - Map.Entry entry = getTSMax(ts); - res.put(entry.getKey(), entry.getValue()); - }); - return res; + variableValues); } } diff --git a/src/main/java/ru/ulstu/extractor/config/MvcConfiguration.java b/src/main/java/ru/ulstu/extractor/config/MvcConfiguration.java index d4f9a9c..a85543c 100644 --- a/src/main/java/ru/ulstu/extractor/config/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/extractor/config/MvcConfiguration.java @@ -16,9 +16,7 @@ import static ru.ulstu.extractor.core.Route.LIST_INDEXED_REPOSITORIES; public class MvcConfiguration implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/{articlename:\\w+}"); registry.addRedirectViewController("/", LIST_INDEXED_REPOSITORIES); - registry.addRedirectViewController("/default", "/home"); } @Override diff --git a/src/main/java/ru/ulstu/extractor/core/Route.java b/src/main/java/ru/ulstu/extractor/core/Route.java index be9cb53..e1605e7 100644 --- a/src/main/java/ru/ulstu/extractor/core/Route.java +++ b/src/main/java/ru/ulstu/extractor/core/Route.java @@ -21,6 +21,7 @@ public class Route { public static final String ADD_RULE = "addRule"; public static final String ASSESSMENTS = "assessments"; public static final String DELETE_RULE = "deleteRule"; + public static final String ADD_MARKUP = "addMarkup"; public static String getLIST_INDEXED_REPOSITORIES() { return LIST_INDEXED_REPOSITORIES; @@ -45,4 +46,8 @@ public class Route { public static String getASSESSMENTS() { return ASSESSMENTS; } + + public static String getADD_MARKUP() { + return ADD_MARKUP; + } } diff --git a/src/main/java/ru/ulstu/extractor/gitrepository/service/IndexService.java b/src/main/java/ru/ulstu/extractor/gitrepository/service/IndexService.java index 8e89f21..9b2cca7 100644 --- a/src/main/java/ru/ulstu/extractor/gitrepository/service/IndexService.java +++ b/src/main/java/ru/ulstu/extractor/gitrepository/service/IndexService.java @@ -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; 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.gitrepository.model.GitRepository; import ru.ulstu.extractor.ts.creator.db.DBTimeSeriesCreator; +import ru.ulstu.extractor.ts.service.ScheduledTimeSeriesService; import java.io.IOException; import java.util.Collections; @@ -30,13 +26,16 @@ public class IndexService { private final GitRepositoryService gitRepositoryService; private final BranchService branchService; private final List timeSeriesCreators; + private final ScheduledTimeSeriesService scheduledTimeSeriesService; public IndexService(GitRepositoryService gitRepositoryService, BranchService branchService, - List timeSeriesCreators) { + List timeSeriesCreators, + ScheduledTimeSeriesService scheduledTimeSeriesService) { this.gitRepositoryService = gitRepositoryService; this.branchService = branchService; this.timeSeriesCreators = timeSeriesCreators; + this.scheduledTimeSeriesService = scheduledTimeSeriesService; } @Transactional @@ -74,6 +73,7 @@ public class IndexService { branch = branchService.findByBranchId(branch.getId()).orElseThrow(() -> new RuntimeException("Branch not found by id")); final Branch branchForSave = branchService.updateStatus(branch, IndexingStatus.FINISHED); timeSeriesCreators.forEach(tsCreator -> tsCreator.addTimeSeries(branchForSave)); + scheduledTimeSeriesService.addTimeSeriesPoints(); LOG.debug("Complete indexing {} branch", branch.getName()); } diff --git a/src/main/java/ru/ulstu/extractor/http/HttpService.java b/src/main/java/ru/ulstu/extractor/http/HttpService.java index 1e53488..97dd0df 100644 --- a/src/main/java/ru/ulstu/extractor/http/HttpService.java +++ b/src/main/java/ru/ulstu/extractor/http/HttpService.java @@ -41,6 +41,26 @@ public class HttpService { 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) { log.debug("Service call: {}", url); try { diff --git a/src/main/java/ru/ulstu/extractor/markup/controller/TimeSeriesMarkupController.java b/src/main/java/ru/ulstu/extractor/markup/controller/TimeSeriesMarkupController.java new file mode 100644 index 0000000..3aef93a --- /dev/null +++ b/src/main/java/ru/ulstu/extractor/markup/controller/TimeSeriesMarkupController.java @@ -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 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 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"; + } +} diff --git a/src/main/java/ru/ulstu/extractor/markup/model/MarkupForm.java b/src/main/java/ru/ulstu/extractor/markup/model/MarkupForm.java new file mode 100644 index 0000000..098a379 --- /dev/null +++ b/src/main/java/ru/ulstu/extractor/markup/model/MarkupForm.java @@ -0,0 +1,33 @@ +package ru.ulstu.extractor.markup.model; + +import java.util.ArrayList; +import java.util.List; + +public class MarkupForm { + private List timeSeriesForMarkupList = new ArrayList<>(); + private Integer branchId; + + public MarkupForm() { + } + + public MarkupForm(Integer branchId, List tss) { + this.timeSeriesForMarkupList = tss; + this.branchId = branchId; + } + + public List getTimeSeriesForMarkupList() { + return timeSeriesForMarkupList; + } + + public void setTimeSeriesForMarkupList(List timeSeriesForMarkupList) { + this.timeSeriesForMarkupList = timeSeriesForMarkupList; + } + + public Integer getBranchId() { + return branchId; + } + + public void setBranchId(Integer branchId) { + this.branchId = branchId; + } +} diff --git a/src/main/java/ru/ulstu/extractor/markup/model/MarkupRow.java b/src/main/java/ru/ulstu/extractor/markup/model/MarkupRow.java new file mode 100644 index 0000000..3989a6f --- /dev/null +++ b/src/main/java/ru/ulstu/extractor/markup/model/MarkupRow.java @@ -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; + } +} diff --git a/src/main/java/ru/ulstu/extractor/markup/model/TimeSeriesForMarkup.java b/src/main/java/ru/ulstu/extractor/markup/model/TimeSeriesForMarkup.java new file mode 100644 index 0000000..3a48a57 --- /dev/null +++ b/src/main/java/ru/ulstu/extractor/markup/model/TimeSeriesForMarkup.java @@ -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 timeSeriesTendencyMap = new HashMap<>(); + private Date dateFrom; + private Date dateTo; + + //input + private List timeSeriesTypes; + private List 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 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 timeSeriesTendencyMap) { + this.timeSeriesTendencyMap = timeSeriesTendencyMap; + } + + public void setMarkup(String markup) { + this.markup = markup; + } + + public List getTimeSeriesTypes() { + return timeSeriesTypes; + } + + public void setTimeSeriesTypes(List timeSeriesTypes) { + this.timeSeriesTypes = timeSeriesTypes; + } + + public List getAntecedentValues() { + return antecedentValues; + } + + public void setAntecedentValues(List antecedentValues) { + this.antecedentValues = antecedentValues; + } + + public void refresh() { + this.antecedentValues = new ArrayList<>(timeSeriesTendencyMap.values()); + this.timeSeriesTypes = timeSeriesTendencyMap.keySet().stream().map(TimeSeries::getTimeSeriesType).collect(Collectors.toList()); + } +} diff --git a/src/main/java/ru/ulstu/extractor/markup/service/MarkupService.java b/src/main/java/ru/ulstu/extractor/markup/service/MarkupService.java new file mode 100644 index 0000000..5c4508d --- /dev/null +++ b/src/main/java/ru/ulstu/extractor/markup/service/MarkupService.java @@ -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 getTimeSeriesForMarkup(List timeSeriesList) { + List 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 values1 = ts1.getValues(); + for (int k = 0; k < values1.size() - 1; k++) { + List 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()); + } +} diff --git a/src/main/java/ru/ulstu/extractor/rule/controller/RuleController.java b/src/main/java/ru/ulstu/extractor/rule/controller/RuleController.java index 4e855df..a9a8258 100644 --- a/src/main/java/ru/ulstu/extractor/rule/controller/RuleController.java +++ b/src/main/java/ru/ulstu/extractor/rule/controller/RuleController.java @@ -54,7 +54,7 @@ public class RuleController { } @GetMapping(DELETE_RULE) - public String deleteRule(Model model, @RequestParam Integer id) { + public String deleteRule(@RequestParam Integer id) { ruleService.deleteById(id); return "redirect:/" + LIST_RULE; } diff --git a/src/main/java/ru/ulstu/extractor/rule/model/AntecedentValue.java b/src/main/java/ru/ulstu/extractor/rule/model/AntecedentValue.java index 3583e6a..39b5eb6 100644 --- a/src/main/java/ru/ulstu/extractor/rule/model/AntecedentValue.java +++ b/src/main/java/ru/ulstu/extractor/rule/model/AntecedentValue.java @@ -11,8 +11,7 @@ public class AntecedentValue extends BaseEntity { public AntecedentValue() { } - public AntecedentValue(Integer id, String antecedentValue) { - this.setId(id); + public AntecedentValue(String antecedentValue) { this.antecedentValue = antecedentValue; } diff --git a/src/main/java/ru/ulstu/extractor/rule/repository/AntecedentValueRepository.java b/src/main/java/ru/ulstu/extractor/rule/repository/AntecedentValueRepository.java index 1ea84d7..3ae5795 100644 --- a/src/main/java/ru/ulstu/extractor/rule/repository/AntecedentValueRepository.java +++ b/src/main/java/ru/ulstu/extractor/rule/repository/AntecedentValueRepository.java @@ -4,5 +4,8 @@ package ru.ulstu.extractor.rule.repository; import org.springframework.data.jpa.repository.JpaRepository; import ru.ulstu.extractor.rule.model.AntecedentValue; +import java.util.Optional; + public interface AntecedentValueRepository extends JpaRepository { + Optional findByAntecedentValue(String antecedentValue); } diff --git a/src/main/java/ru/ulstu/extractor/rule/service/AntecedentValueService.java b/src/main/java/ru/ulstu/extractor/rule/service/AntecedentValueService.java index ef3c124..8de502a 100644 --- a/src/main/java/ru/ulstu/extractor/rule/service/AntecedentValueService.java +++ b/src/main/java/ru/ulstu/extractor/rule/service/AntecedentValueService.java @@ -22,4 +22,9 @@ public class AntecedentValueService { return antecedentValueRepository.findById(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)); + } } diff --git a/src/main/java/ru/ulstu/extractor/rule/service/DbRuleService.java b/src/main/java/ru/ulstu/extractor/rule/service/DbRuleService.java index 198d486..537ef59 100644 --- a/src/main/java/ru/ulstu/extractor/rule/service/DbRuleService.java +++ b/src/main/java/ru/ulstu/extractor/rule/service/DbRuleService.java @@ -2,10 +2,10 @@ package ru.ulstu.extractor.rule.service; import org.springframework.stereotype.Service; 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.repository.RuleRepository; import ru.ulstu.extractor.ts.model.TimeSeriesType; -import ru.ulstu.extractor.ts.service.TimeSeriesService; import java.util.List; import java.util.stream.Collectors; @@ -13,14 +13,11 @@ import java.util.stream.Collectors; @Service public class DbRuleService { private final RuleRepository ruleRepository; - private final TimeSeriesService timeSeriesService; private final AntecedentValueService antecedentValueService; public DbRuleService(RuleRepository ruleRepository, - TimeSeriesService timeSeriesService, AntecedentValueService antecedentValueService) { this.ruleRepository = ruleRepository; - this.timeSeriesService = timeSeriesService; 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) { return ruleRepository.getOne(id); } diff --git a/src/main/java/ru/ulstu/extractor/rule/service/FuzzyInferenceService.java b/src/main/java/ru/ulstu/extractor/rule/service/FuzzyInferenceService.java index 6768c87..28838c6 100644 --- a/src/main/java/ru/ulstu/extractor/rule/service/FuzzyInferenceService.java +++ b/src/main/java/ru/ulstu/extractor/rule/service/FuzzyInferenceService.java @@ -8,6 +8,7 @@ import com.fuzzylite.norm.t.AlgebraicProduct; import com.fuzzylite.norm.t.Minimum; import com.fuzzylite.rule.Rule; import com.fuzzylite.rule.RuleBlock; +import com.fuzzylite.term.Activated; import com.fuzzylite.term.Triangle; import com.fuzzylite.variable.InputVariable; import com.fuzzylite.variable.OutputVariable; @@ -34,8 +35,7 @@ public class FuzzyInferenceService { + " is %s"; private final static String NO_RESULT = "Нет результата"; - private List getRulesFromDb(List dbRules, Map variableValues) { - validateVariables(variableValues, dbRules); + private List mapRulesToString(List dbRules) { return dbRules.stream().map(this::getFuzzyRule).collect(Collectors.toList()); } @@ -51,8 +51,6 @@ public class FuzzyInferenceService { private RuleBlock getRuleBlock(Engine engine, List dbRules, Map variableValues, - Map min, - Map max, List antecedentValues, List consequentValues) { variableValues.forEach((key, value) -> { @@ -60,20 +58,11 @@ public class FuzzyInferenceService { input.setName(key); input.setDescription(""); input.setEnabled(true); - double delta = antecedentValues.size() > 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.setRange(-1, 1); input.setLockValueInRange(false); - for (int i = 0; i < antecedentValues.size(); i++) { - input.addTerm( - new Triangle( - antecedentValues.get(i).getAntecedentValue(), - min.get(key) + i * delta - 0.5 * delta, - min.get(key) + i * delta + delta + 0.5 * delta - ) - ); - } + input.addTerm(new Triangle("спад", -1, 0)); + input.addTerm(new Triangle("стабильно", -0.1, 0.1)); + input.addTerm(new Triangle("рост", 0, 1)); engine.addInputVariable(input); }); @@ -87,7 +76,7 @@ public class FuzzyInferenceService { output.setDefaultValue(Double.NaN); output.setLockValueInRange(false); 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); @@ -99,7 +88,7 @@ public class FuzzyInferenceService { //mamdani.setDisjunction(null); mamdani.setImplication(new AlgebraicProduct()); mamdani.setActivation(new General()); - getRulesFromDb(dbRules, variableValues).forEach(r -> { + mapRulesToString(dbRules).forEach(r -> { LOG.info(r); mamdani.addRule(Rule.parse(r, engine)); }); @@ -115,21 +104,25 @@ public class FuzzyInferenceService { public List getFuzzyInference(List dbRules, List antecedentValues, - Map variableValues, - Map min, - Map max) { + Map variableValues) { + validateVariables(variableValues, dbRules); + variableValues.entrySet().forEach(e -> System.out.println(e.getKey() + " " + e.getValue())); Engine engine = getFuzzyEngine(); List consequentValues = dbRules.stream().map(DbRule::getId).collect(Collectors.toList()); - engine.addRuleBlock(getRuleBlock(engine, dbRules, variableValues, min, max, antecedentValues, consequentValues)); - Map.Entry consequent = getConsequent(engine, variableValues); - if (consequent.getKey().equals(NO_RESULT)) { + engine.addRuleBlock(getRuleBlock(engine, dbRules, variableValues, antecedentValues, consequentValues)); + Map consequents = getConsequent(engine, variableValues); + if (consequents.containsKey(NO_RESULT)) { return new ArrayList<>(); } - return dbRules - .stream() - .filter(r -> r.getId().equals(Integer.valueOf(consequent.getKey()))) - .map(r -> new Assessment(r, consequent.getValue())) - .collect(Collectors.toList()); + List assessments = new ArrayList<>(); + for (Map.Entry consequent : consequents.entrySet()) { + for (DbRule dbRule : dbRules) { + if (dbRule.getId().equals(Integer.valueOf(consequent.getKey()))) { + assessments.add(new Assessment(dbRule, consequent.getValue())); + } + } + } + return assessments; } @@ -146,7 +139,7 @@ public class FuzzyInferenceService { } } - private Map.Entry getConsequent(Engine engine, Map variableValues) { + private Map getConsequent(Engine engine, Map variableValues) { OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME); for (Map.Entry variableValue : variableValues.entrySet()) { InputVariable inputVariable = engine.getInputVariable(variableValue.getKey()); @@ -156,8 +149,8 @@ public class FuzzyInferenceService { if (outputVariable != null) { LOG.info("Output: {}", outputVariable.getValue()); } - return (outputVariable == null || Double.isNaN(outputVariable.getValue())) - ? Map.entry(NO_RESULT, 0.0) - : Map.entry(outputVariable.highestMembershipTerm(outputVariable.getValue()).getName(), outputVariable.getValue()); + return Double.isNaN(outputVariable.getValue()) + ? Map.of(NO_RESULT, 0.0) + : outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); } } diff --git a/src/main/java/ru/ulstu/extractor/ts/controller/TimeSeriesMarkupController.java b/src/main/java/ru/ulstu/extractor/ts/controller/TimeSeriesMarkupController.java deleted file mode 100644 index 5caad5c..0000000 --- a/src/main/java/ru/ulstu/extractor/ts/controller/TimeSeriesMarkupController.java +++ /dev/null @@ -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 ts = timeSeriesService.getAllTimeSeries(); - model.addAttribute("ts", ts); - return "markup"; - } -} diff --git a/src/main/java/ru/ulstu/extractor/ts/model/TimeSeries.java b/src/main/java/ru/ulstu/extractor/ts/model/TimeSeries.java index 76abf70..a7519ce 100644 --- a/src/main/java/ru/ulstu/extractor/ts/model/TimeSeries.java +++ b/src/main/java/ru/ulstu/extractor/ts/model/TimeSeries.java @@ -2,6 +2,7 @@ package ru.ulstu.extractor.ts.model; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; +import org.json.JSONObject; import ru.ulstu.extractor.branch.model.Branch; import ru.ulstu.extractor.core.BaseEntity; @@ -15,6 +16,10 @@ import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static ru.ulstu.extractor.util.JsonUtils.getListOfObjects; @Entity public class TimeSeries extends BaseEntity { @@ -51,6 +56,16 @@ public class TimeSeries extends BaseEntity { this.branch = branch; } + public TimeSeries(JSONObject timeSeries, Optional 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() { return name; } diff --git a/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesType.java b/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesType.java index f694064..27487b7 100644 --- a/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesType.java +++ b/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesType.java @@ -3,9 +3,6 @@ package ru.ulstu.extractor.ts.model; public enum TimeSeriesType { COMMITS("Временной ряд коммитов"), AUTHOR_COMMITS("Временной ряд коммитов авторов"), - AUTHOR_ISSUES("Временной ряд задач авторов"), - AUTHOR_COMPLETED_ISSUES("Временной ряд завершенных задач авторов"), - AUTHORS("Временной ряд авторов"), BRANCHES("Временной ряд веток"), CLASSES("Временной ряд классов"), DEPENDENCIES("Временной ряд зависимостей"), diff --git a/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesValue.java b/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesValue.java index 9b77f2a..e9ac965 100644 --- a/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesValue.java +++ b/src/main/java/ru/ulstu/extractor/ts/model/TimeSeriesValue.java @@ -1,8 +1,11 @@ package ru.ulstu.extractor.ts.model; +import org.json.JSONObject; import ru.ulstu.extractor.core.BaseEntity; import javax.persistence.Entity; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; @Entity @@ -28,6 +31,16 @@ public class TimeSeriesValue extends BaseEntity { 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() { return date; } diff --git a/src/main/java/ru/ulstu/extractor/ts/service/TimeSeriesService.java b/src/main/java/ru/ulstu/extractor/ts/service/TimeSeriesService.java index 234757f..6dba38c 100644 --- a/src/main/java/ru/ulstu/extractor/ts/service/TimeSeriesService.java +++ b/src/main/java/ru/ulstu/extractor/ts/service/TimeSeriesService.java @@ -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; import org.json.JSONArray; @@ -13,19 +8,20 @@ import org.springframework.stereotype.Service; import ru.ulstu.extractor.branch.model.Branch; import ru.ulstu.extractor.http.HttpService; 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.TimeSeriesType; import ru.ulstu.extractor.ts.model.TimeSeriesValue; import ru.ulstu.extractor.ts.repository.TimeSeriesRepository; import ru.ulstu.extractor.ts.repository.TimeSeriesValueRepository; import ru.ulstu.extractor.ts.util.TimeSeriesDateMapper; +import ru.ulstu.extractor.util.JsonUtils; import javax.transaction.Transactional; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; @Service @@ -38,7 +34,8 @@ public class TimeSeriesService { private final TimeSeriesDateMapper.TimeSeriesInterval timeSeriesInterval = TimeSeriesDateMapper.TimeSeriesInterval.HOUR; 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_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, TimeSeriesValueRepository timeSeriesValueRepository, @@ -129,21 +126,42 @@ public class TimeSeriesService { public double getLastTimeSeriesTendency(TimeSeries ts) { 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("Успешно отправлен на сервис сглаживания"); if (response.has("response") && response.getString("response").equals("empty")) { return DEFAULT_TIME_SERIES_TENDENCY; } 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; } + 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) { List timeSeries = getByBranch(b.getId()); return Stream.of(TimeSeriesType.values()).allMatch(type -> timeSeries .stream() .anyMatch(ts -> type == ts.getTimeSeriesType())); } + + public List getGroupedTendencies(List 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()); + } } diff --git a/src/main/java/ru/ulstu/extractor/util/JsonUtils.java b/src/main/java/ru/ulstu/extractor/util/JsonUtils.java new file mode 100644 index 0000000..6ac8927 --- /dev/null +++ b/src/main/java/ru/ulstu/extractor/util/JsonUtils.java @@ -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 getListOfObjects(JSONArray jsonArray) { + List result = new ArrayList<>(); + for (int i = 0; i < jsonArray.length(); i++) { + result.add(jsonArray.getJSONObject(i)); + } + return result; + } +} diff --git a/src/main/resources/db/changelog-20230302_210000-schema.xml b/src/main/resources/db/changelog-20230302_210000-schema.xml new file mode 100644 index 0000000..670658e --- /dev/null +++ b/src/main/resources/db/changelog-20230302_210000-schema.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + 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'; + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml index dfde9f7..aa59e26 100644 --- a/src/main/resources/db/changelog-master.xml +++ b/src/main/resources/db/changelog-master.xml @@ -15,4 +15,5 @@ + diff --git a/src/main/resources/templates/assessments.html b/src/main/resources/templates/assessments.html index a303e2d..d88b271 100644 --- a/src/main/resources/templates/assessments.html +++ b/src/main/resources/templates/assessments.html @@ -31,11 +31,9 @@
Выбрерите ветку для получения оценки репозитория
- -
-
Состояние репозитория описывается следующими выражениями:
+
Состояние репозитория по нескольким правилам описывается следующими выражениями:
вследствие тенденции '' показателя ' - @@ -38,10 +33,13 @@ Link +
diff --git a/src/main/resources/templates/listBranches.html b/src/main/resources/templates/listBranches.html index a9cb074..db6d729 100644 --- a/src/main/resources/templates/listBranches.html +++ b/src/main/resources/templates/listBranches.html @@ -22,19 +22,22 @@ th:text="${branch.name}"> - - - - Индексируется... - - - - + diff --git a/src/main/resources/templates/listRepositories.html b/src/main/resources/templates/listRepositories.html index 8e07b46..8c9289b 100644 --- a/src/main/resources/templates/listRepositories.html +++ b/src/main/resources/templates/listRepositories.html @@ -19,9 +19,9 @@ + th:text="${repo.name}" th:title="${repo.url}"> - diff --git a/src/main/resources/templates/markup.html b/src/main/resources/templates/markup.html index 329d010..fa92dad 100644 --- a/src/main/resources/templates/markup.html +++ b/src/main/resources/templates/markup.html @@ -7,18 +7,44 @@
- - - - - - - - - - - - -
Разметка временных рядов
+
+
+
+ Репозиторий-ветка +
+
+ + +
+
+ +
+
+
+ Охарактеризуйте периоды вашего проекта
+
+
+ +
+
+ +
+ + +
+ +