$ perl XP_efficiency.pl
Usage: perl XP_efficiency.pl --sort_by (age|posts)
Options:
--sort_by, -s: Rank monks by XP/posts or by XP/age
--limit, -l: Limit results to this many
--order, -o: Sort order (asc|desc)
--recent, -r: Limit to monks seen in the last n weeks
--only_include, -i: Skip all monks except this/these one(s)
--force_include, -f: Include this/these monk(s) even if -l is set
--min_posts, -m: Exclude monks with fewer than this many posts
--no_adjust, -n: Don't deduct 0.5 XP for each day of monkhood
--help, -h: Print this help
####
$ perl XP_efficiency.pl --sort_by age --min_posts 200 --recent 3 --limit 10 --order desc
+--------------------------------------------------------------------------------------+
| Pos | St. | Monk name | XP | Level | Age | XP/Age | Posts | XP/Posts |
+--------------------------------------------------------------------------------------+
| 1 2 BrowserUk 159,881 Pope (28) 4,971 36.0644 22,404 6.9144 |
| 2 3 ikegami 127,624 Pope (28) 4,173 34.2347 19,716 6.2615 |
| 3 4 Corion 122,789 Pope (28) 5,783 23.4148 9,796 11.9443 |
| 4 5 GrandFather 72,446 Sage (25) 3,880 20.4526 6,562 10.4489 |
| 5 55 Athanasius 24,854 Canon (20) 1,352 20.1077 1,458 16.1193 |
| 6 664 1nickt 3,115 Curate (13) 214 15.6199 468 6.1987 |
| 7 42 choroba 29,288 Canon (20) 2,112 14.8885 3,739 7.2683 |
| 8 20 toolic 43,023 Bishop (22) 3,130 14.7489 3,597 11.0906 |
| 9 7 tye 70,849 Sage (25) 5,661 13.3272 7,887 8.2652 |
| 10 60 kcott 24,138 Canon (20) 1,939 13.2484 2,407 9.2227 |
+--------------------------------------------------------------------------------------+
####
$ perl XP_efficiency.pl -s posts -m 200 -l 5 -o desc -n
+--------------------------------------------------------------------------------------+
| Pos | St. | Monk name | XP | Level | Age | XP/Age | Posts | XP/Posts |
+--------------------------------------------------------------------------------------+
| 1 82 gryphon 18,249 Abbot (19) 5,643 3.7429 218 83.7110 |
| 2 17 Old_Gray_Bear 46,694 Bishop (22) 4,507 11.9908 598 78.0836 |
| 3 72 gmax 20,739 Abbot (19) 5,164 4.6478 292 71.0240 |
| 4 54 Perlbotics 24,920 Canon (20) 3,056 9.4366 384 64.8958 |
| 5 34 Gavin 32,148 Chancellor (21) 3,599 10.3364 600 53.5800 |
+--------------------------------------------------------------------------------------+
####
$ perl XP_efficiency.pl -s posts -m 200 -l 5 -o desc
+--------------------------------------------------------------------------------------+
| Pos | St. | Monk name | XP | Level | Age | XP/Age | Posts | XP/Posts |
+--------------------------------------------------------------------------------------+
| 1 17 Old_Gray_Bear 46,694 Bishop (22) 4,507 10.8334 598 70.5468 |
| 2 82 gryphon 18,249 Abbot (19) 5,643 2.5855 218 57.8257 |
| 3 54 Perlbotics 24,920 Canon (20) 3,056 8.2794 384 56.9375 |
| 4 72 gmax 20,739 Abbot (19) 5,164 3.4905 292 53.3390 |
| 5 34 Gavin 32,148 Chancellor (21) 3,599 9.1792 600 47.5817 |
+--------------------------------------------------------------------------------------+
####
$ perl XP_efficiency.pl -s posts -m 200 -l 10 -o desc -f 1nickt -f merlyn
+--------------------------------------------------------------------------------------+
| Pos | St. | Monk name | XP | Level | Age | XP/Age | Posts | XP/Posts |
+--------------------------------------------------------------------------------------+
| 1 17 Old_Gray_Bear 46,694 Bishop (22) 4,507 10.8334 598 70.5468 |
| 2 82 gryphon 18,249 Abbot (19) 5,643 2.5855 218 57.8257 |
| 3 54 Perlbotics 24,920 Canon (20) 3,056 8.2794 384 56.9375 |
| 4 72 gmax 20,739 Abbot (19) 5,164 3.4905 292 53.3390 |
| 5 34 Gavin 32,148 Chancellor (21) 3,599 9.1792 600 47.5817 |
| 6 100 scorpio17 16,117 Abbot (19) 3,253 4.5761 323 39.8266 |
| 7 61 hsmyers 23,992 Canon (20) 5,452 3.9352 520 35.6538 |
| 8 44 borisz 27,915 Canon (20) 4,756 5.6351 913 25.3658 |
| 9 129 dvergin 12,732 Monsignor (18) 5,825 1.3724 311 22.2090 |
| 10 58 FunkyMonk 24,201 Canon (20) 3,187 7.6302 1,024 20.5215 |
| 41 6 merlyn 71,157 Sage (25) 5,741 13.1874 6,322 10.3474 |
| 82 664 1nickt 3,115 Curate (13) 214 15.6198 468 6.1987 |
+--------------------------------------------------------------------------------------+
####
#!/usr/bin/perl
use strict;
use warnings;
use feature qw/ say /;
use Data::Dumper; $Data::Dumper::Sortkeys = 1;
use Getopt::Long;
use HTTP::Tiny;
use HTML::TableExtract;
use Time::Piece;
use Number::Format qw/ format_number /;
use List::Util qw/ any /;
my ( $sort_by, $order, $limit, $recent, $only_include, $force_include, $min_posts, $no_adjust );
GetOptions(
'sort_by|s=s' => \$sort_by,
'order|o=s' => \$order,
'limit|l=i' => \$limit,
'recent|r=i' => \$recent,
'only_include|i=s@' => \$only_include,
'force_include|f=s@' => \$force_include,
'min_posts|m=i', => \$min_posts,
'no_adjust|n' => \$no_adjust,
'help|?' => \&print_usage,
);
minor_validation();
my $url = 'http://perlmonks.com/?node_id=3559';
my $headers = ['#', 'User', 'Experience', 'Level', 'Writeups', 'User Since', 'Last Here'];
my $data;
fetch_data( $url );
print_data( $data );
exit;
######################################
sub fetch_data {
my $url = shift;
my $page = HTTP::Tiny->new->get( $url );
die 'Died. No response' if not $page;
die "Died. $page->{'status'} $page->{'reason'}" if not $page->{'success'};
my $table = HTML::TableExtract->new( 'headers' => $headers );
$table->parse( $page->{'content'} );
my $now = time();
foreach my $row ( $table->rows ) {
process_row( $now, $row );
}
}
sub process_row {
my $now = shift;
my $row = shift;
my ( $rank, $monk, $xp, $level, $posts, $created, $last_here ) = @{ $row };
return if $last_here =~ /year/ or ( $last_here =~ /(\d+) week/ and $1 > ( $recent || 4 ) );
return if $sort_by eq 'posts' and $posts eq 'None';
return if $min_posts and ( $posts eq 'None' or $posts < $min_posts );
return if $only_include and not any { $_ eq $monk } @{ $only_include };
$created = Time::Piece->strptime($created, '%Y-%m-%d %R')->epoch;
my $age = $now - $created;
my $age_days = int( $age / 86400 );
my $xp_adjusted = $no_adjust ? $xp : $xp - ( $age_days / 2 );
my %record = (
rank => $rank,
xp => $xp,
level => $level,
posts => $posts,
by_posts => $posts eq 'None' ? 0 : ( $xp_adjusted / $posts ),
age => $age_days,
by_age => ( $xp_adjusted / $age ) * 100000, # for scale
);
$data->{ $monk } = \%record;
}
sub print_data {
my $data = shift;
$sort_by = 'by_' . $sort_by;
my %sortable = ( map { $_ => $data->{ $_ }->{ $sort_by } } keys %{ $data } );
my @results;
foreach my $monk ( sort { $sortable{$a} <=> $sortable{$b} } keys %sortable ) {
$data->{ $monk }->{'posts'} =~ s/None/0/;
push @results, {
rank => $data->{ $monk }->{'rank'},
monk => $monk,
xp => format_number( $data->{ $monk }->{'xp'} ),
level => $data->{ $monk }->{'level'},
age => format_number( $data->{ $monk }->{'age'} ),
by_age => format_number( $data->{ $monk }->{'by_age'}, 4, 1 ),
posts => format_number( $data->{ $monk }->{'posts'} ),
by_posts => format_number( $data->{ $monk }->{'by_posts'}, 4, 1 )
};
}
@results = reverse @results if $order and $order eq 'desc';
print < $limit ) {
last if not defined $force_include;
next unless any { $_ eq $result->{'monk'} } @{ $force_include };
}
format =
@ @### @#### @<<<<<<<<<<<< @>>>>>> @>>>>>>>>>>>>>>> @>>>>>> @>>>>>>> @>>>>>> @>>>>>>>> @
'|', $count, $result->{'rank'}, $result->{'monk'}, $result->{'xp'}, $result->{'level'}, $result->{'age'}, $result->{'by_age'}, $result->{'posts'}, $result->{'by_posts'}, '|'
.
write;
}
print '+--------------------------------------------------------------------------------------+';
}
sub minor_validation {
print_usage() if (
( ! $sort_by or $sort_by !~ /age|posts/ ) or
( $order and $order !~ /asc|desc/ )
);
}
sub print_usage {
say <<"EOT";
Usage: perl $0 --sort_by (age|posts)
Options:
--sort_by, -s: Rank monks by XP/posts or by XP/age
--limit, -l: Limit results to this many
--order, -o: Sort order (asc|desc)
--recent, -r: Limit to monks seen in the last n weeks
--only_include, -i: Skip all monks except this/these one(s)
--force_include, -f: Include this/these monk(s) even if -l is set
--min_posts, -m: Exclude monks with fewer than this many posts
--no_adjust, -n: Don't deduct 0.5 XP for each day of monkhood
--help, -h: Print this help
EOT
exit;
}
__END__