Update to ical2rem version 0.7.
All checks were successful
Remind unit tests / tests (push) Successful in 31s

This commit is contained in:
Dianne Skoll
2024-09-05 12:44:17 -04:00
parent 5d7f55c8d5
commit 7d501cda6f
7 changed files with 392 additions and 20 deletions

View File

@@ -0,0 +1,3 @@
The upstream GitHub project for ical2rem is:
https://github.com/jbalcorn/ical2rem

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Justin B. Alcorn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,103 @@
# ical2rem
The original iCal to Remind script, first released in 2005.
Reads iCal files and outputs remind-compatible files. Tested ONLY with
calendar files created by Mozilla Calendar/Sunbird. Use at your own risk.
## License
In 2005, this was released with the Gnu Public License V2. However, I am changing it to the MIT License, since that provides greater freedom to do with this code what you want.
Copyright (c) 2005, 2007, 2019 Justin B. Alcorn
## How I use Remind and Google Calendar together
- My family has a Google Email domain, and our email addresses all end in the same domain. We all use Google Calendars and I want to mail reminders to each of the family members containing both Google Calendar and .reminder information.
- Under my ~/.rem/ directory each family member has a directory. Each directory contains a standard remind file called 'reminders' that at the very least has the line "INCLUDE /home/jalcorn/.rem/<username>/ical2rem" and flag files indicating whether they want Daily or Weekly reminders. My reminders files references my standard .reminders file, and I also have a flag so if I run a Test run I'll get it. There's actually a lot more files (I have a big family).
````
./rem
./son1:
drwxrwxr-x 2 jalcorn jalcorn 4096 Dec 12 14:02 .
drwxr-xr-x 12 jalcorn jalcorn 4096 Dec 12 14:13 ..
-rw-rw-r-- 1 jalcorn jalcorn 51 Mar 3 06:10 ical2rem
lrwxrwxrwx 1 jalcorn jalcorn 33 Oct 27 2016 son1.ics -> /home/jalcorn/calendars/son1.ics
-rw-rw-r-- 1 jalcorn jalcorn 976 Dec 12 14:02 reminders
-rw-rw-r-- 1 jalcorn jalcorn 0 Oct 27 2016 Weekly
./justin:
drwxrwxr-x 2 jalcorn jalcorn 4096 Feb 27 08:29 .
drwxr-xr-x 12 jalcorn jalcorn 4096 Dec 12 14:13 ..
lrwxrwxrwx 1 jalcorn jalcorn 32 Oct 27 2016 son1.ics -> /home/jalcorn/calendars/son1.ics
-rw-rw-r-- 1 jalcorn jalcorn 0 Nov 7 2016 Daily
lrwxrwxrwx 1 jalcorn jalcorn 34 Oct 27 2016 family.ics -> /home/jalcorn/calendars/family.ics
-rw-rw-r-- 1 jalcorn jalcorn 37320 Mar 3 06:10 ical2rem
lrwxrwxrwx 1 jalcorn jalcorn 34 Oct 27 2016 justin.ics -> /home/jalcorn/calendars/justin.ics
lrwxrwxrwx 1 jalcorn jalcorn 24 Nov 7 2016 reminders -> /home/jalcorn/.reminders
lrwxrwxrwx 1 jalcorn jalcorn 34 Oct 27 2016 vmd.ics -> /home/jalcorn/calendars/vmd.ics
-rw-rw-r-- 1 jalcorn jalcorn 0 Oct 27 2016 Test
-rw-rw-r-- 1 jalcorn jalcorn 0 Nov 7 2016 Weekly
````
- bin/getgooglecals.sh runs out of crontab and downloads whatever calendars I want. Note that we can also download organization calendars, I've included a public one here (Cleveland Heights Vocal Music Department calendar).
- dailyreminders.sh is linked to weeklyreminders.sh and testreminders.sh so I can run it in different modes. The concatenate the various calendar outputs as a single remind file then send the reminders via email.
### Example: .rem/son1/reminders file:
````
INCLUDE /home/jalcorn/.rem/defs.rem
INCLUDE /home/jalcorn/.rem/float
INCLUDE /home/jalcorn/.rem/son1/ical2rem
fset _weeks() coerce("STRING", (trigdate()-today())/7) + plural((trigdate()-today())/7, " week")
FSET _sfun(x) choose(x, -60, 30, 5, 0)
FSET oldfloat(y,m,d) trigger(MAX(realtoday(), date(y,m,d)))
FSET due(y,m,d) "(" + (date(y,m,d)-trigdate()) + ")"
SET fullmoon moondate(2)
REM [trigger(realtoday())] SPECIAL SHADE 145 70 100 %
REM [float(2019,4,15,105)] MSG File tax return [due(2017,4,15)]%
REM PRIORITY 9999 MSG %"%"%
INCLUDE /home/jalcorn/.rem/bdays
SET $LongDeg 81
SET $LongMin 11
SET $LongSec 11
SET $LatDeg 41
SET $LatMin 11
SET $LatSec 11
REM [trigger(moondate(2))] +1 MSG %"Full Moon%" %b%
fset _srtd() coerce("STRING", _no_lz(_am_pm(sunrise(today()))))
fset _sstd() coerce("STRING", _no_lz(_am_pm(sunset(today()))))
MSG Sun is up today from [_srtd()] to [_sstd()].%"%"%
````
## Revision History
### Version 0.7 2024-09-04
- ISSUE 8: New version of remind complains if _sfun isn't defined. Output a header
to define a function that does nothing if the function doesn't exist.
### Version 0.6 2019-03-01
- Publish on GitHub and change license to MIT License
- Add supporting files and explanation of how I use it
### version 0.5.2 2007-03-23
- BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME
- remove project-lead-time, since Category was a non-standard attribute
- NOTE: There is a bug in iCal::Parser v1.14 that causes multiple calendars to
fail if a calendar with recurring events is followed by a calendar with no
recurring events. This has been reported to the iCal::Parser author.
### version 0.5.1 2007-03-21
- BUG: Handle multiple calendars on STDIN
- add --heading option for priority on section headers
### version 0.5 2007-03-21
- Add more help options
- --project-lead-time option
- Supress printing of heading if there are no todos to print
### version 0.4
- Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg
- Change to GetOptions
- Change to pipe
- Add --label, --help options
- Add Help Text
- Change to subroutines
- Efficiency and Cleanup
### version 0.3
- Convert to GPL (Thanks to Mark Stosberg)
- Add usage
### version 0.2
- add command line switches
- add debug code
- add SCHED _sfun keyword
- fix typos
### version 0.1 - ALPHA CODE.

