File Coverage

blib/lib/App/JobLog/Command/info.pm
Criterion Covered Total %
statement 24 101 23.7
branch 0 4 0.0
condition 0 2 0.0
subroutine 8 21 38.1
pod 3 5 60.0
total 35 133 26.3


line stmt bran cond sub pod time code
1             package App::JobLog::Command::info;
2             $App::JobLog::Command::info::VERSION = '1.041';
3             # ABSTRACT: provides general App::JobLog information
4              
5 5     5   5601 use App::JobLog -command;
  5         16  
  5         63  
6 5     5   69204 use autouse 'File::Temp' => qw(tempfile);
  5         10  
  5         40  
7 5     5   1277 use autouse 'Pod::Usage' => qw(pod2usage);
  5         10  
  5         20  
8 5     5   287 use autouse 'Carp' => qw(carp);
  5         7  
  5         34  
9 5     5   417 use autouse 'App::JobLog::Config' => qw(log DIRECTORY);
  5         11  
  5         21  
10 5     5   717 use Class::Autouse qw(Config File::Spec);
  5         10  
  5         35  
11              
12 5     5   290 use Modern::Perl;
  5         10  
  5         38  
13 5     5   839 no if $] >= 5.018, warnings => "experimental::smartmatch";
  5         10  
  5         43  
