User:Allens/GOCE/GOCE.barnstars2.pl

From Wikipedia, the free encyclopedia
#!/usr/bin/perl

use Carp qw(cluck confess);
use Memoize;
use open ":utf8";
use warnings FATAL => qw(uninitialized);
use warnings;
use strict qw(subs refs);

# does not do "most articles first day"!

$which_drive = "May 2012"; # NEED TO SET PROPERLY (1 month ago?)

if (defined($ARGV[0]) && ($ARGV[0] ne '-')) {
  (open(INPUT,$ARGV[0]) || (die "Can't open $ARGV[0] for input: $!; stopped"));
} else {
  (open(INPUT,"-") || (die "Can't dup STDIN for input: $!; stopped"));
}

if (defined($ARGV[1]) && ($ARGV[1] ne '-')) {
  (open(OUTPUT,">>" . $ARGV[1]) || (die "Can't open $ARGV[1] for output: $!; stopped"));
} else {
  (open(OUTPUT,">-") || (die "Can't dup STDOUT for output: $!; stopped"));
}

$largest_article_size = 0;
$largest_article_user = "";
$largest_article_name = "";

%size_poss_ties = ();

%user_header = (); # without =
%user_first_letter = ();
%user_article_size = ();
%user_total_articles = ();
%user_total_words = ();
%user_total_5k = ();
%user_10k_articles = ();
%user_rollover_words = ();
%user_alias = ();
%user_0k_articles = ();
%user_reviewer_articles = ();
%user_reviewer_words = ();
%user_reviewed_articles = ();
%user_reviewed_articles_bad = ();
%user_reviewed_words = ();
%user_reviewed_words_bad = ();

$total_articles = 0;
$total_articles_reviewed = 0;
$total_articles_reviewed_bad = 0;
$total_positive_articles = 0;
$total_words = 0;
$total_words_reviewed = 0;
$total_words_reviewed_bad = 0;

#^=====???=====
#^{{GOCE Article list
# |articles =
# # {{[Cc]ompleted}} [[???]] (###)
# # {{[Ww]orking}} [[???]] (###) <- optional
# |total-articles = ##
# |total-words = ###
# |rollover-words = ###
# }}
#^----

#^====?====

$curr_first_letter = "";

$user = "";
$user_num = 0;

$expecting = "dashes";

$in_comment = 0;

