User:MilHistBot/aclass.pl

From Wikipedia, the free encyclopedia
#!/usr/bin/perl -w
#
# aclass.pl -- Pass or fail an A class review
#     This Bot runs every hour, looking for A class articles that have been promoted by a MilHist administrator
#    If it finds one, it follows the steps involved in promoting or failing it.
# Usage: aclass.pl
#    30 Mar 13 Add failure option
#     2 Jun 14 Add reassessment options
#     7 Jun 14 Error handling
#    14 Aug 14 Change a message
#     6 Sep 14 Enhancements:
#        (a) Do not stop if a bad nomination is found
#        (b) Follow the redirect is the assessment page is a redirect
#        (c) Mark edits as bot but not minor
#        (d) Make STDERR a utf8
#     8 Sep 14 Some templates are listed with underscores instead of spaces
#        Perform step 4a (removal from nom page) first
#    22 Jan 16 Do not leave a blank line behind on the review page
#     9 Nov 16 Patches for nominators in strange format
#    17 Oct 17 Allow for Coordinators on meta
#    12 Nov 17 Higher MilHist Awards
#    20 Nov 17 Higher MilHist Awards
#    22 Feb 18 MilHist awards script

use English;
use strict;
use utf8;
use warnings;

use Carp qw(croak longmess);
use Data::Dumper;
use File::Basename;
use MediaWiki::Bot;
use POSIX;
use unicode::collate;
use XML::Simple;

binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");

my $pagename;
my $talkpage;
my $assessment;
my $outcome;
my $redirect;

my $aclass_review = 'Wikipedia:WikiProject Military history/Assessment/A-Class review';

my $year;
my $month;
my $next_month;
my $next_year;

my $Collator = Unicode::Collate->new();
   
# log in to the wiki
my $editor = MediaWiki::Bot->new({
        assert        => 'bot',
        host        => 'en.wikipedia.org',
        protocol     => 'https',
        operator     => 'Hawekeye7',
    }) or die "new MediaWiki::Bot failed";
   
my $dirname = dirname (__FILE__, '.pl');
push @INC, $dirname;
require Cred;
my $cred = new Cred ();
my $log = $cred->log ();

require showcase;

sub error_exit ($) {
    my @message = @ARG;
    if ($editor->{error}->{code}) {
        push @message, ' (', $editor->{error}->{code} , ') : ' , $editor->{error}->{details};
    }
    $cred->error (@message);
}

sub set_date () {
    my @current_time = gmtime ();
    $year = $current_time[5] + 1900;
    $month = $current_time[4];
    ($next_month, $next_year) = (11 == $month) ? (0, $year+1) : ($month+1, $year);
}

