Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 2964b75c

Přidáno uživatelem Ondřej Váně před asi 4 roky(ů)

Migration project from local storage to git

Zobrazit rozdíly:

pom.xml
20 20
	<dependencies>
21 21
		<dependency>
22 22
			<groupId>org.springframework.boot</groupId>
23
			<artifactId>spring-boot-starter-web</artifactId>
23
			<artifactId>spring-boot-starter</artifactId>
24 24
		</dependency>
25 25

  
26 26
		<dependency>
27 27
			<groupId>org.springframework.boot</groupId>
28
			<artifactId>spring-boot-starter-tomcat</artifactId>
29
			<scope>provided</scope>
28
			<artifactId>spring-boot-starter-test</artifactId>
29
			<scope>test</scope>
30 30
		</dependency>
31 31
		<dependency>
32 32
			<groupId>org.springframework.boot</groupId>
33
			<artifactId>spring-boot-starter-test</artifactId>
34
			<scope>test</scope>
33
			<artifactId>spring-boot-starter-web</artifactId>
34
		</dependency>
35
		<dependency>
36
			<groupId>mysql</groupId>
37
			<artifactId>mysql-connector-java</artifactId>
38
			<scope>runtime</scope>
39
		</dependency>
40
		<dependency>
41
			<groupId>org.springframework.boot</groupId>
42
			<artifactId>spring-boot-starter-data-jpa</artifactId>
43
		</dependency>
44
		<dependency>
45
			<groupId>org.springframework.boot</groupId>
46
			<artifactId>spring-boot-starter-web</artifactId>
47
		</dependency>
48
		<dependency>
49
			<groupId>org.springframework.boot</groupId>
50
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
35 51
		</dependency>
36 52
	</dependencies>
37 53

  
......
42 58
				<artifactId>spring-boot-maven-plugin</artifactId>
43 59
			</plugin>
44 60
		</plugins>
61
		<resources>
62
			<resource>
63
				<directory>src/main/java/resources</directory>
64
				<includes>
