#!/usr/bin/perl

#  MIA-O-Matic, find neglected packages looking at d-d-changes archives
#  Copyright (C) 2002 -- Benjamin Drieu <benj@debian.org>
#  
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

use FreezeThaw qw(freeze thaw);
use Date::Manip;
use strict;
package main;

my @pseudo_packages = ("base", "boot-floppy", "bugs.debian.org",
"cdimage.debian.org", "cdrom", "ftp.debian.org", "general", "install",
"installation", "kernel", "listarchives", "lists.debian.org",
"mirrors", "nonus.debian.org", "potato-cd", "press", "project",
"qa.debian.org", "security.debian.org", "tech-ctte", "wnpp",
"www.debian.org");
my $db = 'packages.db';
my $bugdb = 'bugs.db';

my %packages;
my %b;
my %bugs;
my %rc;

$| = 1;

# Reopen previous state
#open FILE, "$db" or die $!;
#my %packages = thaw (<FILE>);
#close FILE;

print STDERR "Parsing debian-devel-changes archive\n";
$/ = "\n\nFrom ";
while ($_ = <>)
{
    next if (!/^Maintainer:/mig);
    
    my($maintainer) = /^Maintainer: (.*?)\s*[^\s]*@/mi;
    my($maintemail) = /^Maintainer: .*?\s*<([^\s]*@.*)>$/mi;
    my($changedby) = /^Changed-By: (.*?)\s*[^\s]+@/mi;
    my($uploaders) = /^Uploaders: (.*)$/mi;
    my($date) = /^Date: (.*)$/mi;
    my($binary) = /^Binary: (.*)$/mi;
    my($source) = /^Source: (.*)$/mi;
    my ($version) = /^Version: (.*)$/mi;
    if ($date)
    {
	print STDERR "Parsing $source, $version, $maintainer, $changedby, $date\n";
	$packages{$source}->{'binary'} = $binary if $binary;
	$packages{$source}->{'maintainer'} = $maintainer;
	$packages{$source}->{'maintemail'} = $maintemail;
	$packages{$source}->{'uploaders'} = $uploaders if $uploaders;
	$packages{$source}->{'date'} = $date;
	my %version = (
	    'version' => $version,
	    'date' => $date,
	    'changed-by' => $changedby
	    );
	if ($packages{$source}->{'versions'})
	{
	    $packages{$source}->{'versions'} = 
		[ @{$packages{$source}->{'versions'}}, \%version ];
	}
	else
	{
	    $packages{$source}->{'versions'} = [ \%version ];
	}
	foreach my $bin (split ' ', $binary)
	{
	    $b{$bin} = $source;
	}
    }
}

print STDERR "Parsing bugs database\n";
$/ = "\n";
open BUGSDB, $bugdb or die $!;
while (defined($_=<BUGSDB>)) {
    chomp;
    my ($p, $b, $d, $st, $severity, $tags) = m/^(\S+) (\d+) (\d+) (\S+) \[.*\] (\S+) (.*)$/;
    if (defined($p))
    {
	if (grep {$p eq $_} @pseudo_packages)
	{
#	    print STDERR "Skipping metapackage $p\n";
	    next;
	}
	next if ($st =~ /done/);
	next if ($st =~ /forwarded/);
	if (not ($tags =~ /\bpending\b/ 
		 or $tags =~ /\bwontfix\b/ 
		 or $tags =~ /\bupstream\b/ 
		 or $tags =~ /\bfixed\b/ 
		 or $severity =~ /\bfixed\b/)) {
	    if ($severity eq 'grave' or $severity eq 'serious' or $severity eq 'critical')
	    {
		$rc{$p}{$b} = $d;
	    }
	    if (not ($severity eq 'wishlist'))
	    {
		$bugs{$p}{$b} = $d;
	    }
	}   
    } else {
	print "Line badly formatted: $_\n";
    }
}
close BUGDB;

print <<EOF;
<html>
<head><title>Automatic report on neglected packages</title></head>
<body>
This table is automatically generated from a perl script which
computes debian-devel-changes archives and a BTS text dump.
"Neglected" packages are packages that:
<ul>
 <li> have a RC bug unfixed for more than three months
 <li> or have an unfixed normal bug besides wishlist for more that one
 year and withoutany maintainer upload for the same time
 <li> have been NMU\'ed at least three times from last maintainer upload
</ul>
<p>
<table border=\"1\">
<tr bgcolor=\"lightgrey\"><td>Binary</td><td>Source</td><td>Maintainer</td><td>RC bugs</td><td>Normal bugs</td><td>NMUs</td><td>Last maintainer upload</td></tr>
EOF

print STDERR "Computing statistics\n";
 pkg: foreach my $key (sort keys %b)
{
    my ($p) = $packages{$b{$key}};
    my $rcbugs=0, my $normalbugs=0, my $nmu=0;

  bug: foreach my $bug (keys %{$rc{$key}})
    {
	my ($years, $months) = &age($rc{$key}{$bug}); 
	if ($years >= 1 or $months >= 3)
	{
	    $rcbugs++;
	    next bug;
	}
    }
  normalbug: foreach my $bug (keys %{$bugs{$key}})
    {
	my ($years, $months) = &age($bugs{$key}{$bug}); 
	if ($years >= 1)
	{
	    if (not scalar @{$p->{'versions'}})
	    {
		print STDERR "No version for package $key .. very strange !\n";
	    }
	  version: foreach my $version (reverse @{$p->{'versions'}})
	    {
		if (($version->{'changed-by'} eq $p->{'maintainer'}) or
		    (index $p->{'uploaders'}, $version->{'changed-by'} != -1))
		{
		    # This is last upload from current maintainer
		    my ($vyears, $vmonths) = &age($version->{'date'});
		    if ($vyears >= 1 or $vmonths >= 6)
		    {
			$normalbugs++;
		    }
		    next normalbug;
		}
	    }
	}
    }

  version2: foreach my $version (reverse @{$p->{'versions'}})
    {
	if ((! ($version->{'changed-by'} eq $p->{'maintainer'})) and
	    ((index $p->{'uploaders'}, $version->{'changed-by'}) == -1))
	{
	    $nmu++;
	}
	else
	{
	    last version2;
	}
    }

    if ($rcbugs or $normalbugs or $nmu)
    {
	my $color;
	$color = 'yellow' if $nmu;
	$color = 'orange' if $normalbugs;
	$color = 'red' if $rcbugs;
	print "<tr bgcolor=\"$color\"><td>";
	print "<a href=\"http://packages.debian.org/$key\">$key</a></td><td>$b{$key}</td>";
	print "<td><a href=\"http://bugs.debian.org/".$p->{'maintemail'}."\">".$p->{'maintainer'}."</a></td>";
	print "<td><a href=\"http://bugs.debian.org/$key\">$rcbugs</a></td>";
	print "<td><a href=\"http://bugs.debian.org/$key\">$normalbugs</a></td>";
	print "<td>$nmu</td>";
	print "<td>".$p->{'date'}."</td></tr>\n";
    }
}

print "</tr></table><p>Last modified: ".`date`."</body></html>";

# Save state
#open FILE, ">$db" or die $!;
#print FILE freeze %packages;
#close FILE;



sub age
{
    my ($date) = shift;
    my ($err, $epoch);

    $epoch = 'epoch ' if $date =~ /^[0-9]*$/;

    my ($y,$m,$w,$d,$h,$min,$s) = 
	split ':', &DateCalc(ParseDate("$epoch$date"), ParseDate('epoch'.time), \$err, 1);

    return ($y, $m);
}
