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.commit.jaxb.CommitDailyProjectData; 007 import org.hackystat.dailyprojectdata.resource.commit.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.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 Churn data. 023 * Churn is linesAdded + linesDeleted. 024 * <p> 025 * Options: 026 * <ol> 027 * <li> member: The project member whose churn data is to be returned, or "*" for all members. 028 * <li> isCumulative: True or false. Default is false. 029 * </ol> 030 * 031 * @author Philip Johnson 032 */ 033 public class ChurnReducer implements TelemetryReducer { 034 035 /** 036 * Computes and returns the required telemetry streams object. 037 * 038 * @param project The project. 039 * @param dpdClient The DPD Client. 040 * @param interval The interval. 041 * @param options The optional parameters. 042 * 043 * @return Telemetry stream collection. 044 * @throws TelemetryReducerException If there is any error. 045 */ 046 public TelemetryStreamCollection compute(Project project, DailyProjectDataClient dpdClient, 047 Interval interval, String[] options) throws TelemetryReducerException { 048 String member = null; 049 boolean isCumulative = false; 050 //process options 051 if (options.length > 2) { 052 throw new TelemetryReducerException("Churn reducer takes 2 optional parameters."); 053 } 054 if (options.length >= 1) { 055 member = options[0]; 056 } 057 058 if (options.length >= 2) { 059 try { 060 isCumulative = Boolean.valueOf(options[1]); 061 } 062 catch (Exception e) { 063 throw new TelemetryReducerException("Illegal cumulative value.", e); 064 } 065 } 066 067 // Find out the DailyProjectData host, throw error if not found. 068 String dpdHost = System.getProperty(ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY); 069 if (dpdHost == null) { 070 throw new TelemetryReducerException("Null DPD host in ChurnReducer"); 071 } 072 073 // now get the telemetry stream. 074 try { 075 TelemetryStream telemetryStream = this.getStream(dpdClient, project, interval, 076 member, isCumulative, null); 077 TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval); 078 streams.add(telemetryStream); 079 return streams; 080 } 081 catch (Exception e) { 082 throw new TelemetryReducerException(e); 083 } 084 } 085 086 /** 087 * Gets the telemetry stream. 088 * 089 * @param dpdClient The DailyProjectData client we will contact for the data. 090 * @param project The project. 091 * @param interval The interval. 092 * @param member The member, or "*" for all members. 093 * @param isCumulative True for cumulative measure. 094 * @param streamTagValue The tag for the generated telemetry stream. 095 * 096 * @return The telemetry stream as required. 097 * 098 * @throws Exception If there is any error. 099 */ 100 TelemetryStream getStream(DailyProjectDataClient dpdClient, 101 Project project, Interval interval, 102 String member, boolean isCumulative, Object streamTagValue) 103 throws Exception { 104 TelemetryStream telemetryStream = new TelemetryStream(streamTagValue); 105 List<IntervalUtility.Period> periods = IntervalUtility.getPeriods(interval); 106 long cumulativeCount = 0; 107 108 for (IntervalUtility.Period period : periods) { 109 Long value = this.getData(dpdClient, project, period.getStartDay(), period.getEndDay(), 110 member); 111 112 if (value != null) { 113 cumulativeCount += value; 114 } 115 116 if (isCumulative) { 117 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), 118 cumulativeCount)); 119 } 120 else { 121 telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), value)); 122 } 123 } 124 return telemetryStream; 125 } 126 127 /** 128 * Returns a Churn value for the specified time interval, or null if no Churn SensorData. 129 * 130 * @param dpdClient The DailyProjectData client we will use to get this data. 131 * @param project The project. 132 * @param startDay The start day (inclusive). 133 * @param endDay The end day (inclusive). 134 * @param member The member email, or "*" for all members. 135 * @throws TelemetryReducerException If anything goes wrong. 136 * 137 * @return The Churn count, or null if there is no Commit SensorData for that time period. 138 */ 139 Long getData(DailyProjectDataClient dpdClient, Project project, Day startDay, Day endDay, 140 String member) throws TelemetryReducerException { 141 long count = 0; 142 boolean hasData = false; 143 try { 144 // For each day in the interval... 145 for (Day day = startDay; day.compareTo(endDay) <= 0; day = day.inc(1) ) { 146 // Get the DPD... 147 CommitDailyProjectData data = 148 dpdClient.getCommit(project.getOwner(), project.getName(), Tstamp.makeTimestamp(day)); 149 // Go through the DPD per-member data... 150 for (MemberData memberData : data.getMemberData()) { 151 if ((member == null) || "*".equals(member) || 152 (memberData.getMemberUri().endsWith(member))) { 153 hasData = true; 154 count += memberData.getLinesAdded() + memberData.getLinesDeleted() + 155 memberData.getLinesModified(); 156 } 157 } 158 } 159 } 160 catch (Exception ex) { 161 throw new TelemetryReducerException(ex); 162 } 163 164 //Return null if no data, the Churn counts otherwise. 165 return (hasData) ? Long.valueOf(count) : null; 166 } 167 168 }