Changed stylesheets to use relative scaling for horizontal bars.
[onis.git] / lib / Onis / Plugins / Conversations.pm
1 package Onis::Plugins::Conversations;
2
3 use strict;
4 use warnings;
5
6 use Exporter;
7
8 use Onis::Config qw(get_config);
9 use Onis::Html qw(get_filehandle);
10 use Onis::Language qw(translate);
11 use Onis::Data::Core qw(register_plugin get_main_nick nick_to_ident nick_to_name);
12 use Onis::Data::Persistent ();
13
14 =head1 NAME
15
16 Onis::Plugins::Conversations - Who talks with who
17
18 =head1 DESCRIPTION
19
20 This plugins tries to recignise conversations and counts the amount that people
21 talk to each other.
22
23 =cut
24
25 @Onis::Plugins::Conversations::EXPORT_OK = (qw(get_conversations));
26 @Onis::Plugins::Conversations::ISA = ('Exporter');
27
28 our $ConversationCache = Onis::Data::Persistent->new ('ConversationCache', 'partners', qw(time0 time1 time2 time3));
29 our $ConversationData = {};
30
31 our @HorizontalImages = qw#dark-theme/h-red.png dark-theme/h-blue.png dark-theme/h-yellow.png dark-theme/h-green.png#;
32
33 if (get_config ('horizontal_images'))
34 {
35         my @tmp = get_config ('horizontal_images');
36         my $i;
37         
38         if (scalar (@tmp) != 4)
39         {
40                 print STDERR $/, __FILE__, ": The number of horizontal images is not four. The output might look weird.", $/;
41         }
42
43         for ($i = 0; $i < 4; $i++)
44         {
45                 next unless (defined ($tmp[$i]));
46                 $HorizontalImages[$i] = $tmp[$i];
47         }
48 }
49
50 our $NumConversations = 10;
51 if (get_config ('conversations_number'))
52 {
53         my $tmp = get_config ('conversations_number');
54         $tmp =~ s/\D//g;
55         $NumConversations = $tmp if ($tmp);
56 }
57
58 register_plugin ('TEXT', \&add);
59 register_plugin ('OUTPUT', \&output);
60
61 my $VERSION = '$Id: Conversations.pm,v 1.7 2004/09/15 19:42:04 octo Exp $';
62 print STDERR $/, __FILE__, ": $VERSION" if ($::DEBUG);
63
64 return (1);
65
66 sub add
67 {
68         my $data = shift;
69         my $text = $data->{'text'};
70         my $nick = $data->{'nick'};
71         my $ident = $data->{'ident'};
72
73         my $time = int ($data->{'hour'} / 6);
74
75         # <taken from lib/Onis/Plugins/Nicks.pm>
76         my @potential_nicks = split (/[^\w\`\~\^\-\|\[\]]+/, $text);
77         my $talk_to = '';
78         
79         for (@potential_nicks)
80         {
81                 my $other_nick = $_;
82                 my $other_ident = nick_to_ident ($other_nick);
83                 
84                 if ($other_ident)
85                 {
86                         $talk_to = $other_nick;
87                         last;
88                 }
89         }
90         # </taken>
91         
92         if ($talk_to)
93         {
94                 my $key = "$nick:$talk_to";
95                 my @data = $ConversationCache->get ($key);
96                 @data = (0, 0, 0, 0) unless (@data);
97
98                 my $chars = length ($text);
99
100                 $data[$time] += $chars;
101                 
102                 $ConversationCache->put ($key, @data);
103         }
104 }
105
106 sub calculate
107 {
108         for ($ConversationCache->keys ())
109         {
110                 my $key = $_;
111                 my ($nick_from, $nick_to) = split (m/:/, $key);
112                 my @data = $ConversationCache->get ($key);
113
114                 $nick_from = get_main_nick ($nick_from);
115                 $nick_to   = get_main_nick ($nick_to);
116
117                 next if (!$nick_from or !$nick_to);
118                 next if ($nick_from eq $nick_to);
119
120                 if (!defined ($ConversationData->{$nick_from}{$nick_to}))
121                 {
122                         $ConversationData->{$nick_from}{$nick_to} =
123                         {
124                                 total => 0,
125                                 nicks =>
126                                 {
127                                         $nick_from => [0, 0, 0, 0],
128                                         $nick_to   => [0, 0, 0, 0]
129                                 }
130                         };
131                         $ConversationData->{$nick_to}{$nick_from} = $ConversationData->{$nick_from}{$nick_to};
132                 }
133
134                 for (my $i = 0; $i < 4; $i++)
135                 {
136                         $ConversationData->{$nick_from}{$nick_to}{'nicks'}{$nick_from}[$i] += $data[$i];
137                         $ConversationData->{$nick_from}{$nick_to}{'total'} += $data[$i];
138                 }
139         }
140 }
141
142 sub get_top
143 {
144         my $num = shift;
145         my @data = ();
146
147         for (keys %$ConversationData)
148         {
149                 my $nick0 = $_;
150
151                 for (keys %{$ConversationData->{$nick0}})
152                 {
153                         my $nick1 = $_;
154                         next unless ($nick0 lt $nick1);
155
156                         push (@data, [$ConversationData->{$nick0}{$nick1}{'total'}, $nick0, $nick1]);
157                 }
158         }
159
160         @data = sort { $b->[0] <=> $a->[0] } (@data);
161         splice (@data, $num) if (scalar (@data) > $num);
162
163         return (@data);
164 }
165
166 sub output
167 {
168         calculate ();
169
170         my $fh = get_filehandle ();
171         my $title = translate ('Conversation partners');
172
173         my $max_num = 0;
174
175         my @data = get_top ($NumConversations);
176         return (undef) unless (@data);
177
178         for (@data)
179         {
180                 my $nick0 = $_->[1];
181                 my $nick1 = $_->[2];
182                 my $rec = $ConversationData->{$nick0}{$nick1};
183
184                 my $sum0 = 0;
185                 my $sum1 = 0;
186
187                 for (my $i = 0; $i < 4; $i++)
188                 {
189                         $sum0 += $rec->{'nicks'}{$nick0}[$i];
190                         $sum1 += $rec->{'nicks'}{$nick1}[$i];
191                 }
192
193                 $max_num = $sum0 if ($max_num < $sum0);
194                 $max_num = $sum1 if ($max_num < $sum1);
195         }
196         
197         print $fh <<EOF;
198 <table class="plugin conversations">
199   <tr>
200     <th colspan="2">$title</th>
201   </tr>
202 EOF
203         foreach (@data)
204         {
205                 my $nick0 = $_->[1];
206                 my $nick1 = $_->[2];
207                 my $name0 = nick_to_name ($nick0) || $nick0;
208                 my $name1 = nick_to_name ($nick1) || $nick1;
209                 my $rec = $ConversationData->{$nick0}{$nick1};
210
211                 print $fh <<EOF;
212   <tr>
213     <td class="nick left">$name0</td>
214     <td class="nick right">$name1</td>
215   </tr>
216   <tr>
217 EOF
218
219                 print $fh '    <td class="bar horizontal left">';
220                 for (my $i = 3; $i >= 0; $i--)
221                 {
222                         my $width = sprintf ("%.2f", 95 * $rec->{'nicks'}{$nick0}[$i] / $max_num);
223                         my $image = $HorizontalImages[$i];
224                         my $class = '';
225
226                         if    ($i == 3) { $class = qq# class="first"#; }
227                         elsif ($i == 0) { $class = qq# class="last"#;  }
228
229                         print $fh qq#<img src="$image" style="width: $width%;"$class alt="" />#;
230                 }
231                 print $fh qq#</td>\n    <td class="bar horizontal right">#;
232                 for (my $i = 0; $i < 4; $i++)
233                 {
234                         my $width = sprintf ("%.2f", 95 * $rec->{'nicks'}{$nick1}[$i] / $max_num);
235                         my $image = $HorizontalImages[$i];
236                         my $class = '';
237
238                         if    ($i == 0) { $class = qq# class="first"#; }
239                         elsif ($i == 3) { $class = qq# class="last"#;  }
240
241                         print $fh qq#<img src="$image" style="width: $width%;"$class alt="" />#;
242                 }
243                 print $fh "</td>\n  </tr>\n";
244         }
245
246         print $fh "</table>\n\n";
247 }
248
249 =head1 EXPORTED FUNCTIONS
250
251 =over 4
252
253 =item B<get_conversations> (I<$nick>)
254
255 Returns a hashref to the conversations this nick was involved with. The layout
256 is the following. I<$other> is the nick of the person I<$nick> chattet with.
257 The arrays hold the number of characters written by the nick used as the key.
258 The first field contains the characters for the hours 0-5, the second field
259 holds 6-11 and so on.
260
261   {
262     $other =>
263     {
264       total => 0,
265       nicks =>
266       {
267         $nick  => [0, 0, 0, 0],
268         $other => [0, 0, 0, 0]
269       }
270     },
271     ...
272   }
273
274 =cut
275
276 sub get_conversations
277 {
278         my $nick = shift;
279
280         if (!defined ($ConversationData->{$nick}))
281         {
282                 return ({});
283         }
284         else
285         {
286                 return ($ConversationData->{$nick});
287         }
288 }
289
290 =back
291
292 =head1 AUTHOR
293
294 Florian octo Forster, E<lt>octo at verplant.orgE<gt>
295
296 =cut