[RC5] Statsbox or Add-on suggestion-request...

abigail at delanet.com abigail at delanet.com
Sat Jan 22 23:00:46 EST 2000


> Date: Tue, 18 Jan 2000 01:17:07 +0800
> From: "Darren Tay" <gluino at bigfoot.com>
> Subject: [RC5] Statsbox or Add-on suggestion-request...
> 
> - -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> Hi,
> 
> I have an obvious suggestion for providing nicer stats for participants.
> (If this thing already exists somewhere, sorry, could you point me to it
> please?)
> 
> Rather than (or in addition to) the "Participant's Block Submission
> History", could we add a line graph plot of *cumulative* work done VS
> time, for each participant? These stats would be much more useful than
> Block Submission History, esp. for people (like myself) who do not
> fetch-flush on a strictly regular schedule. A cumulative work VS time
> graph would show change in keyrate nicely together with total work done.
> 
> I just did this with MSExcel, using data from Submission History... There
> was a slight problem with Excel97 not being able to automatically
> understand the dates as dates.
> So anyway, if this graph thing can't be done on the statsbox, perhaps
> someone can do an add-on? Perhaps one that reads directly from the html
> table of Submission History. (pretty-please...)


Here's a program I use daily that plots out the cumulative work done
for all todays participants of a team.

Call it as "program --id <ID>", where <ID> is the id number of your team.
Other command line options: "--directory <DIR>", the directory to place
the file in (defaults to current directory), "--file <FILE>", file name
of the image (defaults to people.png), and "--plot <FILE>", the name to
invoke gnuplot with (defaults to /usr/local/bin/gnuplot).

There's an MIT style license slapped on this, so you can use this freely.

#!/opt/perl/bin/perl -w

use strict;
use LWP::Simple;
use Date::Calc qw /Add_Delta_Days/;
use Getopt::Long;

GetOptions ("id=i"         =>  \my $id,
            "directory=s"  =>  \my $dir,
            "file=s"       =>  \my $file,
            "plot=s"       =>  \my $plot,
);

# Check for options, fill in defaults.
die "No or illegal id value\n" unless defined $id && $id !~ /\D/;

# Yeah, so, you can't have a directory or a file called 0.
# Add ?? to Perl, and you can. ;-)
$dir      ||= '.';                 # Directory to place images in.
$file     ||= 'people.png';        # File name.
$plot     ||= '/usr/local/bin/gnuplot';


# URL templates.
my $server  = 'http://stats.distributed.net';
my $begin   = "$server/rc5-64/tmember.php3?team=$id&source=y";
my $history = "$server/rc5-64/phistory.php3?id";


# Get the team submission list of yesterday.
my $text    = get $begin or die "Failed to get $begin\n";

# Get the team name.
my ($name)  = $text =~ m{RC5-64\s+(.*\S)\s+Members\s+Yesterday};
$name = "Team #$id" unless defined $name;


# Our list of participants.
my @participants;


# Daily amounts of the participants, indexed on id.
my %amounts;


# For each participants, get the id, name, and total blocks submitted
# for the team.
while ($text =~
       /<a\s+href="psummary\.php3\?id=(\d+)"><font\s+color="\#\w+">
        ([^<]+).*\n.*\n.*\n\s+<td.+<td.+<td\D+([\d,]+).+<td/xg) {
   (my $total = $3) =~ y/,//d;
    push @participants => [$1, $2, $total];
}


# Initialization of extremes.
my $fdate = "9999/99/99";
my $ldate = "0000/00/00";
my $max   =  0;


# Map month names to month numbers.
my %M = do {my $i;
            map {$_ => ++ $i} qw /Jan Feb Mar Apr May Jun
                                  Jul Aug Sep Oct Nov Dec/};

