| 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 | |||||||