File Coverage

blib/lib/App/bif/log.pm
Criterion Covered Total %
statement 41 184 22.2
branch 3 68 4.4
condition 0 7 0.0
subroutine 13 21 61.9
pod 2 10 20.0
total 59 290 20.3


line stmt bran cond sub pod time code
1             package App::bif::log;
2 6     6   1843 use strict;
  6         12  
  6         163  
3 6     6   30 use warnings;
  6         12  
  6         257  
4 6     6   30 use feature 'state';
  6         12  
  6         459  
5 6     6   35 use locale;
  6         11  
  6         40  
6 6     6   173 use Bif::Mo;
  6         17  
  6         46  
7 6     6   36 use utf8;
  6         16  
  6         47  
8 6     6   5777 use Text::Autoformat qw/autoformat/;
  6         306058  
  6         850  
9 6     6   5417 use Text::FormatTable;
  6         17998  
  6         274  
10 6     6   5003 use Time::Duration qw/ago/;
  6         12152  
  6         475  
11 6     6   44 use Time::Piece;
  6         15  
  6         65  
12 6     6   492 use Time::Seconds;
  6         16  
  6         18830  
13              
14             our $VERSION = '0.1.5_7';
15             extends 'App::bif';
16              
17             sub run {
18 1     1 1 3 my $self = shift;
19 1         4 my $opts = $self->opts;
20              
21 1 50       5 if ( $opts->{id} ) {
22 0         0 my $info = $self->get_node( $opts->{id} );
23 0         0 my $class = "App::bif::log::$info->{kind}";
24              
25 0 0       0 if ( eval "require $class" ) {
26             $opts->{path} = delete $opts->{id}
27 0 0       0 if ( $info->{kind} eq 'project' );
28              
29 0         0 return $class->new( db => $self->db, opts => $opts )->run;
30             }
31 0 0       0 die $@ if $@;
32             return $self->err( 'LogUnimplemented',
33 0         0 'cannnot log type: ' . $info->{kind} );
34             }
35              
36 1 50       5 $opts->{format} = 'short' if delete $opts->{short};
37              
38 1 50       10 return $self->run_by_full if $opts->{format} eq 'full';
39 0 0       0 return $self->run_by_short if $opts->{format} eq 'short';
40 0         0 return $self->err( 'FormatUnsupported',
41             "unsupported format type: $opts->{format}" );
42             }
43              
44             sub run_by_full {
45 1     1 0 3 my $self = shift;
46 1         4 my $opts = $self->opts;
47 1         8 my $db = $self->db();
48              
49 0           my @order;
50 0 0         if ( $opts->{order} eq 'time' ) {
    0          
51 0           @order = ( 'c.mtime DESC', 'c.id DESC' );
52             }
53             elsif ( $opts->{order} eq 'id' ) {
54 0           @order = ( 'c.id DESC', 'c.mtime DESC' );
55             }
56             else {
57 0           return $self->err( 'OrderUnsupported',
58             "unsupported order: $opts->{order}" );
59             }
60              
61 0           state $have_dbix = DBIx::ThinSQL->import(qw/ case concat coalesce sq qv/);
62              
63 0           my ( $yellow, $reset ) = $self->colours(qw/yellow reset/);
64 0           my $now = $self->now;
65              
66 0           my $sth = $db->xprepare(
67             with => 'b',
68             as => sq(
69             select => [ 'b.change_id AS start', 'b.change_id2 AS stop' ],
70             from => 'bifkv b',
71             where => { 'b.key' => 'last_sync' },
72             ),
73             select => [
74             '"c" || c.id AS change_id',
75             'n.path AS path',
76             'COALESCE(c.author,e.name) AS author',
77             "COALESCE(c.author_contact_method || ': ' || c.author_contact,"
78             . "ecm.method || ': ' || ecm.mvalue) AS contact",
79             't.title AS title',
80             'c.uuid AS change_uuid',
81             'c.action',
82             'c.mtime',
83             'c.mtimetz',
84             'c.mtimetzhm',
85             qq{$now - strftime('%s', c.mtime, 'unixepoch') AS mtime_age},
86             'c.message',
87             '0 AS depth',
88             concat(
89             'c.action',
90             case (
91             when => 'b.start',
92             then => qv( $yellow . ' [+]' . $reset ),
93             else => qv(''),
94             ),
95             )->as('action'),
96             ],
97             from => 'changes c',
98             left_join => 'change_deltas cd',
99             on => 'cd.change_id = c.id',
100             inner_join => 'entities e',
101             on => 'e.id = c.identity_id',
102             inner_join => 'entity_contact_methods ecm',
103             on => 'ecm.id = e.default_contact_method_id',
104             inner_join => 'identities i',
105             on => 'i.id = c.identity_id',
106             left_join => 'b',
107             on => 'c.id BETWEEN b.start+1 AND b.stop',
108             left_join => 'topics t',
109             on => 't.id = cd.action_node_id_1',
110             left_join => 'topic_status ts',
111             on => 'ts.id = t.topic_status_id',
112             left_join => 'projects p',
113             on => 'p.id = ts.project_id OR p.id = cd.action_node_id_1',
114             left_join => 'nodes n',
115             on => 'n.id = c.action_node_id_1',
116             order_by => \@order,
117             );
118              
119 0           $sth->execute;
120              
121 0           $self->start_pager;
122              
123 0           while ( my $row = $sth->hashref ) {
124 0           $self->log_comment( $row, 1 );
125             }
126              
127 0           return $self->ok('LogFull');
128             }
129              
130             sub run_by_short {
131 0     0 0   my $self = shift;
132 0 0         return $self->run_by_time if $self->opts->{order} eq 'time';
133 0 0         return $self->run_by_id if $self->opts->{order} eq 'id';
134             }
135              
136             sub run_by_time {
137 0     0 0   my $self = shift;
138 0           my $opts = $self->opts;
139 0           my $now = $self->now;
140 0           my $db = $self->db;
141 0           my ( $dark, $reset, $yellow ) = $self->colours(qw/yellow reset yellow/);
142              
143 0           state $have_thinsql = DBIx::ThinSQL->import(qw/concat coalesce qv case sq/);
144              
145 0           my $sth = $db->xprepare(
146             with => 'b',
147             as => sq(
148             select => [ 'b.change_id AS start', 'b.change_id2 AS stop' ],
149             from => 'bifkv b',
150             where => { 'b.key' => 'last_sync' },
151             ),
152             select => [
153             q{strftime('%H:%M:%S',c.mtime,'unixepoch','localtime')},
154             '"c" || c.id',
155             'COALESCE(c.author_shortname,i.shortname,e.name) AS author',
156             concat(
157             'c.action',
158             case (
159             when => 'b.start',
160             then => qv( $yellow . ' [+]' . $reset ),
161             else => qv(''),
162             )
163             ),
164             q{strftime('%Y-%m-%d',c.mtime,'unixepoch','localtime') AS mdate},
165             qq{$now - strftime('%s', c.mtime, 'unixepoch')},
166             ],
167             from => 'changes c',
168             left_join => 'change_deltas cd',
169             on => 'cd.change_id = c.id',
170             inner_join => 'entities e',
171             on => 'e.id = c.identity_id',
172             inner_join => 'identities i',
173             on => 'i.id = c.identity_id',
174             left_join => 'b',
175             on => 'c.id BETWEEN b.start+1 AND b.stop',
176             left_join => 'topics t',
177             on => 't.id = cd.action_node_id_1',
178             left_join => 'topic_status ts',
179             on => 'ts.id = t.topic_status_id',
180             order_by => [ 'mdate DESC', 'c.mtime ASC', 'c.id DESC' ],
181             );
182              
183 0           $sth->execute;
184              
185 0           $self->start_pager;
186              
187 0           my @days = (
188             qw/Sunday Monday Tuesday Wednesday Thursday Friday
189             Saturday/
190             );
191              
192 0           my $localtime = localtime();
193 0           my $today = $localtime->strftime('%Y-%m-%d');
194 0           my $yesterday =
195             ( $localtime - ONE_HOUR * ( $localtime->hour + 12 ) )
196             ->strftime('%Y-%m-%d');
197              
198 0           my $mdate = '';
199 0           my $table = Text::FormatTable->new(' l l l l ');
200              
201 0           while ( my $n = $sth->arrayref ) {
202 0 0         if ( $n->[4] ne $mdate ) {
203 0 0         $table->rule(' ') if $mdate;
204 0           $mdate = $n->[4];
205 0 0         if ( $mdate eq $today ) {
    0          
206 0           $table->head(
207             $dark . $n->[4],
208             'Who',
209             'Action [today]',
210             'CID' . $reset,
211             );
212             }
213             elsif ( $mdate eq $yesterday ) {
214 0           $table->head(
215             $dark . $n->[4],
216             'Who',
217             'Action [yesterday]',
218             'CID' . $reset
219             );
220             }
221             else {
222 0           $table->head(
223             $dark . $n->[4],
224             'Who',
225             'Action [' . ago( $n->[5], 1 ) . ']',
226             'CID' . $reset,
227             );
228             }
229             }
230              
231 0           $table->row( $n->[0], $n->[2], $n->[3], $n->[1] );
232             }
233              
234 0           print $table->render . "\n";
235              
236 0           return $self->ok('LogTime');
237             }
238              
239             sub run_by_id {
240 0     0 0   my $self = shift;
241 0           my $opts = $self->opts;
242 0           my $now = $self->now;
243 0           my $db = $self->db;
244 0           my ( $dark, $reset, $yellow ) = $self->colours(qw/yellow reset yellow/);
245              
246 0           state $have_thinsql = DBIx::ThinSQL->import(qw/concat coalesce qv case sq/);
247              
248 0           my $sth = $db->xprepare(
249             with => 'b',
250             as => sq(
251             select => [ 'b.change_id AS start', 'b.change_id2 AS stop' ],
252             from => 'bifkv b',
253             where => { 'b.key' => 'last_sync' },
254             ),
255             select => [
256             '"c" || c.id',
257             q{strftime('%Y-%m-%d-%H:%M:%S',c.mtime,'unixepoch','localtime')},
258             'COALESCE(c.author,i.shortname,e.name) AS author',
259             concat(
260             'c.action',
261             case (
262             when => 'b.start',
263             then => qv( $yellow . ' [+]' . $reset ),
264             else => qv(''),
265             )
266             ),
267             ],
268             from => 'changes c',
269             left_join => 'change_deltas cd',
270             on => 'cd.change_id = c.id',
271             inner_join => 'entities e',
272             on => 'e.id = c.identity_id',
273             inner_join => 'identities i',
274             on => 'i.id = c.identity_id',
275             left_join => 'b',
276             on => 'c.id BETWEEN b.start+1 AND b.stop',
277             left_join => 'topics t',
278             on => 't.id = cd.action_node_id_1',
279             left_join => 'topic_status ts',
280             on => 'ts.id = t.topic_status_id',
281             left_join => 'projects p',
282             on => 'p.id = ts.project_id OR p.id = cd.action_node_id_1',
283             order_by => ['c.id DESC'],
284             );
285              
286 0           $sth->execute;
287              
288 0   0       my $data = $sth->arrayrefs || return $self->ok('LogUid');
289 0           $self->start_pager( scalar @$data );
290              
291 0           print $self->render_table( ' l l l l ',
292             [ 'CID', 'Date', 'Who', 'Action' ], $data );
293              
294 0           return $self->ok('LogID');
295             }
296              
297             sub reformat {
298 0     0 0   my $self = shift;
299 0           my $text = shift;
300 0   0       my $depth = shift || 0;
301              
302             # $depth-- if $depth;
303              
304 0           my $left = 2 + 2 * $depth;
305 0           my $indent = ' ' x $depth;
306              
307 0           my @result;
308              
309 0           foreach my $para ( split /\n\n/, $text ) {
310 0 0         if ( $para =~ m/^[^\s]/ ) {
311 0           push( @result, autoformat( $para, { left => $left } ) );
312             }
313             else {
314 0           $para =~ s/^/$indent/gm;
315 0           push( @result, $para, "\n\n" );
316             }
317             }
318              
319 0           return @result;
320             }
321              
322             my $title;
323             my $path;
324              
325             sub log_item {
326 0     0 0   my $self = shift;
327 0           my $row = shift;
328 0           my $type = shift;
329              
330 0           my $yellow = $self->colours('yellow');
331 0           my $dark = $self->colours('yellow');
332 0           my $reset = $self->colours('yellow');
333              
334 0           $title = $row->{title};
335 0           $path = $row->{path};
336              
337 0           ( my $id = $row->{change_id} ) =~ s/(.+)\./$yellow$1$dark\./;
338             my @data = (
339             $self->header(
340             $dark . $yellow . 'change',
341             $dark . $yellow . $row->{change_id},
342             $row->{change_uuid}
343 0           ),
344             );
345              
346 0           push( @data, $self->header( 'Action', $row->{action} ) );
347 0           push( @data, $self->header( 'From', $row->{author}, $row->{contact} ) );
348              
349             # push( @data, $self->header( 'To', $row->{path} ) ) if $row->{path};
350 0           push( @data, $self->header( 'When', $self->ctime_ago($row) ) );
351              
352 0 0 0       if ( $row->{title} and $row->{status} ) {
    0          
353 0           push( @data, $self->header( 'Subject', $row->{title} ) );
354             }
355             elsif ( $row->{title} ) {
356 0           push( @data,
357             $self->header( 'Subject', "[$row->{path}] $row->{title}" ) );
358             }
359              
360 0           foreach my $field (@_) {
361 0 0         next unless defined $field->[1];
362 0           push( @data, $self->header(@$field) );
363             }
364              
365 0           print $self->render_table( ' l l', undef, \@data ) . "\n";
366 0           print $self->reformat( $row->{message} ), "\n";
367              
368 0           return;
369             }
370              
371             my $last_change_id;
372             my $last_row;
373              
374             sub log_start {
375 0     0 0   my $self = shift;
376 0           $last_change_id = 'c0';
377 0           $last_row = undef;
378             }
379              
380             sub log_next {
381 0     0 0   my $self = shift;
382 0           my $row = shift;
383              
384 0           state @data;
385 0           state $yellow = $self->colours('yellow');
386 0           state $dark = $self->colours('dark');
387              
388             # Assume finish
389 0 0         if ( !$row ) {
390             print $self->render_table( 'l l', undef, \@data,
391 0           1 + 2 * ( $last_row->{depth} ) )
392             . "\n";
393              
394 0           print $self->reformat( $last_row->{message}, $last_row->{depth} ), "\n";
395 0           return;
396             }
397              
398 0 0         if ( $row->{change_id} ne $last_change_id ) {
399 0 0         if ($last_row) {
400             print $self->render_table( 'l l', undef, \@data,
401 0           1 + 2 * ( $last_row->{depth} ) )
402             . "\n";
403              
404 0           print $self->reformat( $last_row->{message}, $last_row->{depth} ),
405             "\n";
406             }
407              
408             @data = (
409             $self->header(
410             $dark . $yellow . 'change',
411             $dark . $yellow . $row->{change_id},
412             $row->{change_uuid},
413             ),
414             $self->header( 'Action', $row->{action} ),
415 0           $self->header( 'From', $row->{author}, $row->{contact} ),
416             $self->header( 'When', $self->mtime_ago($row) ),
417             );
418             push( @data,
419             $self->header( $dark . 'Delta', $dark . $row->{delta_value} ) )
420 0 0         if $row->{delta};
421             }
422             else {
423             push( @data,
424 0           $self->header( $dark . 'Delta', $dark . $row->{delta_value} ) );
425             }
426              
427 0           $last_change_id = $row->{change_id};
428 0           $last_row = $row;
429             }
430              
431             sub log_comment {
432 0     0 1   my $self = shift;
433 0           my $row = shift;
434 0           my $raw = shift;
435              
436 0           my @data;
437              
438 0           state $yellow = $self->colours('yellow');
439 0           state $dark = $self->colours('yellow');
440              
441             push(
442             @data,
443             $self->header(
444             $dark . $yellow . 'change',
445             $dark . $yellow . $row->{change_id},
446             $row->{change_uuid},
447 0           ),
448             );
449              
450 0           push( @data, $self->header( 'Action', $row->{action} ) );
451 0           push( @data, $self->header( 'From', $row->{author}, $row->{contact} ) );
452              
453 0 0         $path = $row->{path} if $row->{path};
454 0 0         push( @data, $self->header( 'To', $path ) ) if $path;
455 0           push( @data, $self->header( 'When', $self->mtime_ago($row) ) );
456              
457 0 0         if ( $row->{title} ) {
    0          
    0          
458 0 0         $title = $row->{title} if defined $row->{title};
459 0 0         push( @data, $self->header( 'Subject', "$title" ) ) if $title;
460             }
461             elsif ( $row->{status} ) {
462 0 0         push( @data, $self->header( 'Subject', "[$row->{status}] $title" ) )
463             if $title;
464             }
465             elsif ( !$raw ) {
466 0 0         push( @data, $self->header( 'Subject', "↪ $title" ) ) if $title;
467             }
468              
469 0           foreach my $field (@_) {
470 0 0         next unless defined $field->[1];
471 0           push( @data, $self->header(@$field) );
472             }
473              
474             print $self->render_table( 'l l', undef, \@data,
475 0           1 + 2 * ( $row->{depth} ) )
476             . "\n";
477              
478 0 0         if ( $row->{push_to} ) {
479 0           print "[Pushed to " . $row->{push_to} . "]\n\n\n";
480             }
481             else {
482 0           print $self->reformat( $row->{message}, $row->{depth} ), "\n";
483             }
484             }
485              
486             1;
487             __END__