Index: /trunk/tools/czarmetrics.pl
===================================================================
--- /trunk/tools/czarmetrics.pl	(revision 29686)
+++ /trunk/tools/czarmetrics.pl	(revision 29686)
@@ -0,0 +1,321 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use POSIX qw/strftime/;
+use CGI::Pretty qw[:standard];
+
+use czartool::CzarDb;
+use czartool::Gpc1Db;
+use czartool::Plotter;
+use czartool::StageMetrics;
+
+my $czarDbName = "czardb";
+my $save_temps = undef;
+my $interval = undef;
+my $begin = undef;
+my $end = undef;
+my $burntoolEnd = undef;
+my $day = undef;
+my $verbose = undef;
+
+GetOptions (
+        "dbname=s" => \$czarDbName,
+        "day|y=s" => \$day,
+        "verbose|v" => \$verbose,
+        );
+
+
+print "\n*******************************************************************************\n";
+print "* \n";
+my $quit = 0;
+if (@ARGV) {
+    $quit=1;
+    print "* UNKNKOWN: option                          @ARGV\n";
+}
+if (!$day) {
+    print "* OPTIONAL: choose a single day to plot     -y <date>                   (default=today)\n";
+}
+print "*\n*******************************************************************************\n";
+
+if ($quit) {exit;}
+my @stages = ("burntool", "chip", "cam", "fake", "warp", "stack", "diff", "magic", "magicDS", "dist");
+
+my $gpc1Db = new czartool::Gpc1Db("gpc1", "ippdb01", "ippuser", "ippuser");
+my $czarDb = new czartool::CzarDb($czarDbName, "ippdb01", "ipp", "ipp", $verbose, $save_temps);
+$czarDb->setDateFormat("%Y%m%d-%H%i%s");
+
+# sort out times
+if (!$day) {$day = strftime('%Y-%m-%d', localtime);}
+$begin =  "$day 06:00";
+$end = $czarDb->addInterval($begin, "1 DAY");
+$burntoolEnd = $czarDb->addInterval($begin, "12 HOUR");
+
+
+# create path, dir and html file
+my $path = "./$day";
+mkdir($path, 0777);
+my $plotter = new czartool::Plotter(
+        $czarDb,
+        "%Y%m%d-%H%M%S",
+        "png font \"/usr/share/fonts/corefonts/arial.ttf\" 8",
+        $path,
+        $save_temps);
+
+# create HTML file and write header stuff
+open (HTMLDOC, ">$day/index.html");
+print HTMLDOC "<html>\n";
+print HTMLDOC "<head>\n";
+print HTMLDOC "<title>IPP Metrics for $day</title>\n";
+print HTMLDOC "<h1 align=\"middle\">IPP Metrics for $day</h1>\n";
+
+# summit and burntool exposures
+my $summitExposures = $gpc1Db->countScienceExposuresFromLastNight($day);
+my $burntoolMetrics = new czartool::StageMetrics("burntool", "all_stdscience_labels", $begin, $burntoolEnd);
+$czarDb->runAnalysis($burntoolMetrics);
+print HTMLDOC "<h5  align=\"middle\">Measured from $begin to $end HST<br>\n";
+printf (HTMLDOC "%d exposures taken on summit last night, %d through burntool today</h5>\n", 
+        $summitExposures, $burntoolMetrics->getProcessed() ? $burntoolMetrics->getProcessed() : 0 );
+print HTMLDOC "</head>\n";
+print HTMLDOC "<body>\n";
+
+
+
+
+
+my $labels = undef;
+my $label;
+if (!$czarDb->getStdscienceLabelsInTimePeriod($begin, $end, \$labels)) {next;}
+
+my $size = @{$labels};
+my $table;
+my $haveLabelData;
+my %initialPendingTotals;
+my %processedTotals;
+my %finalPendingTotals;
+my %faultTotals;
+my %labelTables;
+
+
+
+# loop through all labels and create tables for each that have content
+foreach my $row ( @{$labels} ) {
+    my ($label) = @{$row};
+
+
+    $table = "<table border='1'>";
+    $table .= "  <tr>\n";
+    $table .= "    <th>Stage</th>\n";
+    $table .= "    <th>Initial Pending</th>\n";
+    $table .= "    <th>Started</th>\n";
+    $table .= "    <th>25% complete</th>\n";
+    $table .= "    <th>50% complete</th>\n";
+    $table .= "    <th>75% complete</th>\n";
+    $table .= "    <th>90% complete</th>\n";
+    $table .= "    <th>95% complete</th>\n";
+    $table .= "    <th>Finished</th>\n";
+    $table .= "    <th>Time taken</th>\n";
+    $table .= "    <th>Processed</th>\n";
+    $table .= "    <th>Exp per hour</th>\n";
+    $table .= "    <th>Final Pending</th>\n";
+    $table .= "  </tr>\n";
+
+    $haveLabelData = 0;
+    #while ( my ($stage, $value) = each(%processedTotals) ) {
+
+    foreach my $stage ( @stages ){
+
+        my $stageMetrics = new czartool::StageMetrics($stage, $label, $begin, ($stage eq "burntool") ? $burntoolEnd : $end);
+        if (!$czarDb->runAnalysis($stageMetrics)) {next;}
+
+        if (!$stageMetrics->anythingProcessed()) {next;}
+
+        if ($stageMetrics->getProcessed() > 0) {$haveLabelData = 1;};
+
+        $table .= "  <tr>\n";
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getStage() );
+        $table .= sprintf( "    <td>%d</td>\n", $stageMetrics->getInitialPending() ? $stageMetrics->getInitialPending() : "0" );
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getStarted() ? $stageMetrics->getStarted() : "");
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getFinished25() ? $stageMetrics->getFinished25() : "-" );
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getFinished50() ? $stageMetrics->getFinished50() : "-" );
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getFinished75() ? $stageMetrics->getFinished75() : "-" );
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getFinished90() ? $stageMetrics->getFinished90() : "-" );
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getFinished95() ? $stageMetrics->getFinished95() : "-" );
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getFinished100() ? $stageMetrics->getFinished100() : "-" );
+        $table .= sprintf( "    <td>%s</td>\n", $stageMetrics->getTotalTime() ? $stageMetrics->getTotalTime() : "-" );
+        $table .= sprintf( "    <td>%d</td>\n", $stageMetrics->getProcessed() ? $stageMetrics->getProcessed() : "0" );
+        $table .= sprintf( "    <td>%.2f</td>\n", $stageMetrics->getRate() ? $stageMetrics->getRate() : "0" );
+        $table .= sprintf( "    <td>");
+        if ($stageMetrics->getFinalPending() > 0) {$table .= "<font color=\"red\">";}
+        $table .= sprintf( "%d\n", $stageMetrics->getFinalPending() );
+        if ($stageMetrics->getFinalPending() > 0) {$table .= "</font>\n";}
+        $table .= sprintf( "</td>");
+        $table .= "  </tr>\n";
+
+        if (!$initialPendingTotals{$stage}) {$initialPendingTotals{$stage} = 0;}
+        $initialPendingTotals{$stage} = $initialPendingTotals{$stage} + $stageMetrics->getInitialPending();
+        if (!$processedTotals{$stage}) {$processedTotals{$stage} = 0;}
+        $processedTotals{$stage} = $processedTotals{$stage} + $stageMetrics->getProcessed();
+        if (!$finalPendingTotals{$stage}) {$finalPendingTotals{$stage} = 0;}
+        $finalPendingTotals{$stage} = $finalPendingTotals{$stage} + $stageMetrics->getFinalPending();
+        if (!$faultTotals{$stage}) {$faultTotals{$stage} = 0;}
+        $faultTotals{$stage} = $faultTotals{$stage} + $stageMetrics->getFaults();
+    }
+
+    $table .= "</table>";
+    $table .= "<br>\n";
+
+    if ($haveLabelData) { $labelTables{$label} = $table; print "- $label\n" }
+}
+
+
+
+
+# create hyperlink list of list of active labels
+print HTMLDOC "<h2>Active labels for this day</h1>\n";
+foreach my $label (keys (%labelTables)) {
+
+    print HTMLDOC "<a href=\"#$label\">$label</a><br>\n";
+}
+
+# some plots
+print HTMLDOC "<h1 align=\"middle\">Summary plots</h1>\n";
+$plotter->createTimeSeries("all_stdscience_labels", undef, $begin, $end, 0, 0, 0);
+print HTMLDOC "<img src=\"czarplot_linear_all_stdscience_labels_all_stages_t.png\" alt=\"All labels and all stages for $day\" />\n";
+$plotter->createHistogram("all_stdscience_labels", $begin, $end, 0, 0, 0);
+print HTMLDOC "<img src=\"czarplot_linear_all_stdscience_labels_all_stages_h.png\" alt=\"All stages for all_stdscience_labels for $day\" />\n";
+$plotter->createRateTimeSeries("all_stdscience_labels", undef, $begin, $end, "1 HOUR", 0);
+print HTMLDOC "<img src=\"czarplot_linear_all_stdscience_labels_all_stages_r.png\" alt=\"All stages for all_stdscience_labels for $day\" />\n";
+$plotter->plotStorageTimeSeries($begin, $end);
+print HTMLDOC "<img src=\"czarplot_cluster.png\" alt=\"All stages for all_stdscience_labels for $day\" />\n";
+
+
+# survey table
+printSurveyTable();
+
+# table for stage totals
+$table =  "<h2  align=\"middle\">Totals for all labels</h1>\n";
+$table .= "<table border='1'>";
+$table .= "  <tr>\n";
+$table .= "    <th>Stage</th>\n";
+$table .= "    <th>Initial Pending</th>\n";
+$table .= "    <th>Processed</th>\n";
+$table .= "    <th>Final Pending</th>\n";
+$table .= "    <th>Remaining Faults</th>\n";
+$table .= "  </tr>\n";
+foreach my $stage ( @stages ){
+
+    $table .= "  <tr>\n";
+    $table .= sprintf( "    <td>%s</td>\n", $stage );
+    $table .= sprintf( "    <td>%d</td>\n", $initialPendingTotals{$stage} );
+    $table .= sprintf( "    <td>%d</td>\n", $processedTotals{$stage} );
+    $table .= sprintf( "    <td>%d</td>\n", $finalPendingTotals{$stage} );
+    $table .= sprintf( "    <td>%d</td>\n", $faultTotals{$stage} );
+    $table .= "  </tr>\n";
+}
+$table .= "</table>";
+$table .= "<br>\n";
+
+print HTMLDOC $table;
+
+# print all label tables to page
+foreach my $label (keys (%labelTables)) {
+     
+    print HTMLDOC "<a name=\"$label\"></a>\n";
+    print HTMLDOC "<h2  align=\"middle\">$label</h1>\n";
+    $plotter->createTimeSeries($label, undef, $begin, $end, 0, 0, 0);
+    print HTMLDOC "<img src=\"czarplot_linear_".$label."_all_stages_t.png\" alt=\"All stages for $label for $day\" />\n";
+    $plotter->createHistogram($label, $begin, $end, 0, 0, 0);
+    print HTMLDOC "<img src=\"czarplot_linear_".$label."_all_stages_h.png\" alt=\"All stages for $label for $day\" />\n";
+
+    print HTMLDOC $labelTables{$label};
+}
+
+
+print HTMLDOC "<br>\n";
+print HTMLDOC "</body>\n";
+print HTMLDOC "</html>\n";
+close(HTMLDOC);
+
+
+###########################################################################
+#
+# Creates a survey table
+#
+###########################################################################
+
+sub printSurveyTable() {
+
+    print HTMLDOC "<h2  align=\"middle\">Survey statistics</h1>\n";
+    print HTMLDOC "<table border='1'>";
+    print HTMLDOC "  <tr>\n";
+    print HTMLDOC "    <th>Survey</th>\n";
+    print HTMLDOC "    <th>Started burntool</th>\n";
+    print HTMLDOC "    <th>Finished distribution</th>\n";
+    print HTMLDOC "    <th>Time taken</th>\n";
+    print HTMLDOC "  </tr>\n";
+
+# OSS survey
+    my ($started, $finished, $timeTaken) = undef;
+    $czarDb->getDayTimings("OSS.nightlyscience", "burntool", "diff", $begin, \$started, \$finished, \$timeTaken);
+    printSurveyDetails("OSS", $started, $finished, $timeTaken);
+
+# MDF surveys
+    my @mdfs = ("M31.nightlyscience",
+            "MD01.nightlyscience", 
+            "MD02.nightlyscience", 
+            "MD03.nightlyscience", 
+            "MD04.nightlyscience", 
+            "MD05.nightlyscience", 
+            "MD06.nightlyscience", 
+            "MD07.nightlyscience", 
+            "MD08.nightlyscience", 
+            "MD09.nightlyscience", 
+            "MD10.nightlyscience",
+            "MD11.nightlyscience");
+
+    my $earliest = "2099-01-01";
+    my $latest = "2001-01-01";
+
+    my $mdf;
+    my $haveStart = 0;
+    my $haveEnd = 0;
+    foreach $mdf (@mdfs) {
+
+        if ($czarDb->getDayTimings($mdf, "burntool", "dist", $begin, \$started, \$finished, \$timeTaken)) {
+
+            if ($started && $czarDb->isBefore($started, $earliest)) {$earliest = $started; $haveStart = 1;}
+            if ($finished && $czarDb->isBefore($latest, $finished)) {$latest = $finished; $haveEnd = 1;}
+        }
+    }
+
+    if (!$haveStart) {$earliest = undef;}
+    if (!$haveEnd) {$latest = undef;}
+    if (!$haveStart || !$haveEnd) {$timeTaken = undef;}
+
+    else {$timeTaken = $czarDb->diffTimes($latest, $earliest);}
+    printSurveyDetails("MDF", $earliest, $latest, $timeTaken);
+
+    $czarDb->getDayTimings("all_stdscience_labels", "burntool", "dist", $begin, \$started, \$finished, \$timeTaken);
+    printSurveyDetails("All", $started, $finished, $timeTaken);
+
+    print HTMLDOC "</table>\n";
+}
+###########################################################################
+#
+# Creates a row in the survey table
+#
+###########################################################################
+sub printSurveyDetails() {
+    my ($survey, $started, $finished, $timeTaken, $processed, $pending) = @_;
+
+    print HTMLDOC "  <tr>\n";
+    print HTMLDOC "    <td>$survey</td>\n";
+    printf (HTMLDOC "    <td>%s</td>\n", $started ? $started : "no");
+    printf (HTMLDOC "    <td>%s</td>\n", $finished ? $finished : "no");
+    printf (HTMLDOC "    <td>%s</td>\n", $timeTaken ? $timeTaken : "na");
+    print HTMLDOC "  </tr>\n";
+}
+
Index: /trunk/tools/czarplot.pl
===================================================================
--- /trunk/tools/czarplot.pl	(revision 29685)
+++ /trunk/tools/czarplot.pl	(revision 29686)
@@ -9,4 +9,5 @@
 use czartool::CzarDb;
 use czartool::Plotter;
