#! /usr/bin/perl -wT
use strict;
$ENV{PATH}="/bin:/usr/bin";
my %results=('all' => "../popcon-mail/results", 'stable' => "../popcon-mail/results.stable");
my $popbase = "../www";
my %popcon= ('all' => "", 'stable' => "/stable");
my %popfile=('all' => "all-popcon-results.gz", 'stable' => "stable-popcon-results.gz");
my %poptext=('all' => "All reports", 'stable' => "Stable reports");
my $mirrorbase = "/srv/mirrors/debian";
my $docurlbase = "/";
my %popconver=("1.28" => "sarge", "1.41" => "etch", "1.46" => "lenny",
"1.49" => "squeeze", "1.56" => "wheezy", "1.61" => "jessie",
"1.64" => "stretch", "1.67" => "buster");
my %popver=();
my @dists=("main","contrib","non-free","non-US");
my @fields=("inst","vote","old","recent","no-files");
my %maint=();
my %list_header=(
"maint" => <<"EOF",
# is the developer name;
#
#The fields below are the sum for all the packages maintained by that
#developer:
EOF
"source" => <<"EOF",
# is the source package name;
#
#The fields below are the sum for all the binary packages generated by
#that source package:
EOF
"sourcemax" => <<"EOF");
# is the source package name;
#
#The fields below are the maximum for all the binary packages generated by
#that source package:
EOF
# Progress indicator
sub mark
{
print join(" ",$_[0],times),"\n";
}
# HTML templates
sub htmlheader
{
my $name = $_[0];
my $sec = $_[1];
my $report_label = $poptext{$name};
if ($sec){
$report_label = "$poptext{$name} ($sec)";
}
print HTML <<"EOH";
Debian Popularity Contest
EOH
}
sub popconintro
{
my ($name, $sec, $htmlfile) = @_;
my $page = ${htmlfile};
if ($sec) {
$page = "${sec}/${htmlfile}";
}
&htmlheader($name, $sec);
print HTML <<"EOF";
Debian Popularity Contest
The popularity contest project is an attempt to map the usage of
Debian packages. This site publishes the statistics gathered from report
sent by users of the popularity-contest
package. This package sends every week the list of packages installed and the
access time of relevant files to the server via email. Every day the server
anonymizes the result and publishes this survey.
For more information, read the README and the
FAQ.
EOH
}
# Report generators
sub make_sec
{
my $sec="$_[0]/$_[1]";
-d $sec || system("mkdir","-p","$sec");
}
sub print_by
{
my ($dir,$f)=@_;
print HTML ("$f [gz] ");
}
sub make_by
{
my ($popcon,$sec,$order,$pkg,$winner,$listp) = @_;
my (%sum, $me);
my @list = sort {$pkg->{$b}->{$order}<=> $pkg->{$a}->{$order} || $a cmp $b } @{$listp};
$winner->{"$sec/$order"}=$list[0];
open DAT , "|-:utf8", "tee $popcon/$sec/by_$order | gzip -c > $popcon/$sec/by_$order.gz";
if (defined($list_header{$sec}))
{
print DAT $list_header{$sec};
$me="";
}
else
{
print DAT <<"EOF";
#Format
#
# is the package name;
EOF
$me="(maintainer)";
}
print DAT << "EOF";
# is the number of people who installed this package;
# is the number of people who use this package regularly;
# is the number of people who installed, but don't use this package
# regularly;
# is the number of people who upgraded this package recently;
# is the number of people whose entry didn't contain enough
# information (atime and ctime were 0).
#rank name inst vote old recent no-files $me
EOF
my $format="%-5d %-30s".(" %5d"x($#fields+1))." %-32s\n";
my $rank=0;
my $p;
for $p (@list)
{
$rank++;
my $m=(defined($list_header{$sec})?"":"($maint{$p})");
printf DAT $format, $rank, $p, (map {$pkg->{$p}->{$_}} @fields), $m;
$sum{$_}+=$pkg->{$p}->{$_} for (@fields);
}
print DAT '-'x66,"\n";
printf DAT $format, $rank, "Total", map {defined($sum{$_})?$sum{$_}:0} @fields, "";
close DAT;
}
sub make
{
my ($popcon, $sec,$pkg,$winner,$list)=@_;
make_sec ($popcon,$sec);
make_by ($popcon, $sec, $_, $pkg, $winner, $list) for (@fields);
}
sub print_pkg
{
my ($pkg)=@_;
return unless (defined($pkg));
my $size=length $pkg;
$size = 20 if ($size > 20);
my $pkgt=substr($pkg,0,20);
print HTML "$pkgt ",
' 'x(20-$size);
}
my %section=();
my %source=();
#Format
#
#
# is the package name;
# is the number of people who use this package regularly;
# is the number of people who installed, but don't use this package
# regularly;
# is the number of people who upgraded this package recently;
# is the number of people whose entry didn't contain enough
# information (atime and ctime were 0).
sub read_result
{
my ($name) = @_;
my $results = $results{$name};
my (%pkg,%maintpkg,%sourcepkg,%sourcemax,%arch,%vendor,$numsub,%release);
open PKG, "<:utf8","$results" or die "$results not found";
while()
{
my ($type,@values)=split(" ");
if ($type eq "Package:")
{
my @votes = @values;
my $name = shift @votes;
unshift @votes,$votes[0]+$votes[1]+$votes[2]+$votes[3];
$section{$name}='unknown' unless (defined($section{$name}));
$maint{$name}='Not in sid' unless (defined($maint{$name}));
$source{$name}='Not in sid' unless (defined($source{$name}));
for(my $i=0;$i<=$#fields;$i++)
{
my ($f,$v)=($fields[$i],$votes[$i]);
$pkg{$name}->{$f}=$v;
$maintpkg{$maint{$name}}->{$f}+=$v;
$sourcepkg{$source{$name}}->{$f}+=$v;
my($sm)=$sourcemax{$source{$name}}->{$f};
$sourcemax{$source{$name}}->{$f}=$v
if (!defined($sm) || $sm < $v);
}
}
elsif ($type eq "Architecture:")
{
my ($a,$nb)=@values;
$arch{$a}=$nb;
}
elsif ($type eq "Vendor:")
{
my ($a,$nb)=@values;
print STDERR "$a -> $nb\n";
$vendor{$a}=$nb;
}
elsif ($type eq "Submissions:")
{
($numsub)=@values;
}
elsif ($type eq "Release:")
{
my ($a,$nb)=@values;
$release{$a}=$nb;
}
}
close PKG;
return {'name' => $name,
'pkg' => \%pkg,
'maintpkg' => \%maintpkg,
'sourcepkg' => \%sourcepkg,
'sourcemax' => \%sourcemax,
'arch' => \%arch,
'release' => \%release,
'vendor' => \%vendor,
'numsub' => $numsub};
}
sub gen_sections
{
my ($stat) = @_;
my $name = $stat->{'name'};
my %pkg = %{$stat->{'pkg'}};
my %maintpkg = %{$stat->{'maintpkg'}};
my %sourcepkg = %{$stat->{'sourcepkg'}};
my %sourcemax = %{$stat->{'sourcemax'}};
my %arch = %{$stat->{'arch'}};
my %release = %{$stat->{'release'}};
my %vendor = %{$stat->{'vendor'}};
my $numsub = $stat->{'numsub'};
my $popcon = "$popbase$popcon{$name}";
my $popfile = $popfile{$name};
my @pkgs=sort keys %pkg;
my %sections = map {$section{$_} => 1} keys %section;
my @sections = sort keys %sections;
my @maints= sort keys %maintpkg;
my @sources= sort keys %sourcepkg;
my %winner = ();
my ($sec, $dir, $f);
for $sec (@sections)
{
my @list = grep {$section{$_} eq $sec} @pkgs;
make ($popcon, $sec, \%pkg, \%winner, \@list);
}
#There is a hack: '.' is both the current directory and
#the catchall regexp.
for $sec (".",@dists)
{
my @list = grep {$section{$_} =~ /^$sec/ } @pkgs;
make ($popcon, $sec, \%pkg, \%winner, \@list);
}
make ($popcon, "maint", \%maintpkg, \%winner, \@maints);
make ($popcon, "source", \%sourcepkg, \%winner, \@sources);
make ($popcon, "sourcemax", \%sourcemax, \%winner, \@sources);
for $sec (@dists)
{
open HTML , ">:utf8", "$popcon/$sec/index.html"
or print STDERR "error $popcon/$sec/index.html\n";
opendir SEC,"$popcon/$sec";
popconintro($name, $sec, "index.html");
printf HTML ("
");
printf HTML ("
Statistics for the section %-16s sorted by fields: ",$sec);
print_by (".",$_) for (@fields);
print HTML ("\n
\n");
for $dir (sort readdir SEC)
{
-d "$popcon/$sec/$dir" or next;
$dir !~ /^\./ or next;
printf HTML ("%-16s : ",$dir);
print_by ($dir,$_) for (@fields);
print HTML ("\n");
}
print HTML ("\n
\n");
printf HTML ("
");
&legend;
htmlfooter;
closedir SEC;
close HTML;
}
for $sec (@dists)
{
open HTML , ">:utf8", "$popcon/$sec/first.html";
opendir SEC,"$popcon/$sec";
popconintro($name, $sec, "first.html");
printf HTML ("
");
printf HTML ("
First package in section %-16s for fields: ",$sec);
for $f (@fields)
{
print_pkg $winner{"$sec/$f"};
}
print HTML ("\n
\n");
printf HTML ("%-16s : ","subsection");
for $f (@fields)
{
printf HTML ("%-20s ",$f);
}
print HTML ("\n","_"x120,"\n");
for $dir (sort readdir SEC)
{
-d "$popcon/$sec/$dir" or next;
$dir !~ /^\./ or next;
printf HTML ("%-16s : ",$dir);
for $f (@fields)
{
print_pkg $winner{"$sec/$dir/$f"};
}
print HTML ("\n");
}
print HTML ("\n
\n");
printf HTML ("
");
&legend;
htmlfooter;
closedir SEC;
close HTML;
}
open HTML , ">:utf8", "$popcon/index.html";
popconintro($name, "", "index.html");
printf HTML ("
");
printf HTML ("
Download
");
printf HTML ("
Statistics for the whole archive sorted by fields:
");
print_by (".",$_) for (@fields);
print HTML ("
\n \n");
printf HTML ("
Statistics by maintainers sorted by fields:
");
print_by ("maint",$_) for (@fields);
print HTML ("
\n \n");
printf HTML ("
Statistics by source packages (sum) sorted by fields:
");
print_by ("source",$_) for (@fields);
print HTML ("
\n \n");
printf HTML ("
Statistics by source packages (max) sorted by fields:
");
print_by ("sourcemax",$_) for (@fields);
print HTML ("
\n \n");
printf HTML ("
Statistics for sections sorted by fields\n
\n");
for $dir ("main","contrib","non-free","non-US","unknown")
{
-d "$popcon/$dir" or next;
$dir !~ /^\./ or next;
if ($dir eq "unknown")
{
printf HTML ("%-16s : ",$dir);
}
else
{
printf HTML ("%-16s : ",$dir);
}
print_by ($dir,$_) for (@fields);
print HTML ("\n");
}
printf HTML ("
\n");
rawresults($numsub, $popfile);
print HTML <<'EOF';
Statistics per Debian architectures
EOF
for $f (grep { $_ ne 'unknown' } sort keys %arch)
{
my ($port)=split('-',$f);
$port="$port/";
$port="kfreebsd-gnu/" if ($port eq "kfreebsd/");
printf HTML "%-16s : %-10s graph\n",$f,$arch{$f};
}
if (defined $arch{"unknown"}) {
printf HTML "%-16s : %-10s graph\n","unknown",$arch{"unknown"}
}
print HTML "
\n";
print HTML <<'EOF';
EOF
print HTML <<'EOF';
Statistics per popularity-contest releases
EOF
for $f (grep { $_ ne 'unknown' } sort keys %release)
{
my($name) = $f;
$name = "$f ($popconver{$f})" if (defined($popconver{$f}));
printf HTML "%-25s : %-10s \n",$name,$release{$f};
}
if (defined $release{"unknown"}) {
printf HTML "%-25s : %-10s \n","unknown",$release{"unknown"};
}
print HTML "
\n";
print HTML <<'EOF';
EOF
print HTML <<'EOF';
Statistics per distributions reporting to Debian
EOF
for $f (grep { $_ ne 'unknown' } sort keys %vendor)
{
my($name) = $f;
printf HTML "%-25s : %-10s \n",$name,$vendor{$f};
}
if (defined $vendor{"unknown"}) {
printf HTML "%-25s : %-10s \n","unknown",$vendor{"unknown"};
}
print HTML "
\n";
print HTML <<'EOF';
EOF
printf HTML ("
");
htmlfooter;
close HTML;
}
sub read_packages
{
my ($file,$dist);
for $file ("slink","slink-nonUS","potato","potato-nonUS",
"woody","woody-nonUS", values %popconver)
{
open AVAIL, "<:utf8", "$file.sections" or die "Cannot open $file.sections";
while()
{
my ($p,$sec)=split(' ');
defined($sec) or last;
chomp $sec;
$sec =~ m{^(non-US|contrib|non-free)/} or $sec="main/$sec";
$section{$p}=$sec;
$maint{$p}="Not in sid";
$source{$p}="Not in sid";
}
close AVAIL;
}
mark "Reading legacy packages...";
for $dist ("stable", "testing", "unstable")
{
for (glob("$mirrorbase/dists/$dist/*/binary-*/Packages.xz"))
{
/([^[:space:]]+)/ or die("incorrect package name");
my $file = $1;#Untaint
open AVAIL, "-|:encoding(UTF-8)","xzcat $file";
my $p;
while()
{
/^Package: (.+)/ and do {$p=$1;$maint{$p}="bug";$source{$p}=$p;next;};
/^Version: (.+)/ && $p eq "popularity-contest"
and do { $popver{$dist}=$1; next;};
/^Maintainer: ([^()]+) (\(.+\) )*<.+>/
and do { $maint{$p}=join(' ',map{ucfirst($_)} split(' ',lc $1));next;};
/^Source: (\S+)/ and do { $source{$p}=$1;next;};
/^Section: (.+)/ or next;
my $sec = $1;
$sec =~ m{^(non-US|contrib|non-free)/} or $sec="main/$sec";
$section{$p}=$sec;
}
close AVAIL;
}
}
mark "Reading current packages...";
for $dist ("stable", "testing", "unstable")
{
my($v)=$popver{$dist};
$popconver{$v}=defined($popconver{$v})?"$popconver{$v}/$dist":$dist;
}
}
# Main code
read_packages();
mark "Reading packages...";
my %stat = ('all' => read_result('all'),
'stable' => read_result('stable'));
mark "Reading stats...";
for (keys %stat)
{
gen_sections($stat{$_});
}
mark "Building pages";