f3ace5abe3efd2d6287412378acef0b006c25401
[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 = $NickWordsCounter->get ($nick);
366                 if (@counter)
367                 {
368                         my $sum = 0;
369                         for (my $i = 0; $i < 24; $i++)
370                         {
371                                 $NickData->{$main}{'words'}[$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 = sort (sub { $b->[0] <=> $a->[0] }, @{$QuoteCache->{$nick}}, @{$QuoteData->{$main}});
384                         splice (@new, $QuoteCacheSize) if (scalar (@new) > $QuoteCacheSize);
385                         $QuoteData->{$main} = \@new;
386                 }
387         }
388 }
389
390 sub output
391 {
392         calculate ();
393         activetimes ();
394         ranking ();
395 }
396         
397 sub activetimes
398 {
399         my $max = 0;            # the most lines that were written in one hour..
400         my $total = 0;          # the total amount of lines we wrote..
401         my $factor = 0;         # used to find a bar's height
402
403         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);
404
405         my @img_urls = get_config ('vertical_images');
406         if (!@img_urls)
407         {
408                 @img_urls = qw#images/ver0n.png images/ver1n.png images/ver2n.png images/ver3n.png#;
409         }
410
411         my $fh = get_filehandle () or die;
412         
413 # this for loop looks for the most amount of lines in one hour and sets
414 # $most_lines
415         for (keys %$NickData)
416         {
417                 my $nick = $_;
418
419                 for (my $i = 0; $i < 24; $i++)
420                 {
421                         $data[$i] += $NickData->{$nick}{'chars'}[$i];
422                 }
423         }
424
425         for (my $i = 0; $i < 24; $i++)
426         {
427                 $max = $data[$i] if ($max < $data[$i]);
428                 $total += $data[$i];
429         }
430
431         if (!$total)
432         {
433                 $total = 1;
434                 $max = 1;
435         }
436
437         $factor = (($BAR_HEIGHT - 1) / $max);
438
439         my $header = translate ('When do we actually talk here?');
440         print $fh "<h2>$header</h2>\n",
441         qq#<table class="hours_of_day">\n#,
442         qq#  <tr>\n#;
443
444 # this for circles through the four colors. Each color represents six hours.
445 # (4 * 6 hours = 24 hours)
446         for (my $i = 0; $i <= 3; $i++)
447         {
448                 for (my $j = 0; $j <= 5; $j++)
449                 {
450                         my $hour = (($i * 6) + $j);
451                         if (!defined ($data[$hour]))
452                         {
453                                 $data[$hour] = 0;
454                         }
455
456                         my $percent = 100 * ($data[$hour] / $total);
457                         my $height = int ($data[$hour] * $factor) + 1;
458                         my $img_url = $img_urls[$i];
459                         
460                         print $fh '    <td>', sprintf ("%2.1f", $percent),
461                         qq#%<br /><img src="$img_url" style="height: $height#,
462                         qq#px;" alt="" /></td>\n#;
463                 }
464         }
465
466         print $fh "  </tr>\n",
467         qq#  <tr class="hour_row">\n#;
468         print $fh map { "    <td>$_</td>\n" } (0 .. 23);
469         print $fh "  </tr>\n",
470         "</table>\n\n";
471 }
472
473 sub ranking
474 {
475         my $count = 0;
476
477         my @nicks = keys (%$NickData);
478
479         return unless (@nicks);
480         
481         my $max_lines = 1;
482         my $max_words = 1;
483         my $max_chars = 1;
484         
485         my $linescount = 0;
486
487         my $fh = get_filehandle () or die;
488
489         my $sort_field = lc ($SORT_BY);
490
491         my $trans;
492
493         my $tmp;
494         ($tmp) = sort { $NickData->{$b}{'lines_total'} <=> $NickData->{$a}{'lines_total'} } (@nicks);
495         $max_lines = $NickData->{$tmp}{'lines_total'} || 0;
496         
497         ($tmp) = sort { $NickData->{$b}{'words_total'} <=> $NickData->{$a}{'words_total'} } (@nicks);
498         $max_words = $NickData->{$tmp}{'words_total'} || 0;
499         
500         ($tmp) = sort { $NickData->{$b}{'chars_total'} <=> $NickData->{$a}{'chars_total'} } (@nicks);
501         $max_chars = $NickData->{$tmp}{'chars_total'} || 0;
502         
503         $trans = translate ('Most active nicks');
504         
505         print $fh "<h2>$trans</h2>\n";
506         if ($SORT_BY eq 'LINES')
507         {
508                 $trans = translate ('Nicks sorted by numbers of lines written');
509         }
510         elsif ($SORT_BY eq 'WORDS')
511         {
512                 $trans = translate ('Nicks sorted by numbers of words written');
513         }
514         else # ($SORT_BY eq 'CHARS')
515         {
516                 $trans = translate ('Nicks sorted by numbers of characters written');
517         }
518         print $fh "<p>($trans)</p>\n";
519
520         print $fh <<EOF;
521
522 <table class="big_ranking">
523   <tr>
524     <td class="invis">&nbsp;</td>
525 EOF
526         if ($DISPLAY_IMAGES)
527         {
528                 $trans = translate ('Image');
529                 print $fh "    <th>$trans</th>\n";
530         }
531         #if (true)
532         {
533                 $trans = translate ('Nick');
534                 print $fh "    <th>$trans</th>\n";
535         }
536         if ($DISPLAY_LINES ne 'NONE')
537         {
538                 $trans = translate ('Number of Lines');
539                 print $fh "    <th>$trans</th>\n";
540         }
541         if ($DISPLAY_WORDS ne 'NONE')
542         {
543                 $trans = translate ('Number of Words');
544                 print $fh "    <th>$trans</th>\n";
545         }
546         if ($DISPLAY_CHARS ne 'NONE')
547         {
548                 $trans = translate ('Number of Characters');
549                 print $fh "    <th>$trans</th>\n";
550         }
551         if ($DISPLAY_TIMES)
552         {
553                 $trans = translate ('When?');
554                 print $fh "    <th>$trans</th>\n";
555         }
556         
557         $trans = translate ('Random Quote');
558         print $fh "    <th>$trans</th>\n",
559         "  </tr>\n";
560
561         @$SortedNicklist = sort
562         {
563                 $NickData->{$b}{"${sort_field}_total"} <=> $NickData->{$a}{"${sort_field}_total"}
564         } (@nicks);
565
566         @nicks = ();
567
568         for (@$SortedNicklist)
569         {
570                 my $nick = $_;
571                 my $ident = nick_to_ident ($nick);
572                 my $name  = ident_to_name ($ident);
573
574                 $linescount++;
575
576                 # As long as we didn't hit the 
577                 # $LongLines-limit we continue
578                 # our table..
579                 if ($linescount <= $LongLines)
580                 {
581                         my $quote = translate ('-- no quote available --');
582
583                         if (defined ($QuoteData->{$nick}))
584                         {
585                                 my $num = scalar (@{$QuoteData->{$nick}});
586                                 my $rand = int (rand ($num));
587                                 $quote = html_escape ($QuoteData->{$nick}[$rand]);
588                         }
589
590                         my $link = '';
591                         my $image = '';
592                         my $realname = '';
593                         if ($name)
594                         {
595                                 $link     = get_link ($name);
596                                 $image    = get_image ($name);
597                                 $realname = get_realname ($name);
598                         }
599                         
600                         print $fh "  <tr>\n",
601                         qq#    <td class="numeration"># . $linescount . "</td>\n";
602
603                         if ($DISPLAY_IMAGES)
604                         {
605                                 if ($DEFAULT_IMAGE and !$image)
606                                 {
607                                         $image = $DEFAULT_IMAGE;
608                                 }
609                                 
610                                 print $fh qq#    <td class="image">#;
611                                 if ($image)
612                                 {
613                                         if ($link)
614                                         {
615                                                 print $fh qq#<a href="$link">#;
616                                         }
617                                         print $fh qq#<img src="$image" alt="$name" />#;
618                                         if ($link)
619                                         {
620                                                 print $fh "</a>";
621                                         }
622                                 }
623                                 else
624                                 {
625                                         print $fh '&nbsp;';
626                                 }
627                                 print $fh "</td>\n";
628                         }
629                         
630                         my $title = $realname;
631                         if (!$title)
632                         {
633                                 $title = "User: $name; " if ($name);
634                                 $title .= "Ident: $ident";
635                         }
636                         print $fh qq#    <td class="nick" title="$title">#;
637
638                         if ($link)
639                         {
640                                 print $fh qq#<a href="$link">$name</a></td>\n#
641                         }
642                         else
643                         {
644                                 print $fh qq#$name</td>\n#;
645                         }
646                 
647                         if ($DISPLAY_LINES ne 'NONE')
648                         {
649                                 print $fh qq#    <td class="bar">#;
650                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'BAR'))
651                                 {
652                                         my $code = bar ($max_lines, $NickData->{$nick}{'lines'});
653                                         print $fh $code;
654                                 }
655                                 print $fh '&nbsp;' if ($DISPLAY_LINES eq 'BOTH');
656                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'NUMBER'))
657                                 {
658                                         print $fh $NickData->{$nick}{'lines_total'};
659                                 }
660                                 print $fh "</td>\n";
661                         }
662
663                         if ($DISPLAY_WORDS ne 'NONE')
664                         {
665                                 print $fh qq#    <td class="bar">#;
666                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'BAR'))
667                                 {
668                                         my $code = bar ($max_words, $NickData->{$nick}{'words'});
669                                         print $fh $code;
670                                 }
671                                 print $fh '&nbsp;' if ($DISPLAY_WORDS eq 'BOTH');
672                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'NUMBER'))
673                                 {
674                                         print $fh $NickData->{$nick}{'words_total'};
675                                 }
676                                 print $fh "</td>\n";
677                         }
678
679                         if ($DISPLAY_CHARS ne 'NONE')
680                         {
681                                 print $fh qq#    <td class="bar">#;
682                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'BAR'))
683                                 {
684                                         my $code = bar ($max_chars, $NickData->{$nick}{'chars'});
685                                         print $fh $code;
686                                 }
687                                 print $fh '&nbsp;' if ($DISPLAY_CHARS eq 'BOTH');
688                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'NUMBER'))
689                                 {
690                                         print $fh $NickData->{$nick}{'chars_total'};
691                                 }
692                                 print $fh "</td>\n";
693                         }
694
695                         if ($DISPLAY_TIMES)
696                         {
697                                 my $code = bar ($NickData->{$nick}{'chars_total'}, $NickData->{$nick}{'chars'});
698                                 print $fh qq#    <td class="bar">$code</td>\n#;
699                         }
700
701                         print $fh qq#    <td class="quote">$quote</td>\n#,
702                         qq#  </tr>\n#;
703                         
704                         if ($linescount == $LongLines)
705                         {
706                                 print $fh "</table>\n\n";
707                         }
708                 }
709
710                 # Ok, we have too many people to
711                 # list them all so we start a
712                 # smaller table and just list the
713                 # names.. (Six names per line..)
714                 elsif ($linescount <= ($LongLines + 6 * $ShortLines))
715                 {
716                         my $row_in_this_table = int (($linescount - $LongLines - 1) / 6);
717                         my $col_in_this_table = ($linescount - $LongLines - 1) % 6;
718
719                         my $total = 0;
720                         if ($SORT_BY eq 'LINES')
721                         {
722                                 $total = $NickData->{$nick}{'lines_total'};
723                         }
724                         elsif ($SORT_BY eq 'WORDS')
725                         {
726                                 $total = $NickData->{$nick}{'words_total'};
727                         }
728                         else # ($SORT_BY eq 'CHARS')
729                         {
730                                 $total = $NickData->{$nick}{'chars_total'};
731                         }
732
733                         my $title = $name ? get_realname ($name) : '';
734                         if (!$title)
735                         {
736                                 $title = "User: $name; " if ($name);
737                                 $title .= "Ident: $ident";
738                         }
739                         
740                         if ($row_in_this_table == 0 and $col_in_this_table == 0)
741                         {
742                                 $trans = translate ("They didn't write so much");
743                                 print $fh "<h2>$trans</h2>\n",
744                                 qq#<table class="small_ranking">\n#,
745                                 qq#  <tr>\n#;
746                         }
747                         
748                         if ($col_in_this_table == 0 and $row_in_this_table != 0)
749                         {
750                                 print $fh "  </tr>\n",
751                                 qq#  <tr>\n#;
752                         }
753                         
754                         print $fh qq#    <td title="$title">$name ($total)</td>\n#;
755                         
756                         if ($row_in_this_table == $ShortLines and $col_in_this_table == 5)
757                         {
758                                 print $fh "  </tr>\n",
759                                 qq#</table>\n\n#;
760                         }
761                 }
762
763                 # There is no else. There are
764                 # just too many people around.
765                 # I might add a "There are xyz
766                 # unmentioned nicks"-line..
767         }
768
769         if (($linescount > $LongLines)
770                         and ($linescount <= ($LongLines + 6 * $ShortLines)))
771         {
772                 my $col = ($linescount - $LongLines - 1) % 6;
773
774                 while ($col < 5)
775                 {
776                         print $fh qq#    <td>&nbsp;</td>\n#;
777                         $col++;
778                 }
779
780                 print $fh "  </tr>\n";
781         }
782
783         if ($linescount != $LongLines)
784         {
785                 print $fh "</table>\n\n";
786         }
787 }
788
789 # this is called by "&ranking ();" and prints the horizontal usage-bar in the
790 # detailed nick-table
791 sub bar
792 {
793         my $max_num = shift;
794         my $source = shift;
795
796         confess () unless (ref ($source) eq 'ARRAY');
797
798         # BAR_WIDTH is a least 10
799         my $max_width = $BAR_WIDTH - 4;
800         my $factor = 1;
801         my $retval = '';
802
803         my $i;
804         my $j;
805
806         if (!$max_num) { return ($retval); }
807         $factor = $max_width / $max_num;
808
809         for ($i = 0; $i < 4; $i++)
810         {
811                 my $sum = 0;
812                 my $width = 1;
813                 my $img = $H_IMAGES[$i];
814
815                 for ($j = 0; $j < 6; $j++)
816                 {
817                         my $hour = ($i * 6) + $j;
818                         $sum += $source->[$hour];
819                 }
820
821                 $width += int (0.5 + ($sum * $factor));
822                 
823                 $retval .= qq#<img src="$img" style="width: # . $width . q#px"#;
824                 if ($i == 0) { $retval .= qq# class="first"#; }
825                 elsif ($i == 3) { $retval .= qq# class="last"#; }
826                 $retval .= ' alt="" />';
827         }
828
829         return ($retval);
830 }
831
832 =head1 EXPORTED FUNCTIONS
833
834 =over 4
835
836 =item B<get_core_nick_counters> (I<$nick>)
837
838 Returns a hash-ref that containes all the nick-counters available. It looks
839 like this:
840
841     {
842         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)],
843         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)],
844         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)],
845         lines_total => 0,
846         words_total => 0,
847         chars_total => 0
848     }
849
850 =cut
851
852 sub get_core_nick_counters
853 {
854         my $nick = shift;
855
856         if (!defined ($NickData->{$nick}))
857         {
858                 return ({});
859         }
860
861         return ($NickData->{$nick});
862 }
863
864 =item B<get_sorted_nicklist> ()
865
866 Returns an array-ref that containes all nicks, sorted by the field given in the
867 config-file.
868
869 =cut
870
871 sub get_sorted_nicklist
872 {
873         return ($SortedNicklist);
874 }
875
876 =back
877
878 =head1 AUTHOR
879
880 Florian octo Forster, E<lt>octo at verplant.orgE<gt>
881
882 =cut