+use czartool::StageMetrics;
 
 my $czarDbName = "czardb";
@@ -57,4 +58,7 @@
     print "* UNKNKOWN: option                          @ARGV\n";
 }
+if ($analysis && !$stage) {
+    $quit = 1;
+    print "* REQUIRED: choose a stage for analsis      -s <chip|cam|warp|etc>      (default=none)\n";}
 if (!$histogram) {
     print "* OPTIONAL: plot histogram                  -h                          (default=off)\n";}
@@ -98,5 +102,5 @@
 
 # default values
-if (!$nebulous && !$histogram && !$timeSeries) {$timeSeries = 1; $histogram = 0;}
+if (!$nebulous && !$histogram && !$analysis && !$timeSeries) {$timeSeries = 1; $histogram = 0;}
 if (!$verbose) {$verbose = 0;}
 if (!$save_temps) {$save_temps = 0;}
@@ -121,4 +125,6 @@
     $begin =  "$day 06:35";
     $end = "$day 23:59";
+        print "JKJKJK '$begin' '$end' \n";
+
 }
 else {
@@ -147,13 +153,5 @@
 if($analysis) {
 
-    my ($started, $finished, $stuck, $processed, $pending, $faults, $totalTime);
-    $czarDb->runAnalysis($label, $stage, $begin, $end, \$started, \$finished, \$stuck, \$processed, \$pending, \$faults, \$totalTime);
-
-    if (defined $started) {print "* Processing started at $started\n";}
-    if (defined $finished) {print "* Processing finished at $finished and took $totalTime\n";}
-    else {print "* Processing has not finished\n";}
-    if (defined $stuck) {print "* Processing has been stuck since $stuck\n";}
-    print "* $processed exposures have been processed, with $pending pending and $faults faults\n";
-
-    print "*******************************************************************************\n";
+    my $stageMetrics = new czartool::StageMetrics($stage, $label, $begin, $end);
+    if ($czarDb->runAnalysis($stageMetrics)) {$stageMetrics->printMe();}
 }
Index: /trunk/tools/czartool/CzarDb.pm
===================================================================
--- /trunk/tools/czartool/CzarDb.pm	(revision 29685)
+++ /trunk/tools/czartool/CzarDb.pm	(revision 29686)
@@ -10,4 +10,6 @@
 use base 'czartool::MySQLDb';
 our @ISA = qw(czartool::MySQLDb);    # inherits from MySQLDb
+
+use czartool::StageMetrics;
 
 ###########################################################################
@@ -49,4 +51,33 @@
     return 1;
 }
