001 package org.hackystat.telemetry.analyzer.reducer.impl; 002 003 import java.util.List; 004 005 import org.hackystat.dailyprojectdata.client.DailyProjectDataClient; 006 import org.hackystat.dailyprojectdata.resource.codeissue.jaxb.CodeIssueDailyProjectData; 007 import org.hackystat.dailyprojectdata.resource.codeissue.jaxb.CodeIssueData; 008 import org.hackystat.sensorbase.resource.projects.jaxb.Project; 009 import org.hackystat.telemetry.analyzer.model.TelemetryDataPoint; 010 import org.hackystat.telemetry.analyzer.model.TelemetryStream; 011 import org.hackystat.telemetry.analyzer.model.TelemetryStreamCollection; 012 import org.hackystat.telemetry.analyzer.reducer.TelemetryReducer; 013 import org.hackystat.telemetry.analyzer.reducer.TelemetryReducerException; 014 import org.hackystat.telemetry.analyzer.reducer.util.IntervalUtility; 015 import org.hackystat.telemetry.service.server.ServerProperties; 016 import org.hackystat.utilities.time.interval.Interval; 017 import org.hackystat.utilities.time.period.Day; 018 import org.hackystat.utilities.tstamp.Tstamp; 019 020 021 /** 022 * Returns a single stream providing CodeIssue counts. 023 * <p> 024 * Accepts the following options in the following order. 025 * <ol> 026 * <li> Tool: A string indicating the tool whose CodeIssue data is to be counted, or "*" for all 027 * tools found. 028 * Default is "*". 029 * <li> Type: A string to restrict the counts to the CodeIssue's with the specified type, or 030 * "*" to match all types. Default is "*". 031 * </ol> 032 * 033 * @author Philip Johnson 034 */ 035 public class CodeIssueReducer implements TelemetryReducer { 036 037 /** 038 * Computes and returns the required telemetry streams object. 039 * 040 * @param project The project. 041 * @param dpdClient The DPD Client. 042 * @param interval The interval. 043 * @param options The optional parameters. 044 * 045 * @return Telemetry stream collection. 046 * @throws TelemetryReducerException If there is any error. 047 */ 048 public TelemetryStreamCollection compute(Project project, DailyProjectDataClient dpdClient, 049 Interval interval, String[] options) throws TelemetryReducerException { 050 String tool = null; 051 String type = null; 052 //process options 053 if (options.length > 2) { 054 throw new TelemetryReducerException("CodeIssue reducer takes 2 optional parameters."); 055 } 056 057 if (options.length >= 1 && !"*".equals(options[0])) { 058 tool = options[0]; 059 } 060 061 if (options.length >= 2 && !"*".equals(options[1])) { 062 type = options[1]; 063 } 064 065 // Find out the DailyProjectData host, throw error if not found. 066 String dpdHost = System.getProperty(ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY); 067 if (dpdHost == null) { 068 throw new TelemetryReducerException("Null DPD host in BuildReducer"); 069 } 070 071 // now compute the single telemetry stream. 072 try { 073 TelemetryStream telemetryStream = this.getStream(dpdClient, project, interval, 074 tool, type, null); 075 TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval); 076 streams.add(telemetryStream); 077 return streams; 078 } 079 catch (Exception e) { 080 throw new TelemetryReducerException(e); 081 } 082 } 083 084 /** 085 * Gets the telemetry stream. 086 * 087 * @param dpdClient The DailyProjectData client we will contact for the data. 088 * @param project The project. 089 * @param interval The interval. 090 * @param tool The tool whose CodeIssue data is to be returned. 091 * @param type The type of CodeIssue data to return. 092 * @param streamTagValue The tag for the generated telemetry stream. 093 * 094 * @return The telemetry stream as required. 095 * @throws Exception If there is any error. 096 */ 097 TelemetryStream getStream(DailyProjectDataClient dpdClient, 098 Project project, Interval interval, String tool, String type, 099 Object streamTagValue) throws Exception { 100 TelemetryStream telemetryStream = new TelemetryStream(streamTagValue); 101 List<IntervalUtility.Period> periods = IntervalUtility.getPeriods(interval); 102 103 for (IntervalUtility.Period period : periods) { 104 Long value = this.getData(dpdClient, project, period.getStartDay(), period.getEndDay(), 105 tool, type); 106 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), value)); 107 } 108 return telemetryStream; 109 } 110 111 /** 112 * Returns a CodeIssue count for the specified time interval, or null if no SensorData. 113 * 114 * @param dpdClient The DailyProjectData client we will use to get this data. 115 * @param project The project. 116 * @param startDay The start day (inclusive). 117 * @param endDay The end day (inclusive). 118 * @param tool The tool whose CodeIssue data is to be counted, or null for all tools. 119 * @param type The type of CodeIssue data to count, or null for all CodeIssue types. 120 * @throws TelemetryReducerException If anything goes wrong. 121 * 122 * @return The CodeIssue count, or null if there is no CodeIssue SensorData for that time period. 123 */ 124 Long getData(DailyProjectDataClient dpdClient, Project project, Day startDay, Day endDay, 125 String tool, String type) throws TelemetryReducerException { 126 try { 127 // Work backward through the interval, and return as soon as we get matching CodeIssue info. 128 for (Day day = endDay; day.compareTo(startDay) >= 0; day = day.inc(-1) ) { 129 // Get the DPD... 130 CodeIssueDailyProjectData dpdData = 131 dpdClient.getCodeIssue(project.getOwner(), project.getName(), Tstamp.makeTimestamp(day), 132 tool, type); 133 // Keep going if there's no data. 134 if ((dpdData.getCodeIssueData() == null) || dpdData.getCodeIssueData().isEmpty()) { 135 continue; 136 } 137 // Otherwise we have CodeIssue data, so return the total field. 138 return getTotalIssues(dpdData); 139 } 140 } 141 catch (Exception ex) { 142 throw new TelemetryReducerException(ex); 143 } 144 // Never found appropriate CodeIssue data in this interval, so return null. 145 return null; 146 } 147 148 /** 149 * Returns the total number of code issues in this CodeIssueDailyProjectData object. 150 * @param dpdData The CodeIssueDailyProjectData object. 151 * @return The total number of issues. 152 */ 153 private long getTotalIssues (CodeIssueDailyProjectData dpdData) { 154 long count = 0; 155 for (CodeIssueData data : dpdData.getCodeIssueData()) { 156 count += data.getNumIssues(); 157 } 158 return count; 159 } 160 161 }