File Coverage

blib/lib/oEdtk/Tracking.pm
Criterion Covered Total %
statement 35 189 18.5
branch 1 70 1.4
condition 1 37 2.7
subroutine 11 20 55.0
pod 0 7 0.0
total 48 323 14.8


line stmt bran cond sub pod time code
1             package oEdtk::Tracking;
2              
3             my ($_TRACK_SIG, $_TRACK_TRK);
4              
5             BEGIN {
6             $SIG{'__WARN__'} = sub {
7 1         230 warn $_[0];
8 1 50 33     18 if (defined $_TRACK_TRK && $_TRACK_SIG=~/warn/i) {
9             # http://perldoc.perl.org/functions/warn.html
10 0         0 $_TRACK_TRK->track('Warn', 1, $_[0]);
11             }
12 1     1   1907 };
13              
14             $SIG{'__DIE__'} = sub {
15 6         20618 die $_[0];
16 0 0       0 if (defined $_TRACK_TRK) {
17 0         0 $_TRACK_TRK->track('Halt', 1, $_[0]);
18             }
19 1         30 };
20             }
21              
22              
23 1     1   9 use strict;
  1         2  
  1         40  
24 1     1   6 use warnings;
  1         104  
  1         54  
25              
26 1     1   6 use oEdtk::Main;
  1         2  
  1         503  
27 1     1   6 use oEdtk::Config qw(config_read);
  1         2  
  1         47  
28 1     1   6 use oEdtk::DBAdmin qw(db_connect create_table_TRACKING);
  1         1  
  1         52  
29 1     1   4 use oEdtk::Dict;
  1         1  
  1         27  
30 1     1   6 use Config::IniFiles;
  1         1  
  1         28  
31 1     1   5 use Sys::Hostname;
  1         2  
  1         57  
32 1     1   6 use DBI;
  1         1  
  1         29  
33              
34 1     1   5 use Exporter;
  1         2  
  1         2751  
