Merge pull request '#2 -- Add inference dtos' (#22) from 2-execute-rules into master
All checks were successful
CI fuzzy controller / container-test-job (push) Successful in 1m2s

Reviewed-on: #22
This commit is contained in:
romanov73 2025-03-03 22:28:51 +04:00
commit 40fc94c20d
21 changed files with 408 additions and 157 deletions

View File

@ -46,8 +46,7 @@ dependencies {
implementation group: 'org.webjars', name: 'jquery', version: '3.6.0' implementation group: 'org.webjars', name: 'jquery', version: '3.6.0'
implementation group: 'org.webjars', name: 'bootstrap', version: '4.6.0' implementation group: 'org.webjars', name: 'bootstrap', version: '4.6.0'
implementation group: 'org.webjars', name: 'bootstrap-select', version: '1.13.8' implementation group: 'org.webjars', name: 'bootstrap-select', version: '1.13.8'
implementation group: 'org.webjars', name: 'font-awesome', version: '4.7.0' implementation 'org.webjars.npm:bootstrap-icons:1.11.3'
implementation group: 'org.webjars', name: 'highcharts', version: '7.0.0'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
} }

View File

@ -24,4 +24,18 @@ public class ProjectVariableService {
} }
return variableRepository.findByProject(projectService.getById(projectId)); return variableRepository.findByProject(projectService.getById(projectId));
} }
public List<Variable> getInputByProjectId(Integer projectId) {
if (projectId == null || projectId == 0) {
return Collections.emptyList();
}
return variableRepository.getInputByProject(projectService.getById(projectId));
}
public List<Variable> getOutputByProjectId(Integer projectId) {
if (projectId == null || projectId == 0) {
return Collections.emptyList();
}
return variableRepository.getOutputByProject(projectService.getById(projectId));
}
} }

View File

@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import ru.ulstu.fc.rule.model.FuzzyRuleForm; import ru.ulstu.fc.rule.model.FuzzyRuleForm;
import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.FuzzyTerm;
import ru.ulstu.fc.rule.model.Variable; import ru.ulstu.fc.rule.model.dto.VariableDto;
import ru.ulstu.fc.rule.service.FuzzyRuleService; import ru.ulstu.fc.rule.service.FuzzyRuleService;
import ru.ulstu.fc.rule.service.FuzzyTermService; import ru.ulstu.fc.rule.service.FuzzyTermService;
import ru.ulstu.fc.rule.service.VariableService; import ru.ulstu.fc.rule.service.VariableService;
@ -65,11 +65,26 @@ public class FuzzyRuleController {
@ResponseBody @ResponseBody
@GetMapping("/getVariables/{projectId}") @GetMapping("/getVariables/{projectId}")
public List<Variable> getVariables(@PathVariable("projectId") Integer projectId, public List<VariableDto> getVariables(@PathVariable("projectId") Integer projectId,
final HttpServletResponse response) { final HttpServletResponse response) {
response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform"); response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform");
//TODO: return DTO without terms return variableService.getAllDtoByProject(projectId);
return variableService.getAllByProject(projectId); }
@ResponseBody
@GetMapping("/getInputVariables/{projectId}")
public List<VariableDto> getInputVariables(@PathVariable("projectId") Integer projectId,
final HttpServletResponse response) {
response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform");
return variableService.getInputVariablesDtoByProject(projectId);
}
@ResponseBody
@GetMapping("/getOutputVariables/{projectId}")
public List<VariableDto> getOutputVariables(@PathVariable("projectId") Integer projectId,
final HttpServletResponse response) {
response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform");
return variableService.getOutputVariablesDtoByProject(projectId);
} }
@ResponseBody @ResponseBody

View File

