package com.statscollector.gerrit.service; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.ApprovalInfo; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.LabelInfo; import com.statscollector.gerrit.config.GerritConfig; import com.statscollector.gerrit.model.GerritReviewStats; import com.statscollector.gerrit.model.GerritReviewStatsResult; import com.statscollector.gerrit.model.GerritUserCount; /** * I'm a class that takes a list of changes from Gerrit and performs some * business logic on them. * * @author JCannon * */ @Component public class GerritStatisticsHelper { private static final String CODE_REVIEW = "Code-Review"; @Autowired private GerritService gerritService; @Autowired private GerritConfig gerritConfig; final Map<String, List<ChangeInfo>> allChanges = new ConcurrentHashMap<>(); final Map<String, GerritReviewStats> allReviewStats = new ConcurrentHashMap<>(); final static Logger LOGGER = Logger.getLogger(GerritStatisticsService.class); @Async public Future<GerritReviewStatsResult> populateReviewStatsAsync(final String changeStatus, final List<ChangeInfo> noPeerReviewList, final List<ChangeInfo> onePeerReviewList, final List<ChangeInfo> twoPlusPeerReviewList, final List<ChangeInfo> collabrativeDevelopmentList, final List<ChangeInfo> changes) throws IOException, URISyntaxException { LOGGER.info("Starting Thread To Process Changes"); GerritReviewStatsResult result = null; try { populateReviewStats(changeStatus, noPeerReviewList, onePeerReviewList, twoPlusPeerReviewList, collabrativeDevelopmentList, changes); result = new GerritReviewStatsResult(true, changes); } catch(Exception e) { LOGGER.info("CAUGHT EXCEPTION"); result = new GerritReviewStatsResult(false, e, changes); } LOGGER.info("Thread Finished"); return new AsyncResult<GerritReviewStatsResult>(result); } public Map<String, GerritReviewStats> populateReviewStats(final String changeStatus, final List<ChangeInfo> noPeerReviewList, final List<ChangeInfo> onePeerReviewList, final List<ChangeInfo> twoPlusPeerReviewList, final List<ChangeInfo> collabrativeDevelopmentList, final List<ChangeInfo> changes) throws Exception { List<ChangeInfo> populatedChanges = gerritService.populateChangeReviewers(changes); allChanges.put(changeStatus, populatedChanges); for(ChangeInfo gerritChange : populatedChanges) { int numberOfReviewers = numberOfReviewers(gerritChange); switch(numberOfReviewers) { case -1: collabrativeDevelopmentList.add(gerritChange); break; case 0: noPeerReviewList.add(gerritChange); break; case 1: onePeerReviewList.add(gerritChange); break; default: twoPlusPeerReviewList.add(gerritChange); break; } } allReviewStats.put(changeStatus, GerritReviewStats.buildStatsObjectWithValuesAndStatus(noPeerReviewList, onePeerReviewList, twoPlusPeerReviewList, collabrativeDevelopmentList, "", false)); return allReviewStats; } /** * I return the number of reviewers for the provided change, if the change * has been collaboratively developed I return -1 * * @param gerritChange * @return */ private int numberOfReviewers(final ChangeInfo gerritChange) { // LOGGER.info("Calculating changes for change: " + gerritChange); int result = 0; String owner = gerritChange.owner.username; Map<String, LabelInfo> labels = gerritChange.labels; LabelInfo labelInfo = labels.get(CODE_REVIEW); if(null != labelInfo) { List<ApprovalInfo> allLabels = labelInfo.all; if(null != allLabels) { result = result + getNumberOfHumanReviewers(owner, allLabels); } } return result; } private int getNumberOfHumanReviewers(final String owner, final List<ApprovalInfo> allLabels) { int result = 0; for(ApprovalInfo approvalInfo : allLabels) { if(ifReviewerValid(owner, approvalInfo)) { if(isApprovalValueAboveZero(approvalInfo)) { result++; } else if(isApprovalValueBelowZero(approvalInfo)) { return 0; } } } return result; } private boolean ifReviewerValid(final String owner, final ApprovalInfo approvalInfo) { return ifReviewerNotOwner(owner, approvalInfo) && ifReviewerNotExcluded(approvalInfo); } private boolean ifReviewerNotExcluded(final ApprovalInfo approvalInfo) { return !gerritConfig.getReviewersToIgnoreList().contains(approvalInfo.username); } private boolean ifReviewerNotOwner(final String owner, final ApprovalInfo approvalInfo) { return !approvalInfo.username.equals(owner); } private boolean isApprovalValueAboveZero(final ApprovalInfo approvalInfo) { return approvalInfo.value != null && approvalInfo.value > 0; } private boolean isApprovalValueBelowZero(final ApprovalInfo approvalInfo) { return approvalInfo.value != null && approvalInfo.value < 0; } public Map<String, List<ChangeInfo>> getAllChanges() { return allChanges; } public Map<String, GerritReviewStats> getAllReviewStats() { return allReviewStats; } public void setGerritService(final GerritService gerritService) { this.gerritService = gerritService; } public void setGerritConfig(final GerritConfig gerritConfig) { this.gerritConfig = gerritConfig; } @SuppressWarnings("unchecked") public Map<String, GerritUserCount> createHashMapOfAuthorsAndCounts(final List<ChangeInfo>... reviewLists) { Map<String, GerritUserCount> result = new HashMap<String, GerritUserCount>(); for(List<ChangeInfo> list : reviewLists) { for(ChangeInfo changeInfo : list) { if(!StringUtils.isEmpty(changeInfo.owner.username)) { if(result.containsKey(changeInfo.owner.username)) { result.get(changeInfo.owner.username).incrementCount(); } else { result.put(changeInfo.owner.username, new GerritUserCount(changeInfo.owner)); } } } } return result; } @SuppressWarnings("unchecked") public Map<String, GerritUserCount> createHashMapOfReviwersAndCounts(final List<ChangeInfo>... reviewLists) { Map<String, GerritUserCount> result = new HashMap<String, GerritUserCount>(); for(List<ChangeInfo> list : reviewLists) { for(ChangeInfo changeInfo : list) { Map<String, LabelInfo> labels = changeInfo.labels; for(LabelInfo labelInfo : labels.values()) { // Approvers List<AccountInfo> users = getAllNonFilteredReviewersForLabels(labelInfo); for(AccountInfo user : users) { if(result.containsKey(user.username)) { result.get(user.username).incrementCount(); } else { result.put(user.username, new GerritUserCount(user)); } } } } } return result; } private List<AccountInfo> getAllNonFilteredReviewersForLabels(final LabelInfo labelInfo) { List<AccountInfo> result = new ArrayList<AccountInfo>(); addToListIfNotInFilterListAndNotNull(labelInfo.approved, result); addToListIfNotInFilterListAndNotNull(labelInfo.recommended, result); addToListIfNotInFilterListAndNotNull(labelInfo.disliked, result); addToListIfNotInFilterListAndNotNull(labelInfo.rejected, result); return result; } private void addToListIfNotInFilterListAndNotNull(final AccountInfo user, final List<AccountInfo> result) { if(null != user) { if(!gerritConfig.getReviewersToIgnoreList().contains(user.username)) { result.add(user); } } } }