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.devtime.jaxb.DevTimeDailyProjectData; 007 import org.hackystat.dailyprojectdata.resource.devtime.jaxb.MemberData; 008 import org.hackystat.sensorbase.resource.projects.jaxb.Project; 009 import org.hackystat.sensorbase.uripattern.UriPattern; 010 import org.hackystat.telemetry.analyzer.model.TelemetryDataPoint; 011 import org.hackystat.telemetry.analyzer.model.TelemetryStream; 012 import org.hackystat.telemetry.analyzer.model.TelemetryStreamCollection; 013 import org.hackystat.telemetry.analyzer.reducer.TelemetryReducer; 014 import org.hackystat.telemetry.analyzer.reducer.TelemetryReducerException; 015 import org.hackystat.telemetry.analyzer.reducer.util.IntervalUtility; 016 import org.hackystat.telemetry.analyzer.reducer.util.ReducerOptionUtility; 017 import org.hackystat.telemetry.service.server.ServerProperties; 018 import org.hackystat.utilities.time.interval.Interval; 019 import org.hackystat.utilities.time.period.Day; 020 import org.hackystat.utilities.tstamp.Tstamp; 021 022 023 /** 024 * Returns a single stream providing DevTime data in hours. 025 * <p> 026 * Accepts the following options in the following order, although only User and isCumulative 027 * are supported at the current time. 028 * <ol> 029 * <li> EventType: Supply an Event Type to restrict the DevTime to just the time 030 * associated with that Event Type. 031 * Default is "*" which indicates all file types are used in computing the 032 * DevTime. 033 * <li> User: Supply a user email to get DevTime for just that user. 034 * Default is "*" indicating the aggregate DevTime of all project members. 035 * <li> ResourceFilterPattern: Restricts the files over which the DevTime 036 * is computed. Default is "**". 037 * <li> isCumulative: True or false. Default is false. 038 * </ol> 039 * 040 * @author Hongbing Kou, Philip Johnson 041 */ 042 public class DevTimeReducer implements TelemetryReducer { 043 044 045 /** 046 * Computes and returns the required telemetry streams object. 047 * 048 * @param project The project. 049 * @param dpdClient The DPD Client. 050 * @param interval The interval. 051 * @param options The optional parameters. 052 * 053 * @return Telemetry stream collection. 054 * @throws TelemetryReducerException If there is any error. 055 */ 056 public TelemetryStreamCollection compute(Project project, DailyProjectDataClient dpdClient, 057 Interval interval, String[] options) throws TelemetryReducerException { 058 // weird. for some reason we want 'null' as default rather than '*' etc. 059 String eventType = null; 060 String member = null; 061 UriPattern resourcePattern = null; 062 boolean isCumulative = false; 063 //process options 064 if (options.length > 4) { 065 throw new TelemetryReducerException("DevTime reducer takes only 4 optional parameters."); 066 } 067 068 if (options.length >= 1 && ! "*".equals(options[0])) { 069 eventType = options[0]; 070 eventType = eventType.toLowerCase(); 071 } 072 073 if (options.length >= 2 && ! "*".equals(options[1])) { 074 member = options[1]; 075 } 076 077 if (options.length >= 3) { 078 resourcePattern = new UriPattern(options[2]); 079 } 080 081 if (options.length >= 4) { 082 isCumulative = ReducerOptionUtility.parseBooleanOption(4, options[3]); 083 } 084 085 // Find out the DailyProjectData host, throw error if not found. 086 String dpdHost = System.getProperty(ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY); 087 if (dpdHost == null) { 088 throw new TelemetryReducerException("Null DPD host in DevTimeReducer"); 089 } 090 091 // now compute the single telemetry stream. Remember, we only process member and Cumulative. 092 try { 093 TelemetryStream telemetryStream = this.getStream(dpdClient, project, interval, 094 eventType, member, resourcePattern, isCumulative, null); 095 TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval); 096 streams.add(telemetryStream); 097 return streams; 098 } 099 catch (Exception e) { 100 throw new TelemetryReducerException(e); 101 } 102 } 103 104 /** 105 * Gets the telemetry stream. 106 * 107 * @param dpdClient The DailyProjectData client we will contact for the data. 108 * @param project The project. 109 * @param interval The interval. 110 * @param eventType The event type, or null to match all event types. (ignored) 111 * @param member Project member, or null to match all members. 112 * @param filePattern File filter pattern, or null to match all files. (ignored) 113 * @param isCumulative True for cumulative measure. 114 * @param streamTagValue The tag for the generated telemetry stream. 115 * 116 * @return The telemetry stream as required. 117 * @throws Exception If there is any error. 118 */ 119 TelemetryStream getStream(DailyProjectDataClient dpdClient, 120 Project project, Interval interval, String eventType, String member, 121 UriPattern filePattern, boolean isCumulative, Object streamTagValue) 122 throws Exception { 123 TelemetryStream telemetryStream = new TelemetryStream(streamTagValue); 124 List<IntervalUtility.Period> periods = IntervalUtility.getPeriods(interval); 125 double cumulativeDevTime = 0; 126 127 for (IntervalUtility.Period period : periods) { 128 Double value = this.getData(dpdClient, project, period.getStartDay(), period.getEndDay(), 129 eventType, member, filePattern); 130 131 if (value != null) { 132 cumulativeDevTime += value; 133 } 134 135 if (isCumulative) { 136 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), 137 cumulativeDevTime)); 138 } 139 else { 140 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), value)); 141 } 142 } 143 return telemetryStream; 144 } 145 146 /** 147 * Returns a DevTime value for the specified time interval, or null if no SensorData. 148 * Note that the DPD returns DevTime in minutes, but we are going to convert it to hours 149 * since that makes more sense for telemetry. 150 * 151 * NOTE: This function currently ignores the filePattern and eventType constraints and simply 152 * returns either total aggregate devTime (if member is null), or the devTime for a single 153 * member (if the memberEmail is supplied). 154 * 155 * @param dpdClient The DailyProjectData client we will use to get this data. 156 * @param project The project. 157 * @param startDay The start day (inclusive). 158 * @param endDay The end day (inclusive). 159 * @param eventType The event type, or null to match all file types. (ignored) 160 * @param member The project member email, or null to match all members. 161 * @param filePattern File filter pattern, or null to match all files. (ignored) 162 * @throws TelemetryReducerException If anything goes wrong. 163 * 164 * @return The DevTime, or null if there is no DevEvent SensorData for that time period. 165 */ 166 Double getData(DailyProjectDataClient dpdClient, Project project, Day startDay, Day endDay, 167 String eventType, String member, UriPattern filePattern) throws TelemetryReducerException { 168 double devTime = 0; 169 try { 170 // For each day in the interval... 171 for (Day day = startDay; day.compareTo(endDay) <= 0; day = day.inc(1) ) { 172 // Get the DPD... 173 DevTimeDailyProjectData data = 174 dpdClient.getDevTime(project.getOwner(), project.getName(), Tstamp.makeTimestamp(day)); 175 // Go through the DPD per-member data... 176 for (MemberData memberData : data.getMemberData()) { 177 // If this DPD memberdata's owner matches who we want 178 if ((member == null) || "*".equals(member) || 179 (memberData.getMemberUri().endsWith(member))) { 180 devTime += memberData.getDevTime().doubleValue(); 181 } 182 } 183 } 184 } 185 catch (Exception ex) { 186 throw new TelemetryReducerException(ex); 187 } 188 189 //Return null if no data, the DevTime data otherwise. 190 return (devTime > 0) ? new Double((devTime / 60)) : null; 191 } 192 193 }