Fix forgotten dates.
[usenet/newsstats.git] / bin / groupstats.pl
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
15 BEGIN {
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 }
21 use strict;
22 use warnings;
23
24 use NewsStats qw(:DEFAULT :TimePeriods :Output :SQLHelper ReadGroupList);
25
26 use DBI;
27 use Getopt::Long qw(GetOptions);
28 Getopt::Long::config ('bundling');
29
30 ################################# Main program #################################
31
32 ### read commandline options
33 my ($OptBoundType,$OptCaptions,$OptCheckgroupsFile,$OptComments,
34     $OptFileTemplate,$OptFormat,$OptGroupBy,$OptGroupsDB,$LowBound,$OptMonth,
35     $OptNewsgroups,$OptOrderBy,$OptReportType,$OptSums,$UppBound,$OptConfFile);
36 GetOptions ('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
60 if ($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
72 if ($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
83 my $ValidGroups;
84 if ($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
93 my %Conf = %{ReadConfig($OptConfFile)};
94
95 ### override configuration via commandline options
96 my %ConfOverride;
97 $ConfOverride{'DBTableGrps'} = $OptGroupsDB if $OptGroupsDB;
98 &OverrideConfig(\%Conf,\%ConfOverride);
99
100 ### init database
101 my $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
106 my ($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
112 my ($SQLWhereNewsgroups,@SQLBindNewsgroups);
113 if ($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)
121 my ($SQLWhereClause,$SQLHavingClause);
122 # $OptBoundType 'level'
123 if ($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'
147 my ($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
153 my $SQLSelect;
154 my $SQLGroupClause = '';
155 my $Precision = 0;       # number of digits right of decimal point for output
156 if ($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
176 my $Field = ($GroupBy eq 'month') ? 'newsgroup' : 'month';
177 my ($MaxLength,$MaxValLength) = &GetMaxLength($DBHandle,$Conf{'DBTableGrps'},
178                                               $Field,'postings',$SQLWhereClause,
179                                               $SQLHavingClause,
180                                               @SQLBindNewsgroups);
181
182 ### build and execute SQL query
183 my ($DBQuery);
184 # special query preparation for $OptBoundType 'level', 'average' or 'sums'
185 if ($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
230 if ($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
276 groupstats - create reports on newsgroup usage
277
278 =head1 SYNOPSIS
279
280 B<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
284 See L<doc/README>.
285
286 =head1 DESCRIPTION
287
288 This script create reports on newsgroup usage (number of postings per
289 group per month) taken from result tables created by
290 B<gatherstats.pl>.
291
292 =head2 Features and options
293
294 =head3 Time period and newsgroups
295
296 The time period to act on defaults to last month; you can assign another
297 time period or a single month (or drop all time constraints) via the
298 B<--month> option (see below).
299
300 B<groupstats> will process all newsgroups by default; you can limit
301 processing to only some newsgroups by supplying a list of those groups via
302 B<--newsgroups> option (see below). You can include hierarchy levels in
303 the output by adding the B<--sums> switch (see below). Optionally
304 newsgroups not present in a checkgroups file can be excluded from output,
305 sse B<--checkgroups> below.
306
307 =head3 Report type
308
309 You can choose between different B<--report> types: postings per month,
310 average postings per month or all postings summed up; for details, see
311 below.
312
313 =head3 Upper and lower boundaries
314
315 Furthermore you can set an upper and/or lower boundary to exclude some
316 results from output via the B<--lower> and B<--upper> options,
317 respectively. By default, all newsgroups with more and/or less postings
318 per month will be excluded from the result set (i.e. not shown and not
319 considered for average and sum reports). You can change the meaning of
320 those boundaries with the B<--boundary> option. For details, please see
321 below.
322
323 =head3 Sorting and formatting the output
324
325 By default, all results are grouped by month; you can group results by
326 newsgroup instead via the B<--groupy-by> option. Within those groups, the
327 list of newsgroups (or months) is sorted alphabetically (or
328 chronologically, respectively) ascending. You can change that order (and
329 sort by number of postings) with the B<--order-by> option. For details and
330 exceptions, please see below.
331
332 The results will be formatted as a kind of table; you can change the
333 output format to a simple list or just a list of newsgroups and number of
334 postings with the B<--format> option. Captions will be added by means of
335 the B<--caption> option; all comments (and captions) can be supressed by
336 using B<--nocomments>.
337
338 Last but not least you can redirect all output to a number of files, e.g.
339 one for each month, by submitting the B<--filetemplate> option, see below.
340 Captions and comments are automatically disabled in this case.
341
342 =head2 Configuration
343
344 B<groupstats> will read its configuration from F<newsstats.conf>
345 which should be present in the same directory via Config::Auto.
346
347 See doc/INSTALL for an overview of possible configuration options.
348
349 You 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
357 Print out version and copyright information and exit.
358
359 =item B<-h>, B<--help>
360
361 Print this man page and exit.
362
363 =item B<-m>, B<--month> I<YYYY-MM[:YYYY-MM]|all>
364
365 Set processing period to a single month in YYYY-MM format or to a time
366 period between two month in YYYY-MM:YYYY-MM format (two month, separated
367 by a colon). By using the keyword I<all> instead, you can set no
368 processing period to process the whole database.
369
370 =item B<-n>, B<--newsgroups> I<newsgroup(s)>
371
372 Limit processing to a certain set of newsgroups. I<newsgroup(s)> can
373 be a single newsgroup name (de.alt.test), a newsgroup hierarchy
374 (de.alt.*) or a list of either of these, separated by colons, for
375 example
376
377    de.test:de.alt.test:de.newusers.*
378
379 =item B<-s>, B<--sums|--nosums> (sum per hierarchy level)
380
381 Include "virtual" groups for every hierarchy level in output, for
382 example:
383
384     de.alt.ALL 10
385     de.alt.test 5
386     de.alt.admin 7
387
388 See the B<gatherstats> man page for details.
389
390 This option does not work together with the B<--checkgroups> option as
391 all "virtual" groups will not be present in the checkgroups file.
392
393 =item B<--checkgroups> I<filename>
394
395 Restrict output to those newgroups present in a file in checkgroups format
396 (one newgroup name per line; everything after the first whitespace on each
397 line is ignored). All other newsgroups will be removed from output.
398
399 Contrary to B<gatherstats>, I<filename> is not a template, but refers to
400 a single file in checkgroups format.
401
402 The B<--sums> option will not work together with this option as "virtual"
403 groups will not be present in the checkgroups file.
404
405 =item B<-r>, B<--report> I<default|average|sums>
406
407 Choose the report type: I<default>, I<average> or I<sums>
408
409 By default, B<groupstats> will report the number of postings for each
410 newsgroup in each month. But it can also report the average number of
411 postings per group for all months or the total sum of postings per group
412 for all months.
413
414 For report types I<average> and I<sums>, the B<group-by> option has no
415 meaning and will be silently ignored (see below).
416
417 =item B<-l>, B<--lower> I<lower boundary>
418
419 Set the lower boundary. See B<--boundary> below.
420
421 =item B<-l>, B<--upper> I<upper boundary>
422
423 Set the upper boundary. See B<--boundary> below.
424
425 =item B<-b>, B<--boundary> I<boundary type>
426
427 Set the boundary type to one of I<default>, I<level>, I<average> or
428 I<sums>.
429
430 By default, all newsgroups with more postings per month than the upper
431 boundary and/or less postings per month than the lower boundary will be
432 excluded from further processing. For the default report that means each
433 month only newsgroups with a number of postings between the boundaries
434 will be displayed. For the other report types, newsgroups with a number of
435 postings exceeding the boundaries in all (!) months will not be
436 considered.
437
438 For 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
453 With C<groupstats --month 2012-01:2012-03 --lower 25 --report sums>,
454 you'll get the following result:
455
456     ----- All months:
457     de.comp.datenbanken.ms-access        293
458     de.comp.datenbanken.mysql            124
459
460 de.comp.datenbanken.misc has not been considered even though it has 38
461 postings in total, because it has less than 25 postings in every single
462 month. If you want to list all newsgroups with more than 25 postings
463 I<in total>, you'll have to set the boundary type to I<sum>, see below.
464
465 A boundary type of I<level> will show only those newsgroups - at all -
466 that satisfy the boundaries in each and every single month. With the above
467 list of newsgroups and
468 C<groupstats --month 2012-01:2012-03 --lower 25 --boundary level --report sums>,
469 you'll get this result:
470
471     ----- All months:
472     de.comp.datenbanken.ms-access        293
473
474 de.comp.datenbanken.mysql has not been considered because it had less than
475 25 postings in 2012-02 (only).
476
477 You can use that to get a list of newsgroups that have more (or less) then
478 x postings in every month during the whole reporting period.
479
480 A boundary type of I<average> will show only those newsgroups - at all -that
481 satisfy the boundaries on average. With the above list of newsgroups and
482 C<groupstats --month 2012-01:2012-03 --lower 25 --boundary avg --report sums>,
483 you'll get this result:
484
485    ----- All months:
486    de.comp.datenbanken.ms-access        293
487    de.comp.datenbanken.mysql            145
488
489 The 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
495 Last but not least, a boundary type of I<sums> will show only those
496 newsgroups - at all - that satisfy the boundaries with the total sum of
497 all postings during the reporting period. With the above list of
498 newsgroups and
499 C<groupstats --month 2012-01:2012-03 --lower 25 --boundary sum --report sums>,
500 you'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
510 By default, all results are grouped by month, sorted chronologically in
511 ascending 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
520 The results can be grouped by newsgroups instead via
521 B<--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
530 By appending I<-desc> to the group-by option parameter, you can reverse
531 the 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
540 Average and sums reports (see above) will always be grouped by months;
541 this option will therefore be ignored.
542
543 =item B<-o>, B<--order-by> I<default[-desc]|postings[-desc]>
544
545 Within each group (a single month or single newsgroup, see above), the
546 report will be sorted by newsgroup names in ascending alphabetical order
547 by default. You can change the sort order to descending or sort by number
548 of postings instead.
549
550 =item B<-f>, B<--format> I<pretty|list|dump>
551
552 Select 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
561 I<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
568 And 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
577 You can remove the comments by using B<--nocomments>, see below.
578
579 =item B<-c>, B<--captions|--nocaptions>
580
581 Add 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
588 False by default.
589
590 =item B<--comments|--nocomments>
591
592 Add comments (group headers) to I<dump> and I<pretty> output. True by default.
593
594 Use I<--nocomments> to suppress anything except newsgroup names/months and
595 numbers of postings. This is enforced when using B<--filetemplate>, see below.
596
597 =item B<--filetemplate> I<filename template>
598
599 Save output to file(s) instead of dumping it to STDOUT. B<groupstats> will
600 create one file for each month (or each newsgroup, accordant to the
601 setting of B<--group-by>, see above), with filenames composed by adding
602 year and month (or newsgroup names) to the I<filename template>, for
603 example with B<--filetemplate> I<stats>:
604
605     stats-2012-01
606     stats-2012-02
607     ... and so on
608
609 B<--nocomments> is enforced, see above.
610
611 =item B<--groupsdb> I<database table>
612
613 Override I<DBTableGrps> from F<newsstats.conf>.
614
615 =item B<--conffile> I<filename>
616
617 Load configuration from I<filename> instead of F<newsstats.conf>.
618
619 =back
620
621 =head1 INSTALLATION
622
623 See L<doc/INSTALL>.
624
625 =head1 EXAMPLES
626
627 Show number of postings per group for lasth month in I<pretty> format:
628
629     groupstats
630
631 Show that report for January of 2010 and de.alt.* plus de.test,
632 including display of hierarchy levels:
633
634     groupstats --month 2010-01 --newsgroups de.alt.*:de.test --sums
635
636 Only show newsgroups with 30 postings or less last month, ordered
637 by number of postings, descending, in I<pretty> format:
638
639     groupstats --upper 30 --order-by postings-desc
640
641 Show the total of all postings for the year of 2010 for all groups that
642 had 30 postings or less in every single month in that year, ordered by
643 number of postings in descending order:
644
645     groupstats -m 2010-01:2010-12 -u 30 -b level -r sums -o postings-desc
646
647 The 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
651 List number of postings per group for eacht month of 2010 and redirect
652 output to one file for each month, namend stats-2010-01 and so on, in
653 machine-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
664 The script itself.
665
666 =item F<lib/NewsStats.pm>
667
668 Library functions for the NewsStats package.
669
670 =item F<etc/newsstats.conf>
671
672 Runtime configuration file.
673
674 =back
675
676 =head1 BUGS
677
678 Please report any bugs or feature requests to the author or use the
679 bug tracker at L<http://bugs.th-h.de/>!
680
681 =head1 SEE ALSO
682
683 =over 2
684
685 =item -
686
687 L<doc/README>
688
689 =item -
690
691 l>doc/INSTALL>
692
693 =item -
694
695 gatherstats -h
696
697 =back
698
699 This script is part of the B<NewsStats> package.
700
701 =head1 AUTHOR
702
703 Thomas Hochstein <thh@inter.net>
704
705 =head1 COPYRIGHT AND LICENSE
706
707 Copyright (c) 2010-2013 Thomas Hochstein <thh@inter.net>
708
709 This program is free software; you may redistribute it and/or modify it
710 under the same terms as Perl itself.
711
712 =cut
This page took 0.0297500000000001 seconds and 3 git commands to generate.