Compare commits

..

No commits in common. "377c879378930bba3a09276046731f6a7e09a644" and "0f5c8389d8efe77ef325247c9b1e1733c7bd5ea8" have entirely different histories.

7 changed files with 66 additions and 359 deletions

View File

@ -2,7 +2,6 @@ package ru.ulstu.fc.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
@ -11,8 +10,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import java.util.concurrent.TimeUnit;
@Configuration @Configuration
public class MvcConfiguration implements WebMvcConfigurer { public class MvcConfiguration implements WebMvcConfigurer {
@Override @Override
@ -26,11 +23,7 @@ public class MvcConfiguration implements WebMvcConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) { public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry registry
.addResourceHandler("/webjars/**") .addResourceHandler("/webjars/**")
.addResourceLocations("/webjars/") .addResourceLocations("/webjars/");
.setCacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS)
.noTransform()
.mustRevalidate());
;
} }
@Bean @Bean

View File

@ -28,7 +28,7 @@ public class SecurityConfiguration {
log.debug("Security enabled"); log.debug("Security enabled");
http http
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> .authorizeHttpRequests(auth ->
auth.requestMatchers("/").permitAll() auth.requestMatchers("/").permitAll()

View File

@ -1,6 +1,5 @@
package ru.ulstu.fc.rule.controller; package ru.ulstu.fc.rule.controller;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@ -9,29 +8,16 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; 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.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.Variable;
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.VariableService;
import java.util.List;
@Controller @Controller
@RequestMapping("rule") @RequestMapping("rule")
public class FuzzyRuleController { public class FuzzyRuleController {
private final FuzzyRuleService fuzzyRuleService; private final FuzzyRuleService ruleService;
private final VariableService variableService;
private final FuzzyTermService fuzzyTermService;
public FuzzyRuleController(FuzzyRuleService fuzzyRuleService, public FuzzyRuleController(FuzzyRuleService ruleService) {
VariableService variableService, this.ruleService = ruleService;
FuzzyTermService fuzzyTermService) {
this.fuzzyRuleService = fuzzyRuleService;
this.variableService = variableService;
this.fuzzyTermService = fuzzyTermService;
} }
@GetMapping("/edit/{projectId}/{ruleId}") @GetMapping("/edit/{projectId}/{ruleId}")
@ -40,7 +26,7 @@ public class FuzzyRuleController {
model.addAttribute("projectId", projectId); model.addAttribute("projectId", projectId);
model.addAttribute("fuzzyRuleForm", model.addAttribute("fuzzyRuleForm",
(id != null && id != 0) (id != null && id != 0)
? new FuzzyRuleForm(id, fuzzyRuleService.getById(id)) ? new FuzzyRuleForm(id, ruleService.getById(id))
: new FuzzyRuleForm(id, projectId)); : new FuzzyRuleForm(id, projectId));
return "rule/edit"; return "rule/edit";
} }
@ -51,32 +37,15 @@ public class FuzzyRuleController {
model.addAttribute("projectId", fuzzyRuleForm.getProjectId()); model.addAttribute("projectId", fuzzyRuleForm.getProjectId());
return "rule/edit"; return "rule/edit";
} }
fuzzyRuleService.save(fuzzyRuleForm); ruleService.save(fuzzyRuleForm);
return "redirect:/project/edit/" + fuzzyRuleForm.getProjectId(); return "redirect:/project/edit/" + fuzzyRuleForm.getProjectId();
} }
@PostMapping(value = "save", params = "delete") @PostMapping(value = "save", params = "delete")
public String delete(FuzzyRuleForm fuzzyRuleForm) { public String delete(FuzzyRuleForm fuzzyRuleForm) {
if (fuzzyRuleForm != null && fuzzyRuleForm.getId() != null) { if (fuzzyRuleForm != null && fuzzyRuleForm.getId() != null) {
fuzzyRuleService.delete(fuzzyRuleForm); ruleService.delete(fuzzyRuleForm);
} }
return "redirect:/project/edit/" + fuzzyRuleForm.getProjectId(); return "redirect:/project/edit/" + fuzzyRuleForm.getProjectId();
} }
@ResponseBody
@GetMapping("/getVariables/{projectId}")
public List<Variable> getVariables(@PathVariable("projectId") Integer projectId,
final HttpServletResponse response) {
response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform");
//TODO: return DTO without terms
return variableService.getAllByProject(projectId);
}
@ResponseBody
@GetMapping("/getFuzzyTerms/{variableId}")
public List<FuzzyTerm> getTerms(@PathVariable("variableId") Integer variableId,
final HttpServletResponse response) {
response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform");
return fuzzyTermService.getByVariableId(variableId);
}
} }

