File Coverage

File:lib/App/TimeTracker.pm
Coverage:91.2%

linestmtbrancondsubpodtimecode
1package App::TimeTracker;
2
8
8
8
528750
25
188
use strict;
3
8
8
8
41
17
247
use warnings;
4
8
8
8
8
8
8
446
462
246
2457
74
103
use 5.010;
5
6our $VERSION = "2.005";
7# ABSTRACT: Track time spend on projects from the commandline
8
9
8
8
8
1051
23
174
use App::TimeTracker::Data::Task;
10
11
8
8
8
1521
226313
161
use DateTime;
12
8
8
8
43
30
84
use Moose;
13
8
8
8
37718
39
114
use Moose::Util::TypeConstraints;
14
8
8
8
18246
62469
93
use Path::Class::Iterator;
15
8
8
8
1071
445701
1924
use MooseX::Storage::Format::JSONpm;
16
17with qw(
18    MooseX::Getopt
19);
20
21subtype 'TT::DateTime' => as class_type('DateTime');
22subtype 'TT::RT' => as 'Int';
23
24coerce 'TT::RT'
25    => from 'Str'
26    => via {
27    my $raw = $_;
28    $raw=~s/\D//g;
29    return $raw;
30};
31
32coerce 'TT::DateTime'
33    => from 'Str'
34    => via {
35    my $raw = $_;
36    my $dt = DateTime->now;
37    $dt->set_time_zone('local');
38
39    given (length($raw)) {
40        when (5) { # "13:42"
41            $dt = DateTime->today;
42            my ($h,$m)=split(/:/,$raw);
43            $dt->set(hour=>$h, minute=>$m);
44            return $dt;
45        }
46        when (10) { # "2010-02-26"
47            require DateTime::Format::Strptime;
48            my $dp = DateTime::Format::Strptime->new(pattern=>'%Y-%m-%d');
49            $dt = $dp->parse_datetime($raw);
50        }
51        when (16) { # "2010-02-26 23:42"
52            require DateTime::Format::Strptime;
53            my $dp = DateTime::Format::Strptime->new(pattern=>'%Y-%m-%d %H:%M');
54            $dt = $dp->parse_datetime($raw);
55        }
56    }
57    return $dt;
58};
59
60MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
61    'TT::DateTime' => '=s',
62);
63MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
64    'TT::RT' => '=i',
65);
66
67
8
8
8
47
19
107
no Moose::Util::TypeConstraints;
68
69has 'home' => (
70    is=>'ro',
71    isa=>'Path::Class::Dir',
72    traits => [ 'NoGetopt' ],
73    required=>1,
74);
75has 'config' => (
76    is=>'ro',
77    isa=>'HashRef',
78    required=>1,
79    traits => [ 'NoGetopt' ],
80);
81has '_currentproject' => (
82    is=>'ro',
83    isa=>'Str',
84    traits => [ 'NoGetopt' ],
85);
86
87has 'tags' => (
88    isa=>'ArrayRef',
89    is=>'ro',
90    traits => ['Array'],
91    default=>sub {[]},
92    handles => {
93        insert_tag => 'unshift',
94
2
1
3289
7
        add_tag => 'push',
95    }
96);
97
98has '_current_command' => (
99    isa=>'Str',
100
2
1
5
4
    is=>'rw',
101    traits => [ 'NoGetopt' ],
102);
103
104has '_current_task' => (
105    isa=>'App::TimeTracker::Data::Task',
106
2
2
1
1
5
6
3
3
    is=>'rw',
107    traits => [ 'NoGetopt' ],
108
2
2
1
1
4
12
3
5
);
109has '_previous_task' => (
110    isa=>'App::TimeTracker::Data::Task',
111    is=>'rw',
112    traits => [ 'NoGetopt' ],
113);
114
115sub run {
116
1
0
855
    my $self = shift;
117
1
10
    my $command = 'cmd_'.($self->extra_argv->[0] || 'missing');
118
119
1
22
    $self->cmd_commands unless $self->can($command);
120
0
0
    $self->_current_command($command);
121
0
0
    $self->$command;
122}
123
124sub now {
125
2
0
271865
    my $dt = DateTime->now();
126
2
670
    $dt->set_time_zone('local');
127
2
15939
    return $dt;
128}
129
130sub beautify_seconds {
131
10
0
6789
    my ( $self, $s ) = @_;
132
10
41
    return '0' unless $s;
133
8
18
    my ( $m, $h )= (0, 0);
134
135
8
27
    if ( $s >= 60 ) {
136
7
17
        $m = int( $s / 60 );
137
7
17
        $s = $s - ( $m * 60 );
138    }
139
8
44
    if ( $m && $m >= 60 ) {
140
4
10
        $h = int( $m / 60 );
141
4
8
        $m = $m - ( $h * 60 );
142    }
143
8
66
    return sprintf("%02d:%02d:%02d",$h,$m,$s);
144}
145
146sub find_task_files {
147
3
0
9217
    my ($self, $args) = @_;
148
149
3
10
    my ($cmp_from, $cmp_to);
150
151
3
20
    if (my $from = $args->{from}) {
152
2
89
        my $to = $args->{to} || $self->now;
153
2
80
        $to->set(hour=>23,minute=>59,second=>59) unless $to->hour;
154
2
355
        $cmp_from = $from->strftime("%Y%m%d%H%M%S");
155
2
155
        $cmp_to = $to->strftime("%Y%m%d%H%M%S");
156    }
157
3
115
    my $projects;
158
3
14
    if ($args->{projects}) {
159
2
2
6
8
        $projects = join('|',@{$args->{projects}});
160    }
161
162
3
9
    my @found;
163
3
26
    my $iterator = Path::Class::Iterator->new(
164        root => $self->home,
165    );
166
3
7612
    until ($iterator->done) {
167
75
1596
        my $file = $iterator->next;
168
75
78683
        next unless -f $file;
169
69
3654
        my $name = $file->basename;
170
69
552
        next unless $name =~/\.trc$/;
171
172
69
183
        if ($cmp_from) {
173
46
241
            $file =~ /(\d{8})-(\d{6})/;
174
46
2186
            my $time = $1 . $2;
175
46
219
            next if $time < $cmp_from;
176
35
129
            next if $time > $cmp_to;
177        }
178
179
52
592
        next if $projects && ! ($name ~~ /$projects/);
180
181
29
247
        push(@found,$file);
182    }
183
3
68
    return sort @found;
184}
185
1861;