Skip to content

Commit

Permalink
refactor Timestamp & TimeRange, parses event duration & repeater inte…
Browse files Browse the repository at this point in the history
…rval
  • Loading branch information
sharyanto committed Mar 23, 2011
1 parent 8430927 commit d19462e
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 75 deletions.
14 changes: 7 additions & 7 deletions dist.ini
Expand Up @@ -27,14 +27,14 @@ remove = PodVersion
Test::More = 0.96

; for runtime
perl = 5.010000
DateTime = 0
File::Slurp = 0
Log::Any = 0
Moo = 0
perl = 5.010000
DateTime = 0
DateTime::Event::Recurrence = 0
File::Slurp = 0
Log::Any = 0
Moo = 0
String::Escape = 0

; for example script: remind-due-todos
Sub::Spec::CmdLine = 0.26

; for example script: dump-org-structure
String::Escape = 0
53 changes: 22 additions & 31 deletions lib/Org/Document.pm
Expand Up @@ -72,8 +72,8 @@ our $arg_re = qr/(?: '(?<squote> [^']*)' |
(?<bare> \S+) )
/x;
our $args_re = qr/(?: $arg_re (?:[ \t]+ $arg_re)*)/x;
my $tstamp_re = qr/(?:\[\d{4}-\d{2}-\d{2} \s+ [^\]]*\])/x;
my $act_tstamp_re = qr/(?:<\d{4}-\d{2}-\d{2} \s+ [^>]*>)/x;
my $tstamp_re = qr/(?:\[\d{4}-\d{2}-\d{2} [ ] [^\n\]]*\])/x;
my $act_tstamp_re = qr/(?:<\d{4}-\d{2}-\d{2} [ ] [^\n>]*>)/x;
my $fn_name_re = qr/(?:[^ \t\n:\]]+)/x;
my $text_re =
qr(
Expand Down Expand Up @@ -496,36 +496,44 @@ sub _add_text {
$parent, $pass) : undef);
} elsif ($+{trange}) {
require Org::Element::TimeRange;
require Org::Element::Timestamp;
$el = Org::Element::TimeRange->new(
_str => $+{trange},
document => $self, parent => $parent,
datetime1 => __parse_timestamp($+{trange_ts1}),
datetime2 => __parse_timestamp($+{trange_ts2}),
);
my $opts = {allow_event_duration=>0, allow_repeater=>0};
$el->ts1(Org::Element::Timestamp->new(
document=>$self, parent=>$parent));
$el->ts1->_parse_timestamp($+{trange_ts1}, $opts);
$el->ts2(Org::Element::Timestamp->new(
document=>$self, parent=>$parent));
$el->ts2->_parse_timestamp($+{trange_ts2}, $opts);
$el->children([$el->ts1, $el->ts2]);
} elsif ($+{tstamp}) {
require Org::Element::Timestamp;
$el = Org::Element::Timestamp->new(
_str=>$+{tstamp},
document => $self, parent => $parent,
datetime => __parse_timestamp($+{tstamp}),
);
$el->_parse_timestamp($+{tstamp});
} elsif ($+{act_trange}) {
require Org::Element::TimeRange;
require Org::Element::Timestamp;
$el = Org::Element::TimeRange->new(
_str=>$+{act_trange},
document => $self, parent => $parent,
is_active => 1,
datetime1 => __parse_timestamp($+{act_trange_ts1}),
datetime2 => __parse_timestamp($+{act_trange_ts2}),
);
my $opts = {allow_event_duration=>0, allow_repeater=>0};
$el->ts1(Org::Element::Timestamp->new(
document=>$self, parent=>$parent));
$el->ts1->_parse_timestamp($+{act_trange_ts1}, $opts);
$el->ts2(Org::Element::Timestamp->new(
document=>$self, parent=>$parent));
$el->ts2->_parse_timestamp($+{act_trange_ts2}, $opts);
$el->children([$el->ts1, $el->ts2]);
} elsif ($+{act_tstamp}) {
require Org::Element::Timestamp;
$el = Org::Element::Timestamp->new(
_str=>$+{act_tstamp},
document => $self, parent => $parent,
is_active => 1,
datetime => __parse_timestamp($+{act_tstamp}),
);
$el->_parse_timestamp($+{act_tstamp});
} elsif ($+{markup_start}) {
require Org::Element::Text;
$el = Org::Element::Text->new(
Expand Down Expand Up @@ -718,23 +726,6 @@ sub _add_plain_text {
push @{ $parent->children }, $el;
}

# temporary place
sub __parse_timestamp {
require DateTime;
my ($ts) = @_;
$ts =~ /^(?:\[|<)?(\d{4})-(\d{2})-(\d{2}) \s
(?:\w{2,3}
(?:\s (\d{2}):(\d{2}))?)?
(?:\]|>)?
$/x
or die "Can't parse timestamp string: $ts";
my %dt_args = (year => $1, month=>$2, day=>$3);
if (defined($4)) { $dt_args{hour} = $4; $dt_args{minute} = $5 }
my $res = DateTime->new(%dt_args);
$res or die "Invalid date: $ts";
$res;
}

sub __split_tags {
[$_[0] =~ /:([^:]+)/g];
}
Expand Down
24 changes: 7 additions & 17 deletions lib/Org/Element/TimeRange.pm
Expand Up @@ -7,23 +7,17 @@ extends 'Org::Element::Base';

=head1 ATTRIBUTES
=head2 datetime1 => DATETIME_OBJ
=head2 ts1 => TIMESTAMP ELEMENT
=cut

has datetime1 => (is => 'rw');
has ts1 => (is => 'rw');

=head2 datetime2 => DATETIME_OBJ
=head2 ts2 => TIMESTAMP ELEMENT
=cut

has datetime2 => (is => 'rw');

=head2 is_active => BOOL
=cut

has is_active => (is => 'rw');
has ts2 => (is => 'rw');


=head1 METHODS
Expand All @@ -36,13 +30,9 @@ sub as_string {
my ($self) = @_;
return $self->_str if $self->_str;
join("",
$self->is_active ? "<" : "[",
$self->datetime1->ymd, " ",
# XXX Thu 11:59
$self->is_active ? ">--<" : "]--[",
$self->datetime2->ymd, " ",
# XXX Thu 11:59
$self->is_active ? ">" : "]",
$self->ts1->as_string,
"--",
$self->ts2->as_string
);
}

Expand Down
128 changes: 126 additions & 2 deletions lib/Org/Element/Timestamp.pm
Expand Up @@ -13,6 +13,31 @@ extends 'Org::Element::Base';

has datetime => (is => 'rw');

=head2 has_time => BOOL
=cut

has has_time => (is => 'rw');

=head2 event_duration => INT
Event duration in seconds, e.g. for event timestamp like this:
<2011-03-23 10:15-13:25>
event_duration is 7200+600=7800 (2 hours 10 minutes).
=cut

has event_duration => (is => 'rw');

=head2 recurrence => DateTime::Event::Recurrence object
=cut

has recurrence => (is => 'rw');
has _repeater => (is => 'rw'); # stores the raw repeater spec

=head2 is_active => BOOL
=cut
Expand All @@ -26,17 +51,116 @@ has is_active => (is => 'rw');
=cut

our @dow = (undef, qw(Mon Tue Wed Thu Fri Sat Sun));

sub as_string {
my ($self) = @_;
return $self->_str if $self->_str;
my $dt = $self->datetime;
my ($hour2, $min2);
if ($self->event_duration) {
my $hour = $dt->hour;
my $min = $dt->minute;
my $mins = $self->event_duration / 60;
$min2 = $min + $mins;
my $hours = int ($min2 / 60);
$hour2 = $hour + $hours;
$min2 = $min2 % 60;
}
join("",
$self->is_active ? "<" : "[",
$self->datetime->ymd, " ",
# XXX Thu 11:59
$dt->ymd, " ",
$dow[$dt->day_of_week],
$self->has_time ? (
" ",
sprintf("%02d:%02d", $dt->hour, $dt->minute),
defined($hour2) ? (
"-",
sprintf("%02d:%02d", $hour2, $min2),
) : (),
$self->_repeater ? (
" +",
$self->_repeater,
) : (),
) : (),
$self->is_active ? ">" : "]",
);
}

sub _parse_timestamp {
require DateTime;
require DateTime::Event::Recurrence;
my ($self, $str, $opts) = @_;
$opts //= {};
$opts->{allow_event_duration} //= 1;
$opts->{allow_repeater} //= 1;

$str =~ /^(?<open_bracket> \[|<)
(?<year> \d{4})-(?<mon> \d{2})-(?<day> \d{2}) \s
(?:
(?<dow> \w{2,3})
(?:\s
(?<hour> \d{2}):(?<min> \d{2})
(?:-
(?<event_duration>
(?<hour2> \d{2}):(?<min2> \d{2}))
)?
)?
(?:\s\+
(?<repeater>
(?<repeater_interval> \d+)
(?<repeater_unit> [dwmy])
)
)?
)?
(?<close_bracket> \]|>)
$/x
or die "Can't parse timestamp string: $str";
die "Duration not allowed in timestamp: $str"
if !$opts->{allow_event_duration} && $+{event_duration};
die "Repeater ($+{repeater}) not allowed in timestamp: $str"
if !$opts->{allow_repeater} && $+{repeater};

$self->is_active($+{open_bracket} eq '<' ? 1:0)
unless defined $self->is_active;

if ($+{event_duration} && !defined($self->event_duration)) {
$self->event_duration(
($+{hour2}-$+{hour})*3600 +
($+{min2} -$+{min} )*60
);
}

if ($+{repeater} && !$self->recurrence) {
my $r;
my $i = $+{repeater_interval};
my $u = $+{repeater_unit};
if ($u eq 'd') {
$r = DateTime::Event::Recurrence->daily(interval=>$i);
} elsif ($u eq 'w') {
$r = DateTime::Event::Recurrence->weekly(interval=>$i);
} elsif ($u eq 'm') {
$r = DateTime::Event::Recurrence->monthly(interval=>$i);
} elsif ($u eq 'y') {
$r = DateTime::Event::Recurrence->yearly(interval=>$i);
} else {
die "BUG: Unknown repeater unit $u in timestamp $str";
}
$self->recurrence($r);
$self->_repeater($+{repeater});
}

my %dt_args = (year => $+{year}, month=>$+{mon}, day=>$+{day});
if (defined($+{hour})) {
$dt_args{hour} = $+{hour};
$dt_args{minute} = $+{min};
$self->has_time(1);
} else {
$self->has_time(0);
}
$self->datetime(DateTime->new(%dt_args));
}

1;
__END__
Expand Down
24 changes: 22 additions & 2 deletions t/timerange.t
Expand Up @@ -37,9 +37,29 @@ _
my %args = @_;
my $doc = $args{result};
my $elems = $args{elements};
ok( $elems->[0]->is_active, "tr[0] is_active");
ok(!$elems->[3]->is_active, "tr[3] !is_active");
ok( $elems->[0]->ts1->is_active, "tr[0] is_active");
ok(!$elems->[3]->ts1->is_active, "tr[3] !is_active");
},
);

test_parse(
name => 'event duration not allowed in timerange',
filter_elements => sub {
$_[0]->isa('Org::Element::TimeRange') },
doc => <<'_',
<2011-03-23 Wed 11:28-12:00>--<2011-03-24 Thu>
_
dies => 1,
);

test_parse(
name => 'repeater not allowed in timerange',
filter_elements => sub {
$_[0]->isa('Org::Element::TimeRange') },
doc => <<'_',
<2011-03-23 Wed +1w>--<2011-03-24 Thu>
_
dies => 1,
);

done_testing();

0 comments on commit d19462e

Please sign in to comment.