File Coverage

blib/lib/App/Dochazka/CLI/Commands/Interval.pm
Criterion Covered Total %
statement 68 266 25.5
branch 16 160 10.0
condition 0 18 0.0
subroutine 23 35 65.7
pod 10 10 100.0
total 117 489 23.9


line stmt bran cond sub pod time code
1             # *************************************************************************
2             # Copyright (c) 2014-2017, SUSE LLC
3             #
4             # All rights reserved.
5             #
6             # Redistribution and use in source and binary forms, with or without
7             # modification, are permitted provided that the following conditions are met:
8             #
9             # 1. Redistributions of source code must retain the above copyright notice,
10             # this list of conditions and the following disclaimer.
11             #
12             # 2. Redistributions in binary form must reproduce the above copyright
13             # notice, this list of conditions and the following disclaimer in the
14             # documentation and/or other materials provided with the distribution.
15             #
16             # 3. Neither the name of SUSE LLC nor the names of its contributors may be
17             # used to endorse or promote products derived from this software without
18             # specific prior written permission.
19             #
20             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30             # POSSIBILITY OF SUCH DAMAGE.
31             # *************************************************************************
32             #
33             # Interval commands
34             #
35             package App::Dochazka::CLI::Commands::Interval;
36              
37 20     20   414 use 5.012;
  20         72  
38 20     20   107 use strict;
  20         40  
  20         407  
39 20     20   93 use warnings;
  20         57  
  20         629  
40              
41 20     20   107 use App::CELL qw( $CELL $log );
  20         36  
  20         2065  
42 20         1798 use App::Dochazka::CLI qw(
43             $current_emp
44             $debug_mode
45             $prompt_date
46             $prompt_month
47             $prompt_year
48 20     20   125 );
  20         40  
49 20     20   121 use App::Dochazka::CLI::Shared qw( shared_generate_report );
  20         41  
  20         969  
50 20         1639 use App::Dochazka::CLI::Util qw(
51             datelist_from_token
52             determine_employee
53             month_alpha_to_numeric
54             normalize_date
55             normalize_time
56             parse_test
57             refresh_current_emp
58             rest_error
59             truncate_to
60 20     20   118 );
  20         38  
61 20     20   5370 use App::Dochazka::Common::Model::Interval;
  20         15113  
  20         494  
62 20     20   121 use Data::Dumper;
  20         40  
  20         902  
63 20     20   107 use Date::Calc qw( Days_in_Month );
  20         40  
  20         753  
64 20     20   104 use Exporter 'import';
  20         81  
  20         470  
65 20     20   94 use JSON;
  20         37  
  20         168  
66 20     20   1943 use Params::Validate qw( :all );
  20         39  
  20         2764  
67 20     20   121 use Text::Table;
  20         40  
  20         401  
68 20     20   90 use Web::MREST::CLI qw( send_req );
  20         38  
  20         53470  