View File

@@ -0,0 +1,82 @@
#!/usr/bin/perl -w
#
# cal_futureonly.pl -
# Reads iCal files and outputs events between 1 month ago and 1 year from now.
# Copyright (c) 2005, 2007, 2019 Justin B. Alcorn
=head1 SYNOPSIS
cal_futureonly.pl --file=filname.ics > output.ics
--help Usage
--man Complete man page
--infile (REQUIRED) name of input calendar file
--file (REQUIRED) name of output calendar file
Expects an ICAL stream on STDIN. Converts it to the format
used by the C<remind> script and prints it to STDOUT.
=head2 --infile
Input file
=head2 --file
Output File
=cut
use strict;
use Data::ICal;
use Data::ICal::Entry;
use DateTime::Span;
use Data::ICal::DateTime;
use DateTime;
use Getopt::Long 2.24 qw':config auto_help';
use Pod::Usage;
use Data::Dumper;
use vars '$VERSION';
$VERSION = "0.1";
my $help;
my $man;
my $infile;
my $file;
my $debug = 0;
GetOptions (
"help|?" => \$help,
"man" => \$man,
"debug" => \$debug,
"infile=s" => \$infile,
"file=s" => \$file
);
pod2usage(1) if $help;
pod2usage(1) if (! $file);
pod2usage(-verbose => 2) if $man;
my $limit = DateTime->now();
$limit->subtract( months => 1);
my $endlimit = DateTime->now()->add(years =>1);
print STDERR "including events from: ",$limit->ymd," to: ".$endlimit->ymd,"\n" if $debug;
my $span = DateTime::Span->from_datetimes( start => $limit, end => $endlimit );
print STDERR "Parsing $infile\n" if $debug;
my $cal = Data::ICal->new(filename => $infile);
if (! $cal) {
die "Died Trying to read $infile :".$cal->error_message;
}
#my $archive = Data::ICal->new(filename => 'archive.ics');
print "Output = $file\n" if $debug;
my $new = Data::ICal->new();
if (! $new) {
die $new->error_message;
}
my @events = $cal->events($span);
$new->add_entries(@events);
open(NEW, ">$file");
print NEW $new->as_string;
close NEW;
exit 0;
#:vim set ft=perl ts=4 sts=4 expandtab :

