fc8e3dbe957fc581715ec35a8d6882128746be25
[onis.git] / lib / Onis / Plugins / Core.pm
1 package Onis::Plugins::Core;
2
3 use strict;
4 use warnings;
5
6 use Carp (qw(confess));
7 use Exporter;
8
9 =head1 NAME
10
11 Onis::Plugins::Core
12
13 =head1 DESCRIPTION
14
15 Plugin for the main table and the hourly-statistics. This is the most
16 complicated plugin so far.
17
18 =cut
19
20 use Onis::Config qw/get_config/;
21 use Onis::Html qw/html_escape get_filehandle/;
22 use Onis::Language qw/translate/;
23 use Onis::Users (qw(get_realname get_link get_image ident_to_name));
24 use Onis::Data::Core qw#get_all_nicks nick_to_ident ident_to_nick get_main_nick register_plugin#;
25 use Onis::Data::Persistent;
26
27 @Onis::Plugins::Core::EXPORT_OK = (qw(get_core_nick_counters get_sorted_nicklist));
28 @Onis::Plugins::Core::ISA = ('Exporter');
29
30 our $NickLinesCounter = Onis::Data::Persistent->new ('NickLinesCounter', 'nick',
31         qw(
32                 lines00 lines01 lines02 lines03 lines04 lines05 lines06 lines07 lines08 lines09 lines10 lines11
33                 lines12 lines13 lines14 lines15 lines16 lines17 lines18 lines19 lines20 lines21 lines22 lines23
34         )
35 );
36 our $NickWordsCounter = Onis::Data::Persistent->new ('NickWordsCounter', 'nick',
37         qw(
38                 words00 words01 words02 words03 words04 words05 words06 words07 words08 words09 words10 words11
39                 words12 words13 words14 words15 words16 words17 words18 words19 words20 words21 words22 words23
40         )
41 );
42 our $NickCharsCounter = Onis::Data::Persistent->new ('NickCharsCounter', 'nick',
43         qw(
44                 chars00 chars01 chars02 chars03 chars04 chars05 chars06 chars07 chars08 chars09 chars10 chars11
45                 chars12 chars13 chars14 chars15 chars16 chars17 chars18 chars19 chars20 chars21 chars22 chars23
46         )
47 );
48
49 our $QuoteCache = {}; # Saves per-nick information without any modification
50 our $QuoteData = {};  # Is generated before output. Nicks are merged according to Data::Core.
51 our $NickData = {};  # Same as above, but for nicks rather than quotes.
52 our $SortedNicklist = [];
53
54 our @H_IMAGES = qw#dark-theme/h-red.png dark-theme/h-blue.png dark-theme/h-yellow.png dark-theme/h-green.png#;
55 our $QuoteCacheSize = 10;
56 our $QuoteMin = 30;
57 our $QuoteMax = 80;
58 our $WORD_LENGTH = 5;
59 our $SORT_BY = 'LINES';
60 our $DISPLAY_LINES = 'BOTH';
61 our $DISPLAY_WORDS = 'NONE';
62 our $DISPLAY_CHARS = 'NONE';
63 our $DISPLAY_TIMES = 0;
64 our $DISPLAY_IMAGES = 0;
65 our $DEFAULT_IMAGE = '';
66 our $BAR_HEIGHT = 130;
67 our $BAR_WIDTH  = 100;
68 our $LongLines  = 50;
69 our $ShortLines = 10;
70
71 if (get_config ('quote_cache_size'))
72 {
73         my $tmp = get_config ('quote_cache_size');
74         $tmp =~ s/\D//g;
75         $QuoteCacheSize = $tmp if ($tmp);
76 }
77 if (get_config ('quote_min'))
78 {
79         my $tmp = get_config ('quote_min');
80         $tmp =~ s/\D//g;
81         $QuoteMin = $tmp if ($tmp);
82 }
83 if (get_config ('quote_max'))
84 {
85         my $tmp = get_config ('quote_max');
86         $tmp =~ s/\D//g;
87         $QuoteMax = $tmp if ($tmp);
88 }
89 if (get_config ('min_word_length'))
90 {
91         my $tmp = get_config ('min_word_length');
92         $tmp =~ s/\D//g;
93         $WORD_LENGTH = $tmp if ($tmp);
94 }
95 if (get_config ('display_lines'))
96 {
97         my $tmp = get_config ('display_lines');
98         $tmp = uc ($tmp);
99
100         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
101         {
102                 $DISPLAY_LINES = $tmp;
103         }
104         else
105         {
106                 $tmp = get_config ('display_lines');
107                 print STDERR $/, __FILE__, ": ``display_lines'' has been set to the invalid value ``$tmp''. ",
108                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``both''.";
109         }
110 }
111 if (get_config ('display_words'))
112 {
113         my $tmp = get_config ('display_words');
114         $tmp = uc ($tmp);
115
116         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
117         {
118                 $DISPLAY_WORDS = $tmp;
119         }
120         else
121         {
122                 $tmp = get_config ('display_words');
123                 print STDERR $/, __FILE__, ": ``display_words'' has been set to the invalid value ``$tmp''. ",
124                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
125         }
126 }
127 if (get_config ('display_chars'))
128 {
129         my $tmp = get_config ('display_chars');
130         $tmp = uc ($tmp);
131
132         if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
133         {
134                 $DISPLAY_CHARS = $tmp;
135         }
136         else
137         {
138                 $tmp = get_config ('display_chars');
139                 print STDERR $/, __FILE__, ": ``display_chars'' has been set to the invalid value ``$tmp''. ",
140                 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
141         }
142 }
143 if (get_config ('display_times'))
144 {
145         my $tmp = get_config ('display_times');
146
147         if ($tmp =~ m/true|on|yes/i)
148         {
149                 $DISPLAY_TIMES = 1;
150         }
151         elsif ($tmp =~ m/false|off|no/i)
152         {
153                 $DISPLAY_TIMES = 0;
154         }
155         else
156         {
157                 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
158                 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
159         }
160 }
161 if (get_config ('display_images'))
162 {
163         my $tmp = get_config ('display_images');
164
165         if ($tmp =~ m/true|on|yes/i)
166         {
167                 $DISPLAY_IMAGES = 1;
168         }
169         elsif ($tmp =~ m/false|off|no/i)
170         {
171                 $DISPLAY_IMAGES = 0;
172         }
173         else
174         {
175                 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
176                 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
177         }
178 }
179 if (get_config ('default_image'))
180 {
181         $DEFAULT_IMAGE = get_config ('default_image');
182 }
183 if (get_config ('sort_by'))
184 {
185         my $tmp = get_config ('sort_by');
186         $tmp = uc ($tmp);
187
188         if (($tmp eq 'LINES') or ($tmp eq 'WORDS') or ($tmp eq 'CHARS'))
189         {
190                 $SORT_BY = $tmp;
191         }
192         else
193         {
194                 $tmp = get_config ('sort_by');
195                 print STDERR $/, __FILE__, ": ``sort_by'' has been set to the invalid value ``$tmp''. ",
196                 $/, __FILE__, ": Valid values are ``lines'' and ``words''. Using default value ``lines''.";
197         }
198 }
199 if (get_config ('horizontal_images'))
200 {
201         my @tmp = get_config ('horizontal_images');
202         my $i;
203         
204         if (scalar (@tmp) != 4)
205         {
206                 print STDERR $/, __FILE__, ": The number of horizontal images is not four. The output might look weird.", $/;
207         }
208
209         for ($i = 0; $i < 4; $i++)
210         {
211                 if (!defined ($tmp[$i]))
212                 {
213                         next;
214                 }
215
216                 $H_IMAGES[$i] = $tmp[$i];
217         }
218 }
219 if (get_config ('bar_height'))
220 {
221         my $tmp = get_config ('bar_height');
222         $tmp =~ s/\D//g;
223         $BAR_HEIGHT = $tmp if ($tmp >= 10);
224 }
225 if (get_config ('bar_width'))
226 {
227         my $tmp = get_config ('bar_width');
228         $tmp =~ s/\D//g;
229         $BAR_WIDTH = $tmp if ($tmp >= 10);
230 }
231 if (get_config ('longlines'))
232 {
233         my $tmp = get_config ('longlines');
234         $tmp =~ s/\D//g;
235         $LongLines = $tmp if ($tmp);
236 }
237 if (get_config ('shortlines'))
238 {
239         my $tmp = get_config ('shortlines');
240         $tmp =~ s/\D//g;
241         if ($tmp or ($tmp == 0))
242         {
243                 $ShortLines = $tmp;
244         }
245 }
246
247 register_plugin ('TEXT', \&add);
248 register_plugin ('ACTION', \&add);
249 register_plugin ('OUTPUT', \&output);
250
251 my $VERSION = '$Id$';
252 print STDERR $/, __FILE__, ": $VERSION" if ($::DEBUG);
253
254 return (1);
255
256 sub add
257 {
258         my $data = shift;
259
260         my $nick = $data->{'nick'};
261         my $ident = $data->{'ident'};
262         my $hour = int ($data->{'hour'});
263         my $host = $data->{'host'};
264         my $text = $data->{'text'};
265         my $type = $data->{'type'};
266         my $time = $data->{'epoch'};
267
268         my $words = scalar (@{$data->{'words'}});
269         my $chars = length ($text);
270
271         if ($type eq 'ACTION')
272         {
273                 $chars -= (length ($nick) + 3);
274         }
275
276         my @counter = $NickLinesCounter->get ($nick);
277         if (!@counter)
278         {
279                 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
280         }
281         $counter[$hour]++;
282         $NickLinesCounter->put ($nick, @counter);
283
284         @counter = $NickWordsCounter->get ($nick);
285         if (!@counter)
286         {
287                 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
288         }
289         $counter[$hour] += $words;
290         $NickWordsCounter->put ($nick, @counter);
291
292         @counter = $NickCharsCounter->get ($nick);
293         if (!@counter)
294         {
295                 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
296         }
297         $counter[$hour] += $chars;
298         $NickCharsCounter->put ($nick, @counter);
299
300         if ((length ($text) >= $QuoteMin)
301                                 and (length ($text) <= $QuoteMax))
302         {
303                 if (!defined ($QuoteCache->{$nick}))
304                 {
305                         $QuoteCache->{$nick} = [];
306                 }
307                 push (@{$QuoteCache->{$nick}}, [$time, $text]);
308         }
309
310         if (defined ($QuoteCache->{$nick}))
311         {
312                 while (scalar (@{$QuoteCache->{$nick}}) > $QuoteCacheSize)
313                 {
314                         shift (@{$QuoteCache->{$nick}});
315                 }
316         }
317
318         return (1);
319 }
320
321 sub calculate
322 {
323         for (get_all_nicks ())
324         {
325                 my $nick = $_;
326                 my $main = get_main_nick ($nick);
327
328                 if (!defined ($NickData->{$main}))
329                 {
330                         $NickData->{$main} =
331                         {
332                                 lines => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
333                                 words => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
334                                 chars => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
335                                 lines_total => 0,
336                                 words_total => 0,
337                                 chars_total => 0
338                         };
339                 }
340
341                 my @counter = $NickLinesCounter->get ($nick);
342                 if (@counter)
343                 {
344                         my $sum = 0;
345                         for (my $i = 0; $i < 24; $i++)
346                         {
347                                 $NickData->{$main}{'lines'}[$i] += $counter[$i];
348                                 $sum += $counter[$i];
349                         }
350                         $NickData->{$main}{'lines_total'} = $sum;
351                 }
352
353                 @counter = $NickWordsCounter->get ($nick);
354                 if (@counter)
355                 {
356                         my $sum = 0;
357                         for (my $i = 0; $i < 24; $i++)
358                         {
359                                 $NickData->{$main}{'words'}[$i] += $counter[$i];
360                                 $sum += $counter[$i];
361                         }
362                         $NickData->{$main}{'words_total'} = $sum;
363                 }
364
365                 @counter = $NickCharsCounter->get ($nick);
366                 if (@counter)
367                 {
368                         my $sum = 0;
369                         for (my $i = 0; $i < 24; $i++)
370                         {
371                                 $NickData->{$main}{'chars'}[$i] += $counter[$i];
372                                 $sum += $counter[$i];
373                         }
374                         $NickData->{$main}{'chars_total'} = $sum;
375                 }
376
377                 if (!defined ($QuoteData->{$main}))
378                 {
379                         $QuoteData->{$main} = [];
380                 }
381                 if (defined ($QuoteCache->{$nick}))
382                 {
383                         my @new = ();
384                         push (@new, @{$QuoteData->{$main}}) if (@{$QuoteData->{$main}});
385                         push (@new, @{$QuoteCache->{$nick}}) if (@{$QuoteCache->{$nick}});
386
387                         @new = sort { $b->[0] <=> $a->[0] } (@new);
388                         splice (@new, $QuoteCacheSize) if (scalar (@new) > $QuoteCacheSize);
389
390                         $QuoteData->{$main} = \@new;
391                 }
392         }
393 }
394
395 sub output
396 {
397         calculate ();
398         activetimes ();
399         ranking ();
400 }
401         
402 sub activetimes
403 {
404         my $max = 0;            # the most lines that were written in one hour..
405         my $total = 0;          # the total amount of lines we wrote..
406         my $factor = 0;         # used to find a bar's height
407
408         my @data = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
409
410         my @img_urls = get_config ('vertical_images');
411         if (!@img_urls)
412         {
413                 @img_urls = qw#images/ver0n.png images/ver1n.png images/ver2n.png images/ver3n.png#;
414         }
415
416         my $fh = get_filehandle () or die;
417         
418 # this for loop looks for the most amount of lines in one hour and sets
419 # $most_lines
420         for (keys %$NickData)
421         {
422                 my $nick = $_;
423
424                 for (my $i = 0; $i < 24; $i++)
425                 {
426                         $data[$i] += $NickData->{$nick}{'chars'}[$i];
427                 }
428         }
429
430         for (my $i = 0; $i < 24; $i++)
431         {
432                 $max = $data[$i] if ($max < $data[$i]);
433                 $total += $data[$i];
434         }
435
436         if (!$total)
437         {
438                 $total = 1;
439                 $max = 1;
440         }
441
442         $factor = (($BAR_HEIGHT - 1) / $max);
443
444         my $header = translate ('When do we actually talk here?');
445         print $fh "<h2>$header</h2>\n",
446         qq#<table class="hours_of_day">\n#,
447         qq#  <tr>\n#;
448
449 # this for circles through the four colors. Each color represents six hours.
450 # (4 * 6 hours = 24 hours)
451         for (my $i = 0; $i <= 3; $i++)
452         {
453                 for (my $j = 0; $j <= 5; $j++)
454                 {
455                         my $hour = (($i * 6) + $j);
456                         if (!defined ($data[$hour]))
457                         {
458                                 $data[$hour] = 0;
459                         }
460
461                         my $percent = 100 * ($data[$hour] / $total);
462                         my $height = int ($data[$hour] * $factor) + 1;
463                         my $img_url = $img_urls[$i];
464                         
465                         print $fh '    <td>', sprintf ("%2.1f", $percent),
466                         qq#%<br /><img src="$img_url" style="height: $height#,
467                         qq#px;" alt="" /></td>\n#;
468                 }
469         }
470
471         print $fh "  </tr>\n",
472         qq#  <tr class="hour_row">\n#;
473         print $fh map { "    <td>$_</td>\n" } (0 .. 23);
474         print $fh "  </tr>\n",
475         "</table>\n\n";
476 }
477
478 sub ranking
479 {
480         my $count = 0;
481
482         my @nicks = keys (%$NickData);
483
484         return unless (@nicks);
485         
486         my $max_lines = 1;
487         my $max_words = 1;
488         my $max_chars = 1;
489         
490         my $linescount = 0;
491
492         my $fh = get_filehandle () or die;
493
494         my $sort_field = lc ($SORT_BY);
495
496         my $trans;
497
498         my $tmp;
499         ($tmp) = sort { $NickData->{$b}{'lines_total'} <=> $NickData->{$a}{'lines_total'} } (@nicks);
500         $max_lines = $NickData->{$tmp}{'lines_total'} || 0;
501         
502         ($tmp) = sort { $NickData->{$b}{'words_total'} <=> $NickData->{$a}{'words_total'} } (@nicks);
503         $max_words = $NickData->{$tmp}{'words_total'} || 0;
504         
505         ($tmp) = sort { $NickData->{$b}{'chars_total'} <=> $NickData->{$a}{'chars_total'} } (@nicks);
506         $max_chars = $NickData->{$tmp}{'chars_total'} || 0;
507         
508         $trans = translate ('Most active nicks');
509         
510         print $fh "<h2>$trans</h2>\n";
511         if ($SORT_BY eq 'LINES')
512         {
513                 $trans = translate ('Nicks sorted by numbers of lines written');
514         }
515         elsif ($SORT_BY eq 'WORDS')
516         {
517                 $trans = translate ('Nicks sorted by numbers of words written');
518         }
519         else # ($SORT_BY eq 'CHARS')
520         {
521                 $trans = translate ('Nicks sorted by numbers of characters written');
522         }
523         print $fh "<p>($trans)</p>\n";
524
525         print $fh <<EOF;
526
527 <table class="big_ranking">
528   <tr>
529     <td class="invis">&nbsp;</td>
530 EOF
531         if ($DISPLAY_IMAGES)
532         {
533                 $trans = translate ('Image');
534                 print $fh "    <th>$trans</th>\n";
535         }
536         #if (true)
537         {
538                 $trans = translate ('Nick');
539                 print $fh "    <th>$trans</th>\n";
540         }
541         if ($DISPLAY_LINES ne 'NONE')
542         {
543                 $trans = translate ('Number of Lines');
544                 print $fh "    <th>$trans</th>\n";
545         }
546         if ($DISPLAY_WORDS ne 'NONE')
547         {
548                 $trans = translate ('Number of Words');
549                 print $fh "    <th>$trans</th>\n";
550         }
551         if ($DISPLAY_CHARS ne 'NONE')
552         {
553                 $trans = translate ('Number of Characters');
554                 print $fh "    <th>$trans</th>\n";
555         }
556         if ($DISPLAY_TIMES)
557         {
558                 $trans = translate ('When?');
559                 print $fh "    <th>$trans</th>\n";
560         }
561         
562         $trans = translate ('Random Quote');
563         print $fh "    <th>$trans</th>\n",
564         "  </tr>\n";
565
566         @$SortedNicklist = sort
567         {
568                 $NickData->{$b}{"${sort_field}_total"} <=> $NickData->{$a}{"${sort_field}_total"}
569         } (@nicks);
570
571         @nicks = ();
572
573         for (@$SortedNicklist)
574         {
575                 my $nick = $_;
576                 my $ident = nick_to_ident ($nick);
577                 my $name  = ident_to_name ($ident);
578                 my $print = $name || $nick;
579
580                 $linescount++;
581
582                 # As long as we didn't hit the 
583                 # $LongLines-limit we continue
584                 # our table..
585                 if ($linescount <= $LongLines)
586                 {
587                         my $quote = translate ('-- no quote available --');
588
589                         if (@{$QuoteData->{$nick}})
590                         {
591                                 my $num = scalar (@{$QuoteData->{$nick}});
592                                 my $rand = int (rand ($num));
593
594                                 require Data::Dumper;
595                                 print STDOUT Data::Dumper->Dump ([$rand, $QuoteData->{$nick}], ['rand', "QuoteData->{$nick}"]);
596                                 
597                                 $quote = html_escape ($QuoteData->{$nick}[$rand][1]);
598                         }
599
600                         my $link = '';
601                         my $image = '';
602                         my $realname = '';
603                         if ($name)
604                         {
605                                 $link     = get_link ($name);
606                                 $image    = get_image ($name);
607                                 $realname = get_realname ($name);
608                         }
609                         
610                         print $fh "  <tr>\n",
611                         qq#    <td class="numeration"># . $linescount . "</td>\n";
612
613                         if ($DISPLAY_IMAGES)
614                         {
615                                 if ($DEFAULT_IMAGE and !$image)
616                                 {
617                                         $image = $DEFAULT_IMAGE;
618                                 }
619                                 
620                                 print $fh qq#    <td class="image">#;
621                                 if ($image)
622                                 {
623                                         if ($link)
624                                         {
625                                                 print $fh qq#<a href="$link">#;
626                                         }
627                                         print $fh qq#<img src="$image" alt="$name" />#;
628                                         if ($link)
629                                         {
630                                                 print $fh "</a>";
631                                         }
632                                 }
633                                 else
634                                 {
635                                         print $fh '&nbsp;';
636                                 }
637                                 print $fh "</td>\n";
638                         }
639                         
640                         my $title = $realname;
641                         if (!$title)
642                         {
643                                 $title = "User: $name; " if ($name);
644                                 $title .= "Ident: $ident";
645                         }
646                         print $fh qq#    <td class="nick" title="$title">#;
647
648                         if ($link)
649                         {
650                                 print $fh qq#<a href="$link">$print</a></td>\n#
651                         }
652                         else
653                         {
654                                 print $fh qq#$print</td>\n#;
655                         }
656                 
657                         if ($DISPLAY_LINES ne 'NONE')
658                         {
659                                 print $fh qq#    <td class="bar">#;
660                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'BAR'))
661                                 {
662                                         my $code = bar ($max_lines, $NickData->{$nick}{'lines'});
663                                         print $fh $code;
664                                 }
665                                 print $fh '&nbsp;' if ($DISPLAY_LINES eq 'BOTH');
666                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'NUMBER'))
667                                 {
668                                         print $fh $NickData->{$nick}{'lines_total'};
669                                 }
670                                 print $fh "</td>\n";
671                         }
672
673                         if ($DISPLAY_WORDS ne 'NONE')
674                         {
675                                 print $fh qq#    <td class="bar">#;
676                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'BAR'))
677                                 {
678                                         my $code = bar ($max_words, $NickData->{$nick}{'words'});
679                                         print $fh $code;
680                                 }
681                                 print $fh '&nbsp;' if ($DISPLAY_WORDS eq 'BOTH');
682                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'NUMBER'))
683                                 {
684                                         print $fh $NickData->{$nick}{'words_total'};
685                                 }
686                                 print $fh "</td>\n";
687                         }
688
689                         if ($DISPLAY_CHARS ne 'NONE')
690                         {
691                                 print $fh qq#    <td class="bar">#;
692                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'BAR'))
693                                 {
694                                         my $code = bar ($max_chars, $NickData->{$nick}{'chars'});
695                                         print $fh $code;
696                                 }
697                                 print $fh '&nbsp;' if ($DISPLAY_CHARS eq 'BOTH');
698                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'NUMBER'))
699                                 {
700                                         print $fh $NickData->{$nick}{'chars_total'};
701                                 }
702                                 print $fh "</td>\n";
703                         }
704
705                         if ($DISPLAY_TIMES)
706                         {
707                                 my $code = bar ($NickData->{$nick}{'chars_total'}, $NickData->{$nick}{'chars'});
708                                 print $fh qq#    <td class="bar">$code</td>\n#;
709                         }
710
711                         print $fh qq#    <td class="quote">$quote</td>\n#,
712                         qq#  </tr>\n#;
713                         
714                         if ($linescount == $LongLines)
715                         {
716                                 print $fh "</table>\n\n";
717                         }
718                 }
719
720                 # Ok, we have too many people to
721                 # list them all so we start a
722                 # smaller table and just list the
723                 # names.. (Six names per line..)
724                 elsif ($linescount <= ($LongLines + 6 * $ShortLines))
725                 {
726                         my $row_in_this_table = int (($linescount - $LongLines - 1) / 6);
727                         my $col_in_this_table = ($linescount - $LongLines - 1) % 6;
728
729                         my $total = 0;
730                         if ($SORT_BY eq 'LINES')
731                         {
732                                 $total = $NickData->{$nick}{'lines_total'};
733                         }
734                         elsif ($SORT_BY eq 'WORDS')
735                         {
736                                 $total = $NickData->{$nick}{'words_total'};
737                         }
738                         else # ($SORT_BY eq 'CHARS')
739                         {
740                                 $total = $NickData->{$nick}{'chars_total'};
741                         }
742
743                         my $title = $name ? get_realname ($name) : '';
744                         if (!$title)
745                         {
746                                 $title = "User: $name; " if ($name);
747                                 $title .= "Ident: $ident";
748                         }
749                         
750                         if ($row_in_this_table == 0 and $col_in_this_table == 0)
751                         {
752                                 $trans = translate ("They didn't write so much");
753                                 print $fh "<h2>$trans</h2>\n",
754                                 qq#<table class="small_ranking">\n#,
755                                 qq#  <tr>\n#;
756                         }
757                         
758                         if ($col_in_this_table == 0 and $row_in_this_table != 0)
759                         {
760                                 print $fh "  </tr>\n",
761                                 qq#  <tr>\n#;
762                         }
763                         
764                         print $fh qq#    <td title="$title">$print ($total)</td>\n#;
765                         
766                         if ($row_in_this_table == $ShortLines and $col_in_this_table == 5)
767                         {
768                                 print $fh "  </tr>\n",
769                                 qq#</table>\n\n#;
770                         }
771                 }
772
773                 # There is no else. There are
774                 # just too many people around.
775                 # I might add a "There are xyz
776                 # unmentioned nicks"-line..
777         }
778
779         if (($linescount > $LongLines)
780                         and ($linescount <= ($LongLines + 6 * $ShortLines)))
781         {
782                 my $col = ($linescount - $LongLines - 1) % 6;
783
784                 while ($col < 5)
785                 {
786                         print $fh qq#    <td>&nbsp;</td>\n#;
787                         $col++;
788                 }
789
790                 print $fh "  </tr>\n";
791         }
792
793         if ($linescount != $LongLines)
794         {
795                 print $fh "</table>\n\n";
796         }
797 }
798
799 # this is called by "&ranking ();" and prints the horizontal usage-bar in the
800 # detailed nick-table
801 sub bar
802 {
803         my $max_num = shift;
804         my $source = shift;
805
806         confess () unless (ref ($source) eq 'ARRAY');
807
808         # BAR_WIDTH is a least 10
809         my $max_width = $BAR_WIDTH - 4;
810         my $factor = 1;
811         my $retval = '';
812
813         my $i;
814         my $j;
815
816         if (!$max_num) { return ($retval); }
817         $factor = $max_width / $max_num;
818
819         for ($i = 0; $i < 4; $i++)
820         {
821                 my $sum = 0;
822                 my $width = 1;
823                 my $img = $H_IMAGES[$i];
824
825                 for ($j = 0; $j < 6; $j++)
826                 {
827                         my $hour = ($i * 6) + $j;
828                         $sum += $source->[$hour];
829                 }
830
831                 $width += int (0.5 + ($sum * $factor));
832                 
833                 $retval .= qq#<img src="$img" style="width: # . $width . q#px"#;
834                 if ($i == 0) { $retval .= qq# class="first"#; }
835                 elsif ($i == 3) { $retval .= qq# class="last"#; }
836                 $retval .= ' alt="" />';
837         }
838
839         return ($retval);
840 }
841
842 =head1 EXPORTED FUNCTIONS
843
844 =over 4
845
846 =item B<get_core_nick_counters> (I<$nick>)
847
848 Returns a hash-ref that containes all the nick-counters available. It looks
849 like this:
850
851     {
852         lines => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
853         words => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
854         chars => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
855         lines_total => 0,
856         words_total => 0,
857         chars_total => 0
858     }
859
860 =cut
861
862 sub get_core_nick_counters
863 {
864         my $nick = shift;
865
866         if (!defined ($NickData->{$nick}))
867         {
868                 return ({});
869         }
870
871         return ($NickData->{$nick});
872 }
873
874 =item B<get_sorted_nicklist> ()
875
876 Returns an array-ref that containes all nicks, sorted by the field given in the
877 config-file.
878
879 =cut
880
881 sub get_sorted_nicklist
882 {
883         return ($SortedNicklist);
884 }
885
886 =back
887
888 =head1 AUTHOR
889
890 Florian octo Forster, E<lt>octo at verplant.orgE<gt>
891
892 =cut