App-JobLog
view release on metacpan or search on metacpan
lib/App/JobLog/Vacation/Period.pm view on Meta::CPAN
use Exporter 'import';
our @EXPORT_OK = qw(
FLEX
FIXED
ANNUAL
MONTHLY
);
use base 'App::JobLog::Log::Event';
use DateTime;
use App::JobLog::Log::Line;
use App::JobLog::Time qw(tz);
use Carp qw(carp);
use overload '""' => \&to_string;
use overload 'bool' => sub { 1 };
use constant FLEX => 1;
use constant FIXED => 2;
use constant ANNUAL => 1;
use constant MONTHLY => 2;
sub new {
my ( $class, $log_line, %opts ) = @_;
$class = ref $class || $class;
bless {
log => $log_line,
type => 0,
repeats => 0,
tags => [],
events => [],
vacation => [],
%opts
},
$class;
}
sub flex { $_[0]->{type} == FLEX }
sub fixed { $_[0]->{type} == FIXED }
sub annual { $_[0]->{repeats} == ANNUAL }
sub monthly { $_[0]->{repeats} == MONTHLY }
sub repeats { $_[0]->{repeats} }
sub description : lvalue {
$_[0]->data->description;
}
sub clone {
my ($self) = @_;
my $clone = $self->SUPER::clone;
$clone->{type} = $self->{type};
$clone->{repeats} = $self->{repeats};
return $clone;
}
sub cmp {
my ( $self, $other ) = @_;
# when mixed with ordinary events
if ( ref $other eq 'App::JobLog::Log::Event' ) {
# treat as an ordinary event if fixed
return $self->SUPER::cmp($other) if $self->fixed;
# put after ordinary events
return 1;
}
if ( $self->monthly ) {
return -1 unless $other->monthly;
}
elsif ( $self->annual ) {
return 1 if $other->monthly;
return -1 unless $other->annual;
}
return $self->SUPER::cmp($other);
}
# some global variables for use in BNF regex
our ( @dates, $type, @tags, $description );
# log line parser
my $re = qr{
^ (?&ts) : (?&non_ts) $
(?(DEFINE)
(?<ts> (?&date) : (?&date) )
(?<date> (\d{4}\s++\d++\s++\d++\s++\d++\s++\d++\s++\d++) (?{push @dates, $^N}) )
(?<non_ts> (?&flex) : (?&tags) : (?&description))
(?<flex> ([012]{2}) (?{$type = $^N}))
(?<tags> (?:(?&tag)(\s++(?&tag))*+)?)
(?<tag> ((?:[^\s:\\]|(?&escaped))++) (?{push @tags, $^N}))
(?<escaped> \\.)
(?<description> (.++) (?{$description = $^N}))
)
}xi;
sub parse {
my ( $class, $text ) = @_;
$class = ref $class || $class;
local ( @dates, $type, @tags, $description );
if ( $text =~ $re ) {
my $start = _parse_time( $dates[0] );
my $end = _parse_time( $dates[1] );
my %tags = map { $_ => 1 } @tags;
my $tags = [ map { s/\\(.)/$1/g; $_ } sort keys %tags ];
$description = [ map { s/\\(.)/$1/g; $_ } ($description) ];
my ( $type, $repeats ) = split //, $type;
$obj = $class->new(
App::JobLog::Log::Line->new(
description => $description,
time => $start,
tags => $tags
),
type => $type,
repeats => $repeats,
end => $end
);
return $obj;
}
else {
carp "malformed line in vacation file: '$text'";
}
return;
}
sub _parse_time {
my @time = split /\s++/, $_[0];
$date = DateTime->new(
year => $time[0],
month => $time[1],
day => $time[2],
hour => $time[3],
minute => $time[4],
second => $time[5],
time_zone => tz,
lib/App/JobLog/Vacation/Period.pm view on Meta::CPAN
$fmt = '%F';
}
$fmt .= ' %H:%M:%S' if $self->fixed;
my $s;
if ( $self->single_day ) {
$s = $self->start->strftime($fmt);
}
else {
$s = $self->start->strftime($fmt) . ' -- ' . $self->end->strftime($fmt);
}
return $s;
}
# properties part of summary
sub _properties {
my ($self) = @_;
my $s;
if ( $self->fixed ) {
$s = 'fixed';
}
elsif ( $self->flex ) {
$s = 'flex';
}
else {
$s = '';
}
if ( $self->annual ) {
$s .= ' ' if $s;
$s .= 'annual';
}
elsif ( $self->monthly ) {
$s .= ' ' if $s;
$s .= 'monthly';
}
return $s;
}
sub overlap {
my ( $self, $start, $end ) = @_;
if ( $self->annual || $self->monthly ) {
# cloning here should be duplicated work, but better safe than sorry
my $cloned = 0;
if ( $self->annual
|| $self->monthly && $self->start->year != $start->year )
{
$self = $self->clone;
$cloned = 1;
my $delta = $start->year - $self->start->year;
$self->start->add( years => $delta );
$self->end->add( years => $delta );
}
if ( $self->monthly && $self->start->month != $start->month ) {
$self = $self->clone unless $cloned;
my $delta = $start->month - $self->start->month;
$self->start->add( months => $delta );
$self->end->add( months => $delta );
}
}
return $self->SUPER::overlap( $start, $end );
}
# tag part of summary
sub _tags {
my ($self) = @_;
return join ', ', @{ $self->tags };
}
# description part of summary
sub _description {
my ($self) = @_;
return join '; ', @{ $self->description };
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
App::JobLog::Vacation::Period - extension of L<App::JobLog::Log::Event> to handle special properties of vacation periods
=head1 VERSION
version 1.042
=head1 DESCRIPTION
C<App::JobLog::Vacation::Period> extends L<App::JobLog::Log::Event> to add repeating events and flexible
time events and to allow a different serialization convention such that events take a single line in their
file.
=head1 METHODS
=head2 flex
Whether time in a period is "flexible". Flexible time off shrinks or expands to provide
enough work hours to complete the day it occurs in.
=head2 fixed
Whether time in a period is "fixed". Fixed periods have a definite start and end time. Regular
vacation time is just a fixed period of virtual work in the day but at nor particular time
and flexible vacation time is just as much time as you need to fill out your work day, again
without any particular start or end.
=head2 annual
Whether this period repeats annually on a particular range of days in particular months.
=head2 annual
Whether this period repeats monthly on a particular range of days.
=head2 repeats
( run in 1.211 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )