File Coverage

lib/App/TimeTracker/Data/Task.pm
Criterion Covered Total %
statement 85 92 92.3
branch 8 16 50.0
condition 2 6 33.3
subroutine 22 23 95.6
pod 0 9 0.0
total 117 146 80.1


line stmt bran cond sub pod time code
1             package App::TimeTracker::Data::Task;
2 12     12   1733728 use 5.010;
  12         66  
3              
4             # ABSTRACT: App::TimeTracker Task storage
5              
6 12     12   3190 use Moose;
  12         2781873  
  12         85  
7 12     12   80775 use App::TimeTracker::Utils qw(now);
  12         34  
  12         684  
8 12     12   1927 use namespace::autoclean;
  12         27485  
  12         85  
9 12     12   4751 use App::TimeTracker;
  12         39  
  12         461  
10 12     12   6866 use DateTime::Format::ISO8601;
  12         1290567  
  12         605  
11 12     12   7542 use DateTime::Format::Duration;
  12         61451  
  12         674  
12 12     12   5797 use User::pwent;
  12         42807  
  12         58  
13              
14 12     12   6182 use MooseX::Storage;
  12         380081  
  12         56  
15             with Storage(
16             format => [ JSONpm => { json_opts => { pretty => 1, canonical => 1 } } ],
17             io => "File",
18             );
19              
20             MooseX::Storage::Engine->add_custom_type_handler(
21             'DateTime' => (
22             expand => sub { DateTime::Format::ISO8601->parse_datetime(shift) },
23             collapse => sub { (shift)->iso8601 } ) );
24             my $dtf_dur = DateTime::Format::Duration->new( pattern => '%H:%M:%S',
25             normalise => 1 );
26             my $dtf_sec = DateTime::Format::Duration->new( pattern => '%s' );
27              
28             has 'start' => (
29             isa => 'DateTime',
30             is => 'ro',
31             required => 1,
32             default => sub { now() } );
33             has 'stop' => (
34             isa => 'DateTime',
35             is => 'rw',
36             trigger => \&_calc_duration,
37             );
38             has 'seconds' => (
39             isa => 'Maybe[Int]',
40             is => 'rw',
41             lazy_build => 1,
42             );
43              
44             sub _build_seconds {
45 6     6   4502 my $self = shift;
46 6         34 my $delta = now()->subtract_datetime( $self->start );
47 6         2296 my $s = $dtf_sec->format_duration($delta);
48 6 100       1216 return undef unless $s > 1;
49 4         27 return $s;
50             }
51             has 'duration' => (
52             isa => 'Str',
53             is => 'rw',
54             );
55             has 'status' => (
56             isa => 'Str',
57             is => 'rw',
58             );
59              
60             sub _build_user {
61 6     6   13081 return @{ getpw($<) }[0];
  6         36  
62             }
63             has 'user' => (
64             isa => 'Str',
65             is => 'ro',
66             required => 1,
67             lazy_build => 1,
68             );
69             has 'project' => (
70             isa => 'Str',
71             is => 'ro',
72             required => 1,
73             );
74             has 'description' => (
75             isa => 'Maybe[Str]',
76             is => 'ro',
77             required => 0,
78             );
79             has 'tags' => (
80             isa => 'ArrayRef',
81             is => 'ro',
82             default => sub { [] },
83             traits => ['Array'],
84             handles => { has_tags => 'count', },
85             );
86              
87             sub _filepath {
88 14     14   32 my $self = shift;
89 14         383 my $start = $self->start;
90 14         384 my $name = $self->project;
91 14         44 $name =~ s/\W/_/g;
92 14         56 $name =~ s/_+/_/g;
93 14         31 $name =~ s/^_//;
94 14         29 $name =~ s/_$//;
95             return (
96 14         52 $start->year,
97             sprintf( '%02d', $start->month ),
98             $start->strftime("%Y%m%d-%H%M%S") . '_' . $name . '.trc'
99             );
100             }
101              
102             sub _calc_duration {
103 17     17   523 my ( $self, $stop ) = @_;
104 17   66     84 $stop ||= $self->stop;
105 17         547 my $delta = $stop->subtract_datetime( $self->start );
106 17         6717 $self->seconds( $dtf_sec->format_duration($delta) );
107 17         80 $self->duration( $dtf_dur->format_duration($delta) );
108             }
109              
110             sub storage_location {
111 13     13 0 5120 my ( $self, $home ) = @_;
112 13         49 my $file = $home->file( $self->_filepath );
113 13         2833 $file->parent->mkpath;
114 13         1747 return $file;
115             }
116              
117             sub description_short {
118 1     1 0 3 my ($self) = @_;
119 1         34 my $description = $self->description;
120 1 50       4 return unless $description;
121              
122 1         8 $description =~ s/(.{40}[[:alnum:]]*).+$/$1.../;
123 1         5 $description =~ s/^(.{50}).+$/$1.../;
124 1         5 $description =~ s/\.{3,}$/.../;
125 1         5 return $description;
126             }
127              
128             sub save {
129 11     11 0 32 my ( $self, $home ) = @_;
130              
131 11         56 my $file = $self->storage_location($home)->stringify;
132 11         406 $self->store($file);
133 11         17650 return $file;
134             }
135              
136             sub current {
137 13     13 0 2765 my ( $class, $home ) = @_;
138 13         61 $class->_load_from_link( $home, 'current' );
139             }
140              
141             sub previous {
142 3     3 0 447 my ( $class, $home ) = @_;
143 3         10 $class->_load_from_link( $home, 'previous' );
144             }
145              
146             sub _load_from_link {
147 16     16   47 my ( $class, $home, $link ) = @_;
148 16         70 my $file = $home->file($link);
149 16 100       1469 return unless -e $file;
150 11         569 my $linked_file = $file->slurp( chomp => 1 );
151 11 50       2431 return unless -e $linked_file;
152 11         115 return $class->load($linked_file);
153             }
154              
155             sub say_project_tags {
156 13     13 0 33 my $self = shift;
157              
158 13         406 my $tags = $self->tags;
159 13         346 my $rv = $self->project;
160 13 50       44 $rv .= ' (' . join( ', ', @$tags ) . ')' if @$tags;
161 13 50       339 $rv .= ' ' . $self->description if $self->description;
162 13         819 return $rv;
163             }
164              
165             sub do_start {
166 6     6 0 127 my ( $self, $home ) = @_;
167              
168 6         24 my $saved_to = $self->save($home);
169              
170 6         728 my $fh = $home->file('current')->openw;
171 6         1658 say $fh $saved_to;
172 6         162 close $fh;
173              
174 6         39 say "Started working on "
175             . $self->say_project_tags . " at "
176             . $self->start->hms;
177             }
178              
179             sub rounded_minutes {
180 4     4 0 9 my $self = shift;
181 4         121 return sprintf "%.0f", $self->seconds/60;
182             }
183              
184             sub get_detail {
185 0     0 0   my ( $self, $level ) = @_;
186              
187 0           my $detail = {};
188 0 0 0       if ( $level eq 'tag' || $level eq 'description' ) {
189 0           $detail->{tags} = $self->tags;
190             }
191 0 0         if ( $level eq 'description' ) {
192 0           $detail->{desc} = $self->description;
193             }
194 0           return $detail;
195             }
196              
197             __PACKAGE__->meta->make_immutable;
198             1;
199              
200             __END__
201              
202             =pod
203              
204             =encoding UTF-8
205              
206             =head1 NAME
207              
208             App::TimeTracker::Data::Task - App::TimeTracker Task storage
209              
210             =head1 VERSION
211              
212             version 3.004
213              
214             =head1 DESCRIPTION
215              
216             Rather boring class implementing a Task object. Mainly used for storage etc.
217              
218             =head1 AUTHOR
219              
220             Thomas Klausner <domm@cpan.org>
221              
222             =head1 COPYRIGHT AND LICENSE
223              
224             This software is copyright (c) 2011 - 2020 by Thomas Klausner.
225              
226             This is free software; you can redistribute it and/or modify it under
227             the same terms as the Perl 5 programming language system itself.
228              
229             =cut