/* * [HitStats.java] * * Summary: Applet usually run as as application to plot. * * Copyright: (c) 1995-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.5 2006-03-05 */ package com.mindprod.stats; import com.mindprod.common18.BigDate; import com.mindprod.common18.Build; import com.mindprod.common18.Common18; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.VersionCheck; import com.mindprod.csv.CSVReader; import javax.swing.JApplet; import javax.swing.JLabel; import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.io.BufferedReader; import java.io.EOFException; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.TimeZone; import static java.lang.System.*; /** * Applet usually run as as application to plot. * * @author Roedy Green, Canadian Mind Products * @version 1.5 2006-03-05 * @since 1995 */ public final class HitStats extends JApplet { /** * 2 hits per pixel */ static final int HITS_PER_PIXEL = 3; /** * max number of hits we can plot. half a pixel per hit */ static final int MAX_HITS_PER_DAY; /** * lowest number of hits that will fit on plot */ static final int MIN_HITS_PER_DAY = 300; /** * 2 pixels per day */ static final int PIXELS_PER_DAY = 2; /** * height of Applet without application bar */ private static final int APPLET_HEIGHT = 1145; /** * width of Applet without application bar */ private static final int APPLET_WIDTH = 1910; /** * how many day to plot, including today. No scrolling so must fit. 2 pixels per day */ private static final int DAYS_TO_PLOT; private static final String TITLE_STRING = "Canadian Mind Products Hit Stats"; private static final String VERSION_STRING = "1.5"; /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * for for titles and About buttons */ private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 ); static { DAYS_TO_PLOT = ( APPLET_WIDTH - 100 ) / PIXELS_PER_DAY; MAX_HITS_PER_DAY = ( APPLET_HEIGHT - 60 ) * HITS_PER_PIXEL + MIN_HITS_PER_DAY; } /** * title of graph */ private JLabel title; /** * The canvas upon which we draw the graph of hit stats. */ private StatsGraphCanvas canvas; /** * Get readings to plot from a CSV file. * * @return array of daily hit count readings to plot */ @SuppressWarnings( { "UnnecessaryLocalVariable" } ) private static Reading[] getReadings() { // range that we will plot, only get Data for that range. final int todayOrdinal = BigDate.today( TimeZone.getDefault() ).getOrdinal(); // first day to plot final int fromOrdinal = todayOrdinal - DAYS_TO_PLOT + 1;// last n days // last day to plot. final int toOrdinal = todayOrdinal; final File hitsFile = new File( Build.MINDPROD_SOURCE + "/stats", "hits.csv" ); // temporary ArrayList to accumulate the readings. final ArrayList a = new ArrayList<>( DAYS_TO_PLOT ); try { final CSVReader r = new CSVReader( new BufferedReader( new FileReader( hitsFile ) ) ); // get readings from CSV file int prevOrdinal = Integer.MIN_VALUE; try { // loop till hit eof while ( true ) { final String yyyymmdd = r.getYYYYMMDD(); final int hits = r.getInt(); final int ordinal = BigDate.toOrdinal( yyyymmdd ); if ( prevOrdinal < ordinal ) { prevOrdinal = ordinal; } else { throw new IllegalArgumentException( "hits stats out of date order near " + new BigDate( prevOrdinal ) + " to " + new BigDate( ordinal ) + " " + hits + " near line " + r.lineCount() ); } if ( fromOrdinal <= ordinal && ordinal <= toOrdinal ) { a.add( new Reading( ordinal, hits ) ); } r.skipToNextLine(); } } catch ( EOFException e ) { r.close(); } } catch ( IOException e ) { out.println( "trouble reading hits.csv " + e.getMessage() ); return null; } // one reading per sampling. final Reading[] samples = a.toArray( new Reading[ a.size() ] ); assert samples.length > 0 : "no hit counts"; // one slot per day, not one per sample // firstOrd is first day we got. firstOrdinal is first day requested. final int firstOrd = samples[ 0 ].x; // may be shrunk later Reading[] dailyHits = new Reading[ DAYS_TO_PLOT ]; for ( int i = 1; i < samples.length; i++ ) { final int x1 = samples[ i - 1 ].x - firstOrd; final double y1 = samples[ i - 1 ].y; final int x2 = samples[ i ].x - firstOrd; final double y2 = samples[ i ].y; final double hitsPerDay = ( y2 - y1 ) / ( x2 - x1 ); for ( int day = x1 + 1; day <= x2; day++ ) { // same hits per day applies over whole sample range dailyHits[ day ] = new Reading( day + firstOrd, hitsPerDay ); } } // may have some unset days on the head and tail. int headNulls = 0; for ( int i = 0; i < DAYS_TO_PLOT; i++ ) { if ( dailyHits[ i ] == null ) { headNulls++; } else { break; } } int tailNulls = 0; for ( int i = DAYS_TO_PLOT - 1; i >= 0; i-- ) { if ( dailyHits[ i ] == null ) { tailNulls++; } else { break; } } if ( headNulls > 0 || tailNulls > 0 ) { final int newSize = dailyHits.length - headNulls - tailNulls; final Reading[] shrunk = new Reading[ newSize ]; System.arraycopy( dailyHits, headNulls, shrunk, 0, newSize ); dailyHits = shrunk; } out.println( dailyHits[ dailyHits.length - 1 ] ); return dailyHits; } private void layoutGridBag( Container contentPane ) { contentPane.add( title, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 2, 10, 5, 5 ), 340, 13 ) ); contentPane.add( canvas, new GridBagConstraints( 0, 1, 1, 1, 100.0, 100.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 0, 0, 0, 0 ), 0, 0 ) ); } /** * main * * @param args ignored. */ public static void main( String args[] ) { HybridJ.fireup( new HitStats(), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); } // end main /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ public void destroy() { title = null; canvas = null; } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { if ( !VersionCheck.isJavaVersionOK( 1, 8, 0, this ) ) { return; } Common18.setLaf(); Container contentPane = this.getContentPane(); contentPane.setLayout( new GridBagLayout() ); title = new JLabel( TITLE_STRING + " " + VERSION_STRING + " build:" + Build.BUILD_NUMBER, JLabel.CENTER ); title.setFont( FONT_FOR_TITLE ); title.setForeground( FOREGROUND_FOR_TITLE ); canvas = new StatsGraphCanvas( getReadings() ); canvas.setForeground( Color.BLUE ); layoutGridBag( contentPane ); validate(); } // end init } /** * Canvas to draw hit stats. *

* created with Intellij Idea * * @author Roedy Green, Canadian Mind Products */ final class StatsGraphCanvas extends DailyGraphCanvas { /** * how many horizontal pixels before the data after the left Y axis */ private static final int LEFT_MARGIN = 0; /** * how many horizontal pixels after the data before the right Y axis */ private static final int RIGHT_MARGIN = 2; /** * What we plot */ private final Reading[] toPlot; /** * constructor * * @param toPlot readings to plot, e.g. hits stats, fats, weights. */ StatsGraphCanvas( Reading[] toPlot ) { this.toPlot = toPlot; } /** * draw the graph * * @param g graphic context where paint occurs */ public void paint( Graphics g ) { // determine the range of data to plot from the range of data supplied // in the constructor, will usually be shrunk from what was is csv. final int fromOrdinal = toPlot[ 0 ].x; final int toOrdinal = toPlot[ toPlot.length - 1 ].x; setScale( g, fromOrdinal - LEFT_MARGIN, toOrdinal + RIGHT_MARGIN, HitStats.PIXELS_PER_DAY /* scalex */, HitStats.MIN_HITS_PER_DAY, HitStats.MAX_HITS_PER_DAY, 1.0 / HitStats.HITS_PER_PIXEL/* scaley */ ); xaxis( Color.BLACK ); yaxis( 10, 100, false, Color.BLACK ); yaxis( 10, 100, true, Color.BLACK ); plot( toPlot, 0, 0/* no averaging */, Color.RED ); plot( toPlot, 13, 0/* past 2 weeks average, 2 of each day of week. */, Color.BLUE ); } // end paint }