+
+###########################################################################
+#
+# Gets stdscience lables active in the provided time period
+# TODO ugly hack to avoind getting update labels
+#
+###########################################################################
+sub getStdscienceLabelsInTimePeriod {
+    my ($self, $begin, $end, $labels) = @_;
+
+    my $query = $self->{_db}->prepare(<<SQL);
+
+    SELECT DISTINCT label
+        FROM chip 
+        WHERE timestamp >= '$begin' 
+        AND timestamp <= '$end' 
+        AND label NOT LIKE '%_ud%'
+        AND label NOT LIKE 'update%'
+        AND label NOT LIKE 'all_%_labels'
+SQL
+
+    if (!$query->execute) {return 0;}
+
+    ${$labels} = $query->fetchall_arrayref();
+
+    return 1;
+}
+
+
 
 ###########################################################################
@@ -264,4 +295,28 @@
 }
 
+###########################################################################
+#
+# Returns the start and end times during one day for the provided label and start/end stages
+#
+###########################################################################
+sub getDayTimings {
+    my ($self, $label, $startStage, $endStage, $begin, $started, $finished, $timeTaken) = @_;
+
+    my $end = $self->addInterval($begin, "1 DAY");
+
+    my $startStageMetrics = new czartool::StageMetrics($startStage, $label, $begin, $end);
+    my $endStageMetrics = new czartool::StageMetrics($endStage, $label, $begin, $end);
+
+    if (!$self->runAnalysis($startStageMetrics)) {return 0;}
+    if (!$self->runAnalysis($endStageMetrics)) {return 0;}
+
+    ${$started} = $startStageMetrics->getStarted();
+    ${$finished} = $endStageMetrics->getFinished100();
+
+    if (!${$started} || !${$finished}) {${$timeTaken} = undef;}
+    else {${$timeTaken} = $self->diffTimes(${$finished}, ${$started});}
+
+    return 1;
+}
 
 ###########################################################################
