"Fossies" - the Fresh Open Source Software Archive

Member "remind-03.03.09/contrib/ical2rem.pl" (15 Oct 2021, 9372 Bytes) of package /linux/misc/remind-03.03.09.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ical2rem.pl" see the Fossies "Dox" file reference documentation.

    1 #!/usr/bin/perl -w
    2 #
    3 # ical2rem.pl - 
    4 # Reads iCal files and outputs remind-compatible files.   Tested ONLY with
    5 #   calendar files created by Mozilla Calendar/Sunbird. Use at your own risk.
    6 # Copyright (c) 2005, 2007, Justin B. Alcorn
    7 
    8 # This program is free software; you can redistribute it and/or
    9 # modify it under the terms of the GNU General Public License
   10 # as published by the Free Software Foundation; either version 2
   11 # of the License, or (at your option) any later version.
   12 # 
   13 # This program is distributed in the hope that it will be useful,
   14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16 # GNU General Public License for more details.
   17 # 
   18 # You should have received a copy of the GNU General Public License
   19 # along with this program; if not, write to the Free Software
   20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   21 #
   22 #
   23 # version 0.5.2 2007-03-23
   24 #   - BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME
   25 #   - remove project-lead-time, since Category was a non-standard attribute
   26 #   - NOTE: There is a bug in iCal::Parser v1.14 that causes multiple calendars to
   27 #       fail if a calendar with recurring events is followed by a calendar with no
   28 #       recurring events.  This has been reported to the iCal::Parser author.
   29 # version 0.5.1 2007-03-21
   30 #   - BUG: Handle multiple calendars on STDIN
   31 #   - add --heading option for priority on section headers
   32 # version 0.5 2007-03-21
   33 #   - Add more help options
   34 #   - --project-lead-time option
   35 #   - Supress printing of heading if there are no todos to print
   36 # version 0.4
   37 #   - Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg
   38 #   - Change to GetOptions
   39 #   - Change to pipe
   40 #   - Add --label, --help options
   41 #   - Add Help Text
   42 #   - Change to subroutines
   43 #   - Efficiency and Cleanup
   44 # version 0.3
   45 #   - Convert to GPL (Thanks to Mark Stosberg)
   46 #   - Add usage
   47 # version 0.2
   48 #   - add command line switches
   49 #   - add debug code
   50 #   - add SCHED _sfun keyword
   51 #   - fix typos
   52 # version 0.1 - ALPHA CODE.  
   53 
   54 =head1 SYNOPSIS
   55 
   56  cat /path/to/file*.ics | ical2rem.pl > ~/.ical2rem
   57 
   58  All options have reasonable defaults:
   59  --label               Calendar name (Default: Calendar)
   60  --lead-time           Advance days to start reminders (Default: 3)
   61  --todos, --no-todos   Process Todos? (Default: Yes)
   62  --heading             Define a priority for static entries
   63  --help                Usage
   64  --man                 Complete man page
   65 
   66 Expects an ICAL stream on STDIN. Converts it to the format
   67 used by the C<remind> script and prints it to STDOUT. 
   68 
   69 =head2 --label
   70 
   71   ical2rem.pl --label "Bob's Calendar"
   72 
   73 The syntax generated includes a label for the calendar parsed.
   74 By default this is "Calendar". You can customize this with 
   75 the "--label" option.
   76 
   77 =head2 --lead-time 
   78 
   79   ical2rem.pl --lead-time 3
   80  
   81 How may days in advance to start getting reminders about the events. Defaults to 3. 
   82 
   83 =head2 --no-todos
   84 
   85   ical2rem.pl --no-todos
   86 
   87 If you don't care about the ToDos the calendar, this will surpress
   88 printing of the ToDo heading, as well as skipping ToDo processing. 
   89 
   90 =head2 --heading
   91 
   92   ical2rem.pl --heading "PRIORITY 9999"
   93 
   94 Set an option on static messages output.  Using priorities can made the static messages look different from
   95 the calendar entries.  See the file defs.rem from the remind distribution for more information.
   96 
   97 =cut 
   98 
   99 use strict;
  100 use iCal::Parser;
  101 use DateTime;
  102 use Getopt::Long 2.24 qw':config auto_help';
  103 use Pod::Usage;
  104 use Data::Dumper;
  105 use vars '$VERSION';
  106 $VERSION = "0.5.2";
  107 
  108 # Declare how many days in advance to remind
  109 my $DEFAULT_LEAD_TIME = 3;
  110 my $PROCESS_TODOS     = 1;
  111 my $HEADING           = "";
  112 my $help;
  113 my $man;
  114 
  115 my $label = 'Calendar';
  116 GetOptions (
  117     "label=s"     => \$label,
  118     "lead-time=i" => \$DEFAULT_LEAD_TIME,
  119     "todos!"      => \$PROCESS_TODOS,
  120     "heading=s"   => \$HEADING,
  121     "help|?"      => \$help, 
  122     "man"         => \$man
  123 );
  124 pod2usage(1) if $help;
  125 pod2usage(-verbose => 2) if $man;
  126 
  127 my $month = ['None','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  128 
  129 my @calendars;
  130 my $in;
  131 
  132 while (<>) {
  133     $in .= $_;
  134     if (/END:VCALENDAR/) {
  135         push(@calendars,$in);
  136         $in = "";
  137     }
  138 }
  139 my $parser = iCal::Parser->new();
  140 my $hash = $parser->parse_strings(@calendars);
  141 
  142 ##############################################################
  143 #
  144 # Subroutines 
  145 #
  146 #############################################################
  147 #
  148 # _process_todos()
  149 # expects 'todos' hashref from iCal::Parser is input
  150 # returns String to output
  151 sub _process_todos {
  152     my $todos = shift; 
  153     
  154     my ($todo, @newtodos, $leadtime);
  155     my $output = "";
  156 
  157     $output .=  'REM '.$HEADING.' MSG '.$label.' ToDos:%"%"%'."\n";
  158 
  159 # For sorting, make sure everything's got something
  160 #   To sort on.  
  161     my $now = DateTime->now;
  162     for $todo (@{$todos}) {
  163         # remove completed items
  164         if ($todo->{'STATUS'} && $todo->{'STATUS'} eq 'COMPLETED') {
  165             next;
  166         } elsif ($todo->{'DUE'}) {
  167             # All we need is a due date, everything else is sugar
  168             $todo->{'SORT'} = $todo->{'DUE'}->clone;
  169         } elsif ($todo->{'DTSTART'}) {
  170             # for sorting, sort on start date if there's no due date
  171             $todo->{'SORT'} = $todo->{'DTSTART'}->clone;
  172         } else {
  173             # if there's no due or start date, just make it now.
  174             $todo->{'SORT'} = $now;
  175         }
  176         push(@newtodos,$todo);
  177     }
  178     if (! (scalar @newtodos)) {
  179         return "";
  180     }
  181 # Now sort on the new Due dates and print them out.  
  182     for $todo (sort { DateTime->compare($a->{'SORT'}, $b->{'SORT'}) } @newtodos) {
  183         my $due = $todo->{'SORT'}->clone();
  184         my $priority = "";
  185         if (defined($todo->{'PRIORITY'})) {
  186             if ($todo->{'PRIORITY'} == 1) {
  187                 $priority = "PRIORITY 1000";
  188             } elsif ($todo->{'PRIORITY'} == 3) {
  189                 $priority = "PRIORITY 7500";
  190             }
  191         }
  192         if (defined($todo->{'DTSTART'}) && defined($todo->{'DUE'})) {
  193             # Lead time is duration of task + lead time
  194             my $diff = ($todo->{'DUE'}->delta_days($todo->{'DTSTART'})->days())+$DEFAULT_LEAD_TIME;
  195             $leadtime = "+".$diff;
  196         } else {
  197             $leadtime = "+".$DEFAULT_LEAD_TIME;
  198         }
  199         $output .=  "REM ".$due->month_abbr." ".$due->day." ".$due->year." $leadtime $priority MSG \%a $todo->{'SUMMARY'}\%\"\%\"\%\n";
  200     }
  201     $output .= 'REM '.$HEADING.' MSG %"%"%'."\n";
  202     return $output;
  203 }
  204 
  205 
  206 #######################################################################
  207 #
  208 #  Main Program
  209 #
  210 ######################################################################
  211 
  212 print _process_todos($hash->{'todos'}) if $PROCESS_TODOS;
  213 
  214 my ($leadtime, $yearkey, $monkey, $daykey,$uid,%eventsbyuid);
  215 print 'REM '.$HEADING.' MSG '.$label.' Events:%"%"%'."\n";
  216 my $events = $hash->{'events'};
  217 foreach $yearkey (sort keys %{$events} ) {
  218     my $yearevents = $events->{$yearkey};
  219     foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){
  220         my $monevents = $yearevents->{$monkey};
  221         foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) {
  222             my $dayevents = $monevents->{$daykey};
  223             foreach $uid (sort {
  224                             DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'})    
  225                             } keys %{$dayevents}) {
  226                 my $event = $dayevents->{$uid};
  227                if ($eventsbyuid{$uid}) {
  228                     my $curreventday = $event->{'DTSTART'}->clone;
  229                     $curreventday->truncate( to => 'day' );
  230                     $eventsbyuid{$uid}{$curreventday->epoch()} =1;
  231                     for (my $i = 0;$i < $DEFAULT_LEAD_TIME && !defined($event->{'LEADTIME'});$i++) {
  232                         if ($eventsbyuid{$uid}{$curreventday->subtract( days => $i+1 )->epoch() }) {
  233                             $event->{'LEADTIME'} = $i;
  234                         }
  235                     }
  236                 } else {
  237                     $eventsbyuid{$uid} = $event;
  238                     my $curreventday = $event->{'DTSTART'}->clone;
  239                     $curreventday->truncate( to => 'day' );
  240                     $eventsbyuid{$uid}{$curreventday->epoch()} =1;
  241                 }
  242 
  243             }
  244         }
  245     }
  246 }
  247 foreach $yearkey (sort keys %{$events} ) {
  248     my $yearevents = $events->{$yearkey};
  249     foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){
  250         my $monevents = $yearevents->{$monkey};
  251         foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) {
  252             my $dayevents = $monevents->{$daykey};
  253             foreach $uid (sort {
  254                             DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'})
  255                             } keys %{$dayevents}) {
  256                 my $event = $dayevents->{$uid};
  257                 if (exists($event->{'LEADTIME'})) {
  258                     $leadtime = "+".$event->{'LEADTIME'};
  259                 } else {
  260                     $leadtime = "+".$DEFAULT_LEAD_TIME;
  261                 }
  262                 my $start = $event->{'DTSTART'};
  263                 print "REM ".$start->month_abbr." ".$start->day." ".$start->year." $leadtime ";
  264                 if ($start->hour > 0) { 
  265                     print " AT ";
  266                     print $start->strftime("%H:%M");
  267                     print " SCHED _sfun MSG %a %2 ";
  268                 } else {
  269                     print " MSG %a ";
  270                 }
  271                 print "%\"$event->{'SUMMARY'}";
  272                 print " at $event->{'LOCATION'}" if $event->{'LOCATION'};
  273                 print "\%\"%\n";
  274             }
  275         }
  276     }
  277 }
  278 exit 0;
  279 #:vim set ft=perl ts=4 sts=4 expandtab :