commit ca69c8a7e34561d026198ee1415773bcd2c61125
Author: Ondřej Váně <onry.vane@gmail.com>
Date:   Sat Apr 10 12:45:01 2021 +0200

    Business as usual implemented

diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/DatabaseConnection.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/DatabaseConnection.java
index f3e8d00..cd07d87 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/DatabaseConnection.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/DatabaseConnection.java
@@ -6,8 +6,7 @@ import cz.zcu.fav.kiv.antipatterndetectionapp.spring.SpringApplicationContext;
 import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils;
 
 import java.sql.*;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
 
 public class DatabaseConnection {
 
@@ -66,4 +65,47 @@ public class DatabaseConnection {
         }
         return resultSet;
     }
+
+    public List<List<Map<String,Object>>> executeQueriesWithMultipleResults(Project project, List<String> queries) {
+        Statement stmt;
+        List<List<Map<String,Object>>> allResults = new ArrayList<>();
+        ResultSet resultSet = null;
+        try {
+            stmt = this.getDatabaseConnection().createStatement();
+
+            for (String query : queries) {
+                if(queries.indexOf(query) != queries.size()-1){
+                    if(query.contains("?"))
+                        query = query.replace("?", project.getId().toString());
+                    resultSet = stmt.executeQuery(query);
+                } else {
+                    resultSet = stmt.executeQuery(query);
+                }
+
+                if (query.toLowerCase().startsWith("select")) {
+                    allResults.add(resultSetToArrayList(resultSet));
+                }
+
+            }
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+        return allResults;
+    }
+
+    public List<Map<String,Object>> resultSetToArrayList(ResultSet rs) throws SQLException {
+        ResultSetMetaData md = rs.getMetaData();
+        int columns = md.getColumnCount();
+        List<Map<String, Object>> list = new ArrayList<>();
+        while (rs.next()) {
+            Map<String, Object> row = new HashMap<>(columns);
+            for (int i = 1; i <= columns; ++i) {
+                row.put(md.getColumnName(i), rs.getObject(i));
+            }
+            list.add(row);
+        }
+
+        return list;
+    }
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java
index 00c6f01..b6f4cc8 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/detecting/detectors/BusinessAsUsualDetectorImpl.java
@@ -4,10 +4,14 @@ import cz.zcu.fav.kiv.antipatterndetectionapp.detecting.DatabaseConnection;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.AntiPattern;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.Project;
 import cz.zcu.fav.kiv.antipatterndetectionapp.model.QueryResultItem;
+import cz.zcu.fav.kiv.antipatterndetectionapp.model.ResultDetail;
+import cz.zcu.fav.kiv.antipatterndetectionapp.utils.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class BusinessAsUsualDetectorImpl extends AntiPatternDetector {
 
@@ -19,7 +23,12 @@ public class BusinessAsUsualDetectorImpl extends AntiPatternDetector {
             "Absence of a retrospective after individual " +
                     "iterations or after the completion project.");
 
-    private final String sqlFileName = "too_long_sprint.sql";
+    private final String sqlFileName = "business_as_usual.sql";
+
+    /**
+     * Settings
+     */
+    private final float DIVISION_BUSINESS_AS_USUAL_ITERATIONS = (float) 1/3;
 
     @Override
     public AntiPattern getAntiPatternModel() {
@@ -31,9 +40,65 @@ public class BusinessAsUsualDetectorImpl extends AntiPatternDetector {
         return this.sqlFileName;
     }
 
+    /**
+     * Postup detekce:
+     * 1) ke každé iteraci najít všechny aktivity které obsahují název "%retr%"=retrospektiva nebo "%revi%"=revize
+     * (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,
+     * 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
+     * žádnou aktivita => příliš přísná kritéria)
+     * 2) v každé iteraci by měla být alespoň jedna aktivita představující retrospektivu
+     * 3) dále nalézt všechny wiki stránky, které byly upravené nebo vytvořené v dané iteraci
+     * 4) zjistit jestli wiki stránka představuje nějaké poznámky z retrospektivy
+     * 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
+     * 6) pokud nebude nalezen žádný záznam u více jak jedné třetiny iterací, tak je anti-pattern detekován
+     *
+     * @param project            analyzovaný project
+     * @param databaseConnection databázové připojení
+     * @param queries            list sql dotazů
+     * @return výsledek detekce
+     */
     @Override
-    public QueryResultItem analyze(Project project, DatabaseConnection databaseConnection, List<String> sql) {
+    public QueryResultItem analyze(Project project, DatabaseConnection databaseConnection, List<String> queries) {
+        Long totalNumberIterations = 0L;
+        Map<String, Integer> iterationsResults = new HashMap<>();
+
+        // projít výsledky dotazů a dát do jedné mapy => v této mapě by měly být všechny iterace
+        List<List<Map<String, Object>>> resultSets = databaseConnection.executeQueriesWithMultipleResults(project, queries);
+        for (int i = 0; i < resultSets.size(); i++) {
+            List<Map<String, Object>> rs = resultSets.get(i);
+
+            if (i == 0) {
+                totalNumberIterations = (Long) rs.get(0).get("numberOfIterations");
+            }
+
+            if (i == 1) {
+                String iterationName;
+                for (Map<String, Object> map : rs) {
+                    iterationName = (String) map.get("iterationName");
+                    iterationsResults.put(iterationName, 1);
+                }
+            }
+
+            if (i == 2) {
+                String iterationName;
+                for (Map<String, Object> map : rs) {
+                    iterationName = (String) map.get("iterationName");
+                    iterationsResults.put(iterationName, 2);
+                }
+            }
+
+        }
+
+
+        int minRetrospectiveLimit =  totalNumberIterations.intValue() - Math.round(totalNumberIterations * DIVISION_BUSINESS_AS_USUAL_ITERATIONS);
+
+        List<ResultDetail> resultDetails = Utils.createResultDetailsList(
+                new ResultDetail("Project id", project.getId().toString()),
+                new ResultDetail("Min retrospective loimit", String.valueOf(minRetrospectiveLimit)),
+                new ResultDetail("Found retrospectives", String.valueOf(iterationsResults.size())));
+        LOGGER.info(this.antiPattern.getPrintName());
+        LOGGER.info(resultDetails.toString());
 
-        return new QueryResultItem(this.antiPattern, false, null);
+        return new QueryResultItem(this.antiPattern, minRetrospectiveLimit > iterationsResults.size(), resultDetails);
     }
 }
diff --git a/src/main/resources/queries/business_as_usual.sql b/src/main/resources/queries/business_as_usual.sql
index 1039741..515aa23 100644
--- a/src/main/resources/queries/business_as_usual.sql
+++ b/src/main/resources/queries/business_as_usual.sql
@@ -15,17 +15,15 @@ Detection: There will be no activities in the project
            retrospectives (%retr%).
 */
 
-/* Init global variables */
+/* Init project id */
 set @projectId = ?;
 /* Retrospective substring */
 set @restrospectiveSubstring = '%retr%';
-/* Number of developers in project */
-set @numberOfPeople =  (select count(DISTINCT assigneeId) from workunitview where projectId = @projectId and assigneeName != 'unknown');
+/* Revision substring */
+set @revisionSubstring = '%revi%';
 /* Number of iterations for given project */
-set @numberOfIterations = (select COUNT(*) from iteration where superProjectId = @projectId);
-/* Number of wikipages with substring retr */
-set @numberOfWikipageWithRetr = (select count(*) from artifactview where projectId = @projectId AND artifactClass like 'WIKIPAGE' AND (name like @restrospectiveSubstring OR description like @restrospectiveSubstring));
-/* Number of issues with retr root ends same day like iteration and all members of team are logging  time on this issue */
-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);
-/* Show all statistics */
-select @projectId as `projectId`, @numberOfPeople as `numberOfPeople`, @numberOfIterations as `numberOfIterations`, @numberOfWikipageWithRetr as `numberOfWikipageWithRetr`, @numberOfRestrospectiveActivities as `numberOfRestrospectiveActivities`;
+select COUNT(id) as 'numberOfIterations' from iteration where superProjectId = @projectId;
+/* Select all iteration with detected retrospective activities */
+select  iterationName as 'iterationName', count(name) as 'numberOfIssues' from workunitview where projectId = @projectId and (name like @restrospectiveSubstring or name like @revisionSubstring) group by iterationName;
+/* Select all wikipages that were created or updated in iteration and have name with retr or revi*/
+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;
