Permalink
Browse files

refactor Timestamp & TimeRange, parses event duration & repeater inte…

…rval
  • Loading branch information...
1 parent 8430927 commit d19462e1337fa207ed73228b44e79c0b1e616b92 Steven Haryanto (on PC) committed Mar 23, 2011
Showing with 249 additions and 75 deletions.
  1. +7 −7 dist.ini
  2. +22 −31 lib/Org/Document.pm
  3. +7 −17 lib/Org/Element/TimeRange.pm
  4. +126 −2 lib/Org/Element/Timestamp.pm
  5. +22 −2 t/timerange.t
  6. +56 −1 t/timestamp.t
  7. +9 −15 todo.org
View
@@ -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
View
@@ -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(
@@ -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(
@@ -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];
}
@@ -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
@@ -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
);
}
@@ -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
@@ -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__
View
@@ -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();
Oops, something went wrong.

0 comments on commit d19462e

Please sign in to comment.