@ -7,22 +7,34 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import ru.ulstu.fc.project.service.ProjectRulesService;
import ru.ulstu.fc.rule.model.FuzzyRule; import ru.ulstu.fc.rule.model.FuzzyRule;
import ru.ulstu.fc.rule.model.FuzzyRuleForm; import ru.ulstu.fc.rule.model.FuzzyRuleForm;
import ru.ulstu.fc.rule.service.FuzzyRuleService; import ru.ulstu.fc.rule.service.FuzzyRuleService;
import java.util.List;
@RestController @RestController
@RequestMapping("fuzzyRuleRest") @RequestMapping("fuzzyRuleRest")
public class FuzzyRuleRestController { public class FuzzyRuleRestController {
private final FuzzyRuleService ruleService; private final FuzzyRuleService ruleService;
private final ProjectRulesService projectRulesService;
public FuzzyRuleRestController(FuzzyRuleService ruleService) { public FuzzyRuleRestController(FuzzyRuleService ruleService,
ProjectRulesService projectRulesService) {
this.ruleService = ruleService; this.ruleService = ruleService;
this.projectRulesService = projectRulesService;
} }
@GetMapping("/get/{projectId}/{ruleId}") @GetMapping("/getAll/{projectId}")
public FuzzyRule get(@PathVariable(value = "projectId") Integer projectId, public List<FuzzyRule> getAll(@PathVariable(value = "projectId") Integer projectId) {
@PathVariable(value = "ruleId") Integer id) { //TODO: return dto
return projectRulesService.getByProjectId(projectId);
}
@GetMapping("/get/{ruleId}")
public FuzzyRule get(@PathVariable(value = "ruleId") Integer id) {
//TODO: return dto
return ruleService.getById(id); return ruleService.getById(id);
} }

View File

@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import ru.ulstu.fc.rule.model.InferenceData; import ru.ulstu.fc.rule.model.InferenceData;
import ru.ulstu.fc.rule.model.OutputValue; import ru.ulstu.fc.rule.model.OutputValue;
import ru.ulstu.fc.rule.model.ProjectInferenceData;
import ru.ulstu.fc.rule.service.FuzzyInferenceService; import ru.ulstu.fc.rule.service.FuzzyInferenceService;
import java.util.List; import java.util.List;
@ -24,6 +25,11 @@ public class InferenceRestController {
return fuzzyInferenceService.getFuzzyInference(inferenceData.getRules(), return fuzzyInferenceService.getFuzzyInference(inferenceData.getRules(),
inferenceData.getValues(), inferenceData.getValues(),
inferenceData.getInputVariables(), inferenceData.getInputVariables(),
inferenceData.getOutputVariable()); inferenceData.getOutputVariables());
}
@RequestMapping(value = "getProjectInference", method = RequestMethod.POST)
public List<OutputValue> getProjectInference(@RequestBody ProjectInferenceData projectInferenceData) {
return fuzzyInferenceService.getProjectFuzzyInference(projectInferenceData);
} }
} }

View File

@ -7,7 +7,7 @@ public class InferenceData {
private List<String> rules; private List<String> rules;
private Map<String, Double> values; private Map<String, Double> values;
private List<Variable> inputVariables; private List<Variable> inputVariables;
private Variable outputVariable; private List<Variable> outputVariables;
public List<String> getRules() { public List<String> getRules() {
return rules; return rules;
@ -33,11 +33,11 @@ public class InferenceData {
this.inputVariables = inputVariables; this.inputVariables = inputVariables;
} }
public Variable getOutputVariable() { public List<Variable> getOutputVariables() {
return outputVariable; return outputVariables;
} }
public void setOutputVariable(Variable outputVariable) { public void setOutputVariable(List<Variable> outputVariables) {
this.outputVariable = outputVariable; this.outputVariables = outputVariables;
} }
} }

View File

