#! /usr/bin/perl use strict; use warnings; use LWP::Simple qw(); use HTML::Parser; use Date::Parse; use Data::Dumper; use Term::ANSIColor qw(:constants); use DBI; use FindBin; my $user = shift or die "Need to pass in user name!"; my @convert = ( [ 1, '%02d', 'sec' ], [ 60, '%02d:', 'min' ], [ 60, '%2d:', 'hr' ], [ 24, '%d ', 'd' ], ); sub convert_seconds { my $sec = shift; my @c = @convert; my @vals = ($sec); my $output = ' %s'; while (my $c = shift @c) { if ($vals[0] > $c->[0]) { $output = $c->[1] . $output; unshift @vals, int($vals[0] / $c->[0]); $vals[1] %= $c->[0]; $vals[-1] = $c->[2]; } else { last; } } sprintf $output, @vals; } my $html = LWP::Simple::get('http://www.perlmonks.org/index.pl?node=' . $user); if ($html) { my %data = (done => 1); my $p = HTML::Parser->new(api_version => 3); $p->report_tags(qw(tr td)); $p->handler(start => sub { return if $data{done}; my ($tagname, $attr) = @_; if ($tagname eq 'tr') { delete $data{key}; delete $data{td}; } return unless $tagname eq 'td'; #return if keys %$attr; $data{start} = $tagname; }, 'tagname, attr'); $p->handler(end => sub { return if $data{done}; my ($tagname) = @_; return unless $tagname eq 'td'; if (exists $data{key}) { $data{$data{key}} = $data{td}; delete $data{key}; delete $data{td}; } elsif (exists $data{td}) { $data{key} = $data{td}; delete $data{td}; } delete $data{start}; if ($tagname =~ /Scratchpad:/) { $data{done}++; } }, 'tagname'); $p->handler(text => sub { return if $data{done}; my $text = shift; $text =~ s/^\s+//; $text =~ s/[:\s]+$//; $data{td} .= $text; #delete $data{start}; }, 'text'); $p->handler(comment => sub { my ($tagname) = @_; if ($tagname =~ /contained/) { $data{done} = $tagname =~ m./contained.; } }, 'tagname' ); $p->parse($html); $p->eof(); my $table_file = File::Spec->catfile($FindBin::Bin,'stats.' . $user); my $eol = -e $table_file ? "\n" : ', '; my $since = $data{'User since'}; $since =~ s/\s\S*$//; $since =~ s/at //; my $tbegin = str2time($since, 'GMT'); my $tdiff = time - $tbegin; my $duration = $tdiff / (60 * 60 * 24); if ($eol =~ /^\s*$/) { print $user,$/; print '-' x (length $user), $/; } else { print "[$user] stats: "; } printf "Member for: %.3f days%s", $duration, $eol; println('Experience: ', $data{Experience}, $duration); print $eol; println('Writeups: ', $data{Writeups}, $duration); print $eol; printf("Which makes it %s%.3f%s XP per writeup!\n", BOLD.RED, ($data{Experience} / $data{Writeups}, RESET)); #/)); if (-e $table_file) { my $dbh = DBI->connect('dbi:CSV:f_dir=' . $FindBin::Bin . ";csv_eol=\n") or warn "Can't connect to DBI"; $dbh->{csv_tables}->{stats} = { file => $table_file, }; my $total = $dbh->selectall_arrayref('select sum(xp) from stats')->[0][0]; if ($total != $data{Experience}) { require Time::localtime; my $lt = Time::localtime::localtime(time() + (2 * 60 * 60)); my $date = sprintf("%04d-%02d-%02d", $lt->year + 1900, $lt->mon + 1, $lt->mday); #print "Today is $date\n"; my $count = $dbh->selectall_arrayref('select count(*) from stats where date = ?', {}, $date)->[0][0]; my $cur = $dbh->selectall_arrayref('select xp from stats where date = ?', {}, $date); $cur = $cur->[0] while $cur and ref $cur; my $gained = $data{Experience} - $total; printf "%s %s%d%s XP!\n", $gained > 0 ? "Gained" : "Lost", RED.BOLD, abs($gained), RESET; if ($count) { $cur += $gained; print "Updating today ($date) to be XP = $cur\n"; $dbh->do('update stats set xp = ? where date = ?', undef, $cur, $date); } else { $cur = $gained; print "Inserting into today to be XP = $cur\n"; $dbh->do('insert into stats (date,xp) values(?,?)', {}, $date,$cur); } } } } else { print "Can't get node for $user\n"; } sub println { my ($type, $data, $duration) = @_; my $rate = $data / $duration; my $per = 24 * 60 * 60 / $rate; printf("%s %d (%s%.3f%s per day, or 1 per %s%s%s)", $type, $data, BLUE.BOLD, $rate, RESET, BOLD.BLUE, convert_seconds($per), RESET); }