65
					<include>queries/*.sql</include>
66
				</includes>
67
			</resource>
68
		</resources>
45 69
	</build>
46 70

  
47 71
</project>
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/Utils.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp;
2

  
3
import java.io.BufferedReader;
4
import java.io.IOException;
5
import java.io.InputStream;
6
import java.io.InputStreamReader;
7
import java.sql.Date;
8
import java.time.LocalDate;
9
import java.time.temporal.ChronoUnit;
10
import java.util.ArrayList;
11
import java.util.List;
12

  
13
public class Utils {
14

  
15
    public static String bindValues(String query, List<String> parameters) {
16
        for (String parameter : parameters) {
17
            query = query.replace("?", parameter);
18
        }
19
        return query;
20
    }
21

  
22
    public static List<String> loadQueryFromFile(String fileName) {
23
        List<String> queries = new ArrayList<>();
24
        InputStream is = Utils.class.getClassLoader().getResourceAsStream(fileName);
25
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
26
        try {
27
            while (reader.ready()) {
28

  
29
                String line = reader.readLine();
30
                if (line.startsWith("select") || line.startsWith("set")) {
31
                    queries.add(line);
32
                }
33

  
34
            }
35
        } catch (IOException e) {
36
            e.printStackTrace();
37
        }
38
        return queries;
39
    }
40

  
41
    public static long daysBetween(Date firstDate, Date secondDate) {
42
        //24-May-2017, change this to your desired Start Date
43
        LocalDate dateBefore = firstDate.toLocalDate();
44
        //29-July-2017, change this to your desired End Date
45
        LocalDate dateAfter = secondDate.toLocalDate();
46
        return ChronoUnit.DAYS.between(dateBefore, dateAfter);
47
    }
48
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/controller/AppController.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.controller;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.AntiPatternManager;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.AntiPatternsEnum;
6
import cz.zcu.fav.kiv.antipatterndetectionapp.model.AntiPattern;
7
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
8
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Query;
9
import cz.zcu.fav.kiv.antipatterndetectionapp.repository.ProjectRepository;
10
import org.springframework.beans.factory.annotation.Autowired;
11
import org.springframework.stereotype.Controller;
12
import org.springframework.ui.Model;
13
import org.springframework.web.bind.annotation.*;
14

  
15
import java.util.ArrayList;
16
import java.util.List;
17
import java.util.Optional;
18

  
19
@Controller
20
public class AppController {
21

  
22
    @Autowired
23
    private ProjectRepository projectRepository;
24

  
25
    @Autowired
26
    private AntiPatternManager antiPatternManager;
27

  
28
    @GetMapping("/")
29
    public String index(Model model) {
30
        model.addAttribute("query", createQuery());
31
        return "index";
32
    }
33

  
34
    @GetMapping("/projects/{id}")
35
    public String getProjectById(@PathVariable Long id, Model model) {
36
        Optional<Project> foundProject = projectRepository.findById(id);
37
        if (foundProject.isPresent()) {
38
            Project project = foundProject.get();
39
            model.addAttribute("project", project);
40
        }
41
        return "project";
42
    }
43

  
44
    @GetMapping("/anti-patterns")
45
    public @ResponseBody
46
    List<AntiPattern> getAllAntiPatterns() {
47
        List<AntiPattern> antiPatterns = new ArrayList<>();
48
        for (AntiPatternsEnum antiPatternEnum : AntiPatternsEnum.values()) {
49
            AntiPattern antiPattern = new AntiPattern(antiPatternEnum.id, antiPatternEnum.printName, antiPatternEnum.name, antiPatternEnum.description);
50
            antiPatterns.add(antiPattern);
51
        }
52
        return antiPatterns;
53
    }
54

  
55
    @GetMapping("/anti-patterns/{id}")
56
    public String getAntiPatternById(@PathVariable Long id, Model model) {
57
        for (AntiPatternsEnum antiPatternEnum : AntiPatternsEnum.values()) {
58
            if (antiPatternEnum.id.equals(id)) {
59
                model.addAttribute("antiPattern", new AntiPattern(antiPatternEnum.id, antiPatternEnum.printName, antiPatternEnum.name, antiPatternEnum.description));
60
            }
61
        }
62
        return "anti-pattern";
63
    }
64

  
65
    @PostMapping("/analyze")
66
    public String analyze(Model model,
67
                          @RequestParam(value = "selectedProjects", required = false) String[] selectedProjects,
68
                          @RequestParam(value = "selectedAntiPatterns", required = false) String[] selectedAntiPatterns
69
    ) {
70

  
71
        if (selectedProjects == null) {
72
            model.addAttribute("errorMessage", "No project selected." +
73
                    " Select at least one project.");
74
            model.addAttribute("query", createQuery());
75
            return "index";
76
        }
77

  
78
        if (selectedAntiPatterns == null) {
79
            model.addAttribute("errorMessage", "No anti-pattern selected." +
80
                    " Select at least one anti-pattern.");
81
            model.addAttribute("query", createQuery());
82
            return "index";
83
        }
84

  
85
        model.addAttribute("queryResults", antiPatternManager.analyze(createQueryToAnalyze(selectedProjects, selectedAntiPatterns)));
86

  
87
        return "result";
88
    }
89

  
90
    @GetMapping("/about")
91
    public String about() {
92
        return "about";
93
    }
94

  
95
    private Query createQuery() {
96
        List<AntiPattern> antiPatterns = new ArrayList<>();
97
        for (AntiPatternsEnum antiPatternEnum : AntiPatternsEnum.values()) {
98
            AntiPattern antiPattern = new AntiPattern(antiPatternEnum.id, antiPatternEnum.printName, antiPatternEnum.name, antiPatternEnum.description);
99
            antiPatterns.add(antiPattern);
100
        }
101
        List<Project> projects = new ArrayList<>();
102
        for (Project project : projectRepository.findAll()) {
103
            projects.add(project);
104
        }
105

  
106
        return new Query(projects, antiPatterns);
107
    }
108

  
109
    private Query createQueryToAnalyze(String[] selectedProjects, String[] selectedAntiPatterns) {
110
        List<Project> projects = new ArrayList<>();
111
        for (String selectedProject : selectedProjects) {
112
            Optional<Project> project = projectRepository.findById(Long.parseLong(selectedProject));
113
            project.ifPresent(projects::add);
114
        }
115

  
116
        List<AntiPattern> antiPatterns = new ArrayList<>();
117
        for (String selectedAntiPattern : selectedAntiPatterns) {
118
            for (AntiPatternsEnum antiPatternEnum : AntiPatternsEnum.values()) {
119
                if (antiPatternEnum.id.equals(Long.parseLong(selectedAntiPattern))) {
120
                    antiPatterns.add(new AntiPattern(antiPatternEnum.id, antiPatternEnum.printName, antiPatternEnum.name, antiPatternEnum.description));
121
                }
122
            }
123
        }
124

  
125
        return new Query(projects, antiPatterns);
126
    }
127
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/AntiPatternManager.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Query;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.model.QueryResult;
6

  
7
import java.util.List;
8

  
9
public interface AntiPatternManager  {
10
    List<QueryResult> analyze(Query query);
11
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/AntiPatternManagerImpl.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors.*;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.model.*;
6
import org.springframework.stereotype.Service;
7

  
8
import java.util.ArrayList;
9
import java.util.List;
10

  
11
@Service
12
public class AntiPatternManagerImpl implements AntiPatternManager {
13

  
14
    public List<QueryResult> analyze(Query query) {
15
        DatabaseConnection databaseConnection = new DatabaseConnection();
16

  
17
        List<QueryResult> queryResults = new ArrayList<>();
18

  
19

  
20
        for (Project project : query.getProjects()) {
21
            QueryResult queryResult = new QueryResult();
22
            queryResult.setProject(project);
23
            List<QueryResultItem> queryResultItems = new ArrayList<>();
24
            for (AntiPattern antiPattern : query.getAntiPatterns()) {
25
                QueryResultItem queryResultItem = new QueryResultItem();
26
                queryResultItem.setAntiPattern(antiPattern);
27
                queryResultItem.setDetected(getAntiPatternService(antiPattern).analyze(project, databaseConnection));
28
                queryResultItems.add(queryResultItem);
29
            }
30
            queryResult.setQueryResultItems(queryResultItems);
31
            queryResults.add(queryResult);
32

  
33
        }
34

  
35
        databaseConnection.closeConnection();
36

  
37
        return queryResults;
38
    }
39

  
40
    private AntiPatternDetector getAntiPatternService(AntiPattern antiPattern) {
41
        switch (antiPattern.getName()) {
42
            case "TooLongSprint":
43
                return new TooLongSprintDetectorDetector();
44
            case "BusinessAsUsual":
45
                return new BusinessAsUsualDetector();
46
            case "VaryingSprintLength":
47
                return new VaryingSprintLengthDetector();
48
            case "IndifferentSpecialist":
49
                return new IndifferentSpecialistDetector();
50
            case "LongOrNonExistentFeedbackLoops":
51
                return new LongOrNonExistentFeedbackLoopsDetector();
52
            case "RoadToNowhere":
53
                return new RoadToNowhereDetector();
54
            case "SpecifyNothing":
55
                return new SpecifyNothingDetector();
56
        }
57
        return null;
58
    }
59
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/AntiPatternsEnum.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting;
2

  
3
public enum AntiPatternsEnum {
4

  
5
    TooLongSprint(1L,
6
            "TooLongSprint",
7
            "Too Long Sprint - DONE",
8
            "Iterations too long. (ideal iteration length is about 1-2 weeks, " +
9
                    "maximum 3 weeks). It could also be detected here if the length " +
10
                    "of the iteration does not change often (It can change at the " +
11
                    "beginning and at the end of the project, but it should not " +
12
                    "change in the already started project)."),
13

  
14
    VaryingSprintLength(2L,
15
            "VaryingSprintLength",
16
            "Varying Sprint Length - DONE",
17
            "The length of the sprint changes very often. " +
18
                    "It is clear that iterations will be different " +
19
                    "lengths at the beginning and end of the project, " +
20
                    "but the length of the sprint should not change " +
21
                    "during the project."),
22

  
23
    RoadToNowhere(3L,
24
            "RoadToNowhere",
25
            "Road To Nowhere - DONE",
26
            "The project is not sufficiently planned and therefore " +
27
                    "takes place on an ad hoc basis with an uncertain " +
28
                    "outcome and deadline. There is no project plan in the project."),
29

  
30
    SpecifyNothing(4L,
31
            "SpecifyNothing",
32
            "Specify Nothing - DONE",
33
            "The specification is not done intentionally. Programmers are " +
34
                    "expected to work better without written specifications."),
35

  
36
    BusinessAsUsual(5L,
37
            "BusinessAsUsual",
38
            "Business As Usual - DONE",
39
            "Absence of a retrospective after individual " +
40
                    "iterations or after the completion project."),
41

  
42
    IndifferentSpecialist(6L,
43
            "IndifferentSpecialist",
44
            "Indifferent specialist",
45
            "A team member who is a specialist in just one thing and does not want to learn new things. " +
46
                    "He refuses to learn new things outside of his specialization. It often disparages other " +
47
                    "\"technologies\". They reduce the seriousness of things that do not specialize in his specialization."),
48

  
49
    LongOrNonExistentFeedbackLoops(7L,
50
            "LongOrNonExistentFeedbackLoops",
51
            "Long Or Non-Existent Feedback Loops",
52
            "Long spacings between customer feedback or no feedback. The customer " +
53
                    "enters the project and sees the final result. In the end, the customer " +
54
                    "may not get what he really wanted. With long intervals of feedback, " +
55
                    "some misunderstood functionality can be created and we have to spend " +
56
                    "a lot of effort and time to redo it. ");
57

  
58

  
59

  
60

  
61
    public final Long id;
62
    public final String name;
63
    public final String printName;
64
    public final String description;
65

  
66
    AntiPatternsEnum(Long id, String name, String printName, String description) {
67
        this.id = id;
68
        this.name = name;
69
        this.printName = printName;
70
        this.description = description;
71
    }
72
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/DatabaseConnection.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting;
2

  
3
import java.sql.Connection;
4
import java.sql.DriverManager;
5
import java.sql.SQLException;
6

  
7
public class DatabaseConnection {
8

  
9
    private static final String connectionUrl = "jdbc:mysql://localhost:3306/ppicha";
10
    private static final String user = "root";
11
    private static final String password = "";
12

  
13
    private Connection databaseConnection;
14

  
15
    public DatabaseConnection() {
16
        this.databaseConnection = createConnection();
17
    }
18

  
19
    private Connection createConnection() {
20
        Connection conn = null;
21
        try {
22
             conn = DriverManager.getConnection(connectionUrl, user, password);
23

  
24
        } catch (SQLException e) {
25
            e.printStackTrace();
26
        }
27
        return conn;
28
    }
29

  
30
    public void closeConnection() {
31
        try {
32
            this.databaseConnection.close();
33
        } catch (SQLException e) {
34
            e.printStackTrace();
35
        }
36
    }
37

  
38
    public Connection getDatabaseConnection() {
39
        return databaseConnection;
40
    }
41
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/AntiPatternDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.Utils;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
6
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
7

  
8
import java.sql.ResultSet;
9
import java.sql.SQLException;
10
import java.sql.Statement;
11
import java.util.Arrays;
12
import java.util.List;
13

  
14

  
15
public abstract class AntiPatternDetector {
16
    public abstract boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection);
17

  
18
    ResultSet executeQuery(Project project, String queryFileName, DatabaseConnection databaseConnection){
19
        Statement stmt;
20
        ResultSet resultSet = null;
21
        try {
22
            stmt = databaseConnection.getDatabaseConnection().createStatement();
23

  
24
        List<String> queries = Utils.loadQueryFromFile(queryFileName);
25
        ResultSet rs = null;
26
        for (String query : queries) {
27
            if(queries.indexOf(query) != queries.size()-1){
28
                if(query.contains("?"))
29
                    query = Utils.bindValues(query, Arrays.asList(project.getId().toString()));
30
                stmt.executeQuery(query);
31
            } else {
32
                resultSet = stmt.executeQuery(query);
33
            }
34

  
35
        }
36
        } catch (SQLException e) {
37
            e.printStackTrace();
38
        }
39
        return resultSet;
40
    }
41
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
6

  
7
import java.sql.ResultSet;
8
import java.sql.SQLException;
9

  
10
public class BusinessAsUsualDetector extends AntiPatternDetector {
11
    @Override
12
    public boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection) {
13

  
14
        int numberOfIterations = 0;
15
        int numberOfWikipageWithRetr = 0;
16
        int numberOfRestrospectiveActivities = 0;
17

  
18
        try {
19
            ResultSet rs = super.executeQuery(analyzedProject, "./queries/business_as_usual.sql", databaseConnection);
20
            if (rs != null) {
21
                while (rs.next()) {
22
                    numberOfIterations = rs.getInt("numberOfIterations");
23
                    numberOfWikipageWithRetr = rs.getInt("numberOfWikipageWithRetr");
24
                    numberOfRestrospectiveActivities = rs.getInt("numberOfRestrospectiveActivities");
25
                    System.out.println("Project Id: " + analyzedProject.getId());
26
                    System.out.println("numberOfWikipageWithRetr:" + numberOfWikipageWithRetr);
27
                    System.out.println("numberOfRestrospectiveActivities:" + numberOfRestrospectiveActivities);
28

  
29
                }
30
            }
31

  
32
        } catch (SQLException e) {
33
            e.printStackTrace();
34
        }
35
        if((numberOfWikipageWithRetr > 0) && (numberOfRestrospectiveActivities > 0)){
36
            return false;
37
        } else {
38
            return true;
39
        }
40
    }
41
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/IndifferentSpecialistDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
6

  
7
public class IndifferentSpecialistDetector extends AntiPatternDetector {
8
    @Override
9
    public boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection) {
10
        return false;
11
    }
12
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/LongOrNonExistentFeedbackLoopsDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
6

  
7
public class LongOrNonExistentFeedbackLoopsDetector extends AntiPatternDetector {
8

  
9
    @Override
10
    public boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection) {
11
        /*
12

  
13
        int counter = 0;
14
        long daysTolerance = 3;
15
        boolean isFirstIteration = true;
16
        int numberOfIterations = 0;
17
        double averageLengthOfIteration = 0;
18
        Date firstIterationDueDate = null;
19
        Date secondIterationDueDate = null;
20

  
21
        try {
22
            ResultSet rs = super.executeQuery(analyzedProject, "./queries/long_or_non_existent_feedback_loops.sql", databaseConnection);
23
            if (rs != null) {
24

  
25

  
26
                while (rs.next()) {
27
                    if (isFirstIteration) {
28
                        numberOfIterations = rs.getInt("numberOfIterations");
29
                        averageLengthOfIteration = rs.getDouble("averageIterationLength");
30
                        firstIterationDueDate = rs.getDate("dueDate");
31
                        isFirstIteration = false;
32
                        continue;
33
                    }
34

  
35
                    secondIterationDueDate = rs.getDate("dueDate");
36
                    long numberOfDatesBetween = Utils.daysBetween(firstIterationDueDate, secondIterationDueDate);
37
                    firstIterationDueDate = secondIterationDueDate;
38
                    if (Math.abs(numberOfDatesBetween - averageLengthOfIteration) <= daysTolerance) {
39
                        counter++;
40
                    }
41
                }
42
            }
43

  
44
        } catch (SQLException e) {
45
            e.printStackTrace();
46
        }
47

  
48
        return counter != numberOfIterations;*/
