Compare commits

..

No commits in common. "cd5e8ec0a7a6e15dfd99a494979d39562f45708c" and "941a251536bb2d23be5ca757a14d459e6764dd19" have entirely different histories.

31 changed files with 220 additions and 595 deletions

View File

@ -7,18 +7,15 @@ 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, DbRuleService dbRuleService) { public GitExtractorApplication(IndexService indexService) {
this.indexService = indexService; this.indexService = indexService;
this.dbRuleService = dbRuleService;
} }
public static void main(String[] args) { public static void main(String[] args) {
@ -28,6 +25,6 @@ public class GitExtractorApplication {
@EventListener(ApplicationReadyEvent.class) @EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() { public void doSomethingAfterStartup() {
indexService.indexFailedBranchesOnStart(); indexService.indexFailedBranchesOnStart();
//dbRuleService.generateRulesFromMarkup();
} }
} }

View File

@ -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.getAssessments(branchId.get())); model.addAttribute("assessments", assessmentService.getAssessmentsByForecastTendencies(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());

View File

@ -2,16 +2,21 @@ 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 {
@ -30,13 +35,97 @@ public class AssessmentService {
this.fuzzyInferenceService = fuzzyInferenceService; this.fuzzyInferenceService = fuzzyInferenceService;
} }
public List<Assessment> getAssessments(Integer branchId) { public List<Assessment> getAssessmentsByForecastTendencies(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;
} }
} }

View File

@ -16,7 +16,9 @@ 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

View File

@ -21,7 +21,6 @@ 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;
@ -46,8 +45,4 @@ public class Route {
public static String getASSESSMENTS() { public static String getASSESSMENTS() {
return ASSESSMENTS; return ASSESSMENTS;
} }
public static String getADD_MARKUP() {
return ADD_MARKUP;
}
} }

View File

