line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package App::JobLog::Log::Synopsis; |
2
|
|
|
|
|
|
|
$App::JobLog::Log::Synopsis::VERSION = '1.039'; |
3
|
|
|
|
|
|
|
# ABSTRACT: consolidates App::JobClock::Log::Event objects for display |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
|
6
|
2
|
|
|
2
|
|
1304
|
use Exporter 'import'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
172
|
|
7
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
8
|
|
|
|
|
|
|
collect |
9
|
|
|
|
|
|
|
MERGE_ALL |
10
|
|
|
|
|
|
|
MERGE_ADJACENT |
11
|
|
|
|
|
|
|
MERGE_ADJACENT_SAME_TAGS |
12
|
|
|
|
|
|
|
MERGE_SAME_TAGS |
13
|
|
|
|
|
|
|
MERGE_SAME_DAY |
14
|
|
|
|
|
|
|
MERGE_SAME_DAY_SAME_TAGS |
15
|
|
|
|
|
|
|
MERGE_NONE |
16
|
|
|
|
|
|
|
); |
17
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
18
|
|
|
|
|
|
|
merge => [ |
19
|
|
|
|
|
|
|
qw( |
20
|
|
|
|
|
|
|
MERGE_ALL |
21
|
|
|
|
|
|
|
MERGE_ADJACENT |
22
|
|
|
|
|
|
|
MERGE_ADJACENT_SAME_TAGS |
23
|
|
|
|
|
|
|
MERGE_SAME_TAGS |
24
|
|
|
|
|
|
|
MERGE_SAME_DAY |
25
|
|
|
|
|
|
|
MERGE_SAME_DAY_SAME_TAGS |
26
|
|
|
|
|
|
|
MERGE_NONE |
27
|
|
|
|
|
|
|
) |
28
|
|
|
|
|
|
|
] |
29
|
|
|
|
|
|
|
); |
30
|
|
|
|
|
|
|
|
31
|
2
|
|
|
2
|
|
11
|
use Modern::Perl; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
18
|
|
32
|
2
|
|
|
2
|
|
343
|
use autouse 'Carp' => qw(carp); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
18
|
|
33
|
2
|
|
|
2
|
|
192
|
use autouse 'App::JobLog::Time' => qw(now); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
7
|
|
34
|
2
|
|
|
2
|
|
160
|
use Class::Autouse qw(DateTime); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
15
|
|
35
|
2
|
|
|
2
|
|
111
|
no if $] >= 5.018, warnings => "experimental::smartmatch"; |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
29
|
|
36
|
|
|
|
|
|
|
|
37
|
2
|
|
|
2
|
|
145
|
use constant MERGE_ALL => 1; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
128
|
|
38
|
2
|
|
|
2
|
|
10
|
use constant MERGE_ADJACENT => 2; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
129
|
|
39
|
2
|
|
|
2
|
|
12
|
use constant MERGE_ADJACENT_SAME_TAGS => 3; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
95
|
|
40
|
2
|
|
|
2
|
|
10
|
use constant MERGE_SAME_TAGS => 4; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
119
|
|
41
|
2
|
|
|
2
|
|
9
|
use constant MERGE_SAME_DAY => 5; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
91
|
|
42
|
2
|
|
|
2
|
|
10
|
use constant MERGE_SAME_DAY_SAME_TAGS => 6; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
112
|
|
43
|
2
|
|
|
2
|
|
9
|
use constant MERGE_NONE => 0; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
4860
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# takes in a bunch of App::JobClock::Log::Event objects |
47
|
|
|
|
|
|
|
# returns a bunch of App::JobClock::Log::Synopsis objects |
48
|
|
|
|
|
|
|
sub collect { |
49
|
7940
|
|
|
7940
|
1
|
13861
|
my ( $day, $merge_level ) = @_; |
50
|
7940
|
|
|
|
|
10629
|
my ( @synopses, $previous, @current_day ); |
51
|
7940
|
|
|
|
|
10113
|
for my $e ( @{ $day->events }, @{ $day->vacation } ) { |
|
7940
|
|
|
|
|
22377
|
|
|
7940
|
|
|
|
|
20538
|
|
52
|
200
|
|
|
|
|
295
|
my $do_merge = 0; |
53
|
200
|
|
|
|
|
280
|
my $mergand = $previous; |
54
|
200
|
100
|
|
|
|
478
|
if ($previous) { |
55
|
124
|
|
|
|
|
213
|
for ($merge_level) { |
56
|
124
|
|
|
|
|
165
|
when (MERGE_ALL) { $do_merge = 1 } |
|
0
|
|
|
|
|
0
|
|
57
|
124
|
|
|
|
|
165
|
when (MERGE_ADJACENT) { $do_merge = $previous->adjacent($e) } |
|
0
|
|
|
|
|
0
|
|
58
|
124
|
|
|
|
|
155
|
when (MERGE_SAME_TAGS) { |
59
|
0
|
|
|
|
|
0
|
for my $o (@synopses) { |
60
|
0
|
0
|
|
|
|
0
|
if ( $o->same_tags($e) ) { |
61
|
0
|
|
|
|
|
0
|
$mergand = $o; |
62
|
0
|
|
|
|
|
0
|
$do_merge = 1; |
63
|
0
|
|
|
|
|
0
|
last; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
} |
67
|
124
|
|
|
|
|
148
|
when (MERGE_SAME_DAY) { $do_merge = 1 } |
|
0
|
|
|
|
|
0
|
|
68
|
124
|
|
|
|
|
144
|
when (MERGE_SAME_DAY_SAME_TAGS) { |
69
|
0
|
|
|
|
|
0
|
for my $s (@current_day) { |
70
|
0
|
0
|
|
|
|
0
|
if ( $s->same_tags($e) ) { |
71
|
0
|
|
|
|
|
0
|
$do_merge = 1; |
72
|
0
|
|
|
|
|
0
|
$mergand = $s; |
73
|
0
|
|
|
|
|
0
|
last; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
} |
77
|
124
|
|
|
|
|
172
|
when (MERGE_ADJACENT_SAME_TAGS) { |
78
|
124
|
|
33
|
|
|
264
|
$do_merge = $previous->adjacent($e) |
79
|
|
|
|
|
|
|
&& $previous->same_tags($e) |
80
|
|
|
|
|
|
|
} |
81
|
0
|
|
|
|
|
0
|
when (MERGE_NONE) { $do_merge = 0 } |
|
0
|
|
|
|
|
0
|
|
82
|
0
|
|
|
|
|
0
|
default { carp 'unfamiliar merge level' }; |
|
0
|
|
|
|
|
0
|
|
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# keep vacation and regular events apart |
87
|
200
|
|
66
|
|
|
665
|
$do_merge &&= ref $mergand->last_event eq ref $e; |
88
|
|
|
|
|
|
|
|
89
|
200
|
100
|
|
|
|
417
|
if ($do_merge) { |
90
|
124
|
|
|
|
|
258
|
$mergand->merge($e); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
else { |
93
|
76
|
|
|
|
|
263
|
$previous = _new( $e, $merge_level ); |
94
|
76
|
|
|
|
|
211
|
push @synopses, $previous; |
95
|
76
|
|
|
|
|
205
|
push @current_day, $previous; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
7940
|
|
|
|
|
45970
|
$day->{synopses} = \@synopses; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# test to make sure this and the given event |
102
|
|
|
|
|
|
|
sub same_tags { |
103
|
124
|
|
|
124
|
0
|
9728
|
my ( $self, $event ) = @_; |
104
|
124
|
|
|
|
|
263
|
for my $e ( $self->events ) { |
105
|
|
|
|
|
|
|
return 0 |
106
|
217
|
|
|
|
|
620
|
unless $e->all_tags( @{ $event->tags } ) |
107
|
217
|
50
|
33
|
|
|
260
|
&& $event->all_tags( @{ $e->tags } ); |
|
217
|
|
|
|
|
565
|
|
108
|
|
|
|
|
|
|
} |
109
|
124
|
|
|
|
|
572
|
return 1; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub same_day { |
113
|
0
|
|
|
0
|
0
|
0
|
my ( $self, $event ) = @_; |
114
|
0
|
|
|
|
|
0
|
my $d1 = ( $self->events )[-1]->end; |
115
|
0
|
|
|
|
|
0
|
my $d2 = $event->start; |
116
|
|
|
|
|
|
|
return |
117
|
0
|
|
0
|
|
|
0
|
$d1->day == $d2->day |
118
|
|
|
|
|
|
|
&& $d1->month == $d2->month |
119
|
|
|
|
|
|
|
&& $d1->year == $d2->year; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# whether given event is immediately adjacent to last event in synopsis |
123
|
|
|
|
|
|
|
sub adjacent { |
124
|
124
|
|
|
124
|
0
|
166
|
my ( $self, $event ) = @_; |
125
|
124
|
100
|
|
|
|
410
|
return 1 if !$event->can('end'); # notes are always considered adjacent |
126
|
93
|
|
33
|
|
|
1130
|
my $d1 = ( $self->events )[-1]->end || now; |
127
|
93
|
|
|
|
|
3975
|
my $d2 = $event->start; |
128
|
93
|
|
|
|
|
335
|
return DateTime->compare( $d1, $d2 ) == 0; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# add an event to the events described |
132
|
124
|
|
|
124
|
0
|
138
|
sub merge { push @{ $_[0]{events} }, $_[1] } |
|
124
|
|
|
|
|
448
|
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
|
135
|
0
|
|
|
0
|
1
|
0
|
sub date { $_[0]->{events}[0]->start } |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub description { |
139
|
76
|
|
|
76
|
1
|
130
|
my ($self) = @_; |
140
|
76
|
50
|
|
|
|
195
|
unless ( exists $self->{description} ) { |
141
|
76
|
|
|
|
|
89
|
my ( %seen, @descriptions ); |
142
|
76
|
|
|
|
|
185
|
for my $e ( $self->events ) { |
143
|
200
|
|
|
|
|
225
|
for my $d ( @{ $e->data->description } ) { |
|
200
|
|
|
|
|
568
|
|
144
|
200
|
50
|
|
|
|
611
|
unless ( $seen{$d} ) { |
145
|
200
|
|
|
|
|
466
|
$seen{$d} = 1; |
146
|
200
|
|
|
|
|
283
|
chomp $d; # got newline from log |
147
|
200
|
|
|
|
|
522
|
push @descriptions, $d; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
} |
151
|
76
|
|
|
|
|
147
|
my $s = $descriptions[0]; |
152
|
76
|
|
|
|
|
189
|
for my $d ( @descriptions[ 1 .. $#descriptions ] ) { |
153
|
124
|
50
|
|
|
|
492
|
$s .= $s =~ /\w$/ ? '; ' : ' '; |
154
|
124
|
|
|
|
|
233
|
$s .= $d; |
155
|
|
|
|
|
|
|
} |
156
|
76
|
|
|
|
|
350
|
$self->{description} = $s; |
157
|
|
|
|
|
|
|
} |
158
|
76
|
|
|
|
|
281
|
return $self->{description}; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub tags { |
163
|
76
|
|
|
76
|
1
|
142
|
my ($self) = @_; |
164
|
76
|
|
|
|
|
106
|
my %seen; |
165
|
76
|
|
|
|
|
130
|
my $s = ''; |
166
|
76
|
|
|
|
|
190
|
for my $e ( $self->events ) { |
167
|
200
|
|
|
|
|
238
|
for my $t ( @{ $e->tags } ) { |
|
200
|
|
|
|
|
559
|
|
168
|
12
|
|
|
|
|
126
|
$seen{$t} = 1; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} |
171
|
76
|
|
|
|
|
392
|
return ( sort keys %seen ); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub tag_string { |
176
|
87
|
|
|
87
|
1
|
132
|
my ($self) = @_; |
177
|
|
|
|
|
|
|
$self->{tag_string} = join ', ', $self->tags |
178
|
87
|
100
|
|
|
|
330
|
unless exists $self->{tag_string}; |
179
|
87
|
|
|
|
|
375
|
return $self->{tag_string}; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
|
183
|
733
|
|
|
733
|
1
|
873
|
sub events { @{ $_[0]->{events} } } |
|
733
|
|
|
|
|
2505
|
|
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
186
|
124
|
|
|
124
|
1
|
265
|
sub last_event { ( $_[0]->events )[-1] } |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# constructs a single-event synopsis |
189
|
|
|
|
|
|
|
# NOTE: not a package method |
190
|
|
|
|
|
|
|
sub _new { |
191
|
76
|
|
|
76
|
|
174
|
my ( $event, $merge_level ) = @_; |
192
|
76
|
50
|
33
|
|
|
422
|
carp 'requires event argument' |
193
|
|
|
|
|
|
|
unless $event && $event->isa('App::JobLog::Log::Note'); |
194
|
76
|
|
|
|
|
1346
|
my ( $one_interval, $one_day ); |
195
|
76
|
|
|
|
|
194
|
for ($merge_level) { |
196
|
76
|
|
|
|
|
173
|
when (MERGE_ALL) { ( $one_interval, $one_day ) = ( 0, 0 ) } |
|
0
|
|
|
|
|
0
|
|
197
|
76
|
|
|
|
|
146
|
when (MERGE_ADJACENT) { ( $one_interval, $one_day ) = ( 1, 1 ) } |
|
0
|
|
|
|
|
0
|
|
198
|
76
|
|
|
|
|
111
|
when (MERGE_ADJACENT_SAME_TAGS) { |
199
|
65
|
|
|
|
|
156
|
( $one_interval, $one_day ) = ( 1, 1 ) |
200
|
|
|
|
|
|
|
} |
201
|
11
|
|
|
|
|
39
|
when (MERGE_SAME_TAGS) { ( $one_interval, $one_day ) = ( 0, 0 ) } |
|
0
|
|
|
|
|
0
|
|
202
|
11
|
|
|
|
|
42
|
when (MERGE_SAME_DAY) { ( $one_interval, $one_day ) = ( 0, 1 ) } |
|
0
|
|
|
|
|
0
|
|
203
|
11
|
|
|
|
|
60
|
when (MERGE_SAME_DAY_SAME_TAGS) { |
204
|
0
|
|
|
|
|
0
|
( $one_interval, $one_day ) = ( 0, 1 ) |
205
|
|
|
|
|
|
|
} |
206
|
11
|
|
|
|
|
40
|
when (MERGE_NONE) { ( $one_interval, $one_day ) = ( 1, 1 ) } |
|
11
|
|
|
|
|
94
|
|
207
|
|
|
|
|
|
|
} |
208
|
76
|
|
|
|
|
850
|
return bless { |
209
|
|
|
|
|
|
|
events => [$event], |
210
|
|
|
|
|
|
|
one_interval => $one_interval, |
211
|
|
|
|
|
|
|
one_day => $one_day |
212
|
|
|
|
|
|
|
}, |
213
|
|
|
|
|
|
|
__PACKAGE__; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
|
217
|
88
|
|
|
88
|
1
|
265
|
sub single_interval { $_[0]->{one_interval} } |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
|
220
|
0
|
|
|
0
|
1
|
0
|
sub single_day { $_[0]->{one_day} } |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub duration { |
224
|
88
|
|
|
88
|
1
|
144
|
my ($self) = @_; |
225
|
88
|
|
|
|
|
204
|
my @events = $self->events; |
226
|
88
|
50
|
|
|
|
362
|
return 0 unless $events[0]->can('end'); # notes have no duration |
227
|
88
|
50
|
|
|
|
1014
|
if ( $self->single_interval ) { |
228
|
88
|
|
|
|
|
164
|
my ( $se, $ee ) = ( $events[0], $events[$#events] ); |
229
|
88
|
|
66
|
|
|
271
|
my ( $start, $end ) = ( $se->start, $ee->end || now ); |
230
|
88
|
|
|
|
|
3856
|
return $end->epoch - $start->epoch; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
else { |
233
|
0
|
|
|
|
|
0
|
my $d = 0; |
234
|
0
|
|
|
|
|
0
|
$d += $_->duration for @events; |
235
|
0
|
|
|
|
|
0
|
return $d; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub time_fmt { |
241
|
152
|
|
|
152
|
1
|
284
|
my ($self) = @_; |
242
|
152
|
|
|
|
|
320
|
my @events = $self->events; |
243
|
152
|
|
|
|
|
323
|
my ( $se, $ee ) = ( $events[0], $events[$#events] ); |
244
|
152
|
100
|
100
|
|
|
584
|
if ( @events == 1 && !$se->can('end') ) { # single note |
245
|
2
|
|
|
|
|
28
|
return $se->start->strftime('%l:%M %P'); |
246
|
|
|
|
|
|
|
} |
247
|
150
|
100
|
|
|
|
1085
|
my ( $start, $end ) = |
248
|
|
|
|
|
|
|
( $se->start, $ee->can('end') ? $ee->end : $ee->start ); |
249
|
150
|
|
|
|
|
231
|
my $s; |
250
|
150
|
100
|
|
|
|
485
|
if ($end) { |
251
|
138
|
50
|
33
|
|
|
5661
|
return 'vacation' |
252
|
|
|
|
|
|
|
if ref $se eq 'App::JobLog::Vacation::Period' && !$se->fixed; |
253
|
138
|
|
66
|
|
|
414
|
my $same_period = $start->hour < 12 && $end->hour < 12 |
254
|
|
|
|
|
|
|
|| $start->hour >= 12 && $end->hour >= 12; |
255
|
138
|
100
|
66
|
|
|
2398
|
if ( $same_period |
|
|
|
100
|
|
|
|
|
256
|
|
|
|
|
|
|
&& $start->hour == $end->hour |
257
|
|
|
|
|
|
|
&& $start->minute == $end->minute ) |
258
|
|
|
|
|
|
|
{ |
259
|
62
|
|
|
|
|
1082
|
$s = $start->strftime('%l:%M %P'); |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
else { |
262
|
76
|
100
|
|
|
|
528
|
my ( $f1, $f2 ) = |
263
|
|
|
|
|
|
|
( $same_period ? '%l:%M' : '%l:%M %P', '%l:%M %P' ); |
264
|
76
|
|
|
|
|
280
|
$s = $start->strftime($f1) . ' - ' . $end->strftime($f2); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
else { |
268
|
12
|
|
|
|
|
123
|
$s = $start->strftime('%l:%M %P') . ' - ongoing'; |
269
|
|
|
|
|
|
|
} |
270
|
150
|
|
|
|
|
12642
|
$s =~ s/ / /; # strftime tends to add in an extra space |
271
|
150
|
|
|
|
|
443
|
$s =~ s/^ //; |
272
|
150
|
|
|
|
|
620
|
return $s; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
1; |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
__END__ |