@@ -271,16 +326,14 @@
 ###########################################################################
 sub runAnalysis {
-    my ($self, 
-            $label, 
-            $stage, 
-            $fromTime, 
-            $toTime, 
-            $_started, 
-            $_finished, 
-            $_stuck,
-            $_processed,
-            $_pending,
-            $_faults,
-            $_totalTime) = @_;
+    my ($self, $stageMetrics) = @_;
+
+    if (!$stageMetrics) {return 0;}
+
+    my $stage = $stageMetrics->getStage();
+    my $label = $stageMetrics->getLabel();
+    my $fromTime = $stageMetrics->getStartTime();
+    my $toTime = $stageMetrics->getEndTime();
+
+    $toTime = $self->addInterval($toTime, "20 MINUTE"); # TODO dodgy
 
     my $query = $self->{_db}->prepare(<<SQL);
@@ -293,6 +346,4 @@
 SQL
 
-    $query->execute;
-
     my $lastProcessed = -1;
     my $lastPending = -1;
@@ -300,20 +351,49 @@
     my $lastTimestamp = undef;
     my ($linearProcessed, $linearPending, $linearFaults);
+
+    # first, count toatl processed in time window so that we can find time of 50%, 75%, 90% etc
+    $query->execute;
+    my $totalProcessed = 0;
+    my $firstRow = 1;
+    while (my @row = $query->fetchrow_array()) {
+        my ($thisTimestamp, $thisPending, $thisFaults, $thisProcessed) = @row;
+
+        # get linear values
+        if($lastProcessed != -1 && $thisProcessed > $lastProcessed) {
+            
+            $totalProcessed = $totalProcessed + ($thisProcessed - $lastProcessed);
+        }
+
+        $lastProcessed = $thisProcessed;
+        if ($firstRow) {$stageMetrics->setInitialPending($thisPending); $firstRow = 0;}
+    }
+
+    # nothing to do here
+    if (!$totalProcessed) {return 1;}
+
+    my $percent25 = $totalProcessed * 0.25;
+    my $percent50 = $totalProcessed * 0.5;
+    my $percent75 = $totalProcessed * 0.75;
+    my $percent90 = $totalProcessed * 0.9;
+    my $percent95 = $totalProcessed * 0.95;
+
     my ($derivProcessed, $derivPending, $derivFaults);
-    my $started = undef;
-    my $finished = undef;
-    
     my $notProcessing = undef;
     my $howLongNotProcessing = undef;
     my $notProcessingLongerThanInterval = 0;
-
     my $stuck = undef;
     my $howLongStuck = undef;
     my $stuckLongerThanInterval = 0;
-
     my $stuffToDo = undef;
-    my $interval = "00:20:00";
-
+    my $interval = "00:20:00"; # TODO dodgy - see above
+    my $started = undef;
+    my $finished = undef;
+   
+    my ($have25, $have50, $have75, $have90, $have95, $have100) = 0;
+
+    # now, loop again, this time recording more data
+    $query->execute;
     $linearProcessed = 0;
+    $lastProcessed = -1;
     while (my @row = $query->fetchrow_array()) {
         my ($thisTimestamp, $thisPending, $thisFaults, $thisProcessed) = @row;
@@ -326,4 +406,10 @@
         $linearPending = $thisPending;
         $linearFaults = $thisFaults;
+
+        if (!$have25 && $linearProcessed >= $percent25) {$stageMetrics->setFinished25($thisTimestamp); $have25 = 1;}
+        if (!$have50 && $linearProcessed >= $percent50) {$stageMetrics->setFinished50($thisTimestamp); $have50 = 1;}
+        if (!$have75 && $linearProcessed >= $percent75) {$stageMetrics->setFinished75($thisTimestamp); $have75 = 1;}
+        if (!$have90 && $linearProcessed >= $percent90) {$stageMetrics->setFinished90($thisTimestamp); $have90 = 1;}
+        if (!$have95 && $linearProcessed >= $percent95) {$stageMetrics->setFinished95($thisTimestamp); $have95 = 1;}
 
         # get first derivative values
@@ -363,19 +449,11 @@
                 if (defined $notProcessing) {
                     
-                    if ($stuckLongerThanInterval) {
-
-#                        print "* $stage procesing was stuck for  $howLongStuck, from $stuck until $lastTimestamp\n";
-                    }
-                    if ($notProcessingLongerThanInterval) {
-
-            #            print " * $stage not procesing for  $howLongNotProcessing, from $notProcessing until $thisTimestamp\n";
-                    }
                     $notProcessing = undef; 
                     $stuck = undef; 
                 }
                 if (!defined $started) {
+
                     $started = $lastTimestamp;
                     $finished = undef; 
-#                    print "* $stage processing started at $started\n";
                 }
             }
@@ -418,33 +496,44 @@
     }
 
