Revize 8808bbe4
Přidáno uživatelem Ondřej Váně před asi 4 roky(ů)
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/controller/AppController.java | ||
---|---|---|
82 | 82 |
public String about() { |
83 | 83 |
return "about"; |
84 | 84 |
} |
85 |
|
|
86 |
@GetMapping("/configuration") |
|
87 |
public String configuration(Model model) { |
|
88 |
model.addAttribute("antiPatterns", antiPatternService.antiPatternsToModel(antiPatternService.getAllAntiPatterns())); |
|
89 |
return "configuration"; |
|
90 |
} |
|
91 |
|
|
92 |
@PostMapping("/configuration") |
|
93 |
public String configurationPost(Model model, |
|
94 |
@RequestParam(value = "configValues", required = false) String[] configValues, |
|
95 |
@RequestParam(value = "configNames", required = false) String[] configNames) { |
|
96 |
|
|
97 |
if (antiPatternService.saveNewConfiguration(configNames, configValues)) { |
|
98 |
model.addAttribute("successMessage", "All threshold values has been successfully saved."); |
|
99 |
} else { |
|
100 |
model.addAttribute("errorMessage", "One or more configuration values are not in correct format"); |
|
101 |
} |
|
102 |
|
|
103 |
model.addAttribute("antiPatterns", antiPatternService.antiPatternsToModel(antiPatternService.getAllAntiPatterns())); |
|
104 |
|
|
105 |
return "configuration"; |
|
106 |
} |
|
85 | 107 |
} |
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/TooLongSprintDetectorImpl.java | ||
---|---|---|
1 | 1 |
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors; |
2 | 2 |
|
3 | 3 |
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection; |
4 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.AntiPattern; |
|
5 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project; |
|
6 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.QueryResultItem; |
|
7 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.ResultDetail; |
|
4 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.*; |
|
8 | 5 |
import org.slf4j.Logger; |
9 | 6 |
import org.slf4j.LoggerFactory; |
10 | 7 |
|
11 | 8 |
import java.util.ArrayList; |
9 |
import java.util.HashMap; |
|
12 | 10 |
import java.util.List; |
13 | 11 |
import java.util.Map; |
14 | 12 |
|
... | ... | |
23 | 21 |
"maximum 3 weeks). It could also be detected here if the length " + |
24 | 22 |
"of the iteration does not change often (It can change at the " + |
25 | 23 |
"beginning and at the end of the project, but it should not " + |
26 |
"change in the already started project)."); |
|
24 |
"change in the already started project).", |
|
25 |
new HashMap<>() {{ |
|
26 |
put("maxIterationLength", new Configuration<Integer>("maxIterationLength", |
|
27 |
"Max Iteration Length", |
|
28 |
"Maximum iteration length in days", 21)); |
|
29 |
put("maxNumberOfTooLongIterations", new Configuration<Integer>("maxNumberOfTooLongIterations", |
|
30 |
"Max number of foo long iterations", |
|
31 |
"Maximum number of too long iterations in project", 1)); |
|
32 |
}} |
|
33 |
); |
|
27 | 34 |
|
28 | 35 |
private final String SQL_FILE_NAME = "too_long_sprint.sql"; |
29 | 36 |
// sql queries loaded from sql file |
30 | 37 |
private List<String> sqlQueries; |
31 | 38 |
|
32 |
/** |
|
33 |
* SETTINGS |
|
34 |
*/ |
|
35 |
private static final int MAX_NUMBER_OF_TOO_LONG_ITERATIONS = 1; |
|
36 |
private static final int MAX_ITERATION_LENGTH = 21; |
|
37 |
|
|
38 |
|
|
39 | 39 |
@Override |
40 | 40 |
public AntiPattern getAntiPatternModel() { |
41 | 41 |
return this.antiPattern; |
... | ... | |
51 | 51 |
this.sqlQueries = queries; |
52 | 52 |
} |
53 | 53 |
|
54 |
private Integer getMaxIterationLength(){ |
|
55 |
return (Integer) this.antiPattern.getConfigurations().get("maxIterationLength").getValue(); |
|
56 |
} |
|
57 |
|
|
58 |
private Integer getMaxNumberOfTooLongIterations(){ |
|
59 |
return (Integer) this.antiPattern.getConfigurations().get("maxNumberOfTooLongIterations").getValue(); |
|
60 |
} |
|
61 |
|
|
54 | 62 |
/** |
55 | 63 |
* Postup detekce: |
56 | 64 |
* 1) najít všechny iterace danného projektu |
57 |
* 2) odebrat první a poslední iteraci( ty mohou být z důvodu nastartování projektu dlouhé) |
|
65 |
* 2) odebrat první a poslední iteraci ( ty mohou být z důvodu nastartování projektu dlouhé)
|
|
58 | 66 |
* 3) zjistit jejich délku (rozdíl mezi start date a end date) |
59 | 67 |
* 4) pokud iterace přesháne délku 21 dní (3 týdny), tak jsou označeny jako moc dlouhé |
60 | 68 |
* 5) pokud je nalezena jedna nebo více iterací jako dlouhé, tak je anti pattern detekován |
... | ... | |
66 | 74 |
@Override |
67 | 75 |
public QueryResultItem analyze(Project project, DatabaseConnection databaseConnection) { |
68 | 76 |
|
77 |
// get configuration |
|
78 |
int maxIterationLength = getMaxIterationLength(); |
|
79 |
int maxNumberOfTooLongIterations = getMaxNumberOfTooLongIterations(); |
|
80 |
|
|
81 |
// auxiliary variables |
|
69 | 82 |
int numberOfLongIterations = 0; |
70 | 83 |
int totalCountOfIteration = 0; |
71 | 84 |
|
... | ... | |
77 | 90 |
if (!iterationLengths.containsKey("iterationLength") || iterationLengths.get("iterationLength") == null) |
78 | 91 |
continue; |
79 | 92 |
int iterationLength = (int) iterationLengths.get("iterationLength"); |
80 |
if (iterationLength > MAX_ITERATION_LENGTH) {
|
|
93 |
if (iterationLength > maxIterationLength) {
|
|
81 | 94 |
numberOfLongIterations++; |
82 | 95 |
} |
83 | 96 |
} |
84 | 97 |
List<ResultDetail> resultDetails = new ArrayList<>(); |
85 | 98 |
resultDetails.add(new ResultDetail("Count of iterations without first and last", String.valueOf(totalCountOfIteration))); |
86 | 99 |
resultDetails.add(new ResultDetail("Number of too long iterations", String.valueOf(numberOfLongIterations))); |
87 |
if (numberOfLongIterations >= MAX_NUMBER_OF_TOO_LONG_ITERATIONS) {
|
|
100 |
if (numberOfLongIterations >= maxNumberOfTooLongIterations) {
|
|
88 | 101 |
resultDetails.add(new ResultDetail("Conclusion", "One or more iteration is too long")); |
89 | 102 |
} else { |
90 | 103 |
resultDetails.add(new ResultDetail("Conclusion", "All iterations in limit")); |
... | ... | |
93 | 106 |
LOGGER.info(this.antiPattern.getPrintName()); |
94 | 107 |
LOGGER.info(resultDetails.toString()); |
95 | 108 |
|
96 |
return new QueryResultItem(this.antiPattern, numberOfLongIterations >= 1, resultDetails);
|
|
109 |
return new QueryResultItem(this.antiPattern, numberOfLongIterations >= maxNumberOfTooLongIterations, resultDetails);
|
|
97 | 110 |
} |
98 | 111 |
} |
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/AntiPattern.java | ||
---|---|---|
1 | 1 |
package cz.zcu.fav.kiv.antipatterndetectionapp.model; |
2 | 2 |
|
3 |
import java.util.Map; |
|
4 |
|
|
3 | 5 |
public class AntiPattern { |
4 | 6 |
|
5 | 7 |
private Long id; |
6 | 8 |
private String printName; |
7 | 9 |
private String name; |
8 | 10 |
private String description; |
11 |
private Map<String, Configuration> configurations; |
|
9 | 12 |
|
10 | 13 |
public AntiPattern(Long id, String printName, String name, String description) { |
11 | 14 |
this.id = id; |
... | ... | |
14 | 17 |
this.description = description; |
15 | 18 |
} |
16 | 19 |
|
20 |
public AntiPattern(Long id, String printName, String name, String description, Map<String, Configuration> configurations) { |
|
21 |
this.id = id; |
|
22 |
this.printName = printName; |
|
23 |
this.name = name; |
|
24 |
this.description = description; |
|
25 |
this.configurations = configurations; |
|
26 |
} |
|
27 |
|
|
17 | 28 |
public Long getId() { |
18 | 29 |
return id; |
19 | 30 |
} |
... | ... | |
46 | 57 |
this.description = description; |
47 | 58 |
} |
48 | 59 |
|
60 |
public Map<String, Configuration> getConfigurations() { |
|
61 |
return configurations; |
|
62 |
} |
|
63 |
|
|
64 |
public void setConfigurations(Map<String, Configuration> configurations) { |
|
65 |
this.configurations = configurations; |
|
66 |
} |
|
67 |
|
|
49 | 68 |
@Override |
50 | 69 |
public String toString() { |
51 | 70 |
return "AntiPattern{" + |
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/Configuration.java | ||
---|---|---|
1 |
package cz.zcu.fav.kiv.antipatterndetectionapp.model; |
|
2 |
|
|
3 |
public class Configuration<T> { |
|
4 |
private String name; |
|
5 |
private String printName; |
|
6 |
private String description; |
|
7 |
private T value; |
|
8 |
|
|
9 |
public Configuration(String name, String printName, String description, T value) { |
|
10 |
this.name = name; |
|
11 |
this.printName = printName; |
|
12 |
this.description = description; |
|
13 |
this.value = value; |
|
14 |
} |
|
15 |
|
|
16 |
public String getName() { |
|
17 |
return name; |
|
18 |
} |
|
19 |
|
|
20 |
public void setName(String name) { |
|
21 |
this.name = name; |
|
22 |
} |
|
23 |
|
|
24 |
public String getPrintName() { |
|
25 |
return printName; |
|
26 |
} |
|
27 |
|
|
28 |
public void setPrintName(String printName) { |
|
29 |
this.printName = printName; |
|
30 |
} |
|
31 |
|
|
32 |
public String getDescription() { |
|
33 |
return description; |
|
34 |
} |
|
35 |
|
|
36 |
public void setDescription(String description) { |
|
37 |
this.description = description; |
|
38 |
} |
|
39 |
|
|
40 |
public T getValue() { |
|
41 |
return value; |
|
42 |
} |
|
43 |
|
|
44 |
public void setValue(T value) { |
|
45 |
this.value = value; |
|
46 |
} |
|
47 |
} |
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternService.java | ||
---|---|---|
17 | 17 |
|
18 | 18 |
List<AntiPatternDetector> getAllAntiPatternsForGivenIds(Long[] ids); |
19 | 19 |
|
20 |
|
|
20 |
boolean saveNewConfiguration(String[] configNames, String[] configValues); |
|
21 | 21 |
} |
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/service/AntiPatternServiceImpl.java | ||
---|---|---|
50 | 50 |
} |
51 | 51 |
return antiPatternDetectors; |
52 | 52 |
} |
53 |
|
|
54 |
@Override |
|
55 |
public boolean saveNewConfiguration(String[] configNames, String[] configValues) { |
|
56 |
List<AntiPatternDetector> antiPatternDetectors = antiPatternRepository.getAllAntiPatterns(); |
|
57 |
|
|
58 |
for (AntiPatternDetector antiPatternDetector : antiPatternDetectors) { |
|
59 |
// not every anti-pattern should have configuration |
|
60 |
if (antiPatternDetector.getAntiPatternModel().getConfigurations() == null) { |
|
61 |
continue; |
|
62 |
} |
|
63 |
for (int i = 0; i < configNames.length; i++) { |
|
64 |
if (antiPatternDetector.getAntiPatternModel().getConfigurations().containsKey(configNames[i])) { |
|
65 |
|
|
66 |
if (antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).getValue().getClass() == Integer.class) { |
|
67 |
try { |
|
68 |
antiPatternDetector.getAntiPatternModel().getConfigurations().get(configNames[i]).setValue((Integer.parseInt(configValues[i]))); |
|
69 |
} catch (NumberFormatException e) { |
|
70 |
return false; |
|
71 |
} |
|
72 |
|
|
73 |
} |
|
74 |
|
|
75 |
|
|
76 |
} |
|
77 |
} |
|
78 |
} |
|
79 |
return true; |
|
80 |
} |
|
53 | 81 |
} |
src/main/webapp/WEB-INF/templates/about.html | ||
---|---|---|
21 | 21 |
<br> |
22 | 22 |
<strong>Author:</strong> Ondřej Váně |
23 | 23 |
<br> |
24 |
<strong>E-mail:</strong> <a href="mailto:onry.vane@gmail.com">onry.vane@gmail.com</a>
|
|
24 |
<strong>E-mail:</strong> <a href="mailto:vaneo@students.zcu.cz">vaneo@students.zcu.cz</a>
|
|
25 | 25 |
</p> |
26 | 26 |
<p> |
27 | 27 |
This application is used to detect presence of anti-patterns in project management tools data. Seven selected anti-patterns are implemented in this application. |
src/main/webapp/WEB-INF/templates/configuration.html | ||
---|---|---|
1 |
<!DOCTYPE HTML> |
|
2 |
<html xmlns:th="http://www.thymeleaf.org"> |
|
3 |
<head> |
|
4 |
<title>Anti Pattern Detector - Configuration</title> |
|
5 |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> |
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|
7 |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> |
|
8 |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> |
|
9 |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> |
|
10 |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> |
|
11 |
|
|
12 |
<style> |
|
13 |
.analyze-button-container { |
|
14 |
margin: 20px 40% 40px; |
|
15 |
} |
|
16 |
</style> |
|
17 |
|
|
18 |
</head> |
|
19 |
<body> |
|
20 |
<!-- Navigation bar imported --> |
|
21 |
<div th:replace="fragments/navbar :: navBar"></div> |
|
22 |
<!-- ./Navigation bar imported --> |
|
23 |
|
|
24 |
<!-- Page body --> |
|
25 |
<div class="container"> |
|
26 |
<h1>Configuration</h1> |
|
27 |
|
|
28 |
<!-- Form for configuration values --> |
|
29 |
<form action="#" th:action="@{/configuration}" th:object="${antiPatterns}" method="post"> |
|
30 |
<div th:each="antiPattern : ${antiPatterns}"> |
|
31 |
<h3 th:text="${antiPattern.printName}"></h3> |
|
32 |
<div th:each="config : ${antiPattern.configurations}"> |
|
33 |
<div class="form-group row"> |
|
34 |
<label th:text="${config.value.printName}" th:for="${config.value.name}" |
|
35 |
class="col-sm-5 col-form-label"></label> |
|
36 |
<div class="col-sm-5"> |
|
37 |
<input th:value="${config.value.value}" type="number" class="form-control" th:id="${config.value.name}" |
|
38 |
name="configValues"> |
|
39 |
<input th:value="${config.value.name}" style="display: none" class="form-control" |
|
40 |
name="configNames"> |
|
41 |
<small th:text="${config.value.description}" th:value="${config.value.name}" |
|
42 |
class="form-text text-muted"></small> |
|
43 |
</div> |
|
44 |
</div> |
|
45 |
|
|
46 |
</div> |
|
47 |
</div> |
|
48 |
<!-- Container for show error message --> |
|
49 |
<div class="container"> |
|
50 |
<div th:if="${errorMessage}" th:text="${errorMessage}" class="alert alert-danger" role="alert"> |
|
51 |
</div> |
|
52 |
</div> |
|
53 |
<!-- ./Container for show error message --> |
|
54 |
<!-- Container for show success message --> |
|
55 |
<div class="container"> |
|
56 |
<div th:if="${successMessage}" th:text="${successMessage}" class="alert alert-success" role="alert"> |
|
57 |
</div> |
|
58 |
</div> |
|
59 |
<!-- ./Container for show success message --> |
|
60 |
<div class="analyze-button-container"> |
|
61 |
<button type="submit" class="btn btn-primary btn-lg btn-block">Save</button> |
|
62 |
</div> |
|
63 |
</form> |
|
64 |
<!-- ./Form for configuration values --> |
|
65 |
</div> |
|
66 |
<!-- ./Page body --> |
|
67 |
|
|
68 |
</body> |
|
69 |
</html> |
|
70 |
|
src/main/webapp/WEB-INF/templates/fragments/navbar.html | ||
---|---|---|
9 | 9 |
<li class="nav-item active"> |
10 | 10 |
<a class="nav-link" th:href="@{/}">Home</a> |
11 | 11 |
</li> |
12 |
<li class="nav-item active"> |
|
13 |
<a class="nav-link" th:href="@{/configuration}">Configuration</a> |
|
14 |
</li> |
|
12 | 15 |
<li class="nav-item active"> |
13 | 16 |
<a class="nav-link" th:href="@{/about}">About</a> |
14 | 17 |
</li> |
Také k dispozici: Unified diff
Implemented changable configuration by UI
- values extracted only for too long sprint anti pattern