| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package App::JobLog::Command::when; |
|
2
|
|
|
|
|
|
|
$App::JobLog::Command::when::VERSION = '1.040'; |
|
3
|
|
|
|
|
|
|
# ABSTRACT: when you'll be done for the day |
|
4
|
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
1643
|
use App::JobLog -command; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
15
|
|
|
6
|
2
|
|
|
2
|
|
736
|
use Modern::Perl; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
12
|
|
|
7
|
2
|
|
|
|
|
11
|
use Class::Autouse qw( |
|
8
|
|
|
|
|
|
|
App::JobLog::Log |
|
9
|
|
|
|
|
|
|
App::JobLog::Log::Day |
|
10
|
2
|
|
|
2
|
|
223
|
); |
|
|
2
|
|
|
|
|
4
|
|
|
11
|
2
|
|
|
2
|
|
122
|
use autouse 'App::JobLog::TimeGrammar' => qw(parse daytime); |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
13
|
|
|
12
|
2
|
|
|
2
|
|
148
|
use autouse 'Carp' => qw(carp); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
9
|
|
|
13
|
2
|
|
|
2
|
|
165
|
use autouse 'Getopt::Long::Descriptive' => qw(prog_name); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
8
|
|
|
14
|
2
|
|
|
|
|
8
|
use autouse 'App::JobLog::Config' => qw( |
|
15
|
|
|
|
|
|
|
columns |
|
16
|
|
|
|
|
|
|
is_hidden |
|
17
|
|
|
|
|
|
|
merge |
|
18
|
2
|
|
|
2
|
|
110
|
); |
|
|
2
|
|
|
|
|
4
|
|
|
19
|
2
|
|
|
|
|
9
|
use autouse 'App::JobLog::Log::Format' => qw( |
|
20
|
|
|
|
|
|
|
display |
|
21
|
|
|
|
|
|
|
single_interval |
|
22
|
|
|
|
|
|
|
summary |
|
23
|
2
|
|
|
2
|
|
177
|
); |
|
|
2
|
|
|
|
|
4
|
|
|
24
|
2
|
|
|
2
|
|
174
|
use autouse 'App::JobLog::Time' => qw(today now); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
9
|
|
|
25
|
|
|
|
|
|
|
|
|
26
|
2
|
|
|
2
|
|
158
|
use constant FULL_FORMAT => '%l:%M:%S %p on %A, %B %d, %Y'; |
|
|
2
|
|
|
|
|
11
|
|
|
|
2
|
|
|
|
|
104
|
|
|
27
|
2
|
|
|
2
|
|
9
|
use constant SAME_YEAR_FORMAT => '%l:%M:%S %p on %A, %B %d'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
99
|
|
|
28
|
2
|
|
|
2
|
|
10
|
use constant SAME_WEEK_FORMAT => '%l:%M:%S %p on %A'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
83
|
|
|
29
|
2
|
|
|
2
|
|
9
|
use constant SAME_DAY_FORMAT => '%l:%M:%S %p'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
1986
|
|
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub execute { |
|
32
|
0
|
|
|
0
|
1
|
|
my ( $self, $opt, $args ) = @_; |
|
33
|
|
|
|
|
|
|
|
|
34
|
0
|
|
0
|
|
|
|
my $tags = $opt->tag || []; |
|
35
|
0
|
|
0
|
|
|
|
my $excluded_tags = $opt->exclude_tag || []; |
|
36
|
0
|
|
0
|
|
|
|
my $match = $opt->match || []; |
|
37
|
0
|
|
0
|
|
|
|
my $no_match = $opt->no_match || []; |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# validate regexes, if any, while generating test |
|
40
|
|
|
|
|
|
|
|
|
41
|
0
|
|
|
|
|
|
my $test = |
|
42
|
|
|
|
|
|
|
App::JobLog::Command::summary::_make_test( $tags, $excluded_tags, $match, |
|
43
|
|
|
|
|
|
|
$no_match, undef ); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# parse time expression |
|
46
|
0
|
|
|
|
|
|
my $days; |
|
47
|
0
|
|
0
|
|
|
|
my $start = ( join ' ', @$args ) || 'today'; |
|
48
|
0
|
|
|
|
|
|
eval { ( $days, undef ) = summary "$start through today", $test, {} }; |
|
|
0
|
|
|
|
|
|
|
|
49
|
0
|
0
|
|
|
|
|
$self->usage_error($@) if $@; |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# check for long task |
|
52
|
0
|
|
|
|
|
|
my ($last_e) = App::JobLog::Log->new->last_event; |
|
53
|
0
|
0
|
0
|
|
|
|
if ( $last_e && $last_e->is_open ) { |
|
54
|
0
|
|
|
|
|
|
my ( $then, $today ) = ( $last_e->start, today ); |
|
55
|
0
|
0
|
0
|
|
|
|
if ( |
|
|
|
|
0
|
|
|
|
|
|
56
|
|
|
|
|
|
|
!( |
|
57
|
|
|
|
|
|
|
$then->year == $today->year |
|
58
|
|
|
|
|
|
|
&& $then->month == $today->month |
|
59
|
|
|
|
|
|
|
&& $then->day == $today->day |
|
60
|
|
|
|
|
|
|
) |
|
61
|
|
|
|
|
|
|
) |
|
62
|
|
|
|
|
|
|
{ |
|
63
|
0
|
|
|
|
|
|
print <
|
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
WARNING! The last event in the log has been open since before 12:00 am today! |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
END |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
} |
|
70
|
0
|
|
|
|
|
|
my $remaining = 0; |
|
71
|
0
|
|
|
|
|
|
$remaining += $_->time_remaining for @$days; |
|
72
|
0
|
0
|
|
|
|
|
if ( $remaining == 0 ) { |
|
73
|
0
|
|
|
|
|
|
say "\nyou are just now done"; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
|
|
|
|
|
|
else { |
|
76
|
0
|
|
|
|
|
|
my $then = now->add( seconds => $remaining ); |
|
77
|
0
|
|
|
|
|
|
my $format; |
|
78
|
0
|
0
|
|
|
|
|
if ( $then->year == now->year ) { |
|
79
|
0
|
|
|
|
|
|
my $delta = abs $then->delta_days(now)->in_units('days'); |
|
80
|
0
|
0
|
0
|
|
|
|
if ( $delta > 7 ) { |
|
|
|
0
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
|
$format = SAME_YEAR_FORMAT; |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
elsif ( $then->month == now->month && $then->day == now->day ) { |
|
84
|
0
|
|
|
|
|
|
$format = SAME_DAY_FORMAT; |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
else { |
|
87
|
0
|
|
|
|
|
|
$format = SAME_WEEK_FORMAT; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
else { |
|
91
|
0
|
|
|
|
|
|
$format = FULL_FORMAT; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
0
|
0
|
|
|
|
|
if ( $then < now ) { |
|
94
|
0
|
|
0
|
|
|
|
$then = ( $days->[-1]->last_event->end // now )->add( seconds => $remaining ); |
|
95
|
0
|
|
|
|
|
|
say "\nyou were finished at " . $then->strftime($format); |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
else { |
|
98
|
0
|
|
|
|
|
|
say "\nyou will be finished at " . $then->strftime($format); |
|
99
|
|
|
|
|
|
|
} |
|
100
|
0
|
|
|
|
|
|
print "\n"; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
0
|
|
|
0
|
1
|
|
sub usage_desc { '%c ' . __PACKAGE__->name . ' %o ' } |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub abstract { |
|
107
|
0
|
|
|
0
|
1
|
|
'report when work is done for the day'; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub full_description { |
|
111
|
|
|
|
|
|
|
<
|
|
112
|
|
|
|
|
|
|
Calculate when you'll be done for the day given how much work you've already done. |
|
113
|
|
|
|
|
|
|
If no time expression is provided, all task time since the beginning of the day |
|
114
|
|
|
|
|
|
|
is considered. A useful time expression is 'pay period' (equivalently, 'pay', 'payperiod', |
|
115
|
|
|
|
|
|
|
or 'pp'). |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
It is possible that you won't want all tasks in the log in the given period included in the |
|
118
|
|
|
|
|
|
|
time worked sum. As with the summary command, events may be filtered in numerous ways: by tag, |
|
119
|
|
|
|
|
|
|
or terms used in descriptions. If tags to match are provided, only those events |
|
120
|
|
|
|
|
|
|
that contain at least one such tag will be shown. If tags not to match are provided, only those |
|
121
|
|
|
|
|
|
|
events that contain none of these tags will be shown. |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
If you provide description filters to match or avoid, these will be interpreted as regexes. Try 'perldoc perlre' |
|
124
|
|
|
|
|
|
|
for more details, or perhaps 'perldoc perlretut' (these will only work if you have the Perl documentation |
|
125
|
|
|
|
|
|
|
installed on your machine). If you don't want to worry about regular expressions, simple strings will work. |
|
126
|
|
|
|
|
|
|
Prefix your expression with '(?i)' to turn off case sensitivity. And don't enclose regexes in slashes or any other |
|
127
|
|
|
|
|
|
|
sort of delimiter. Use 'ab', not '/ab/' or 'm!ab!', etc. Finally, you may need to enclose your regexes in single quotes |
|
128
|
|
|
|
|
|
|
to prevent the shell from trying to interpret them. |
|
129
|
|
|
|
|
|
|
END |
|
130
|
0
|
|
|
0
|
0
|
|
} |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub options { |
|
133
|
|
|
|
|
|
|
return ( |
|
134
|
|
|
|
|
|
|
[ |
|
135
|
0
|
|
|
0
|
0
|
|
"Use '@{[prog_name]} help " |
|
|
0
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
. __PACKAGE__->name |
|
137
|
|
|
|
|
|
|
. '\' to see full details.' |
|
138
|
|
|
|
|
|
|
], |
|
139
|
|
|
|
|
|
|
[], |
|
140
|
|
|
|
|
|
|
[ |
|
141
|
|
|
|
|
|
|
'tag|t=s@', |
|
142
|
|
|
|
|
|
|
'filter events to include only those with given tags; ' |
|
143
|
|
|
|
|
|
|
. 'multiple tags may be specified' |
|
144
|
|
|
|
|
|
|
], |
|
145
|
|
|
|
|
|
|
[ |
|
146
|
|
|
|
|
|
|
'exclude-tag|T=s@', |
|
147
|
|
|
|
|
|
|
'filter events to exclude those with given tags; ' |
|
148
|
|
|
|
|
|
|
. 'multiple tags may be specified' |
|
149
|
|
|
|
|
|
|
], |
|
150
|
|
|
|
|
|
|
[ |
|
151
|
|
|
|
|
|
|
'match|m=s@', |
|
152
|
|
|
|
|
|
|
'filter events to include only those one of whose descriptions matches the given regex; ' |
|
153
|
|
|
|
|
|
|
. 'multiple regexes may be specified' |
|
154
|
|
|
|
|
|
|
], |
|
155
|
|
|
|
|
|
|
[ |
|
156
|
|
|
|
|
|
|
'no-match|M=s@', |
|
157
|
|
|
|
|
|
|
'filter events to include only those one of whose descriptions do not match the given regex; ' |
|
158
|
|
|
|
|
|
|
. 'multiple regexes may be specified' |
|
159
|
|
|
|
|
|
|
], |
|
160
|
|
|
|
|
|
|
[ 'no-vacation|V', 'do not display vacation hours' ], |
|
161
|
|
|
|
|
|
|
); |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
1; |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=pod |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=encoding UTF-8 |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head1 NAME |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
App::JobLog::Command::when - when you'll be done for the day |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=head1 VERSION |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
version 1.040 |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
houghton@NorthernSpy:~job when --help |
|
181
|
|
|
|
|
|
|
job |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
job when [-MmTtV] [long options...] |
|
184
|
|
|
|
|
|
|
Use 'job help when' to see full details. |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
-t --tag filter events to include only those with given |
|
187
|
|
|
|
|
|
|
tags; multiple tags may be specified |
|
188
|
|
|
|
|
|
|
-T --exclude-tag filter events to exclude those with given tags; |
|
189
|
|
|
|
|
|
|
multiple tags may be specified |
|
190
|
|
|
|
|
|
|
-m --match filter events to include only those one of |
|
191
|
|
|
|
|
|
|
whose descriptions matches the given regex; |
|
192
|
|
|
|
|
|
|
multiple regexes may be specified |
|
193
|
|
|
|
|
|
|
-M --no-match filter events to include only those one of |
|
194
|
|
|
|
|
|
|
whose descriptions do not match the given |
|
195
|
|
|
|
|
|
|
regex; multiple regexes may be specified |
|
196
|
|
|
|
|
|
|
-V --no-vacation do not display vacation hours |
|
197
|
|
|
|
|
|
|
--help this usage screen |
|
198
|
|
|
|
|
|
|
houghton@NorthernSpy:~$ job w payperiod |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
you will be finished at 7:17:32 pm |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
B says when you will be able to punch out for the day. It does this by iterating over |
|
205
|
|
|
|
|
|
|
the days in some range of dates, adding up the time worked and subtracted the work hours expected. If no argument |
|
206
|
|
|
|
|
|
|
is given, the range only includes the current day. (See the C parameter of L.) |
|
207
|
|
|
|
|
|
|
If you wish to use the pay period as your interval, you need to have defined the C parameter of |
|
208
|
|
|
|
|
|
|
L. |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Various options are provided to facilitate eliminating certain tasks from the calculation. This is useful if you |
|
211
|
|
|
|
|
|
|
have more than one employer and you are committed to working a certain number of hours a day for each. |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
This command was inspired by my wife Paula, who frequently wanted to know when I'd be done for the day. In an earlier |
|
216
|
|
|
|
|
|
|
incarnation of this application one obtained it by passing in the option C<-p> and I knew it as the Paula feature. |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
L, L, L, L, |
|
221
|
|
|
|
|
|
|
L |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head1 AUTHOR |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
David F. Houghton |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
This software is copyright (c) 2011 by David F. Houghton. |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
232
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=cut |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
__END__ |