-    ${$_processed} = $linearProcessed;
-    ${$_pending} = $stuffToDo;
-    ${$_faults} = $lastFaults;
+    $stageMetrics->setProcessed($linearProcessed);
+    $stageMetrics->setFinalPending($stuffToDo);
+    $stageMetrics->setFaults($lastFaults);
+
+
+    if (!defined $finished) {
+
+        $stageMetrics->setFinished25(undef);
+        $stageMetrics->setFinished50(undef);
+        $stageMetrics->setFinished75(undef);
+        $stageMetrics->setFinished90(undef);
+        $stageMetrics->setFinished95(undef);
+        $stageMetrics->setFinished100(undef);
+        $stageMetrics->setTotalTime($self->diffTimes($lastTimestamp, $started));
+    }
 
     if (defined $finished) {
-        ${$_finished} = $finished;
-        ${$_totalTime} = $self->diffTimes($finished, $started);
-    }
-
-    elsif(defined $started) {
-    
-        ${$_finished} = undef;
-    }
-    
+
+        $stageMetrics->setFinished100($finished);
+        $stageMetrics->setTotalTime($self->diffTimes($finished, $started));
+    }
+
+
     if (!defined $started){
     
-        ${$_started} = undef;
+        $stageMetrics->setStarted(undef);
     }
     else {
     
-        ${$_started} = $started;
+        my $rate = ($stageMetrics->getProcessed()/$self->getTimeInSecs($stageMetrics->getTotalTime) ) * 3600.0;
+        $stageMetrics->setRate($rate);
+        $stageMetrics->setStarted($started);
     }
     if ($stuck && $stuckLongerThanInterval && !defined $finished) {
 
-        ${$_stuck} = $stuck;
+        $stageMetrics->setStuck($stuck);
     }
     else {
     
-        ${$_stuck} = undef;
+        $stageMetrics->setStuck(undef);
     }
 
