/* * ----------------------------------------------------------------------- * File: $HeadURL$ * Revision $LastChangedRevision$ * Last Modified: $LastChangedDate$ * Last Change by: $LastChangedBy$ * ----------------------------------------------------------------------- * Copyright (C) 2007 Keith Stribley <devel@thanlwinsoft.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * ----------------------------------------------------------------------- */ /** * */ package org.thanlwinsoft.languagetest.eclipse.chart; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.eclipse.birt.chart.model.Chart; import org.eclipse.birt.chart.model.ChartWithAxes; import org.eclipse.birt.chart.model.attribute.Anchor; import org.eclipse.birt.chart.model.attribute.AxisType; import org.eclipse.birt.chart.model.attribute.FontDefinition; import org.eclipse.birt.chart.model.attribute.IntersectionType; import org.eclipse.birt.chart.model.attribute.LineStyle; import org.eclipse.birt.chart.model.attribute.Marker; import org.eclipse.birt.chart.model.attribute.MarkerType; import org.eclipse.birt.chart.model.attribute.TickStyle; import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl; import org.eclipse.birt.chart.model.attribute.impl.FontDefinitionImpl; import org.eclipse.birt.chart.model.component.Axis; import org.eclipse.birt.chart.model.component.Series; import org.eclipse.birt.chart.model.component.impl.SeriesImpl; import org.eclipse.birt.chart.model.data.DateTimeDataSet; import org.eclipse.birt.chart.model.data.NumberDataElement; import org.eclipse.birt.chart.model.data.NumberDataSet; import org.eclipse.birt.chart.model.data.SeriesDefinition; import org.eclipse.birt.chart.model.data.impl.DateTimeDataSetImpl; import org.eclipse.birt.chart.model.data.impl.NumberDataElementImpl; import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl; import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl; import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl; import org.eclipse.birt.chart.model.type.ScatterSeries; import org.eclipse.birt.chart.model.type.impl.ScatterSeriesImpl; import org.thanlwinsoft.languagetest.MessageUtil; import org.thanlwinsoft.languagetest.eclipse.LanguageTestPlugin; import org.thanlwinsoft.languagetest.eclipse.chart.view.ChartHistoryProvider; import org.thanlwinsoft.schemas.languagetest.history.ItemType; import org.thanlwinsoft.schemas.languagetest.history.ModuleHistoryType; import org.thanlwinsoft.schemas.languagetest.history.ResultType; import org.thanlwinsoft.languagetest.language.test.TestType; /** * @author keith * */ public class CumulativeTestPassChart implements ChartHistoryProvider { public final static int CUMULATIVE_PASSES = 0; public final static int TEST_COUNT = 1; private final static int NEW_BIN = 0; // count of new items on one day private final static int TEST_BIN = 1; // count of items tested on one day private final static int PASS_BIN = 2; // count of items passed 1st time on one day private final static int DELTA_PASS_BIN = 3; // difference between previous pass state and todays private final static int NUM_BINS = 4; private int type = CUMULATIVE_PASSES; private SortedMap <Date,Integer[]>resultMap = null; //<long time, int[]> private DateTimeDataSet timeValues = null; private NumberDataSet passValues = null; private NumberDataSet totalValues = null; public final static String CHART_LABEL_SIZE_PREF = "ChartLabelSize"; public final static String CHART_TITLE_SIZE_PREF = "ChartTitleSize"; public CumulativeTestPassChart() { reset(); LanguageTestPlugin.getPrefStore().setDefault(CHART_LABEL_SIZE_PREF, 8.0); LanguageTestPlugin.getPrefStore().setDefault(CHART_TITLE_SIZE_PREF, 9.0); } /* (non-Javadoc) * @see org.thanlwinsoft.languagetest.eclipse.views.ChartHistoryProvider#createChart() */ @SuppressWarnings("unchecked") public Chart createChart() { if (resultMap.size() == 0) return null; float titleFontSize = LanguageTestPlugin.getPrefStore().getFloat(CHART_TITLE_SIZE_PREF); float labelFontSize = LanguageTestPlugin.getPrefStore().getFloat(CHART_LABEL_SIZE_PREF); ChartWithAxes cwaScatter = ChartWithAxesImpl.create( ); // Plot cwaScatter.getBlock( ).setBackground( ColorDefinitionImpl.WHITE( ) ); cwaScatter.getPlot( ).getClientArea( ).getOutline( ).setVisible( false ); cwaScatter.getPlot( ) .getClientArea( ) .setBackground( ColorDefinitionImpl.create( 255, 255, 225 ) ); // Title String title = ""; switch (type) { case CUMULATIVE_PASSES: title = MessageUtil.getString("CumulativeGraphTitle"); break; case TEST_COUNT: title = MessageUtil.getString("TestCountGraphTitle"); break; } cwaScatter.getTitle( ) .getLabel( ) .getCaption( ) .setValue( title );//$NON-NLS-1$ FontDefinition tfd = FontDefinitionImpl.copyInstance(cwaScatter.getTitle().getLabel().getCaption().getFont()); tfd.setSize(titleFontSize); cwaScatter.getTitle().getLabel().getCaption().setFont(tfd); //Legend cwaScatter.getLegend( ).setVisible( true ); FontDefinition lfd = FontDefinitionImpl.copyInstance(cwaScatter.getLegend().getText().getFont()); lfd.setSize(labelFontSize); cwaScatter.getLegend().getText().setFont(lfd); cwaScatter.getLegend().setAnchor(Anchor.EAST_LITERAL); // X-Axis Axis xAxisPrimary = ( (ChartWithAxesImpl) cwaScatter ).getPrimaryBaseAxes( )[0]; //xAxisPrimary.setType( AxisType.LINEAR_LITERAL ); xAxisPrimary.setType( AxisType.DATE_TIME_LITERAL ); xAxisPrimary.getLabel( ) .getCaption( ) .setColor( ColorDefinitionImpl.GREEN( ).darker( ) ); xAxisPrimary.getLabel().getCaption().setFont(FontDefinitionImpl.copyInstance(lfd)); xAxisPrimary.getMajorGrid( ).setTickStyle( TickStyle.BELOW_LITERAL ); xAxisPrimary.getMajorGrid( ) .getLineAttributes( ) .setStyle( LineStyle.DOTTED_LITERAL ); xAxisPrimary.getMajorGrid( ) .getLineAttributes( ) .setColor( ColorDefinitionImpl.GREY( ) ); xAxisPrimary.getMajorGrid( ).getLineAttributes( ).setVisible( true ); xAxisPrimary.getOrigin( ).setType( IntersectionType.VALUE_LITERAL ); xAxisPrimary.getTitle().getCaption().setValue(MessageUtil.getString("DateAxisTitle")); xAxisPrimary.getTitle( ).setVisible( true ); FontDefinition fd = FontDefinitionImpl.copyInstance( xAxisPrimary.getTitle().getCaption().getFont()); fd.setSize(labelFontSize); xAxisPrimary.getTitle().getCaption().setFont(fd); // Y-Axis Axis yAxisPrimary = ( (ChartWithAxesImpl) cwaScatter ).getPrimaryOrthogonalAxis( xAxisPrimary ); yAxisPrimary.getLabel( ) .getCaption( ) .setColor( ColorDefinitionImpl.BLUE( ) ); yAxisPrimary.getLabel().getCaption().setFont(FontDefinitionImpl.copyInstance(lfd)); yAxisPrimary.setType( AxisType.LINEAR_LITERAL ); yAxisPrimary.getMajorGrid( ).setTickStyle( TickStyle.LEFT_LITERAL ); yAxisPrimary.getMajorGrid( ) .getLineAttributes( ) .setStyle( LineStyle.DOTTED_LITERAL ); yAxisPrimary.getMajorGrid( ) .getLineAttributes( ) .setColor( ColorDefinitionImpl.GREY( ) ); yAxisPrimary.getMajorGrid( ).getLineAttributes( ).setVisible( true ); yAxisPrimary.getOrigin( ).setType( IntersectionType.MIN_LITERAL ); NumberDataElement yOrigin = NumberDataElementImpl.create(0.0); yAxisPrimary.getOrigin().setValue(yOrigin); // Data Set //NumberDataSet timeValues = NumberDataSetImpl.create(getTimeArray()); timeValues = DateTimeDataSetImpl.create(getTimeArray()); passValues = null; totalValues = null; switch (type) { case CUMULATIVE_PASSES: passValues = NumberDataSetImpl.create(getIntegratedArray(DELTA_PASS_BIN)); totalValues = NumberDataSetImpl.create(getIntegratedArray(NEW_BIN)); break; case TEST_COUNT: passValues = NumberDataSetImpl.create(getIntArray(PASS_BIN)); totalValues = NumberDataSetImpl.create(getIntArray(TEST_BIN)); break; default: // for testing only } // X-Series Series seBase = SeriesImpl.create( ); seBase.setDataSet( timeValues ); SeriesDefinition sdX = SeriesDefinitionImpl.create( ); xAxisPrimary.getSeriesDefinitions( ).add( sdX ); sdX.getSeries( ).add( seBase ); // Y-Series ScatterSeries ss = (ScatterSeries) ScatterSeriesImpl.create( ); for ( int i = 0; i < ss.getMarkers( ).size( ); i++ ) { ( (Marker) ss.getMarkers( ).get( i ) ).setType( MarkerType.TRIANGLE_LITERAL ); } ss.getLabel( ).setVisible( false ); ss.setSeriesIdentifier(MessageUtil.getString("NumItemsPassed")); ss.setDataSet( passValues ); ss.getLineAttributes().setColor(ColorDefinitionImpl.GREEN()); ss.getLineAttributes().setStyle(LineStyle.SOLID_LITERAL); ss.getLineAttributes().setVisible(true); ss.setPaletteLineColor(true); ScatterSeries ss2 = (ScatterSeries) ScatterSeriesImpl.create( ); for ( int i = 0; i < ss2.getMarkers( ).size( ); i++ ) { Marker marker = (Marker) ss2.getMarkers( ).get( i ); marker.setType( MarkerType.DIAMOND_LITERAL ); } ss2.getLabel( ).setVisible( false ); ss2.setSeriesIdentifier(MessageUtil.getString("TotalNumItems")); ss2.setDataSet( totalValues ); ss2.getLineAttributes().setColor(ColorDefinitionImpl.BLUE()); ss2.getLineAttributes().setStyle(LineStyle.SOLID_LITERAL); ss2.getLineAttributes().setVisible(true); ss2.setPaletteLineColor(true); SeriesDefinition sdY = SeriesDefinitionImpl.create( ); yAxisPrimary.getSeriesDefinitions( ).add( sdY ); sdY.getSeriesPalette( ).update( ColorDefinitionImpl.GREEN() ); sdY.getSeries( ).add( ss ); SeriesDefinition sdY2 = SeriesDefinitionImpl.create( ); yAxisPrimary.getSeriesDefinitions( ).add( sdY2 ); sdY2.getSeriesPalette( ).update( ColorDefinitionImpl.BLUE() ); sdY2.getSeries( ).add( ss2 ); return cwaScatter; } /** * @param pass_bin2 * @return */ private double [] getIntArray(int bin) { double [] values = new double [resultMap.size()]; Iterator<Map.Entry<Date,Integer[]>> iBin = resultMap.entrySet().iterator(); int i = 0; while (iBin.hasNext()) { Map.Entry<Date,Integer[]> me = iBin.next(); Integer [] bins = me.getValue(); values[i++] = bins[bin]; } return values; } /** * @param new_bin2 * @return */ private double [] getIntegratedArray(int bin) { double [] values = new double [resultMap.size()]; Iterator<Map.Entry<Date,Integer[]>> iBin = resultMap.entrySet().iterator(); int i = 0; int sum = 0; while (iBin.hasNext()) { Map.Entry<Date,Integer[]> me = iBin.next(); Integer [] bins = me.getValue(); sum += bins[bin]; values[i++] = sum; } return values; } /** * @return */ private long [] getTimeArray() { long [] values = new long [resultMap.size()]; if (resultMap.size() == 0) return values; Iterator<Date> iBin = resultMap.keySet().iterator(); int i = 0; while (iBin.hasNext()) { Date date = iBin.next(); values[i++] = date.getTime(); } return values; } // /** // * @return // */ // private double [] getDayArray() // { // double [] values = new double [resultMap.size()]; // if (resultMap.size() == 0) return values; // Iterator<Date> iBin = resultMap.keySet().iterator(); // int i = 0; // final double MSEC_IN_DAY = 3600 * 24 * 1000; // values[i++] = 0; // Date startDate = iBin.next(); // while (iBin.hasNext()) // { // Date date = iBin.next(); // values[i++] = (date.getTime() - startDate.getTime()) / MSEC_IN_DAY; // } // return values; // } /* (non-Javadoc) * @see org.thanlwinsoft.languagetest.eclipse.views.ChartHistoryProvider#parse(org.thanlwinsoft.schemas.languagetest.ModuleHistoryType, org.thanlwinsoft.schemas.languagetest.TestType) */ public void parse(ModuleHistoryType testHistory, TestType type) { for (int i = 0; i < testHistory.sizeOfItemArray(); i++) { ItemType item = testHistory.getItemArray(i); org.thanlwinsoft.schemas.languagetest.history.TestType tt = null; if (TestType.LISTENING_FOREIGN_NATIVE.equals(type)) { tt = item.getFL(); } else if (TestType.READING_FOREIGN_NATIVE.equals(type)) { tt = item.getFR(); } else if (TestType.READING_NATIVE_FOREIGN.equals(type)) { tt = item.getNR(); } if (tt != null) { processItemResults(tt); } } } protected void processItemResults(org.thanlwinsoft.schemas.languagetest.history.TestType tt) { boolean prevResult = false; Date prevDate = new Date(0); // This assumes that results are sorted with most recent at the end for (int j = 0; j < tt.sizeOfResultArray(); j++) { ResultType result = tt.getResultArray(j); Date date = new Date(result.getTime()); GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(date); // round to the nearest day calendar.set(Calendar.HOUR, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); Integer [] bins = null; if (resultMap.containsKey(calendar.getTime())) { bins = resultMap.get(calendar.getTime()); } else { bins = new Integer [NUM_BINS]; Arrays.fill(bins, 0); resultMap.put(calendar.getTime(), bins); } if (j == 0) { bins[NEW_BIN]++; } if (prevDate.equals(calendar.getTime()) == false) { bins[TEST_BIN]++; if (result.getPass()) bins[PASS_BIN]++; if (prevResult != result.getPass()) { int delta = (result.getPass()) ? 1 : -1; bins[DELTA_PASS_BIN] += delta; } } prevResult = result.getPass(); prevDate = calendar.getTime(); } } /* (non-Javadoc) * @see org.thanlwinsoft.languagetest.eclipse.views.ChartHistoryProvider#reset() */ public void reset() { resultMap = new TreeMap<Date, Integer[]>(); } /* (non-Javadoc) * @see org.thanlwinsoft.languagetest.eclipse.views.ChartHistoryProvider#getTypes() */ public String[] getTypes() { final String [] typeNames = new String [] { MessageUtil.getString("CumulativePasses"), MessageUtil.getString("TestCount")//, "Testing" }; return typeNames; } /* (non-Javadoc) * @see org.thanlwinsoft.languagetest.eclipse.views.ChartHistoryProvider#setType(int) */ public void setType(int type) { this.type = type; } }