@ -1,10 +1,12 @@
package ru.ulstu.fc.rule.model; package ru.ulstu.fc.rule.model;
public class OutputValue { public class OutputValue {
private String variable;
private String fuzzyTerm; private String fuzzyTerm;
private Double degree; private Double degree;
public OutputValue(String fuzzyTerm, Double degree) { public OutputValue(String variable, String fuzzyTerm, Double degree) {
this.variable = variable;
this.fuzzyTerm = fuzzyTerm; this.fuzzyTerm = fuzzyTerm;
this.degree = degree; this.degree = degree;
} }
@ -24,4 +26,12 @@ public class OutputValue {
public void setDegree(Double degree) { public void setDegree(Double degree) {
this.degree = degree; this.degree = degree;
} }
public String getVariable() {
return variable;
}
public void setVariable(String variable) {
this.variable = variable;
}
} }

View File

@ -0,0 +1,26 @@
package ru.ulstu.fc.rule.model;
import ru.ulstu.fc.rule.model.dto.VariableValueDto;
import java.util.List;
public class ProjectInferenceData {
private Integer projectId;
private List<VariableValueDto> variableValues;
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public List<VariableValueDto> getVariableValues() {
return variableValues;
}
public void setVariableValues(List<VariableValueDto> variableValues) {
this.variableValues = variableValues;
}
}

View File

@ -0,0 +1,42 @@
package ru.ulstu.fc.rule.model.dto;
import ru.ulstu.fc.rule.model.FuzzyTerm;
public class FuzzyTermDto {
private Integer id;
private String description;
private Double crispValue;
public FuzzyTermDto() {
}
public FuzzyTermDto(FuzzyTerm fuzzyTerm) {
this.description = fuzzyTerm.getDescription();
this.crispValue = fuzzyTerm.getCrispValue();
this.id = fuzzyTerm.getId();
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getCrispValue() {
return crispValue;
}
public void setCrispValue(Double crispValue) {
this.crispValue = crispValue;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}

View File

@ -0,0 +1,55 @@
package ru.ulstu.fc.rule.model.dto;
import ru.ulstu.fc.rule.model.Variable;
import java.util.ArrayList;
import java.util.List;
public class VariableDto {
private Integer id;
private String name;
private boolean input;
private List<FuzzyTermDto> terms = new ArrayList<>();
public VariableDto() {
}
public VariableDto(Variable variable) {
this.id = variable.getId();
this.name = variable.getName();
this.terms = variable.getFuzzyTerms().stream().map(FuzzyTermDto::new).toList();
this.input = variable.isInput();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<FuzzyTermDto> getTerms() {
return terms;
}
public void setTerms(List<FuzzyTermDto> terms) {
this.terms = terms;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public boolean isInput() {
return input;
}
public void setInput(boolean input) {
this.input = input;
}
}

View File

@ -0,0 +1,22 @@
package ru.ulstu.fc.rule.model.dto;
public class VariableValueDto {
private String variableName;
private Double value;
public String getVariableName() {
return variableName;
}
public void setVariableName(String variableName) {
this.variableName = variableName;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
}

View File

@ -1,6 +1,8 @@
package ru.ulstu.fc.rule.repository; package ru.ulstu.fc.rule.repository;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.ulstu.fc.project.model.Project; import ru.ulstu.fc.project.model.Project;
import ru.ulstu.fc.rule.model.Variable; import ru.ulstu.fc.rule.model.Variable;
@ -11,4 +13,10 @@ public interface VariableRepository extends JpaRepository<Variable, Integer> {
List<Variable> findByProject(Project project); List<Variable> findByProject(Project project);
List<Variable> getByProject(Project project); List<Variable> getByProject(Project project);
@Query("SELECT v FROM Variable v WHERE v.project = :project AND v.input = true")
List<Variable> getInputByProject(@Param("project") Project project);
@Query("SELECT v FROM Variable v WHERE v.project = :project AND v.input = false")
List<Variable> getOutputByProject(@Param("project") Project project);
} }

View File

@ -14,10 +14,17 @@ import com.fuzzylite.variable.OutputVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.ulstu.fc.project.service.ProjectRulesService;
import ru.ulstu.fc.project.service.ProjectService;
import ru.ulstu.fc.project.service.ProjectVariableService;
import ru.ulstu.fc.rule.model.FuzzyRule;
import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.FuzzyTerm;
import ru.ulstu.fc.rule.model.OutputValue; import ru.ulstu.fc.rule.model.OutputValue;
import ru.ulstu.fc.rule.model.ProjectInferenceData;
import ru.ulstu.fc.rule.model.Variable; import ru.ulstu.fc.rule.model.Variable;
import ru.ulstu.fc.rule.model.dto.VariableValueDto;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -31,9 +38,17 @@ public class FuzzyInferenceService {
+ " is %s"; + " is %s";
private final static String NO_RESULT = "Нет результата"; private final static String NO_RESULT = "Нет результата";
private final Engine fuzzyEngine; private final Engine fuzzyEngine;
private final ProjectService projectService;
private final FuzzyRuleService fuzzyRuleService;
private final ProjectRulesService projectRulesService;
private final ProjectVariableService projectVariableService;
public FuzzyInferenceService(Engine fuzzyEngine) { public FuzzyInferenceService(Engine fuzzyEngine, ProjectService projectService, FuzzyRuleService fuzzyRuleService, ProjectRulesService projectRulesService, ProjectVariableService projectVariableService) {
this.fuzzyEngine = fuzzyEngine; this.fuzzyEngine = fuzzyEngine;
this.projectService = projectService;
this.fuzzyRuleService = fuzzyRuleService;
this.projectRulesService = projectRulesService;
this.projectVariableService = projectVariableService;
} }
private List<String> getDemoRules() { private List<String> getDemoRules() {
@ -48,6 +63,7 @@ public class FuzzyInferenceService {
final InputVariable input = new InputVariable(); final InputVariable input = new InputVariable();
input.setName(variable.getName()); input.setName(variable.getName());
input.setDescription(""); input.setDescription("");
variable.getFuzzyTerms().sort(Comparator.comparing(FuzzyTerm::getCrispValue));
input.setRange(0, variable.getFuzzyTerms().get(variable.getFuzzyTerms().size() - 1).getCrispValue()); input.setRange(0, variable.getFuzzyTerms().get(variable.getFuzzyTerms().size() - 1).getCrispValue());
input.setEnabled(true); input.setEnabled(true);
input.setLockValueInRange(false); input.setLockValueInRange(false);
@ -89,9 +105,9 @@ public class FuzzyInferenceService {
private RuleBlock getRuleBlock(Engine engine, private RuleBlock getRuleBlock(Engine engine,
List<String> rules, List<String> rules,
List<Variable> inputVariables, List<Variable> inputVariables,
Variable outputVariable) { List<Variable> outputVariables) {
inputVariables.stream().map(this::getInputVariable).forEach(engine::addInputVariable); inputVariables.stream().map(this::getInputVariable).forEach(engine::addInputVariable);
engine.addOutputVariable(getOutputVariable(outputVariable)); outputVariables.stream().map(this::getOutputVariable).forEach(engine::addOutputVariable);
RuleBlock mamdani = new RuleBlock(); RuleBlock mamdani = new RuleBlock();
mamdani.setName("mamdani"); mamdani.setName("mamdani");
@ -106,22 +122,21 @@ public class FuzzyInferenceService {
} }
private List<OutputValue> getConsequent(Engine engine, Map<String, Double> variableValues) { private List<OutputValue> getConsequent(Engine engine, Map<String, Double> variableValues) {
OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME); List<OutputVariable> outputVariables = engine.getOutputVariables();
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());
inputVariable.setValue(variableValue.getValue()); inputVariable.setValue(variableValue.getValue());
} }
engine.process(); engine.process();
if (outputVariable != null) {
LOG.info("Output: {}", outputVariable.getValue()); return outputVariables
}
return Double.isNaN(outputVariable.getValue())
? List.of(new OutputValue(NO_RESULT, 0.0))
: outputVariable.fuzzyOutput()
.getTerms()
.stream() .stream()
.map(t -> new OutputValue(t.getTerm().getName(), t.getDegree())) .filter(v -> !Double.isNaN(v.getValue()))
.collect(Collectors.toList()); .map(OutputVariable::fuzzyOutput)
.map(a -> new OutputValue(a.getName(),
a.getTerms().getFirst().getTerm().getName(),
a.getTerms().getFirst().getDegree()))
.toList();
} }
public List<OutputValue> getFuzzyInference(Map<String, Double> vals) { public List<OutputValue> getFuzzyInference(Map<String, Double> vals) {
@ -137,19 +152,40 @@ public class FuzzyInferenceService {
new FuzzyTerm("высокий", 500000.0)) new FuzzyTerm("высокий", 500000.0))
) )
), ),
new Variable("кредит", List.of( List.of(new Variable("кредит", List.of(
new FuzzyTerm("небольшой", 20000.0), new FuzzyTerm("небольшой", 20000.0),
new FuzzyTerm("средний", 100000.0), new FuzzyTerm("средний", 100000.0),
new FuzzyTerm("большой", 1000000.0))) new FuzzyTerm("большой", 1000000.0))))
); );
} }
public List<OutputValue> getFuzzyInference(List<String> rules, public List<OutputValue> getFuzzyInference(List<String> rules,
Map<String, Double> values, Map<String, Double> values,
List<Variable> inputVariables, List<Variable> inputVariables,
Variable outputVariable) { List<Variable> outputVariables) {
fuzzyEngine.getRuleBlocks().clear(); fuzzyEngine.getRuleBlocks().clear();
fuzzyEngine.addRuleBlock(getRuleBlock(fuzzyEngine, rules, inputVariables, outputVariable)); fuzzyEngine.getInputVariables().clear();
fuzzyEngine.getOutputVariables().clear();
fuzzyEngine.addRuleBlock(getRuleBlock(fuzzyEngine, rules, inputVariables, outputVariables));
return getConsequent(fuzzyEngine, values); return getConsequent(fuzzyEngine, values);
} }
public List<OutputValue> getProjectFuzzyInference(ProjectInferenceData projectInferenceData) {
List<String> fuzzyRules = projectRulesService.getByProjectId(projectInferenceData.getProjectId())
.stream()
.map(FuzzyRule::getContent)
.toList();
Map<String, Double> variableValues = projectInferenceData.getVariableValues()
.stream()
.collect(Collectors.toMap(
VariableValueDto::getVariableName,
VariableValueDto::getValue));
List<Variable> inputVariables = projectVariableService.getInputByProjectId(projectInferenceData.getProjectId());
List<Variable> outputVariables = projectVariableService.getOutputByProjectId(projectInferenceData.getProjectId());
return getFuzzyInference(fuzzyRules,
variableValues,
inputVariables,
outputVariables);
}
} }