49
        return false;
50
    }
51
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/RoadToNowhereDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4

  
5
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
6
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
7

  
8
import java.sql.ResultSet;
9
import java.sql.SQLException;
10

  
11
public class RoadToNowhereDetector extends AntiPatternDetector {
12
    @Override
13
    public boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection) {
14

  
15
        int numberOfIssuesForProjectPlan = 0;
16
        int numberOfWikiPagesForProjectPlan = 0;
17

  
18
        try {
19
            ResultSet rs = super.executeQuery(analyzedProject, "./queries/road_to_nowhere.sql", databaseConnection);
20
            if (rs != null) {
21
                while (rs.next()) {
22
                    numberOfIssuesForProjectPlan = rs.getInt("numberOfIssuesForProjectPlan");
23
                    numberOfWikiPagesForProjectPlan = rs.getInt("numberOfWikiPagesForProjectPlan");
24
                }
25
            }
26

  
27
        } catch (SQLException e) {
28
            e.printStackTrace();
29
        }
30
        return numberOfIssuesForProjectPlan <= 0 && numberOfWikiPagesForProjectPlan <= 0;
31
    }
32
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/SpecifyNothingDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4

  
5
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
6
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
7

  
8
import java.sql.ResultSet;
9
import java.sql.SQLException;
10

  
11
public class SpecifyNothingDetector extends AntiPatternDetector {
12

  
13
    @Override
14
    public boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection) {
15

  
16
        /* Settings */
17
        int minimumNumberOfWikiPages = 1;
18
        int minimumNumberOfActivities = 1;
19
        double minimumAverageLengthOfIssueDescription = 200;
20

  
21
        /* Init values */
22
        int numberOfWikiPages = 0;
23
        int numberOfActivitiesForSpecification = 0;
24
        double averageLengthOfIssueDescription = 0;
25

  
26
        try {
27
            ResultSet rs = super.executeQuery(analyzedProject, "./queries/specify_nothing.sql", databaseConnection);
28
            if (rs != null) {
29
                while (rs.next()) {
30
                    numberOfWikiPages = rs.getInt("numberOfWikiPages");
31
                    numberOfActivitiesForSpecification = rs.getInt("numberOfActivitiesForSpecification");
32
                    averageLengthOfIssueDescription = rs.getDouble("averageLengthOfIssueDescription");
33
                }
34
            }
35

  
36
        } catch (SQLException e) {
37
            e.printStackTrace();
38
        }
39

  
40
        return numberOfWikiPages < minimumNumberOfWikiPages
41
                && numberOfActivitiesForSpecification < minimumNumberOfActivities
42
                && !(averageLengthOfIssueDescription >= minimumAverageLengthOfIssueDescription);
43
    }
