Fix ea91003a99e679caa5bf56915f085a7c4931805b.
[usenet/newsstats.git] / bin / groupstats.pl
... / ...
CommitLineData
1#! /usr/bin/perl
2#
3# groupstats.pl
4#
5# This script will get statistical data on newgroup usage
6# from a database.
7#
8# It is part of the NewsStats package.
9#
10# Copyright (c) 2010-2013 Thomas Hochstein <thh@inter.net>
11#
12# It can be redistributed and/or modified under the same terms under
13# which Perl itself is published.
14
15BEGIN {
16 our $VERSION = "0.01";
17 use File::Basename;
18 # we're in .../bin, so our module is in ../lib
19 push(@INC, dirname($0).'/../lib');
20}
21use strict;
22use warnings;
23
24use NewsStats qw(:DEFAULT :TimePeriods :Output :SQLHelper ReadGroupList);
25
26use DBI;
27use Getopt::Long qw(GetOptions);
28Getopt::Long::config ('bundling');
29
30################################# Main program #################################
31
32### read commandline options
33my ($OptBoundType,$OptCaptions,$OptCheckgroupsFile,$OptComments,
34 $OptFileTemplate,$OptFormat,$OptGroupBy,$OptGroupsDB,$LowBound,$OptMonth,
35 $OptNewsgroups,$OptOrderBy,$OptReportType,$OptSums,$UppBound,$OptConfFile);
36GetOptions ('b|boundary=s' => \$OptBoundType,
37 'c|captions!' => \$OptCaptions,
38 'checkgroups=s' => \$OptCheckgroupsFile,
39 'comments!' => \$OptComments,
40 'filetemplate=s' => \$OptFileTemplate,
41 'f|format=s' => \$OptFormat,
42 'g|group-by=s' => \$OptGroupBy,
43 'groupsdb=s' => \$OptGroupsDB,
44 'l|lower=i' => \$LowBound,
45 'm|month=s' => \$OptMonth,
46 'n|newsgroups=s' => \$OptNewsgroups,
47 'o|order-by=s' => \$OptOrderBy,
48 'r|report=s' => \$OptReportType,
49 's|sums!' => \$OptSums,
50 'u|upper=i' => \$UppBound,
51 'conffile=s' => \$OptConfFile,
52 'h|help' => \&ShowPOD,
53 'V|version' => \&ShowVersion) or exit 1;
54# parse parameters
55# $OptComments defaults to TRUE
56$OptComments = 1 if (!defined($OptComments));
57# force --nocomments when --filetemplate is used
58$OptComments = 0 if ($OptFileTemplate);
59# parse $OptBoundType
60if ($OptBoundType) {
61 if ($OptBoundType =~ /level/i) {
62 $OptBoundType = 'level';
63 } elsif ($OptBoundType =~ /av(era)?ge?/i) {
64 $OptBoundType = 'average';
65 } elsif ($OptBoundType =~ /sums?/i) {
66 $OptBoundType = 'sum';
67 } else {
68 $OptBoundType = 'default';
69 }
70}
71# parse $OptReportType
72if ($OptReportType) {
73 if ($OptReportType =~ /av(era)?ge?/i) {
74 $OptReportType = 'average';
75 } elsif ($OptReportType =~ /sums?/i) {
76 $OptReportType = 'sum';
77 } else {
78 $OptReportType = 'default';
79 }
80}
81# honor $OptCheckgroupsFile,
82# warn for $OptSums if set concurrently
83my $ValidGroups;
84if ($OptCheckgroupsFile) {
85 # read list of newsgroups from --checkgroups
86 # into a hash reference
87 $ValidGroups = &ReadGroupList($OptCheckgroupsFile);
88 &Bleat(1,"--sums option can't possibly work with --checkgroups option set")
89 if $OptSums;
90}
91
92### read configuration
93my %Conf = %{ReadConfig($OptConfFile)};
94
95### override configuration via commandline options
96my %ConfOverride;
97$ConfOverride{'DBTableGrps'} = $OptGroupsDB if $OptGroupsDB;
98&OverrideConfig(\%Conf,\%ConfOverride);
99
100### init database
101my $DBHandle = InitDB(\%Conf,1);
102
103### get time period and newsgroups, prepare SQL 'WHERE' clause
104# get time period
105# and set caption for output and expression for SQL 'WHERE' clause
106my ($CaptionPeriod,$SQLWherePeriod) = &GetTimePeriod($OptMonth);
107# bail out if --month is invalid
108&Bleat(2,"--month option has an invalid format - ".
109 "please use 'YYYY-MM', 'YYYY-MM:YYYY-MM' or 'ALL'!") if !$CaptionPeriod;
110# get list of newsgroups and set expression for SQL 'WHERE' clause
111# with placeholders as well as a list of newsgroup to bind to them
112my ($SQLWhereNewsgroups,@SQLBindNewsgroups);
113if ($OptNewsgroups) {
114 ($SQLWhereNewsgroups,@SQLBindNewsgroups) = &SQLGroupList($OptNewsgroups);
115 # bail out if --newsgroups is invalid
116 &Bleat(2,"--newsgroups option has an invalid format!")
117 if !$SQLWhereNewsgroups;
118}
119
120### build SQL WHERE clause (and HAVING clause, if needed)
121my ($SQLWhereClause,$SQLHavingClause);
122# $OptBoundType 'level'
123if ($OptBoundType and $OptBoundType ne 'default') {
124 $SQLWhereClause = SQLBuildClause('where',$SQLWherePeriod,
125 $SQLWhereNewsgroups,&SQLHierarchies($OptSums));
126 $SQLHavingClause = SQLBuildClause('having',&SQLSetBounds($OptBoundType,
127 $LowBound,$UppBound));
128# $OptBoundType 'threshold' / 'default' or none
129} else {
130 $SQLWhereClause = SQLBuildClause('where',$SQLWherePeriod,
131 $SQLWhereNewsgroups,&SQLHierarchies($OptSums),
132 &SQLSetBounds('default',$LowBound,$UppBound));
133}
134
135### get sort order and build SQL 'ORDER BY' clause
136# force to 'month' for $OptReportType 'average' or 'sum'
137$OptGroupBy = 'month' if ($OptReportType and $OptReportType ne 'default');
138# default to 'newsgroup' for $OptBoundType 'level' or 'average'
139$OptGroupBy = 'newsgroup' if (!$OptGroupBy and
140 $OptBoundType and $OptBoundType ne 'default');
141# default to 'newsgroup' if $OptGroupBy is not set and
142# just one newsgroup is requested, but more than one month
143$OptGroupBy = 'newsgroup' if (!$OptGroupBy and $OptMonth and $OptMonth =~ /:/
144 and $OptNewsgroups and $OptNewsgroups !~ /[:*%]/);
145# parse $OptGroupBy to $GroupBy, create ORDER BY clause $SQLOrderClause
146# if $OptGroupBy is still not set, SQLSortOrder() will default to 'month'
147my ($GroupBy,$SQLOrderClause) = SQLSortOrder($OptGroupBy, $OptOrderBy);
148# $GroupBy will contain 'month' or 'newsgroup' (parsed result of $OptGroupBy)
149# set it to 'month' or 'key' for OutputData()
150$GroupBy = ($GroupBy eq 'month') ? 'month' : 'key';
151
152### get report type and build SQL 'SELECT' query
153my $SQLSelect;
154my $SQLGroupClause = '';
155my $Precision = 0; # number of digits right of decimal point for output
156if ($OptReportType and $OptReportType ne 'default') {
157 $SQLGroupClause = 'GROUP BY newsgroup';
158 # change $SQLOrderClause: replace everything before 'postings'
159 $SQLOrderClause =~ s/BY.+postings/BY postings/;
160 if ($OptReportType eq 'average') {
161 $SQLSelect = "'All months',newsgroup,AVG(postings)";
162 $Precision = 2;
163 # change $SQLOrderClause: replace 'postings' with 'AVG(postings)'
164 $SQLOrderClause =~ s/postings/AVG(postings)/;
165 } elsif ($OptReportType eq 'sum') {
166 $SQLSelect = "'All months',newsgroup,SUM(postings)";
167 # change $SQLOrderClause: replace 'postings' with 'SUM(postings)'
168 $SQLOrderClause =~ s/postings/SUM(postings)/;
169 }
170 } else {
171 $SQLSelect = 'month,newsgroup,postings';
172};
173
174### get length of longest newsgroup name delivered by query
175### for formatting purposes
176my $Field = ($GroupBy eq 'month') ? 'newsgroup' : 'month';
177my ($MaxLength,$MaxValLength) = &GetMaxLength($DBHandle,$Conf{'DBTableGrps'},
178 $Field,'postings',$SQLWhereClause,
179 $SQLHavingClause,
180 @SQLBindNewsgroups);
181
182### build and execute SQL query
183my ($DBQuery);
184# special query preparation for $OptBoundType 'level', 'average' or 'sums'
185if ($OptBoundType and $OptBoundType ne 'default') {
186 # prepare and execute first query:
187 # get list of newsgroups meeting level conditions
188 $DBQuery = $DBHandle->prepare(sprintf('SELECT newsgroup FROM %s.%s %s '.
189 'GROUP BY newsgroup %s',
190 $Conf{'DBDatabase'},$Conf{'DBTableGrps'},
191 $SQLWhereClause,$SQLHavingClause));
192 $DBQuery->execute(@SQLBindNewsgroups)
193 or &Bleat(2,sprintf("Can't get groups data for %s from %s.%s: %s\n",
194 $CaptionPeriod,$Conf{'DBDatabase'},$Conf{'DBTableGrps'},
195 $DBI::errstr));
196 # add newsgroups to a comma-seperated list ready for IN(...) query
197 my $GroupList;
198 while (my ($Newsgroup) = $DBQuery->fetchrow_array) {
199 $GroupList .= ',' if $GroupList;
200 $GroupList .= "'$Newsgroup'";
201 };
202 # enhance $WhereClause
203 if ($GroupList) {
204 $SQLWhereClause = SQLBuildClause('where',$SQLWhereClause,
205 sprintf('newsgroup IN (%s)',$GroupList));
206 } else {
207 # condition cannot be satisfied;
208 # force query to fail by adding '0=1'
209 $SQLWhereClause = SQLBuildClause('where',$SQLWhereClause,'0=1');
210 }
211}
212
213# prepare query
214$DBQuery = $DBHandle->prepare(sprintf('SELECT %s FROM %s.%s %s %s %s',
215 $SQLSelect,
216 $Conf{'DBDatabase'},$Conf{'DBTableGrps'},
217 $SQLWhereClause,$SQLGroupClause,
218 $SQLOrderClause));
219
220# execute query
221$DBQuery->execute(@SQLBindNewsgroups)
222 or &Bleat(2,sprintf("Can't get groups data for %s from %s.%s: %s\n",
223 $CaptionPeriod,$Conf{'DBDatabase'},$Conf{'DBTableGrps'},
224 $DBI::errstr));
225
226### output results
227# set default to 'pretty'
228$OptFormat = 'pretty' if !$OptFormat;
229# print captions if --caption is set
230if ($OptCaptions && $OptComments) {
231 # print time period with report type
232 my $CaptionReportType= '(number of postings for each month)';
233 if ($OptReportType and $OptReportType ne 'default') {
234 $CaptionReportType= '(average number of postings for each month)'
235 if $OptReportType eq 'average';
236 $CaptionReportType= '(number of all postings for that time period)'
237 if $OptReportType eq 'sum';
238 }
239 printf("# ----- Report for %s %s\n",$CaptionPeriod,$CaptionReportType);
240 # print newsgroup list if --newsgroups is set
241 printf("# ----- Newsgroups: %s\n",join(',',split(/:/,$OptNewsgroups)))
242 if $OptNewsgroups;
243 # print boundaries, if set
244 my $CaptionBoundary= '(counting only month fulfilling this condition)';
245 if ($OptBoundType and $OptBoundType ne 'default') {
246 $CaptionBoundary= '(every single month)' if $OptBoundType eq 'level';
247 $CaptionBoundary= '(on average)' if $OptBoundType eq 'average';
248 $CaptionBoundary= '(all month summed up)' if $OptBoundType eq 'sum';
249 }
250 printf("# ----- Threshold: %s %s x %s %s %s\n",
251 $LowBound ? $LowBound : '',$LowBound ? '=>' : '',
252 $UppBound ? '<=' : '',$UppBound ? $UppBound : '',$CaptionBoundary)
253 if ($LowBound or $UppBound);
254 # print primary and secondary sort order
255 printf("# ----- Grouped by %s (%s), sorted %s%s\n",
256 ($GroupBy eq 'month') ? 'Months' : 'Newsgroups',
257 ($OptGroupBy and $OptGroupBy =~ /-?desc$/i) ? 'descending' : 'ascending',
258 ($OptOrderBy and $OptOrderBy =~ /posting/i) ? 'by number of postings ' : '',
259 ($OptOrderBy and $OptOrderBy =~ /-?desc$/i) ? 'descending' : 'ascending');
260}
261
262# output data
263&OutputData($OptFormat,$OptComments,$GroupBy,$Precision,
264 $OptCheckgroupsFile ? $ValidGroups : '',
265 $OptFileTemplate,$DBQuery,$MaxLength,$MaxValLength);
266
267### close handles
268$DBHandle->disconnect;
269
270__END__
271
272################################ Documentation #################################
273
274=head1 NAME
275
276groupstats - create reports on newsgroup usage
277
278=head1 SYNOPSIS
279
280B<groupstats> [B<-Vhcs> B<--comments>] [B<-m> I<YYYY-MM>[:I<YYYY-MM>] | I<all>] [B<-n> I<newsgroup(s)>] [B<--checkgroups> I<checkgroups file>] [B<-r> I<report type>] [B<-l> I<lower boundary>] [B<-u> I<upper boundary>] [B<-b> I<boundary type>] [B<-g> I<group by>] [B<-o> I<order by>] [B<-f> I<output format>] [B<--filetemplate> I<filename template>] [B<--groupsdb> I<database table>] [--conffile I<filename>]
281
282=head1 REQUIREMENTS
283
284See L<doc/README>.
285
286=head1 DESCRIPTION
287
288This script create reports on newsgroup usage (number of postings per
289group per month) taken from result tables created by
290B<gatherstats.pl>.
291
292=head2 Features and options
293
294=head3 Time period and newsgroups
295
296The time period to act on defaults to last month; you can assign another
297time period or a single month (or drop all time constraints) via the
298B<--month> option (see below).
299
300B<groupstats> will process all newsgroups by default; you can limit
301processing to only some newsgroups by supplying a list of those groups via
302B<--newsgroups> option (see below). You can include hierarchy levels in
303the output by adding the B<--sums> switch (see below). Optionally
304newsgroups not present in a checkgroups file can be excluded from output,
305sse B<--checkgroups> below.
306
307=head3 Report type
308
309You can choose between different B<--report> types: postings per month,
310average postings per month or all postings summed up; for details, see
311below.
312
313=head3 Upper and lower boundaries
314
315Furthermore you can set an upper and/or lower boundary to exclude some
316results from output via the B<--lower> and B<--upper> options,
317respectively. By default, all newsgroups with more and/or less postings
318per month will be excluded from the result set (i.e. not shown and not
319considered for average and sum reports). You can change the meaning of
320those boundaries with the B<--boundary> option. For details, please see
321below.
322
323=head3 Sorting and formatting the output
324
325By default, all results are grouped by month; you can group results by
326newsgroup instead via the B<--groupy-by> option. Within those groups, the
327list of newsgroups (or months) is sorted alphabetically (or
328chronologically, respectively) ascending. You can change that order (and
329sort by number of postings) with the B<--order-by> option. For details and
330exceptions, please see below.
331
332The results will be formatted as a kind of table; you can change the
333output format to a simple list or just a list of newsgroups and number of
334postings with the B<--format> option. Captions will be added by means of
335the B<--caption> option; all comments (and captions) can be supressed by
336using B<--nocomments>.
337
338Last but not least you can redirect all output to a number of files, e.g.
339one for each month, by submitting the B<--filetemplate> option, see below.
340Captions and comments are automatically disabled in this case.
341
342=head2 Configuration
343
344B<groupstats> will read its configuration from F<newsstats.conf>
345which should be present in the same directory via Config::Auto.
346
347See doc/INSTALL for an overview of possible configuration options.
348
349You can override some configuration options via the B<--groupsdb> option.
350
351=head1 OPTIONS
352
353=over 3
354
355=item B<-V>, B<--version>
356
357Print out version and copyright information and exit.
358
359=item B<-h>, B<--help>
360
361Print this man page and exit.
362
363=item B<-m>, B<--month> I<YYYY-MM[:YYYY-MM]|all>
364
365Set processing period to a single month in YYYY-MM format or to a time
366period between two month in YYYY-MM:YYYY-MM format (two month, separated
367by a colon). By using the keyword I<all> instead, you can set no
368processing period to process the whole database.
369
370=item B<-n>, B<--newsgroups> I<newsgroup(s)>
371
372Limit processing to a certain set of newsgroups. I<newsgroup(s)> can
373be a single newsgroup name (de.alt.test), a newsgroup hierarchy
374(de.alt.*) or a list of either of these, separated by colons, for
375example
376
377 de.test:de.alt.test:de.newusers.*
378
379=item B<-s>, B<--sums|--nosums> (sum per hierarchy level)
380
381Include "virtual" groups for every hierarchy level in output, for
382example:
383
384 de.alt.ALL 10
385 de.alt.test 5
386 de.alt.admin 7
387
388See the B<gatherstats> man page for details.
389
390This option does not work together with the B<--checkgroups> option as
391all "virtual" groups will not be present in the checkgroups file.
392
393=item B<--checkgroups> I<filename>
394
395Restrict output to those newgroups present in a file in checkgroups format
396(one newgroup name per line; everything after the first whitespace on each
397line is ignored). All other newsgroups will be removed from output.
398
399Contrary to B<gatherstats>, I<filename> is not a template, but refers to
400a single file in checkgroups format.
401
402The B<--sums> option will not work together with this option as "virtual"
403groups will not be present in the checkgroups file.
404
405=item B<-r>, B<--report> I<default|average|sums>
406
407Choose the report type: I<default>, I<average> or I<sums>
408
409By default, B<groupstats> will report the number of postings for each
410newsgroup in each month. But it can also report the average number of
411postings per group for all months or the total sum of postings per group
412for all months.
413
414For report types I<average> and I<sums>, the B<group-by> option has no
415meaning and will be silently ignored (see below).
416
417=item B<-l>, B<--lower> I<lower boundary>
418
419Set the lower boundary. See B<--boundary> below.
420
421=item B<-l>, B<--upper> I<upper boundary>
422
423Set the upper boundary. See B<--boundary> below.
424
425=item B<-b>, B<--boundary> I<boundary type>
426
427Set the boundary type to one of I<default>, I<level>, I<average> or
428I<sums>.
429
430By default, all newsgroups with more postings per month than the upper
431boundary and/or less postings per month than the lower boundary will be
432excluded from further processing. For the default report that means each
433month only newsgroups with a number of postings between the boundaries
434will be displayed. For the other report types, newsgroups with a number of
435postings exceeding the boundaries in all (!) months will not be
436considered.
437
438For example, lets take a list of newsgroups like this:
439
440 ----- 2012-01:
441 de.comp.datenbanken.misc 6
442 de.comp.datenbanken.ms-access 84
443 de.comp.datenbanken.mysql 88
444 ----- 2012-02:
445 de.comp.datenbanken.misc 8
446 de.comp.datenbanken.ms-access 126
447 de.comp.datenbanken.mysql 21
448 ----- 2012-03:
449 de.comp.datenbanken.misc 24
450 de.comp.datenbanken.ms-access 83
451 de.comp.datenbanken.mysql 36
452
453With C<groupstats --month 2012-01:2012-03 --lower 25 --report sums>,
454you'll get the following result:
455
456 ----- All months:
457 de.comp.datenbanken.ms-access 293
458 de.comp.datenbanken.mysql 124
459
460de.comp.datenbanken.misc has not been considered even though it has 38
461postings in total, because it has less than 25 postings in every single
462month. If you want to list all newsgroups with more than 25 postings
463I<in total>, you'll have to set the boundary type to I<sum>, see below.
464
465A boundary type of I<level> will show only those newsgroups - at all -
466that satisfy the boundaries in each and every single month. With the above
467list of newsgroups and
468C<groupstats --month 2012-01:2012-03 --lower 25 --boundary level --report sums>,
469you'll get this result:
470
471 ----- All months:
472 de.comp.datenbanken.ms-access 293
473
474de.comp.datenbanken.mysql has not been considered because it had less than
47525 postings in 2012-02 (only).
476
477You can use that to get a list of newsgroups that have more (or less) then
478x postings in every month during the whole reporting period.
479
480A boundary type of I<average> will show only those newsgroups - at all -that
481satisfy the boundaries on average. With the above list of newsgroups and
482C<groupstats --month 2012-01:2012-03 --lower 25 --boundary avg --report sums>,
483you'll get this result:
484
485 ----- All months:
486 de.comp.datenbanken.ms-access 293
487 de.comp.datenbanken.mysql 145
488
489The average number of postings in the three groups is:
490
491 de.comp.datenbanken.misc 12.67
492 de.comp.datenbanken.ms-access 97.67
493 de.comp.datenbanken.mysql 48.33
494
495Last but not least, a boundary type of I<sums> will show only those
496newsgroups - at all - that satisfy the boundaries with the total sum of
497all postings during the reporting period. With the above list of
498newsgroups and
499C<groupstats --month 2012-01:2012-03 --lower 25 --boundary sum --report sums>,
500you'll finally get this result:
501
502 ----- All months:
503 de.comp.datenbanken.misc 38
504 de.comp.datenbanken.ms-access 293
505 de.comp.datenbanken.mysql 145
506
507
508=item B<-g>, B<--group-by> I<month[-desc]|newsgroups[-desc]>
509
510By default, all results are grouped by month, sorted chronologically in
511ascending order, like this:
512
513 ----- 2012-01:
514 de.comp.datenbanken.ms-access 84
515 de.comp.datenbanken.mysql 88
516 ----- 2012-02:
517 de.comp.datenbanken.ms-access 126
518 de.comp.datenbanken.mysql 21
519
520The results can be grouped by newsgroups instead via
521B<--group-by> I<newsgroup>:
522
523 ----- de.comp.datenbanken.ms-access:
524 2012-01 84
525 2012-02 126
526 ----- de.comp.datenbanken.mysql:
527 2012-01 88
528 2012-02 21
529
530By appending I<-desc> to the group-by option parameter, you can reverse
531the sort order - e.g. B<--group-by> I<month-desc> will give:
532
533 ----- 2012-02:
534 de.comp.datenbanken.ms-access 126
535 de.comp.datenbanken.mysql 21
536 ----- 2012-01:
537 de.comp.datenbanken.ms-access 84
538 de.comp.datenbanken.mysql 88
539
540Average and sums reports (see above) will always be grouped by months;
541this option will therefore be ignored.
542
543=item B<-o>, B<--order-by> I<default[-desc]|postings[-desc]>
544
545Within each group (a single month or single newsgroup, see above), the
546report will be sorted by newsgroup names in ascending alphabetical order
547by default. You can change the sort order to descending or sort by number
548of postings instead.
549
550=item B<-f>, B<--format> I<pretty|list|dump>
551
552Select the output format, I<pretty> being the default:
553
554 ----- 2012-01:
555 de.comp.datenbanken.ms-access 84
556 de.comp.datenbanken.mysql 88
557 ----- 2012-02:
558 de.comp.datenbanken.ms-access 126
559 de.comp.datenbanken.mysql 21
560
561I<list> format looks like this:
562
563 2012-01 de.comp.datenbanken.ms-access 84
564 2012-01 de.comp.datenbanken.mysql 88
565 2012-02 de.comp.datenbanken.ms-access 126
566 2012-02 de.comp.datenbanken.mysql 21
567
568And I<dump> format looks like this:
569
570 # 2012-01:
571 de.comp.datenbanken.ms-access 84
572 de.comp.datenbanken.mysql 88
573 # 2012-02:
574 de.comp.datenbanken.ms-access 126
575 de.comp.datenbanken.mysql 21
576
577You can remove the comments by using B<--nocomments>, see below.
578
579=item B<-c>, B<--captions|--nocaptions>
580
581Add captions to output, like this:
582
583 ----- Report for 2012-01 to 2012-02 (number of postings for each month)
584 ----- Newsgroups: de.comp.datenbanken.*
585 ----- Threshold: 10 => x <= 20 (on average)
586 ----- Grouped by Newsgroups (ascending), sorted by number of postings descending
587
588False by default.
589
590=item B<--comments|--nocomments>
591
592Add comments (group headers) to I<dump> and I<pretty> output. True by default.
593
594Use I<--nocomments> to suppress anything except newsgroup names/months and
595numbers of postings. This is enforced when using B<--filetemplate>, see below.
596
597=item B<--filetemplate> I<filename template>
598
599Save output to file(s) instead of dumping it to STDOUT. B<groupstats> will
600create one file for each month (or each newsgroup, accordant to the
601setting of B<--group-by>, see above), with filenames composed by adding
602year and month (or newsgroup names) to the I<filename template>, for
603example with B<--filetemplate> I<stats>:
604
605 stats-2012-01
606 stats-2012-02
607 ... and so on
608
609B<--nocomments> is enforced, see above.
610
611=item B<--groupsdb> I<database table>
612
613Override I<DBTableGrps> from F<newsstats.conf>.
614
615=item B<--conffile> I<filename>
616
617Load configuration from I<filename> instead of F<newsstats.conf>.
618
619=back
620
621=head1 INSTALLATION
622
623See L<doc/INSTALL>.
624
625=head1 EXAMPLES
626
627Show number of postings per group for lasth month in I<pretty> format:
628
629 groupstats
630
631Show that report for January of 2010 and de.alt.* plus de.test,
632including display of hierarchy levels:
633
634 groupstats --month 2010-01 --newsgroups de.alt.*:de.test --sums
635
636Only show newsgroups with 30 postings or less last month, ordered
637by number of postings, descending, in I<pretty> format:
638
639 groupstats --upper 30 --order-by postings-desc
640
641Show the total of all postings for the year of 2010 for all groups that
642had 30 postings or less in every single month in that year, ordered by
643number of postings in descending order:
644
645 groupstats -m 2010-01:2010-12 -u 30 -b level -r sums -o postings-desc
646
647The same for the average number of postings in the year of 2010:
648
649 groupstats -m 2010-01:2010-12 -u 30 -b level -r avg -o postings-desc
650
651List number of postings per group for eacht month of 2010 and redirect
652output to one file for each month, namend stats-2010-01 and so on, in
653machine-readable form (without formatting):
654
655 groupstats -m 2010-01:2010-12 -f dump --filetemplate stats
656
657
658=head1 FILES
659
660=over 4
661
662=item F<bin/groupstats.pl>
663
664The script itself.
665
666=item F<lib/NewsStats.pm>
667
668Library functions for the NewsStats package.
669
670=item F<etc/newsstats.conf>
671
672Runtime configuration file.
673
674=back
675
676=head1 BUGS
677
678Please report any bugs or feature requests to the author or use the
679bug tracker at L<http://bugs.th-h.de/>!
680
681=head1 SEE ALSO
682
683=over 2
684
685=item -
686
687L<doc/README>
688
689=item -
690
691l>doc/INSTALL>
692
693=item -
694
695gatherstats -h
696
697=back
698
699This script is part of the B<NewsStats> package.
700
701=head1 AUTHOR
702
703Thomas Hochstein <thh@inter.net>
704
705=head1 COPYRIGHT AND LICENSE
706
707Copyright (c) 2010-2012 Thomas Hochstein <thh@inter.net>
708
709This program is free software; you may redistribute it and/or modify it
710under the same terms as Perl itself.
711
712=cut
This page took 0.012619 seconds and 4 git commands to generate.