foreach my $participant (@participants) {
    my ($id, $name, $total) = @$participant;
    print "Fetching $name ($id)\n";

    # Get the submission history of a participant.
    my $text = get "$history=$id" or die "Failed to get $history=$id\n";

    my @list;

    # Parse the text for date and blocks on that date.
    while ($text =~
          m{<td>(\d{1,2})-(\w{3})-(\d{4})</td>\s+<td align="right">([,\d]+)}g) {
        my ($year, $month, $day, $amount) = ($3, $M {$2}, $1, $4);
        $amount =~ y/,//d;
        push @list => [$year, $month, $day, $amount];
    }

    unless (@list) {
        warn "No entries found for $name\n";
        next;
    }

    # Sort the entries. It should already be reverse sorted, but we
    # don't want to count on it.
    # The sorting time is dwarved by the fetch time, so don't bother
    # with fancy preprocessing.
    $amounts {$id} = [sort {$a -> [0] <=> $b -> [0] ||
                            $a -> [1] <=> $b -> [1] ||
                            $a -> [2] <=> $b -> [2]} @list];


    # Insert 0's for days not submitted.
    my ($cy, $cm, $cd) = @{$amounts {$id} -> [0]} [0, 1, 2];
    my @tmp = shift @{$amounts {$id}};
    while (@{$amounts {$id}}) {
       ($cy, $cm, $cd) = Add_Delta_Days ($cy, $cm, $cd, 1);
        while ($cy != $amounts {$id} -> [0] -> [0] ||
               $cm != $amounts {$id} -> [0] -> [1] ||
               $cd != $amounts {$id} -> [0] -> [2]) {
            push @tmp => [$cy, $cm, $cd => 0];
           ($cy, $cm, $cd) = Add_Delta_Days ($cy, $cm, $cd, 1);
        }
        push @tmp => shift @{$amounts {$id}};
    }

    # Map the date fields into a string.
    $amounts {$id} = [map {[sprintf ("%04d/%02d/%02d" => @$_ [0, 1, 2]) =>
                            $_ -> [3]]} @tmp];
            
    # Sum to get totals.
    foreach my $j (1 .. @{$amounts {$id}} - 1) {
        $amounts {$id} -> [$j] -> [1] += $amounts {$id} -> [$j - 1] -> [1]
    }

    # Now, if $total is less than the total, we need to subtract.
    # This happens if a participant has submitted blocks before joining
    # the team, and never retired the blocks to the team.
    my $diff = $amounts {$id} -> [-1] -> [1] - $total;

    # $diff shouldn't be < 0....
    if ($diff > 0) {
        foreach my $entry (@{$amounts {$id}}) {
            $entry -> [1] -= $diff;
        }

        # If the amount is zero or less, no submitted blocks belong to
        # the team.
        $amounts {$id} = [grep {$_ -> [1] > 0} @{$amounts {$id}}];
    }

    # Keep track of the extremes.
    $max   = $amounts {$id} -> [-1] -> [1] if
             $max   <  $amounts {$id} -> [-1] -> [1];
    $fdate = $amounts {$id} -> [ 0] -> [0] if
             $fdate gt $amounts {$id} -> [ 0] -> [0];
    $ldate = $amounts {$id} -> [-1] -> [0] if
             $ldate lt $amounts {$id} -> [-1] -> [0];
}

# Remove participants with no info.
@participants = grep {$amounts {$_ -> [0]}} @participants;

# Done if there are no participants.
exit unless @participants;

# Reverse sort the participants on total number of blocks submitted.
# This makes the legenda order match the left endpoints of the graph
# when viewed vertically; which is important since the PNG driver only
# uses a limited number of colours.
@participants = sort {$amounts {$b -> [0]} -> [-1] -> [1] <=>
                      $amounts {$a -> [0]} -> [-1] -> [1]} @participants;

# Round the max to a "round" number.
$max = 100_000 * (1 + int ($max / 100_000));

local *PLOT;
open   PLOT, "| $plot" or die;
# open   PLOT, "> /tmp/plot" or die;

$" = ",\\\n";
print PLOT <<EOT;

# Driver.
set terminal  png small color

# Output file.
set output   "$dir/$file"

# Title for the graph.
set title    "People submitting blocks for $name on $ldate"

# We have dates on the x-axis.
set xdata     time

# Formats for the tics.
set format x "%d/%m/%y"
set format y "%.0f"

# Labels.
set ylabel   "Blocks"

# How the grid looks like.
set grid      xtics ytics

# Format of dates in the data.
set timefmt  "%Y/%m/%d"

# First the ranges, than the plots.
plot ["$fdate":"$ldate"] [0:$max] \\
@{[map {"'-' using 1:2 smooth unique title '$_->[1]'"} @participants]}
EOT

# Data for each participant.
$" = " ";
foreach my $participant (@participants) {
    print PLOT map {"@$_\n"} @{$amounts {$participant -> [0]}};
    print PLOT "e\n";
}

close PLOT;

__END__

=pod

This program is copyright 1999 by Abigail.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

=cut

--
To unsubscribe, send 'unsubscribe rc5' to majordomo at lists.distributed.net
rc5-digest subscribers replace rc5 with rc5-digest



More information about the rc5 mailing list