sub nominators () {
    my $nom_text = $editor->get_text ($assessment) or
        error_exit ("Unable to find '$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
        error_exit "Unable to find nominator";

    return @nominators;
}

sub step1 ($$) {
    $cred->showtime ("Step1: Archiving the nomination page\n");
    my ($comment, $summary) = @ARG;
    my $text = $editor->get_text ($assessment) or
        error_exit ("Unable to find '$assessment'");

    my $page_text = join "\n",
        '{{subst:archive top}}',
        "$comment ~~~~",
        $text,
        '{{subst:archive bottom}}';

    $editor->edit ({
        page => $assessment,
        text => $page_text,
        summary => $summary,
        minor => 0,
    }) or
        error_exit ("unable to edit '$assessment'");

}

sub newaction ($$$$$$) {
    my ($action, $date, $link, $result, $revid, $id) = @ARG;
    my $newaction = join "\n",
        "|action${id}=$action",
        "|action${id}date=$date",
        "|action${id}link=$link",
        "|action${id}result=$result",
        "|action${id}oldid=$revid";
    return $newaction;
}

sub update_article_history ($$$$$$) {
    my ($text, $action, $date, $link, $result, $revid) = @ARG;
    $text =~ s/{{Article\s*History/{{ArticleHistory/is;
    my ($articleHistory) = $text =~ /{{ArticleHistory(.+?)}}/gis;
    if ($articleHistory) {
       
        my $has_nested_text;
        while ($text =~ /{{ArticleHistory[^}]+({{[^}]+}})/) {
#            print "Nested text!!!!\n";
            my $nested_text = $1;
#            print "nested text=$nested_text\n";   
            my $transformed_text = $nested_text;
            $transformed_text =~ s/{{(.+)}}/%%<$1>%%/;
#            print "transformed text=$transformed_text\n";   
            $text =~ s/\Q$nested_text\E/$transformed_text/;
            $has_nested_text = 1;                                    
        }

#        print "articlehistory='$articleHistory'\n";
        for (my $id = 1;; ++$id) {
            if ($articleHistory =~ /action$id/) {
#                print "\t\tfound action$id\n";
            } else {
#                print "\t\tno $id - going with that\n";
                my $newaction = newaction ($action, $date, $link, $result,  $revid, $id);
                $text =~ s/{{Article\s*History(.+?)}}/{{ArticleHistory$1\n$newaction\n}}/is;
                last;
            }
        }
       
        if ($has_nested_text) {
            $text =~ s/%%</{{/g;
            $text =~ s/>%%/}}/g;
        }

    } else {
        my $newaction = newaction ($action, $date, $link, $result, $revid, 1);
        $text =~ s/^/{{ArticleHistory\n$newaction\n}}\n/is;
    }
    return $text;
}

sub step23 ($$) {
    my ($result, $summary) = @ARG;

    $cred->showtime ("Step2: Get the permanent link\n");
    my ($history) = $editor->get_history ($pagename, 1) or
        error_exit ("Unable to get history of '$pagename'");

    my $revision = $history->{revid};

    $cred->showtime ("Step3: Update the talk page\n");
    my $text = $editor->get_text ($talkpage) or
        error_exit ("Unable to find '$talkpage'");
    $text = update_article_history ($text, 'WAR', '~~~~~', $assessment, $result, $revision); 
#    print $text, "\n";

    foreach ($text) {
        if ($outcome eq 'pass') {
            s/((WikiProject Ships|WPSHIPS|WikiProject Aviation|ShipwrecksWikiProject|WikiProject Military history|WPMILHIST|MILHIST).+?)class=(Start|Stub|B|C|GA)/$1class=A/igs;
        }
        if ($outcome eq 'demoted') {
            s/((WikiProject Ships|WPSHIPS|WikiProject Aviation|ShipwrecksWikiProject|WikiProject Military history|WPMILHIST|MILHIST).+?)class=A/$1class=C/igs;
        }
        s/A-class=current/A-Class=$outcome/igs;
    }
#    print $text, "\n";
   
    $editor->edit ({
        page => $talkpage,
        text => $text,
        summary => $summary,
        minor => 0,
    }) or
        error_exit ("unable to edit '$talkpage'");

}

sub step4a ($) {
    $cred->showtime ("Step4a: Update the review page\n");
    my ($summary) = @ARG;

    my $text = $editor->get_text ($aclass_review) or
        error_exit ("Unable to find '$aclass_review'");

#    print $text, "\n";
   
    $text =~ s/WikiProject_Military_history/WikiProject Military history/g;
    my $p = $redirect // $assessment;
    $text =~ s/{{\Q$p\E}}\s*\n//s or
        error_exit ("Unable to find '$p' on '$aclass_review'");
    undef $redirect;               
   
#    print $text, "\n"; ############

    $editor->edit ({
        page => $aclass_review,
        text => $text,
        summary => $summary,
        minor => 0,
    }) or
        error_exit ("Unable to edit '$aclass_review'");
}

sub step4b ($$) {
    $cred->showtime ("Step4b: Archive the review\n");
    my ($result, $summary) = @ARG;

    my $archive = "Wikipedia:WikiProject Military history/Assessment/$year";
    my $text = $editor->get_text ($archive);
   
    $text or do {

        $text = join "",
            "{{WPMILHIST Archive|category=review}}\n",
            "{{WPMILHIST Navigation}} __FORCETOC__\n",
            "== Promoted ==\n",
            "[[Wikipedia:WikiProject Military history/Assessment/$year/Promoted]]\n",
            "\n",
            "== Failed ==\n",
            "[[Wikipedia:WikiProject Military history/Assessment/$year/Failed]]\n",
            "\n",
            "== Kept ==\n",
            "[[Wikipedia:WikiProject Military history/Assessment/$year/Kept]]\n",
            "\n",
            "== Demoted ==\n",
            "[[Wikipedia:WikiProject Military history/Assessment/$year/Demoted]]\n",
            "[[Category:Requests for military history A-Class review| ]]\n",
            "\n";

        $editor->edit ({
            page => $archive,
            text => $text,
            summary => "Created new archive page for $year",
            minor => 0,
        }) or
            error_exit ("Unable to find '$archive'");
    };

    $archive = "Wikipedia:WikiProject Military history/Assessment/$year/$result";
    $text = $editor->get_text ($archive);
   
    $text or do {

        my $category = $result = 'Promoted' || $result eq 'Kept' ? 'Successful' : 'Failed';
       
        $text = join "",
            "== $result ==\n",
            "<!--Please add new reviews directly below this line-->\n",
            "\n",
            "<!--Add archived reviews at the top please-->\n",
            "\n",
            "<noinclude>[[Category:$category requests for military history A-Class review| ]]</noinclude>\n",
            "\n";

        $editor->edit ({
            page => $archive,
            text => $text,
            summary => "Created new page for $year/$result",
            minor => 0,
        }) or
            error_exit ("Unable to find '$archive'");
    };

    $cred->showtime ("    updating $archive\n");
    $text =~ s/(<!--Please add new reviews directly below this line-->)/$1\n{{$assessment}}/s or
        $cred->showtime ("unable to find review section - please fix the comment on the page!\n");   
#    print $text, "\n";

    $editor->edit ({
        page => $archive,
        text => $text,
        summary => $summary,
        minor => 0,
    }) or
        error_exit ("Unable to edit '$archive'");
}

sub step5 ($) {
    $cred->showtime ("Step5: Update the announcements page\n");
    my ($summary) = @ARG;
    my $announcements = "Template:WPMILHIST Announcements";
    my $text = $editor->get_text ($announcements) or
        error_exit ("unable to find '$announcements'");

#    print $text, "\n";
    my $link = "[[$assessment|$pagename]]";
    $text =~ s/WP:/Wikipedia:/g;
    $text =~ s/(•)* \Q$link//s;
#    print $text, "\n";
   
    $editor->edit ({
        page => $announcements,
        text => $text,
        summary => $summary,
        minor => 0,
    }) or
        error_exit ("Unable to edit '$announcements'");
}

sub step6 ($) {
    $cred->showtime ("Step6: Update the article showcase\n");
    my ($summary) = @ARG;
    my $showcase_a = 'Wikipedia:WikiProject Military history/Showcase/A';
    my $text = $editor->get_text ($showcase_a) or
        error_exit ("unable to find '$showcase_a'");
#    print $text, "\n";
    my $showcase = new showcase ($text);


    if ($outcome eq 'pass') {
        $showcase->add ($pagename);
    }

    if ($outcome eq 'demoted') {
        $showcase->del ($pagename);
    }

    $editor->edit ({
        page => $showcase_a,
        text => $showcase->text,
        summary => $summary,
        minor => 0,
    }) or
        error_exit ("unable to edit '$showcase_a'");
}

sub step7 ($) {
    $cred->showtime ("Step7: Update the newsletter\n");
    my ($summary) = @ARG;

    sub nominator_list () {
        my @nominators = map {"[[User:$ARG|$ARG]]"} nominators ();
        if (1 == @nominators) {
            return $nominators[0];
        } else {
            my $last = pop @nominators;
            my $nominator_list = join ', ', @nominators;
            $nominator_list = join ' and ', $nominator_list, $last;
            return $nominator_list;
        }
    }

    my $nominator_list = nominator_list ();
    my $new_entry = "; [[$pagename]] ($nominator_list)\n";

    my @months = qw(January February March April May June July August September October November December);
   
    my $next_date = "$months[$next_month] $next_year";
    my $newsletter ="Wikipedia:WikiProject Military history/News/$next_date/Articles";
    my $text = $editor->get_text ($newsletter);

    if ($text) {
        $cred->showtime ("\tupdating $newsletter\n");

        my @a = split(/^/, $text);
        my @b;
        my $inserting = 0;
        my $inserted = 0;

        foreach (@a) {
            if (/New A-Class articles/) {
                $inserting = 1;
            }
            if (/Footer/) {
                if  (! $inserted) {
                    my $x = pop @b;
                    if ($x !~ /^\s*$/) {
                        push @b, $x;
                    }
                    push @b, $new_entry, "\n";
                }
            }
            if ($inserting) {
                if (/^;\[\[(.+?)\]\]/) {
                    if ($Collator->cmp ($1, $new_entry) > 0) {
                        push @b, "\n", $new_entry;
                        $inserting = 0;
                        $inserted = 1;
                    }
                }
                if (/^$/) {
                    push @b, "\n", $new_entry;
                    $inserting = 0;
                    $inserted = 1;
                }
            }
            push @b, $ARG;
        }

        $text = join '', @b;

    }else {
        $cred->showtime ("\tcreating $newsletter\n");

        $text = join "",
            "{{Wikipedia:WikiProject Military history/News/$next_date/Header}}__NOTOC__\n\n",
            "{{WPMILHIST Newsletter section header 2|New featured articles}}\n\n",
            "{{WPMILHIST Newsletter section header 2|New featured lists}}\n\n",
            "{{WPMILHIST Newsletter section header 2|New featured topics}}\n\n",
            "{{WPMILHIST Newsletter section header 2|New featured pictures}}\n\n",
            "{{WPMILHIST Newsletter section header 2|New featured portals}}\n\n",
            "{{WPMILHIST Newsletter section header 2|New A-Class articles}}\n",
            $new_entry, "\n",
            "{{Wikipedia:WikiProject Military history/News/$next_date/Footer}}\n\n";
    }

    $editor->edit ({
        page => $newsletter,
        text => $text,
        summary => $summary,
        minor => 0,
    }) or
        error_exit ("unable to edit '$newsletter'");
}

sub step8 ($$) {
    $cred->showtime ("Step8: Tracking award eligibility\n");
    my ($coordinator, $summary) = @ARG;

    sub awards ($) {
        my ($nominator) = @ARG;
        my @archives = ('Wikipedia talk:WikiProject Military history/Awards',
                'Wikipedia talk:WikiProject Military history/Awards/ACR/Archive 2',
                'Wikipedia talk:WikiProject Military history/Awards/ACR/Archive 1');
       
        foreach my $archive (@archives) {   
            my $text = $editor->get_text ($archive) or
                error_exit ("unable to find '$archive'");

            my @lines = reverse split(/^/, $text);
            foreach (@lines) {   
                if (/$nominator \((\d+)\)/i) {
                    my $count = $1;
                    $cred->showtime ("$nominator has $count A class medals\n");
                    return $count;
                }   
            }
        }
        return 0;       
    }

    sub award ($) {
        my ($awards) = @ARG;
        my $award;
        my $articles_per_award;
        my $tracking;
        my $acm_tracking = 'Wikipedia:WikiProject Military history/Awards/ACM/Eligibility tracking';
        my $acc_tracking = 'Wikipedia:WikiProject Military history/Awards/ACC/Eligibility tracking';
   
        if ($awards < 5) {
            $award = 'A-Class medal';
            $articles_per_award = 3;
            $tracking = $acm_tracking;
        } elsif ($awards < 10) {
            $award = 'A-Class medal with Oak Leaves';
            $articles_per_award = 3;
            $tracking = $acm_tracking;
        } elsif ($awards < 20) {
            $award = 'A-Class medal with Swords';
            $articles_per_award = 3;
            $tracking = $acm_tracking;
        } elsif ($awards < 35) {
            $award = 'A-Class medal with Diamonds';   
            $articles_per_award = 3;
            $tracking = $acm_tracking;
        } elsif ($awards < 40) {
            $award = 'A-Class cross';
            $articles_per_award = 5;
            $tracking = $acc_tracking;
        } elsif ($awards < 46) {
            $award = 'A-Class cross with Oak Leaves';
            $articles_per_award = 5;
            $tracking = $acc_tracking;
        } elsif ($awards < 56) {
            $award = 'A-Class cross with Swords';
            $articles_per_award = 5;
            $tracking = $acc_tracking;
        } else {
            $award = 'A-Class cross with Diamonds';
            $articles_per_award = 5;
            $tracking = $acc_tracking;
        }
       
        return ($award, $articles_per_award, $tracking);       
    }

    sub nomination ($$$@) {
        my ($coordinator, $nominee, $award, $awards, @pages) = @ARG;
        my $nomination_page = 'Wikipedia_talk:WikiProject_Military_history/Awards';

        my $text = $editor->get_text ($nomination_page) or
            error_exit ("unable to find '$nomination_page'");

        my @a = reverse split(/^/, $text);
        my @b;
        my $inserted = 0;
        my $skipping = 1;
       
        foreach (@a) {
            push @b, $ARG;
            if ($award =~ /A-Class medal/) {
                if (/<!--/ && $skipping) {
                    $skipping = 0;
                    next;
                }
            }
            if (/<!--/ && !$inserted) {
                push @b, "*'''Support''' As nominator $coordinator via ~~~~\n\n";
                push @b, "{{WPMILHIST Award nomination|nominee=$nominee|citation=[[$pages[0]]], [[$pages[1]]], and [[$pagename]]|award=$award|status=nominated}}\n";
#                push @b, "''$award'': for [[$pages[0]]], [[$pages[1]]], and [[$pagename]]\n";
                push @b, "===$nominee ($awards)===\n";
                $inserted = 1;               
            }
        }
        $text = join '', reverse @b;
       
        $cred->showtime ("\tupdating nomination page $nomination_page\n");
        $editor->edit ({
            page => $nomination_page,
            text => $text,
            summary => "$nomineee ($awards)",
            minor => 0,
        }) or
            error_exit ("unable to edit $nomination_page");
    }
   
    # Find the nominators on the tracking pages
    my @nominators = nominators ();
    foreach my $nominator (@nominators) {
        my $awards = awards($nominator) + 1;
        my ($award, $articles_per_award, $tracking) = award ($awards);
        my $text = $editor->get_text ($tracking) or
            error_exit ("unable to find '$tracking'");

        foreach ($text) {
            if (/# '''$nominator:*'''(.*)/) {
                $cred->showtime ("\tFound nominator $nominator in tracking list\n");
                my $string = $1;
                my $count = 0;
                my @pages;
                while ($string =~ /\[\[File:Symbol a class.svg\|15px\|link=(.+?)\]\]/g) {
                        $count++;
                        push @pages, $1;
                }
                $cred->showtime ("\tfound $count nominations for $nominator\n");
                ++$count;
           
                if ($count < $articles_per_award) {
                    s/(# '''$nominator:*'''.*)/$1 [[File:Symbol a class.svg|15px|link=$pagename]]/;
                } else {
                    s/(# '''$nominator:*''')(.+)/$1/;
                    nomination ($coordinator, $nominator, $award, $awards, $pages[0], $pages[1], $pagename);
                }
            } else {
                $cred->showtime ("\tCould not find nominator $nominator in tracking list -- adding\n");
                my @a = split(/^/, $text);
                my @b;
                my $found = 0;
                my $inserted = 0;
                foreach (@a) {
                    if (!$inserted) {
                        if (/^# '''(.+):*'''/) {
                            $found++;
                            if ($1 gt $nominator) {
                                push @b, "# '''$nominator:''' [[File:Symbol a class.svg|15px|link=$pagename]]\n";
                                $inserted = 1;
                            }
                        }
                        if (/^$/ && $found) {
                            push @b, "# '''$nominator:''' [[File:Symbol a class.svg|15px|link=$pagename]]\n";
                            $inserted = 1;
                        }
                    }
                    push @b, $ARG;
                }
                $text = join '', @b;
            }
       
            $cred->showtime ("\tupdating tracking page $tracking\n");
            $editor->edit ({
                page => $tracking,
                text => $text,
                summary => $summary,
                minor => 0,
            }) or
                error_exit ("unable to edit $tracking");
        }
    }             
}

sub pass ($) {
    $cred->showtime ("Passing $pagename\n");
    my ($coordinator) = @ARG;
    my $comment = "Article '''promoted''' by $coordinator via";
    my $summary = "$pagename Passed A class review";

    step4a ($summary);
    step1 ($comment, $summary);
    step23 ('approved', $summary);
    step4b ('Promoted', $summary);
    step5 ($summary);
    step6 ($summary);
    step7 ($summary);
    step8 ($coordinator, $summary);
    $cred->showtime ("done\n");
}

sub failed ($) {
    $cred->showtime ("Failing $pagename\n");
    my ($coordinator) = @ARG;
    my $comment = "'''No consensus to promote''' at this time - $coordinator via";
    my $summary = "$pagename failed A class review";

    step4a ($summary);
    step1 ($comment, $summary);
    step23 ('not approved', $summary);
    step4b ('Failed', $summary);
    step5 ($summary);
    $cred->showtime ("done\n");
}

sub kept ($) {
    $cred->showtime ("Keeping $pagename\n");
    my ($coordinator) = @ARG;
    my $comment = "Article still meets A-Class criteria  - $coordinator via";
    my $summary = "$pagename kept after A class reappaisal";

    step4a ($summary);
    step1 ($comment, $summary);
    step23 ('kept', $summary);
    step4b ('Kept', $summary);
    step5 ($summary);
    $cred->showtime ("done\n");
}

sub demoted ($) {
    $cred->showtime ("Demoting $pagename\n");
    my ($coordinator) = @ARG;
    my $comment = "Article no longer meets A-Class criteria - $coordinator via";
    my $summary = "$pagename demoted after A class reappaisal";

    step4a ($summary);
    step1 ($comment, $summary);
    step23 ('demoted', $summary);
    step4b ('Demoted', $summary);
    step5 ($summary);
    step6 ($summary);
    $cred->showtime ("done\n");
}

sub find_pages () {
    my @pages;
    my $text = $editor->get_text($aclass_review) or
        error_exit "Unable to find '$aclass_review'\n";
   
#    print $text;
    my @lines = split/\n/, $text;
    foreach (@lines) {
        s/_/ /g;
        if (/ACR\/(Closing|Instructions)/) {
            next;
        }
        if (/{{Wikipedia:WikiProject Military history\/Assessment\/(.+)}}/) {
            push @pages, $1;
        }
    }
    return @pages;
}

sub iscoordinator ($) {
    my ($user) = @ARG;
    my $category = 'WikiProject Military history coordinators';
    my @coordinators = $editor->get_pages_in_category ($category);
    foreach my $coordinator (@coordinators) {
#        print $coordinator, "\n";
        return 1 if "User:$user" eq $coordinator;
    }
   
    # Try on meta
    my $meta = MediaWiki::Bot->new ({
#        assert        => 'bot',
        host        => 'meta.wikimedia.org',
        protocol     => 'https',
    }) or die "new MediaWiki::Bot failed";

    $meta->login ({
        username => $cred->user,
        password => $cred->password
    }) or die $meta->{error}->{code} . ': ' . $meta->{error}->{details};

    my @meta_coordinators = $meta->get_pages_in_category ($category);
    foreach my $meta_coordinator (@meta_coordinators) {
#        print $meta_coordinator, "\n";
        return 1 if "User:$user" eq $meta_coordinator;
    }

    return 0;
}

sub whodunnit ($) {
    my ($status) = @ARG;
    my $user;
    my @history = $editor->get_history ($talkpage) or
        error_exit ("unable to get history of $talkpage'");
       
    foreach my $history (@history) {
#        print "user=", $history->{user}, ", revid=", $history->{revid}, "\n";
        $user //= $history->{user};
        my $text = $editor->get_text ($talkpage, $history->{revid}) or
            error_exit ("unable to find '$talkpage' revid $history->{revid}");
        if ($text =~ /A-Class\s*=\s*(\w+)/i) {
#            print "status=$1\n";
            last if ($1 ne $status);
        } else {
            last;
        }
        $user = $history->{user};
    }
#    print "user=", $user, "\n";
    return $user;
}

sub outcome () {
    while (1) {
        my $text = $editor->get_text ($talkpage) or
            error_exit ("Unable to find '$talkpage'");

        if ($text !~ /A-Class\s*=\s*(\w+)/i) {
            $cred->showtime ("unable to find A-Class on $talkpage\n");
           
            # See if the assessment is a redirect
            my $t = $editor->get_text($assessment) or
                error_exit "Unable to find '$assessment'\n";

            if ($t =~ /REDIRECT \[\[(.+)\]\]/) {
                    $cred->showtime ("'$assessment' redirects to '$1'\n");
                    $redirect = $assessment;
                    $assessment = $1;
                    if ($assessment =~ /Wikipedia:WikiProject Military history\/Assessment\/(.+)/) {
                        $pagename = $1;
                        $talkpage = "Talk:$pagename";
                        redo;
                    }
            }
            return 'unknown';
        }
        return lc $1;
    }
}

sub clear_outcome () {
    while (1) {
        my $text = $editor->get_text ($talkpage) or
            error_exit ("Unable to find '$talkpage'");

        if ($text !~ /A-Class=(\w+)/i) {
            $cred->showtime ("unable to find A-Class on $talkpage\n");
           
            # See if the assessment is a redirect
            my $t = $editor->get_text($assessment) or
                error_exit "Unable to find '$assessment'\n";
            if ($t =~ /REDIRECT \[\[(.+)\]\]/) {
                    $cred->showtime ("'$assessment' redirects to '$1'\n");
                    $redirect = $assessment;
                    $assessment = $1;
                    if ($assessment =~ /Wikipedia:WikiProject Military history\/Assessment\/(.+)/) {
                        $pagename = $1;
                        $talkpage = "Talk:$pagename";
                        redo;
                    }
            }
        }
    }
}

sub process_pages () {
   
    set_date ();
    my @pages = find_pages ();
    foreach my $p (@pages) {
        $pagename = $p;

        $cred->showtime ("Checking $pagename...\n");
        $assessment = "Wikipedia:WikiProject Military history/Assessment/$pagename";
        $pagename =~ s/\/archive\d+//;
        $pagename =~ s/_/ /g;       
        $talkpage = "Talk:$pagename";
        $redirect = undef;               

        $outcome = outcome ();
        $cred->showtime ($outcome, "\n");

        if ($outcome eq 'current') {
            next;
        } elsif ($outcome eq 'unknown') {
            next;
        } else {
           
            my $user = whodunnit ($outcome);
            if (iscoordinator ($user)) {
                $cred->showtime ("$user is a ccoordinator\n");
            } else {
                $cred->warning ("$user is NOT a ccoordinator\n");
                next;
            }

            my $coordinator = "[[User:$user|$user]] ([[User talk:$user|talk]])";
            if ($outcome eq 'pass') {
                pass ($coordinator);
            } elsif ($outcome eq 'fail') {
                failed ($coordinator);
            } elsif ($outcome eq 'kept') {
                kept ($coordinator);
            } elsif ($outcome eq 'demoted') {
                demoted ($coordinator);
            } else {
                $cred->warning ("unknown outcome: '$outcome'\n");
            }
        }
    }
}

#       Main program
$editor->login ({
    username => $cred->user,
    password => $cred->password
}) or die $editor->{error}->{code} . ': ' . $editor->{error}->{details};

$cred->showtime ("begin\n");
process_pages ();
$cred->showtime ("end\n");

exit 0;