View File

@ -5,6 +5,7 @@ import ru.ulstu.fc.project.service.ProjectService;
import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.FuzzyTerm;
import ru.ulstu.fc.rule.model.Variable; import ru.ulstu.fc.rule.model.Variable;
import ru.ulstu.fc.rule.model.VariableForm; import ru.ulstu.fc.rule.model.VariableForm;
import ru.ulstu.fc.rule.model.dto.VariableDto;
import ru.ulstu.fc.rule.repository.VariableRepository; import ru.ulstu.fc.rule.repository.VariableRepository;
import java.util.List; import java.util.List;
@ -69,7 +70,28 @@ public class VariableService {
return variableRepository.getByProject(projectService.getById(projectId)); return variableRepository.getByProject(projectService.getById(projectId));
} }
public List<VariableDto> getAllDtoByProject(Integer projectId) {
return variableRepository.getByProject(projectService.getById(projectId))
.stream()
.map(VariableDto::new)
.toList();
}
public void checkIsCurrentUserVariableWithThrow(Variable variable) { public void checkIsCurrentUserVariableWithThrow(Variable variable) {
projectService.checkIsCurrentUserProjectWithThrow(variable.getProject()); projectService.checkIsCurrentUserProjectWithThrow(variable.getProject());
} }
public List<VariableDto> getInputVariablesDtoByProject(Integer projectId) {
return variableRepository.getInputByProject(projectService.getById(projectId))
.stream()
.map(VariableDto::new)
.toList();
}
public List<VariableDto> getOutputVariablesDtoByProject(Integer projectId) {
return variableRepository.getOutputByProject(projectService.getById(projectId))
.stream()
.map(VariableDto::new)
.toList();
}
} }

