File Coverage

lib/App/TimeTracker/Data/Task.pm
Criterion Covered Total %
statement 85 97 87.6
branch 8 18 44.4
condition 2 6 33.3
subroutine 22 24 91.6
pod 0 10 0.0
total 117 155 75.4


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