001 package org.hackystat.projectbrowser.page.projectportfolio.detailspanel.chart; 002 003 import java.io.Serializable; 004 import java.util.List; 005 import org.apache.wicket.markup.html.panel.Panel; 006 import org.hackystat.projectbrowser.page.projectportfolio.jaxb.Measures.Measure; 007 import org.hackystat.projectbrowser.page.projectportfolio.jaxb.Measures.Measure. 008 ParticipationParameters; 009 010 /** 011 * Classify stream into 3 categories: GOOD, AVERAGE and POOR, 012 * according to the participation of members. 013 * 014 * @author Shaoxuan Zhang 015 * 016 */ 017 public class StreamParticipationClassifier implements Serializable, StreamClassifier { 018 019 /** Support serialization. */ 020 private static final long serialVersionUID = 8335959874933854182L; 021 /** Name of this classifier. */ 022 public static final String name = "Participation"; 023 /** The expected percentage of members that participated. */ 024 private double memberPercentage; 025 /** The lowest value with which a member is consider as participating. */ 026 private double thresholdValue; 027 /** The expected percentage of time that has events happened. */ 028 private double frequencyPercentage; 029 /** If the condition scale with granularity. */ 030 private boolean scaleWithGranularity; 031 032 /** 033 * @param memberPercentage the {@link memberPercentage} to set. 034 * @param thresholdValue the {@link thresholdValue} to set. 035 * @param frequencyPercentage the {@link frequencyPercentage} to set. 036 * @param scaleWithGranularity the scaleWithGranularity to set. 037 */ 038 public StreamParticipationClassifier(final double memberPercentage, final double thresholdValue, 039 final double frequencyPercentage, boolean scaleWithGranularity) { 040 this.memberPercentage = memberPercentage; 041 this.thresholdValue = thresholdValue; 042 this.frequencyPercentage = frequencyPercentage; 043 this.scaleWithGranularity = scaleWithGranularity; 044 } 045 046 /** 047 * Parse the given MiniBarChart and produce a StreamCategory result. 048 * If there are more than {@link memberPercentage} members who contribute value higher 049 * than {@link thresholdValue} in more than {@link frequencyPercentage} of time, the stream will 050 * be consider as GOOD. 051 * 052 * If there are not enough active members to get a GOOD, but the merged stream satisfies 053 * the GOOD criteria except member percentage, the stream will be considered as AVERAGE. 054 * 055 * Otherwise, it will consider as POOR. 056 * @param chart the input chart 057 * @return StreamCategory enumeration. 058 */ 059 public PortfolioCategory getStreamCategory(MiniBarChart chart) { 060 double thresholdValue = this.thresholdValue; 061 if (this.scaleWithGranularity) { 062 if ("Week".equals(chart.granularity)) { 063 thresholdValue *= 7; 064 } 065 else if ("Month".equals(chart.granularity)) { 066 thresholdValue *= 30; 067 } 068 } 069 int activeMember = 0; 070 for (List<Double> stream : chart.streams) { 071 if (isTheStreamGood(stream, thresholdValue)) { 072 activeMember++; 073 } 074 } 075 if (activeMember > this.memberPercentage * chart.streams.size() / 100) { 076 return PortfolioCategory.GOOD; 077 } 078 if (isTheStreamGood(chart.streamData, thresholdValue)) { 079 return PortfolioCategory.AVERAGE; 080 } 081 return PortfolioCategory.POOR; 082 } 083 084 /** 085 * Check if the stream contains more than {@link frequencyPercentage} values that are greater 086 * than {@link thresholdValue}. 087 * @param stream the given stream 088 * @param thresholdValue the filter threshold. 089 * @return true or false. 090 */ 091 private boolean isTheStreamGood(List<Double> stream, double thresholdValue) { 092 int goodValueCount = 0; 093 for (Double value : stream) { 094 if (value >= thresholdValue) { 095 goodValueCount++; 096 } 097 } 098 if (goodValueCount >= this.frequencyPercentage / 100 * stream.size() ) { 099 return true; 100 } 101 return false; 102 } 103 104 /** 105 * @param memberPercentage the memberPercentage to set 106 */ 107 public void setMemberPercentage(double memberPercentage) { 108 this.memberPercentage = memberPercentage; 109 } 110 111 /** 112 * @return the memberPercentage 113 */ 114 public double getMemberPercentage() { 115 return memberPercentage; 116 } 117 118 /** 119 * @param thresholdValue the thresholdValue to set 120 */ 121 public void setThresholdValue(double thresholdValue) { 122 this.thresholdValue = thresholdValue; 123 } 124 125 /** 126 * @return the thresholdValue 127 */ 128 public double getThresholdValue() { 129 return thresholdValue; 130 } 131 132 /** 133 * @param frequencyPercentage the frequencyPercentage to set 134 */ 135 public void setFrequencyPercentage(double frequencyPercentage) { 136 this.frequencyPercentage = frequencyPercentage; 137 } 138 139 /** 140 * @return the frequencyPercentage 141 */ 142 public double getFrequencyPercentage() { 143 return frequencyPercentage; 144 } 145 146 /** 147 * Return the panel for users to configure this stream trend classifer. 148 * User can customize higher, lower thresholds and if higher is better. 149 * @param id The Wicket component id. 150 * @return a Panel 151 */ 152 public Panel getConfigurationPanel(String id) { 153 return new StreamParticipationClassifierConfigurationPanel(id, this); 154 } 155 156 /** 157 * Return the category of the given value. 158 * If the value < 0, NA will be return. Otherwise, always return OTHER. 159 * @param value the given value. 160 * @return a {@link PortfolioCategory} result 161 */ 162 public PortfolioCategory getValueCategory(double value) { 163 if (value < 0) { 164 return PortfolioCategory.NA; 165 } 166 return PortfolioCategory.OTHER; 167 } 168 169 /** 170 * @return the name of this classifier. 171 */ 172 public String getName() { 173 return name; 174 } 175 176 /** 177 * Save classifier's setting into the given {@link Measure} instance. 178 * @param measure the given {@link Measure} instance 179 */ 180 public void saveSetting(Measure measure) { 181 ParticipationParameters param = new ParticipationParameters(); 182 param.setFrequencyPercentage(frequencyPercentage); 183 param.setMemberPercentage(memberPercentage); 184 param.setThresoldValue(thresholdValue); 185 param.setScaleWithGranularity(this.scaleWithGranularity); 186 measure.setParticipationParameters(param); 187 } 188 }