35              
36             our $VERSION = 0.8022;
37             our @ISA = qw(Exporter);
38             our @EXPORT_OK = qw(stats_iddest stats_week stats_month);
39              
40              
41             sub new {
42 0     0 0   my ($class, $source, %params) = @_;
43 0   0       $source = $source || ($ARGV[1] || $ARGV[0]);
44 0 0         if ($source=~/^\-/){
45 0           $source = $ARGV[0];
46             }
47 0           my $cfg = config_read('EDTK_DB');
48              
49             # Load the dictionary to normalize entity names.
50 0           my $dict = oEdtk::Dict->new($cfg->{'EDTK_DICO'}, { invert => 1 });
51              
52 0           my $mode = uc($cfg->{'EDTK_TRACK_MODE'});
53 0 0         if ($mode eq 'NONE') {
54 0           warn "INFO : Tracking is currently disabled...\n";
55             # Return a dummy object if tracking is disabled.
56 0           return bless { dict => $dict, mode => $mode }, $class;
57             }
58              
59 0           my $table = $cfg->{'EDTK_DBI_TRACKING'};
60 0           my $dbh = db_connect($cfg, 'EDTK_DBI_DSN', { AutoCommit => 1 });
61              
62             # XXX Should we ensure there is at least one key defined?
63 0   0       my $keys = $params{'keys'} || [];
64              
65 0 0         if (@$keys > $cfg->{'EDTK_MAX_USER_KEY'}) {
66 0           die "ERROR: too many tracking keys: got " . @$keys . ", max " .
67             $cfg->{'EDTK_MAX_USER_KEY'};
68             }
69              
70             # Check that all the keys are at most 8 characters long, and otherwise
71             # truncate them. Also ensure we don't have the same key several times.
72 0           my %seen = ();
73 0           my @userkeys = ();
74 0           foreach (@$keys) {
75 0           my $key = uc($_);
76 0 0         if (length($key) > 8) {
77 0           $key =~ s/^(.{8}).*$/$1/;
78 0           warn "INFO : column \"\U$_\E\" too long, truncated to \"$key\"\n";
79             }
80 0 0         if (exists($seen{$key})) {
81 0           die "ERROR: duplicate column \"$key\"";
82             }
83 0           push(@userkeys, $key);
84 0           $seen{$key} = 1;
85             }
86              
87             # Extract application name from the script name.
88 0           my $app = $0;
89 0           $app =~ s/^.*?[\/\\]?([A-Z0-9-_]+)\.pl$/$1/;
90 0 0         if (length($app) > 20) {
91 0           $app =~ s/\.pl$//i;
92 0           $app =~ /(.{20})$/;
93 0           warn "INFO : application name \"$app\" too long, truncated to \"$1\"\n";
94 0           $app = $1;
95             }
96              
97             # Validate the editing mode.
98 0           my $edmode = _validate_edmode($params{'edmode'});
99              
100             # Limit username length to 10 characters per the table schema.
101 0   0       my $user = $params{'user'} || 'None';
102 0 0         if (length($user) > 10) {
103 0           $user =~ s/^(.{10}).*$/$1/;
104 0           warn "INFO : username \"$params{'user'}\" too long, truncated to \"$user\"\n";
105             }
106              
107             # Truncate if necessary, by taking at most 128 characters on the right.
108 0 0         if (length($source) > 128) {
109 0           $source = substr($source, -128, 128);
110             }
111              
112 0           my $self = bless {
113             dict => $dict,
114             mode => $mode,
115             table=> $table,
116             edmode=>$edmode,
117             id => oe_ID_LDOC(),
118             seq => 1,
119             keys => \@userkeys,
120             user => $user,
121             source=>$source,
122             app => $app,
123             dbh => $dbh
124             }, $class;
125              
126 0   0       my $entity = $params{'entity'} || $cfg->{'EDTK_CORP'};
127 0           $self->set_entity($entity);
128              
129             # Create the table in the SQLite case.
130 0 0         if ($dbh->{'Driver'}->{'Name'} eq 'SQLite') {
131 0           eval { create_table_TRACKING($dbh, $table, $cfg->{'EDTK_MAX_USER_KEY'}); };
  0            
132 0 0         if ($@) {
133 0           warn "INFO : Could not create table : $@\n";
134             }
135             }
136              
137 0           $self->track('Job', 1, join (' ', @ARGV)); # conserver le join pour placer tous les parametres libres dans la zone de message
138 0 0 0       if (defined $cfg->{'EDTK_TRACK_SIG'} && $cfg->{'EDTK_TRACK_SIG'}!~/no/i) {
139 0           $_TRACK_SIG = $cfg->{'EDTK_TRACK_SIG'};
140 0           warn "INFO : tracking catchs SIG messages -> '$_TRACK_SIG' set ('warn' for all, 'halt' for die only)\n";
141 0           $_TRACK_TRK = $self;
142             }
143 0           return $self;
144             }
145              
146              
147             sub track {
148 0     0 0   my ($self, $job, $count, @data) = @_;
149              
150 0 0         return if $self->{'mode'} eq 'NONE';
151              
152 0   0       $count ||= 1;
153              
154 0           my @usercols = @{$self->{'keys'}};
  0            
155 0 0         if (@data > (@usercols +1)) {
156             # max is @usercols nbcol + 1 for message col
157 0           warn "INFO : Too much values : got " . @data . ", expected " . (@usercols +1) . " maximum\n";
158             }
159              
160             # Validate the job event.
161 0           $job = _validate_event($job);
162              
163             # GENERATE SQL REQUEST.
164 0           my $values = {
165             ED_TSTAMP => oe_now_time(),
166             ED_USER => $self->{'user'},
167             ED_SEQ => $self->{'seq'}++,
168             ED_SNGL_ID => $self->{'id'},
169             ED_APP => $self->{'app'},
170             ED_MOD_ED => $self->{'edmode'},
171             ED_JOB_EVT => $job,
172             ED_OBJ_COUNT => $count,
173             ED_CORP => $self->{'entity'},
174             ED_SOURCE => $self->{'source'},
175             ED_HOST => hostname()
176             };
177              
178 0           foreach my $i (0 .. $#data) {
179             # ajout d'une colonne message pour gérer les messages et les warning
180             # pour assurer la compatibilité avec l'existant on va inverser
181             # les data pour mettre le message en tête en attendant le job_evt
182             ################## PBM DONNEES NON ALIMENTEES A REGARDER DE PRES
183 0   0       my $val = $data[$i] || "";
184 0   0       $values->{'ED_MESSAGE'} = $val . " " . ($values->{'ED_MESSAGE'} || "");
185              
186             # s'il n'y a qu'une data, on s'assure de ne pas la mettre inutilement dans une colonne utilisateur
187 0 0         if ($#data > 0) {
188 0 0 0       if (defined($data[$i]) && length($data[$i]) > 128) {
189 0           warn "INFO : \"$data[$i]\" truncated to 128 characters\n";
190 0           $data[$i] =~ s/^(.{128}).*$/$1/;
191             }
192 0           $values->{"ED_K${i}_NAME"} = $usercols[$i];
193 0           $values->{"ED_K${i}_VAL"} = $val;
194             }
195             }
196              
197 0 0 0       if ($job eq 'W' || $job eq 'H') { # Halt or Warn event
    0          
198             # si le job_evt est 'Warning' ou 'Halt' on gère les messages et la source
199 0           $values->{'ED_MESSAGE'} =~ s/\s+/ /g;
200 0           $values->{'ED_MESSAGE'} =~ s/^(.{256}).*$/$1/;
201 0 0         $values->{'ED_SOURCE'} = $self->{'source'} if ($job eq 'H');
202              
203             } elsif ($job eq 'J') { # JOB event
204 0           $values->{'ED_SOURCE'} = $self->{'source'};
205              
206             } else {
207 0           undef ($values->{'ED_MESSAGE'});
208             }
209              
210 0           my @cols = keys(%$values);
211 0           my $table = $self->{'table'};
212 0           my $sql = "INSERT INTO $table (" . join(', ', @cols) . ") VALUES (" .
213             join(', ', ('?') x @cols) . ")";
214              
215 0           my $dbh = $self->{'dbh'};
216 0           my $sth = $dbh->prepare($sql);
217 0 0         $sth->execute(values(%$values)) or die $sth->errstr;
218              
219 0 0         if (!$dbh->{'AutoCommit'}) {
220 0 0         $dbh->commit or die $dbh->errstr;
221             }
222             }
223              
224              
225             sub set_entity {
226 0     0 0   my ($self, $entity) = @_;
227              
228 0 0 0       if (!defined($entity) || length($entity) == 0) {
229 0           warn "INFO : Tracking::set_entity() called with an undefined entity!\n";
230 0           return;
231             }
232             # warn "INFO : translate >$entity< \n";
233 0           $entity =$self->{'dict'}->translate($entity);
234 0           $self->{'entity'} = $entity;
235             # warn $self->{'entity'}. " \$self->{'entity'}\n";
236             }
237              
238              
239             sub end {
240 0     0 0   my $self = shift;
241 0           $self->track('Halt', 1);
242             }
243              
244              
245             # Pour chaque application, pour chaque entité juridique, et pour chaque semaine
246             # le nombre de documents dans le tracking.
247             sub stats_week {
248             # passer les options par clefs de hash...
249 0     0 0   my ($dbh, $cfg, $start, $end, $excluded_users) = @_;
250              
251 0           my $table = $cfg->{'EDTK_STATS_TRACKING'};
252 0           my $innersql = "SELECT ED_CORP, ED_APP, "
253             . "'S' || TO_CHAR(TO_DATE(ED_TSTAMP, 'YYYYMMDDHH24MISS'), 'IW') AS ED_WEEK "
254             . "FROM $table "
255             . "WHERE ED_JOB_EVT = 'D' AND ED_TSTAMP >= ? ";
256 0           my @vals = ($start);
257 0 0         if (defined($end)) {
258 0           $innersql .= " AND ED_TSTAMP <= ? ";
259 0           push(@vals, $end);
260             }
261              
262 0 0         if (defined $excluded_users ) {
263 0           my @excluded = split (/,\s*/, $excluded_users);
264 0           for (my $i =0 ; $i <= $#excluded ; $i++ ){
265 0           $innersql .= " AND ED_USER != ? ";
266             }
267 0           push(@vals, @excluded);
268             }
269              
270 0           my $sql = "SELECT i.ED_CORP, i.ED_APP, i.ED_WEEK, COUNT(*) AS ED_COUNT " .
271             "FROM ($innersql) i GROUP BY ED_CORP, ED_APP, ED_WEEK ";
272              
273             # warn "\nINFO : $sql \n";
274             #SELECT i.ED_CORP, i.ED_APP, i.ED_WEEK, COUNT(*) AS ED_COUNT
275             # FROM (
276             # SELECT ED_CORP, ED_APP, 'S' || TO_CHAR(TO_DATE(ED_TSTAMP, 'YYYYMMDDHH24MISS'), 'IW') AS ED_WEEK
277             # FROM EDTK_TRACKING_2010 WHERE ED_JOB_EVT = 'D' AND ED_TSTAMP >= '20101212'
278             # ) i
279             # GROUP BY ED_CORP, ED_APP, ED_WEEK;
280              
281 0           my $rows = $dbh->selectall_arrayref($sql, { Slice => {} }, @vals);
282             # use Data::Dumper;
283             # print Dumper($rows);
284             # {
285             # 'ED_COUNT' => '4',
286             # 'ED_APP' => 'FUS-AC007',
287             # 'ED_CORP' => 'CPLTR',
288             # 'ED_WEEK' => 'S51'
289             # },
290              
291 0           return $rows;
292             }
293              
294              
295             sub stats_iddest {
296             # passer les options par clefs de hash...
297 0     0 0   my ($dbh, $cfg, $start, $end, $excluded_users, $ed_app) = @_;
298              
299 0           my $table = $cfg->{'EDTK_STATS_TRACKING'};
300 0           my $innersql = "SELECT ED_CORP, ED_K1_VAL AS ED_EMET, ED_K0_VAL AS ED_IDDEST, ED_APP, "
301             . "'S' || TO_CHAR(TO_DATE(ED_TSTAMP, 'YYYYMMDDHH24MISS'), 'IW') AS ED_WEEK "
302             . "FROM $table "
303             . "WHERE ED_JOB_EVT = 'D' AND ED_TSTAMP >= ? ";
304 0           my @vals = ($start);
305 0 0         if (defined($end)) {
306 0           $innersql .= " AND ED_TSTAMP <= ? ";
307 0           push(@vals, $end);
308             }
309              
310 0 0         if (defined $excluded_users ) {
311 0           my @excluded = split (/,\s*/, $excluded_users);
312 0           for (my $i =0 ; $i <= $#excluded ; $i++ ){
313 0           $innersql .= " AND ED_USER != ? ";
314             }
315 0           push(@vals, @excluded);
316             }
317              
318 0 0         if (defined $ed_app ) {
319 0           $innersql .= " AND ED_APP = ? ";
320 0           push(@vals, $ed_app);
321             }
322              
323              
324 0           my $sql = "SELECT i.ED_CORP, i.ED_EMET, i.ED_IDDEST, i.ED_APP, i.ED_WEEK, COUNT(*) AS ED_COUNT " .
325             "FROM ($innersql) i GROUP BY i.ED_CORP, i.ED_EMET, i.ED_IDDEST, i.ED_APP, i.ED_WEEK ";
326            
327             # warn "INFO : $sql \n";
328             # warn "INFO : @vals \n";
329             # SELECT i.ED_CORP, i.ED_SECTION, i.ED_IDDEST, i.ED_APP, i.ED_WEEK, COUNT(*) AS ED_COUNT
330             # FROM (
331             # SELECT ED_CORP, ED_K1_VAL AS ED_SECTION, ED_K0_VAL AS ED_IDDEST, ED_APP,
332             # 'S' || TO_CHAR(TO_DATE(ED_TSTAMP, 'YYYYMMDDHH24MISS'), 'IW') AS ED_WEEK
333             # FROM EDTK_TRACKING_2010 WHERE ED_JOB_EVT = 'D' AND ED_TSTAMP >= ?
334             # AND ED_TSTAMP <= ? AND ED_USER != ? AND ED_APP = ? ) i
335             # GROUP BY i.ED_CORP, i.ED_SECTION, i.ED_IDDEST, i.ED_APP, i.ED_WEEK
336              
337 0           my $rows = $dbh->selectall_arrayref($sql, { Slice => {} }, @vals);
338             # use Data::Dumper;
339             # print Dumper($rows);
340             # {
341             # 'ED_COUNT' => '2',
342             # 'ED_APP' => 'CTP-AC001',
343             # 'ED_IDDEST' => '0000428193',
344             # 'ED_CORP' => 'CORP_1',
345             # 'ED_WEEK' => 'S50',
346             # 'ED_EMET' => 'P004'
347             # },
348              
349 0           return $rows;
350             }
351              
352              
353             # Pour chaque application, pour chaque E.R., pour chaque entité juridique
354             # et pour chaque mois, le nombre de documents dans le tracking.
355             sub stats_month {
356 0     0 0   my ($dbh, $cfg, $start, $end, $excluded_users) = @_;
357              
358 0           my $table = $cfg->{'EDTK_STATS_TRACKING'};
359 0           my $innersql = "SELECT ED_APP, ED_CORP, ED_K1_VAL AS ED_EMET, "
360             . "'M' || TO_CHAR(TO_DATE(ED_TSTAMP, 'YYYYMMDDHH24MISS'), 'MM') AS ED_MONTH "
361             . "FROM $table WHERE ED_JOB_EVT = 'D' AND ED_TSTAMP >= ? "; # AND ED_K1_NAME = 'SECTION'
362 0           my @vals = ($start);
363              
364 0 0         if (defined($end)) {
365 0           $innersql .= " AND ED_TSTAMP <= ? ";
366 0           push(@vals, $end);
367             }
368            
369 0 0         if (defined $excluded_users ) {
370 0           my @excluded = split (/,\s*/, $excluded_users);
371 0           for (my $i =0 ; $i <= $#excluded ; $i++ ){
372 0           $innersql .= " AND ED_USER != ? ";
373             }
374 0           push(@vals, @excluded);
375             }
376              
377 0           my $sql = "SELECT i.ED_APP, i.ED_CORP, i.ED_EMET, i.ED_MONTH, COUNT(*) AS ED_COUNT " .
378             "FROM ($innersql) i GROUP BY ED_APP, ED_CORP, ED_EMET, ED_MONTH ";
379              
380 0           my $rows = $dbh->selectall_arrayref($sql, { Slice => {} }, @vals);
381              
382             # use Data::Dumper;
383             # print Dumper($rows);
384             # 'ED_MONTH' => 'M12',
385             # 'ED_COUNT' => '1',
386             # 'ED_CORP' => 'CORP_1',
387             # 'ED_APP' => 'DEV-CAMELEON',
388             # 'ED_EMET' => '37043'
389              
390 0           return $rows;
391             }
392              
393              
394              
395             #my $_PRGNAME;
396              
397             sub _validate_event {
398             # Job Event : looking for one of the following :
399             # Job (default), Spool, Document, Line, Warning, Error, Halt (critic), Reject
400 0     0     my $job = shift;
401              
402 0 0         warn "INFO : Halt event in Tracking = $job\n" if ($job =~/^H/);
403 0 0 0       if (!defined $job || $job !~ /^([JSDLWEHR])/) {
404 0 0         die "ERROR: Invalid job event : " . (defined $job ? $job : '(undef)') . "\n"
405             . "\t valid events are : Job / Spool / Document / Line / Warning / Reject / Error / Halt (critic)\n"
406             ;
407             }
408 0           return $1;
409             }
410              
411             #{
412             #my $_edmode;
413             #
414             # sub display_edmode {
415             # if (!defined $_edmode) {
416             # $_edmode = _validate_edmode(shift);
417             # }
418             # return $_edmode;
419             # }
420              
421             sub _validate_edmode {
422             # Printing Mode : looking for one of the following :
423             # Undef (default), Batch, Tp, Web, Mail, probinG
424 0     0     my $edmode = shift;
425            
426 0 0 0       if (!defined $edmode || $edmode !~ /^([BTMWG])/) {
427 0           return 'U';
428             }
429 0           return $1;
430             }
431             #}
432              
433             1;