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

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

Fix stat cleanup too large

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