org2remind.pl 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/perl
  2. # org2remind.pl: perl utility to convert org-mode appointments to remind
  3. # Copyright (C) 2011 Matt Lundin <mdl at imapmail.org>
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or (at
  7. # your option) any later version.
  8. # This program is distributed in the hope that it will be useful, but
  9. # WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. # General Public License for more details.
  12. # You should have received a copy of the GNU General Public License
  13. # along with this program. If not, see <http://www.gnu.org/licenses/>
  14. #######################################################################
  15. # Remind is a command line calendar application for Unix/Linux that
  16. # can, among other things, spit out formatted plain text calendars and
  17. # agendas.
  18. # http://www.roaringpenguin.com/products/remind
  19. #
  20. # To convert org-mode appointments to remind data, simply call the
  21. # following script on one or more org files:
  22. # perl org2remind.pl ~/org/*.org
  23. #
  24. # The results of this script can saved in a file...
  25. # perl org2remind.pl ~/org/*.org >> org.rem
  26. # ...or piped directly into a remind command...
  27. # perl org2remind.pl ~/org/*.org | remind -
  28. #
  29. # For a nice calendar of the current month, use the following:
  30. # perl org2remind.pl ~/org/*.org | remind -c -
  31. #
  32. # This is similar to org2rem.el (in the contrib directory of the
  33. # org-mode repository), except that, instead of saving files from
  34. # within org-mode, it allows for easier and faster access to
  35. # org/remind data from the command line.
  36. #
  37. # Currently, the script supports appointments (i.e., active
  38. # timestamps) and the org-diary-class sexp.
  39. #
  40. # TODO add option to include SCHEDULED and DEADLINE items.
  41. # TODO parse simple diary sexps
  42. # TODO accomodate spans <ts1>--<ts2>
  43. use strict;
  44. $/ = "\n*";
  45. # TODO check CPAN for localization tools
  46. my @daysofweek = qw/ Mon Tue Wed Thu Fri Sat Sun /;
  47. my @months = qw/ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec /;
  48. sub subtract_time {
  49. my @time1 = split(/:/, shift(@_));
  50. my $minutes1 = $time1[0] * 60 + $time1[1];
  51. my @time2 = split(/:/, shift(@_));
  52. my $minutes2 = $time2[0] * 60 + $time2[1];
  53. return $minutes2 - $minutes1;
  54. }
  55. sub trim_headline {
  56. my $headline = shift @_;
  57. # get rid of opening asterisks
  58. $headline =~ s/^\** //;
  59. # get rid of priorities - remind chokes on them
  60. $headline =~ s/\[#[A-Z]\]//;
  61. # nuke link brackets - remind chokes on them
  62. $headline =~ s/\]\[/ - /;
  63. $headline =~ s/\[\[|\]\]//;
  64. # get rid of tags
  65. $headline =~ s/:[a-zA-Z0-9:]+://;
  66. # trim trailing whitespace
  67. $headline =~ s/\s+$//;
  68. return $headline;
  69. }
  70. while (<>) {
  71. my @lines = split(/\n/, $_);
  72. my $headline = shift @lines;
  73. my $headtrim = 0;
  74. for (@lines) {
  75. if ( /<([0-9]{4}-[0-9]{2}-[0-9]{2}) # YYYY-MM-DD
  76. (?:\s+[A-Za-z]{3}) # day of week (English)
  77. (?:\s+([0-9]{2}:[0-9]{2}))? # 1st time of day
  78. (?:-([0-9]{2}:[0-9]{2}))? # second time of day
  79. (?:\s+(\+[0-9]+[dmwy]))?> # repeater
  80. /x ) {
  81. next if ( /(SCHEDULED|DEADLINE):/ );
  82. my ($timestamp, $hour1, $hour2, $repeater) = ($1, $2, $3, $4);
  83. if ($headtrim == 0) {
  84. $headline = trim_headline($headline);
  85. # only need to trim headline once
  86. $headtrim = 1;
  87. }
  88. # recurring events
  89. # TODO Figure out way to support multiples of month and year
  90. # Right now, only +1m and +1y work. This is due to a limitation
  91. # in my knowledge of remind syntax.
  92. my $trigdate;
  93. if (defined $repeater) {
  94. $repeater =~ /([0-9]+)([dmwy])/;
  95. my ($interval, $type) = ($1, $2);
  96. if ($type eq "y") {
  97. my @ymd = split(/-/, $timestamp);
  98. $trigdate = "[trigdate() >= '$timestamp']";
  99. $timestamp = "$months[$ymd[1] - 1] $ymd[2]";
  100. } elsif ($type eq "w") {
  101. $timestamp = sprintf("%s *%d", $timestamp, ($interval * 7));
  102. } elsif ($type eq "m") {
  103. my @ymd = split(/-/, $timestamp);
  104. $trigdate = "[trigdate() >= '$timestamp']";
  105. $timestamp = $ymd[2];
  106. } elsif ($type eq "d") {
  107. $timestamp = sprintf("%s *%d", $timestamp, $interval);
  108. }
  109. }
  110. # calculate duration
  111. $timestamp = join(" ", $timestamp,"AT",$hour1) if defined($hour1);
  112. if (defined $hour2) {
  113. my $duration = subtract_time $hour1, $hour2;
  114. $timestamp = sprintf("%s +%d", $timestamp, $duration);
  115. }
  116. $timestamp = sprintf("%s %s %s", $timestamp, "SATISFY", $trigdate)
  117. if defined($trigdate);
  118. printf "REM %s MSG %s\n", $timestamp, $headline;
  119. }
  120. # org-diary-class
  121. if (/%%\(org-diary-class\s+
  122. ([0-9]{1,2}\s+[0-9]{1,2}\s+[0-9]{4})\s+ # start mdy
  123. ([0-9]{1,2}\s+[0-9]{1,2}\s+[0-9]{4})\s+ # end mdy
  124. ([0-9])\s* # dow
  125. ([0-9])? # iso exception - not yet supported
  126. \)\s*(.*)$
  127. /x) {
  128. my ($start, $end, $dow, $iso, $event) = ($1, $2, $3, $4, $5);
  129. # differentiate between "<%%.*>" and "%%.* event"
  130. # the former uses the headline; the latter the string after sexp
  131. $headline = $event unless (/<%%.*>/);
  132. $dow = $daysofweek[$dow - 1];
  133. my ($m1, $d1, $y1) = split(/\s+/, $start);
  134. my ($m2, $d2, $y2) = split(/\s+/, $end);
  135. if ($headtrim == 0) {
  136. $headline = trim_headline($headline);
  137. $headtrim = 1;
  138. }
  139. printf
  140. "REM %s UNTIL %d-%02d-%02d SATISFY [trigdate() >= '%d-%02d-%02d'] MSG %s\n",
  141. $dow, $y2, $m2, $d2, $y1, $m1, $d1, $headline;
  142. }
  143. # TODO add support for basic diary sexps
  144. }
  145. }