8ef72393475544e9a058fe80c9b9d3bf146fda37
[psist.git] / Psist.pm
1 # $HeadURL: http://svn.mzet.net/psist/Psist.pm $
2 # $Author: zet $
3 # $Date: 2006-08-24 15:39:25 +0200 (Thu, 24 Aug 2006) $
4 # $Revision: 12 $
5
6 package Psist;
7
8 use strict;
9 use Time::Local;
10
11 sub new
12 {
13   my $type = shift;
14   my %args = @_;
15   my $self = {
16     profile => $args{profile},
17     outfile => $args{outfile},
18     silent  => $args{silent},
19     verbose => 1,
20     version => $args{version},
21     history => 30,
22
23     start   => time,
24     now     => 0,
25
26     jids    => {},
27     nicks   => {},
28     snicks  => [],
29     lines   => 0,
30     days    => [],
31     hours   => []
32   };
33   my ($mday,$mon,$year) = (localtime(time))[3,4,5];
34   $self->{now} = timelocal(0, 0, 0, $mday, $mon, $year);
35   
36   my $i = 0;
37   my $j = 0;
38   while($i < $self->{history}) {
39     while($j < 4) {
40       $self->{days}[$i][$j] = 0;
41       $j++;
42     }
43     $j = 0;
44     $i++;
45   }
46
47   $i = 0;
48   while($i < 24) {
49     $self->{hours}[$i] = 0;
50     $i++;
51   }
52
53   bless($self, $type);
54   return $self;
55 }
56
57 sub run
58 {
59   my $self = shift;
60   my $time;
61   
62   print "psist v$self->{version} - PSI Statistics Generator\n\n" unless $self->{silent};
63
64   $self->load_config();
65   $self->parse_history();
66   $self->gen_html();
67
68   $time = time - $self->{start};
69   print "\n",'DONE. Generated in ',sprintf('%02d:%02d:%02d', $time / 3600, ($time % 3600) / 60, ($time % 3600) % 60),"\n";
70 }
71
72 sub load_config
73 {
74   my $self = shift;
75   my $row;
76   my $i;
77   my $nick;
78   my $jid;
79   
80   print "loading contacts...\n" unless $self->{silent};
81   open(CONFIGXML, $self->{profile} . "/accounts.xml");
82   while($row = <CONFIGXML>) {
83     if($row =~ /<name type="QString">(.*?)<\/name>/) {
84         $nick = $1;
85     }
86     if($row =~ /<jid type="QString">(.*?)<\/jid>/) {
87       if(length($1) == 0) {
88           next;
89       }
90       if (length($nick) == 0) {
91           $nick = $jid;
92       }
93       $jid = $1;
94       $self->{jids}{$jid} = $nick;
95       $self->{nicks}{$nick}{lines} = 0;
96       $self->{nicks}{$nick}{linest}[0] = 0;
97       $self->{nicks}{$nick}{linest}[1] = 0;
98       $self->{nicks}{$nick}{linest}[2] = 0;
99       $self->{nicks}{$nick}{linest}[3] = 0;
100       $self->{nicks}{$nick}{to} = 0;
101       $self->{nicks}{$nick}{sfrom} = 0;
102       $self->{nicks}{$nick}{sto} = 0;
103       $self->{nicks}{$nick}{last} = 0;
104     }
105   }
106   close(CONFIGXML);
107 }
108
109 sub parse_history
110 {
111   my $self = shift;
112   my ($jidf,$nick);
113   my ($row,$ts,$tsfull,$dago,$dp);
114
115   print "parsing history data...\n" unless $self->{silent};
116   foreach my $jid (keys %{$self->{jids}}) {
117     $nick = $self->{jids}{$jid};
118     $jidf = $jid;
119     $jidf =~ s/@/_at_/;
120     $jidf =~ s/\+/%2b/;
121     open(HISTFIL, $self->{profile} . '/history/' . $jidf . '.history') or next;
122     print '  >> "',$jid,'" (',$nick,')',"\n" if $self->{verbose};
123     while($row = <HISTFIL>) {
124
125       # row processing
126       if($row =~ /^\|(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\|1\|([a-z]+)\|.{4}\|(.*)/) {
127         $ts = timelocal(0, 0, 0, $3, $2 - 1, $1);
128         $tsfull = timelocal($6, $5, $4, $3, $2 - 1, $1);
129         $dp = ($4 < 6) ? 0 : (($4 < 12) ? 1 : (($4 < 18) ? 2 : 3));
130         $dago = int(($self->{now} - $ts) / 86400);
131         if($dago < $self->{history}) {
132           $self->{days}[$dago][$dp]++;
133         }
134         $self->{hours}[$4]++;
135         $self->{lines}++;
136
137         $self->{nicks}{$nick}{lines}++;
138         $self->{nicks}{$nick}{linest}[$dp]++;
139         if($7 eq 'to') {
140           $self->{nicks}{$nick}{to}++;
141         }
142         if($self->{nicks}{$nick}{last} < $tsfull) {
143           $self->{nicks}{$nick}{last} = $tsfull;
144         }
145       }
146     }
147     close(HISTFIL);
148   }
149   
150   print "sorting contacts by lines count...\n" unless $self->{silent};
151   @{$self->{snicks}} = sort { $self->{nicks}{$b}{lines} <=> $self->{nicks}{$a}{lines} } keys %{$self->{nicks}};
152 }
153
154 sub gen_html
155 {
156   my $self = shift;
157
158   my ($sec,$min,$hour,$mday,$mon,$year) = (localtime(time));
159   my ($max_d,$max_h,$sum_h,$i,$sum);
160   my ($time,$nick);
161   
162   $mon++;
163   $year += 1900;
164
165   # max for days
166   $max_d = 1;
167   $i = $self->{history};
168   while($i > 0) {
169     $i--;
170     $sum = $self->{days}[$i][0] + $self->{days}[$i][1] + $self->{days}[$i][2] + $self->{days}[$i][3];
171     $max_d = ($sum > $max_d) ? $sum : $max_d;
172   }
173
174   # max and sum for hours
175   $max_h = 0;
176   $sum_h = 0;
177   $i = 0;
178   while($i < 24) {
179     $sum_h += $self->{hours}[$i];
180     $max_h = ($self->{hours}[$i] > $max_h) ? $self->{hours}[$i] : $max_h;
181     $i++;
182   }
183   $max_h = ($max_h == 0) ? 1 : $max_h;
184   $sum_h = ($sum_h == 0) ? 1 : $sum_h;
185   
186
187   print "generating HTML file...\n" unless $self->{silent};
188   open(HTMLFIL, '> '.$self->{outfile});
189   print HTMLFIL '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',"\n";
190   print HTMLFIL '<html>',"\n";
191   print HTMLFIL '<head>',"\n";
192   print HTMLFIL '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',"\n";
193   print HTMLFIL '<title>',$self->translate('PSI Statistics'),'</title>',"\n";
194   print HTMLFIL '<link type="text/css" rel="stylesheet" href="style.css" media="screen,projection" />',"\n";
195   print HTMLFIL '</head>',"\n";
196   print HTMLFIL '<body>',"\n";
197   print HTMLFIL '<div align="center">',"\n";
198   print HTMLFIL '<span class="title">',$self->translate('PSI Statistics'),'</span><br /><br />',"\n";
199   print HTMLFIL $self->translate('Statistics have been generated at '),'"',sprintf('%02d.%02d.%d %02d:%02d:%02d', $mday, $mon, $year, $hour, $min, $sec),'"<br /><br /><br />',"\n";
200
201   # daily activity
202   print HTMLFIL '<div class="headtext">',$self->translate('Daily activity'),'</div>',"\n";
203   print HTMLFIL '<table border="0"><tr>',"\n";
204   $i = $self->{history};
205   while($i > 0) {
206     $i--;
207     $sum = $self->{days}[$i][0] + $self->{days}[$i][1] + $self->{days}[$i][2] + $self->{days}[$i][3];
208     print HTMLFIL '<td align="center" valign="bottom" class="asmall">',$sum,'<br />',"\n";
209     print HTMLFIL '<img src="./red-v.png" width="15" height="',int($self->{days}[$i][3] * 100 / $max_d),'" alt="',$sum,'" title="',$sum,'" /><br />',"\n";
210     print HTMLFIL '<img src="./yellow-v.png" width="15" height="',int($self->{days}[$i][2] * 100 / $max_d),'" alt="',$sum,'" title="',$sum,'" /><br />',"\n";
211     print HTMLFIL '<img src="./green-v.png" width="15" height="',int($self->{days}[$i][1] * 100 / $max_d),'" alt="',$sum,'" title="',$sum,'" /><br />',"\n";
212     print HTMLFIL '<img src="./blue-v.png" width="15" height="',int($self->{days}[$i][0] * 100 / $max_d),'" alt="',$sum,'" title="',$sum,'" /><br />',"\n";
213     print HTMLFIL '</td>',"\n";
214   }
215   print HTMLFIL '</tr><tr>',"\n";
216   $i = $self->{history};
217   while($i > 0) {
218     $i--;
219     print HTMLFIL '<td class="rankc10center" align="center">',$i,'</td>',"\n";
220   }
221   print HTMLFIL '</tr></table>',"\n";
222   print HTMLFIL '<table align="center" border="0" width="520"><tr>',"\n";
223   print HTMLFIL '<td align="center" class="asmall"><img src="./blue-h.png" width="40" height="15" align="middle" alt="0-5" /> = 0-5</td>',"\n";
224   print HTMLFIL '<td align="center" class="asmall"><img src="./green-h.png" width="40" height="15" align="middle" alt="6-11" /> = 6-11</td>',"\n";
225   print HTMLFIL '<td align="center" class="asmall"><img src="./yellow-h.png" width="40" height="15" align="middle" alt="12-17" /> = 12-17</td>',"\n";
226   print HTMLFIL '<td align="center" class="asmall"><img src="./red-h.png" width="40" height="15" align="middle" alt="18-23" /> = 18-23</td>',"\n";
227   print HTMLFIL '</tr></table><br />',"\n";
228   
229   # most active times
230   print HTMLFIL '<div class="headtext">',$self->translate('Most active times'),'</div>',"\n";
231   print HTMLFIL '<table border="0"><tr>',"\n";
232   $i = 0;
233   while($i < 24) {
234     print HTMLFIL '<td align="center" valign="bottom" class="asmall">',sprintf('%.1f', $self->{hours}[$i] * 100 / $sum_h),'%<br /><img src="./',(($i < 6) ? 'blue' : (($i < 12) ? 'green' : (($i < 18) ? 'yellow' : 'red' ))),'-v.png" width="15" height="',int($self->{hours}[$i] * 100 / $max_h),'" alt="',$self->{hours}[$i],'" title="',$self->{hours}[$i],'"/></td>',"\n";
235     $i++;
236   }
237   print HTMLFIL '</tr><tr>',"\n";
238   $i = 0;
239   while($i < 24) {
240     print HTMLFIL '<td class="',(($self->{hours}[$i] == $max_h) ? 'hi' : ''),'rankc10center" align="center">',$i,'</td>',"\n";
241     $i++;
242   }
243   print HTMLFIL '</tr></table><br />',"\n";
244
245   # contacts
246   print HTMLFIL '<div class="headtext">',$self->translate('Contacts'),'</div>',"\n";
247   print HTMLFIL '<table border="0" width="794">',"\n";
248   print HTMLFIL '<tr><td>&nbsp;</td><td class="tdtop" width="25%">',$self->translate('Nick'),'</td><td class="tdtop" style="text-align: right" width="12%">',$self->translate('Posts'),'</td><td class="tdtop" width="65">',$self->translate('Time'),'</td><td class="tdtop" width="10%">',$self->translate('To'),'</td><td class="tdtop" width="10%">',$self->translate('From'),'</td><td class="tdtop" width="25%">',$self->translate('Last message'),'</td></tr>',"\n";
249   $i = 0;
250   foreach $nick (@{$self->{snicks}}) {
251     $i++;
252     if($self->{nicks}{$nick}{lines} > 0) {
253       $sum = $self->{nicks}{$nick}{linest}[0] + $self->{nicks}{$nick}{linest}[1] + $self->{nicks}{$nick}{linest}[2] + $self->{nicks}{$nick}{linest}[3];
254       $sum = ($sum > 0) ? $sum : 1;
255       my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($self->{nicks}{$nick}{last}));
256       $year += 1900;
257       $mon++;
258       print HTMLFIL '<tr><td class="',(($i == 1) ? 'hi' : ''),'rankc">',$i,'</td><td style="background-color: #babadc">', $nick, '</td><td style="background-color: #babadc; text-align: right">',$self->{nicks}{$nick}{lines},'</td><td style="background-color: #babadc; text-align: center">';
259       print HTMLFIL '<img src="./blue-h.png" border="0" width="',int($self->{nicks}{$nick}{linest}[0] * 60 / $sum),'" height="15" alt="',$self->{nicks}{$nick}{linest}[0],'" title="',$self->{nicks}{$nick}{linest}[0],'" />';
260       print HTMLFIL '<img src="./green-h.png" border="0" width="',int($self->{nicks}{$nick}{linest}[1] * 60 / $sum),'" height="15" alt="',$self->{nicks}{$nick}{linest}[1],'" title="',$self->{nicks}{$nick}{linest}[1],'" />';
261       print HTMLFIL '<img src="./yellow-h.png" border="0" width="',int($self->{nicks}{$nick}{linest}[2] * 60 / $sum),'" height="15" alt="',$self->{nicks}{$nick}{linest}[2],'" title="',$self->{nicks}{$nick}{linest}[2],'" />';
262       print HTMLFIL '<img src="./red-h.png" border="0" width="',int($self->{nicks}{$nick}{linest}[3] * 60 / $sum),'" height="15" alt="',$self->{nicks}{$nick}{linest}[3],'" title="',$self->{nicks}{$nick}{linest}[3],'" />';
263       print HTMLFIL '</td><td style="background-color: #babadc">',sprintf('%.2f', $self->{nicks}{$nick}{to} * 100 / $self->{nicks}{$nick}{lines}),'%</td><td style="background-color: #babadc">',sprintf('%.2f', 100 - $self->{nicks}{$nick}{to} * 100 / $self->{nicks}{$nick}{lines}),'%</td>';
264       print HTMLFIL '<td style="background-color: #babadc">',sprintf('%02d.%02d.%d %02d:%02d:%02d', $mday, $mon, $year, $hour, $min, $sec),'</td>';
265       print HTMLFIL '</tr>',"\n";
266     }
267   }
268   print HTMLFIL '</table><br />',"\n";
269   
270   # footer
271   print HTMLFIL $self->translate('Total number of lines'),': ',$self->{lines},'<br /><br />',"\n";
272   print HTMLFIL '<span class="small">',"\n";
273   print HTMLFIL $self->translate('Stats generated by'),' <a href="http://www.mzet.net/psist" class="background">psist</a> v',$self->{version},'<br />',"\n";
274   print HTMLFIL 'psist by <a href="http://www.mzet.net/" class="background">Michal Zbortek (zet)</a><br />',"\n";
275   $time = time - $self->{start};
276   print HTMLFIL $self->translate('Stats generated in'),' ',sprintf('%02d:%02d:%02d', $time / 3600, ($time % 3600) / 60, ($time % 3600) % 60),"\n";
277   print HTMLFIL '</span>',"\n";
278   
279   print HTMLFIL '</div>',"\n";
280   print HTMLFIL '</body>',"\n";
281   print HTMLFIL '</html>',"\n";
282   close(HTMLFIL);
283 }
284
285 sub translate
286 {
287   my $self = shift;
288   my $message = shift;
289   
290   return $message;
291 }
292
293 return 1;
294