View File

@@ -0,0 +1,45 @@
#!/bin/bash
r=`basename $0`
if [ $r == 'weeklyreminders.sh' ];
then
t=14;
w=Weekly;
elif [ $r == 'dailyreminders.sh' ];
then
t=3;
w=Daily;
else
t=5
w=Test;
fi
cd .rem
for d in * ;
do
if [ "$( ls -A $d/$w 2>/dev/null )" ];
then
echo "Sending a $w reminder to $d"
ft=/tmp/$d-t-$$.txt
f=/tmp/$d-$$.txt
echo "Reminders for next $t days:" >> $f
cat /dev/null > $d/ical2rem
for c in $d/*.ics
do
calname=`basename $c .ics | tr a-z A-Z`
cat $c 2>/dev/null | sed -e "s/^SUMMARY:/SUMMARY: {${calname}} /" \
| sed -e 's/DT\([A-Z]*\);TZID=UTC:\([0-9T]*\)/DT\1:\2Z/' >> $ft
done
cat $ft | ~/bin/ical2rem.pl --label "Online Calendar" --heading "PRIORITY 9999" --lead-time $t >> $d/ical2rem
if [ -e $d/reminders ];then r="${d}/reminders"; else r="${d}/ical2rem";fi
/usr/bin/remind -q -iplain=1 $r >> $f
echo "
All calendars can be accessed by logging into https://calendar.google.com/ as $d@jalcorn.net
" >> $f
cat $f | mail -s "$w Reminders for $d" $d@jalcorn.net;
cat $f
rm $f
rm $ft
fi;
done

View File

@@ -0,0 +1,21 @@
#!/bin/sh
#
# Get google calendars, fix issues caused by changes in Google calendars, and remove all past events.
#
# Obviously, I've removed the private hashes from private calendars.
#
cd ~/calendars
wget -q -O full/justin.ics --no-check-certificate https://www.google.com/calendar/ical/jbalcorn\%40gmail.com/private-aaaaaaaaaaaaaaaaaaaaaaaaaa/basic.ics
wget -q -O full/family.ics --no-check-certificate https://www.google.com/calendar/ical/jalcorn.net_aaaaaaaaaaaaaaaaaaaaaaaaaa\%40group.calendar.google.com/private-6c42a79dec0b3b3bb7b9b0ebf9776bc1/basic.ics
wget -q -O full/son1.ics --no-check-certificate https://www.google.com/calendar/ical/son1\%40jalcorn.net/private-aaaaaaaaaaaaaaaaaaaaaaaaaa/basic.ics
wget -q -O full/vmd.ics --no-check-certificate https://calendar.google.com/calendar/ical/chuh.org_0pmkefjkiqc4snoel7occlslh8%40group.calendar.google.com/public/basic.ics
for i in full/*.ics;do
cat $i 2>/dev/null | sed -e 's/DT\([A-Z]*\);TZID=UTC:\([0-9T]*\)/DT\1:\2Z/' > /tmp/temp.ics
cp /tmp/temp.ics $i
done
~/bin/cal_futureonly.pl --infile=full/justin.ics --file=justin.ics
~/bin/cal_futureonly.pl --infile=full/family.ics --file=family.ics
~/bin/cal_futureonly.pl --infile=full/son1.ics --file=son1.ics
~/bin/cal_futureonly.pl --infile=full/vmd.ics --file=vmd.ics

View File

@@ -19,7 +19,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# version 0.6 2019-03-01
# - Updates to put on GitHub
# version 0.5.2 2007-03-23
# - BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME
# - remove project-lead-time, since Category was a non-standard attribute
@@ -32,7 +33,7 @@
# version 0.5 2007-03-21
# - Add more help options
# - --project-lead-time option
# - Suppress printing of heading if there are no todos to print
# - Supress printing of heading if there are no todos to print
# version 0.4
# - Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg
# - Change to GetOptions
@@ -56,12 +57,19 @@
cat /path/to/file*.ics | ical2rem.pl > ~/.ical2rem
All options have reasonable defaults:
--label Calendar name (Default: Calendar)
--label Calendar name (Default: Calendar)
--start Start of time period to parse (parsed by str2time)
--end End of time period to parse
--lead-time Advance days to start reminders (Default: 3)
--todos, --no-todos Process Todos? (Default: Yes)
--heading Define a priority for static entries
--help Usage
--man Complete man page
--iso8601 Use YYYY-MM-DD date format
--locations, --no-locations Include location? (Default: Yes)
--end-times, --no-end-times Include event end times in reminder text
(Default: No)
--heading Define a priority for static entries
--help Usage
--debug Enable debug output
--man Complete man page
Expects an ICAL stream on STDIN. Converts it to the format
used by the C<remind> script and prints it to STDOUT.
@@ -74,6 +82,14 @@ The syntax generated includes a label for the calendar parsed.
By default this is "Calendar". You can customize this with
the "--label" option.
=head2 --iso8601
Use YYYY-MM-DD date format in output instead of Mmm DD YYYY
=head2 --locations, --no-locations
Whether or not to include locations in events
=head2 --lead-time
ical2rem.pl --lead-time 3
@@ -84,7 +100,7 @@ How may days in advance to start getting reminders about the events. Defaults to
ical2rem.pl --no-todos
If you don't care about the ToDos the calendar, this will suppress
If you don't care about the ToDos the calendar, this will surpress
printing of the ToDo heading, as well as skipping ToDo processing.
=head2 --heading
@@ -98,6 +114,7 @@ the calendar entries. See the file defs.rem from the remind distribution for mo
use strict;
use iCal::Parser;
use Date::Parse;
use DateTime;
use Getopt::Long 2.24 qw':config auto_help';
use Pod::Usage;
@@ -108,19 +125,31 @@ $VERSION = "0.5.2";
# Declare how many days in advance to remind
my $DEFAULT_LEAD_TIME = 3;
my $PROCESS_TODOS = 1;
my $HEADING = "";
my $HEADING = "";
my $help;
my $debug;
my $man;
my $iso8601;
my $do_location = 1;
my $do_end_times;
my $start;
my $end;
my $label = 'Calendar';
GetOptions (
"label=s" => \$label,
"start=s" => \$start,
"end=s" => \$end,
"lead-time=i" => \$DEFAULT_LEAD_TIME,
"todos!" => \$PROCESS_TODOS,
"iso8601!" => \$iso8601,
"locations!" => \$do_location,
"end-times!" => \$do_end_times,
"heading=s" => \$HEADING,
"help|?" => \$help,
"debug" => \$debug,
"man" => \$man
);
) or pod2usage(1);
pod2usage(1) if $help;
pod2usage(-verbose => 2) if $man;
@@ -136,8 +165,22 @@ while (<>) {
$in = "";
}
}
my $parser = iCal::Parser->new();
print STDERR "Read all calendars\n" if $debug;
my(%parser_opts) = ("debug" => $debug);
if ($start) {
my $t = str2time($start);
die "Invalid time $start\n" if (! $t);
$parser_opts{'start'} = DateTime->from_epoch(epoch => $t);
}
if ($end) {
my $t = str2time($end);
die "Invalid time $end\n" if (! $t);
$parser_opts{'end'} = DateTime->from_epoch(epoch => $t);
}
print STDERR "About to parse calendars\n" if $debug;
my $parser = iCal::Parser->new(%parser_opts);
my $hash = $parser->parse_strings(@calendars);
print STDERR "Calendars parsed\n" if $debug;
##############################################################
#
@@ -209,6 +252,13 @@ sub _process_todos {
#
######################################################################
# Issue 8 https://github.com/jbalcorn/ical2rem/issues/8
# Make sure there is a _sfun function declared in the reminder file. We'll just make it do nothing here.
print 'IF args("_sfun") < 1
FSET _sfun(x) choose(x,0)
ENDIF
';
print _process_todos($hash->{'todos'}) if $PROCESS_TODOS;
my ($leadtime, $yearkey, $monkey, $daykey,$uid,%eventsbyuid);
@@ -260,20 +310,67 @@ foreach $yearkey (sort keys %{$events} ) {
$leadtime = "+".$DEFAULT_LEAD_TIME;
}
my $start = $event->{'DTSTART'};
print "REM ".$start->month_abbr." ".$start->day." ".$start->year." $leadtime ";
if ($start->hour > 0) {
print " AT ";
print $start->strftime("%H:%M");
print " +15 MSG %a %2 ";
} else {
print " MSG %a ";
my $end = $event->{'DTEND'};
my $duration = "";
if ($end and ($start->hour or $start->minute or $end->hour or $end->minute)) {
# We need both an HH:MM version of the delta, to put in the
# DURATION specifier, and a human-readable version of the
# delta, to put in the message if the user requested it.
my $seconds = $end->epoch - $start->epoch;
my $minutes = int($seconds / 60);
my $hours = int($minutes / 60);
$minutes -= $hours * 60;
$duration = sprintf("DURATION %d:%02d ", $hours, $minutes);
}
print "%\"$event->{'SUMMARY'}";
print " at $event->{'LOCATION'}" if $event->{'LOCATION'};
print "\%\"%\n";
print "REM ";
if ($iso8601) {
print $start->strftime("%F ");
} else {
print $start->month_abbr." ".$start->day." ".$start->year." ";
}
print "$leadtime ";
if ($duration or $start->hour > 0 or $start->minute > 0) {
print "AT ";
print $start->strftime("%H:%M");
print " SCHED _sfun ${duration}MSG %a %2 ";
} else {
print "MSG %a ";
}
print "%\"", &quote($event->{'SUMMARY'});
print(" at ", &quote($event->{'LOCATION'}))
if ($do_location and $event->{'LOCATION'});
print "\%\"";
if ($do_end_times and ($start->hour or $start->minute or
$end->hour or $end->minute)) {
my $start_date = $start->strftime("%F");
my $start_time = $start->strftime("%k:%M");
my $end_date = $end->strftime("%F");
my $end_time = $end->strftime("%k:%M");
# We don't want leading whitespace; some strftime's support
# disabling the pdding in the format string, but not all,
# so for maximum portability we do it ourselves.
$start_time =~ s/^\s+//;
$end_time =~ s/^\s+//;
my(@pieces);
if ($start_date ne $end_date) {
push(@pieces, $end_date);
}
if ($start_time ne $end_time) {
push(@pieces, $end_time);
}
print " (-", join(" ", @pieces), ")";
}
print "%\n";
}
}
}
}
sub quote {
local($_) = @_;
s/\[/["["]/g;
return $_;
}
exit 0;
#:vim set ft=perl ts=4 sts=4 expandtab :