View File

@ -9,6 +9,7 @@ logging.level.javax.management.remote.rmi=off
logging.level.java.rmi.server=off logging.level.java.rmi.server=off
logging.level.org.apache.tomcat=INFO logging.level.org.apache.tomcat=INFO
logging.level.org.apache.tomcat.util.net=WARN logging.level.org.apache.tomcat.util.net=WARN
extractor.custom-projects-dir=
server.error.include-stacktrace=always server.error.include-stacktrace=always
server.error.include-exception=true server.error.include-exception=true
server.error.include-message=always server.error.include-message=always

View File

@ -1,253 +0,0 @@
// Rules parsing
// antecedent
function getAntecedent(rule) {
let withoutIf = rule.split('if');
return withoutIf[1].trim().split('then')[0].trim();
}
// TODO: remove duplicate
function getAntecedentComponents(antecedent) {
return antecedent.split('and').map((i) => i.trim());
}
// consequent
function getConsequent(rule) {
let withoutIf = rule.split('if');
return withoutIf[1].trim().split('then')[1].trim();
}
// TODO: remove duplicate
function getConsequentComponents(consequent) {
return consequent.split('and').map((i) => i.trim());
}
// common
function getVariable(variableComponents) {
return variableComponents.split('is')[0].trim();
}
function getVariableValue(variableComponents) {
return variableComponents.split('is')[1].trim();
}
// Rules creation
/* exported MessageTypesEnum */
let MessageTypesEnum = {
INFO: "info",
SUCCESS: "success",
WARNING: "warning",
DANGER: "danger"
};
Object.freeze(MessageTypesEnum);
function isEmpty(value) {
if (typeof value === "function") {
return false;
}
return (value == null || value.length === 0);
}
/* exported showFeedbackMessage */
function showFeedbackMessage(message, type) {
alert(type + ' ' + message);
}
/* exported errorHandler */
function errorHandler(response, callBack, errorCallBack) {
if (!isEmpty(response.error)) {
// TODO: add l10n
// showFeedbackMessage(response.error.code + ": " + response.error.message, MessageTypesEnum.DANGER);
if (!isEmpty(errorCallBack)) {
errorCallBack(response.data);
}
throw response.error.code + ": " + response.error.message +
" / Details: " + response.error.data;
}
if (!isEmpty(callBack)) {
callBack(response);
}
}
/* exported getFromRest */
function getFromRest(url, callBack) {
$.ajax({
url: url,
method: 'get',
cache: true,
dataType: 'json',
success: function (response) {
errorHandler(response, callBack);
}
});
}
/* exported createRule */
function createRule() {
let ruleString = "if ";
let inp = $('.selectpicker.inputVar').children(':selected').map(function () {
return $(this).text();
}).get();
let inpVal = $('.selectpicker.inputVal').children(':selected').map(function () {
return $(this).text();
}).get();
let out = $('.selectpicker.outVar').children(':selected').map(function () {
return $(this).text();
}).get();
let outVal = $('.selectpicker.outVal').children(':selected').map(function () {
return $(this).text();
}).get();
for (let i = 0; i < inp.length; i++) {
if (i > 0) {
ruleString += ' and ';
}
ruleString += inp[i] + " is " + inpVal[i];
}
ruleString += " then ";
for (let i = 0; i < out.length; i++) {
if (i > 0) {
ruleString += ' and ';
}
ruleString += out[i] + " is " + outVal[i];
}
$('#ruleContent').val(ruleString);
}
/* exported fillSelect */
function fillSelect(selectElement, values, selectedVal) {
$(selectElement).html("");
$.each(values, function (key, value) {
$(selectElement).append($("<option />").val(value.id).text(value.name));
});
$(selectElement).children().each(function () {
if ($(this).text() == selectedVal) {
$(this).prop('selected', true);
}
});
}
function fillFuzzyTerms(variablesElement, fuzzyTermsElement, termVal) {
getFromRest("/rule/getFuzzyTerms/" + $(variablesElement).val(), function (fuzzyTerms) {
let fuzzyTermsData = [];
$.each(fuzzyTerms, function (key, value) {
fuzzyTermsData.push({
id: value.id,
name: value.description
});
});
fillSelect(fuzzyTermsElement, fuzzyTermsData, termVal);
$(fuzzyTermsElement).selectpicker("refresh");
$(fuzzyTermsElement).trigger("change");
});
}
function fillVariables(projectId, variablesElement, variableVal) {
getFromRest("/rule/getVariables/" + projectId, function (variables) {
let variablesData = [];
$.each(variables, function (key, value) {
variablesData.push({
id: value.id,
name: value.name
});
});
fillSelect(variablesElement, variablesData, variableVal);
$(variablesElement).selectpicker("refresh");
$(variablesElement).trigger("change");
});
}
function variableValueChanged(variablesElement, fuzzyTermsElement) {
fillFuzzyTerms(variablesElement, fuzzyTermsElement);
}
function fuzzyTermsValueChanged() {
createRule();
}
function createVariableSelect(cls, projectId, variableVal) {
let variablesElement = $("<select class='selectpicker " + cls + " m-2' data-live-search='true data-width='70%'></select>");
fillVariables(projectId, variablesElement, variableVal);
return variablesElement;
}
function createFuzzyTermsSelect(cls, variablesElement, termVal) {
let fuzzyTermsElement = $("<select class='selectpicker " + cls + " m-2' data-live-search='true data-width='70%'></select>");
if ($(variablesElement).val()) {
fillFuzzyTerms(variablesElement, fuzzyTermsElement, termVal);
}
return fuzzyTermsElement;
}
function removeAntecedent(buttonElement) {
$(buttonElement).parent().remove();
fuzzyTermsValueChanged();
}
function removeConsequent(buttonElement) {
$(buttonElement).parent().remove();
//fuzzyTermsValueChanged();
}
function addAntecedent(parentElement, projectId, variableVal, termVal) {
let rowElement = $("<div class='row'></div>");
if ($(parentElement).find('.row').length) {
$(rowElement).append("<label class='col col-md-1 m-2'>И</label>");
} else {
$(rowElement).append("<label class='col col-md-1 m-2'> </label>");
}
let variablesElement = createVariableSelect('inputVar', projectId, variableVal);
let fuzzyTermsElement = createFuzzyTermsSelect('inputVal', variablesElement, termVal);
$(variablesElement).on("change", function () {
variableValueChanged(variablesElement, fuzzyTermsElement)
});
$(fuzzyTermsElement).on("change", function () {
fuzzyTermsValueChanged()
});
$(rowElement).append(variablesElement);
$(rowElement).append("<label class='col col-md-1 m-2'>есть</label>");
$(rowElement).append(fuzzyTermsElement);
if ($(parentElement).find('.row').length) {
$(rowElement).append("<a href='#' class='btn btn-outline-dark m-2' onclick='removeAntecedent($(this))'>-</a>");
}
$(parentElement).append(rowElement);
}
function addConsequent(parentElement, projectId, variableVal, termVal) {
let rowElement = $("<div class='row'></div>");
if ($(parentElement).find('.row').length) {
$(rowElement).append("<label class='col col-md-1 m-2'>И</label>");
} else {
$(rowElement).append("<label class='col col-md-1 m-2'> </label>");
}
let variablesElement = createVariableSelect('outVar', projectId, variableVal);
let fuzzyTermsElement = createFuzzyTermsSelect('outVal', variablesElement, termVal);
$(variablesElement).on("change", function () {
variableValueChanged(variablesElement, fuzzyTermsElement)
});
$(fuzzyTermsElement).on("change", function () {
fuzzyTermsValueChanged()
});
$(rowElement).append(variablesElement);
$(rowElement).append("<label class='col col-md-1 m-2'>есть</label>");
$(rowElement).append(fuzzyTermsElement);
if ($(parentElement).find('.row').length) {
$(rowElement).append("<a href='#' class='btn btn-outline-dark m-2' onclick='removeConsequent($(this))'>-</a>");
}
$(parentElement).append(rowElement);
}
function addAntecedentFromRule(parentElement, projectId, ruleContent) {
let antecedentComponents = getAntecedentComponents(getAntecedent(ruleContent));
for (let i = 0; i < antecedentComponents.length; i++) {
let a = antecedentComponents[i];
addAntecedent(parentElement, projectId, getVariable(a), getVariableValue(a));
}
}
function addConsequentFromRule(parentElement, projectId, ruleContent) {
let consequentComponents = getConsequentComponents(getConsequent(ruleContent));
for (let i = 0; i < consequentComponents.length; i++) {
let c = consequentComponents[i];
addConsequent(parentElement, projectId, getVariable(c), getVariableValue(c));
}
}