14              
15             # using quasi-pod -- == instead of = -- to make this work with Pod::Weaver
16              
17             sub execute {
18 0     0 1   my ( $self, $opt, $args ) = @_;
19 0           $self->simple_command_check($args);
20 0           my ( $fh, $fn ) = tempfile( UNLINK => 1 );
21 0           my ($executable) = reverse File::Spec->splitpath($0);
22 0           my $text;
23 0           my @options = ( -verbose => 2, -exitval => 0, -input => $fn );
24 0           for ( $opt->verbosity ) {
25 0           when ('man') {
26 0           $text =
27             $self->_header($executable)
28             . $self->_body($executable)
29             . $self->_footer($executable);
30             my $perldoc =
31 0           File::Spec->catfile( $Config::Config{scriptdir}, 'perldoc' );
32 0 0         unless ( -e $perldoc ) {
33 0           carp 'Cannot find perldoc. Text will not be paged.';
34 0           push @options, -noperldoc => 1;
35             }
36             }
37 0           when ('verbose') {
38 0           $text =
39             $self->_header($executable)
40             . $self->_basic_usage($executable)
41             . $self->_footer($executable);
42 0           push @options, -noperldoc => 1;
43             }
44 0           default {
45 0           $text =
46             $self->_header($executable) . <_footer($executable);
47              
48             ==head1 For More Information
49              
50             $executable info --help
51             END
52 0           push @options, -noperldoc => 1;
53             }
54             }
55              
56 0           $text = <
57             $text
58             ==cut
59             END
60 0           $text =~ s/^==(\w)/=$1/gm;
61 0           print $fh $text;
62 0           $fh->close;
63 0           pod2usage(@options);
64             }
65              
66 0     0 1   sub usage_desc { '%c ' . __PACKAGE__->name }
67              
68 0     0 1   sub abstract { 'describe job log' }
69              
70             sub full_description {
71             <
72             Describes application and provides usage information.
73             END
74 0     0 0   }
75              
76             sub options {
77             return (
78             [
79 0     0 0   "verbosity" => hidden => {
80             one_of => [
81             [ 'verbose|v' => 'longer documentation' ],
82             [ 'man' => 'extensive documentation in pager' ],
83             ],
84             }
85             ]
86             );
87             }
88              
89             # obtain all the
90             sub _unambiguous_prefixes {
91 0     0     my ( $self, $command ) = @_;
92              
93             # borrowing this from App::Cmd::Command::commands
94             my @commands =
95 0           map { ( $_->command_names )[0] } $self->app->command_plugins;
  0            
96 0           my %counts;
97 0           for my $cmd (@commands) {
98 0           for my $prefix ( _prefixes($cmd) ) {
99 0           $counts{$prefix}++;
100             }
101             }
102 0           my @prefixes;
103 0           for my $prefix ( _prefixes($command) ) {
104 0 0         push @prefixes, $prefix if $counts{$prefix} == 1;
105             }
106 0           return @prefixes;
107             }
108              
109             # obtain all the prefixes of a word
110             sub _prefixes {
111 0     0     my $cmd = shift;
112 0           my @prefixes;
113 0           for ( my ( $i, $lim ) = ( 0, length $cmd ) ; $i < $lim ; ++$i ) {
114 0           push @prefixes, substr $cmd, 0, $lim - $i;
115             }
116 0           return @prefixes;
117             }
118              
119             sub _header {
120 0     0     my ( $self, $executable ) = (@_);
121 0           return <
122             ==head1 Job Log
123              
124             work log management
125              
126 0   0       version @${[ $App::JobLog::Command::info::VERSION // 'DEVELOPMENT' ]}
127              
128             This application allows one to keep a simple, human readable log
129             of one's activities. B also facilitates searching, summarizing,
130             and extracting information from this log as needed.
131             END
132             }
133              
134             sub _body {
135 0     0     my ( $self, $executable ) = (@_);
136 0           return $self->_basic_usage($executable) . $self->_advanced_usage();
137             }
138              
139             sub _basic_usage {
140 0     0     my ( $self, $executable ) = (@_);
141 0           return <
142              
143             ==head1 Usage
144              
145             B keeps a log of events and notes. If you begin a new task you type
146              
147 0           $executable @{[App::JobLog::Command::add->name]} what I am doing now
148              
149 0           and it appends the following, modulo changes in time, to @{[log]}:
150              
151             2011 2 1 15 19 12::what I am doing now
152              
153             The portion before the first colon is a timestamp in year month day hour minute second format.
154             The portion after the second colon is your description of the event.
155              
156             If you wish to take a note, you type
157              
158 0           $executable @{[App::JobLog::Command::note->name]} something I should remember
159              
160 0           and it appends the following to @{[log]}:
161              
162             2011 2 1 15 19 12:something I should remember
163              
164             Again, the portion before the first colon is a timestamp. The portion after the ENOTEE is
165             the body of the note.
166              
167             The text between the two colons, or between the first colon and the ENOTEE tag, which is blank in these
168             examples, is a list of space-delimited tags one can use to categorize things. For
169             instance, if you were performing this task for Acme Widgets you might have typed
170              
171 0           $executable @{[App::JobLog::Command::add->name]} -t "Acme Widgets" what I am doing now
172              
173             producing
174              
175             2011 2 1 15 19 12:Acme\\ Widgets:what I am doing now
176              
177             Note the I<\\> character. This is the escape character which neutralizes any special value of
178             the character after it -- I<\\>, I<:>, or a whitespace character.
179              
180             You may tag an event multiple times. E.g.,
181              
182 0           $executable @{[App::JobLog::Command::add->name]} -t "Acme Widgets" -t foo -t bar what I am doing now
183              
184             producing
185              
186             2011 2 1 15 19 12:Acme\\ Widgets foo bar:what I am doing now
187              
188             For readability it is probably best to avoid spaces in tags.
189              
190             Since one usually works on a particular project for an extended period of time, if you specify no tags
191             the event or note is given the same tags as the preceding event/note. For example,
192              
193 0           $executable @{[App::JobLog::Command::add->name]} -t foo what I am doing now
194 0           $executable @{[App::JobLog::Command::add->name]} now something else
195              
196             would produce something like
197              
198             2011 2 1 15 19 12:foo:what I am doing now
199             2011 2 1 16 19 12:foo:now something else
200              
201             When you are done with the last task of the day, or your stop to take a break, you type
202              
203 0           $executable @{[App::JobLog::Command::done->name]}
204              
205             which adds something like
206              
207             2011 2 1 16 19 12:DONE
208              
209             to the log. Note the single colon. In this case I is not a tag. Tags are always sandwiched between
210             two delimiters. I here just marks the line as the end of a task.
211              
212             When you come back to work you can type
213              
214 0           $executable @{[App::JobLog::Command::resume->name]}
215              
216             to add a new line to the log with the same description and tags as the last task you began.
217              
218             ==head2 Summary Commands
219              
220             The log is of little use if you cannot extract useful reports of what it contains. For this there are a
221             variety of commands.
222              
223             ==over 8
224              
225 0           ==item B<@{[App::JobLog::Command::summary->name]}>
226              
227             The most extensive and featureful log report command. Example:
228              
229             \$ job summary yesterday
230             Monday, 14 March
231             9:46 - 10:11 am 0.41 widgets modifying name normalization code to use dates
232             10:17 - 10:55 am 0.62 widgets modifying name normalization code to use dates
233             1:49 - 2:08 pm 0.32 widgets testing PGA file to see whether Felix Frankfurter is still there
234              
235             TOTAL HOURS 1.35
236             widgets 1.35
237              
238 0           ==item B<@{[App::JobLog::Command::last->name]}>
239              
240             The last event recorded. Example:
241              
242             \$ job last
243             Tuesday, 15 March
244             5:07 pm - ongoing 0.00 foo muttering
245              
246             TOTAL HOURS 0.00
247             foo 0.00
248              
249 0           ==item B<@{[App::JobLog::Command::today->name]}>
250              
251             Everything you've done today. Example:
252              
253             \$ job today
254             Tuesday, 15 March
255             11:33 - 11:35 am 0.04 widgets checking up on Lem's issue with pipeline
256             11:38 - 11:46 am 0.12 widgets checking up on Lem's issue with pipeline; figuring out null pointer in multi-threaded code
257             12:40 - 1:11 pm 0.52 widgets debugging null pointers
258              
259             TOTAL HOURS 0.68
260             widgets 0.68
261              
262             ==back
263              
264             ==head2 Obtaining Further Information
265              
266             If you wish further information there are severals routes:
267              
268             ==over 8
269              
270             ==item B<$executable>
271              
272             If you invoke B without any arguments you will receive a list of its commands.
273              
274             ==item B<$executable commands>
275              
276             Another way to obtain a list of commands.
277              
278             ==item B<--help>
279              
280             Every command has a C<--help> option which will provide minimal help text and a complete list of the options the command
281             understands.
282              
283             ==item B<$executable help >
284              
285             The C command will provide a command's full usage text.
286              
287 0           ==item B<$executable @{[__PACKAGE__->name]} --man>
288              
289             This command's C<--man> option provides still more extensive help text.
290              
291             ==item B
292              
293             The Perl modules of which this application is composed each have their own documentation. For example, try
294              
295             perldoc App::JobLog
296              
297             ==back
298              
299             B any unambigous prefix of a command will do. All the following are equivalent:
300              
301 0           @{[join "\n", map {" $executable $_ doing something"} $self->_unambiguous_prefixes(App::JobLog::Command::add->name)]}
  0            
302              
303             This means that for almost all commands you need only use the first letter of the command name.
304             END
305             }
306              
307             sub _advanced_usage {
308 0     0     my ( $self, $executable ) = (@_);
309 0           return <
310              
311             ==head1 Environment Variables
312              
313             B is sensitive to a single environment variable:
314              
315 0           ==head2 @{[DIRECTORY()]}
316              
317             By default B keeps the log and all other files in a hidden directory called F<.joblog> in your home
318 0           directory. If @{[DIRECTORY()]} is set, however, it will keep its files here. This is mostly useful for
319             testing, though if you find F<.joblog> already is in use by some other application you can use this variable
320             to prevent collisions. Collisions will only occur if the files F or F exist in this
321             directory, and B will only alter these files if you append an event to the log or modify some
322             configuration parameters.
323              
324 0           All other configuration is done through the B<@{[App::JobLog::Command::configure->name]}> command.
325              
326             ==head1 Time Expressions
327              
328             B goes to considerable trouble to interpret whatever time expressions you might throw at it.
329             For example, it understands all of the following:
330              
331             1
332             11/24 to today
333             17 dec, 2024
334             1 april, 2022 to 1-23-2002
335             2023.6.5 - 10.26.2020
336             2-22 till yesterday
337             24 apr
338             27 november, 1995 through 10
339             3-4-2004
340             3-9 - today
341             4.23- 16 november, 1992
342             8/1/1997 through yesterday
343             june 14
344             last month - 6.14
345             pay period
346             2010
347             June 2010
348             2010/6
349             Feb 1 - 14
350             ever
351              
352             Every expression represents an interval of time. It either names an interval or defines it as the span from
353             the beginning of one interval to the end of another.
354              
355             ==head2 Time Grammar
356              
357             Here is a complete BNF-style grammar of the time expressions understood by B. In this set of rules
358             C represents some amount of whitespace, C represents a digit, and C<\\x>, where C is a number,
359             represents a back reference to the corresponding matched group in the same rule. After the first three
360             rules the remainder are alphabetized to facilitate finding them in the list. All expressions must match the
361             first rule.
362              
363 0           If you find this system of rules opaque or unwieldy, you can use the B<@{[App::JobLog::Command::parse->name]}>
364             command to test an expression and see what time interval it is interpreted as.
365              
366 0           @{[_bnf()]}
367             END
368             }
369              
370             sub _footer {
371 0     0     my ( $self, $executable ) = (@_);
372 0           return <
373              
374             ==head1 License etc.
375              
376             Author David Houghton
377             dfhoughton\@gmail.com
378             Copyright (c) 2011
379             License Perl_5
380             END
381             }
382              
383             # the complete bnf diagram for time grammar, also maintained
384             # in App::JobLog::TimeGrammar for lack of introspection in pod
385             sub _bnf {
386 0     0     return <
387             = s* ( | ) s*
388             = "all" | "always" | "ever" | [ [ "the" s ] ( "entire" | "whole" ) s ] "log"
389             = [ ]
390              
391             = "at" | "@"
392             = [ ( s | s* s* )
393             = [ s ]
394             = "beg" [ "in" [ "ning" ] ]
395             = |
396             = d{1,2} s
397             = "-" | "/" | "."
398             = d{1,2} s [ "," ] s d{4}
399             = d{1,2}
400             = |
401             = "january" | "february" | "march" | "april" | "may" | "june" | "july" | "august" | "september" | "october" | "november" | "december"
402             = |
403             = "sunday" | "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday"
404             = d{4} ( ) d{1,2} \\1 d{1,2}
405             = d{1,2} d{1,2}
406             = s d{1,2} "," s d{4}
407             = |
408             = [ s ]
409             = [ s ]
410             = [ s ]
411             = "last" | "this" | "next"
412             = |
413             = |
414             = |
415             = s d{1,2}
416             = | [ s "of" ]
417             = [","] s
418             = | |
419             = "now"
420             = | | |
421             = | | |
422             = "pay" | "pp" | "pay" s* "period"
423             = "week" | "month" | "year" |
424             = | [ s "of" [ s "the" ] ]
425             = [ s* ]
426             = "yesterday" | "today" | "tomorrow"
427             = "jan" | "feb" | "mar" | "apr" | "may" | "jun" | "jul" | "aug" | "sep" | "oct" | "nov" | "dec"
428             = "sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat"
429             = s* ( "-"+ | ( "through" | "thru" | "to" | "til" [ "l" ] | "until" ) ) s*
430             = [ "the" s ] ( | "end" )
431            
432             = ( "a" | "p" ) ( "m" | ".m." )
433             = d{1,2} ( ) d{1,2} \\1 d{4}
434             = | | | |
435             = |
436             = d{4}
437             = d{1,2}
438             END
439             }
440              
441             1;
442              
443             __END__