@@ -517,5 +606,5 @@
             my $timeDiff = $self->diffTimesInSecs($thisTimestamp, $lastTimestamp);
                 
-            if ($thisProcessed >= $lastProcessed){
+            if ($thisProcessed >= $lastProcessed && $timeDiff){
                 $derivProcessed = abs($thisProcessed - $lastProcessed)/$timeDiff; 
             }
@@ -1512,5 +1601,4 @@
     $self->setRevision(12);
 }
-
 1;
 
Index: /trunk/tools/czartool/Gpc1Db.pm
===================================================================
--- /trunk/tools/czartool/Gpc1Db.pm	(revision 29685)
+++ /trunk/tools/czartool/Gpc1Db.pm	(revision 29686)
@@ -56,4 +56,33 @@
 
     return "ERROR";
+}
+
+###########################################################################
+#
+# Returns the number of science exposures taken 'last night' for a given day
+# 
+# Notes:
+#
+# - 03:00 HST = 17:00 last night UTC
+# - 17:00 HST = 07:00 this morning UTC
+# - 'OBJECT' = science exposures
+#
+###########################################################################
+sub countScienceExposuresFromLastNight {
+    my ($self, $day) = @_;
+
+    my $begin = "$day 03"; 
+    my $end = "$day 17"; 
+
+    my $query = $self->{_db}->prepare(<<SQL);
+    SELECT COUNT(*)
+        FROM summitExp 
+        WHERE dateobs > '$begin' 
+        AND dateobs < '$end' AND
+        exp_type = 'OBJECT';
+SQL
+
+    $query->execute;
+return scalar $query->fetchrow_array();
 }
 
@@ -158,5 +187,4 @@
     }
 
-
     $query->execute;
     return scalar $query->fetchrow_array();
