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.codeissue.jaxb.CodeIssueDailyProjectData;
007    import org.hackystat.dailyprojectdata.resource.codeissue.jaxb.CodeIssueData;
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 CodeIssue counts.
023     * <p>
024     * Accepts the following options in the following order.
025     * <ol>
026     *  <li> Tool: A string indicating the tool whose CodeIssue data is to be counted, or "*" for all
027     *       tools found.
028     *       Default is "*".
029     *  <li> Type: A string to restrict the counts to the CodeIssue's with the specified type, or 
030     *       "*" to match all types.  Default is "*".
031     * </ol>
032     * 
033     * @author Philip Johnson
034     */
035    public class CodeIssueReducer implements TelemetryReducer {
036     
037      /**
038       * Computes and returns the required telemetry streams object.
039       *
040       * @param project The project.
041       * @param dpdClient The DPD Client.
042       * @param interval The interval.
043       * @param options The optional parameters.
044       *
045       * @return Telemetry stream collection.
046       * @throws TelemetryReducerException If there is any error.
047       */
048      public TelemetryStreamCollection compute(Project project, DailyProjectDataClient dpdClient, 
049          Interval interval, String[] options) throws TelemetryReducerException {
050        String tool = null;
051        String type = null;
052        //process options
053        if (options.length > 2) {
054          throw new TelemetryReducerException("CodeIssue reducer takes 2 optional parameters.");
055        }
056    
057        if (options.length >= 1 && !"*".equals(options[0])) {
058          tool = options[0];
059        }
060    
061        if (options.length >= 2 && !"*".equals(options[1])) {
062          type = options[1];
063        }
064        
065        // Find out the DailyProjectData host, throw error if not found.
066        String dpdHost = System.getProperty(ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY);
067        if (dpdHost == null) {
068          throw new TelemetryReducerException("Null DPD host in BuildReducer");
069        }
070    
071        // now compute the single telemetry stream. 
072        try {
073          TelemetryStream telemetryStream = this.getStream(dpdClient, project, interval,  
074              tool, type, null);
075          TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval);
076          streams.add(telemetryStream);
077          return streams;
078        } 
079        catch (Exception e) {
080          throw new TelemetryReducerException(e);
081        }
082      }
083    
084      /**
085       * Gets the telemetry stream.
086       * 
087       * @param dpdClient The DailyProjectData client we will contact for the data. 
088       * @param project The project.
089       * @param interval The interval.
090       * @param tool The tool whose CodeIssue data is to be returned.
091       * @param type The type of CodeIssue data to return.
092       * @param streamTagValue The tag for the generated telemetry stream.
093       * 
094       * @return The telemetry stream as required.
095       * @throws Exception If there is any error.
096       */
097      TelemetryStream getStream(DailyProjectDataClient dpdClient, 
098          Project project, Interval interval, String tool, String type, 
099          Object streamTagValue) throws Exception {
100        TelemetryStream telemetryStream = new TelemetryStream(streamTagValue);
101        List<IntervalUtility.Period> periods = IntervalUtility.getPeriods(interval);
102    
103        for (IntervalUtility.Period period : periods) {
104          Long value = this.getData(dpdClient, project, period.getStartDay(), period.getEndDay(),
105              tool, type);
106          telemetryStream.addDataPoint(new TelemetryDataPoint(period.getTimePeriod(), value));
107        }
108        return telemetryStream;
109      }
110      
111      /**
112       * Returns a CodeIssue count for the specified time interval, or null if no SensorData. 
113       *
114       * @param dpdClient The DailyProjectData client we will use to get this data. 
115       * @param project The project.
116       * @param startDay The start day (inclusive).
117       * @param endDay The end day (inclusive).
118       * @param tool The tool whose CodeIssue data is to be counted, or null for all tools.
119       * @param type The type of CodeIssue data to count, or null for all CodeIssue types.
120       * @throws TelemetryReducerException If anything goes wrong.
121       *
122       * @return The CodeIssue count, or null if there is no CodeIssue SensorData for that time period. 
123       */
124      Long getData(DailyProjectDataClient dpdClient, Project project, Day startDay, Day endDay, 
125          String tool, String type) throws TelemetryReducerException {
126        try {
127          // Work backward through the interval, and return as soon as we get matching CodeIssue info.
128          for (Day day = endDay; day.compareTo(startDay) >= 0; day = day.inc(-1) ) {
129            // Get the DPD...
130            CodeIssueDailyProjectData dpdData = 
131              dpdClient.getCodeIssue(project.getOwner(), project.getName(), Tstamp.makeTimestamp(day),
132                  tool, type);
133            // Keep going if there's no data.
134            if ((dpdData.getCodeIssueData() == null) || dpdData.getCodeIssueData().isEmpty()) {
135              continue;
136            }
137            // Otherwise we have CodeIssue data, so return the total field.
138            return getTotalIssues(dpdData);
139          }
140        }
141        catch (Exception ex) {
142          throw new TelemetryReducerException(ex);
143        }
144        // Never found appropriate CodeIssue data in this interval, so return null.
145        return null;
146      }
147      
148      /**
149       * Returns the total number of code issues in this CodeIssueDailyProjectData object.
150       * @param dpdData The CodeIssueDailyProjectData object.
151       * @return The total number of issues. 
152       */
153      private long getTotalIssues (CodeIssueDailyProjectData dpdData) {
154        long count = 0;
155        for (CodeIssueData data : dpdData.getCodeIssueData()) {
156          count += data.getNumIssues();
157        }
158        return count;
159      }
160    
161    }