69              
70              
71              
72              
73             =head1 NAME
74              
75             App::Dochazka::CLI::Commands::Interval - Interval commands
76              
77              
78              
79              
80             =head1 PACKAGE VARIABLES
81              
82             =cut
83              
84             our @EXPORT_OK = qw(
85             interval_date
86             interval_date_date1
87             interval_datelist
88             interval_month
89             interval_new_date_time_date1_time1
90             interval_new_time_time1
91             interval_new_timerange
92             interval_num_num1
93             interval_promptdate
94             interval_tsrange
95             );
96              
97              
98              
99              
100             =head1 FUNCTIONS
101              
102             The functions in this module are called from the parser when it recognizes a command.
103              
104              
105             =head2 Command handlers
106              
107             Functions called from the parser
108              
109              
110             =head3 interval_new_date_time_date1_time1
111              
112             INTERVAL NEW _DATE _TIME _DATE1 _TIME1 _TERM
113             INTERVAL NEW _DATE _TIME _HYPHEN _DATE1 _TIME1 _TERM
114              
115             =cut
116              
117             sub interval_new_date_time_date1_time1 {
118 2 50   2 1 9 print "Entering " . __PACKAGE__ . "::interval_new_date_time_date1_time1\n" if $debug_mode;
119 2         6 my ( $ts, $th ) = @_;
120              
121             # parse test
122 2 50       10 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
123              
124 0 0       0 print Dumper( $th ) if $debug_mode;
125              
126 0         0 my $status = _tsrange_from_dates_and_times( $th->{_DATE}, $th->{_DATE1}, $th->{_TIME}, $th->{_TIME1} );
127 0 0       0 return $status unless $status->ok;
128              
129 0         0 return _interval_new( $th->{_TERM}, $status->payload, $th->{_REST} );
130             }
131              
132              
133             =head3 interval_new_time_time1
134              
135             =cut
136              
137             sub interval_new_time_time1 {
138 9 50   9 1 28 print "Entering " . __PACKAGE__ . "::interval_new_time_time1\n" if $debug_mode;
139 9         21 my ( $ts, $th ) = @_;
140              
141             # parse test
142 9 50       37 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
143              
144 0 0       0 print Dumper( $th ) if $debug_mode;
145              
146 0         0 my $status = _tsrange_from_dates_and_times( $th->{_DATE}, undef, $th->{_TIME}, $th->{_TIME1} );
147 0 0       0 return $status unless $status->ok;
148              
149 0         0 return _interval_new( $th->{_TERM}, $status->payload, $th->{_REST} );
150             }
151              
152              
153             =head3 interval_new_timerange
154              
155             INTERVAL _TIMERANGE _TERM
156              
157             =cut
158              
159             sub interval_new_timerange {
160 4 50   4 1 16 print "Entering " . __PACKAGE__ . "::interval_new_timerange\n" if $debug_mode;
161 4         11 my ( $ts, $th ) = @_;
162              
163             # parse test
164 4 50       21 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
165              
166 0 0       0 print Dumper( $th ) if $debug_mode;
167              
168 0         0 my ( $rt0, $rt1 ) = $th->{_TIMERANGE} =~ m/\A(\d{1,2}:\d{1,2})-(\d{1,2}:\d{1,2})/;
169 0         0 my $status = _tsrange_from_dates_and_times( $th->{_DATE}, undef, $rt0, $rt1 );
170 0 0       0 return $status unless $status->ok;
171              
172 0 0       0 print "tsrange: " . $status->payload . "\n" if $debug_mode;
173              
174 0         0 return _interval_new( $th->{_TERM}, $status->payload, $th->{_REST} );
175             }
176              
177              
178             =head3 interval_date
179              
180             INTERVAL _DATE
181             EMPLOYEE_SPEC INTERVAL _DATE
182             INTERVAL FETCH _DATE
183             EMPLOYEE_SPEC INTERVAL FETCH _DATE
184             INTERVAL FILLUP _DATE
185             EMPLOYEE_SPEC INTERVAL FILLUP _DATE
186             INTERVAL FILLUP DRY_RUN _DATE
187             EMPLOYEE_SPEC INTERVAL FILLUP DRY_RUN _DATE
188             INTERVAL SUMMARY _DATE
189             EMPLOYEE_SPEC INTERVAL SUMMARY _DATE
190             INTERVAL REPORT _DATE
191             EMPLOYEE_SPEC INTERVAL REPORT _DATE
192             INTERVAL DELETE _DATE
193             EMPLOYEE_SPEC INTERVAL DELETE _DATE
194              
195             =cut
196              
197             sub interval_date {
198 6 50   6 1 17 print "Entering " . __PACKAGE__ . "::interval_date\n" if $debug_mode;
199 6         11 my ( $ts, $th ) = @_;
200              
201             # parse test
202 6 50       25 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
203              
204 0 0       0 print Dumper( $th ) if $debug_mode;
205              
206             # determine employee
207 0         0 my $status = determine_employee( $th->{'EMPLOYEE_SPEC'} );
208 0 0       0 return $status unless $status->ok;
209 0         0 my $emp = $status->payload;
210              
211             # determine date
212 0         0 my $date = normalize_date( $th->{'_DATE'} );
213 0 0       0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' ) unless $date;
214              
215 0         0 return _interval_fillup_delete_print( $th, $emp, "[ $date 00:00, $date 24:00 )" );
216             }
217              
218             =head3 interval_date_date1
219              
220             INTERVAL _DATE _DATE1
221             EMPLOYEE_SPEC INTERVAL _DATE _DATE1
222             INTERVAL FETCH _DATE _DATE1
223             EMPLOYEE_SPEC INTERVAL FETCH _DATE _DATE1
224             INTERVAL FILLUP _DATE _DATE1
225             EMPLOYEE_SPEC INTERVAL FILLUP _DATE _DATE1
226             INTERVAL FILLUP DRY_RUN _DATE _DATE1
227             EMPLOYEE_SPEC INTERVAL FILLUP DRY_RUN _DATE _DATE1
228             INTERVAL DELETE _DATE _DATE1
229             EMPLOYEE_SPEC INTERVAL DELETE _DATE _DATE1
230             INTERVAL _DATE _HYPHEN _DATE1
231             EMPLOYEE_SPEC INTERVAL _DATE _HYPHEN _DATE1
232             INTERVAL FETCH _DATE _HYPHEN _DATE1
233             EMPLOYEE_SPEC INTERVAL FETCH _DATE _HYPHEN _DATE1
234             INTERVAL FILLUP _DATE _HYPHEN _DATE1
235             EMPLOYEE_SPEC INTERVAL FILLUP _DATE _HYPHEN _DATE1
236             INTERVAL FILLUP DRY_RUN _DATE _HYPHEN _DATE1
237             EMPLOYEE_SPEC INTERVAL FILLUP DRY_RUN _DATE _HYPHEN _DATE1
238             INTERVAL SUMMARY _DATE _HYPHEN _DATE1
239             EMPLOYEE_SPEC INTERVAL SUMMARY _DATE _HYPHEN _DATE1
240             INTERVAL REPORT _DATE _HYPHEN _DATE1
241             EMPLOYEE_SPEC INTERVAL REPORT _DATE _HYPHEN _DATE1
242             INTERVAL DELETE _DATE _HYPHEN _DATE1
243             EMPLOYEE_SPEC INTERVAL DELETE _DATE _HYPHEN _DATE1
244              
245             =cut
246              
247             sub interval_date_date1 {
248 12 50   12 1 34 print "Entering " . __PACKAGE__ . "::interval_date_date1\n" if $debug_mode;
249 12         22 my ( $ts, $th ) = @_;
250              
251             # parse test
252 12 50       44 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
253              
254 0 0       0 print Dumper( $th ) if $debug_mode;
255              
256             # determine employee
257 0         0 my $status = determine_employee( $th->{'EMPLOYEE_SPEC'} );
258 0 0       0 return $status unless $status->ok;
259 0         0 my $emp = $status->payload;
260              
261             # determine date
262 0         0 my $date = normalize_date( $th->{'_DATE'} );
263 0 0       0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' ) unless $date;
264 0         0 my $date1 = normalize_date( $th->{'_DATE1'} );
265 0 0       0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' ) unless $date1;
266              
267 0         0 return _interval_fillup_delete_print( $th, $emp, "[ $date 00:00, $date1 24:00 )" );
268             }
269              
270             =head3 interval_month
271              
272             INTERVAL _MONTH [_NUM]
273             EMPLOYEE_SPEC INTERVAL _MONTH [_NUM]
274             INTERVAL FETCH _MONTH [_NUM]
275             EMPLOYEE_SPEC INTERVAL FETCH _MONTH [_NUM]
276             INTERVAL FILLUP _MONTH [_NUM]
277             EMPLOYEE_SPEC INTERVAL FILLUP _MONTH [_NUM]
278             INTERVAL FILLUP DRY_RUN _MONTH [_NUM]
279             EMPLOYEE_SPEC INTERVAL FILLUP DRY_RUN _MONTH [_NUM]
280             INTERVAL SUMMARY _MONTH [_NUM]
281             EMPLOYEE_SPEC INTERVAL SUMMARY _MONTH [_NUM]
282             INTERVAL REPORT _MONTH [_NUM]
283             EMPLOYEE_SPEC INTERVAL REPORT _MONTH [_NUM]
284             INTERVAL DELETE _MONTH [_NUM]
285             EMPLOYEE_SPEC INTERVAL DELETE _MONTH [_NUM]
286              
287             =cut
288              
289             sub interval_month {
290 12 50   12 1 36 print "Entering " . __PACKAGE__ . "::interval_month\n" if $debug_mode;
291 12         20 my ( $ts, $th ) = @_;
292              
293             # parse test
294 12 50       43 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
295              
296 0 0       0 print Dumper( $th ) if $debug_mode;
297              
298             # determine employee
299 0         0 my $status = determine_employee( $th->{'EMPLOYEE_SPEC'} );
300 0 0       0 return $status unless $status->ok;
301 0         0 my $emp = $status->payload;
302              
303             # determine lower and upper bounds
304             # - month
305 0         0 my $nmonth = month_alpha_to_numeric( $th->{_MONTH} );
306             # - year
307 0   0     0 my $year = $th->{'_NUM'} || $prompt_year;
308             # - normalize
309 0         0 my $date = normalize_date( "$year-$nmonth-1" );
310 0 0       0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' ) unless $date;
311 0         0 my $date1 = normalize_date( "$year-$nmonth-" .
312             Days_in_Month( $year, $nmonth ) );
313 0 0       0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' ) unless $date1;
314              
315 0         0 return _interval_fillup_delete_print( $th, $emp, "[ $date 00:00, $date1 24:00 )" );
316             }
317              
318             =head3 interval_num_num1
319              
320             INTERVAL _NUM [_NUM1]
321             EMPLOYEE_SPEC INTERVAL _NUM [_NUM1]
322             INTERVAL FETCH _NUM [_NUM1]
323             EMPLOYEE_SPEC INTERVAL FETCH _NUM [_NUM1]
324             INTERVAL FILLUP _NUM [_NUM1]
325             EMPLOYEE_SPEC INTERVAL FILLUP _NUM [_NUM1]
326             INTERVAL FILLUP DRY_RUN _NUM [_NUM1]
327             EMPLOYEE_SPEC INTERVAL FILLUP DRY_RUN _NUM [_NUM1]
328             INTERVAL SUMMARY _NUM [_NUM1]
329             EMPLOYEE_SPEC INTERVAL SUMMARY _NUM [_NUM1]
330             INTERVAL REPORT _NUM [_NUM1]
331             EMPLOYEE_SPEC INTERVAL REPORT _NUM [_NUM1]
332             INTERVAL DELETE _NUM [_NUM1]
333             EMPLOYEE_SPEC INTERVAL DELETE _NUM [_NUM1]
334              
335             =cut
336              
337             sub interval_num_num1 {
338 12 50   12 1 35 print "Entering " . __PACKAGE__ . "::interval_num_num1\n" if $debug_mode;
339 12         26 my ( $ts, $th ) = @_;
340              
341             # parse test
342 12 50       46 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
343              
344 0 0       0 print Dumper( $th ) if $debug_mode;
345              
346             # determine employee
347 0         0 my $status = determine_employee( $th->{'EMPLOYEE_SPEC'} );
348 0 0       0 return $status unless $status->ok;
349 0         0 my $emp = $status->payload;
350              
351             # determine lower and upper bounds
352             # - numeric month
353 0         0 my $nmonth;
354 0 0 0     0 if ( $th->{'_NUM'} >= 0 and $th->{'_NUM'} <= 12 ) {
355 0         0 $nmonth = $th->{'_NUM'};
356             } else {
357 0         0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' );
358             }
359             # - year
360 0   0     0 my $year = $th->{'_NUM1'} || $prompt_year;
361             # - normalize
362 0         0 my $date = normalize_date( "$year-$nmonth-1" );
363 0 0       0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' ) unless $date;
364 0         0 my $date1 = normalize_date( "$year-$nmonth-" .
365             Days_in_Month( $year, $nmonth ) );
366 0 0       0 return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE' ) unless $date1;
367              
368 0         0 return _interval_fillup_delete_print( $th, $emp, "[ $date 00:00, $date1 24:00 )" );
369             }
370              
371             =head3 interval_tsrange
372              
373             INTERVAL FILLUP _TSRANGE
374             EMPLOYEE_SPEC INTERVAL FILLUP _TSRANGE
375             INTERVAL FILLUP DRY_RUN _TSRANGE
376             EMPLOYEE_SPEC INTERVAL FILLUP DRY_RUN _TSRANGE
377             INTERVAL SUMMARY _TSRANGE
378             EMPLOYEE_SPEC INTERVAL SUMMARY _TSRANGE
379             INTERVAL REPORT _TSRANGE
380             EMPLOYEE_SPEC INTERVAL REPORT _TSRANGE
381              
382             =cut
383              
384             sub interval_tsrange {
385 0 0   0 1 0 print "Entering " . __PACKAGE__ . "::interval_tsrange\n" if $debug_mode;
386 0         0 my ( $ts, $th ) = @_;
387              
388             # parse test
389 0 0       0 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
390              
391 0 0       0 print Dumper( $th ) if $debug_mode;
392              
393             # determine employee
394 0         0 my $status = determine_employee( $th->{'EMPLOYEE_SPEC'} );
395 0 0       0 return $status unless $status->ok;
396 0         0 my $emp = $status->payload;
397            
398 0         0 return _interval_fillup_delete_print( $th, $emp, $th->{_TSRANGE} );
399             }
400              
401             =head3 interval_datelist
402              
403             =cut
404              
405             sub interval_datelist {
406 0 0   0 1 0 print "Entering " . __PACKAGE__ . "::interval_datelist\n" if $debug_mode;
407 0         0 my ( $ts, $th ) = @_;
408              
409             # parse test
410 0 0       0 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
411              
412 0 0       0 print Dumper( $th ) if $debug_mode;
413              
414             # determine employee
415 0         0 my $status = determine_employee( $th->{'EMPLOYEE_SPEC'} );
416 0 0       0 return $status unless $status->ok;
417 0         0 my $emp = $status->payload;
418 0         0 $log->debug( "EID: " . $emp->eid );
419              
420             # determine dry_run
421 0 0       0 my $dry_run = exists( $th->{'DRY_RUN'} ) ? 1 : 0;
422 0         0 $log->debug( "dry_run: $dry_run" );
423              
424             # check datelist for sanity
425 0         0 my $regex = qr/^(\d{1,2},|\d{1,2}-\d{1,2},)*(\d{1,2}|\d{1,2}-\d{1,2})$/;
426 0 0       0 return $CELL->status_err( "Invalid datelist" ) unless $th->{"_REST"} =~ $regex;
427              
428             # convert datelist into reference to array of dates
429 0         0 my $dl = datelist_from_token( $prompt_month, $th->{_REST} );
430 0         0 $log->debug( "datelist: " . Dumper( $dl ) );
431            
432             return _fillup(
433             eid => $emp->eid,
434             code => $th->{_TERM},
435 0         0 date_list => $dl,
436             dry_run => $dry_run,
437             clobber => 1,
438             );
439             }
440              
441             =head3 interval_promptdate
442              
443             INTERVAL
444             EMPLOYEE_SPEC INTERVAL
445             INTERVAL FETCH
446             EMPLOYEE_SPEC INTERVAL FETCH
447             INTERVAL FILLUP
448             EMPLOYEE_SPEC INTERVAL FILLUP
449             INTERVAL SUMMARY
450             EMPLOYEE_SPEC INTERVAL SUMMARY
451             INTERVAL REPORT
452             EMPLOYEE_SPEC INTERVAL REPORT
453             INTERVAL DELETE
454             EMPLOYEE_SPEC INTERVAL DELETE
455              
456             =cut
457              
458             sub interval_promptdate {
459 6 50   6 1 16 print "Entering " . __PACKAGE__ . "::interval_promptdate\n" if $debug_mode;
460 6         13 my ( $ts, $th ) = @_;
461              
462             # parse test
463 6 50       21 return parse_test( $ts, $th ) if $ts eq 'PARSE_TEST';
464              
465 0 0         print Dumper( $th ) if $debug_mode;
466              
467             # determine employee
468 0           my $status = determine_employee( $th->{'EMPLOYEE_SPEC'} );
469 0 0         return $status unless $status->ok;
470 0           my $emp = $status->payload;
471              
472 0           return _interval_fillup_delete_print( $th, $emp, "[ $prompt_date 00:00, $prompt_date 24:00 )" );
473             }
474              
475             sub _interval_fillup_delete_print {
476 0     0     my ( $th, $emp, $tsr ) = @_;
477              
478 0 0         if ( $th->{'FILLUP'} ) {
    0          
    0          
    0          
479 0           my %ARGS;
480 0           $ARGS{eid} = $emp->eid;
481 0           $ARGS{tsrange} = $tsr;
482 0 0         $ARGS{dry_run} = exists( $th->{'DRY_RUN'} ) ? 1 : 0;
483 0           $ARGS{clobber} = 0;
484 0           return _fillup( %ARGS );
485             } elsif ( $th->{'DELETE'} ) {
486 0           return _delete_intervals_tsrange( $emp->eid, $tsr );
487             } elsif ( $th->{'SUMMARY'} ) {
488 0           return _interval_summary( $emp->eid, $tsr );
489             } elsif ( $th->{'REPORT'} ) {
490 0           return _interval_report( $emp, $tsr );
491             } else {
492 0           return _print_intervals_tsrange( $emp, $tsr );
493             }
494             }
495              
496              
497             =head2 Helper functions
498              
499             Functions called from command handlers
500              
501              
502             =head3 _interval_new
503              
504             Takes code, tsrange and, optionally, long_desc. Converts the code into an AID,
505             sets up and sends the "POST interval/new" REST request, and returns the
506             resulting status object.
507              
508             =cut
509              
510             sub _interval_new {
511 0     0     my ( $code, $tsrange, $long_desc ) = validate_pos( @_,
512             { type => SCALAR },
513             { type => SCALAR },
514             { type => SCALAR|UNDEF, optional => 1 },
515             );
516              
517             # get aid from code
518 0           my $status = send_req( 'GET', "activity/code/$code" );
519 0 0         if ( $status->not_ok ) {
520 0 0 0       if ( $status->code eq "DISPATCH_SEARCH_EMPTY" and
521             $status->text =~ m/Search over activity with key -\>code equals .+\<- returned nothing/
522             ) {
523 0           return $CELL->status_err( 'DOCHAZKA_CLI_WRONG_ACTIVITY', args => [ $code ] );
524             }
525 0 0         return rest_error( $status, "Determine AID from code" ) unless $status->ok;
526             }
527 0           my $aid = $status->payload->{'aid'};
528              
529             # assemble entity
530 0           my $entity_perl = {
531             'aid' => $aid,
532             'intvl' => $tsrange,
533             };
534 0 0         $entity_perl->{'long_desc'} = $long_desc if $long_desc;
535 0           my $entity = encode_json $entity_perl;
536              
537             # send the request
538 0           $status = send_req( 'POST', "interval/new", $entity );
539 0 0         if ( $status->not_ok ) {
540             # if ... possible future checks for common errors
541             # elsif ... other common errors
542 0 0         return rest_error( $status, "Insert new attendance interval" ) unless $status->ok;
543             }
544              
545 0           return $CELL->status_ok( 'DOCHAZKA_CLI_NORMAL_COMPLETION',
546             payload => _print_interval( $status->payload ) );
547             }
548              
549              
550             =head3 _tsrange_from_dates_and_times
551              
552             Given two dates and two times, returns a full-fledged tsrange.
553             If the first date is undef or empty, use the prompt date.
554             If the second date is undef or empty, use the first date.
555              
556             =cut
557              
558             sub _tsrange_from_dates_and_times {
559 0     0     my ( $d0, $d1, $t0, $t1 ) = @_;
560              
561             # normalize dates and times
562             BREAK_OUT: {
563 0           my $s = 1;
  0            
564 0           my $flagged;
565              
566             # normalize_date will replace an undefined or empty date with the prompt date
567 0 0         if ( $s = normalize_date( $d0 ) ) {
568 0           $d0 = $s;
569             } else {
570 0           $flagged = $d0;
571             }
572              
573             # for the second date, we have to check for undefined/empty-ness ourselves
574 0 0 0       $d1 = $d0 unless defined( $d1 ) and length( $d1 ) > 0;
575              
576 0 0         if ( $s = normalize_date( $d1 ) ) {
577 0           $d1 = $s;
578             } else {
579 0           $flagged = $d1;
580             }
581              
582 0 0         if ( $s = normalize_time( $t0 ) ) {
583 0           $t0 = $s;
584             } else {
585 0           $flagged = $t0;
586             }
587              
588 0 0         if ( $s = normalize_time( $t1 ) ) {
589 0           $t1 = $s;
590             } else {
591 0           $flagged = $t1;
592             }
593              
594 0 0         last BREAK_OUT unless $flagged;
595 0 0         $flagged = 'undefined' if not defined $flagged;
596 0           return $CELL->status_err( 'DOCHAZKA_CLI_INVALID_DATE_OR_TIME', args => [ $flagged ] );
597             }
598              
599 0           return $CELL->status_ok( 'DOCHAZKA_CLI_NORMAL_COMPLETION', payload => "[ $d0 $t0, $d1 $t1 )" );
600             }
601              
602             =head3 _print_interval
603              
604             Given an interval object (blessed or unblessed), construct a string
605             suitable for on-screen display.
606              
607             =cut
608              
609             sub _print_interval {
610 0     0     my ( $int ) = @_;
611            
612             # get the activity code from the 'aid' property
613 0           my $status = send_req( 'GET', "activity/aid/" . $int->{'aid'} );
614 0 0         return rest_error( $status, "Determine activity code from AID" ) unless $status->ok;
615 0           my $code = $status->payload->{'code'};
616              
617             # convert the interval into a readable form
618 0           my $intvl = $int->{'intvl'};
619 0           my $iid = $int->{'iid'};
620              
621 0           my $out = '';
622 0           $out .= "Interval IID $iid\n";
623 0           $out .= "$intvl $code";
624 0 0         $out .= " " . $int->{'long_desc'} if defined( $int->{'long_desc'} );
625 0           $out .= "\n";
626              
627 0           return $out;
628             }
629              
630             =head3 _print_intervals_tsrange
631              
632             Given an employee object and a tsrange, print all matching intervals
633              
634             =cut
635              
636             sub _print_intervals_tsrange {
637 0     0     my ( $emp, $tsr ) = @_;
638 0           my $eid = $emp->eid;
639 0           my $nick = $emp->nick;
640              
641 0           my $status = send_req( 'GET', "interval/eid/$eid/$tsr" );
642 0 0 0       if ( $status->not_ok and $status->code eq 'DISPATCH_NOTHING_IN_TSRANGE' ) {
643 0           return $CELL->status_ok( 'DOCHAZKA_CLI_NORMAL_COMPLETION', payload => $status->text );
644             }
645 0 0         return rest_error( $status, "Get intervals for employee $nick (EID $eid) in range $tsr" )
646             unless $status->ok;
647              
648 0           my $pl = '';
649 0           $pl .= "Attendance intervals of $nick (EID $eid)\n";
650 0           $pl .= "in the range $tsr\n\n";
651              
652 0           my $t = Text::Table->new( 'IID', 'Begin', 'End', 'Code', 'Description' );
653 0           my $partial_intervals_present = 0;
654 0           for my $props ( @{ $status->payload } ) {
  0            
655 0           my $int = App::Dochazka::Common::Model::Interval->spawn( $props );
656 0           my $iid;
657 0 0         if ( $int->partial ) {
658 0           $partial_intervals_present = 1;
659 0           $iid = $int->iid . '**';
660             } else {
661 0           $iid = $int->iid;
662             }
663 0           $t->add(
664             $iid,
665             _begin_and_end_from_intvl( $int->intvl ),
666             $int->code,
667             truncate_to( $int->long_desc ),
668             );
669             }
670 0           $pl .= $t;
671 0 0         $pl .= "\nPartial intervals signified by **\n" if $partial_intervals_present;
672              
673 0           return $CELL->status_ok( 'DOCHAZKA_CLI_NORMAL_COMPLETION', payload => $pl );
674             }
675              
676              
677             =head3 _begin_and_end_from_intvl
678              
679             =cut
680              
681             sub _begin_and_end_from_intvl {
682 0     0     my $intvl = shift;
683              
684 0           my ( $d0, $t0, $d1, $t1 ) = $intvl =~
685             m/(\d{4,4}-\d{2,2}-\d{2,2}).*(\d{2,2}:\d{2,2}):\d{2,2}.*(\d{4,4}-\d{2,2}-\d{2,2}).*(\d{2,2}:\d{2,2}):\d{2,2}/;
686              
687 0           return ( "$d0 $t0", "$d1 $t1" );
688             }
689              
690             =head3 _delete_intervals_tsrange
691              
692             Given an EID and a tsrange, delete all matching intervals
693              
694             =cut
695              
696             sub _delete_intervals_tsrange {
697 0     0     my ( $eid, $tsr ) = @_;
698 0           my $status = send_req( 'DELETE', "interval/eid/$eid/$tsr" );
699 0 0         return $status unless $status->ok;
700 0           my $count = $status->{'count'};
701 0           return $CELL->status_ok( 'DOCHAZKA_CLI_NORMAL_COMPLETION',
702             payload => "$count intervals deleted in range $tsr" );
703             }
704              
705             =head3 _interval_summary
706              
707             Given an EID and a tsrange, call the "interval/sumary/eid/:eid/:tsrange"
708             resource.
709              
710             =cut
711              
712             sub _interval_summary {
713 0     0     my ( $eid, $tsr ) = @_;
714 0           my $status = send_req( 'GET', "interval/summary/eid/$eid/$tsr" );
715 0 0         return $status unless $status->ok;
716 0           $status->code( 'DOCHAZKA_CLI_NORMAL_COMPLETION' );
717 0           return $status;
718             }
719              
720             =head3 _interval_report
721              
722             Given an employee object and a tsrange, POST to the "genreport" resource with
723             an entity body:
724              
725             {
726             "path" : "suse-cz-monthly.mc",
727             "parameters" : {
728             "employee" : $EMPLOYEE_OBJECT_JSON,
729             "tsrange" : "$TSRANGE"
730             }
731             }
732              
733             =cut
734              
735             sub _interval_report {
736 0     0     my ( $emp, $tsr ) = @_;
737 0           my $emp_json = JSON->new->convert_blessed->encode( $emp );
738 0           my $entity = <<"EOS";
739             {
740             "path" : "suse-cz-monthly.mc",
741             "parameters" : {
742             "employee" : $emp_json,
743             "tsrange" : "$tsr"
744             }
745             }
746             EOS
747 0           return shared_generate_report( $entity );
748             }
749              
750             =head3 _fillup
751              
752             =cut
753              
754             sub _fillup {
755 0     0     my ( %ARGS ) = validate( @_, {
756             eid => { type => SCALAR },
757             code => { type => SCALAR, optional => 1 },
758             date_list => { type => ARRAYREF, optional => 1 },
759             tsrange => { type => SCALAR, optional => 1 },
760             dry_run => { type => SCALAR },
761             clobber => { type => SCALAR, default => 1 },
762             } );
763              
764 0           my $request_body = encode_json( \%ARGS );
765              
766 0           my $status = send_req( 'POST', "interval/fillup", $request_body );
767 0 0         return $status unless $status->ok;
768              
769 0           my ( $pl, $count );
770 0 0         if ( $status->code eq 'DISPATCH_FILLUP_INTERVALS_CREATED' ) {
771 0           my $tmp = $status->payload->{'success'}->{'count'};
772 0           $pl .= "$tmp intervals successfully inserted\n";
773 0           $tmp = $status->payload->{'failure'}->{'count'};
774 0           $pl .= "$tmp intervals not inserted due to conflicts\n";
775 0 0         if ( exists( $status->payload->{'clobbered'} ) ) {
776 0           $tmp = $status->payload->{'clobbered'}->{'count'};
777 0           $pl .= "$tmp existing intervals clobbered\n";
778             }
779             }
780 0           $count = $status->{'count'};
781 0           return $CELL->status_ok( 'DOCHAZKA_CLI_NORMAL_COMPLETION', payload => $pl );
782             }
783              
784             1;