Index: /trunk/tools/czartool/MySQLDb.pm
===================================================================
--- /trunk/tools/czartool/MySQLDb.pm	(revision 29685)
+++ /trunk/tools/czartool/MySQLDb.pm	(revision 29686)
@@ -98,4 +98,20 @@
 ###########################################################################
 #
+# Subtracts the provided interval from the provided time
+#
+###########################################################################
+sub subtractInterval {
+    my ($self, $time, $interval) = @_;
+
+      my $query = $self->{_db}->prepare(<<SQL);
+          SELECT '$time' - INTERVAL $interval; 
+SQL
+    $query->execute;
+
+return scalar $query->fetchrow_array();
+}
+
+###########################################################################
+#
 # Finds the difference of two times
 #
@@ -104,6 +120,26 @@
     my ($self, $time1, $time2) = @_;
 
-      my $query = $self->{_db}->prepare(<<SQL);
+    if (!$time1 || !$time2) {return 0;}
+
+    my $query = $self->{_db}->prepare(<<SQL);
          SELECT TIMEDIFF('$time1','$time2'); 
+SQL
+
+    $query->execute;
+    return scalar $query->fetchrow_array();
+}
+
+###########################################################################
+#
+# Returns the provided time in seconds
+#
+###########################################################################
+sub getTimeInSecs {
+    my ($self, $time) = @_;
+
+    if (!$time) {return 0;}
+
+      my $query = $self->{_db}->prepare(<<SQL);
+         SELECT TIME_TO_SEC('$time'); 
 SQL
 
@@ -120,5 +156,7 @@
     my ($self, $time1, $time2) = @_;
 
-      my $query = $self->{_db}->prepare(<<SQL);
+    if (!$time1 || !$time2) {return 0;}
+
+    my $query = $self->{_db}->prepare(<<SQL);
          SELECT TIME_TO_SEC(TIMEDIFF('$time1','$time2')); 
 SQL
@@ -138,20 +176,4 @@
       my $query = $self->{_db}->prepare(<<SQL);
           SELECT INTERVAL('$interval1', '$interval2'); 
-SQL
-    $query->execute;
-
-return scalar $query->fetchrow_array();
-}
-
-###########################################################################
-#
-# Subtracts the provided interval from the provided time
-#
-###########################################################################
-sub subtractInterval {
-    my ($self, $time, $interval) = @_;
-
-      my $query = $self->{_db}->prepare(<<SQL);
-          SELECT '$time' - INTERVAL $interval; 
 SQL
     $query->execute;
Index: /trunk/tools/czartool/Plotter.pm
===================================================================
--- /trunk/tools/czartool/Plotter.pm	(revision 29685)
+++ /trunk/tools/czartool/Plotter.pm	(revision 29686)
@@ -67,8 +67,11 @@
     my $stages = undef;                 
 
-    if (!$selectedStage) {$stages = \@allStages;}
+    if (!$selectedStage) {
+        $stages = \@allStages;
+        $selectedStage = 'all_stages';
+    }
     else {$stages = ["$selectedStage"]};        
 
-    $self->{_czarDb}->getTimeMinMaxDiff($label, $selectedStage, $beginTime, $endTime, \$minX, \$maxX, \$timeDiff);
+    $self->{_czarDb}->getTimeMinMaxDiff($label, "chip", $beginTime, $endTime, \$minX, \$maxX, \$timeDiff);
 
     my $divX;
@@ -95,4 +98,5 @@
 #        "set xrange [\"$minX\":\"$maxX\"];" .
         "set boxwidth;" .
+        "set xtic rotate by -90 scale 0;" .
         "set style data histogram;" .
         "set style histogram rowstacked;" .
@@ -124,5 +128,6 @@
     print GP ";\n";
     close GP;
-}                                                    
+}
+
 ###########################################################################
 #
