Many many fixes. This version is actually kind of useable.
[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 = 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                 my $print = $name || $nick;
574
575                 $linescount++;
576
577                 # As long as we didn't hit the 
578                 # $LongLines-limit we continue
579                 # our table..
580                 if ($linescount <= $LongLines)
581                 {
582                         my $quote = translate ('-- no quote available --');
583
584                         if (@{$QuoteData->{$nick}})
585                         {
586                                 my $num = scalar (@{$QuoteData->{$nick}});
587                                 my $rand = int (rand ($num));
588                                 $quote = html_escape ($QuoteData->{$nick}[$rand][1]);
589                         }
590
591                         my $link = '';
592                         my $image = '';
593                         my $realname = '';
594                         if ($name)
595                         {
596                                 $link     = get_link ($name);
597                                 $image    = get_image ($name);
598                                 $realname = get_realname ($name);
599                         }
600                         
601                         print $fh "  <tr>\n",
602                         qq#    <td class="numeration"># . $linescount . "</td>\n";
603
604                         if ($DISPLAY_IMAGES)
605                         {
606                                 if ($DEFAULT_IMAGE and !$image)
607                                 {
608                                         $image = $DEFAULT_IMAGE;
609                                 }
610                                 
611                                 print $fh qq#    <td class="image">#;
612                                 if ($image)
613                                 {
614                                         if ($link)
615                                         {
616                                                 print $fh qq#<a href="$link">#;
617                                         }
618                                         print $fh qq#<img src="$image" alt="$name" />#;
619                                         if ($link)
620                                         {
621                                                 print $fh "</a>";
622                                         }
623                                 }
624                                 else
625                                 {
626                                         print $fh '&nbsp;';
627                                 }
628                                 print $fh "</td>\n";
629                         }
630                         
631                         my $title = $realname;
632                         if (!$title)
633                         {
634                                 $title = "User: $name; " if ($name);
635                                 $title .= "Ident: $ident";
636                         }
637                         print $fh qq#    <td class="nick" title="$title">#;
638
639                         if ($link)
640                         {
641                                 print $fh qq#<a href="$link">$print</a></td>\n#
642                         }
643                         else
644                         {
645                                 print $fh qq#$print</td>\n#;
646                         }
647                 
648                         if ($DISPLAY_LINES ne 'NONE')
649                         {
650                                 print $fh qq#    <td class="bar">#;
651                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'BAR'))
652                                 {
653                                         my $code = bar ($max_lines, $NickData->{$nick}{'lines'});
654                                         print $fh $code;
655                                 }
656                                 print $fh '&nbsp;' if ($DISPLAY_LINES eq 'BOTH');
657                                 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'NUMBER'))
658                                 {
659                                         print $fh $NickData->{$nick}{'lines_total'};
660                                 }
661                                 print $fh "</td>\n";
662                         }
663
664                         if ($DISPLAY_WORDS ne 'NONE')
665                         {
666                                 print $fh qq#    <td class="bar">#;
667                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'BAR'))
668                                 {
669                                         my $code = bar ($max_words, $NickData->{$nick}{'words'});
670                                         print $fh $code;
671                                 }
672                                 print $fh '&nbsp;' if ($DISPLAY_WORDS eq 'BOTH');
673                                 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'NUMBER'))
674                                 {
675                                         print $fh $NickData->{$nick}{'words_total'};
676                                 }
677                                 print $fh "</td>\n";
678                         }
679
680                         if ($DISPLAY_CHARS ne 'NONE')
681                         {
682                                 print $fh qq#    <td class="bar">#;
683                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'BAR'))
684                                 {
685                                         my $code = bar ($max_chars, $NickData->{$nick}{'chars'});
686                                         print $fh $code;
687                                 }
688                                 print $fh '&nbsp;' if ($DISPLAY_CHARS eq 'BOTH');
689                                 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'NUMBER'))
690                                 {
691                                         print $fh $NickData->{$nick}{'chars_total'};
692                                 }
693                                 print $fh "</td>\n";
694                         }
695
696                         if ($DISPLAY_TIMES)
697                         {
698                                 my $code = bar ($NickData->{$nick}{'chars_total'}, $NickData->{$nick}{'chars'});
699                                 print $fh qq#    <td class="bar">$code</td>\n#;
700                         }
701
702                         print $fh qq#    <td class="quote">$quote</td>\n#,
703                         qq#  </tr>\n#;
704                         
705                         if ($linescount == $LongLines)
706                         {
707                                 print $fh "</table>\n\n";
708                         }
709                 }
710
711                 # Ok, we have too many people to
712                 # list them all so we start a
713                 # smaller table and just list the
714                 # names.. (Six names per line..)
715                 elsif ($linescount <= ($LongLines + 6 * $ShortLines))
716                 {
717                         my $row_in_this_table = int (($linescount - $LongLines - 1) / 6);
718                         my $col_in_this_table = ($linescount - $LongLines - 1) % 6;
719
720                         my $total = 0;
721                         if ($SORT_BY eq 'LINES')
722                         {
723                                 $total = $NickData->{$nick}{'lines_total'};
724                         }
725                         elsif ($SORT_BY eq 'WORDS')
726                         {
727                                 $total = $NickData->{$nick}{'words_total'};
728                         }
729                         else # ($SORT_BY eq 'CHARS')
730                         {
731                                 $total = $NickData->{$nick}{'chars_total'};
732                         }
733
734                         my $title = $name ? get_realname ($name) : '';
735                         if (!$title)
736                         {
737                                 $title = "User: $name; " if ($name);
738                                 $title .= "Ident: $ident";
739                         }
740                         
741                         if ($row_in_this_table == 0 and $col_in_this_table == 0)
742                         {
743                                 $trans = translate ("They didn't write so much");
744                                 print $fh "<h2>$trans</h2>\n",
745                                 qq#<table class="small_ranking">\n#,
746                                 qq#  <tr>\n#;
747                         }
748                         
749                         if ($col_in_this_table == 0 and $row_in_this_table != 0)
750                         {
751                                 print $fh "  </tr>\n",
752                                 qq#  <tr>\n#;
753                         }
754                         
755                         print $fh qq#    <td title="$title">$print ($total)</td>\n#;
756                         
757                         if ($row_in_this_table == $ShortLines and $col_in_this_table == 5)
758                         {
759                                 print $fh "  </tr>\n",
760                                 qq#</table>\n\n#;
761                         }
762                 }
763
764                 # There is no else. There are
765                 # just too many people around.
766                 # I might add a "There are xyz
767                 # unmentioned nicks"-line..
768         }
769
770         if (($linescount > $LongLines)
771                         and ($linescount <= ($LongLines + 6 * $ShortLines)))
772         {
773                 my $col = ($linescount - $LongLines - 1) % 6;
774
775                 while ($col < 5)
776                 {
777                         print $fh qq#    <td>&nbsp;</td>\n#;
778                         $col++;
779                 }
780
781                 print $fh "  </tr>\n";
782         }
783
784         if ($linescount != $LongLines)
785         {
786                 print $fh "</table>\n\n";
787         }
788 }
789
790 # this is called by "&ranking ();" and prints the horizontal usage-bar in the
791 # detailed nick-table
792 sub bar
793 {
794         my $max_num = shift;
795         my $source = shift;
796
797         confess () unless (ref ($source) eq 'ARRAY');
798
799         # BAR_WIDTH is a least 10
800         my $max_width = $BAR_WIDTH - 4;
801         my $factor = 1;
802         my $retval = '';
803
804         my $i;
805         my $j;
806
807         if (!$max_num) { return ($retval); }
808         $factor = $max_width / $max_num;
809
810         for ($i = 0; $i < 4; $i++)
811         {
812                 my $sum = 0;
813                 my $width = 1;
814                 my $img = $H_IMAGES[$i];
815
816                 for ($j = 0; $j < 6; $j++)
817                 {
818                         my $hour = ($i * 6) + $j;
819                         $sum += $source->[$hour];
820                 }
821
822                 $width += int (0.5 + ($sum * $factor));
823                 
824                 $retval .= qq#<img src="$img" style="width: # . $width . q#px"#;
825                 if ($i == 0) { $retval .= qq# class="first"#; }
826                 elsif ($i == 3) { $retval .= qq# class="last"#; }
827                 $retval .= ' alt="" />';
828         }
829
830         return ($retval);
831 }
832
833 =head1 EXPORTED FUNCTIONS
834
835 =over 4
836
837 =item B<get_core_nick_counters> (I<$nick>)
838
839 Returns a hash-ref that containes all the nick-counters available. It looks
840 like this:
841
842     {
843         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)],
844         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)],
845         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)],
846         lines_total => 0,
847         words_total => 0,
848         chars_total => 0
849     }
850
851 =cut
852
853 sub get_core_nick_counters
854 {
855         my $nick = shift;
856
857         if (!defined ($NickData->{$nick}))
858         {
859                 return ({});
860         }
861
862         return ($NickData->{$nick});
863 }
864
865 =item B<get_sorted_nicklist> ()
866
867 Returns an array-ref that containes all nicks, sorted by the field given in the
868 config-file.
869
870 =cut
871
872 sub get_sorted_nicklist
873 {
874         return ($SortedNicklist);
875 }
876
877 =back
878
879 =head1 AUTHOR
880
881 Florian octo Forster, E<lt>octo at verplant.orgE<gt>
882
883 =cut