LINE: while (defined($line = <INPUT>)) {
  chomp($line);
  if ($line =~ m/^====([0-9a-zA-Z])====$/) {
    $curr_first_letter = uc($1);
    if ($curr_first_letter =~ m/^\d$/) {
      $curr_first_letter = "0&ndash;9";
    }
  } elsif ($line =~ m/^====0(?:-|&ndash)9====$/) {
    $curr_first_letter = "0&ndash;9";
  } elsif (($line =~ m/^\s*$/) || ($line =~ m/^===Totals===\s*$/) ||
	   ($line =~ m/^\s*\{\{CompactTOC8\b/i) ||
	   ($line =~ m/^\s*\{\{Compact\s+ToC\b/i)) {
    next LINE;
  } elsif ($line =~ m/^\s*<!--/) {
    unless ($line =~ m/-->/) {
      $in_comment = 1;
    }
    next LINE;
  } elsif ($in_comment) {
    if ($line =~ m/-->/) {
      $in_comment = 0;
    }
    next LINE;
  } elsif ($line =~ m/^\s*\#\s*\{\{\s*[Ww]orking\b/) {
    next LINE;
  } elsif ($line =~ m/^=====((?:[^=]+|.)+?)=====\s*$/) {
    my $header = $1;
    $header =~ s/^\s+//;
    $header =~ s/\s+$//;
    unless (($expecting eq "header") || ($expecting eq "dashes")) {
      warn "Unexpected header '$header' (expected $expecting)\n";
    }
    $user = $header;
    my $alias = $user;
    my $found = 0;
    if ($header =~ m/\[\[\s*User:([^\]\|]+)/i) {
      $user = $1;
      $found = 1;
      $alias = $user;
      my $qm_user = quotemeta($user);
      if ($header =~ m/\[\[\s*User:$qm_user\|\s*([\s\w\.\&;]+)\]\]/) {
	$alias = $1;
	$alias =~ s/\s+$//;
      }
    } elsif ($header =~ m/\[\[\s*User[\s_][Tt]alk:([^\]\|]+)/) {
      $user = $1;
      $found = 1;
      $alias = $user;
    } elsif ($header =~ m/^([0-9A-Za-z])$/) {
      $curr_first_letter = uc($1);
      if ($curr_first_letter =~ m/^\d$/) {
	$curr_first_letter = "0&ndash;9";
      }
      next LINE;
    } else {
      warn "Can't distinguish user in '$header'\n";
    }
    if (exists($user_header{$user})) {
      die "Duplicate user '$user' (line '$line'); stopped";
    }
    $user_alias{$user} = $alias;
    if ($found) {
      $user_header{$user} = "[[User:$user|$alias]] ("
	. "[[User talk:$user|talk]])";
      if ($user =~ m/^[^0-9A-Za-z]*([0-9A-Za-z])/) {
	$curr_first_letter = uc($1);
	if ($curr_first_letter =~ m/^\d$/) {
	  $curr_first_letter = "0&ndash;9";
	}
      }
    } else {
      $user_header{$user} = $header;
    }
    $user_first_letter{$user} = $curr_first_letter;
    $user_total_articles{$user} = 0;
    $user_total_words{$user} = 0;
    $user_total_5k{$user} = 0;
    $user_0k_articles{$user} = 0;
    $user_rollover_words{$user} = 0;
    $expecting = "GOCE";
    $user_num{$user} = $user_num;
    $user_num++;
    $user_reviewed_articles{$user} = 0;
    $user_reviewed_articles_bad{$user} = 0;
    unless (exists($user_reviewer_articles{$user})) {
      $user_reviewer_articles{$user} = 0;
    }
    $user_reviewed_words{$user} = 0;
    $user_reviewed_words_bad{$user} = 0;
    unless (exists($user_reviewer_words{$user})) {
      $user_reviewer_words{$user} = 0;
    }
  } elsif ($line =~ m/^----\s*$/) {
    unless (($expecting eq "dashes") || ($expecting eq "rollover")) {
      warn "$user; Unexpected dashes '$line' (expecting $expecting)\n";
    }
    $expecting = "header";
  } elsif (! length($user)) {
    warn "Unexpected line '$line' - no current user\n";
  } elsif ($line =~ m/^\{\{\s*GOCE\s+Article\s+list\s*$/) {
    unless ($expecting eq "GOCE") {
      warn "$user: Unexpected GOCE: '$line' (expecting $expecting)\n";
    }
    $expecting = "articles";
  } elsif ($line =~ m/^\s*\|\s*articles\s*=\s*$/) {
    unless ($expecting eq "articles") {
      warn "$user: Unexpected articles: '$line'\n";
    }
    $expecting = "Completed";
  } elsif ($line =~
	   m/^\s*\#\s*(?:<[Ss]>\s*)?\{\{\s*[Cc]ompleted\s*\}\}\s*
	     \'*\[\[((?:[^]]+|.)*?)\]\]\'*\s*
             (?:\(section\)\s*)?(\([^)]*\)|[\d,]+)\s*(.*)$/x) {
    my $article = $1;
    my $words = $2;
    my $rest = $3;
    unless ($expecting eq "Completed") {
      warn "$user: Unexpected Completed: '$line' (expecting $expecting)\n";
    }
    unless (defined($article) && length($article) && defined($words) &&
	    length($words)) {
      $expecting = "Completed";
      next LINE;
    }
    $article =~ s/\pC+//g;
    $article =~ s/pZ+$//;
    $article =~ s/^pZ+//;
    $words =~ s/^\s*[\\(]+\s*//;
    $words =~ s/\s*[\\)]+\s*$//;
    $words =~ s/,+//g;
    if ($words =~ m/^\s*(?:app\w*?x\.?\s*)?(\d+)(?:\s*words)?\s*$/i) {
      $words = ($1+0);
    } elsif (! length($words)) {
      $words = 0;
    } elsif ($words =~ m/^\s*zero\s*$/i) {
      $words = 0;
    } else {
      warn "$user: Can't interpret words '$words' from '" . $line
	. "'; treating as 0\n";
      $words = 0;
    }
    if (defined($rest) && length($rest)) {
      if ($rest =~ m/(?:\{\{rejected\}\}|decline|denied|disagree|invalid|
		       \bnot\b|redflag|\bX\b|Cross|\bN[ao&]?\b|down\b|
		       nay|negative)/xi) {
	warn "$user: Skipping '$article' and subtracting 1200 due to '"
	  . $rest . "'\n";
	$user_total_words{$user} -= 1200;
	$user_reviewed_articles{$user}++;
	$user_reviewed_words{$user} += $words;
	$user_reviewed_articles_bad{$user}++;
	$user_reviewed_words_bad{$user} += $words;
	$total_articles_reviewed++;
	$total_articles_reviewed_bad++;
	$total_words_reviewed += $words;
	$total_words_reviewed_bad += $words;
	$expecting = "Completed";
	my $user2 = "";
	if ($rest =~ m/\[\[\s*User:([^\]\|]+)/i) {
	  $user2 = $1;
	} elsif ($rest =~ m/\[\[\s*User[\s_][Tt]alk:([^\]\|]+)/) {
	  $user2 = $1;
	} else {
	  warn "$user: Can't figure out reviewer from '$rest'\n";
	}
	if (length($user2)) {
	  if (exists($user_reviewer_articles{$user2})) {
	    $user_reviewer_articles{$user2}++;
	  } else {
	    $user_reviewer_articles{$user2} = 1;
	  }
	  if (exists($user_reviewer_words{$user2})) {
	    $user_reviewer_words{$user2} += $words;
	  } else {
	    $user_reviewer_words{$user2} = $words;
	  }
	}
	next LINE;
      } elsif ($rest =~ m/\{\{(?:check[-\s\w]*|y[a&]?|Done[-\w]*|Yes[-\w]*|
			    aye|Tick|[\w\s]*\bpositive|up-arrow)\}\}/xi) {
	$user_reviewed_articles{$user}++;
	$user_reviewed_words{$user} += $words;
	$total_articles_reviewed++;
	$total_words_reviewed += $words;
	my $user2 = "";
	if ($rest =~ m/\[\[\s*User:([^\]\|]+)/i) {
	  $user2 = $1;
	} elsif ($rest =~ m/\[\[\s*User[\s_][Tt]alk:([^\]\|]+)/) {
	  $user2 = $1;
	} else {
	  warn "$user: Can't figure out reviewer for '$article' from '"
	    . $rest . "'\n";
	}
	if (length($user2)) {
	  if (exists($user_reviewer_articles{$user2})) {
	    $user_reviewer_articles{$user2}++;
	  } else {
	    $user_reviewer_articles{$user2} = 1;
	  }
	  if (exists($user_reviewer_words{$user2})) {
	    $user_reviewer_words{$user2} += $words;
	  } else {
	    $user_reviewer_words{$user2} = $words;
	  }
	}
      }
    }
    if ($words > $largest_article_size) {
      $largest_article_size = $words;
      $largest_article_user = $user;
      $largest_article_name = $article;
    } elsif ($words && ($words == $largest_article_size)) {
      $size_poss_ties{$words} = 1;
    }
    $user_article_size{$user}{$article} = $words;
    $user_total_articles{$user}++;
    $total_articles++;
    $user_total_words{$user} += $words;
    $total_words += $words;
    if ($words == 0) {
      $user_0k_articles{$user}++;
    } else {
      $total_positive_articles++;
    }
    if ($words >= 5000) {
      $user_total_5k{$user} += int($words/5000);
    }
    if ($words >= 10000) {
      $user_10k_articles{$user}{$article} = $words;
    }
    $expecting = "Completed";
  } elsif ($line =~ m/^\s*\#\s*\{\{\s*[Cc]ompleted\s*\}\}\s*<!--/) {
    unless ($expecting eq "Completed") {
      warn "$user: Unexpected Completed: '$line' (expecting $expecting)\n";
    }
    $expecting = "Completed";
  } elsif ($line =~ m/^\s*\#\s*\{\{\s*[Cc]ompleted\s*\}\}\s*\[\[\]\]/) {
    unless ($expecting eq "Completed") {
      warn "$user: Unexpected Completed: '$line' (expecting $expecting)\n";
    }
    $expecting = "Completed";
  } elsif ($line =~ m/^\s*\|\s*total-(?:articles|words)\b/i) {
    unless (($expecting eq "Completed") || ($expecting eq "rollover")) {
      warn "$user: Unexpected total-articles/words '$line' (expecting "
	. $expecting . ")\n";
    }
    $expecting = "rollover";
  } elsif ($line =~ m/^\s*\|\s*rollo?ver-words\s*=\s*(-?[\d,]+)\s*$/i) {
    my $words = $1;
    unless ($expecting eq "rollover") {
      warn "$user: Unexpected rollover-words '$line' (expecting $expecting)\n";
    }
    # note for future improvement: Read old file to figure out
    $words =~ s/,+//g;
    if ($words =~ m/^(-?\d+)$/) {
      my $rollover = ($1+0);
      if ($user_total_articles{$user} > 0) {
	$user_rollover_words{$user} = $rollover;
      } elsif ($rollover != 0) {
	warn "$user: Not doing rollover ($rollover): no articles\n";
      }
    } else {
      warn "$user: Can't interpret rollover-words '$words' (from '" . $line
	. "'); treating as 0\n";
    }
    $expecting = "close";
  } elsif ($line =~ m/^\s*\|\s*rollo?ver-words\s*=\s*(-?[\d,]+)\s*\}\}\s*$/i) {
    my $words = $1;
    unless ($expecting eq "rollover") {
      warn "$user: Unexpected rollover-words '$line' (expecting $expecting)\n";
    }
    # note for future improvement: Read old file to figure out
    $words =~ s/,+//g;
    if ($words =~ m/^(-?\d+)$/) {
      my $rollover = ($1+0);
      if ($user_total_articles{$user} > 0) {
	$user_rollover_words{$user} = $rollover;
      } elsif ($rollover != 0) {
	warn "$user: Not doing rollover ($rollover): no articles\n";
      }
    } else {
      warn "$user: Can't interpret rollover-words '$words' (from '" . $line
	. "'); treating as 0\n";
    }
    $expecting = "dashes";
  } elsif ($line =~ m/^\s*\}\}\s*$/) {
    unless ($expecting eq "close") {
      warn "$user: Unexpected close '$line' (expecting $expecting)\n";
    }
    $expecting = "dashes";
  } else {
    warn "$user: Can't interpret line '$line' (expecting $expecting)\n";
  }
}
close(INPUT);
warn "Finished reading input; have " . scalar(keys %user_header)
  . " users; largest article was '$largest_article_name' ("
  . "$largest_article_size) by $largest_article_user; "
  . scalar(keys %user_10k_articles) . " users had 10k+ articles\n";

%awards_total_count =
  (4000 => "Modest",
   8000 => "Working Wikipedian",
   12000 => "Cleanup",
   20000 => "Tireless Contributor",
   30000 => "(old school) League of Copy Editors",
   40000 => "(modern) GOCE",
   60000 => "Diligence",
   80000 => "Order of the Superior Scribe",
   100000 => "Most Excellent Order of the Caretaker");

%total_count_barnstars =
  ("Modest" => "{{subst:The Modest Barnstar|1=",
   "Working Wikipedian" =>
   "{{subst:The Working Wikipedian's Barnstar|2=wiki|1=",
   "Cleanup" => "{{subst:The Cleanup Barnstar|1=",
   "Tireless Contributor" => "{{subst:The Tireless Contributor Barnstar|1=",
   "(old school) League of Copy Editors" =>
   "{{subst:The Copyeditor's Barnstar|1=",
   "(modern) GOCE" => "{{subst:Copyedit Barnstar|2=alt|1=",
   "Diligence" => "{{subst:The Barnstar of Diligence|1=",
   "Order of the Superior Scribe" => "{{subst:Superior|1=");

#$largest_article_size = 0;
#$largest_article_user = "";
#$largest_article_name = "";

#%user_header = (); # without =
#%user_first_letter = ();
#%user_article_size = ();
#%user_total_articles = ();
#%user_total_words = ();
#%user_total_5k = ();
#%user_10k_articles = ();
#%user_rollover_words = ();
#%user_alias = ();
#%user_0k_articles = ();
#%user_reviewer_articles = ();
#%user_reviewer_words = ();
#%user_reviewed_articles = ();
#%user_reviewed_articles_bad = ();
#%user_reviewed_words = ();
#%user_reviewed_words_bad = ();

# for anyone (Kvng, for instance) who only did reviewing
foreach $user (sort(keys %user_reviewer_articles)) {
  if (exists($user_num{$user})) {
    next;
  }
  $user_num{$user} = $user_num;
  $user_num++;
  $user_header{$user} = "[[User:$user|$user]] ([[User talk:$user|talk]])";
  if ($user =~ m/^[^0-9A-Za-z]*([0-9A-Za-z])/) {
    my $first = uc($1);
    if ($first =~ m/^\d$/) {
      $first = "0&ndash;9";
    }
    $user_first_letter{$user} = $first;
  } else {
    $user_first_letter{$user} = $curr_first_letter;
  }
  $user_total_articles{$user} = 0;
  $user_total_words{$user} = 0;
  $user_total_5k{$user} = 0;
  $user_rollover_words{$user} = 0;
  $user_alias{$user} = $user;
  $user_0k_articles{$user} = 0;
  $user_reviewed_articles{$user} = 0;
  $user_reviewed_articles_bad{$user} = 0;
  $user_reviewed_words{$user} = 0;
  $user_reviewed_words_bad{$user} = 0;
}

#=====$user_header=====
#* total-articles = $user_total_articles
#* total-words = $user_total_words
#* rollover-words = $user_rollover_words
#* grand total = $user_total_words+$user_rollover_words
#* new rollover words = ###
#* barnstars =
#**[for total words]
#**[for total article rank?]
#**[for total words rank?]
#**[for total 5k+ rank?]
#**[for largest article?]
#**[for articles >= 10k?]

if (exists($size_poss_ties{$largest_article_size})) {
  warn "Have tie for largest article; check manually!\n";
}

# figure out ranks here

@users_by_num_articles =
  sort {$user_total_articles{$b} <=> $user_total_articles{$a}}
  (keys %user_total_articles);
@users_by_num_articles = grep {$user_total_articles{$_} > 0}
  (@users_by_num_articles);
%user_rank_articles = ();
%rank_articles_users = ();
$curr_num = $user_total_articles{$users_by_num_articles[0]};
$curr_rank = 1;
$user_rank_articles{$users_by_num_articles[0]} = $curr_rank;
$rank_articles_users{$curr_rank}{$users_by_num_articles[0]} = 1;
RANK_ARTICLES: for (my $i = 1; $i <= $#users_by_num_articles; $i++) {
  if ($user_total_articles{$users_by_num_articles[$i]} < $curr_num) {
    $curr_rank++;
    if ($curr_rank > 5) {
      last RANK_ARTICLES;
    }
  }
  $user_rank_articles{$users_by_num_articles[$i]} = $curr_rank;
  $curr_num = $user_total_articles{$users_by_num_articles[$i]};
  $rank_articles_users{$curr_rank}{$users_by_num_articles[$i]} = 1;
}

@users_by_num_words =
  sort {$user_total_words{$b} <=> $user_total_words{$a}}
  (keys %user_total_words);
@users_by_num_words = grep {$user_total_words{$_} > 0}
  (@users_by_num_words);
%user_rank_words = ();
%rank_words_users = ();
$curr_num = $user_total_words{$users_by_num_words[0]};
$curr_rank = 1;
$user_rank_words{$users_by_num_words[0]} = $curr_rank;
$rank_words_users{$curr_rank}{$users_by_num_words[0]} = 1;
RANK_WORDS: for (my $i = 1; $i <= $#users_by_num_words; $i++) {
  if ($user_total_words{$users_by_num_words[$i]} < $curr_num) {
    $curr_rank++;
    if ($curr_rank > 5) {
      last RANK_WORDS;
    }
  }
  $user_rank_words{$users_by_num_words[$i]} = $curr_rank;
  $curr_num = $user_total_words{$users_by_num_words[$i]};
  $rank_words_users{$curr_rank}{$users_by_num_words[$i]} = 1;
}

@users_by_num_5k =
  sort {$user_total_5k{$b} <=> $user_total_5k{$a}}
  (keys %user_total_5k);
@users_by_num_5k = grep {$user_total_5k{$_} > 0}
  (@users_by_num_5k);
%user_rank_5k = ();
%rank_5k_users = ();
$curr_num = $user_total_5k{$users_by_num_5k[0]};
$curr_rank = 1;
$user_rank_5k{$users_by_num_5k[0]} = $curr_rank;
$rank_5k_users{$curr_rank}{$users_by_num_5k[0]} = 1;
RANK_5K: for (my $i = 1; $i <= $#users_by_num_5k; $i++) {
  if ($user_total_5k{$users_by_num_5k[$i]} < $curr_num) {
    $curr_rank++;
    if ($curr_rank > 5) {
      last RANK_5K;
    }
  }
  $user_rank_5k{$users_by_num_5k[$i]} = $curr_rank;
  $curr_num = $user_total_5k{$users_by_num_5k[$i]};
  $rank_5k_users{$curr_rank}{$users_by_num_5k[$i]} = 1;
}

sub percent {
  if ($_[0] < 0.1) {
    return (int((1000*$_[0])+0.5)/10);
  } else {
    return int((100*$_[0])+0.5);
  }
}
  

sub num_format { # from Number-Format-1.73
  my $num = $_[0];
  unless (defined($num)) {
    confess "Undefined input to num_format; stopped";
  }
  unless ($num == int($num)) {
    confess "$num is not an integer; stopped";
  }
  if (abs($num) < 1000) {
    return $num; # efficiency
  }
  my $neg = 0;
  if ($num != abs($num)) {
    $neg = 1;
    $num = abs($num);
  }
  $num = '0'x(3 - (length($num) % 3)) . $num;
  $num = join(",", grep {$_ ne ''} (split(/(...)/, $num)));
  $num =~ s/^0*,?//;
  if ($num eq '') {
    $num = 0;
  }
  if ($neg) {
    $num = "-" . $num;
  }
  return $num;
}
memoize('num_format');

$drive_link =
  "[[Wikipedia:WikiProject Guild of Copy Editors/Backlog elimination drives/"
  . $which_drive . "|" . $which_drive . " Backlog Elimination Drive]]";

print OUTPUT "===$drive_link===\n";

print OUTPUT "* Total articles: " . num_format($total_articles) . "\n";
print OUTPUT "** Total articles with above 0 words: "
  . num_format($total_positive_articles) . "\n";
print OUTPUT "** Total articles reviewed: "
  . num_format($total_articles_reviewed) . " ("
  . percent($total_articles_reviewed/($total_articles+$total_articles_reviewed_bad))
  . "%)\n";
print OUTPUT "** Total articles reviewed that were bad: "
  . num_format($total_articles_reviewed_bad) . "\n";
print OUTPUT "* Total words: " . num_format($total_words) . "\n";
print OUTPUT "** Total words reviewed: "
  . num_format($total_words_reviewed) . " ("
  . percent($total_words_reviewed/($total_words+$total_words_reviewed_bad))
  . "%)\n";
print OUTPUT "** Total words reviewed that were bad: "
  . num_format($total_words_reviewed_bad) . "\n\n";

# print out overall chart here

sub sort_users {
  return(sort {($user_first_letter{$a} cmp $user_first_letter{$b}) ||
		 ($user_num{$a} <=> $user_num{$b})} (@_));
}

print OUTPUT "\{| class=\"wikitable\"\n";
print OUTPUT "|+ '''Gold Star Award Leaderboard'''\n";
print OUTPUT "!\n";
print OUTPUT "! Articles\n";
print OUTPUT "! Words\n";
print OUTPUT "! 5k+ Articles\n";
for $rank (1..5) {
  print OUTPUT "|-\n";
  print OUTPUT "| $rank.\n";
  print OUTPUT "| "
    . join(", ",map {$user_alias{$_}
		       . " (" . num_format($user_total_articles{$_}) . ")"}
	   (sort_users(keys %{ $rank_articles_users{$rank} }))) . "\n";
  print OUTPUT "| "
    . join(", ",map {$user_alias{$_}
		       . " (" . num_format($user_total_words{$_}) . ")"}
	   (sort_users(keys %{ $rank_words_users{$rank} }))) . "\n";
  print OUTPUT "| "
    . join(", ",map {$user_alias{$_} . " (" . $user_total_5k{$_} . ")"}
	   (sort_users(keys %{ $rank_5k_users{$rank} }))) . "\n";
}


#{| class="wikitable"
#|+ '''Gold Star Award Leaderboard'''
#!
#! Articles
#! Words
#! 5k+ Articles
#|-
#| 1.
#| ??? (###)
#| ??? (###)
#| ??? (###)

#|}

print OUTPUT "|}\n\n";

print OUTPUT "* Most articles, first day: '''''FILL THIS IN!!'''''\n";
print OUTPUT "* Largest single article: \[\[$largest_article_name\]\] ("
  . num_format($largest_article_size) . ") - " 
  . $user_alias{$largest_article_user} . "\n";

if (scalar(keys %user_10k_articles)) {
  $to_print_10k = "* 10K article(s):";
  
  foreach $user (sort(keys %user_10k_articles)) {
    $to_print_10k .= ", " . $user_alias{$user};
    if (scalar(keys %{ $user_10k_articles{$user} }) > 1) {
      $to_print_10k .= " (" . scalar(keys %{ $user_10k_articles{$user} })
	. ")";
    }
  }
  $to_print_10k =~ s/:,/:/;
  print OUTPUT $to_print_10k . ".\n";
}
print OUTPUT "----\n";
print OUTPUT "\n";

$num_users_with_barnstars = 0;
$num_users_with_multiple_barnstars = 0;
$curr_first_letter = "";

%num_to_th = (1 => '1st',
	      2 => '2nd',
	      3 => '3rd',
	      4 => '4th',
	      5 => '5th');

%headers = ();
@users = ();
foreach $user (keys %user_num) {
  unless (($user_total_articles{$user} > 0) ||
	  ($user_reviewer_articles{$user} > 0)) {
    next;
  }
  push @users, $user;
  $headers{$user_first_letter{$user}} = 1;
}
@users = sort_users(@users);

$toc = "Compact ToC|side=yes";

if (exists($headers{"0&ndash;9"})) {
  $toc .= "|num=yes";
}
foreach $letter ('A'..'Z') {
  unless (exists($headers{$letter})) {
    $toc .= "|" . lc($letter) . "=";
  }
}

print OUTPUT "{{$toc}}\n";
print OUTPUT "\n";

foreach $user (@users) {
  if (length($user_first_letter{$user}) &&
      ($user_first_letter{$user} ne $curr_first_letter)) {
    print OUTPUT "====" . $user_first_letter{$user} . "====\n";
    $curr_first_letter = $user_first_letter{$user};
  }
  print OUTPUT "=====" . $user_header{$user} . "=====\n";
  if ($user_0k_articles{$user} > 0) {
   print OUTPUT "* total-articles = "
      . num_format($user_total_articles{$user}) . " ("
	. percent($user_total_articles{$user}/$total_articles) . "% of total; "
	  . num_format($user_0k_articles{$user}) . " 0-words)\n";
  } else {
    print OUTPUT "* total-articles = "
      . num_format($user_total_articles{$user}) . " ("
	. percent($user_total_articles{$user}/$total_articles) . "% of total)\n";
  }
  unless (($user_total_articles{$user} < 10) &&
	  ($user_reviewed_articles{$user} == 0)) {
    if ($user_total_articles{$user} > 0) {
      my $percent_reviewed = percent($user_reviewed_articles{$user}/
				     $user_total_articles{$user});
      print OUTPUT "** user-reviewed-articles = "
	. num_format($user_reviewed_articles{$user}) . " ("
	  . $percent_reviewed . "%)\n";
    } elsif ($user_reviewed_articles{$user} > 0) {
      print OUTPUT "** user-reviewed-articles = "
	. num_format($user_reviewed_articles{$user}) . "\n";
    }
    if ($user_reviewed_articles_bad{$user} > 0) {
      print OUTPUT "** user-reviewed-articles-bad = "
	. num_format($user_reviewed_articles_bad{$user}) . "\n";
    }
  }
  print OUTPUT "* total-words = "
    . num_format($user_total_words{$user}) . " ("
      . percent($user_total_words{$user}/$total_words) . "% of total)\n";
  unless (($user_total_articles{$user} < 10) &&
	  ($user_reviewed_articles{$user} == 0)) {
    if ($user_total_words{$user} > 0) {
      my $percent_reviewed = percent($user_reviewed_words{$user}/
				     $user_total_words{$user});
      print OUTPUT "** user-reviewed-words = "
	. num_format($user_reviewed_words{$user}) . " ("
	  . $percent_reviewed . "%)\n";
    } elsif ($user_reviewed_words{$user} > 0) {
      print OUTPUT "** user-reviewed-words = "
	. num_format($user_reviewed_words{$user}) . "\n";
    }
    if ($user_reviewed_words_bad{$user} > 0) {
      print OUTPUT "** user-reviewed-words-bad = "
	. num_format($user_reviewed_words_bad{$user}) . "\n";
    }
  }
  print OUTPUT "* rollover-words = "
    . num_format($user_rollover_words{$user}) . "\n";
  my $grand_total = $user_total_words{$user} + $user_rollover_words{$user};
  print OUTPUT "* grand total = " . num_format($grand_total) . "\n";
  my $curr_barnstar_total = "";
  my $curr_barnstar_rollover = $grand_total;
  foreach $barnstar_level (keys %awards_total_count) {
    if ($barnstar_level < $grand_total) {
      my $diff = $grand_total - $barnstar_level;
      if ($diff < $curr_barnstar_rollover) {
	$curr_barnstar_total = $awards_total_count{$barnstar_level};
	$curr_barnstar_rollover = $diff;
      }
    }
  }
  print OUTPUT "* new rollover words = "
    . num_format($curr_barnstar_rollover) . "\n";
  if ($user_reviewer_articles{$user} > 0) {
    print OUTPUT "* articles-reviewer-for = "
      . num_format($user_reviewer_articles{$user}) . " ("
	. percent($user_reviewer_articles{$user}/
		  $total_articles_reviewed)
	  . "% of reviewed)\n";
    if ($total_words_reviewed > 0) {
      print OUTPUT "* words-reviewer-for = "
	. num_format($user_reviewer_words{$user}) . " ("
	  . percent($user_reviewer_words{$user}/
		    $total_words_reviewed)
	    . "% of reviewed)\n";
    } else {
      print OUTPUT "* words-reviewer-for = "
	. num_format($user_reviewer_words{$user}) . "\n";
    }
  }
  my $num_barnstars = ((length($curr_barnstar_total) > 0) +
		       ($user eq $largest_article_user) +
		       exists($user_10k_articles{$user}) +
		       exists($user_rank_articles{$user}) +
		       exists($user_rank_words{$user}) +
		       exists($user_rank_5k{$user}));
  if ($num_barnstars > 0) {
    my(@barnstars_to_print) = ();
    $num_users_with_barnstars++;
    my $list_stars = "**";
    if ($num_barnstars > 1) {
      print OUTPUT "* barnstars =\n";
      $num_users_with_multiple_barnstars++;
    } else {
      print OUTPUT "* barnstar =";
      $list_stars = "";
    }
    my $articles_term = "article";
    if ($user_total_articles{$user} > 1) {
      $articles_term = "articles";
    }
    if (length($curr_barnstar_total)) {
      print OUTPUT "$list_stars $curr_barnstar_total\n";
      my $barnstar;
      if (exists($total_count_barnstars{$curr_barnstar_total})) {
	$barnstar = $total_count_barnstars{$curr_barnstar_total}
	  . "This barnstar is awarded to '''" . $user_alias{$user}
	    . "''' for copy editing " . num_format($user_total_articles{$user})
	      . " $articles_term with a total of "
		. num_format($user_total_words{$user})
		  . " words during the [[WP:GOCE|Guild of Copy Editors']] "
		    . $drive_link . ". --~~~~}}";
      } else { # Caretaker's Star
	$barnstar =
	  "{| class=\"barnstar\" style=\"border:1px solid gray; background:#ffffcc;\"
|-
|rowspan=\"2\" style=\"padding-right:5px;\" | [[File:Most Excellent Order of Star Scribes.png|120px]]
|style=\"font-size:1.65em; padding:0; height: 1.1em;\" | '''The Most Excellent Order of the Caretaker's Star'''
|-
|style=\"border-top: 1px solid gray;\" | For exceptional copyediting efforts during the [[WP:GOCE|Guild of Copy Editors']] " . $drive_link . " (editing " . num_format($user_total_articles{$user}) . " articles with a combined total of " . num_format($user_total_words{$user}) . " words, '''" . $user_alias{$user} . "''' is presented with this exclusive, brilliant, Most Excellent Order of the Caretaker's Star. Thank you for this magnificent contribution. --~~~~
|}";
      }
      push @barnstars_to_print, $barnstar;
    }
    # do rank barnstars
    if (exists($user_rank_articles{$user})) {
      my $rank = $user_rank_articles{$user};
      my $equal = "";
      if (scalar(keys %{ $rank_articles_users{$rank} }) > 1) {
	print OUTPUT "$list_stars equal " . $num_to_th{$rank}
	  . " place, number of articles ("
	    . num_format($user_total_articles{$user}) . ")\n";
	$equal = " (Equal)";
      } else {
	print OUTPUT "$list_stars " . $num_to_th{$rank}
	  . " place, number of articles ("
	    . num_format($user_total_articles{$user}) . ")\n";
      }

      my $barnstar;
      if ($rank == 1) {
	$barnstar = "{{subst:GOCE Award|1='''Article Count -" . $equal
	  . " 1st Place'''<br/>This '''Guild of Copy Editors Gold Star Award''' is awarded to '''"
	  . $user_alias{$user}
	    . "''' for copy editing the largest number of articles ("
	    . num_format($user_total_articles{$user})
	      . ") during the [[WP:GOCE|Guild of Copy Editors']] " . $drive_link
		. ". Congratulations on achieving first place in this category. Thank you for your hard work. --~~~~}}";
      } else {
	$barnstar = "{| style=\"border: 1px solid gray; background-color: #fffff0;\"
|rowspan=\"2\" valign=\"middle\" | [[Image:Goce silver barnstar.png|75 px]]
|rowspan=\"2\" |
|style=\"font-size: x-large; padding: 0; vertical-align: middle; height: 1.1em;\" | '''Leaderboard Award - Number of articles edited -" . $equal . " " . $num_to_th{$rank}
  . " Place'''
|-
|style=\"vertical-align: middle; border-top: 1px solid gray;\" | This '''Leaderboard Award''' is awarded to '''" . $user_alias{$user}
  . "''' for copy editing " . num_format($user_total_articles{$user})
    . " articles during the [[WP:GOCE|Guild of Copy Editors']] " . $drive_link
      . ". Congratulations on winning this prestigious award. Thank you for your efforts. --~~~~
|}";
      }
      push @barnstars_to_print, $barnstar;
    }
    if (exists($user_rank_words{$user})) {
      my $rank = $user_rank_words{$user};
      my $equal = "";
      if (scalar(keys %{ $rank_words_users{$rank} }) > 1) {
	print OUTPUT "$list_stars equal " . $num_to_th{$rank}
	  . " place, word count ("
	    . num_format($user_total_words{$user}) . ")\n";
	$equal = " (Equal)";
      } else {
	print OUTPUT "$list_stars " . $num_to_th{$rank}
	  . " place, word count ("
	    . num_format($user_total_words{$user}) . ")\n";
      }
      my $barnstar;
      if ($rank == 1) {
	$barnstar =
	  "{{subst:GOCE Award|1='''Word Count -" . $equal
	    . " 1st Place'''<br/>This '''Guild of Copy Editors Gold Star Award''' is awarded to '''"
	      . $user_alias{$user} . "''' for copy editing the largest number of words (" . num_format($user_total_words{$user}) . " during the [[WP:GOCE|Guild of Copy Editors']] "
		. $drive_link . ". Congratulations on achieving first place in this category. Thank you for your hard work. --~~~~}}";
      } else {
	$barnstar =
	  "{| style=\"border: 1px solid gray; background-color: #fffff0;\"
|rowspan=\"2\" valign=\"middle\" | [[Image:Goce silver barnstar.png|75 px]]
|rowspan=\"2\" |
|style=\"font-size: x-large; padding: 0; vertical-align: middle; height: 1.1em;\" | '''Leaderboard Award - Number of words edited -"
	    . $equal . " " . $num_to_th{$rank} . " Place'''
|-
|style=\"vertical-align: middle; border-top: 1px solid gray;\" | This '''Leaderboard Award''' is awarded to '''"
	      . $user_alias{$user} . "''' for copy editing articles to a total of "
		. num_format($user_total_words{$user})
		  . " words during the [[WP:GOCE|Guild of Copy Editors']] "
		    . $drive_link
		      . ". Congratulations on winning this prestigious award. Thank you for your efforts. --~~~~
|}";
      }
      push @barnstars_to_print, $barnstar;
    }
    if (exists($user_rank_5k{$user})) {
      my $rank = $user_rank_5k{$user};
      my $equal = "";
      if (scalar(keys %{ $rank_5k_users{$rank} }) > 1) {
	print OUTPUT "$list_stars equal " . $num_to_th{$rank}
	  . " place, 5k+ (" . $user_total_5k{$user} . ")\n";
	$equal = " (Equal)";
      } else {
	print OUTPUT "$list_stars " . $num_to_th{$rank} . " place, 5k+ ("
	  . $user_total_5k{$user} . ")\n";
      }
      my $barnstar;
      if ($rank == 1) {
	$barnstar = "{{subst:GOCE Award|1='''Articles of 5K+ Words -" . $equal
	  . " 1st Place'''<br/>This '''Guild of Copy Editors Gold Star Award''' is awarded to '''"
	    .$user_alias{$user} . "''' for copy editing the largest number ("
	      . $user_total_5k{$user}
		. ") of articles of 5000 words or more during the [[WP:GOCE|Guild of Copy Editors']] " . $drive_link
		  . ". Congratulations on achieving first place in this category. Thank you for your hard work. --~~~~}}";
      } else {
	$barnstar = "{| style=\"border: 1px solid gray; background-color: #fffff0;\"
|rowspan=\"2\" valign=\"middle\" | [[Image:Goce silver barnstar.png|75 px]]
|rowspan=\"2\" |
|style=\"font-size: x-large; padding: 0; vertical-align: middle; height: 1.1em;\" | '''Leaderboard Award - Number of 5k articles edited -"
	  . $equal . " " . $num_to_th{$rank} . " Place'''
|-
|style=\"vertical-align: middle; border-top: 1px solid gray;\" | This '''Leaderboard Award''' is awarded to '''"
	    . $user_alias{$user} . "''' for copy editing "
	      . $user_total_5k{$user} . " articles of 5000 words or more during the [[WP:GOCE|Guild of Copy Editors']] "
		. $drive_link
		  . ". Congratulations on winning this prestigious award. Thank you for your efforts. --~~~~
|}";
      }
      push @barnstars_to_print, $barnstar;
    }
    if ($user eq $largest_article_user) {
      print OUTPUT "$list_stars Largest single article: [["
	. $largest_article_name
	  . "]] (" . num_format($largest_article_size) . ")\n";
      my $barnstar = "{| style=\"border: 1px solid gray; background-color: #fdffe7;\"
|rowspan=\"2\" valign=\"middle\" | [[File:Main Barnstar.png]]
|rowspan=\"2\" |
|style=\"font-size: x-large; padding: 0; vertical-align: middle; height: 1.1em;\" | '''Special Barnstar: Largest Single Article'''
|-
|style=\"vertical-align: middle; border-top: 1px solid gray;\" | This Special barnstar is awarded to '''" . $user_alias{$user} . "''' for courageously editing the largest single article, [[" . $largest_article_name . "]] (" . num_format($largest_article_size) . " words), during the [[WP:GOCE|Guild of Copy Editors']] " . $drive_link . ". Thank you for tackling this enormous article! --~~~~
|}";
      push @barnstars_to_print, $barnstar;
    }
    if (exists($user_10k_articles{$user})) {
      print OUTPUT "$list_stars article >= 10k ("
	. scalar(keys %{ $user_10k_articles{$user} }) . ")\n";
      if (scalar(keys %{ $user_10k_articles{$user} }) == 1) {
	my $article = (keys %{ $user_10k_articles{$user} })[0];
	my $barnstar = "{{subst:The 10k Copyedit Barnstar|1="
	  . "This barnstar is awarded to '''" . $user_alias{$user}
	    . "''' for copy editing [[" . $article . "]] ("
	      . num_format($user_article_size{$user}{$article})
		. " words) during the [[WP:GOCE|Guild of Copy Editors']] "
		  . $drive_link
		    . ". Thank you for taking on this major task. --~~~~}}";
	push @barnstars_to_print, $barnstar;
      } else {
	my $num_articles = scalar(keys %{ $user_10k_articles{$user} });
	my $barnstar =
	  "{{subst:The 10k Copyedit Barnstar|1=This barnstar is awarded to '''"
	    . $user_alias{$user} . "''' for copy-editing "
	      . num_format($num_articles)
		. " articles of 10,000 words or more during the "
		  . "[[WP:GOCE|Guild of Copy Editors']] " . $drive_link
		    . ". Thank you for taking on such a heavy workload. --~~~~}}";
	push @barnstars_to_print, $barnstar;
      }
    }

    if (scalar(@barnstars_to_print)) {
      print OUTPUT "{{pre2|<nowiki>\n";
      print OUTPUT join("\n",@barnstars_to_print) . "\n";
      print OUTPUT "</nowiki>}}\n";
    }
  } else {
    print OUTPUT "* barnstar = (none)\n";
  }
  print OUTPUT "\n";
}
close(OUTPUT);
warn "Finished; had " . scalar(@users) . " users editing/reviewing, "
  . $num_users_with_barnstars . " users with barnstars, "
  . $num_users_with_multiple_barnstars . " with multiple barnstars\n";