001    /*
002    @license.text@
003     */
004    package biz.hammurapi.metrics.persistent;
005    
006    import java.io.File;
007    import java.io.IOException;
008    import java.sql.SQLException;
009    import java.util.ArrayList;
010    import java.util.Collection;
011    import java.util.Date;
012    import java.util.HashMap;
013    import java.util.Iterator;
014    
015    import javax.xml.parsers.FactoryConfigurationError;
016    import javax.xml.parsers.ParserConfigurationException;
017    import javax.xml.transform.TransformerException;
018    import javax.xml.transform.stream.StreamResult;
019    
020    import org.jfree.chart.ChartFactory;
021    import org.jfree.chart.JFreeChart;
022    import org.jfree.chart.plot.PlotOrientation;
023    import org.jfree.data.time.TimeSeries;
024    import org.jfree.data.time.TimeSeriesCollection;
025    import org.jfree.data.xy.DefaultHighLowDataset;
026    import org.jfree.data.xy.OHLCDataset;
027    
028    import biz.hammurapi.config.MapContext;
029    import biz.hammurapi.config.RuntimeConfigurationException;
030    import biz.hammurapi.metrics.MetricsException;
031    import biz.hammurapi.sql.SQLProcessor;
032    import biz.hammurapi.sql.hypersonic.HypersonicServerDataSource;
033    import biz.hammurapi.util.ClassTransformerFactory;
034    import biz.hammurapi.util.Visitor;
035    
036    
037    /**
038     * @author Pavel Vlasov
039     * @version $Revision: 1.2 $
040     */
041    public class PeriodVisualizer {
042            private AbstractPeriod period;
043        
044            /**
045         * Constructor from array of metric names
046         * @param processor Processor to retrieve metrics from the database
047         * @param metrics Metrics to be retrieved
048         * @param from Start of metrics period
049         * @param to End of metrics period
050         * @param cacheMode Cache mode
051         * @param outputFormat FORMAT_PNG or FORMAT_JPG
052         * @param width Chart width
053         * @param height Chart height
054         * @param maxSlices Maximum number of slices on chart. Series having more slices will be downsliced - adjacent slices will be merged. 
055         * @throws SQLException
056         */
057        public PeriodVisualizer(AbstractPeriod period) {
058            this.period=period;
059        }
060            
061            private JFreeChart intencityChart;
062            
063            /**
064             * @return
065             */
066            public JFreeChart intensityChart() {
067                    if (intencityChart==null) {
068                            TimeSeriesCollection tsc=new TimeSeriesCollection();
069                    TimeSeries timeSeries = new TimeSeries(normalizeName(period.getName()), SliceTimePeriod.class);
070                            SliceTimePeriod sp=null;
071                    for (int i=0; i<period.getSlices().length; i++) {
072                        sp=new SliceTimePeriod(period.getSlices()[i], sp);
073                        timeSeries.add(sp, period.getSlices()[i].getMeasurements());
074                    }
075                    
076                    tsc.addSeries(timeSeries);
077                    
078                    intencityChart = ChartFactory.createXYBarChart(
079                            "Intensity (measurements/slice)",  // Title
080                            "Time",           // X-Axis label
081                            true,
082                            "Intensity",           // Y-Axis label
083                            tsc,          // Dataset
084                            PlotOrientation.VERTICAL,
085                            true,                // Show legend
086                            true,
087                            true
088                           );
089                    }
090                    
091                    return intencityChart;
092            }
093            
094            private JFreeChart totalChart;
095            
096            /**
097             * @return
098             */
099            public JFreeChart totalChart() {
100                    if (totalChart==null) {
101                            TimeSeriesCollection tsc=new TimeSeriesCollection();
102                    TimeSeries timeSeries = new TimeSeries(normalizeName(period.getName()), SliceTimePeriod.class);
103                            SliceTimePeriod sp=null;
104                    for (int i=0; i<period.getSlices().length; i++) {
105                        sp=new SliceTimePeriod(period.getSlices()[i], sp);
106                        timeSeries.add(sp, period.getSlices()[i].getTotal());
107                    }
108                    
109                    tsc.addSeries(timeSeries);
110                    
111                    totalChart = ChartFactory.createXYBarChart(
112                            "Total (sum of values per slice)",  // Title
113                            "Time",           // X-Axis label
114                            true,
115                            "Total",           // Y-Axis label
116                            tsc,          // Dataset
117                            PlotOrientation.VERTICAL,
118                            true,                // Show legend
119                            true,
120                            true
121                           );
122                    }
123                    
124                    return totalChart;
125            }
126            
127            private JFreeChart averageChart;
128            
129            private String normalizeName(String name) {
130                    return name.length()>25 ? "Category" : name;
131            }
132            
133            /**
134             * @return
135             */
136            public JFreeChart averageChart() {
137                    if (averageChart==null) {
138                            TimeSeriesCollection tsc=new TimeSeriesCollection();
139                    TimeSeries timeSeries = new TimeSeries(normalizeName(period.getName()), SliceTimePeriod.class);
140                            SliceTimePeriod sp=null;
141                    for (int i=0; i<period.getSlices().length; i++) {
142                        sp=new SliceTimePeriod(period.getSlices()[i], sp);
143                        timeSeries.add(sp, period.getSlices()[i].getTotal()/period.getSlices()[i].getMeasurements());
144                    }
145                    
146                    tsc.addSeries(timeSeries);
147                    
148                        averageChart = ChartFactory.createTimeSeriesChart(
149                                "Average",  // Title
150                                "Time",     // X-Axis label
151                                "Average",  // Y-Axis label
152                                tsc,        // Dataset
153                                true,       // Show legend
154                                true,
155                                true
156                               );
157                    }
158                    
159                    return averageChart;
160            }
161            
162            private JFreeChart childrenIntencityChart;
163            
164            /**
165             * @return
166             * @throws SQLException
167             */
168            public JFreeChart childrenIntensityChart() throws SQLException {
169                    if (childrenIntencityChart==null && period.getSubCategories().size()>1) {
170                            TimeSeriesCollection tsc=new TimeSeriesCollection();
171                            Iterator it=period.getSubCategories().iterator();
172                    while (it.hasNext()) {
173                            Period p=(Period) it.next();
174                            TimeSeries timeSeries = new TimeSeries(period.getLabel(p.getId()), SliceTimePeriod.class);
175                                    SliceTimePeriod sp=null;
176                            for (int i=0; i<p.getSlices().length; i++) {
177                                sp=new SliceTimePeriod(p.getSlices()[i], sp);
178                                timeSeries.add(sp, p.getSlices()[i].getMeasurements());
179                            }
180                            tsc.addSeries(timeSeries);                      
181                    }
182                    
183                        childrenIntencityChart = ChartFactory.createTimeSeriesChart(
184                                "Intensity (measurements/time)",  // Title
185                                "Time",     // X-Axis label
186                                "Intensity",  // Y-Axis label
187                                tsc,        // Dataset
188                                true,       // Show legend
189                                true,
190                                true
191                               );
192                    }
193                    
194                    return childrenIntencityChart;
195            }
196            
197            private JFreeChart childrenTotalChart;
198    
199            /**
200             * @return
201             * @throws SQLException
202             */
203            public JFreeChart childrenTotalChart() throws SQLException {
204                    if (childrenTotalChart==null && period.getSubCategories().size()>1) {
205                            TimeSeriesCollection tsc=new TimeSeriesCollection();
206                            Iterator it=period.getSubCategories().iterator();
207                    while (it.hasNext()) {
208                            Period p=(Period) it.next();
209                            TimeSeries timeSeries = new TimeSeries(period.getLabel(p.getId()), SliceTimePeriod.class);
210                                    SliceTimePeriod sp=null;
211                            for (int i=0; i<p.getSlices().length; i++) {
212                                sp=new SliceTimePeriod(p.getSlices()[i], sp);
213                                timeSeries.add(sp, p.getSlices()[i].getTotal());
214                            }
215                            tsc.addSeries(timeSeries);                      
216                    }
217                    
218                        childrenTotalChart = ChartFactory.createTimeSeriesChart(
219                                "Total (sum of values per slice)",  // Title
220                                "Time",     // X-Axis label
221                                "Total",  // Y-Axis label
222                                tsc,        // Dataset
223                                true,       // Show legend
224                                true,
225                                true
226                               );
227                    }
228                    
229                    return childrenTotalChart;
230            }
231    
232            private JFreeChart childrenAverageChart;
233            
234            /**
235             * @return
236             * @throws SQLException
237             */
238            public JFreeChart childrenAverageChart() throws SQLException {
239                    if (childrenAverageChart==null && period.getSubCategories().size()>1) {
240                            TimeSeriesCollection tsc=new TimeSeriesCollection();
241                            Iterator it=period.getSubCategories().iterator();
242                    while (it.hasNext()) {
243                            Period p=(Period) it.next();
244                            TimeSeries timeSeries = new TimeSeries(period.getLabel(p.getId()), SliceTimePeriod.class);
245                                    SliceTimePeriod sp=null;
246                            for (int i=0; i<p.getSlices().length; i++) {
247                                sp=new SliceTimePeriod(p.getSlices()[i], sp);
248                                timeSeries.add(sp, p.getSlices()[i].getTotal()/p.getSlices()[i].getMeasurements());
249                            }
250                            tsc.addSeries(timeSeries);                      
251                    }
252                    
253                        childrenAverageChart = ChartFactory.createTimeSeriesChart(
254                                "Average",  // Title
255                                "Time",     // X-Axis label
256                                "Average",  // Y-Axis label
257                                tsc,        // Dataset
258                                true,       // Show legend
259                                true,
260                                true
261                               );
262                    }
263                    return childrenAverageChart;
264            }
265    
266            private JFreeChart mainChart;
267    
268        /**
269             * @param i
270             * @return
271             */
272            public JFreeChart mainChart() {         
273                    if (mainChart==null) {
274                            Slice[] periodSlices = period.getSlices();
275                            int size = periodSlices.length;
276                            
277                            Date[] dates=new Date[size];
278                            double[] higs=new double[size];
279                            double[] lows=new double[size];
280                            double[] opens=new double[size];
281                            double[] closes=new double[size];
282                            double[] volumes=new double[size];
283                            
284                            for (int k=0; k<size; k++) {
285                                Slice slice=periodSlices[k];
286                                dates[k]=new Date(slice.getTimeFrom());
287                                higs[k]=slice.getMaxValue();
288                                lows[k]=slice.getMinValue();                            
289                                double avg = slice.getMeasurements()==0 ? 0 : slice.getTotal()/slice.getMeasurements();
290                                opens[k]=Math.max(slice.getMeasurements()==0 ? avg : avg-slice.getDeviation(), slice.getMinValue());                            
291                                closes[k]=Math.min(slice.getMeasurements()==0 ? avg : opens[k]+slice.getDeviation(), slice.getMaxValue());
292                                volumes[k]=slice.getTotal();
293                            }
294                            
295                            OHLCDataset dataSet=new DefaultHighLowDataset(
296                                            normalizeName(period.getName()),
297                                    dates,
298                                    higs,
299                                    lows,
300                                    opens,
301                                    closes,
302                                    volumes);
303                            
304                            mainChart = ChartFactory.createCandlestickChart(
305                                            normalizeName(period.getName()),
306                                    "Time",
307                                    "Value",
308                                    dataSet,
309                                    true
310                                   );
311                    }
312                    
313                    return mainChart;
314            }
315    
316            /**
317             * @return Returns the period.
318             */
319            public AbstractPeriod getPeriod() {
320                    return period;
321            }
322            
323            private Collection children;
324            
325            public Collection getChildren() throws SQLException {
326                    if (children==null) {
327                            children=new ArrayList();
328                            Iterator it=period.getSubCategories().iterator();
329                            while (it.hasNext()) {
330                                    children.add(new PeriodVisualizer((Period) it.next()));
331                            }
332                    }
333                    
334                    return children;
335            }
336    
337            /**
338                     * @param root
339                     */
340                    public static void generateHtmlDoc(AbstractPeriod root, final File outputDir, final int width, final int height) {
341                            root.accept(new Visitor() {
342                                    
343                                    ClassTransformerFactory ctf=new ClassTransformerFactory(null, null, true);
344            
345                                    public boolean visit(Object target) {
346                                            try {
347                                                    AbstractPeriod period = (AbstractPeriod) target;
348                                                    ctf.transform(period, "category", null, null, null, null, new StreamResult(new File(outputDir, target instanceof Period ? "period_"+((Period) period).getId()+".html" : "index.html")));
349            //                                                                              DOMUtils.serialize(period, "category", new File(outputDir, "period_"+period.getId()+".xml"));                                                                   
350                                                    PeriodVisualizer visualizer=new PeriodVisualizer(period);
351                                                    String periodId = period instanceof Period ? String.valueOf(((Period) period).getId()) : ((SyntheticPeriod) period).getId();                                                            
352                                                    
353                                                    ChartHelper chartHelper = new ChartHelper(ChartHelper.FORMAT_JPG, width, height);                                                                               
354                                                    chartHelper.output(visualizer.averageChart(), new File(outputDir, "average_"+periodId+".jpg"));
355                                                    chartHelper.output(visualizer.childrenAverageChart(), new File(outputDir, "childrenAverage_"+periodId+".jpg"));
356                                                    chartHelper.output(visualizer.childrenIntensityChart(), new File(outputDir, "childrenIntensity_"+periodId+".jpg"));
357                                                    chartHelper.output(visualizer.childrenTotalChart(), new File(outputDir, "childrenTotal_"+periodId+".jpg"));
358                                                    chartHelper.output(visualizer.intensityChart(), new File(outputDir, "intensity_"+periodId+".jpg"));
359                                                    chartHelper.output(visualizer.mainChart(), new File(outputDir, "main_"+periodId+".jpg"));
360                                                    chartHelper.output(visualizer.totalChart(), new File(outputDir, "total_"+periodId+".jpg"));
361                                            } catch (ParserConfigurationException e) {
362                                                    throw new RuntimeConfigurationException(e);
363                                            } catch (FactoryConfigurationError e) {
364                                                    throw new RuntimeConfigurationException(e);
365                                            } catch (IOException e) {
366                                                    throw new RuntimeConfigurationException(e);
367                                            } catch (TransformerException e) {
368                                                    throw new RuntimeConfigurationException(e);
369                                            } catch (SQLException e) {
370                                                    throw new RuntimeConfigurationException(e);
371                                            }
372                                            return true;
373                                    }
374                                    
375                            });
376                    }
377                    
378                    public static void main(String args[]) throws Exception {
379                            File outputDir=new File("_MetricDebug"); 
380                            if (outputDir.exists()) {
381                                    if (!outputDir.isDirectory()) {
382                                            throw new MetricsException(outputDir.getAbsolutePath()+" in not a directory");
383                                    }
384                            } else {
385                                    if (!outputDir.mkdirs()) {
386                                            throw new MetricsException("Cannot create directory "+outputDir.getAbsolutePath());
387                                    }
388                            }
389                            
390                            // Generation of html's
391                            SQLProcessor processor=new SQLProcessor(new HypersonicServerDataSource("localhost/metrics", null), new MapContext(new HashMap()));
392                            new MetricNormalizer(processor).normalize();
393                            PeriodFactory factory=new PeriodFactory(processor);                     
394                            MetricLocator locator=new MetricLocator(processor);
395                            AbstractPeriod root=factory.getPeriod(locator.find("poc.wsusjxla3b3a147.send size").intValue(), 0, Long.MAX_VALUE, 20, true);
396                            PeriodVisualizer.generateHtmlDoc(root, outputDir, 500, 300);
397                            
398                    }
399    }