9 |
9 |
import org.slf4j.Logger;
|
10 |
10 |
import org.slf4j.LoggerFactory;
|
11 |
11 |
|
12 |
|
import java.util.HashMap;
|
|
12 |
import java.math.BigDecimal;
|
|
13 |
import java.sql.Date;
|
|
14 |
import java.util.ArrayList;
|
13 |
15 |
import java.util.List;
|
14 |
16 |
import java.util.Map;
|
15 |
17 |
|
... | ... | |
31 |
33 |
/**
|
32 |
34 |
* Settings
|
33 |
35 |
*/
|
34 |
|
private final float DIVISION_LONG_OR_NON_FEEDBACK_ITERATIONS = (float) 1/3;
|
|
36 |
private final float DIVISION_LONG_OR_NON_FEEDBACK_ITERATIONS = (float) 1 / 3;
|
35 |
37 |
|
36 |
38 |
/* Settings */
|
37 |
39 |
private final int MINIMUM_NUMBER_OF_WIKI_PAGES = 1;
|
... | ... | |
49 |
51 |
|
50 |
52 |
/**
|
51 |
53 |
* Postup detekce:
|
52 |
|
* A)
|
53 |
|
* 1) najít všechny aktivity, které by mohli indikovat schůzku se zákazníkem
|
54 |
|
* 2) udělat join na iterace
|
55 |
|
* 3) v ideálním případě by měla mít každá iterace jeden nalezený issue
|
56 |
|
* 4) pokud nebude nalezen žádný iisue, tak to znamená že tým nezaznamenává schůzky do úkolů => je nutné ještě prověřit wiki stránky
|
57 |
|
* 5) zkusit nalézt všechny wiki stránky, které by mohly souviset s demem
|
58 |
|
* 6) dle datumu úpravy udělat join na iterace (jedna wiki více schůzek)
|
59 |
|
* 7) v ideálním případě by měla mít opět každá iterace jeden záznam
|
60 |
|
* 8) dle prahových hodnot vyhodnotit výsledky
|
|
54 |
* A)
|
|
55 |
* 1) najít všechny aktivity, které by mohli představovat zákaznické demo (název bude obsahovat substring)
|
|
56 |
* 2) zjistit průměrnou délku iterací
|
|
57 |
* 3) zjistit počet iterací
|
|
58 |
* 4) nejprve porovnat s počtem iterací => iterace a nalezené aktivity by se měly ideálně rovnat (mohou být menší i větší ale né o moc menší)
|
|
59 |
* 5) u každých dvou po sobě jdoucích aktivitách udělat rozdíl datumů a porovnat s průměrnou délkou iterace => rozdíl by se neměl moc lišit od průěrné délky iterace
|
|
60 |
* 6) pokud u bodu 4) dojde k detekci máleho počtu nalezených aktivit (tým nedělá aktivity na schůzky a může zaznamenávat pouze do wiki)
|
|
61 |
* 7) najít všechny wiki stránky a udělat join kdy se měnily (může být použita jedná stránka pro více schůzek) s příslušným názvem
|
|
62 |
* 8) udělat group podle dne
|
61 |
63 |
*
|
62 |
64 |
* @param project analyzovaný project
|
63 |
65 |
* @param databaseConnection databázové připojení
|
... | ... | |
66 |
68 |
*/
|
67 |
69 |
@Override
|
68 |
70 |
public QueryResultItem analyze(Project project, DatabaseConnection databaseConnection, List<String> queries) {
|
69 |
|
Long totalNumberIterations = 0L;
|
70 |
|
Map<String, Integer> iterationsResults = new HashMap<>();
|
|
71 |
long totalNumberIterations = 0;
|
|
72 |
int averageIterationLength = 0;
|
|
73 |
int numberOfIterationsWitchContainsAtLeastOneActivityForFeedback = 0;
|
|
74 |
List<Date> feedbackActivityEndDates = new ArrayList<>();
|
|
75 |
Date projectStartDate = null;
|
|
76 |
Date projectEndDate = null;
|
71 |
77 |
|
72 |
78 |
List<List<Map<String, Object>>> resultSets = databaseConnection.executeQueriesWithMultipleResults(project, queries);
|
73 |
79 |
for (int i = 0; i < resultSets.size(); i++) {
|
74 |
80 |
List<Map<String, Object>> rs = resultSets.get(i);
|
75 |
81 |
|
76 |
|
if (i == 0) {
|
77 |
|
totalNumberIterations = (Long) rs.get(0).get("numberOfIterations");
|
78 |
|
}
|
|
82 |
switch (i) {
|
|
83 |
case 0:
|
|
84 |
totalNumberIterations = (long) rs.get(0).get("numberOfIterations");
|
|
85 |
break;
|
|
86 |
case 1:
|
|
87 |
averageIterationLength = ((BigDecimal) rs.get(0).get("averageIterationLength")).intValue();
|
|
88 |
break;
|
|
89 |
case 2:
|
|
90 |
if (rs.size() != 0) {
|
|
91 |
numberOfIterationsWitchContainsAtLeastOneActivityForFeedback = ((Long) rs.get(0).get("totalCountOfIterationsWithFeedbackActivity")).intValue();
|
|
92 |
}
|
|
93 |
break;
|
|
94 |
case 3:
|
|
95 |
Date activityEndDate;
|
|
96 |
for (Map<String, Object> map : rs) {
|
|
97 |
activityEndDate = (Date) map.get("endDate");
|
|
98 |
feedbackActivityEndDates.add(activityEndDate);
|
|
99 |
}
|
|
100 |
break;
|
|
101 |
case 4:
|
|
102 |
projectStartDate = (Date) rs.get(0).get("startDate");
|
|
103 |
break;
|
|
104 |
case 5:
|
|
105 |
projectEndDate = (Date) rs.get(0).get("endDate");
|
|
106 |
break;
|
|
107 |
default:
|
79 |
108 |
|
80 |
|
if (i == 1) {
|
81 |
|
String iterationName;
|
82 |
|
for (Map<String, Object> map : rs) {
|
83 |
|
iterationName = (String) map.get("iterationName");
|
84 |
|
iterationsResults.put(iterationName, 1);
|
85 |
|
}
|
86 |
109 |
}
|
87 |
110 |
}
|
88 |
|
int minFeedbackLimit = totalNumberIterations.intValue() - Math.round(totalNumberIterations * DIVISION_LONG_OR_NON_FEEDBACK_ITERATIONS);
|
89 |
111 |
|
90 |
|
List<ResultDetail> resultDetails = Utils.createResultDetailsList(
|
91 |
|
new ResultDetail("Project id", project.getId().toString()));
|
|
112 |
double halfNumberOfIterations = totalNumberIterations / 2.0;
|
|
113 |
|
|
114 |
// pokud je počet iterací, které obsahují alespoň jednu aktivitu s feedbackem, tak je to ideální případ
|
|
115 |
if (totalNumberIterations <= numberOfIterationsWitchContainsAtLeastOneActivityForFeedback) {
|
|
116 |
List<ResultDetail> resultDetails = Utils.createResultDetailsList(
|
|
117 |
new ResultDetail("Number of iterations", Long.toString(totalNumberIterations)),
|
|
118 |
new ResultDetail("Number of iterations with feedback loops", Integer.toString(numberOfIterationsWitchContainsAtLeastOneActivityForFeedback)),
|
|
119 |
new ResultDetail("Conclusion", "In each iteration is at least one activity that represents feedback loop"));
|
|
120 |
|
|
121 |
|
|
122 |
return new QueryResultItem(this.antiPattern, false, resultDetails);
|
|
123 |
|
|
124 |
// pokud alespoň v polovině iteracích došlo ke kontaktu se zákazníkem => zkontrolovat rozestupy
|
|
125 |
} else if (feedbackActivityEndDates.size() > halfNumberOfIterations) {
|
|
126 |
|
|
127 |
Date firstDate = projectStartDate;
|
|
128 |
Date secondDate = null;
|
|
129 |
|
|
130 |
for (Date feedbackActivityDate : feedbackActivityEndDates) {
|
|
131 |
secondDate = feedbackActivityDate;
|
|
132 |
long daysBetween = Utils.daysBetween(firstDate, secondDate);
|
|
133 |
firstDate = secondDate;
|
92 |
134 |
|
93 |
|
if ((totalNumberIterations.intValue() != iterationsResults.size())) {
|
94 |
|
if (totalNumberIterations - iterationsResults.size() > minFeedbackLimit) {
|
95 |
|
return new QueryResultItem(this.antiPattern, true , resultDetails);
|
96 |
|
} else {
|
97 |
|
return new QueryResultItem(this.antiPattern, false , resultDetails);
|
|
135 |
if (daysBetween >= 2 * averageIterationLength) {
|
|
136 |
List<ResultDetail> resultDetails = Utils.createResultDetailsList(
|
|
137 |
new ResultDetail("Days between", Long.toString(daysBetween)),
|
|
138 |
new ResultDetail("Average iteration length", Integer.toString(averageIterationLength)),
|
|
139 |
new ResultDetail("Conclusion", "Customer feedback loop is too long"));
|
98 |
140 |
|
|
141 |
|
|
142 |
return new QueryResultItem(this.antiPattern, true, resultDetails);
|
|
143 |
}
|
99 |
144 |
}
|
|
145 |
|
|
146 |
// rozestupy feedbacků jsou ok
|
|
147 |
List<ResultDetail> resultDetails = Utils.createResultDetailsList(
|
|
148 |
new ResultDetail("Average iteration length", Integer.toString(averageIterationLength)),
|
|
149 |
new ResultDetail("Conclusion", "Customer feedback has been detected and there is not too much gap between them"));
|
|
150 |
|
|
151 |
|
|
152 |
return new QueryResultItem(this.antiPattern, false, resultDetails);
|
|
153 |
|
|
154 |
// bylo nalezeno příliš málo aktivit => zkusit se podívat ve wiki stránkách
|
100 |
155 |
} else {
|
101 |
|
return new QueryResultItem(this.antiPattern, false , resultDetails);
|
|
156 |
// TODO udělat analýzu WIKI stránek
|
102 |
157 |
}
|
|
158 |
|
|
159 |
List<ResultDetail> resultDetails = Utils.createResultDetailsList(
|
|
160 |
new ResultDetail("Project id", project.getId().toString()));
|
|
161 |
|
|
162 |
return new QueryResultItem(this.antiPattern, true, resultDetails);
|
103 |
163 |
}
|
104 |
164 |
}
|
Long or non existant feedback loops