@ -1,3 +1,8 @@
/*
* 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;
@ -13,7 +18,6 @@ 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;
@ -26,16 +30,13 @@ 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
@ -73,7 +74,6 @@ 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());
} }

View File

@ -41,26 +41,6 @@ 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 {

View File

@ -1,75 +0,0 @@
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";
}
}

View File

@ -1,33 +0,0 @@
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;
}
}

View File

@ -1,31 +0,0 @@
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;
}
}

View File

@ -1,88 +0,0 @@
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());
}
}

View File

@ -1,94 +0,0 @@
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());
}
}

View File

@ -54,7 +54,7 @@ public class RuleController {
} }
@GetMapping(DELETE_RULE) @GetMapping(DELETE_RULE)
public String deleteRule(@RequestParam Integer id) { public String deleteRule(Model model, @RequestParam Integer id) {
ruleService.deleteById(id); ruleService.deleteById(id);
return "redirect:/" + LIST_RULE; return "redirect:/" + LIST_RULE;
} }

View File

@ -11,7 +11,8 @@ public class AntecedentValue extends BaseEntity {
public AntecedentValue() { public AntecedentValue() {
} }
public AntecedentValue(String antecedentValue) { public AntecedentValue(Integer id, String antecedentValue) {
this.setId(id);
this.antecedentValue = antecedentValue; this.antecedentValue = antecedentValue;
} }

View File

@ -4,8 +4,5 @@ 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);
} }

View File

@ -22,9 +22,4 @@ 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));
}
} }

View File

@ -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,11 +13,14 @@ 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;
} }
@ -43,17 +46,6 @@ 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);
} }

View File

@ -8,7 +8,6 @@ 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;
@ -35,7 +34,8 @@ public class FuzzyInferenceService {
+ " is %s"; + " is %s";
private final static String NO_RESULT = "Нет результата"; private final static String NO_RESULT = "Нет результата";
private List<String> mapRulesToString(List<DbRule> dbRules) { private List<String> getRulesFromDb(List<DbRule> dbRules, Map<String, Double> variableValues) {
validateVariables(variableValues, dbRules);
return dbRules.stream().map(this::getFuzzyRule).collect(Collectors.toList()); return dbRules.stream().map(this::getFuzzyRule).collect(Collectors.toList());
} }
@ -51,6 +51,8 @@ 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) -> {
@ -58,11 +60,20 @@ public class FuzzyInferenceService {
input.setName(key); input.setName(key);
input.setDescription(""); input.setDescription("");
input.setEnabled(true); input.setEnabled(true);
input.setRange(-1, 1); 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.setLockValueInRange(false); input.setLockValueInRange(false);
input.addTerm(new Triangle("спад", -1, 0)); for (int i = 0; i < antecedentValues.size(); i++) {
input.addTerm(new Triangle("стабильно", -0.1, 0.1)); input.addTerm(
input.addTerm(new Triangle("рост", 0, 1)); new Triangle(
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);
}); });
@ -76,7 +87,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 + 1)); output.addTerm(new Triangle(consequentValues.get(i).toString(), i, i + 2.1));
} }
engine.addOutputVariable(output); engine.addOutputVariable(output);
@ -88,7 +99,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());
mapRulesToString(dbRules).forEach(r -> { getRulesFromDb(dbRules, variableValues).forEach(r -> {
LOG.info(r); LOG.info(r);
mamdani.addRule(Rule.parse(r, engine)); mamdani.addRule(Rule.parse(r, engine));
}); });
@ -104,25 +115,21 @@ 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,
validateVariables(variableValues, dbRules); Map<String, Double> min,
variableValues.entrySet().forEach(e -> System.out.println(e.getKey() + " " + e.getValue())); Map<String, Double> max) {
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, antecedentValues, consequentValues)); engine.addRuleBlock(getRuleBlock(engine, dbRules, variableValues, min, max, antecedentValues, consequentValues));
Map<String, Double> consequents = getConsequent(engine, variableValues); Map.Entry<String, Double> consequent = getConsequent(engine, variableValues);
if (consequents.containsKey(NO_RESULT)) { if (consequent.getKey().equals(NO_RESULT)) {
return new ArrayList<>(); return new ArrayList<>();
} }
List<Assessment> assessments = new ArrayList<>(); return dbRules
for (Map.Entry<String, Double> consequent : consequents.entrySet()) { .stream()
for (DbRule dbRule : dbRules) { .filter(r -> r.getId().equals(Integer.valueOf(consequent.getKey())))
if (dbRule.getId().equals(Integer.valueOf(consequent.getKey()))) { .map(r -> new Assessment(r, consequent.getValue()))
assessments.add(new Assessment(dbRule, consequent.getValue())); .collect(Collectors.toList());
}
}
}
return assessments;
} }
@ -139,7 +146,7 @@ public class FuzzyInferenceService {
} }
} }
private Map<String, Double> getConsequent(Engine engine, Map<String, Double> variableValues) { private Map.Entry<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());
@ -149,8 +156,8 @@ public class FuzzyInferenceService {
if (outputVariable != null) { if (outputVariable != null) {
LOG.info("Output: {}", outputVariable.getValue()); LOG.info("Output: {}", outputVariable.getValue());
} }
return Double.isNaN(outputVariable.getValue()) return (outputVariable == null || Double.isNaN(outputVariable.getValue()))
? Map.of(NO_RESULT, 0.0) ? Map.entry(NO_RESULT, 0.0)
: outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); : Map.entry(outputVariable.highestMembershipTerm(outputVariable.getValue()).getName(), outputVariable.getValue());
} }
} }

View File

@ -0,0 +1,27 @@
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";
}
}

View File

@ -2,7 +2,6 @@ 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;
@ -16,10 +15,6 @@ 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 {
@ -56,16 +51,6 @@ 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;
} }

View File

@ -3,6 +3,9 @@ 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("Временной ряд зависимостей"),

View File

@ -1,11 +1,8 @@
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
@ -31,16 +28,6 @@ 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;
} }

View File

@ -1,3 +1,8 @@
/*
* 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;
@ -8,20 +13,19 @@ 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
@ -34,8 +38,7 @@ 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/getMaxSmoothing"; private final static String TIME_SERIES_TENDENCY_URL = "http://time-series.athene.tech/api/1.0/getSpecificMethodSmoothed";
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,
@ -126,42 +129,21 @@ 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 JsonTimeSeries(normalizeTimeSeries(ts)))); JSONObject response = httpService.post(TIME_SERIES_TENDENCY_URL, new JSONObject(new SmoothingTimeSeries(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());
}
} }

View File

@ -1,18 +0,0 @@
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;
}
}

View File

@ -1,49 +0,0 @@
<?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>

View File

@ -15,5 +15,4 @@
<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>

View File

@ -31,9 +31,11 @@
<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

View File

@ -1,3 +1,8 @@
<!--
~ 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">
@ -33,13 +38,10 @@
<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="/time-series-markup">Разметка данных</a> <a class="nav-link" href="/listRules" th:text="Правила">Link</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/listRules">Правила</a> <a class="nav-link" href="/assessments" th:text="Рекомендации">Link</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/assessments">Рекомендации</a>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -22,7 +22,6 @@
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}}">
@ -31,13 +30,11 @@
<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" <a role="button" class="btn btn-danger" th:if="${branch.indexingStatus.name() != 'INDEXING'}"
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>

View File

@ -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.name}" th:title="${repo.url}"></a></td> th:text="${repo.url}"></a></td>
<td> <td>
<a role="button" class="btn btn-danger pull-right" th:href="@{'deleteRepository?id=' + ${repo.id}}" <a role="button" class="btn btn-danger" 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>

View File

@ -7,44 +7,18 @@
<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">
<form action="#" th:action="${@route.ADD_MARKUP}" method="post" th:object="${markupForm}"> <table class="table table-striped">
<div class="row"> <thead class="thead-dark">
<div class="col-md-2 col-sm-12"> <tr>
Репозиторий-ветка <th scope="col" colspan="10">Разметка временных рядов</th>
</div> </tr>
<div class="col-md-6 col-sm-12"> </thead>
<select id="select-branch" class="selectpicker" data-live-search="true" th:field="*{branchId}" <tbody>
data-width="90%"> <tr th:each="t: ${ts}">
<option value="">Все ветки</option> <td><span class="badge badge-success" th:text="${t.name}"></span></td>
<option th:each="branch : ${branches}" <td><span class="badge badge-success" th:text="${#lists.size(t.values)}"></span></td>
th:value="${branch.id}" </tr>
th:utext="${branch.gitRepository.url} + ' - '+ ${branch.name}"> </tbody>
</option> </table>
</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>