Index: /trunk/tools/czartool/StageMetrics.pm
===================================================================
--- /trunk/tools/czartool/StageMetrics.pm	(revision 29686)
+++ /trunk/tools/czartool/StageMetrics.pm	(revision 29686)
@@ -0,0 +1,129 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+package czartool::StageMetrics;
+
+###########################################################################
+#
+# Constructor
+#
+###########################################################################
+sub new {
+    my $class = shift;
+    my $self = {
+
+        stage => shift,
+        label => shift,
+        startTime => shift,
+        endTime => shift
+
+    };
+
+    $self->{started} = undef;
+    $self->{stuck} = undef;
+    $self->{processed} = undef;
+    $self->{initialPending} = undef;
+    $self->{finalPending} = undef;
+    $self->{faults} = undef;
+    $self->{totalTime} = undef;
+    $self->{finished25} = undef;
+    $self->{finished50} = undef;
+    $self->{finished75} = undef;
+    $self->{finished90} = undef;
+    $self->{finished95} = undef;
+    $self->{finished100} = undef;
+    $self->{rate} = undef;
+
+    bless $self, $class;
+    return $self;
+}
+
+###########################################################################
+#
+# Returns whether anything has been processed or not
+#
+###########################################################################
+sub anythingProcessed() {
+    my $self = shift; 
+
+    if (!$self->{processed}) {return 0;}
+    if ($self->{processed} < 1) {return 0;}
+    return 1;
+}
+
+###########################################################################
+#
+# Prints all metrics
+#
+###########################################################################
+sub printMe() {
+    my $self = shift;
+
+
+    print "+---------------------+---------------------+------------+-----------+---------+-------+--------+--------+\n";
+    print "|       started       |       finished      | time taken | processed | pending |  rate | faults | stuck? |\n";
+    print "+---------------------+---------------------+------------+-----------+---------+-------+--------+--------+\n";
+    printf( "| %20s", $self->{started} ? $self->{started} : "no");
+    #printf( "| %s", $self->{finished25} ? $self->{finished25} : "no");
+    #printf( "| %s", $self->{finished50} ? $self->{finished50} : "no");
+    #printf( "| %s", $self->{finished75} ? $self->{finished75} : "no");
+    #printf( "| %s", $self->{finished90} ? $self->{finished90} : "no");
+    #printf( "| %s", $self->{finished95} ? $self->{finished95} : "no");
+    printf( "| %20s", $self->{finished100} ? $self->{finished100} : "no");
+    printf( "| %10s ", $self->{totalTime} ? $self->{totalTime} : "na");
+    printf( "| %5d     ", $self->{processed} ? $self->{processed} : 0);
+    printf( "| %5d   ", $self->{pending} ? $self->{pending} : 0);
+    printf( "| %4.2f ", $self->{rate} ? $self->{rate} : 0);
+    printf( "| %5d  ", $self->{faults} ? $self->{faults} : 0);
+    printf( "| %4s   ", $self->{stuck} ? "YES" : "no");
+    print "|\n";
+    print "+---------------------+---------------------+------------+-----------+---------+-------+--------+--------+\n";
+}
+
+###########################################################################
+#
+# Setters for all class variables
+#
+###########################################################################
+sub setStarted() {my $self = shift; $self->{started} = shift;}
+sub setStuck() {my $self = shift; $self->{stuck} = shift;}
+sub setProcessed() {my $self = shift; $self->{processed} = shift;}
+sub setInitialPending() {my $self = shift; $self->{initialPending} = shift;}
+sub setFinalPending() {my $self = shift; $self->{finalPending} = shift;}
+sub setFaults() {my $self = shift; $self->{faults} = shift;}
+sub setTotalTime() {my $self = shift; $self->{totalTime} = shift;}
+sub setFinished25() {my $self = shift; $self->{finished25} = shift;}
+sub setFinished50() {my $self = shift; $self->{finished50} = shift;}
+sub setFinished75() {my $self = shift; $self->{finished75} = shift;}
+sub setFinished90() {my $self = shift; $self->{finished90} = shift;}
+sub setFinished95() {my $self = shift; $self->{finished95} = shift;}
+sub setFinished100() {my $self = shift; $self->{finished100} = shift;}
+sub setRate() {my $self = shift; $self->{rate} = shift;}
+
+###########################################################################
+#
+# Getters for all class variables
+#
+###########################################################################
+sub getStartTime() {my $self = shift; return $self->{startTime};}
+sub getEndTime() {my $self = shift; return $self->{endTime};}
+sub getLabel() {my $self = shift; return $self->{label};}
+sub getStage() {my $self = shift; return $self->{stage};}
+sub getStarted() {my $self = shift; return $self->{started};}
+sub getStuck() {my $self = shift; return $self->{stuck};}
+sub getProcessed() {my $self = shift; return $self->{processed};}
+sub getInitialPending() {my $self = shift; return $self->{initialPending};}
+sub getFinalPending() {my $self = shift; return $self->{finalPending};}
+sub getFaults() {my $self = shift; return $self->{faults};}
+sub getTotalTime() {my $self = shift; return $self->{totalTime};}
+sub getFinished25() {my $self = shift; return $self->{finished25};}
+sub getFinished50() {my $self = shift; return $self->{finished50};}
+sub getFinished75() {my $self = shift; return $self->{finished75};}
+sub getFinished90() {my $self = shift; return $self->{finished90};}
+sub getFinished95() {my $self = shift; return $self->{finished95};}
+sub getFinished100() {my $self = shift; return $self->{finished100};}
+sub getRate() {my $self = shift; return $self->{rate};}
+
+1;
