001 package org.hackystat.tickertape.datasource.hackystat; 002 003 import java.util.HashSet; 004 import java.util.Set; 005 006 import javax.xml.datatype.XMLGregorianCalendar; 007 008 import org.hackystat.dailyprojectdata.client.DailyProjectDataClient; 009 import org.hackystat.dailyprojectdata.client.DailyProjectDataClientException; 010 import org.hackystat.dailyprojectdata.resource.coverage.jaxb.ConstructData; 011 import org.hackystat.dailyprojectdata.resource.coverage.jaxb.CoverageDailyProjectData; 012 import org.hackystat.dailyprojectdata.resource.filemetric.jaxb.FileData; 013 import org.hackystat.dailyprojectdata.resource.filemetric.jaxb.FileMetricDailyProjectData; 014 import org.hackystat.sensorbase.client.SensorBaseClient; 015 import org.hackystat.sensorbase.client.SensorBaseClientException; 016 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataIndex; 017 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataRef; 018 import org.hackystat.utilities.tstamp.Tstamp; 019 020 /** 021 * A datasource corresponding to Hackystat services (sensorbase and dailyprojectdata). 022 * @author Philip Johnson 023 */ 024 public class HackystatProject { 025 026 /** The sensorbase host URL, such as http://dasha.ics.hawaii.edu:9876/sensorbase. */ 027 private String sensorbaseHost; 028 /** The dailyprojectdata host URL, such as http://dasha.ics.hawaii.edu:9877/dailyprojectdata. */ 029 private String dpdHost; 030 /** The user used to get data, such as johnson@hawaii.edu. */ 031 private String user; 032 /** The password for this user account. */ 033 private String password; 034 /** The project whose data will be retrieved. */ 035 private String projectName; 036 /** The owner of the project. */ 037 private String projectOwner; 038 /** The interval in minutes for getting data. */ 039 private int interval; 040 041 /** 042 * Creates a new Hackystat instance, initializing the state. 043 * @param sensorbaseHost The sensorbase host. 044 * @param dailyprojectdataHost The dpd host. 045 * @param user The sensorbase user. 046 * @param password The user's password. 047 * @param projectName A project for which this user has access. 048 * @param projectOwner The project's owner. 049 * @param interval The interval in minutes for which we should ask for data. 050 */ 051 public HackystatProject(String sensorbaseHost, String dailyprojectdataHost, String user, 052 String password, String projectName, String projectOwner, int interval) { 053 this.sensorbaseHost = sensorbaseHost; 054 this.dpdHost = dailyprojectdataHost; 055 this.user = user; 056 this.password = password; 057 this.projectName = projectName; 058 this.projectOwner = projectOwner; 059 this.interval = interval; 060 } 061 062 /** 063 * Contacts Hackystat services, gets data, and returns a summary as a string. 064 * @return The summary of Hackystat activity. 065 */ 066 public String getInfo() { 067 // First, validate that we are authorized for these Hackystat services. 068 SensorBaseClient sensorbaseClient; 069 DailyProjectDataClient dpdClient; 070 try { 071 sensorbaseClient = new SensorBaseClient(sensorbaseHost, user, password); 072 dpdClient = new DailyProjectDataClient(dpdHost, user, password); 073 sensorbaseClient.authenticate(); 074 dpdClient.authenticate(); 075 } 076 catch (Exception e) { 077 return "Error getting Hackystat info. Message is: " + e.getMessage(); 078 } 079 // Second, validate that we can get info about the specified project. 080 try { 081 sensorbaseClient.inProject(projectName, projectOwner); 082 } 083 catch (Exception e) { 084 return String.format("%s is not in project %s", user, projectName); 085 } 086 087 // We're OK, so figure out the time interval of interest. 088 XMLGregorianCalendar endTime = Tstamp.makeTimestamp(); 089 XMLGregorianCalendar startTime = Tstamp.incrementMinutes(endTime, (-1 * interval)); 090 091 // Now get information. 092 StringBuffer info = new StringBuffer(); 093 info.append(getDevEventInfo(sensorbaseClient, startTime, endTime)).append(' '); 094 info.append(getCommitInfo(sensorbaseClient, startTime, endTime)).append(' '); 095 info.append(getUnitTestInfo(sensorbaseClient, startTime, endTime)).append(' '); 096 info.append(getBuildInfo(sensorbaseClient, startTime, endTime)).append(' '); 097 info.append(getCoverageInfo(sensorbaseClient, dpdClient, startTime, endTime)).append(' '); 098 info.append(getSizeInfo(sensorbaseClient, dpdClient, startTime, endTime)).append(' '); 099 100 // Return the info we've found. 101 return info.toString().trim().replace(" ", " "); 102 } 103 104 105 /** 106 * Returns a summary string regarding DevEvent info, or the empty string if no info available. 107 * @param client The SensorBaseClient. 108 * @param startTime The start time. 109 * @param endTime The end time. 110 * @return The summary string, or an empty string. 111 */ 112 private String getDevEventInfo(SensorBaseClient client, XMLGregorianCalendar startTime, 113 XMLGregorianCalendar endTime) { 114 String message = ""; 115 try { 116 SensorDataIndex index = client.getProjectSensorData(projectOwner, projectName, 117 startTime, endTime, "DevEvent"); 118 message = getWorkers(index, "been editing files."); 119 } 120 catch (Exception e) { 121 message = "Error retrieving DevEvent data. "; 122 } 123 return message; 124 } 125 126 /** 127 * Returns a summary string regarding Commit info, or the empty string if no info available. 128 * @param client The SensorBaseClient. 129 * @param startTime The start time. 130 * @param endTime The end time. 131 * @return The summary string, or an empty string. 132 */ 133 private String getCommitInfo(SensorBaseClient client, XMLGregorianCalendar startTime, 134 XMLGregorianCalendar endTime) { 135 String message = ""; 136 try { 137 SensorDataIndex index = client.getProjectSensorData(projectOwner, projectName, 138 startTime, endTime, "Commit"); 139 message = getWorkers(index, "committed files."); 140 } 141 catch (Exception e) { 142 message = "Error retrieving Commit data. "; 143 } 144 return message; 145 } 146 147 /** 148 * Returns a summary string regarding Commit info, or the empty string if no info available. 149 * @param client The SensorBaseClient. 150 * @param startTime The start time. 151 * @param endTime The end time. 152 * @return The summary string, or an empty string. 153 */ 154 private String getUnitTestInfo(SensorBaseClient client, XMLGregorianCalendar startTime, 155 XMLGregorianCalendar endTime) { 156 String message = ""; 157 try { 158 SensorDataIndex index = client.getProjectSensorData(projectOwner, projectName, 159 startTime, endTime, "UnitTest"); 160 message = getWorkers(index, "run tests."); 161 } 162 catch (Exception e) { 163 message = "Error retrieving UnitTest data. "; 164 } 165 return message; 166 } 167 168 /** 169 * Returns a summary string regarding Build info, or the empty string if no info available. 170 * @param client The SensorBaseClient. 171 * @param startTime The start time. 172 * @param endTime The end time. 173 * @return The summary string, or an empty string. 174 */ 175 private String getBuildInfo(SensorBaseClient client, XMLGregorianCalendar startTime, 176 XMLGregorianCalendar endTime) { 177 String message = ""; 178 try { 179 SensorDataIndex index = client.getProjectSensorData(projectOwner, projectName, 180 startTime, endTime, "Build"); 181 message = getWorkers(index, "built the system."); 182 } 183 catch (Exception e) { 184 message = "Error retrieving Build data. "; 185 } 186 return message; 187 } 188 189 /** 190 * Returns a summary string regarding Build info, or the empty string if no info available. 191 * @param client The SensorBaseClient. 192 * @param dpdClient The DailyProjectData client. 193 * @param startTime The start time. 194 * @param endTime The end time. 195 * @return The summary string, or an empty string. 196 */ 197 private String getCoverageInfo(SensorBaseClient client, DailyProjectDataClient dpdClient, 198 XMLGregorianCalendar startTime, XMLGregorianCalendar endTime) { 199 String message = ""; 200 try { 201 SensorDataIndex index = client.getProjectSensorData(projectOwner, projectName, 202 startTime, endTime, "Coverage"); 203 if (!index.getSensorDataRef().isEmpty()) { 204 CoverageDailyProjectData dpd = dpdClient.getCoverage(projectOwner, projectName, startTime, 205 "Line"); 206 double covered = 0.0; 207 double total = 0.0; 208 for (ConstructData data : dpd.getConstructData()) { 209 covered += data.getNumCovered(); 210 total += data.getNumCovered() + data.getNumUncovered(); 211 } 212 int percentCovered = (total == 0) ? 0 : (int)((covered / total) * 100); 213 message = String.format("Line coverage is %s percent.", percentCovered); 214 } 215 } 216 catch (SensorBaseClientException e) { 217 message = "Error retrieving Coverage sensor data. "; 218 } 219 catch (DailyProjectDataClientException e) { 220 message = "Error retrieving Coverage DPD data. "; 221 } 222 return message; 223 } 224 225 /** 226 * Returns a summary string regarding Size info, or the empty string if no info available. 227 * @param client The SensorBaseClient. 228 * @param dpdClient The DailyProjectData client. 229 * @param startTime The start time. 230 * @param endTime The end time. 231 * @return The summary string, or an empty string. 232 */ 233 private String getSizeInfo(SensorBaseClient client, DailyProjectDataClient dpdClient, 234 XMLGregorianCalendar startTime, XMLGregorianCalendar endTime) { 235 String message = ""; 236 try { 237 SensorDataIndex index = client.getProjectSensorData(projectOwner, projectName, 238 startTime, endTime, "FileMetric"); 239 if (!index.getSensorDataRef().isEmpty()) { 240 FileMetricDailyProjectData dpd = dpdClient.getFileMetric(projectOwner, projectName, 241 startTime, "TotalLines"); 242 int totalLines = 0; 243 for (FileData data : dpd.getFileData()) { 244 totalLines += data.getSizeMetricValue(); 245 } 246 message = String.format("The system has %s lines of code.", totalLines); 247 } 248 } 249 catch (SensorBaseClientException e) { 250 message = "Error retrieving FileMetric sensor data. "; 251 } 252 catch (DailyProjectDataClientException e) { 253 message = "Error retrieving FileMetric DPD data. "; 254 } 255 return message; 256 } 257 258 259 /** 260 * Returns a string naming the users in the passed index who have been working on the specified 261 * activity, or the empty string if there is nothing in the index. 262 * @param index The index. 263 * @param activity the activity. 264 * @return The string, possibly empty. 265 */ 266 private String getWorkers (SensorDataIndex index, String activity) { 267 // Find all of the developers who have been committing during this interval. 268 Set<String> workers = new HashSet<String>(); 269 String message = ""; 270 for (SensorDataRef ref : index.getSensorDataRef()) { 271 workers.add(ref.getOwner()); 272 } 273 // Create a message indicating who has been working 274 if (!workers.isEmpty()) { 275 StringBuffer buff = new StringBuffer(); 276 for (String worker : workers) { 277 //String newWorker = worker.replace("@", "+at+").replace(".", "+dot+"); 278 buff.append(worker).append(' '); 279 } 280 message = buff.toString() + 281 ((workers.size() == 1) ? " has " : " have ") + activity + " "; 282 } 283 return message; 284 } 285 }