File: | lib/App/TimeTracker/Data/Task.pm |
Coverage: | 89.7% |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package App::TimeTracker::Data::Task; | ||||||
2 | 8 8 8 8 8 8 | 953424 389 132 41 23 97 | use 5.010; | ||||
3 | |||||||
4 | # ABSTRACT: TimeTracker Task storage | ||||||
5 | |||||||
6 | 8 8 8 | 2568 2001481 86 | use Moose; | ||||
7 | 8 8 8 | 43179 35447 67 | use namespace::autoclean; | ||||
8 | 8 8 8 | 1175 68 186 | use App::TimeTracker; | ||||
9 | 8 8 8 | 2503 466651 396 | use DateTime::Format::ISO8601; | ||||
10 | 8 8 8 | 2954 31137 351 | use DateTime::Format::Duration; | ||||
11 | 8 8 8 | 1788 21061 64 | use User::pwent; | ||||
12 | |||||||
13 | 8 8 8 | 2169 136662 71 | use MooseX::Storage; | ||||
14 | with Storage( | ||||||
15 | format => [ JSONpm => { json_opts => { pretty => 1 } } ], | ||||||
16 | io => "File", | ||||||
17 | ); | ||||||
18 | |||||||
19 | MooseX::Storage::Engine->add_custom_type_handler( | ||||||
20 | 'DateTime' => ( | ||||||
21 | expand => sub { DateTime::Format::ISO8601->parse_datetime(shift) }, | ||||||
22 | collapse => sub { (shift)->iso8601 } | ||||||
23 | ) | ||||||
24 | ); | ||||||
25 | my $dtf_dur = DateTime::Format::Duration->new(pattern => '%H:%M:%S', 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 { DateTime->now(time_zone=>'local') } | ||||||
33 | ); | ||||||
34 | has 'stop' => ( | ||||||
35 | isa=>'DateTime', | ||||||
36 | is=>'rw', | ||||||
37 | trigger=>\&_calc_duration, | ||||||
38 | ); | ||||||
39 | has 'seconds' => ( | ||||||
40 | isa=>'Maybe[Int]', | ||||||
41 | is=>'rw', | ||||||
42 | lazy_build=>1, | ||||||
43 | ); | ||||||
44 | sub _build_seconds { | ||||||
45 | 1 | 3142 | my $self = shift; | ||||
46 | 1 | 9 | my $delta = DateTime->now(time_zone=>'local')->subtract_datetime($self->start); | ||||
47 | 1 | 16373 | my $s =$dtf_sec->format_duration($delta); | ||||
48 | 1 | 184 | return undef unless $s > 1; | ||||
49 | 1 | 10 | return $s; | ||||
50 | } | ||||||
51 | has 'duration' => ( | ||||||
52 | isa=>'Str', | ||||||
53 | is=>'rw', | ||||||
54 | ); | ||||||
55 | has 'status' => ( | ||||||
56 | isa=>'Str', | ||||||
57 | is=>'rw', | ||||||
58 | ); | ||||||
59 | sub _build_user { | ||||||
60 | 1 1 | 554 8 | return @{getpw( $< )}[0]; | ||||
61 | } | ||||||
62 | has 'user' => ( | ||||||
63 | isa=>'Str', | ||||||
64 | is=>'ro', | ||||||
65 | required => 1, | ||||||
66 | lazy_build => 1, | ||||||
67 | ); | ||||||
68 | has 'project' => ( | ||||||
69 | isa=>'Str', | ||||||
70 | is=>'ro', | ||||||
71 | required=>1, | ||||||
72 | ); | ||||||
73 | has 'tags' => ( | ||||||
74 | isa=>'ArrayRef', | ||||||
75 | is=>'ro', | ||||||
76 | default=>sub { [] }, | ||||||
77 | traits => ['Array'], | ||||||
78 | ); | ||||||
79 | |||||||
80 | sub _filepath { | ||||||
81 | 4 | 51 | my $self = shift; | ||||
82 | 4 | 31 | my $start = $self->start; | ||||
83 | 4 | 51 | my $name = $self->project; | ||||
84 | 4 | 34 | $name=~s/\W/_/g; | ||||
85 | 4 | 10 | $name=~s/_+/_/g; | ||||
86 | 4 | 11 | $name=~s/^_//; | ||||
87 | 4 | 10 | $name=~s/_$//; | ||||
88 | 4 | 30 | return ($start->year,sprintf('%02d',$start->month),$start->strftime("%Y%m%d-%H%M%S").'_'.$name.'.trc'); | ||||
89 | } | ||||||
90 | |||||||
91 | sub _calc_duration { | ||||||
92 | 3 | 421 | my ( $self, $stop ) = @_; | ||||
93 | 3 | 22 | $stop ||= $self->stop; | ||||
94 | 3 | 93 | my $delta = $stop->subtract_datetime($self->start); | ||||
95 | 3 | 1046 | $self->seconds($dtf_sec->format_duration($delta)); | ||||
96 | 3 | 716 | $self->duration($dtf_dur->format_duration($delta)); | ||||
97 | } | ||||||
98 | |||||||
99 | sub storage_location { | ||||||
100 | 3 | 0 | 5036 | my ($self, $home) = @_; | |||
101 | 3 | 13 | my $file = $home->file($self->_filepath); | ||||
102 | 3 | 1437 | $file->parent->mkpath; | ||||
103 | 3 | 479 | return $file; | ||||
104 | } | ||||||
105 | |||||||
106 | sub save { | ||||||
107 | 1 | 0 | 3 | my ($self, $home) = @_; | |||
108 | |||||||
109 | 1 | 4 | my $file = $self->storage_location($home)->stringify; | ||||
110 | 1 | 64 | $self->store($file); | ||||
111 | 1 | 555 | return $file; | ||||
112 | } | ||||||
113 | |||||||
114 | sub current { | ||||||
115 | 1 | 0 | 303024 | my ($class, $home) = @_; | |||
116 | 1 | 5 | $class->_load_from_link($home, 'current'); | ||||
117 | } | ||||||
118 | |||||||
119 | sub previous { | ||||||
120 | 1 | 0 | 2128 | my ($class, $home) = @_; | |||
121 | 1 | 5 | $class->_load_from_link($home, 'previous'); | ||||
122 | } | ||||||
123 | |||||||
124 | sub _load_from_link { | ||||||
125 | 2 | 8 | my ($class, $home, $link) = @_; | ||||
126 | 2 | 14 | my $file = $home->file($link); | ||||
127 | 2 | 649 | return unless -e $file; | ||||
128 | 2 | 127 | return $class->load($file->slurp(chomp=>1)); | ||||
129 | } | ||||||
130 | |||||||
131 | sub say_project_tags { | ||||||
132 | 1 | 0 | 3 | my $self = shift; | |||
133 | |||||||
134 | 1 | 10 | my $tags = $self->tags; | ||||
135 | 1 | 14 | my $rv = $self->project; | ||||
136 | 1 | 10 | $rv .= ' ('.join(', ',@$tags).')' if @$tags; | ||||
137 | 1 | 30 | return $rv; | ||||
138 | } | ||||||
139 | |||||||
140 | sub do_start { | ||||||
141 | 1 | 0 | 160 | my ($self, $home) = @_; | |||
142 | |||||||
143 | 1 | 4 | my $saved_to = $self->save($home); | ||||
144 | |||||||
145 | 1 | 72 | my $fh = $home->file('current')->openw; | ||||
146 | 1 | 493 | say $fh $saved_to; | ||||
147 | 1 | 15 | close $fh; | ||||
148 | |||||||
149 | 1 | 5 | say "Started working on ".$self->say_project_tags ." at ". $self->start->hms; | ||||
150 | } | ||||||
151 | |||||||
152 | sub rounded_minutes { | ||||||
153 | 2 | 0 | 3575 | my $self = shift; | |||
154 | 2 | 17 | my $sec = $self->seconds; | ||||
155 | 2 | 23 | my $rest = 60 - $sec % 60; | ||||
156 | 2 | 8 | my $min = ( $sec + $rest ) /60; | ||||
157 | 2 | 15 | return $min; | ||||
158 | } | ||||||
159 | |||||||
160 | __PACKAGE__->meta->make_immutable; | ||||||
161 | 1; | ||||||
162 |