View File

@ -5,8 +5,7 @@ function getAntecedent(rule) {
return withoutIf[1].trim().split('then')[0].trim(); return withoutIf[1].trim().split('then')[0].trim();
} }
// TODO: remove duplicate function getRuleComponents(antecedent) {
function getAntecedentComponents(antecedent) {
return antecedent.split('and').map((i) => i.trim()); return antecedent.split('and').map((i) => i.trim());
} }
@ -16,11 +15,6 @@ function getConsequent(rule) {
return withoutIf[1].trim().split('then')[1].trim(); return withoutIf[1].trim().split('then')[1].trim();
} }
// TODO: remove duplicate
function getConsequentComponents(consequent) {
return consequent.split('and').map((i) => i.trim());
}
// common // common
function getVariable(variableComponents) { function getVariable(variableComponents) {
return variableComponents.split('is')[0].trim(); return variableComponents.split('is')[0].trim();
@ -56,8 +50,6 @@ function showFeedbackMessage(message, type) {
/* exported errorHandler */ /* exported errorHandler */
function errorHandler(response, callBack, errorCallBack) { function errorHandler(response, callBack, errorCallBack) {
if (!isEmpty(response.error)) { if (!isEmpty(response.error)) {
// TODO: add l10n
// showFeedbackMessage(response.error.code + ": " + response.error.message, MessageTypesEnum.DANGER);
if (!isEmpty(errorCallBack)) { if (!isEmpty(errorCallBack)) {
errorCallBack(response.data); errorCallBack(response.data);
} }
@ -70,15 +62,12 @@ function errorHandler(response, callBack, errorCallBack) {
} }
/* exported getFromRest */ /* exported getFromRest */
function getFromRest(url, callBack) { async function getFromRest(url) {
$.ajax({ return await $.ajax({
url: url, url: url,
method: 'get', method: 'get',
cache: true, cache: true,
dataType: 'json', dataType: 'json'
success: function (response) {
errorHandler(response, callBack);
}
}); });
} }
@ -120,14 +109,14 @@ function fillSelect(selectElement, values, selectedVal) {
$(selectElement).append($("<option />").val(value.id).text(value.name)); $(selectElement).append($("<option />").val(value.id).text(value.name));
}); });
$(selectElement).children().each(function () { $(selectElement).children().each(function () {
if ($(this).text() == selectedVal) { if ($(this).text() === selectedVal) {
$(this).prop('selected', true); $(this).prop('selected', true);
} }
}); });
} }
function fillFuzzyTerms(variablesElement, fuzzyTermsElement, termVal) { async function fillFuzzyTerms(variablesElement, fuzzyTermsElement, termVal) {
getFromRest("/rule/getFuzzyTerms/" + $(variablesElement).val(), function (fuzzyTerms) { let fuzzyTerms = await getFromRest("/rule/getFuzzyTerms/" + $(variablesElement).val());
let fuzzyTermsData = []; let fuzzyTermsData = [];
$.each(fuzzyTerms, function (key, value) { $.each(fuzzyTerms, function (key, value) {
fuzzyTermsData.push({ fuzzyTermsData.push({
@ -136,13 +125,16 @@ function fillFuzzyTerms(variablesElement, fuzzyTermsElement, termVal) {
}); });
}); });
fillSelect(fuzzyTermsElement, fuzzyTermsData, termVal); fillSelect(fuzzyTermsElement, fuzzyTermsData, termVal);
$(fuzzyTermsElement).selectpicker("refresh");
$(fuzzyTermsElement).trigger("change");
});
} }
function fillVariables(projectId, variablesElement, variableVal) { async function fillVariables(projectId, variablesElement, variableVal, cls) {
getFromRest("/rule/getVariables/" + projectId, function (variables) { let url = '/rule/';
if (cls === 'inputVar') {
url += 'getInputVariables/';
} else {
url += 'getOutputVariables/';
}
let variables = await getFromRest(url + projectId);
let variablesData = []; let variablesData = [];
$.each(variables, function (key, value) { $.each(variables, function (key, value) {
variablesData.push({ variablesData.push({
@ -151,29 +143,29 @@ function fillVariables(projectId, variablesElement, variableVal) {
}); });
}); });
fillSelect(variablesElement, variablesData, variableVal); fillSelect(variablesElement, variablesData, variableVal);
$(variablesElement).selectpicker("refresh");
$(variablesElement).trigger("change");
});
} }
function variableValueChanged(variablesElement, fuzzyTermsElement) { async function variableValueChanged(variablesElement, fuzzyTermsElement) {
fillFuzzyTerms(variablesElement, fuzzyTermsElement); await fillFuzzyTerms(variablesElement, fuzzyTermsElement);
} $(".selectpicker").selectpicker("refresh");
function fuzzyTermsValueChanged() {
createRule(); createRule();
} }
function createVariableSelect(cls, projectId, variableVal) { function fuzzyTermsValueChanged() {
$(".selectpicker").selectpicker("refresh");
createRule();
}
async function createVariableSelect(cls, projectId, variableVal) {
let variablesElement = $("<select class='selectpicker " + cls + " m-2' data-live-search='true data-width='70%'></select>"); let variablesElement = $("<select class='selectpicker " + cls + " m-2' data-live-search='true data-width='70%'></select>");
fillVariables(projectId, variablesElement, variableVal); await fillVariables(projectId, variablesElement, variableVal, cls);
return variablesElement; return variablesElement;
} }
function createFuzzyTermsSelect(cls, variablesElement, termVal) { async function createFuzzyTermsSelect(cls, variablesElement, termVal) {
let fuzzyTermsElement = $("<select class='selectpicker " + cls + " m-2' data-live-search='true data-width='70%'></select>"); let fuzzyTermsElement = $("<select class='selectpicker " + cls + " m-2' data-live-search='true data-width='70%'></select>");
if ($(variablesElement).val()) { if ($(variablesElement).val()) {
fillFuzzyTerms(variablesElement, fuzzyTermsElement, termVal); await fillFuzzyTerms(variablesElement, fuzzyTermsElement, termVal);
} }
return fuzzyTermsElement; return fuzzyTermsElement;
} }
@ -188,15 +180,15 @@ function removeConsequent(buttonElement) {
fuzzyTermsValueChanged(); fuzzyTermsValueChanged();
} }
function addAntecedent(parentElement, projectId, variableVal, termVal) { async function addAntecedent(parentElement, projectId, variableVal, termVal) {
let rowElement = $("<div class='row'></div>"); let rowElement = $("<div class='row'></div>");
if ($(parentElement).find('.row').length) { if ($(parentElement).find('.row').length) {
$(rowElement).append("<label class='col col-md-1 m-2'>И</label>"); $(rowElement).append("<label class='col col-md-1 m-2'>И</label>");
} else { } else {
$(rowElement).append("<label class='col col-md-1 m-2'> </label>"); $(rowElement).append("<label class='col col-md-1 m-2'> </label>");
} }
let variablesElement = createVariableSelect('inputVar', projectId, variableVal); let variablesElement = await createVariableSelect('inputVar', projectId, variableVal);
let fuzzyTermsElement = createFuzzyTermsSelect('inputVal', variablesElement, termVal); let fuzzyTermsElement = await createFuzzyTermsSelect('inputVal', variablesElement, termVal);
$(variablesElement).on("change", function () { $(variablesElement).on("change", function () {
variableValueChanged(variablesElement, fuzzyTermsElement) variableValueChanged(variablesElement, fuzzyTermsElement)
}); });
@ -210,19 +202,20 @@ function addAntecedent(parentElement, projectId, variableVal, termVal) {
$(rowElement).append("<a href='#' class='btn btn-outline-dark m-2' onclick='removeAntecedent($(this))'>-</a>"); $(rowElement).append("<a href='#' class='btn btn-outline-dark m-2' onclick='removeAntecedent($(this))'>-</a>");
} }
$(parentElement).append(rowElement); $(parentElement).append(rowElement);
$(".selectpicker").selectpicker("refresh");
} }
function addConsequent(parentElement, projectId, variableVal, termVal) { async function addConsequent(parentElement, projectId, variableVal, termVal) {
let rowElement = $("<div class='row'></div>"); let rowElement = $("<div class='row'></div>");
if ($(parentElement).find('.row').length) { if ($(parentElement).find('.row').length) {
$(rowElement).append("<label class='col col-md-1 m-2'>И</label>"); $(rowElement).append("<label class='col col-md-1 m-2'>И</label>");
} else { } else {
$(rowElement).append("<label class='col col-md-1 m-2'> </label>"); $(rowElement).append("<label class='col col-md-1 m-2'> </label>");
} }
let variablesElement = createVariableSelect('outVar', projectId, variableVal); let variablesElement = await createVariableSelect('outVar', projectId, variableVal);
let fuzzyTermsElement = createFuzzyTermsSelect('outVal', variablesElement, termVal); let fuzzyTermsElement = await createFuzzyTermsSelect('outVal', variablesElement, termVal);
$(variablesElement).on("change", function () { $(variablesElement).on("change", async function () {
variableValueChanged(variablesElement, fuzzyTermsElement) await variableValueChanged(variablesElement, fuzzyTermsElement)
}); });
$(fuzzyTermsElement).on("change", function () { $(fuzzyTermsElement).on("change", function () {
fuzzyTermsValueChanged() fuzzyTermsValueChanged()
@ -234,20 +227,34 @@ function addConsequent(parentElement, projectId, variableVal, termVal) {
$(rowElement).append("<a href='#' class='btn btn-outline-dark m-2' onclick='removeConsequent($(this))'>-</a>"); $(rowElement).append("<a href='#' class='btn btn-outline-dark m-2' onclick='removeConsequent($(this))'>-</a>");
} }
$(parentElement).append(rowElement); $(parentElement).append(rowElement);
$(".selectpicker").selectpicker("refresh");
} }
function addAntecedentFromRule(parentElement, projectId, ruleContent) { async function addAntecedentFromRule(parentElement, projectId, ruleContent) {
let antecedentComponents = getAntecedentComponents(getAntecedent(ruleContent)); let antecedentComponents = getRuleComponents(getAntecedent(ruleContent));
for (let i = 0; i < antecedentComponents.length; i++) { for (let i = 0; i < antecedentComponents.length; i++) {
let a = antecedentComponents[i]; let a = antecedentComponents[i];
addAntecedent(parentElement, projectId, getVariable(a), getVariableValue(a)); await addAntecedent(parentElement, projectId, getVariable(a), getVariableValue(a));
} }
} }
function addConsequentFromRule(parentElement, projectId, ruleContent) { async function addConsequentFromRule(parentElement, projectId, ruleContent) {
let consequentComponents = getConsequentComponents(getConsequent(ruleContent)); let consequentComponents = getRuleComponents(getConsequent(ruleContent));
for (let i = 0; i < consequentComponents.length; i++) { for (let i = 0; i < consequentComponents.length; i++) {
let c = consequentComponents[i]; let c = consequentComponents[i];
addConsequent(parentElement, projectId, getVariable(c), getVariableValue(c)); await addConsequent(parentElement, projectId, getVariable(c), getVariableValue(c));
} }
} }
async function initSelects() {
let ruleContentEl = $('#ruleContent');
let projectIdEl = $('#projectId');
if ($(ruleContentEl).val()) {
await addAntecedentFromRule($('#rulesAntecedent'), $(projectIdEl).val(), $(ruleContentEl).val());
await addConsequentFromRule($('#rulesConsequent'), $(projectIdEl).val(), $(ruleContentEl).val());
} else {
await addAntecedent($('#rulesAntecedent'), $(projectIdEl).val());
await addConsequent($('#rulesConsequent'), $(projectIdEl).val());
}
$(".selectpicker").selectpicker("refresh");
}

View File

@ -11,7 +11,8 @@
<script type="text/javascript" src="/webjars/bootstrap-select/1.13.8/js/bootstrap-select.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap-select/1.13.8/js/bootstrap-select.min.js"></script>
<link rel="stylesheet" href="/webjars/bootstrap/4.6.0/css/bootstrap.min.css"/> <link rel="stylesheet" href="/webjars/bootstrap/4.6.0/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/webjars/bootstrap-select/1.13.8/css/bootstrap-select.min.css"/> <link rel="stylesheet" href="/webjars/bootstrap-select/1.13.8/css/bootstrap-select.min.css"/>
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/> <link rel="stylesheet" href="/webjars/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css"/>
</head> </head>
<body> <body>

View File

@ -35,7 +35,13 @@
<div class="row" th:each="v, iter : ${variables}"> <div class="row" th:each="v, iter : ${variables}">
<div class="col col-md-12"> <div class="col col-md-12">
<a th:href="@{'/variable/edit/' + ${projectId}+'/'+${v.id}}"> <a th:href="@{'/variable/edit/' + ${projectId}+'/'+${v.id}}">
<span class="badge badge-light" th:text="${iter.index+1} + '. ' + ${v.name}"></span> <h3>
<span class="badge badge-light">
<span th:text="${iter.index+1} + '. ' + ${v.name}"></span>
<i th:if="${v.input}" class="bi bi-box-arrow-in-right"></i>
<i th:if="! ${v.input}" class="bi bi-box-arrow-right"></i>
</span>
</h3>
</a> </a>
</div> </div>
</div> </div>
@ -48,7 +54,7 @@
<div class="row" th:each="r, iter : ${rules}"> <div class="row" th:each="r, iter : ${rules}">
<div class="col col-md-12"> <div class="col col-md-12">
<a th:href="@{'/rule/edit/' + ${projectId}+'/'+${r.id}}"> <a th:href="@{'/rule/edit/' + ${projectId}+'/'+${r.id}}">
<div class="rule row" th:text="${r.content}"></div> <div class="rule row d-none" th:text="${r.content}"></div>
</a> </a>
</div> </div>
</div> </div>
@ -61,8 +67,8 @@
<script type="text/javascript"> <script type="text/javascript">
function addRule(index, el, rule) { function addRule(index, el, rule) {
let ruleHtml = "<div class='col col-md-12'><span class='badge badge-light'>" + (index + 1) + ". Если</span></div>" let ruleHtml = "<div class='col col-md-12'><span class='badge badge-light'>" + (index + 1) + ". Если</span></div>"
let antecedentComponents = getAntecedentComponents(getAntecedent(rule)); let antecedentComponents = getRuleComponents(getAntecedent(rule));
let consequentComponents = getConsequentComponents(getConsequent(rule)); let consequentComponents = getRuleComponents(getConsequent(rule));
for (let i = 0; i < antecedentComponents.length; i++) { for (let i = 0; i < antecedentComponents.length; i++) {
let a = antecedentComponents[i]; let a = antecedentComponents[i];
if (i > 0) { if (i > 0) {
@ -70,9 +76,9 @@
} else { } else {
ruleHtml += "<div class='col col-md-1'></div>"; ruleHtml += "<div class='col col-md-1'></div>";
} }
ruleHtml += "<div class='col col-md-4'><span class='badge badge-primary'>"+getVariable(a)+"</span></div>"; ruleHtml += "<div class='col col-md-4'><span class='badge badge-primary'>" + getVariable(a) + "</span></div>";
ruleHtml += "<div class='col col-md-3'><span class='badge badge-light'>есть</span></div>"; ruleHtml += "<div class='col col-md-3'><span class='badge badge-light'>есть</span></div>";
ruleHtml += "<div class='col col-md-4'><span class='badge badge-success'>"+getVariableValue(a)+"</span></div>"; ruleHtml += "<div class='col col-md-4'><span class='badge badge-success'>" + getVariableValue(a) + "</span></div>";
} }
ruleHtml += "<div class='col col-md-12'><span class='badge badge-light'>То</span></div>" ruleHtml += "<div class='col col-md-12'><span class='badge badge-light'>То</span></div>"
for (let i = 0; i < consequentComponents.length; i++) { for (let i = 0; i < consequentComponents.length; i++) {
@ -87,11 +93,12 @@
ruleHtml += "<div class='col col-md-4'><span class='badge badge-success'>" + getVariableValue(c) + "</span></div>"; ruleHtml += "<div class='col col-md-4'><span class='badge badge-success'>" + getVariableValue(c) + "</span></div>";
} }
$(el).html(ruleHtml); $(el).html(ruleHtml);
$(el).removeClass('d-none');
} }
$('.rule').each(function(index) {
$('.rule').each(function (index) {
addRule(index, $(this), $(this).text()); addRule(index, $(this), $(this).text());
}); });
</script> </script>
</div> </div>

View File

@ -7,16 +7,19 @@
<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">
<div class="form-group">
<a href="/project/edit/0" class="btn btn-outline-dark"> <a href="/project/edit/0" class="btn btn-outline-dark">
<i class="fa fa-plus-square" aria-hidden="true">Добавить проект</i> <i class="bi bi-plus-square" aria-hidden="true"></i> Добавить проект
</a> </a>
</div>
<ul> <div th:each="p : ${projects}">
<li th:each="p : ${projects}">
<a th:href="@{'/project/edit/' + ${p.id}}"> <a th:href="@{'/project/edit/' + ${p.id}}">
<span th:text="${p.name} + ' от ' + ${#dates.format(p.createDate, 'dd.MM.yyyy HH:mm')}"></span> <h3>
<span class="badge badge-light"
th:text="${p.name} + ' от ' + ${#dates.format(p.createDate, 'dd.MM.yyyy HH:mm')}">
</span>
</h3>
</a> </a>
</li> </div>
</ul>
</div> </div>
</html> </html>

View File

@ -1,30 +0,0 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
<title>Список правил</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<div class="container" layout:fragment="content">
<h3> Список правил</h3>
<div class="rowд">
<div class="col col-md-12">
<span class="badge badge-light">1. Если</span>
</div>
<div class="col col-md-3">
<span class="badge badge-primary">Переменная</span>
</div>
<div class="col col-md-3">
<span class="badge badge-light">есть</span>
</div>
<div class="col col-md-3">
<span class="badge badge-success">значение</span>
</div>
<div class="col col-md-3">
<span class="badge badge-danger">И / ИЛИ</span>
</div>
</div>
</div>
</html>

View File

@ -50,15 +50,9 @@
</div> </div>
<script type="text/javascript" src="/js/fuzzyRule.js"></script> <script type="text/javascript" src="/js/fuzzyRule.js"></script>
<script type="text/javascript"> <script type="text/javascript">
let ruleContentEl = $('#ruleContent'); (async () => {
let projectIdEl = $('#projectId'); await initSelects();
if ($(ruleContentEl).val()) { })();
addAntecedentFromRule($('#rulesAntecedent'), $(projectIdEl).val(), $(ruleContentEl).val());
addConsequentFromRule($('#rulesConsequent'), $(projectIdEl).val(), $(ruleContentEl).val());
} else {
addAntecedent($('#rulesAntecedent'), $(projectIdEl).val());
addConsequent($('#rulesConsequent'), $(projectIdEl).val());
}
</script> </script>
</form> </form>
</div> </div>

View File

@ -39,8 +39,10 @@
<div class="row" th:each="t, iter : ${fuzzyTerms}"> <div class="row" th:each="t, iter : ${fuzzyTerms}">
<div class="col col-md-12"> <div class="col col-md-12">
<a th:href="@{'/fuzzyTerm/edit/' + ${projectId} + '/' + ${variableId}+'/'+${t.id}}"> <a th:href="@{'/fuzzyTerm/edit/' + ${projectId} + '/' + ${variableId}+'/'+${t.id}}">
<h3>
<span class="badge badge-light" <span class="badge badge-light"
th:text="${iter.index+1} + '. ' + ${t.description}"></span> th:text="${iter.index+1} + '. ' + ${t.description}"></span>
</h3>
</a> </a>
</div> </div>
</div> </div>