package App::TimeTracker::Data::Task; use 5.010; # ABSTRACT: App::TimeTracker Task storage use Moose; use App::TimeTracker::Utils qw(now); use namespace::autoclean; use App::TimeTracker; use DateTime::Format::ISO8601; use DateTime::Format::Duration; use User::pwent; use MooseX::Storage; with Storage( format => [ JSONpm => { json_opts => { pretty => 1 } } ], io => "File", ); MooseX::Storage::Engine->add_custom_type_handler( 'DateTime' => ( expand => sub { DateTime::Format::ISO8601->parse_datetime(shift) }, collapse => sub { (shift)->iso8601 } ) ); my $dtf_dur = DateTime::Format::Duration->new(pattern => '%H:%M:%S', normalise=>1); my $dtf_sec = DateTime::Format::Duration->new(pattern => '%s'); has 'start' => ( isa=>'DateTime', is=>'ro', required=>1, default=>sub { now() } ); has 'stop' => ( isa=>'DateTime', is=>'rw', trigger=>\&_calc_duration, ); has 'seconds' => ( isa=>'Maybe[Int]', is=>'rw', lazy_build=>1, ); sub _build_seconds { my $self = shift; my $delta = now()->subtract_datetime($self->start); my $s =$dtf_sec->format_duration($delta); return undef unless $s > 1; return $s; } has 'duration' => ( isa=>'Str', is=>'rw', ); has 'status' => ( isa=>'Str', is=>'rw', ); sub _build_user { return @{getpw( $< )}[0]; } has 'user' => ( isa=>'Str', is=>'ro', required => 1, lazy_build => 1, ); has 'project' => ( isa=>'Str', is=>'ro', required=>1, ); has 'description' => ( isa=>'Maybe[Str]', is=>'ro', required=>0, ); has 'tags' => ( isa=>'ArrayRef', is=>'ro', default=>sub { [] }, traits => ['Array'], handles => { has_tags => 'count', }, ); sub _filepath { my $self = shift; my $start = $self->start; my $name = $self->project; $name=~s/\W/_/g; $name=~s/_+/_/g; $name=~s/^_//; $name=~s/_$//; return ($start->year,sprintf('%02d',$start->month),$start->strftime("%Y%m%d-%H%M%S").'_'.$name.'.trc'); } sub _calc_duration { my ( $self, $stop ) = @_; $stop ||= $self->stop; my $delta = $stop->subtract_datetime($self->start); $self->seconds($dtf_sec->format_duration($delta)); $self->duration($dtf_dur->format_duration($delta)); } sub storage_location { my ($self, $home) = @_; my $file = $home->file($self->_filepath); $file->parent->mkpath; return $file; } sub description_short { my ($self) = @_; my $description = $self->description; $description =~ s/(.{40}[[:alnum:]]*).+$/$1.../; $description =~ s/^(.{50}).+$/$1.../; $description =~ s/\.{3,}$/.../; return $description; } sub save { my ($self, $home) = @_; my $file = $self->storage_location($home)->stringify; $self->store($file); return $file; } sub current { my ($class, $home) = @_; $class->_load_from_link($home, 'current'); } sub previous { my ($class, $home) = @_; $class->_load_from_link($home, 'previous'); } sub _load_from_link { my ($class, $home, $link) = @_; my $file = $home->file($link); return unless -e $file; my $linked_file = $file->slurp(chomp=>1); return unless -e $linked_file; return $class->load($linked_file); } sub say_project_tags { my $self = shift; my $tags = $self->tags; my $rv = $self->project; $rv .= ' ('.join(', ',@$tags).')' if @$tags; $rv .= ' '.$self->description if $self->description; return $rv; } sub do_start { my ($self, $home) = @_; my $saved_to = $self->save($home); my $fh = $home->file('current')->openw; say $fh $saved_to; close $fh; say "Started working on ".$self->say_project_tags ." at ". $self->start->hms; } sub rounded_minutes { my $self = shift; my $sec = $self->seconds; my $rest = 60 - $sec % 60; my $min = ( $sec + $rest ) /60; return $min; } __PACKAGE__->meta->make_immutable; 1; =pod =head1 NAME App::TimeTracker::Data::Task - App::TimeTracker Task storage =head1 VERSION version 2.017 =head1 DESCRIPTION Rather boring class implementing a Task object. Mainly used for storage etc. =head1 AUTHOR Thomas Klausner =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2011 by Thomas Klausner. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut __END__