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