#!/usr/bin/perl -w
# reviews.pl -- Count reviews per user
# Usage: reviews.pl
# 18 Oct 18 Created
use English;
use strict;
use utf8;
use warnings;
use DateTime;
use Data::Dumper;
use Cred;
use MilHist::Bot;
use MilHist::Parser;
use MilHist::Table;
use MilHist::Template;
binmode (STDERR, ':utf8');
binmode (STDOUT, ':utf8');
my $cred = new Cred ();
my $editor = MilHist::Bot->new ($cred) or
die "new MediaWiki::Bot failed";
sub bold ($) {
return "'''" . $ARG[0] . "'''";
}
sub dates () {
my $today = DateTime->now ();
my $last_of_month = $today->clone ();
$last_of_month->subtract ('months' => 1) until $last_of_month->month % 3 == 1;
$last_of_month = $last_of_month->subtract (days => $today->day ());
my $first_of_month = DateTime->new ('day' => 1, 'month' => $last_of_month->month (), 'year' => $last_of_month->year ())->subtract ('months' => 2);
my $start = $first_of_month->strftime ("%Y-%m-%d");
my $end = $last_of_month->strftime ("%Y-%m-%d");
my $display_start = $first_of_month->strftime ("%B");
my $display_end = $last_of_month->strftime ("%B %Y");
return ($start, $end, $display_start, $display_end);
}
sub list_to_hash ($) {
my ($list) = @ARG;
my %a = $list =~ /\[\[(.+?)\|(.+?)\]\]/g;
return %a;
}
sub aclass_nominators ($) {
my ($assessment) = @ARG;
my $nom_text = $editor->fetch ($assessment);
my @nm = ($nom_text =~ /<small>''Nominator\(s\):(.+)/ig);
my @nominators = ($nm[0] =~ /\[\[User:(.+?)\|/gi);
@nominators or
@nominators = $nm[0] =~ /{{u\|(.+?)}}/gi;
@nominators or
@nominators = $nm[0] =~ /{{user0\|(.+?)}}/gi;
@nominators or
$editor->error ("Unable to find nominator of '$assessment'");
return @nominators;
}
sub nominators ($$) {
my ($type, $review) = @ARG;
my @nominators;
if ($type eq 'ACR' || $type eq 'FAC') {
@nominators = aclass_nominators ($review);
} elsif ($type eq 'PR') {
my $nominator = creator ($review);
@nominators = $nominator ? ($nominator) : ();
# print "\tnominator='$nominator'\n";
} else {
$cred->warning ("unknown review type ($type) -- skipping\n");
}
# foreach my $nominator (@nominators) {
# print "\tnominator='$nominator'\n";
# }
my %nominators = map { $ARG => 1 } @nominators;
return %nominators;
}
sub creator ($) {
my ($review) = @ARG;
my @history = $editor->get_history ($review);
# Review may not be started yet
return undef unless @history;
my $history = pop @history;
my $creator = $history->{'user'};
return $creator;
}
sub ga_reviews ($$$$) {
my ($type, $start, $end, $hash) = @ARG;
my $reviews = {};
foreach my $review (keys %$hash) {
my $reviewer = creator ($review);
next unless $reviewer;
++$reviews->{$reviewer};
$cred->showtime ("$type: '$review' reviewed by '$reviewer' ($reviews->{$reviewer})\n");
}
return $reviews;
}
sub reviews ($$$$) {
my ($type, $start, $end, $hash) = @ARG;
my $reviews = {};
foreach my $review (keys %$hash) {
my %nominators = nominators ($type, $review);
my %reviewers;
# Zero is possible, as the review page may not have been created yet
my @history = $editor->get_history ($review);
# print "revisions = ", scalar @history, "\n";
foreach my $history (@history) {
my $date = $history->{timestamp_date};
next if $date gt $end;
last if $date lt $start;
my $reviewer = $history->{'user'};
next if $nominators{$reviewer};
$reviewers{$reviewer} = 1;
}
foreach my $reviewer (keys %reviewers) {
++$reviews->{$reviewer};
$cred->showtime ("$type: '$review' reviewed by '$reviewer' ($reviews->{$reviewer})\n");
}
}
return $reviews;
}
sub tally ($$$) {
my ($reviews, $type, $tally) = @ARG;
foreach my $reviewer (keys %$tally) {
$reviews->{$reviewer} //= {};
$reviews->{$reviewer}->{$type} = $tally->{$reviewer};
}
}
sub award ($) {
my ($tally) = @ARG;
my $award = ($tally >= 15) ? 'WikiChevrons' :
($tally >= 8) ? 'The Content Review Medal of Merit (Military history)' :
($tally >= 4) ? 'The Milhist reviewing award (2 stripes)' :
'The Milhist reviewing award (1 stripe)';
return $award;
}
sub nomination ($$$$) {
my ($reviewer, $tally, $display_start, $display_end) = @ARG;
my $nomination = new MilHist::Template ('name' => "WPMILHIST Award nomination");
$nomination->add ('nominee' => $reviewer);
$nomination->add ('citation' => "for $display_start to $display_end reviews");
$nomination->add ('award' => award ($tally));
$nomination->add ('status' => 'nominated');
my $text = $nomination->text ();
return $text;
}
sub report ($$$) {
my ($reviews, $display_start, $display_end) = @ARG;
my $heading = ['User', 'FA', 'A-Class', 'GA', 'PR', 'Total', 'Nomination'];
my @rows;
my ($fac_total, $acr_total, $ga_total, $pr_total);
foreach my $reviewer (keys %$reviews) {
my $fac = $reviews->{$reviewer}->{'FAC'};
my $acr = $reviews->{$reviewer}->{'ACR'};
my $ga = $reviews->{$reviewer}->{'GA'};
my $pr = $reviews->{$reviewer}->{'PR'};
my $total = ($fac // 0) + ($acr // 0) + ($ga // 0) + ($pr // 0);
$fac_total += $fac // 0;
$acr_total += $acr // 0;
$ga_total += $ga // 0;
$pr_total += $pr // 0;
my $nomination = nomination ($reviewer, $total, $display_start, $display_end);
my $row = [bold ($reviewer) => $fac // '', $acr // '', $ga // '', $pr // '', $total, $nomination];
push @rows, $row;
}
my $grand_total = $fac_total + $acr_total + $ga_total + $pr_total;
my $row = [bold ('total') => bold ($fac_total), bold ($acr_total), bold ($ga_total), bold ($pr_total), bold ($grand_total), ''];
push @rows, $row;
my $table = new MilHist::Table ();
$table->options ({'align' => ['left', 'right', 'right', 'right', 'right', 'right', ]});
my $text = $table->tabulate ($heading, \@rows);
# print $text;
return $text;
}
sub post_report ($$$) {
my ($report, $display_start, $display_end) = @ARG;
my $coordination_page = 'Wikipedia Talk:WikiProject_Military_history/Coordinators';
my $text = $editor->fetch ($coordination_page);
$text .= join '', "\n",
"==$display_start to $display_end reviewing tallies==\n",
$report,
"\n",
"~~~~\n";
$cred->showtime ("Adding report to $coordination_page\n");
$editor->edit ({
page => $coordination_page,
text => $text,
summary => "$display_start to $display_end reviewing tallies",
minor => 0,
}) or
$editor->error ("unable to edit '$coordination_page'");
}
$cred->showtime ("started\n");
eval {
my ($start, $end, $display_start, $display_end) = dates ();
my $announcements = 'Template:WPMILHIST Announcements';
my @history = $editor->get_history ($announcements) or
$editor->error ("Unable get history of '$announcements'");
my (%acr, %fac, %pr, %ga);
$cred->showtime ("Tallying reviews\n");
foreach my $history (@history) {
my $date = $history->{timestamp_date};
next if $date gt $end;
last if $date lt $start;
my $text = $editor->get_text ($announcements, $history->{revid}) or
$editor->error ("Unable to find '$announcements:$history->{revid}')");
my $parser = new MilHist::Parser ('text' => $text);
my $template = $parser->find ('WPMILHIST Announcements/Shell') or
die "unable to find WPMILHIST Announcements/Shell in $announcements:$history->{revid}";
my @ga_nominees = $parser->find ('WPMHA/GAN') or
die "unable to find good_article_nominees parameter";
foreach my $nominee (@ga_nominees) {
my $candidate = join '', 'Talk:', $nominee->get (1), '/GA', ($nominee->get (2) // 1);
$ga{$candidate} = 1;
}
my $fac_list = $template->get ('featured_article_candidates') or
die "unable to find featured_article_candidates parameter";
%fac = (%fac, list_to_hash ($fac_list));
my $acr_list = $template->get ('A-Class_reviews') or
die "unable to find A-Class_reviews parameter";
%acr = (%acr, list_to_hash ($acr_list));
my $pr_list = $template->get ('peer_reviews') or
die "unable to find peer_reviews parameter";
%pr = (%pr, list_to_hash ($pr_list));
}
$cred->showtime ("Tallying reviewers\n");
my $fac_reviews = reviews ('FAC', $start, $end, \%fac);
my $acr_reviews = reviews ('ACR', $start, $end, \%acr);
my $ga_reviews = ga_reviews ('GA', $start, $end, \%ga);
my $pr_reviews = reviews ('PR', $start, $end, \%pr);
my $reviews = {};
tally ($reviews, 'FAC' => $fac_reviews);
tally ($reviews, 'ACR' => $acr_reviews);
tally ($reviews, 'GA' => $ga_reviews);
tally ($reviews, 'PR' => $pr_reviews);
my $report = report ($reviews, $display_start, $display_end);
post_report ($report, $display_start, $display_end);
};
if ($EVAL_ERROR) {
$cred->error ($EVAL_ERROR);
}
$cred->showtime ("finished\n");
exit 0;