Refactor postingstats.

- Make all text configurable (i18n).
- Generalize to make it usable for HostStats.
- Fallback to last month if no month is given.
- Add option handling, import VERSION, add POD.

Signed-off-by: Thomas Hochstein <thh@thh.name>
This commit is contained in:
Thomas Hochstein 2025-05-11 14:31:53 +02:00
parent 83d4da5e30
commit 29e9784048

View file

@ -2,88 +2,93 @@
#
# postingstats.pl
#
# This script will create a posting statistic for de.admin.lists
# from NewsStats output.
# 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 Thomas Hochstein <thh@inter.net>
# Copyright (c) 2010-2012, 2025 Thomas Hochstein <thh@thh.name>
#
# It can be redistributed and/or modified under the same terms under
# which Perl itself is published.
#
# $~ groupstats.pl --nocomments --sums --format dump | postingstats.pl xxxx-xx
# Usage:
# $~ groupstats.pl --nocomments --sums --format dump | postingstats.pl -t groups
# $~ cliservstats.pl -t server --nocomments --sums --format dump | postingstats.pl -t hosts
#
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
our $VERSION = '0.01';
##### ----- 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';
};
my $Timestamp = time;
##### ----- 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";
}
##### ----- main loop -----
# get $month from ARGV
my $month = shift;
# read from STDIN
my (%postings, $hierarchy, $maxlength);
while(<>) {
my ($group, $postings) = split;
$hierarchy = $postings if $group eq 'de.ALL';
next if $group =~ /ALL$/;
$postings{$group} = $postings;
$maxlength = length($group) if length($group) > $maxlength;
}
# print to STDOUT
my $heading = ' Postingstatistik fuer de.* im Monat '.$month;
my $padding = ' ' x (($maxlength+TABLEWIDTH-2-length($heading))/2);
my $timestamp = time;
my $counter = 0;
print <<HEADER;
From: Thomas Hochstein <thh\@inter.net>
Newsgroups: de.admin.news.misc
Subject: Postingstatistik fuer de.* im Monat $month
Message-ID: <destat-postings-$month.$timestamp\@mid.news.szaf.org>
##### ----- configuration --------------------------------------------
my $TLH = 'de';
my %Heading = ('GroupStats' => 'Postingstatistik fuer de.* im Monat '.$Month,
'HostStats' => 'Serverstatistik fuer de.* im Monat '.$Month
);
my %TH = ('counter' => 'Nr.',
'value' => 'Anzahl',
'percentage' => 'Prozent'
);
my %LeadIn = ('GroupStats' => <<GROUPSIN, 'HostStats' => <<HOSTSIN);
From: Thomas Hochstein <thh\@thh.name>
Newsgroups: local.test
Subject: Postingstatistik fuer de.* im Monat $Month
Message-ID: <destat-postings-$Month.$Timestamp\@mid.news.szaf.org>
Approved: thh\@thh.name
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
User-Agent: postingstats.pl/$VERSION (NewsStats)
HEADER
GROUPSIN
From: Thomas Hochstein <thh\@thh.name>
Newsgroups: local.test
Subject: Serverstatistik fuer de.* im Monat $Month
Message-ID: <destat-hosts-$Month.$Timestamp\@mid.news.szaf.org>
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)
print &divider('=',$maxlength);
printf(": %s%s%s :\n",$padding,$heading,$padding);
print &divider('=',$maxlength);
printf(": Nr. : Anzahl : Prozent : %-*s :\n",$maxlength,'Newsgroup');
print &divider('-',$maxlength);
foreach my $group (sort { $postings{$b} <=> $postings {$a}} keys %postings) {
$counter++;
printf(": %3u. : %6u : %6.2f%% : %-*s :\n",$counter,$postings{$group},&percentage($hierarchy,$postings{$group}),$maxlength,$group);
}
print &divider('-',$maxlength);
printf(": : %6u : %s : %-*s :\n",$hierarchy,'100.00%',$maxlength,'de.*');
print &divider('=',$maxlength);
print <<LEADOUT;
HOSTSIN
my %LeadOut = ('GroupStats' => <<GROUPSOUT, 'HostStats' => <<HOSTSOUT);
Alle Zahlen wurden ermittelt auf einem Newsserver mit redundanter Anbin-
dung fuer de.* unter Anwendung ueblicher Filtermassnahmen. Steuernach-
@ -96,4 +101,253 @@ Summe der Postinganzahlen der Einzelgruppen.
Die Daten stehen graphisch aufbereitet unter <http://usenet.dex.de/> zur
Verfuegung.
LEADOUT
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
##### ----- 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";
}
##### ----- main loop ------------------------------------------------
my (%Value, $SumName, $SumTotal, $MaxLength);
$MaxLength = 0;
if ($Type eq 'GroupStats') {
$SumName = "$TLH.ALL";
$TH{'name'} = 'Newsgroup'
} elsif ($Type eq 'HostStats') {
$SumName = 'ALL';
$TH{'name'} = 'Server'
}
# read from STDIN
while(<>) {
my ($Name, $Value) = split;
$SumTotal = $Value if $Name eq $SumName;
next if $Name =~ /ALL$/;
$Value{$Name} = $Value;
$MaxLength = length($Name) if length($Name) > $MaxLength;
}
# print to STDOUT
my $PaddingLeft = ' ' x int((($MaxLength+TABLEWIDTH-2-length($Heading{$Type}))/2));
my $PaddingRight = $PaddingLeft;
$PaddingLeft .= ' ' if (length($Heading{$Type}) + (length($PaddingLeft) * 2) < $MaxLength+TABLEWIDTH);
my $Counter = 0;
print $LeadIn{$Type};
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);
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);
}
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<postingstats> B<-t> I<groups|hosts> [B<-Vh> [B<-m> I<YYYY-MM>]
=head1 REQUIREMENTS
See L<doc/README>.
=head1 DESCRIPTION
This script will re-format reports on newsgroup usage created by
B<groupstats.pl> or B<cliservstats.pl> and create a message that can
be posted to Usenet.
=head2 Features and options
B<postingstats> 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<dump> format with I<sums>.
B<postingstats> 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<postingstats> can be piped to any C<inews> implementation,
e.g. C<tinews.pl> from L<ftp://ftp.tin.org/pub/news/clients/tin/tools/tinews.pl>.
=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<GroupStats> and I<HostStats>. Used to display a
heading.
=item C<%TH>
Hash with keys for I<counter>, I<value> and I<percentage>. Used to
create the table header for I<number>, I<quantity> and I<percentage>.
I<counter> must not be longer than 3 characters, I<value> no longer
than 6 characters and I<percentage> no longer than 7 characters.
Output will be truncated otherwise.
=item C<%LeadIn>
Hash with keys for I<GroupStats> and I<HostStats>. 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<GroupStats> and I<HostStats>. 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<groups|hosts>
Set report type to posting statistics or hosts statistics accordingly.
=item B<-m>, B<--month> I<YYYY-MM>
Set month for display.
=back
=head1 INSTALLATION
See L<doc/INSTALL>.
=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:
cliservstats.pl -t server --nocomments --sums --format dump | postingstats.pl -t hosts
=head1 FILES
=over 4
=item F<bin/postingstats.pl>
The script itself.
=item F<lib/NewsStats.pm>
Library functions for the NewsStats package.
=item F<etc/newsstats.conf>
Runtime configuration file.
=back
=head1 BUGS
Please report any bugs or feature requests to the author or use the
bug tracker at L<https://code.virtcomm.de/thh/newsstats/issues>!
=head1 SEE ALSO
=over 2
=item -
L<doc/README>
=item -
l>doc/INSTALL>
=item -
groupstats -h
=item -
cliservstats -h
=back
This script is part of the B<NewsStats> package.
=head1 AUTHOR
Thomas Hochstein <thh@thh.name>
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2010-2012, 2025 Thomas Hochstein <thh@thh.name>
This program is free software; you may redistribute it and/or modify it
under the same terms as Perl itself.
=cut