Merge branch 'thh-small-changes' into next
[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.02";
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>] [B<--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 etc/ via Config::Auto or from a configuration file
346submitted by the B<--conffile> option.
347
348See doc/INSTALL for an overview of possible configuration options.
349
350You can override some configuration options via the B<--groupsdb> option.
351
352=head1 OPTIONS
353
354=over 3
355
356=item B<-V>, B<--version>
357
358Print out version and copyright information and exit.
359
360=item B<-h>, B<--help>
361
362Print this man page and exit.
363
364=item B<-m>, B<--month> I<YYYY-MM[:YYYY-MM]|all>
365
366Set processing period to a single month in YYYY-MM format or to a time
367period between two month in YYYY-MM:YYYY-MM format (two month, separated
368by a colon). By using the keyword I<all> instead, you can set no
369processing period to process the whole database.
370
371=item B<-n>, B<--newsgroups> I<newsgroup(s)>
372
373Limit processing to a certain set of newsgroups. I<newsgroup(s)> can
374be a single newsgroup name (de.alt.test), a newsgroup hierarchy
375(de.alt.*) or a list of either of these, separated by colons, for
376example
377
378 de.test:de.alt.test:de.newusers.*
379
380=item B<-s>, B<--sums|--nosums> (sum per hierarchy level)
381
382Include "virtual" groups for every hierarchy level in output, for
383example:
384
385 de.alt.ALL 10
386 de.alt.test 5
387 de.alt.admin 7
388
389See the B<gatherstats> man page for details.
390
391This option does not work together with the B<--checkgroups> option as
392all "virtual" groups will not be present in the checkgroups file.
393
394=item B<--checkgroups> I<filename>
395
396Restrict output to those newgroups present in a file in checkgroups format
397(one newgroup name per line; everything after the first whitespace on each
398line is ignored). All other newsgroups will be removed from output.
399
400Contrary to B<gatherstats>, I<filename> is not a template, but refers to
401a single file in checkgroups format.
402
403The B<--sums> option will not work together with this option as "virtual"
404groups will not be present in the checkgroups file.
405
406=item B<-r>, B<--report> I<default|average|sums>
407
408Choose the report type: I<default>, I<average> or I<sums>
409
410By default, B<groupstats> will report the number of postings for each
411newsgroup in each month. But it can also report the average number of
412postings per group for all months or the total sum of postings per group
413for all months.
414
415For report types I<average> and I<sums>, the B<group-by> option has no
416meaning and will be silently ignored (see below).
417
418=item B<-l>, B<--lower> I<lower boundary>
419
420Set the lower boundary. See B<--boundary> below.
421
422=item B<-l>, B<--upper> I<upper boundary>
423
424Set the upper boundary. See B<--boundary> below.
425
426=item B<-b>, B<--boundary> I<boundary type>
427
428Set the boundary type to one of I<default>, I<level>, I<average> or
429I<sums>.
430
431By default, all newsgroups with more postings per month than the upper
432boundary and/or less postings per month than the lower boundary will be
433excluded from further processing. For the default report that means each
434month only newsgroups with a number of postings between the boundaries
435will be displayed. For the other report types, newsgroups with a number of
436postings exceeding the boundaries in all (!) months will not be
437considered.
438
439For example, lets take a list of newsgroups like this:
440
441 ----- 2012-01:
442 de.comp.datenbanken.misc 6
443 de.comp.datenbanken.ms-access 84
444 de.comp.datenbanken.mysql 88
445 ----- 2012-02:
446 de.comp.datenbanken.misc 8
447 de.comp.datenbanken.ms-access 126
448 de.comp.datenbanken.mysql 21
449 ----- 2012-03:
450 de.comp.datenbanken.misc 24
451 de.comp.datenbanken.ms-access 83
452 de.comp.datenbanken.mysql 36
453
454With C<groupstats --month 2012-01:2012-03 --lower 25 --report sums>,
455you'll get the following result:
456
457 ----- All months:
458 de.comp.datenbanken.ms-access 293
459 de.comp.datenbanken.mysql 124
460
461de.comp.datenbanken.misc has not been considered even though it has 38
462postings in total, because it has less than 25 postings in every single
463month. If you want to list all newsgroups with more than 25 postings
464I<in total>, you'll have to set the boundary type to I<sum>, see below.
465
466A boundary type of I<level> will show only those newsgroups - at all -
467that satisfy the boundaries in each and every single month. With the above
468list of newsgroups and
469C<groupstats --month 2012-01:2012-03 --lower 25 --boundary level --report sums>,
470you'll get this result:
471
472 ----- All months:
473 de.comp.datenbanken.ms-access 293
474
475de.comp.datenbanken.mysql has not been considered because it had less than
47625 postings in 2012-02 (only).
477
478You can use that to get a list of newsgroups that have more (or less) then
479x postings in every month during the whole reporting period.
480
481A boundary type of I<average> will show only those newsgroups - at all -that
482satisfy the boundaries on average. With the above list of newsgroups and
483C<groupstats --month 2012-01:2012-03 --lower 25 --boundary avg --report sums>,
484you'll get this result:
485
486 ----- All months:
487 de.comp.datenbanken.ms-access 293
488 de.comp.datenbanken.mysql 145
489
490The average number of postings in the three groups is:
491
492 de.comp.datenbanken.misc 12.67
493 de.comp.datenbanken.ms-access 97.67
494 de.comp.datenbanken.mysql 48.33
495
496Last but not least, a boundary type of I<sums> will show only those
497newsgroups - at all - that satisfy the boundaries with the total sum of
498all postings during the reporting period. With the above list of
499newsgroups and
500C<groupstats --month 2012-01:2012-03 --lower 25 --boundary sum --report sums>,
501you'll finally get this result:
502
503 ----- All months:
504 de.comp.datenbanken.misc 38
505 de.comp.datenbanken.ms-access 293
506 de.comp.datenbanken.mysql 145
507
508
509=item B<-g>, B<--group-by> I<month[-desc]|newsgroups[-desc]>
510
511By default, all results are grouped by month, sorted chronologically in
512ascending order, like this:
513
514 ----- 2012-01:
515 de.comp.datenbanken.ms-access 84
516 de.comp.datenbanken.mysql 88
517 ----- 2012-02:
518 de.comp.datenbanken.ms-access 126
519 de.comp.datenbanken.mysql 21
520
521The results can be grouped by newsgroups instead via
522B<--group-by> I<newsgroup>:
523
524 ----- de.comp.datenbanken.ms-access:
525 2012-01 84
526 2012-02 126
527 ----- de.comp.datenbanken.mysql:
528 2012-01 88
529 2012-02 21
530
531By appending I<-desc> to the group-by option parameter, you can reverse
532the sort order - e.g. B<--group-by> I<month-desc> will give:
533
534 ----- 2012-02:
535 de.comp.datenbanken.ms-access 126
536 de.comp.datenbanken.mysql 21
537 ----- 2012-01:
538 de.comp.datenbanken.ms-access 84
539 de.comp.datenbanken.mysql 88
540
541Average and sums reports (see above) will always be grouped by months;
542this option will therefore be ignored.
543
544=item B<-o>, B<--order-by> I<default[-desc]|postings[-desc]>
545
546Within each group (a single month or single newsgroup, see above), the
547report will be sorted by newsgroup names in ascending alphabetical order
548by default. You can change the sort order to descending or sort by number
549of postings instead.
550
551=item B<-f>, B<--format> I<pretty|list|dump>
552
553Select the output format, I<pretty> being the default:
554
555 ----- 2012-01:
556 de.comp.datenbanken.ms-access 84
557 de.comp.datenbanken.mysql 88
558 ----- 2012-02:
559 de.comp.datenbanken.ms-access 126
560 de.comp.datenbanken.mysql 21
561
562I<list> format looks like this:
563
564 2012-01 de.comp.datenbanken.ms-access 84
565 2012-01 de.comp.datenbanken.mysql 88
566 2012-02 de.comp.datenbanken.ms-access 126
567 2012-02 de.comp.datenbanken.mysql 21
568
569And I<dump> format looks like this:
570
571 # 2012-01:
572 de.comp.datenbanken.ms-access 84
573 de.comp.datenbanken.mysql 88
574 # 2012-02:
575 de.comp.datenbanken.ms-access 126
576 de.comp.datenbanken.mysql 21
577
578You can remove the comments by using B<--nocomments>, see below.
579
580=item B<-c>, B<--captions|--nocaptions>
581
582Add captions to output, like this:
583
584 ----- Report for 2012-01 to 2012-02 (number of postings for each month)
585 ----- Newsgroups: de.comp.datenbanken.*
586 ----- Threshold: 10 => x <= 20 (on average)
587 ----- Grouped by Newsgroups (ascending), sorted by number of postings descending
588
589False by default.
590
591=item B<--comments|--nocomments>
592
593Add comments (group headers) to I<dump> and I<pretty> output. True by default.
594
595Use I<--nocomments> to suppress anything except newsgroup names/months and
596numbers of postings. This is enforced when using B<--filetemplate>, see below.
597
598=item B<--filetemplate> I<filename template>
599
600Save output to file(s) instead of dumping it to STDOUT. B<groupstats> will
601create one file for each month (or each newsgroup, accordant to the
602setting of B<--group-by>, see above), with filenames composed by adding
603year and month (or newsgroup names) to the I<filename template>, for
604example with B<--filetemplate> I<stats>:
605
606 stats-2012-01
607 stats-2012-02
608 ... and so on
609
610B<--nocomments> is enforced, see above.
611
612=item B<--groupsdb> I<database table>
613
614Override I<DBTableGrps> from F<newsstats.conf>.
615
616=item B<--conffile> I<filename>
617
618Load configuration from I<filename> instead of F<newsstats.conf>.
619
620=back
621
622=head1 INSTALLATION
623
624See L<doc/INSTALL>.
625
626=head1 EXAMPLES
627
628Show number of postings per group for lasth month in I<pretty> format:
629
630 groupstats
631
632Show that report for January of 2010 and de.alt.* plus de.test,
633including display of hierarchy levels:
634
635 groupstats --month 2010-01 --newsgroups de.alt.*:de.test --sums
636
637Only show newsgroups with 30 postings or less last month, ordered
638by number of postings, descending, in I<pretty> format:
639
640 groupstats --upper 30 --order-by postings-desc
641
642Show the total of all postings for the year of 2010 for all groups that
643had 30 postings or less in every single month in that year, ordered by
644number of postings in descending order:
645
646 groupstats -m 2010-01:2010-12 -u 30 -b level -r sums -o postings-desc
647
648The same for the average number of postings in the year of 2010:
649
650 groupstats -m 2010-01:2010-12 -u 30 -b level -r avg -o postings-desc
651
652List number of postings per group for eacht month of 2010 and redirect
653output to one file for each month, namend stats-2010-01 and so on, in
654machine-readable form (without formatting):
655
656 groupstats -m 2010-01:2010-12 -f dump --filetemplate stats
657
658
659=head1 FILES
660
661=over 4
662
663=item F<bin/groupstats.pl>
664
665The script itself.
666
667=item F<lib/NewsStats.pm>
668
669Library functions for the NewsStats package.
670
671=item F<etc/newsstats.conf>
672
673Runtime configuration file.
674
675=back
676
677=head1 BUGS
678
679Please report any bugs or feature requests to the author or use the
680bug tracker at L<http://bugs.th-h.de/>!
681
682=head1 SEE ALSO
683
684=over 2
685
686=item -
687
688L<doc/README>
689
690=item -
691
692l>doc/INSTALL>
693
694=item -
695
696gatherstats -h
697
698=back
699
700This script is part of the B<NewsStats> package.
701
702=head1 AUTHOR
703
704Thomas Hochstein <thh@inter.net>
705
706=head1 COPYRIGHT AND LICENSE
707
708Copyright (c) 2010-2013 Thomas Hochstein <thh@inter.net>
709
710This program is free software; you may redistribute it and/or modify it
711under the same terms as Perl itself.
712
713=cut
This page took 0.012665 seconds and 4 git commands to generate.