44
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/TooLongSprintDetectorDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
5
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
6

  
7
import java.sql.ResultSet;
8
import java.sql.SQLException;
9

  
10
public class TooLongSprintDetectorDetector extends AntiPatternDetector {
11

  
12

  
13
    @Override
14
    public boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection) {
15

  
16
        int counter = 0;
17
        int maximumNumberOfTooLongIterations = 2;
18

  
19
        try {
20
            ResultSet rs = super.executeQuery(analyzedProject, "./queries/too_long_sprint.sql", databaseConnection);
21
            if (rs != null) {
22
                while (rs.next()) {
23
                    boolean isTooLongSprint = rs.getBoolean("isTooLongSprint");
24
                    if(isTooLongSprint) {
25
                        counter++;
26
                    }
27
                }
28
            }
29

  
30
        } catch (SQLException e) {
31
            e.printStackTrace();
32
        }
33
        return counter >= maximumNumberOfTooLongIterations;
34
    }
35
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/VaryingSprintLengthDetector.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.detecting.detectors;
2

  
3

  
4

  
5
import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
6
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
7

  
8
import java.sql.ResultSet;
9
import java.sql.SQLException;
10

  
11
public class VaryingSprintLengthDetector extends AntiPatternDetector {
12
    @Override
13
    public boolean analyze(Project analyzedProject, DatabaseConnection databaseConnection) {
14

  
15
        // Settings
16
        // Maximum difference between two iterations
17
        int maximumDaysDifference = 5;
18
        // How many times the length of the iteration can change
19
        int maximumIterationChange = 2;
20

  
21
        int counter = 0;
22

  
23
        try {
24
            ResultSet rs = super.executeQuery(analyzedProject, "./queries/varying_sprint_length.sql", databaseConnection);
25
            if (rs != null) {
26
                int firstIterationLength = Integer.MIN_VALUE;
27
                int secondIterationLength;
28
                while (rs.next()) {
29
                    int iterationLength = rs.getInt("iterationLength");
30
                    if (firstIterationLength == Integer.MIN_VALUE) {
31
                        firstIterationLength = iterationLength;
32
                        continue;
33
                    } else {
34
                        secondIterationLength = iterationLength;
35
                    }
36

  
37
                    if (Math.abs(firstIterationLength - secondIterationLength) >= maximumDaysDifference) {
38
                        counter = counter + 1;
39
                    }
40
                    firstIterationLength = secondIterationLength;
41
                }
42
            }
43

  
44
        } catch (SQLException e) {
45
            e.printStackTrace();
46
        }
47
        return counter >= maximumIterationChange;
48
    }
49
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/AntiPattern.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.model;
2

  
3
public class AntiPattern {
4

  
5
    private Long id;
6
    private String printName;
7
    private String name;
8
    private String description;
9

  
10
    public AntiPattern(Long id, String printName, String name, String description) {
11
        this.id = id;
12
        this.printName = printName;
13
        this.name = name;
14
        this.description = description;
15
    }
16

  
17
    public Long getId() {
18
        return id;
19
    }
20

  
21
    public void setId(Long id) {
22
        this.id = id;
23
    }
24

  
25
    public String getPrintName() {
26
        return printName;
27
    }
28

  
29
    public void setPrintName(String printName) {
30
        this.printName = printName;
31
    }
32

  
33
    public String getName() {
34
        return name;
35
    }
36

  
37
    public void setName(String name) {
38
        this.name = name;
39
    }
40

  
41
    public String getDescription() {
42
        return description;
43
    }
44

  
45
    public void setDescription(String description) {
46
        this.description = description;
47
    }
48

  
49
    @Override
50
    public String toString() {
51
        return "AntiPattern{" +
52
                "id=" + id +
53
                ", printName='" + printName + '\'' +
54
                ", name='" + name + '\'' +
55
                ", description='" + description + '\'' +
56
                '}';
57
    }
58
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/Project.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.model;
2

  
3
import javax.persistence.Entity;
4
import javax.persistence.GeneratedValue;
5
import javax.persistence.GenerationType;
6
import javax.persistence.Id;
7

  
8
@Entity
9
public class Project {
10

  
11
    @Id
12
    @GeneratedValue(strategy = GenerationType.AUTO)
13
    private Long id;
14
    private String name;
15
    private String description;
16

  
17
    public Project() {
18
    }
19

  
20
    public Project(String name, String description) {
21
        this.name = name;
22
        this.description = description;
23
    }
24

  
25
    public Long getId() {
26
        return id;
27
    }
28

  
29
    public void setId(Long id) {
30
        this.id = id;
31
    }
32

  
33
    public String getName() {
34
        return name;
35
    }
36

  
37
    public void setName(String name) {
38
        this.name = name;
39
    }
40

  
41
    public String getDescription() {
42
        return description;
43
    }
44

  
45
    public void setDescription(String description) {
46
        this.description = description;
47
    }
48

  
49
    @Override
50
    public String toString() {
51
        return "Project{" +
52
                "id=" + id +
53
                ", name='" + name + '\'' +
54
                ", description='" + description + '\'' +
55
                '}';
56
    }
57
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/Query.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.model;
2

  
3
import java.util.List;
4

  
5
public class Query {
6
    private List<Project> projects;
7
    private List<AntiPattern> antiPatterns;
8

  
9
    public Query(List<Project> projects, List<AntiPattern> antiPatterns) {
10
        this.projects = projects;
11
        this.antiPatterns = antiPatterns;
12
    }
13

  
14
    public List<Project> getProjects() {
15
        return projects;
16
    }
17

  
18
    public void setProjects(List<Project> projects) {
19
        this.projects = projects;
20
    }
21

  
22
    public List<AntiPattern> getAntiPatterns() {
23
        return antiPatterns;
24
    }
25

  
26
    public void setAntiPatterns(List<AntiPattern> antiPatterns) {
27
        this.antiPatterns = antiPatterns;
28
    }
29
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/QueryResult.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.model;
2

  
3
import java.util.List;
4

  
5
public class QueryResult {
6
    private Project project;
7
    private List<QueryResultItem> queryResultItems;
8

  
9
    public Project getProject() {
10
        return project;
11
    }
12

  
13
    public void setProject(Project project) {
14
        this.project = project;
15
    }
16

  
17
    public List<QueryResultItem> getQueryResultItems() {
18
        return queryResultItems;
19
    }
20

  
21
    public void setQueryResultItems(List<QueryResultItem> queryResultItems) {
22
        this.queryResultItems = queryResultItems;
23
    }
24

  
25
    public QueryResult() {
26
    }
27

  
28
    public QueryResult(Project project, List<QueryResultItem> queryResultItems) {
29
        this.project = project;
30
        this.queryResultItems = queryResultItems;
31
    }
32
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/model/QueryResultItem.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.model;
2

  
3
public class QueryResultItem {
4
    private AntiPattern antiPattern;
5
    private boolean isDetected;
6

  
7
    public QueryResultItem() {
8
    }
9

  
10
    public QueryResultItem(AntiPattern antiPattern, boolean isDetected) {
11
        this.antiPattern = antiPattern;
12
        this.isDetected = isDetected;
13
    }
14

  
15
    public AntiPattern getAntiPattern() {
16
        return antiPattern;
17
    }
18

  
19
    public void setAntiPattern(AntiPattern antiPattern) {
20
        this.antiPattern = antiPattern;
21
    }
22

  
23
    public boolean isDetected() {
24
        return isDetected;
25
    }
26

  
27
    public void setDetected(boolean detected) {
28
        isDetected = detected;
29
    }
30
}
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/repository/ProjectRepository.java
1
package cz.zcu.fav.kiv.antipatterndetectionapp.repository;
2

  
3
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
4
import org.springframework.data.repository.CrudRepository;
5

  
6
public interface ProjectRepository extends CrudRepository<Project, Long> {
7
}
src/main/resources/application.properties
1

  
1
spring.datasource.url=jdbc:mysql://localhost:3306/ppicha
2
spring.datasource.username=root
3
spring.datasource.password=
src/main/resources/queries/business_as_usual.sql
1
/*
2
Anti-pattern name: Business as usual (No sprint retrospective)
3

  
4
Description: Absence of a retrospective after individual
5
             iterations or after the completion project.
6

  
7
Detection: There will be no activities in the project
8
           that would indicate that a retrospective is
9
           taking place (issue with the name of the
10
           retrospective, issue on which all team members
11
           log, issue that is repeated periodically,
12
           issue to which no commit is bound, issue which
13
           will be marked as administration or something like that).
14
           There will be no notes in the wiki or other tool called
15
           retrospectives (%retr%).
16
*/
17

  
18
/* Init global variables */
19
set @projectId = ?;
20
/* Retrospective substring */
21
set @restrospectiveSubstring = '%retr%';
22
/* Number of developers in project */
23
set @numberOfPeople =  (select count(DISTINCT assigneeId) from workunitview where projectId = @projectId and assigneeName != 'unknown');
24
/* Number of iterations for given project */
25
set @numberOfIterations = (select COUNT(*) from iteration where superProjectId = @projectId);
26
/* Number of wikipages with substring retr */
27
set @numberOfWikipageWithRetr = (select count(*) from artifactview where projectId = @projectId AND artifactClass like 'WIKIPAGE' AND (name like @restrospectiveSubstring OR description like @restrospectiveSubstring));
28
/* Number of issues with retr root ends same day like iteration and all members of team are logging  time on this issue */
29
set @numberOfRestrospectiveActivities = (select COUNT(distinct activityEndDate) from (select workunitview.id, workunitview.activityEndDate from workunitview INNER JOIN fieldchangeview on workunitview.id = fieldchangeview.itemId where workunitview.projectId = @projectId AND fieldchangeview.changeName LIKE 'LOGTIME' AND (abs(datediff(workunitview.activityEndDate, workunitview.iterationEndDate) = 0)) AND (workunitview.name like @restrospectiveSubstring OR workunitview.description LIKE @restrospectiveSubstring) GROUP by workunitview.id HAVING COUNT(DISTINCT fieldchangeview.authorId) = @numberOfPeople) as test);
30
/* Show all statistics */
31
select @projectId as `projectId`, @numberOfPeople as `numberOfPeople`, @numberOfIterations as `numberOfIterations`, @numberOfWikipageWithRetr as `numberOfWikipageWithRetr`, @numberOfRestrospectiveActivities as `numberOfRestrospectiveActivities`;
src/main/resources/queries/long_or_non_existent_feedback_loops.sql
1
/*
2
Anti-pattern name: Long Or Non-Existant Feedback Loops (No Customer feedback)
3

  
4
Description: Long spacings between customer feedback or no feedback. The customer
5
             enters the project and sees the final result. In the end, the customer
6
             may not get what he really wanted. With long intervals of feedback,
7
             some misunderstood functionality can be created and we have to spend
8
             a lot of effort and time to redo it.
9

  
10

  
11
Detection: How to choose what is the optimal spacing between feedbacks? In ASWI,
12
           it was mostly after each iteration, ie 2-3 weeks apart. Check if there
13
           is an activity that is repeated periodically, all team members or
14
           leaders log time on it (essentially a similar issue as in the anti-Business
15
           as usual model). Search for an activity named "DEMO", "CUSTOMER", etc.
16
           Search for some records from the demo in the wiki. Similar to Business as usual.
17

  
18
           POZNÁMKY:
19
                     1) naléz aktivity, které mohou odpovídat schůzce se zákazníkem
20
                     2) nalezené aktivity zgrupovat podle dueDate
21
                     3) následně udělat časový rozdíl těchto aktivit (Python)
22
                     4) a porovnat s průměrnou délkou iterace
23

  
24

  
25

  
26

  
27
*/
28

  
29
/* Init project id */
30
set @projectId = ?;
31
/* Number of iterations for project */
32
set @numberOfIterations = (SELECT count(id) FROM `iteration` WHERE superProjectId = @projectId);
33
/* Average iteration length */
34
set @averageIterationLength = (SELECT AVG(abs(datediff(iteration.endDate, iteration.startDate))) FROM `iteration` WHERE superProjectId = @projectId);
35
/* Look for issues that should indicate customer demo or customer feedback */
36
select workunitview.duedate as `dueDate`, @numberOfIterations as `numberOfIterations`, @averageIterationLength as `averageIterationLength` FROM `workunitview` where workunitview.projectId = @projectId and (workunitview.name like '%demo%' or workunitview.description like '%demo%' or workunitview.name like '%zákazník%' or workunitview.description like '%zákazník%' or workunitview.name like '%cust%' or workunitview.description like '%cust%' or workunitview.name like '%zadavatel%' or workunitview.description like '%zadavatel%' or workunitview.name like '%předvedení%' or workunitview.description like '%předvedení%' or workunitview.name like '%presentation%' or workunitview.description like '%presentation%') group by workunitview.duedate;
37

  
38

  
39

  
src/main/resources/queries/road_to_nowhere.sql
1
/*
2
Anti-pattern name: Road To Nowhere
3

  
4
Description: The project is not sufficiently planned and therefore
5
             takes place on an ad hoc basis with an uncertain
6
             outcome and deadline. There is no project plan in the project.
7

  
8
Detection: There is no activity in ALM that would indicate the creation
9
           of a project plan. There will be no document in the wiki
10
           called the "Project Plan". Project plan should be created in first or
11
           second iteration. Also could be detected with field change view. If is
12
           a lot of changes on issues in the beginning of the iteration so then could
13
           indicate some planning.
14
*/
15
set @projectId = ?;
16
set @firstIterationStartDate = (select startDate from iteration where superProjectId = @projectId ORDER BY startDate LIMIT 1 offset 0);
17
set @secondIterationStartDate = (select startDate from iteration where superProjectId = @projectId ORDER BY startDate LIMIT 1 offset 1);
18
set @numberOfIssuesForProjectPlan = (SELECT count(*) from workunitview where projectId = @projectId  and (workunitview.name like '%plán%projektu%' or workunitview.description like '%plán%projektu%' or workunitview.name like '%project%plan%' or workunitview.description like '%project%plan%' or workunitview.name like '%plan%project%' or workunitview.description like '%plan%project%' or workunitview.name like '%proje%plán%' or workunitview.description like '%proje%plán%') AND (iterationStartDate = @firstIterationStartDate OR iterationStartDate = @secondIterationStartDate));
19
set @numberOfWikiPagesForProjectPlan = (SELECT count(*) from artifactview where projectId = @projectId AND artifactClass like 'WIKIPAGE' AND (artifactview.name like '%plán%projektu%' or artifactview.description like '%plán%projektu%' or artifactview.name like '%project%plan%' or artifactview.description like '%project%plan%' or artifactview.name like '%plan%project%' or artifactview.description like '%plan%project%' or artifactview.name like '%proje%plán%' or artifactview.description like '%proje%plán%'));
20
select @projectId as `projectId`, @numberOfIssuesForProjectPlan as `numberOfIssuesForProjectPlan`, @numberOfWikiPagesForProjectPlan as `numberOfWikiPagesForProjectPlan`;
src/main/resources/queries/specify_nothing.sql
1
/*
2
Anti-pattern name: Specify nothing
3

  
4
Description: The specification is not done intentionally. Programmers are
5
             expected to work better without written specifications.
6

  
7
Detection: No specification artifact. There is no issue that will have something
8
           like "DSP, SPECIFICATIONS, ETC." in the title. Initially, meetings
9
           with the customer should be more frequent to clarify the project framework.
10
            No entry in the wiki with the project specification.
11
*/
12

  
13
/* Init project id */
14
set @projectId = ?;
15
/* Find number of wikipages with some project specification */
16
set @numberOfWikiPages = (select count(name) from artifactview where projectId = @projectId and (name like '%DSP%' or name like '%specifikace%' or name like '%specification%' or description like '%DSP%' or description like '%specifikace%' or description like '%specification%'));
17
/* Find activities for creating DSP or project specification */
18
set @numberOfActivitiesForSpecification = (SELECT count(id) from workunitview where projectId = @projectId and (name like '%DSP%' or name like '%specifikace%' or name like '%specification%' or description like '%DSP%' or description like '%specifikace%' or description like '%specification%'));
19
/* Count average length of issues description */
20
set @averageLengthOfIssueDescription = (select AVG(CHAR_LENGTH(workunitview.description)) from workunitview where workunitview.projectId = @projectId);
21
/* Show all statistics */
22
select @projectId as `projectId`, @numberOfWikiPages as `numberOfWikiPages`, @numberOfActivitiesForSpecification as `numberOfActivitiesForSpecification`, @averageLengthOfIssueDescription as `averageLengthOfIssueDescription`;
src/main/resources/queries/too_long_sprint.sql
1
/*
2
Anti-pattern name: Too Long Sprint
3

  
4
Description: Iterations too long. (ideal iteration length is about 1-2 weeks,
5
             maximum 3 weeks). It could also be detected here if the length
6
             of the iteration does not change often (It can change at the
7
             beginning and at the end of the project, but it should not
8
             change in the already started project).
9

  
10
Detection: Detect the beginning and end of the iteration and what is
11
           the interval between these time points. We should exclude
12
           the initial and final iterations, as they could skew the result.
13
*/
14

  
15
/* Init project id */
16
set @projectId = ?;
17
/* Maximum iteration length in days */
18
set @maxSprintLength = 20;
19
/* Exclude first and last iteration? */
20
set @excludeFirstAndLastIteration = true;
21
/* Id of first iteration */
22
set @idOfFirstIteration = (select id from iteration where iteration.superProjectId = @projectId order by startDate limit 1);
23
/* Id of last iteration */
24
set @idOfLastIteration = (select id from iteration where iteration.superProjectId = @projectId order by startDate desc limit 1);
25
/* Select all too long iterations */
26
select datediff(iteration.endDate, iteration.startDate) as `iterationLength`, if(datediff(iteration.endDate, iteration.startDate) > @maxSprintLength, true, false) as `isTooLongSprint`, iteration.startDate as `iterationStartDate` from iteration where iteration.superProjectId = @projectId and iteration.id != if(@excludeFirstAndLastIteration = true, @idOfFirstIteration, -1) and iteration.id != if(@excludeFirstAndLastIteration = true, @idOfLastIteration, -1) order by iteration.startDate;
src/main/resources/queries/varying_sprint_length.sql
1
/*
2
Anti-pattern name: Varying Sprint Length
3

  
4
Description: The length of the sprint changes very often.
5
             It is clear that iterations will be different
6
             lengths at the beginning and end of the project,
7
             but the length of the sprint should not change
8
             during the project.
9

  
10

  
11
Detection: Detect sprint lengths throughout the project
12
           and see if they are too different. Possibility to
13
           eliminate the first and last sprint. It could be
14
           otherwise long. Detection would be similar to
15
           Too Long sprint anti-pattern.
16
*/
17

  
18
/* Init project id */
19
set @projectId = ?;
20
/* Exclude first and last iteration? */
21
set @excludeFirstAndLastIteration = true;
22
/* Id of first iteration */
23
set @idOfFirstIteration = (select id from iteration where iteration.superProjectId = @projectId order by startDate limit 1);
24
/* Id of last iteration */
25
set @idOfLastIteration = (select id from iteration where iteration.superProjectId = @projectId order by startDate desc limit 1);
26
/* Select all iterations with their length */
27
select datediff(endDate, startDate) as `iterationLength` from iteration where iteration.superProjectId = @projectId and iteration.id != if(@excludeFirstAndLastIteration = true, @idOfFirstIteration, -1) and iteration.id != if(@excludeFirstAndLastIteration = true, @idOfLastIteration, -1) order by iteration.startDate;
28

  
src/main/resources/templates/about.html
1
<!DOCTYPE HTML>
2
<html xmlns:th="http://www.thymeleaf.org">
3
<head>
4
    <title>Anti Pattern Detector - About</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
</head>
13
<body>
14
<!-- Navigation bar imported -->
15
<div th:replace="fragments/navbar :: navBar"></div>
16
<!-- ./Navigation bar imported -->
17
<div class="container">
18
    <h1>About</h1>
19
    <p>
20
        Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean placerat. Vivamus luctus egestas leo. Aliquam ante. Praesent in mauris eu tortor porttitor accumsan. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Fusce consectetuer risus a nunc. Proin in tellus sit amet nibh dignissim sagittis. Fusce wisi. Maecenas lorem. Integer malesuada.
21

  
22
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Integer imperdiet lectus quis justo. Aenean vel massa quis mauris vehicula lacinia. Integer in sapien. Maecenas fermentum, sem in pharetra pellentesque, velit turpis volutpat ante, in pharetra metus odio a lectus. Phasellus enim erat, vestibulum vel, aliquam a, posuere eu, velit. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Nulla non lectus sed nisl molestie malesuada. Nulla est. Cras elementum. Quisque tincidunt scelerisque libero. Nullam rhoncus aliquam metus. Vivamus luctus egestas leo. Aliquam erat volutpat. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Nullam dapibus fermentum ipsum. Etiam quis quam.
23

  
24
        Nullam feugiat, turpis at pulvinar vulputate, erat libero tristique tellus, nec bibendum odio risus sit amet ante. Phasellus enim erat, vestibulum vel, aliquam a, posuere eu, velit. Quisque tincidunt scelerisque libero. Mauris dolor felis, sagittis at, luctus sed, aliquam non, tellus. Nullam sit amet magna in magna gravida vehicula. Phasellus faucibus molestie nisl. In enim a arcu imperdiet malesuada. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam lectus justo, vulputate eget mollis sed, tempor sed magna. Praesent id justo in neque elementum ultrices. In enim a arcu imperdiet malesuada. Aliquam ornare wisi eu metus. Morbi imperdiet, mauris ac auctor dictum, nisl ligula egestas nulla, et sollicitudin sem purus in lacus. Vestibulum erat nulla, ullamcorper nec, rutrum non, nonummy ac, erat. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Fusce tellus odio, dapibus id fermentum quis, suscipit id erat. Mauris metus. Nulla non lectus sed nisl molestie malesuada. Et harum quidem rerum facilis est et expedita distinctio. Ut tempus purus at lorem.
25

  
26
        Phasellus enim erat, vestibulum vel, aliquam a, posuere eu, velit. Pellentesque sapien. Morbi leo mi, nonummy eget tristique non, rhoncus non leo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Duis ante orci, molestie vitae vehicula venenatis, tincidunt ac pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum fermentum tortor id mi. Nullam sit amet magna in magna gravida vehicula. Aliquam id dolor. Donec quis nibh at felis congue commodo.
27

  
28
        Curabitur ligula sapien, pulvinar a vestibulum quis, facilisis vel sapien. Aenean fermentum risus id tortor. Etiam sapien elit, consequat eget, tristique non, venenatis quis, ante. Aenean id metus id velit ullamcorper pulvinar. Nunc auctor. Maecenas aliquet accumsan leo. Sed convallis magna eu sem. Mauris tincidunt sem sed arcu. Fusce consectetuer risus a nunc. Nulla turpis magna, cursus sit amet, suscipit a, interdum id, felis. Fusce tellus.
29
    </p>
30

  
31
</body>
32
</html>
src/main/resources/templates/anti-pattern.html
1
<!DOCTYPE html>
2
<html xmlns:th="http://www.thymeleaf.org">
3
<head>
4
    <meta charset="UTF-8">
5
    <title>Anti Pattern Detector - Anti Pattern details</title>
6
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
7
    <meta name="viewport" content="width=device-width, initial-scale=1">
8
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
9
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
10
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
11
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
12

  
13
    <style>
14
        .card {
15
            margin-top: 40px;
16
            margin-left: 5%;
17
            margin-right: 5%;
18

  
19
        }
20
    </style>
21
</head>
22
<body>
23
<!-- Navigation bar imported -->
24
<div th:replace="fragments/navbar :: navBar"></div>
25
<!-- ./Navigation bar imported -->
26
<div class="card">
27
    <h5 class="card-header">Anti Pattern</h5>
28
    <div class="card-body">
29
        <div class="form-group">
30
            <label for="projectId">Anti Pattern Id:</label>
31
            <input disabled type="text" class="form-control" id="projectId" th:value="${antiPattern.id}">
32
        </div>
33
        <div class="form-group">
34
            <label for="projectId">Project id:</label>
35
            <input disabled type="text" class="form-control" id="projectName" th:value="${antiPattern.printName}">
36
        </div>
37
        <div class="form-group">
38
            <label for="projectId">Project id:</label>
39
            <textarea disabled class="form-control" id="projectDescription" rows="5" th:text="${antiPattern.description}"></textarea>
40
        </div>
41
    </div>
42
</div>
43

  
44
</body>
45
</html>
src/main/resources/templates/fragments/navbar.html
1
<div th:fragment="navBar" xmlns:th="http://www.w3.org/1999/xhtml">
2
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
3
        <a class="navbar-brand">Anti Pattern Detector</a>
4
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
5
            <span class="navbar-toggler-icon"></span>
6
        </button>
7
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
8
            <ul class="navbar-nav mr-auto">
9
                <li class="nav-item active">
10
                    <a class="nav-link" th:href="@{/}">Home</a>
11
                </li>
12
                <li class="nav-item active">
13
                    <a class="nav-link" th:href="@{/about}">About</a>
14
                </li>
15
        </div>
16
    </nav>
17
</div>
src/main/resources/templates/index.html
1
<!DOCTYPE HTML>
2
<html xmlns:th="http://www.thymeleaf.org">
3
<head>
4
    <title>Anti Pattern Detector</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
        tbody tr:hover {
14
            background-color: #F8F9FA;
15
        }
16

  
17
        .analyze-button-container {
18
            margin-left: 40%;
19
            margin-right: 40%;
20
            margin-bottom: 40px;
21
        }
22
    </style>
23

  
24
    <script>
25
        function checkAllProjects(checkBox) {
26
            var projects = document.querySelectorAll('*[id^="project_"]');
27
            var i;
28
            for (i = 0; i < projects.length; i++) {
29
                projects[i].checked = !!checkBox.checked;
30
            }
31
        }
32

  
33
        function checkAllAntiPatterns(checkBox) {
34
            var antiPatterns = document.querySelectorAll('*[id^="anti-pattern"]');
35
            var i;
36
            for (i = 0; i < antiPatterns.length; i++) {
37
                antiPatterns[i].checked = !!checkBox.checked;
38
            }
39
        }
40
    </script>
41
</head>
42
<body>
43
<!-- Navigation bar imported -->
44
<div th:replace="fragments/navbar :: navBar"></div>
45
<!-- ./Navigation bar imported -->
46
<form action="#" th:action="@{/analyze}" th:object="${query}" method="post">
47
    <div class="container">
48
        <div class="row">
49
            <div class="col">
50
                <h1>Projects</h1>
51
            </div>
52
            <div class="col">
53
                <h1>Anti Patterns</h1><br>
54
            </div>
55
        </div>
56

  
57
        <div class="row">
58
            <div class="col">
59
                <table class="table">
60
                    <thead>
61
                    <tr>
62
                        <th scope="col">#</th>
63
                        <th scope="col">Project Name</th>
64
                        <th scope="col">Detail</th>
65
                        <th scope="col">Analyze?</th>
66
                    </tr>
67
                    </thead>
68
                    <tbody>
69
                    <tr th:each="project : ${query.projects}">
70
                        <td th:text="${project.id}"></td>
71
                        <td th:text="${project.name}"></td>
72
                        <td><a th:href="@{/projects/} + ${project.id}">Show</a></td>
73
                        <td style="text-align: center">
74
                            <input checked type="checkbox" class="form-check-input" name="selectedProjects" th:value="${project.id}" th:id="@{project_} + ${project.id}">
75
                        </td>
76
                    </tr>
77
                    <tr>
78
                        <td></td>
79
                        <td></td>
80
                        <td></td>
81
                        <td>
82
                            <input checked type="checkbox" class="form-check-input" id="select_all_projects"
83
                                   onclick="checkAllProjects(this)">
84
                            <label class="form-check-label" for="select_all_projects">Select All</label>
85
                        </td>
86
                    </tr>
87
                    </tbody>
88
                </table>
89
            </div>
90
            <div class="col">
91
                <table class="table">
92
                    <thead>
93
                    <tr>
94
                        <th scope="col">#</th>
95
                        <th scope="col">Anti Pattern</th>
96
                        <th scope="col">Detail</th>
97
                        <th scope="col">Analyze?</th>
98
                    </tr>
99
                    </thead>
100
                    <tbody>
101
                    <tr th:each="antiPattern : ${query.antiPatterns}">
102
                        <td th:text="${antiPattern.id}"></td>
103
                        <td th:text="${antiPattern.printName}"></td>
104
                        <td><a th:href="@{/anti-patterns/} + ${antiPattern.id}">Show</a></td>
105
                        <td style="text-align: center">
106
                            <input checked type="checkbox" class="form-check-input" name="selectedAntiPatterns" th:value="${antiPattern.id}" th:id="@{anti-pattern_} + ${antiPattern.id}">
107
                        </td>
108
                    </tr>
109
                    <tr>
110
                        <td></td>
111
                        <td></td>
... Rozdílový soubor je zkrácen, protože jeho délka přesahuje max. limit.

Také k dispozici: Unified diff