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.build.jaxb.BuildDailyProjectData; 007 import org.hackystat.dailyprojectdata.resource.build.jaxb.MemberData; 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.analyzer.reducer.util.ReducerOptionUtility; 016 import org.hackystat.telemetry.service.server.ServerProperties; 017 import org.hackystat.utilities.time.interval.Interval; 018 import org.hackystat.utilities.time.period.Day; 019 import org.hackystat.utilities.tstamp.Tstamp; 020 021 022 /** 023 * Returns a single stream providing Build data. 024 * <p> 025 * Accepts the following options in the following order. 026 * <ol> 027 * <li> User: Supply a user email to get Build counts for just that user. 028 * Default is "*" indicating the aggregate Build Data for all project members. 029 * <li> Result: One of Success, Failure, or *, indicating whether the count is 030 * just of successful builds, failed builds, or all builds. Default is "*". 031 * <li> Type: A string to restrict the counts to those builds with a "Type" property 032 * matching this string, or "*" to indicate all builds regardless of Type. 033 * Default is "*". 034 * <li> isCumulative: True or false. Default is false. 035 * </ol> 036 * 037 * @author Philip Johnson 038 */ 039 public class BuildReducer implements TelemetryReducer { 040 041 /** 042 * Computes and returns the required telemetry streams object. 043 * 044 * @param project The project. 045 * @param dpdClient The DPD Client. 046 * @param interval The interval. 047 * @param options The optional parameters. 048 * 049 * @return Telemetry stream collection. 050 * @throws TelemetryReducerException If there is any error. 051 */ 052 public TelemetryStreamCollection compute(Project project, DailyProjectDataClient dpdClient, 053 Interval interval, String[] options) throws TelemetryReducerException { 054 // weird. for some reason we want 'null' as default rather than '*' etc. 055 String member = null; 056 String result = null; 057 String type = null; 058 boolean isCumulative = false; 059 //process options 060 if (options.length > 4) { 061 throw new TelemetryReducerException("Build reducer takes 4 optional parameters."); 062 } 063 064 if (options.length >= 1 && !"*".equals(options[0])) { 065 member = options[0]; 066 } 067 068 if (options.length >= 2 && !"*".equals(options[1])) { 069 result = options[1]; 070 } 071 072 if (options.length >= 3 && !"*".equals(options[2])) { 073 type = options[2]; 074 } 075 076 if (options.length >= 4) { 077 isCumulative = ReducerOptionUtility.parseBooleanOption(4, options[3]); 078 } 079 080 // Find out the DailyProjectData host, throw error if not found. 081 String dpdHost = System.getProperty(ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY); 082 if (dpdHost == null) { 083 throw new TelemetryReducerException("Null DPD host in BuildReducer"); 084 } 085 086 // now compute the single telemetry stream. 087 try { 088 TelemetryStream telemetryStream = this.getStream(dpdClient, project, interval, 089 member, result, type, isCumulative, null); 090 TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval); 091 streams.add(telemetryStream); 092 return streams; 093 } 094 catch (Exception e) { 095 throw new TelemetryReducerException(e); 096 } 097 } 098 099 /** 100 * Gets the telemetry stream. 101 * 102 * @param dpdClient The DailyProjectData client we will contact for the data. 103 * @param project The project. 104 * @param interval The interval. 105 * @param member Project member, or null to match all members. 106 * @param result The build result, either FAILURE, SUCCESS, or null to match both. 107 * @param type The value of the build 'Type' property, a string or null to match anything. 108 * @param isCumulative True for cumulative measure. 109 * @param streamTagValue The tag for the generated telemetry stream. 110 * 111 * @return The telemetry stream as required. 112 * @throws Exception If there is any error. 113 */ 114 TelemetryStream getStream(DailyProjectDataClient dpdClient, 115 Project project, Interval interval, String member, String result, 116 String type, boolean isCumulative, Object streamTagValue) 117 throws Exception { 118 TelemetryStream telemetryStream = new TelemetryStream(streamTagValue); 119 List<IntervalUtility.Period> periods = IntervalUtility.getPeriods(interval); 120 long cumulativeBuilds = 0; 121 122 for (IntervalUtility.Period period : periods) { 123 Long value = this.getData(dpdClient, project, period.getStartDay(), period.getEndDay(), 124 member, result, type); 125 126 if (value != null) { 127 cumulativeBuilds += value; 128 } 129 130 if (isCumulative) { 131 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), 132 cumulativeBuilds)); 133 } 134 else { 135 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), value)); 136 } 137 } 138 return telemetryStream; 139 } 140 141 /** 142 * Returns a Build count for the specified time interval, or null if no SensorData. 143 * 144 * @param dpdClient The DailyProjectData client we will use to get this data. 145 * @param project The project. 146 * @param startDay The start day (inclusive). 147 * @param endDay The end day (inclusive). 148 * @param member The project member email, or null to match all members. 149 * @param result The result, either SUCCESS, FAILURE, or null for both. 150 * @param type The 'Type' property, or null to match anything. 151 * @throws TelemetryReducerException If anything goes wrong. 152 * 153 * @return The Build count, or null if there is no Build SensorData for that time period. 154 */ 155 Long getData(DailyProjectDataClient dpdClient, Project project, Day startDay, Day endDay, 156 String member, String result, String type) throws TelemetryReducerException { 157 long buildCount = 0; 158 String typeString = (type == null) ? "*" : type; 159 try { 160 // For each day in the interval... 161 for (Day day = startDay; day.compareTo(endDay) <= 0; day = day.inc(1) ) { 162 // Get the DPD for the required 'Type' property. 163 BuildDailyProjectData data = 164 dpdClient.getBuild(project.getOwner(), project.getName(), Tstamp.makeTimestamp(day), 165 typeString); 166 // Go through the DPD per-member data... 167 for (MemberData memberData : data.getMemberData()) { 168 // Check to see if this data is for the given member and of the appropriate type. 169 if ((member == null) || "*".equals(member) || 170 (memberData.getMemberUri().endsWith(member))) { 171 if ((result == null) || "*".equals(result)) { 172 buildCount += memberData.getFailure() + memberData.getSuccess(); 173 } 174 else if ("Success".equals(result)) { 175 buildCount += memberData.getSuccess(); 176 } 177 else if ("Failure".equals(result)) { 178 buildCount += memberData.getFailure(); 179 } 180 } 181 } 182 } 183 } 184 catch (Exception ex) { 185 throw new TelemetryReducerException(ex); 186 } 187 188 //Return null if no data, the Build count data otherwise. 189 return (buildCount > 0) ? Long.valueOf(buildCount) : null; 190 } 191 192 }