http://www.perlmonks.org?node_id=25218
Category: PerlMonks Related Scripts
Author/Contact Info ase
Description: Here's my contribution to statistics nuts like myself.
This utility Logs in to Perlmonks (using ZZamboni's PerlmonksChat module), gets your writeup page and creates 3 plots from the data, which are ftp'd to a server of your choice.
I run it every few days to update the graphs. All modules besides PerlMonksChat.pm are available at CPAN. See my home node for an example of the results.

Update: I no longer post the graphs on my home node. The updated code given in the replies to this node is more modern. Thanks to everyone for the kind comments I received when I first wrote this.

#!/usr/bin/perl -w
#mstats Version 1.0
#7/29/2000 by ase  (alevenson@uswest.net)
#
#Freely redistributable under the same terms as perl itself.
#
use strict;              #Of course.
use PerlMonksChat;       #access perlmonks and get the page
use HTML::TableExtract;  #extract the HTML table 
use Date::Manip;         #manipulate the Create times
use GD::Graph::lines3d;  #create the plots
use Net::FTP;            #Send them to a webserver

#user configuration
$Date::Manip::TZ='PST5PDT';  #timezone:see Date::Manip docs 
my $user = 'ase';            #monk Username (change)

#ftp info
my $ftpurl  = 'CHANGETHIS.ftp';  #ftp url
my $ftpuser = 'CHANGETHIS';   #user name
my $ftppass = '*******';    #passwd (protect this script)
#end of user configuration

#users home node (shouldn't have to change)
my $url = "http://www.perlmonks.org/index.pl?" .
          "node=Perl+Monks+User+Search&" .
          "usersearch=$user&orderby=createtime%20DESC";

my $p = new PerlMonksChat;   #Create Chat Object
$p->add_cookies();           #Get the login cookie

my $c = $p->getpage($url);       #get the users home node
$c=~s/bgcolor=>/bgcolor="">/mg;  #makes HTML::TableExtract happy

my $te = new HTML::TableExtract(headers=>['Rep','Create Time'] )
         ->parse($c); #Create the parser and get the table

#process the table data into a hash
my %data;                 
for my $ts ($te->table_states) {  #only one table should be returned
   for my $row ($ts->rows) {      #the row contains [rep, create time]
      if ($row->[1]) {            #if there's a create time
          my $key = UnixDate($row->[1],"%Q");  #makes key 'yyyymmdd'
             if(defined($row->[0])) {             #if there's a Rep
              $data{$key}{REP}+=$row->[0];  #add it to the rep total f
+or that day
            $data{$key}{NUM}++;   #increment number of posts for that 
+day
        }
      }
   }
 }

#create a list of lists for the plot objects
my ($count,$rep);  #running total number of writeups and reps
my @data;          #the data: munged for GD::graph::lines3d objects
my $flag;          #has $birth been set?
my $birth;         #earliest create time in the data

for my $key (sort keys %data) {   #loop through dates from earliest to
+ latest;
    if(!$flag) {                  #first item? (earliest)
       $birth=$key;               #Set it
       $flag=1;                   #don't set it again.
    }
    $count+=$data{$key}{NUM};     #Add days writeups to total
    $rep+=$data{$key}{REP};       #add days rep to total
    push @{$data[0]},             #days since $birth
                     sprintf("%d",Delta_Format(DateCalc($birth,$key),0
+,"%dt"));
    push @{$data[1]},$count/($data[0][-1]+1); #avg post/day to date
    push @{$data[2]},$rep/($data[0][-1]+1);   #avg rep/day to date
    push @{$data[3]},sprintf("%.3f",$rep/$count); #avg rep/post to dat
+e
}

#create some plot settings
my %plot= (avg=>{
             ylabel => 'Reputation/Writeup',
             title  => 'Rep/Writeup',
             column => 3,
        },
          post=>{
                ylabel => 'Writeups/Day',
                title  => 'Writeups/Day',
                column => 1,
        },
           rep=>{
                ylabel => 'Reputation/Day',
        title  => 'Rep/Day',
                column => 2,
                },
         );

#this does most of the work in actually making the plots
for my $key (keys %plot) {
    $plot{$key}{GD} = new GD::Graph::lines3d(350,200);
    $plot{$key}{GD}
           ->set(x_label => 'Days since ' . UnixDate($birth,"%b %d, %Y
+"),
                 y_label => 'Avg ' . $plot{$key}{ylabel},
                 title   => sprintf("%s %.3f)","Recent Avg. " . 
                                    $plot{$key}{title} .
                                    "(currently",$data[$plot{$key}{col
+umn}][-1])
                );
    $plot{$key}{PLOT} = $plot{$key}{GD}
                         ->plot([$data[0],$data[$plot{$key}{column}]])
+;
    open(IMG,">$key.png") or die $!;
    binmode IMG;
    print IMG $plot{$key}{PLOT}->png;
    close IMG;
}

#Now send them to our webserver
my $ftp = Net::FTP->new($ftpurl);
$ftp->login($ftpuser,$ftppass);

for my $file (keys %plot) {
    $ftp->binary;
    $ftp->put("$file.png");
}

$ftp->quit;

__END__
Replies are listed 'Best First'.
Re: Personal Monk Stats plot creator (xrepwalker patch)
by OeufMayo (Curate) on Jun 13, 2001 at 22:06 UTC

    I did this version of ase's excellent stat grapher a while ago but never posted it. Too bad ase don't come around the monastery often these days, as he wrote some really good nodes, be sure to read them!

    I revamped a bit the whole thing, the main difference in this version is that it uses jcwren's xluke_repwalker.pl stats file to get the infos, instead of grabbing the page from perlmonks.

    Enjoy!

    #!/usr/bin/perl -w # mstats Version 1.2 # 7/29/2000 by ase (alevenson@uswest.net) # 6/13/2001 by OeufMayo (briac@pilpre.com) # # To do: give the choice between repwalker format and # epoptai's reputer format # #Freely redistributable under the same terms as perl itself. # use strict; # Of course. use Date::Manip; # Manipulate the Create times use GD::Graph::lines3d; # Create the plots use Net::FTP; use vars qw($VERSION); $VERSION = 1.2; # User configuration $Date::Manip::TZ = 'PST5PDT'; # Timezone:see Date::Manip docs # ftp info my $ftpurl = 'ftp.foo.bar'; # ftp url my $ftpuser = 'login'; # user name my $ftppass = 'password'; # passwd (protect this script) my $ftpdir = '/home'; # directory # Monk Username my $user = 'Anonymous Monk'; # Path for the repwalker rep file my $stats = "/usr/monkstats/rep.$user"; #end of user configuration my %data; open (REP, $stats) or die "Can't open stats file $stats: $!\n"; # Process the table data into a hash while (<REP>){ # id title rep date $_ =~ m/^"(\d+)","([^"]+)","(-?\d+)","([^ ]+)\s[0-9:]+"$/; my $key = UnixDate($4,"%Q"); $data{$key}{REP} += $3; # Add it to the rep total for that day $data{$key}{NUM}++; # Increment number of posts for that d +ay } # Create a list of lists for the plot objects my ($count,$rep); # Running total number of writeups and reps my @g_data; # The data: munged for GD::graph::lines3d objects my $birth; # Earliest create time in the data for my $key (sort keys %data) { # Loop through chronologically $birth ||= $key; $count += $data{$key}{NUM}; # Add days writeups to total $rep += $data{$key}{REP}; # Add days rep to total push @{$g_data[0]}, # Days since $b +irth sprintf("%d",Delta_Format(DateCalc($birth,$key),0 +,"%dt")); push @{$g_data[1]},$count/($g_data[0][-1]+1); # Avg post/da +y to date push @{$g_data[2]},$rep/($g_data[0][-1]+1); # Avg rep/d +ay to date push @{$g_data[3]},sprintf("%.3f",$rep/$count); # Avg rep/post +to date } # Create some plot settings my %plot= (avg=>{ ylabel => 'Reputation/Writeup', title => 'Rep/Writeup', column => 3, }, post=>{ ylabel => 'Writeups/Day', title => 'Writeups/Day', column => 1, }, rep=>{ ylabel => 'Reputation/Day', title => 'Rep/Day', column => 2, }, ); # This does most of the work in actually making the plots for my $key (keys %plot) { $plot{$key}{GD} = new GD::Graph::lines3d(350,200); $plot{$key}{GD}->set( x_label => 'Days since ' . UnixDate($birth,"%b %d, %Y"), y_label => 'Avg ' . $plot{$key}{ylabel}, x_label_skip => 7, # You may want to change this to get a +nice x-scaled graphed title => sprintf("%s %.2f)","Recent Avg. " . $plot{$key}{title} . "(currently",$g_data[$plot{$key}{column}][-1]) ); $plot{$key}{PLOT} = $plot{$key}{GD}->plot( [$g_data[0],$g_data[$plot{$key}{column}]] ); open(IMG,">$key.png") or die $!; binmode IMG; print IMG $plot{$key}{PLOT}->png; close IMG; } # Now send them to our webserver my $ftp = Net::FTP->new($ftpurl); $ftp->login($ftpuser,$ftppass); $ftp->cwd($ftpdir) if $ftpdir; for my $file (keys %plot) { $ftp->binary; $ftp->put("$file.png"); } $ftp->quit;
    <kbd>--
    my $OeufMayo = new PerlMonger::Paris({http => 'paris.mongueurs.net'});</kbd>
RE: (Guildenstern) Personal Monk Stats plot creator
by Guildenstern (Deacon) on Aug 29, 2000 at 23:12 UTC
    Nice work! I like being able to see this info in a graphical context. I only have a couple of suggestions for improving it. These came up through personal trial and error trying to get this to run for myself.
    1. Suggest using something else than PerlMonksChat for getting a user's page. This one gave me a lot of problems, and I ended up throwing it away in favor of a request that looks like this :
      my $url ="$pmsite?user=$username&" ."passwd=$password&op=login&node=perl+monks+user+search&" ."usersearch=$username&orderby=createtime%20desc&count=$i";

      (Code shamelessly stolen from jcwren's luke_repwalker.pl). This brings up point 2,
    2. Some people (not myself..yet) have more than 50 writeups, so the url query will have to be repeated multiple times in order to get all writeups. Granted, your code will give a good synopsis, but I imagine there are people out there who want all the info at once. For an example of how to do multiple fetches to get all of the writeups, look again to luke_repwalker
    Other than those suggestions, I would say your code works great for me.
    Update (2): Fixed the code wrap. Thnx nuance.
    Guildenstern
    Negaterd character class uber alles!