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

Last change on this file since 1620 was 1581, checked in by nanardon, 9 years ago

Fix: with nullValues, ensure all filtered objects are count

File size: 7.7 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            name   => { ro => 1, inline => 1, },
35            cn     => { ro => 1, inline => 1, iname => 'name' },
36            date   => { ro => 1, inline => 1, },
37            create => { ro => 1, inline => 1, },
38            filter => { multiple => 1 },
39            refFilter => { multiple => 1 },
40            refall => { },
41            otype  => { },
42            attribute => { },
43            description => { },
44            delay => { },
45            lastStatId => { },
46            aggregateFunction => {
47                can_values => sub { qw(sum avg count) },
48            },
49            nullValue => { },
50            display => { },
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
82=cut
83
84sub compute {
85    my ($self) = @_;
86
87    my $otype = $self->_get_attributes('otype') or return;
88    my $attribute = $self->_get_attributes('attribute') or return;
89
90    my %stats;
91    my $reffiltered = undef;
92
93    my $op = $self->_get_attributes('aggregateFunction');
94    if (my $attr = $self->base->attribute($otype, $attribute)) {
95        if (my $obj = $attr->reference) {
96            if (my @reffilter = $self->_get_attributes('refFilter')) {
97                $reffiltered = {};
98                foreach ($self->base->search_objects($obj, @reffilter)) {
99                    $reffiltered->{$_} = 1;
100                }
101            }
102        }
103        if ($self->_get_attributes('refall') && $attr->has_values_list && !$op) {
104            foreach ($attr->can_values) {
105                if ($reffiltered && !$reffiltered->{$_}) {
106                    next;
107                }
108                $stats{$_} = 0;
109            } 
110        }
111    } else {
112        $self->base->log(LA_ERR, "Cannot find attribute %s for object type %s", $attribute, $otype);
113    }
114
115    my %filtered;
116    if (my @fil = $self->_get_attributes('filter')) {
117        %filtered = map { $_ => 1 } $self->base->search_objects($otype, @fil);
118    } else {
119        %filtered = map { $_ => 1 } $self->base->list_objects($otype);
120    }
121
122    my %results = $self->base->attributes_summary_by_object($otype, $attribute);
123
124    my $nullValue = $self->_get_attributes('nullValue');
125    my %aggdata;
126    if ($nullValue) {
127        $results{ $_ } ||= [''] foreach(keys %filtered);
128    }
129    foreach my $id (keys %results) {
130        $filtered{ $id } or next;
131        foreach (@{ $results{ $id }}) {
132            next unless (defined($_) || $nullValue);
133            if ($_ && $reffiltered && !$reffiltered->{$_}) {
134                next;
135            }
136            $_ ||= '(none)';
137            if ($op) {
138                push(@{$aggdata{$id}}, $_);
139            } else {
140                $stats{ $_ } ||= 0;
141                $stats{ $_ }++;
142            }
143        }
144    }
145    if ($op) {
146        foreach my $key (keys %aggdata) {
147            if ($op eq 'sum') {
148                my $sum = 0;
149                $sum += $_ foreach (@{ $aggdata{$key} });
150                $stats{ $key } = $sum;
151            }
152            elsif ($op eq 'count') {
153                $stats{ $key } = scalar(@{ $aggdata{$key} });
154            }
155            elsif ($op eq 'average') {
156                my $sum = 0;
157                $sum += $_ foreach (@{ $aggdata{$key} });
158                $stats{ $key } = $sum / scalar(@{ $aggdata{$key} });
159            }
160        }
161    }
162
163    return \%stats;
164}
165
166=head2 collect()
167
168Store statistics for $attribute on $otype. @filters allow to limit result on
169$otype object matching the filters
170
171=cut
172
173sub collect {
174    my ($self) = @_;
175
176    my $otype = $self->_get_attributes('otype') or return;
177    my $attribute = $self->_get_attributes('attribute') or return;
178
179    if (!$self->_get_attributes('needRun')) {
180        return;
181    }
182
183    $self->base->log(LA_NOTICE, "Collecting data for stat: %s", $self->id);
184
185    my $stats = $self->compute();
186
187    my $sthr = $self->db->prepare(
188        q{ INSERT INTO statsentry (okey) values (?) }
189    );
190    $sthr->execute($self->Iid);
191
192    my $id = $self->db->last_insert_id(undef,undef,'statsentry',undef);
193
194    my $sthv =  $self->db->prepare(
195        q{
196        INSERT INTO statvalues (sid, value, count) values (?,?,?)
197        }
198    );
199    foreach (keys %$stats) {
200        $sthv->execute($id, $_, $stats->{$_});
201    }
202    $self->_set_c_fields('lastStatId' => $id) or do {
203        $self->base->log(LA_ERR, "Cannot update Stat object");
204        return;
205    };
206
207    return 1;
208}
209
210=head2 getStat($id)
211
212Return the statistics value for this stat entry.
213
214If not given, C<$id> will be the last entry found
215
216=cut
217
218sub getStat {
219    my ($self, $id) = @_;
220
221    ($id) ||= $self->_get_attributes('lastStatId');
222
223    if (!$id) {
224        $self->base->log(LA_ERR, "No Stat Id given for stat name %s", $self->id);
225        return;
226    }
227
228    my $sthentry = $self->db->prepare('SELECT * from statsentry where okey = ? and id = ?');
229    $sthentry->execute($self->Iid, $id);
230    if (!(my $res = $sthentry->fetchrow_hashref)) {
231        $self->base->log(LA_ERR, "No stat entry %d found for stat %s", $id, $self->id);
232        return;
233    }
234
235    my $sthvalues = $self->db->prepare("SELECT * FROM statvalues where sid = ?");
236    $sthvalues->execute($id);
237
238    my $stats = {};
239    while (my $res = $sthvalues->fetchrow_hashref) {
240        $stats->{$res->{value}} = $res->{count};
241    }
242
243    $stats
244}
245
246=head2 getAllStat()
247
248Return a hashref containing all statistics value per date
249
250=cut
251
252sub getAllStat {
253    my ($self) = @_;
254
255    my $sth = $self->db->prepare(q{
256        SELECT count, value, to_char(tstamp, 'YYYY-MM-DD HH24:MI') as d from statsentry join
257                      statvalues
258            on statsentry.id = statvalues.sid
259            where statsentry.okey = ?
260                order by statsentry.tstamp, value
261    });
262    $sth->execute($self->Iid);
263
264    my $stats = {};
265    while(my $res = $sth->fetchrow_hashref) {
266        $stats->{$res->{d}}{$res->{value}} = $res->{count};
267    }
268
269    return $stats;
270}
271
2721;
273
274__END__
275
276=head1 SEE ALSO
277
278L<LATMOS::Accounts::Bases::Sql>
279
280=head1 AUTHOR
281
282Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
283
284=head1 COPYRIGHT AND LICENSE
285
286Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
287
288This library is free software; you can redistribute it and/or modify
289it under the same terms as Perl itself, either Perl version 5.10.0 or,
290at your option, any later version of Perl 5 you may have available.
291
292
293=cut
Note: See TracBrowser for help on using the repository browser.