source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Sql/Stat.pm @ 1933

Last change on this file since 1933 was 1933, checked in by nanardon, 7 years ago

Add documentation about statistics

File size: 7.9 KB
Line 
1package LATMOS::Accounts::Bases::Sql::Stat;
2
3use 5.010000;
4use strict;
5use warnings;
6use LATMOS::Accounts::Log;
7
8use base qw(LATMOS::Accounts::Bases::Sql::objects);
9use LATMOS::Accounts::I18N;
10
11our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
12
13=head1 NAME
14
15LATMOS::Accounts::Bases::Sql::Site - Common Location/Address object
16
17=head1 DESCRIPTION
18
19Store common to many people office address (typically building location).
20
21=cut
22
23sub _object_table { 'stat' }
24
25sub _key_field { 'name' }
26
27sub _has_extended_attributes { 1 }
28
29sub _get_attr_schema {
30    my ($class, $base) = @_;
31
32    $class->SUPER::_get_attr_schema($base,
33        {
34            cn     => { ro => 1, inline => 1, iname => 'name' },
35            description => { },
36            otype  => { },
37            filter => { multiple => 1 },
38            attribute => { },
39            refFilter => { multiple => 1 },
40            refall => { },
41            nullValue => { },
42            aggregateFunction => {
43                can_values => sub { qw(sum avg count) },
44            },
45            display => { },
46            delay => { },
47            lastStatId => { },
48            needRun => {
49                ro => 1,
50                managed => 1,
51                get => sub {
52                    my ($attr) = @_;
53                    my ($self) = $attr->object;
54
55                    my $days = $self->_get_attributes('delay') || 7;
56                    my $sth = $self->db->prepare(
57                        q{
58                        select tstamp from statsentry where okey = ?
59                            and tstamp > now() - (? * '1 day'::interval)
60                            order by tstamp desc
61                            limit 1
62                        }
63                    );
64                    $sth->execute($self->Iid, $days);
65                    if (my $res = $sth->fetchrow_hashref) {
66                        $self->base->log(LA_DEBUG, "Stat %s done recently: %s", $self->id, $res->{tstamp});
67                        return undef;
68                    } else {
69                        return 1;
70                    }
71                },
72            },
73        }
74    )
75}
76
77=head2 compute()
78
79Compute stat value and return hash with var/val peer value
80
81=cut
82
83sub compute {
84    my ($self) = @_;
85
86    my $otype = $self->_get_attributes('otype') or return;
87    my $attribute = $self->_get_attributes('attribute') or return;
88
89    my %stats;
90    my $reffiltered = undef;
91
92    my $op = $self->_get_attributes('aggregateFunction');
93
94    my ($refOtype, $refAttr) = ($otype, undef);
95
96    foreach (split(/\./, $attribute)) {
97        if (my $attr = $self->base->attribute($refOtype, $_)) {
98            if (my $obj = $attr->reference) {
99                ($refOtype, $refAttr) = ($obj, $attr);
100            } else {
101                ($refOtype, $refAttr) = (undef, undef);
102            }
103        }
104    }
105
106    if ($refAttr && $refOtype) {
107        if (my @reffilter = $self->_get_attributes('refFilter')) {
108            $reffiltered = {};
109            foreach ($self->base->search_objects($refOtype, @reffilter)) {
110                $reffiltered->{$_} = 1;
111            }
112        }
113        if ($self->_get_attributes('refall') && $refAttr->has_values_list && !$op) {
114            foreach ($refAttr->can_values) {
115                if ($reffiltered && !$reffiltered->{$_}) {
116                    next;
117                }
118                $stats{$_} = 0;
119            } 
120        }
121    } else {
122        $self->base->log(LA_ERR, "Cannot find attribute %s for object type %s", $attribute, $otype);
123    }
124
125    my %filtered;
126    if (my @fil = $self->_get_attributes('filter')) {
127        %filtered = map { $_ => 1 } $self->base->search_objects($otype, @fil);
128    } else {
129        %filtered = map { $_ => 1 } $self->base->list_objects($otype);
130    }
131
132    my %results = $self->base->attributes_summary_by_object($otype, $attribute);
133
134    my $nullValue = $self->_get_attributes('nullValue');
135    my %aggdata;
136    if ($nullValue) {
137        $results{ $_ } ||= [''] foreach(keys %filtered);
138    }
139    foreach my $id (keys %results) {
140        $filtered{ $id } or next;
141        foreach (@{ $results{ $id }}) {
142            next unless (defined($_) || $nullValue);
143            if ($_ && $reffiltered && !$reffiltered->{$_}) {
144                next;
145            }
146            defined($_) && $_ ne '' or $_ = '(none)';
147            if ($op) {
148                push(@{$aggdata{$id}}, $_);
149            } else {
150                $stats{ $_ } ||= 0;
151                $stats{ $_ }++;
152            }
153        }
154    }
155    if ($op) {
156        foreach my $key (keys %aggdata) {
157            if ($op eq 'sum') {
158                my $sum = 0;
159                $sum += $_ foreach (@{ $aggdata{$key} });
160                $stats{ $key } = $sum;
161            }
162            elsif ($op eq 'count') {
163                $stats{ $key } = scalar(@{ $aggdata{$key} });
164            }
165            elsif ($op eq 'average') {
166                my $sum = 0;
167                $sum += $_ foreach (@{ $aggdata{$key} });
168                $stats{ $key } = $sum / scalar(@{ $aggdata{$key} });
169            }
170        }
171    }
172
173    return \%stats;
174}
175
176=head2 collect()
177
178Store statistics for $attribute on $otype. @filters allow to limit result on
179$otype object matching the filters
180
181=cut
182
183sub collect {
184    my ($self) = @_;
185
186    my $otype = $self->_get_attributes('otype') or return;
187    my $attribute = $self->_get_attributes('attribute') or return;
188
189    if (!$self->_get_attributes('needRun')) {
190        return;
191    }
192
193    $self->base->log(LA_NOTICE, "Collecting data for stat: %s", $self->id);
194
195    my $stats = $self->compute();
196
197    my $sthr = $self->db->prepare(
198        q{ INSERT INTO statsentry (okey) values (?) }
199    );
200    $sthr->execute($self->Iid);
201
202    my $id = $self->db->last_insert_id(undef,undef,'statsentry',undef);
203
204    my $sthv =  $self->db->prepare(
205        q{
206        INSERT INTO statvalues (sid, value, count) values (?,?,?)
207        }
208    );
209    foreach (keys %$stats) {
210        $sthv->execute($id, $_, $stats->{$_});
211    }
212    $self->_set_c_fields('lastStatId' => $id) or do {
213        $self->base->log(LA_ERR, "Cannot update Stat object");
214        return;
215    };
216
217    return 1;
218}
219
220=head2 getStat($id)
221
222Return the statistics value for this stat entry.
223
224If not given, C<$id> will be the last entry found
225
226=cut
227
228sub getStat {
229    my ($self, $id) = @_;
230
231    ($id) ||= $self->_get_attributes('lastStatId');
232
233    if (!$id) {
234        $self->base->log(LA_ERR, "No Stat Id given for stat name %s", $self->id);
235        return;
236    }
237
238    my $sthentry = $self->db->prepare('SELECT * from statsentry where okey = ? and id = ?');
239    $sthentry->execute($self->Iid, $id);
240    if (!(my $res = $sthentry->fetchrow_hashref)) {
241        $self->base->log(LA_ERR, "No stat entry %d found for stat %s", $id, $self->id);
242        return;
243    }
244
245    my $sthvalues = $self->db->prepare("SELECT * FROM statvalues where sid = ?");
246    $sthvalues->execute($id);
247
248    my $stats = {};
249    while (my $res = $sthvalues->fetchrow_hashref) {
250        $stats->{$res->{value}} = $res->{count};
251    }
252
253    $stats
254}
255
256=head2 getAllStat()
257
258Return a hashref containing all statistics value per date
259
260=cut
261
262sub getAllStat {
263    my ($self) = @_;
264
265    my $sth = $self->db->prepare(q{
266        SELECT count, value, to_char(tstamp, 'YYYY-MM-DD HH24:MI') as d from statsentry join
267                      statvalues
268            on statsentry.id = statvalues.sid
269            where statsentry.okey = ?
270                order by statsentry.tstamp, value
271    });
272    $sth->execute($self->Iid);
273
274    my $stats = {};
275    while(my $res = $sth->fetchrow_hashref) {
276        $stats->{$res->{d}}{$res->{value}} = $res->{count};
277    }
278
279    return $stats;
280}
281
2821;
283
284__END__
285
286=head1 SEE ALSO
287
288L<LATMOS::Accounts::Bases::Sql>
289
290=head1 AUTHOR
291
292Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
293
294=head1 COPYRIGHT AND LICENSE
295
296Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
297
298This library is free software; you can redistribute it and/or modify
299it under the same terms as Perl itself, either Perl version 5.10.0 or,
300at your option, any later version of Perl 5 you may have available.
301
302
303=cut
Note: See TracBrowser for help on using the repository browser.