- Fix clientstats doc (copied from hoststats). - Add some more examples ro README. Signed-off-by: Thomas Hochstein <thh@thh.name>
450 lines
13 KiB
Perl
Executable file
450 lines
13 KiB
Perl
Executable file
#!/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 <thh@thh.name>
|
|
#
|
|
# 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' => <<GROUPSIN, 'HostStats' => <<HOSTSIN, 'ClientStats' => <<CLIENTSIN);
|
|
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=us-ascii
|
|
Content-Transfer-Encoding: 7bit
|
|
User-Agent: postingstats.pl/$VERSION (NewsStats)
|
|
|
|
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)
|
|
|
|
HOSTSIN
|
|
From: Thomas Hochstein <thh\@thh.name>
|
|
Newsgroups: local.test
|
|
Subject: Newsreaderstatistik fuer de.* im Monat $Month
|
|
Message-ID: <destat-clients-$Month.$Timestamp\@mid.news.szaf.org>
|
|
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' => <<GROUPSOUT, 'HostStats' => <<HOSTSOUT, 'ClientStats' => <<CLIENTSOUT);
|
|
|
|
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. Crosspostings werden in jeder Gruppe,
|
|
in die sie gerichtet sind, gezaehlt, aber bei Ermittlung der Summe be-
|
|
reinigt; daher ist die Postinganzahl fuer de.* gesamt niedriger als die
|
|
Summe der Postinganzahlen der Einzelgruppen.
|
|
|
|
Die Daten stehen graphisch aufbereitet unter <http://usenet.dex.de/> 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<postingstats> [B<-Vh>] [B<-t> I<groups|hosts|clients>] [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>, B<hoststats.pl> or B<clientstats.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>;
|
|
I<versions> from B<clientstas.pl> are optional.
|
|
|
|
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 which differs between I<groups>
|
|
on one hand and I<hosts> or I<clients> on the other hand.
|
|
|
|
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>
|
|
(present in C</contrib/>).
|
|
|
|
=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 (only for I<groups>).
|
|
|
|
=item C<%Heading>
|
|
|
|
Hash with keys for I<GroupStats>, I<HostStats> and I<ClientStats>.
|
|
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>, I<HostStats> and I<ClientStats>.
|
|
Used to create the headers for the postings. Can contain other text
|
|
that will be shown before C<%Heading>.
|
|
|
|
=item C<%LeadOut>
|
|
|
|
Hash with keys for I<GroupStats>, I<HostStats> and I<ClientStats>.
|
|
Will be shown at the end of the posting.
|
|
|
|
=back
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 3
|
|
|
|
=item B<-V>, B<--version>
|
|
|
|
Display version and copyright information and exit.
|
|
|
|
=item B<-h>, B<--help>
|
|
|
|
Display this man page and exit.
|
|
|
|
=item B<-t>, B<--type> I<groups|hosts|clients>
|
|
|
|
Set report type to posting statistics, hosts statistics or client
|
|
statistics accordingly.
|
|
|
|
=item B<-m>, B<--month> I<YYYY-MM>
|
|
|
|
Set month (for display only).
|
|
|
|
=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 -m 2012-01 | 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<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 -
|
|
|
|
hoststats -h
|
|
|
|
=item -
|
|
|
|
clientstats -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
|