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__ |