#!/usr/bin/perl # # postingstats.pl # # This script will create statistic postings from NewsStats output. # It defaults to statistics for de.* posted to de.admin.lists, but # defaults can be changed at ----- configuration -----. # # It is part of the NewsStats package. # # Copyright (c) 2010-2012, 2025 Thomas Hochstein # # It can be redistributed and/or modified under the same terms under # which Perl itself is published. # # Usage: # $~ groupstats.pl --nocomments --sums --format dump | postingstats.pl -t groups # $~ hoststats.pl --nocomments --sums --format dump | postingstats.pl -t hosts # $~ clientstats.pl --nocomments --sums --versions --format dump | postingstats.pl -t clients # BEGIN { use File::Basename; # we're in .../bin, so our module is in ../lib push(@INC, dirname($0).'/../lib'); } use strict; use warnings; use NewsStats qw(:DEFAULT LastMonth); use Getopt::Long qw(GetOptions); Getopt::Long::config ('bundling'); use constant TABLEWIDTH => 28; # width of table without newsgroup name ##### ----- pre-config ----------------------------------------------- ### read commandline options my ($Month, $Type); GetOptions ('m|month=s' => \$Month, 't|type=s' => \$Type, 'h|help' => \&ShowPOD, 'V|version' => \&ShowVersion) or exit 1; $Month = &LastMonth if !$Month; if ($Month !~ /^\d{4}-\d{2}$/) { $Month = &LastMonth; &Bleat(1,"--month option has an invalid format - set to $Month."); }; # parse $Type if (!$Type) { # default $Type = 'GroupStats'; } elsif ($Type =~ /(news)?groups?/i) { $Type = 'GroupStats'; } elsif ($Type =~ /(host|server)s?/i) { $Type = 'HostStats'; } elsif ($Type =~ /(client|reader)s?/i) { $Type = 'ClientStats'; }; my $Timestamp = time; ##### ----- configuration -------------------------------------------- my $TLH = 'de'; my %Heading = ('GroupStats' => 'Postingstatistik fuer de.* im Monat '.$Month, 'HostStats' => 'Serverstatistik fuer de.* im Monat '.$Month, 'ClientStats' => 'Newsreaderstatistik fuer de.* im Monat '.$Month ); my %TH = ('counter' => 'Nr.', 'value' => 'Anzahl', 'percentage' => 'Prozent' ); my %LeadIn = ('GroupStats' => < < < Newsgroups: local.test Subject: Postingstatistik fuer de.* im Monat $Month Message-ID: Approved: thh\@thh.name Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit User-Agent: postingstats.pl/$VERSION (NewsStats) GROUPSIN From: Thomas Hochstein Newsgroups: local.test Subject: Serverstatistik fuer de.* im Monat $Month Message-ID: Approved: thh\@thh.name Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit User-Agent: postingstats.pl/$VERSION (NewsStats) HOSTSIN From: Thomas Hochstein Newsgroups: local.test Subject: Newsreaderstatistik fuer de.* im Monat $Month Message-ID: Approved: thh\@thh.name Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit User-Agent: postingstats.pl/$VERSION (NewsStats) CLIENTSIN my %LeadOut = ('GroupStats' => < < < zur Verfuegung. GROUPSOUT Alle Zahlen wurden ermittelt auf einem Newsserver mit redundanter Anbin- dung fuer de.* unter Anwendung ueblicher Filtermassnahmen. Steuernach- richten werden nicht erfasst; Postings, die supersedet oder gecancelt wurden, bleiben erfasst, sofern sie das System ueberhaupt (und vor der Loeschnachricht) erreicht haben. HOSTSOUT Alle Zahlen wurden ermittelt auf einem Newsserver mit redundanter Anbin- dung fuer de.* unter Anwendung ueblicher Filtermassnahmen. Steuernach- richten werden nicht erfasst; Postings, die supersedet oder gecancelt wurden, bleiben erfasst, sofern sie das System ueberhaupt (und vor der Loeschnachricht) erreicht haben. Versionsangaben werden nur gezaehlt, wenn Sie ermittelbar sind; daher kann die Summe der Newsreader-Versionen kleiner sein als die Postingzahl fuer den Newsreader. Ausserdem koennen an einem Beitrag mehrere Clients beteiligt sein, bspw. der Newsreader und ein lokaler Server wie der Hamster. Daher kann die Summe aller Newsreader groesser sein als die Summe der Postings; auch ergeben die Prozentzahlen dementsprechend in der Summe mehr als 100%. CLIENTSOUT ##### ----- subroutines ---------------------------------------------- sub Percentage { # calculate percentage rate from base value and percentage my ($Base,$Percentage) = @_; return ($Percentage * 100 / $Base); } sub Divider { # build a divider line of $Symbol as wide as the table is my ($Symbol,$MaxLength) = @_; return ':' . $Symbol x ($MaxLength+TABLEWIDTH) . ":\n"; } sub SingleVersion { my ($LastName,$RSubValue,$RValue,$RMaxLength) = @_; # get version to add to client name my ($Version) = keys %{$$RSubValue{$LastName}}; $Version =~ s/^- //; # add version to client name by creating a new name # and deleting the old one my ($NameVersion) = $LastName . ' ' . $Version; $$RValue{$NameVersion} = $$RValue{$LastName}; delete($$RValue{$LastName}); $$RMaxLength = length($NameVersion) if length($NameVersion) > $$RMaxLength; # delete single version delete($$RSubValue{$LastName}); } ##### ----- main loop ------------------------------------------------ my (%Value, %SubValue, $SubCounter, $LastName, $SumName, $SumTotal, $MaxLength); if ($Type eq 'GroupStats') { $SumName = "$TLH.ALL"; $TH{'name'} = 'Newsgroup' } elsif ($Type eq 'HostStats') { $SumName = 'ALL'; $TH{'name'} = 'Postingserver' } elsif ($Type eq 'ClientStats') { $SumName = 'ALL'; $TH{'name'} = 'Newsreader / Client' } ### read from STDIN $MaxLength = 0; while(<>) { my ($Name, $Value) = $_ =~ /(.+) (\d+)$/; $SumTotal = $Value if $Name eq $SumName; next if $Name =~ /ALL$/; # handle client versions if ($Type eq 'ClientStats' and $Name =~ /^- /) { $SubValue{$LastName}{$Name} = $Value; $SubCounter++; } else { # clients with just one version &SingleVersion($LastName,\%SubValue,\%Value,\$MaxLength) if ($LastName && $SubCounter == 1); # reset version counter and client name $SubCounter = 0; $LastName = $Name; $Value{$Name} = $Value; $MaxLength = length($Name) if length($Name) > $MaxLength; } } # clients with just one version (last iteration) &SingleVersion($LastName,\%SubValue,\%Value,\$MaxLength) if ($LastName && $SubCounter == 1); ### print to STDOUT # calculate padding for $Heading my $PaddingLeft = ' ' x int((($MaxLength+TABLEWIDTH-2-length($Heading{$Type}))/2)); my $PaddingRight = $PaddingLeft; $PaddingLeft .= ' ' if (length($Heading{$Type}) + (length($PaddingLeft) * 2) +2 < $MaxLength+TABLEWIDTH); print $LeadIn{$Type}; # print table header print &Divider('=',$MaxLength); printf(": %s%s%s :\n",$PaddingLeft,$Heading{$Type},$PaddingRight); print &Divider('=',$MaxLength); printf(": %-3s : %-6s : %-7s : %-*s :\n", substr($TH{'counter'},0,3), substr($TH{'value'},0,6), substr($TH{'percentage'},0,7), $MaxLength,$TH{'name'}); print &Divider('-',$MaxLength); # print table my $Counter = 0; foreach my $Name (sort { $Value{$b} <=> $Value{$a} } keys %Value) { $Counter++; printf(": %3u. : %6u : %6.2f%% : %-*s :\n", $Counter,$Value{$Name},&Percentage($SumTotal,$Value{$Name}), $MaxLength,$Name); # handle client versions if ($SubValue{$Name}) { foreach my $SubName (sort { $SubValue{$Name}{$b} <=> $SubValue{$Name}{$a} } keys %{$SubValue{$Name}}) { printf(": : %6u : %6.2f%% : %-*s :\n", $SubValue{$Name}{$SubName}, &Percentage($SumTotal,$SubValue{$Name}{$SubName}), $MaxLength,$SubName); } } } # print table footer print &Divider('-',$MaxLength); printf(": : %6u : %s : %-*s :\n",$SumTotal,'100.00%',$MaxLength,''); print &Divider('=',$MaxLength); print $LeadOut{$Type}; __END__ ################################ Documentation ################################# =head1 NAME postingstats - format and post reports =head1 SYNOPSIS B B<-t> I [B<-Vh> [B<-m> I] =head1 REQUIREMENTS See L. =head1 DESCRIPTION This script will re-format reports on newsgroup usage created by B, B or B and create a message that can be posted to Usenet. =head2 Features and options B will create a table with entries numbered from most to least and percentages calculated from the sum total of all values. It depends on a sorted list on STDIN in I format with I. B needs a B<--type> and a B<--month> to create a caption and select matching lead-ins and lead-outs. B<--type> is also needed to catch the correct sum total from input. It will default to posting statistics (number of postings per group) and last month. Output from B can be piped to any C implementation, e.g. C from L (present in C). =head2 Configuration Configuration is done by changing the code in the C<----- configuration -----> section. =over 3 =item C<$TLH> Top level hierarchy the report was created for. Used for display and sum total. =item C<%Heading> Hash with keys for I, I and I. Used to display a heading. =item C<%TH> Hash with keys for I, I and I. Used to create the table header for I, I and I. I must not be longer than 3 characters, I no longer than 6 characters and I no longer than 7 characters. Output will be truncated otherwise. =item C<%LeadIn> Hash with keys for I, I and I. Used to create the headers for our posting. Can contain other text that will be shown before C<%Heading>. =item C<%LeadOut> Hash with keys for I, I and I. Will be shown at the end of our posting. =back =head1 OPTIONS =over 3 =item B<-V>, B<--version> Print out version and copyright information and exit. =item B<-h>, B<--help> Print this man page and exit. =item B<-t>, B<--type> I Set report type to posting statistics, hosts statistics or client statistics accordingly. =item B<-m>, B<--month> I Set month for display. =back =head1 INSTALLATION See L. =head1 USAGE Create a posting from a posting statistics report for last month: groupstats.pl --nocomments --sums --format dump | postingstats.pl -t groups Create a posting from a posting statistics report for 2012-01: groupstats.pl --nocomments --sums --format dump | postingstats.pl -t groups -m 2012-01 Create a posting from a host statistics report for last month: hoststats.pl --nocomments --sums --format dump | postingstats.pl -t hosts Create a posting from a client statistics report for last month: clientstats.pl --nocomments --sums --versions --format dump | postingstats.pl -t clients =head1 FILES =over 4 =item F The script itself. =item F Library functions for the NewsStats package. =item F Runtime configuration file. =back =head1 BUGS Please report any bugs or feature requests to the author or use the bug tracker at L! =head1 SEE ALSO =over 2 =item - L =item - L =item - groupstats -h =item - hoststats -h =item - clientstats -h =back This script is part of the B package. =head1 AUTHOR Thomas Hochstein =head1 COPYRIGHT AND LICENSE Copyright (c) 2010-2012, 2025 Thomas Hochstein This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself. =cut