View File

@ -40,7 +40,7 @@
</div> </div>
</div> </div>
</div> </div>
<a th:href="@{'/variable/edit/' + ${projectId}+'/0'}" class="btn btn-outline-dark">Добавить переменную</a> <a th:href="@{'/variable/edit/' + ${projectId}+'/0'}" class="btn btn-outline-dark">Добавить преременную</a>
</div> </div>
<div class="col col-md-6"> <div class="col col-md-6">
<h4> Список правил</h4> <h4> Список правил</h4>
@ -57,38 +57,39 @@
class="btn btn-outline-dark">Добавить правило</a> class="btn btn-outline-dark">Добавить правило</a>
</div> </div>
</div> </div>
<script type="text/javascript" src="/js/fuzzyRule.js"></script> <script>
<script type="text/javascript"> function getAntecedent(rule) {
withoutIf = rule.split('if');
return withoutIf[1].trim().split('then')[0].trim();
}
function getAntecedentComponents(antecedent) {
return antecedent.split('and').map((i) => i.trim());
}
function getVariable(antecedent) {
return antecedent.split('is')[0].trim();
}
function getVariableValue(antecedent) {
return antecedent.split('is')[1].trim();
}
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>" ruleHtml = "<div class='col col-md-12'><span class='badge badge-light'>"+(index+1) +". Если</span></div>"
let antecedentComponents = getAntecedentComponents(getAntecedent(rule)); antecedentComponents = getAntecedentComponents(getAntecedent(rule));
let consequentComponents = getConsequentComponents(getConsequent(rule));
for (let i = 0; i < antecedentComponents.length; i++) { for (let i = 0; i < antecedentComponents.length; i++) {
let a = antecedentComponents[i]; a = antecedentComponents[i];
if (i > 0) { if (i > 0) {
ruleHtml += "<div class='col col-md-1'><span class='badge badge-danger'>И</span></div>"; ruleHtml += "<div class='col col-md-12'><span class='badge badge-danger'>И</span></div>";
} else {
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-2 offset-md-1'><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-2'><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-2'><span class='badge badge-success'>"+getVariableValue(a)+"</span></div>";
}
ruleHtml += "<div class='col col-md-12'><span class='badge badge-light'>То</span></div>"
for (let i = 0; i < consequentComponents.length; i++) {
let c = consequentComponents[i];
if (i > 0) {
ruleHtml += "<div class='col col-md-1'><span class='badge badge-danger'>И</span></div>";
} else {
ruleHtml += "<div class='col col-md-1'></div>";
}
ruleHtml += "<div class='col col-md-4'><span class='badge badge-primary'>" + getVariable(c) + "</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(c) + "</span></div>";
} }
$(el).html(ruleHtml); $(el).html(ruleHtml);
} }
$('.rule').each(function(index) { $('.rule').each(function(index ) {
addRule(index, $(this), $(this).text()); addRule(index, $(this), $(this).text());
}); });

