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.coverage.jaxb.CoverageDailyProjectData; 007 import org.hackystat.dailyprojectdata.resource.coverage.jaxb.ConstructData; 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 Coverage data. 023 * <p> 024 * Options: 025 * <ol> 026 * <li> mode: One of 'Percentage', 'NumCovered', or 'NumUncovered'. Default is 'Percentage'. 027 * <li> granularity: A string indicating the type of coverage, such as 'line', 'method', 'class'. 028 * Default is 'method'. 029 * </ol> 030 * 031 * @author Philip Johnson, Cedric Zhang 032 */ 033 public class CoverageReducer implements TelemetryReducer { 034 035 /** Possible mode values. */ 036 public enum Mode { PERCENTAGE, NUMCOVERED, NUMUNCOVERED } 037 038 /** 039 * Computes and returns the required telemetry streams object. 040 * 041 * @param project The project. 042 * @param dpdClient The DPD Client. 043 * @param interval The interval. 044 * @param options The optional parameters. 045 * 046 * @return Telemetry stream collection. 047 * @throws TelemetryReducerException If there is any error. 048 */ 049 public TelemetryStreamCollection compute(Project project, DailyProjectDataClient dpdClient, 050 Interval interval, String[] options) throws TelemetryReducerException { 051 Mode mode = Mode.PERCENTAGE; 052 String granularity = null; 053 // process options 054 if (options.length > 2) { 055 throw new TelemetryReducerException("Coverage reducer takes 2 optional parameters."); 056 } 057 if (options.length >= 1) { 058 try { 059 mode = Mode.valueOf(options[0].toUpperCase()); 060 } 061 catch (Exception e) { 062 throw new TelemetryReducerException("Illegal mode value.", e); 063 } 064 } 065 066 if (options.length >= 2) { 067 granularity = options[1]; 068 } 069 070 // Find out the DailyProjectData host, throw error if not found. 071 String dpdHost = System.getProperty(ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY); 072 if (dpdHost == null) { 073 throw new TelemetryReducerException("Null DPD host in DevTimeReducer"); 074 } 075 076 // now get the telemetry stream. 077 try { 078 TelemetryStream telemetryStream = this.getStream(dpdClient, project, interval, 079 mode, granularity, null); 080 TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval); 081 streams.add(telemetryStream); 082 return streams; 083 } 084 catch (Exception e) { 085 throw new TelemetryReducerException(e); 086 } 087 } 088 089 /** 090 * Gets the telemetry stream. 091 * 092 * @param dpdClient The DailyProjectData client we will contact for the data. 093 * @param project The project. 094 * @param interval The interval. 095 * @param mode The mode (PERCENTAGE, NUMCOVERED, NUMUNCOVERED). 096 * @param granularity The type of coverage. 097 * @param streamTagValue The tag for the generated telemetry stream. 098 * 099 * @return The telemetry stream as required. 100 * 101 * @throws Exception If there is any error. 102 */ 103 TelemetryStream getStream(DailyProjectDataClient dpdClient, 104 Project project, Interval interval, Mode mode, 105 String granularity, Object streamTagValue) 106 throws Exception { 107 TelemetryStream telemetryStream = new TelemetryStream(streamTagValue); 108 List<IntervalUtility.Period> periods = IntervalUtility.getPeriods(interval); 109 110 for (IntervalUtility.Period period : periods) { 111 Long value = this.getData(dpdClient, project, period.getStartDay(), period.getEndDay(), 112 mode, granularity); 113 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), value)); 114 } 115 116 return telemetryStream; 117 } 118 119 /** 120 * Returns a Coverage value for the specified time interval, or null if no SensorData. Note that 121 * we return a Long, so percentage is a Long ranging from 0 to 100. (There is no fractional 122 * coverage percentage.) 123 * 124 * We work backward through the time interval, and return the Coverage value for the first day in 125 * the interval for which Coverage data exists. 126 * 127 * @param dpdClient The DailyProjectData client we will use to get this data. 128 * @param project The project. 129 * @param startDay The start day (inclusive). 130 * @param endDay The end day (inclusive). 131 * @param mode The mode: PERCENTAGE, NUMCOVERED, or NUMUNCOVERED. 132 * @param granularity The type of coverage, such as 'line' or 'method'. 133 * @throws TelemetryReducerException If anything goes wrong. 134 * 135 * @return The Coverage value, or null if there is no Coverage SensorData for that time period. 136 */ 137 Long getData(DailyProjectDataClient dpdClient, Project project, Day startDay, Day endDay, 138 Mode mode, String granularity) throws TelemetryReducerException { 139 try { 140 // Work backward through the interval, and return as soon as we get Coverage info. 141 for (Day day = endDay; day.compareTo(startDay) >= 0; day = day.inc(-1) ) { 142 // Get the DPD... 143 CoverageDailyProjectData dpdData = 144 dpdClient.getCoverage(project.getOwner(), project.getName(), Tstamp.makeTimestamp(day), 145 granularity); 146 // Go to the next day in the interval if we don't have anything for this day. 147 if ((dpdData.getConstructData() == null) || dpdData.getConstructData().isEmpty()) { 148 continue; 149 } 150 151 // Otherwise we have coverage data, so get the total covered and uncovered values. 152 int totalCovered = 0; 153 int totalUncovered = 0; 154 for (ConstructData data : dpdData.getConstructData()) { 155 totalCovered += data.getNumCovered(); 156 totalUncovered += data.getNumUncovered(); 157 } 158 159 // Now return the value based upon mode. 160 switch (mode) { 161 case NUMCOVERED: 162 return Long.valueOf(totalCovered); 163 case NUMUNCOVERED: 164 return Long.valueOf(totalUncovered); 165 case PERCENTAGE: 166 double total = totalCovered + totalUncovered; 167 int percent = (int)((totalCovered / total) * 100.0); 168 return Long.valueOf(percent); 169 default: 170 throw new TelemetryReducerException("Unknown mode: " + mode); 171 } 172 } 173 } 174 catch (Exception ex) { 175 throw new TelemetryReducerException(ex); 176 } 177 // Never found appropriate coverage data in this interval, so return null. 178 return null; 179 } 180 181 }