Revize ca69c8a7
Přidáno uživatelem Ondřej Váně před asi 4 roky(ů)
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/DatabaseConnection.java | ||
---|---|---|
6 | 6 |
import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils; |
7 | 7 |
|
8 | 8 |
import java.sql.*; |
9 |
import java.util.Arrays; |
|
10 |
import java.util.List; |
|
9 |
import java.util.*; |
|
11 | 10 |
|
12 | 11 |
public class DatabaseConnection { |
13 | 12 |
|
... | ... | |
66 | 65 |
} |
67 | 66 |
return resultSet; |
68 | 67 |
} |
68 |
|
|
69 |
public List<List<Map<String,Object>>> executeQueriesWithMultipleResults(Project project, List<String> queries) { |
|
70 |
Statement stmt; |
|
71 |
List<List<Map<String,Object>>> allResults = new ArrayList<>(); |
|
72 |
ResultSet resultSet = null; |
|
73 |
try { |
|
74 |
stmt = this.getDatabaseConnection().createStatement(); |
|
75 |
|
|
76 |
for (String query : queries) { |
|
77 |
if(queries.indexOf(query) != queries.size()-1){ |
|
78 |
if(query.contains("?")) |
|
79 |
query = query.replace("?", project.getId().toString()); |
|
80 |
resultSet = stmt.executeQuery(query); |
|
81 |
} else { |
|
82 |
resultSet = stmt.executeQuery(query); |
|
83 |
} |
|
84 |
|
|
85 |
if (query.toLowerCase().startsWith("select")) { |
|
86 |
allResults.add(resultSetToArrayList(resultSet)); |
|
87 |
} |
|
88 |
|
|
89 |
} |
|
90 |
} catch (SQLException e) { |
|
91 |
e.printStackTrace(); |
|
92 |
} |
|
93 |
|
|
94 |
return allResults; |
|
95 |
} |
|
96 |
|
|
97 |
public List<Map<String,Object>> resultSetToArrayList(ResultSet rs) throws SQLException { |
|
98 |
ResultSetMetaData md = rs.getMetaData(); |
|
99 |
int columns = md.getColumnCount(); |
|
100 |
List<Map<String, Object>> list = new ArrayList<>(); |
|
101 |
while (rs.next()) { |
|
102 |
Map<String, Object> row = new HashMap<>(columns); |
|
103 |
for (int i = 1; i <= columns; ++i) { |
|
104 |
row.put(md.getColumnName(i), rs.getObject(i)); |
|
105 |
} |
|
106 |
list.add(row); |
|
107 |
} |
|
108 |
|
|
109 |
return list; |
|
110 |
} |
|
69 | 111 |
} |
src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java | ||
---|---|---|
4 | 4 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.AntiPattern; |
5 | 5 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project; |
6 | 6 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.QueryResultItem; |
7 |
import cz.zcu.fav.kiv.antipatterndetectionapp.model.ResultDetail; |
|
8 |
import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils; |
|
7 | 9 |
import org.slf4j.Logger; |
8 | 10 |
import org.slf4j.LoggerFactory; |
9 | 11 |
|
12 |
import java.util.HashMap; |
|
10 | 13 |
import java.util.List; |
14 |
import java.util.Map; |
|
11 | 15 |
|
12 | 16 |
public class BusinessAsUsualDetectorImpl extends AntiPatternDetector { |
13 | 17 |
|
... | ... | |
19 | 23 |
"Absence of a retrospective after individual " + |
20 | 24 |
"iterations or after the completion project."); |
21 | 25 |
|
22 |
private final String sqlFileName = "too_long_sprint.sql"; |
|
26 |
private final String sqlFileName = "business_as_usual.sql"; |
|
27 |
|
|
28 |
/** |
|
29 |
* Settings |
|
30 |
*/ |
|
31 |
private final float DIVISION_BUSINESS_AS_USUAL_ITERATIONS = (float) 1/3; |
|
23 | 32 |
|
24 | 33 |
@Override |
25 | 34 |
public AntiPattern getAntiPatternModel() { |
... | ... | |
31 | 40 |
return this.sqlFileName; |
32 | 41 |
} |
33 | 42 |
|
43 |
/** |
|
44 |
* Postup detekce: |
|
45 |
* 1) ke každé iteraci najít všechny aktivity které obsahují název "%retr%"=retrospektiva nebo "%revi%"=revize |
|
46 |
* (mohlo by se dále detekovat že si na této aktivitě logují všichni členové, tato aktivita by měla být bez commitu, |
|
47 |
* měla by být dokončena někdy ke konci iterace => pokud by se ale vzaly v úvahu všechny tyto atributy, tak nenajedem |
|
48 |
* žádnou aktivita => příliš přísná kritéria) |
|
49 |
* 2) v každé iteraci by měla být alespoň jedna aktivita představující retrospektivu |
|
50 |
* 3) dále nalézt všechny wiki stránky, které byly upravené nebo vytvořené v dané iteraci |
|
51 |
* 4) zjistit jestli wiki stránka představuje nějaké poznámky z retrospektivy |
|
52 |
* 5) výsledky wiki stránek a aktivit dát dohromady a u každé iterace by měl být alespoň jeden záznam |
|
53 |
* 6) pokud nebude nalezen žádný záznam u více jak jedné třetiny iterací, tak je anti-pattern detekován |
|
54 |
* |
|
55 |
* @param project analyzovaný project |
|
56 |
* @param databaseConnection databázové připojení |
|
57 |
* @param queries list sql dotazů |
|
58 |
* @return výsledek detekce |
|
59 |
*/ |
|
34 | 60 |
@Override |
35 |
public QueryResultItem analyze(Project project, DatabaseConnection databaseConnection, List<String> sql) { |
|
61 |
public QueryResultItem analyze(Project project, DatabaseConnection databaseConnection, List<String> queries) { |
|
62 |
Long totalNumberIterations = 0L; |
|
63 |
Map<String, Integer> iterationsResults = new HashMap<>(); |
|
64 |
|
|
65 |
// projít výsledky dotazů a dát do jedné mapy => v této mapě by měly být všechny iterace |
|
66 |
List<List<Map<String, Object>>> resultSets = databaseConnection.executeQueriesWithMultipleResults(project, queries); |
|
67 |
for (int i = 0; i < resultSets.size(); i++) { |
|
68 |
List<Map<String, Object>> rs = resultSets.get(i); |
|
69 |
|
|
70 |
if (i == 0) { |
|
71 |
totalNumberIterations = (Long) rs.get(0).get("numberOfIterations"); |
|
72 |
} |
|
73 |
|
|
74 |
if (i == 1) { |
|
75 |
String iterationName; |
|
76 |
for (Map<String, Object> map : rs) { |
|
77 |
iterationName = (String) map.get("iterationName"); |
|
78 |
iterationsResults.put(iterationName, 1); |
|
79 |
} |
|
80 |
} |
|
81 |
|
|
82 |
if (i == 2) { |
|
83 |
String iterationName; |
|
84 |
for (Map<String, Object> map : rs) { |
|
85 |
iterationName = (String) map.get("iterationName"); |
|
86 |
iterationsResults.put(iterationName, 2); |
|
87 |
} |
|
88 |
} |
|
89 |
|
|
90 |
} |
|
91 |
|
|
92 |
|
|
93 |
int minRetrospectiveLimit = totalNumberIterations.intValue() - Math.round(totalNumberIterations * DIVISION_BUSINESS_AS_USUAL_ITERATIONS); |
|
94 |
|
|
95 |
List<ResultDetail> resultDetails = Utils.createResultDetailsList( |
|
96 |
new ResultDetail("Project id", project.getId().toString()), |
|
97 |
new ResultDetail("Min retrospective loimit", String.valueOf(minRetrospectiveLimit)), |
|
98 |
new ResultDetail("Found retrospectives", String.valueOf(iterationsResults.size()))); |
|
99 |
LOGGER.info(this.antiPattern.getPrintName()); |
|
100 |
LOGGER.info(resultDetails.toString()); |
|
36 | 101 |
|
37 |
return new QueryResultItem(this.antiPattern, false, null);
|
|
102 |
return new QueryResultItem(this.antiPattern, minRetrospectiveLimit > iterationsResults.size(), resultDetails);
|
|
38 | 103 |
} |
39 | 104 |
} |
src/main/resources/queries/business_as_usual.sql | ||
---|---|---|
15 | 15 |
retrospectives (%retr%). |
16 | 16 |
*/ |
17 | 17 |
|
18 |
/* Init global variables */
|
|
18 |
/* Init project id */
|
|
19 | 19 |
set @projectId = ?; |
20 | 20 |
/* Retrospective substring */ |
21 | 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');
|
|
22 |
/* Revision substring */
|
|
23 |
set @revisionSubstring = '%revi%';
|
|
24 | 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`; |
|
25 |
select COUNT(id) as 'numberOfIterations' from iteration where superProjectId = @projectId; |
|
26 |
/* Select all iteration with detected retrospective activities */ |
|
27 |
select iterationName as 'iterationName', count(name) as 'numberOfIssues' from workunitview where projectId = @projectId and (name like @restrospectiveSubstring or name like @revisionSubstring) group by iterationName; |
|
28 |
/* Select all wikipages that were created or updated in iteration and have name with retr or revi*/ |
|
29 |
select iteration.name as 'iterationName', count(distinct(artifactview.name)) as 'numberOfWikiPages' from artifactview inner join fieldchangeview on artifactview.id = fieldchangeview.itemId inner join iteration on (fieldchangeview.itemCreated between iteration.startDate and iteration.endDate) and iteration.superProjectId = @projectId where artifactview.projectId = @projectId and artifactview.artifactClass like 'WIKIPAGE' and (artifactview.name like '%retr%' or artifactview.description like '%retr%') group by iteration.id order by iteration.name; |
Také k dispozici: Unified diff
Business as usual implemented