View File

@ -5,12 +5,11 @@
<head> <head>
<title>Редактирование правила</title> <title>Редактирование правила</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script></script>
</head> </head>
<div class="container" layout:fragment="content"> <div class="container" layout:fragment="content">
<h3>Редактирование правила:</h3> <h3>Редактирование правила:</h3>
<form th:action="@{/rule/save}" th:object="${fuzzyRuleForm}" method="post"> <form th:action="@{/rule/save}" th:object="${fuzzyRuleForm}" method="post">
<input type="hidden" id="projectId" th:field="*{projectId}"> <input type="hidden" th:field="*{projectId}">
<input type="hidden" th:field="*{id}"> <input type="hidden" th:field="*{id}">
<div class="form-group"> <div class="form-group">
<label for="ruleContent">Правило</label> <label for="ruleContent">Правило</label>
@ -26,41 +25,38 @@
</p> </p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col col-md-1 m-2">Если</label> <label class="col col-md-1">Если</label>
<div id="rulesAntecedent"></div> <select id="select-variable" class="selectpicker m-2" data-live-search="true" data-width="70%">
<div> <option>Скорость</option>
<a href="#" class="btn btn-outline-dark" </select>
th:onclick="addAntecedent($('#rulesAntecedent'), $('#projectId').val());">+</a>
</div>
<label class="col col-md-1 m-2">То</label>
<div id="rulesConsequent"></div>
<div>
<a href="#" class="btn btn-outline-dark"
th:onclick="addConsequent($('#rulesConsequent'), $('#projectId').val());">+</a>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button name="save" type="submit" class="btn btn-outline-dark">Сохранить</button> <label class="col col-md-1">есть</label>
<button name="delete" <select id="select-val" class="selectpicker m-2" data-live-search="true" data-width="70%">
type="submit" <option>Высокая</option>
class="btn btn-outline-dark" </select>
onclick="return confirm('Удалить запись?')">
Удалить
</button>
<a th:href="@{'/project/edit/' + ${projectId}}" class="btn btn-outline-dark">Отмена</a>
</div> </div>
<script type="text/javascript" src="/js/fuzzyRule.js"></script> <div class="form-group">
<script type="text/javascript"> <label class="col col-md-1">то</label>
let ruleContentEl = $('#ruleContent'); <select id="select-val1" class="selectpicker m-2" data-live-search="true" data-width="70%">
let projectIdEl = $('#projectId'); <option>Действие</option>
if ($(ruleContentEl).val()) { </select>
addAntecedentFromRule($('#rulesAntecedent'), $(projectIdEl).val(), $(ruleContentEl).val()); </div>
addConsequentFromRule($('#rulesConsequent'), $(projectIdEl).val(), $(ruleContentEl).val()); <div class="form-group">
} else { <label class="col col-md-1">есть</label>
addAntecedent($('#rulesAntecedent'), $(projectIdEl).val()); <select id="select-val4" class="selectpicker m-2" data-live-search="true" data-width="70%">
addConsequent($('#rulesConsequent'), $(projectIdEl).val()); <option>Сливать воду</option>
} </select>
</script> </div>
<button name="save" type="submit" class="btn btn-outline-dark">Сохранить</button>
<button name="delete"
type="submit"
class="btn btn-outline-dark"
onclick="return confirm('Удалить запись?')">
Удалить
</button>
<a th:href="@{'/project/edit/' + ${projectId}}" class="btn btn-outline-dark">Отмена</a>
</form> </form>
</div> </div>
</html> </html>