Rockbox General > Rockbox General Discussion
I've Made an iCal to .memo Exporter
jasonsilver:
This is likely the wrong forum, but I couldn't get the more appropriate forum to open up for me-- weird.
I looked all over, and couldn't find it already done, so I've written a Perl script to share with the community.
I use multiple Google calendars. If you download all calendars as a zip, then inflate them into one folder, then place this script in the folder and run it from the command line, it is SUPPOSED to create a .memo file called 'output_memo.txt'.
I would love code improvements, bug reports, and general suggestions should you have them.
I'll try pasting it in, as well as attaching my script. (UPDATE: had to rename the script to .txt to upload. Re-rename it back to .pl)
Thanks!
Jason Silver, dedicated RockBox user and longtime lurker. (sorry). :-[
--- Code: ---#!/usr/bin/perl -w
use strict;
use Time::Local;
local $SIG{__WARN__}=sub{}; #Temporarily suppress warnings
my %ITEM = (); #Create hash for each appointment's data
my @t = localtime(time);
my $gmt_offset_in_seconds = timegm(@t) - timelocal(@t); # this calculates the offset for server time from the iCal's "zulu" time (GMT).
my @all_files = <*>;
open (OUTPUT, '>output_memo.txt'); # empty the file, and get ready to write (change > to >> to append)
foreach my $file (@all_files) {
if($file =~ /\.ics/){ # only process files with the right extension
open FILE, $file or die $!;
my @lines = <FILE>;
close FILE;
my $this_ical_file = join ("",@lines);
$this_ical_file =~ s/\r//g; # icky window line endings removed
$this_ical_file =~ s/\n //g; # a new line starting with a space is actually meant to be wrap indicatio, so unwrap.
my @events = split(/BEGIN:VEVENT/, $this_ical_file); # we make an array, each element being an event
LOOP: # a way to escape the foreach when we don't want to include an appointment
foreach my $event (@events){
for (keys %ITEM){ delete $ITEM{$_};} # is this the best way to empty a hash? I was declaring the hash here everytime, but I think that's wrong.
$event =~ s/[^a-zA-Z0-9: \n-;!\.,=\?\@\#\$\%\^\&\*\(\)]//g; # I only want certain expected characters. Buhbye to everything else.
my @event_details = split(/\n/,$event);
# we'll make a hash here, called %ITEM, which has all the various types of features an iCal file has
foreach my $line(@event_details){
if($line =~ /:/){ # I think they should all have colons, but just in case.
my ($name,$value) = split(/:/,$line,2); # the 2 means split first occurence of delimiter (I think)
if($name =~ /DTSTART/){ # sometimes DTSTART has extensions. But I don't care.
$name = "DTSTART";
if(length($value) < 15){ $value = $value . "T000000";} # make sure we can pass the substr below by adding 0 time to all day events
}
if($name =~ /DTEND/){
$name = "DTEND";
if(length($value) < 15){ $value = $value . "T000000";}
}
$ITEM{$name} = $value; # fill hash with details of this appointment, like start time, recurring rule, location, etc.
}
}
## hash is filled, let's analyze this appointment:
# I want the localtime that corresponds to that gmtime:
if(length($ITEM{'DTSTART'}) >= 15 && length($ITEM{'DTEND'}) >= 15){ # so I don't get any problems with substr. Is this even needed?
my $start_unixtime = (timegm( (substr ($ITEM{'DTSTART'},13,2)),(substr($ITEM{'DTSTART'},11,2)),(substr($ITEM{'DTSTART'},9,2)),(substr($ITEM{'DTSTART'},6,2)),(substr($ITEM{'DTSTART'},4,2)-1),(substr($ITEM{'DTSTART'},0,4)))) - $gmt_offset_in_seconds;
my $end_unixtime = (timegm( (substr($ITEM{'DTEND'},13,2)),(substr($ITEM{'DTEND'},11,2)),(substr($ITEM{'DTEND'},9,2)),(substr($ITEM{'DTEND'},6,2)),(substr($ITEM{'DTEND'},4,2)-1),(substr($ITEM{'DTEND'},0,4)) )) - $gmt_offset_in_seconds;
# now the length of the appointment
my $appointment_length = ($end_unixtime - $start_unixtime) / 60; # subtraction gives us seconds, dividing by 60 gives us minutes, so appointment length is in minutes.
# now checkout the recurring factor: (one off appt is "63", yearly is "62", monthly is "61" Weekly is "60")
# NOTE that this is my best guess of the weird .memo format. Who made this anyway? Couldn't find any docs on it.
my $appointment_type = 0;
if($ITEM{'RRULE'} =~ /WEEKLY/){
$appointment_type = "60";
}
elsif($ITEM{'RRULE'} =~ /MONTHLY/){
$appointment_type = "61";
}
elsif($ITEM{'RRULE'} =~ /YEARLY/){
$appointment_type = "62";
}
else{
$appointment_type = "63";
# if it's in the past, we don't even want it on the export
if ($end_unixtime < time){ # could possibly modify this to get two weeks in the past by substracting additional seconds from time
next LOOP;
}
}
# Recurring Rules might be marked to end at a certain time. If that time is in the past, we don't want it exported
# RRULE:FREQ=WEEKLY;BYDAY=MO;WKST=MO;UNTIL=20090817T223000Z
my @recurring_detail = split(/;/,$ITEM{'RRULE'});
foreach my $breakdown (@recurring_detail){
my ($name,$value) = split(/=/,$breakdown);
if($name eq "UNTIL" && length($value) >= 15){
if (timegm( (substr ($value,13,2)),(substr($value,11,2)),(substr($value,9,2)),(substr($value,6,2)),(substr($value,4,2)-1),(substr($value,0,4))) - $gmt_offset_in_seconds < time){
print "Skipping " . $ITEM{'SUMMARY'} . "\n";
next LOOP;
}
}
}
# Make this time-zone corrected date/time look the way RockBox wants it to:
my ($sec,$min,$hour,$day,$month,$year) = (localtime($start_unixtime))[0,1,2,3,4,5,6];
$year = $year + 1900;
$month++;
# date
print OUTPUT sprintf("%02d", $day) . sprintf("%02d", $month) . $year . $appointment_type;
# time
print OUTPUT "[" . $hour . ":" . sprintf("%02d",$min) . "] ";
# title
print OUTPUT $ITEM{'SUMMARY'};
# location?
if($ITEM{'LOCATION'}) { print OUTPUT " \@ " . $ITEM{'LOCATION'}; }
# length
print OUTPUT " (" . $appointment_length . " min) ";
# description and status
print OUTPUT substr($ITEM{'DESCRIPTION'},0,20) . "(" . $ITEM{'STATUS'} . ")";
print OUTPUT "\n";
}
}
}
}
close (OUTPUT);
# open the file again to sort it (will this help time related appointments sort correctly?)
open FILE, "output_memo.txt";
my @contents = <FILE>;
close FILE;
@contents = sort(@contents);
# and save...
open (SAVE, '>output_memo.txt');
print SAVE @contents;
close SAVE;
print "Complete\n\n";
exit;
--- End code ---
Chronon:
Actually, you would do better to post this to the wiki. An entry in the UsefulTools page might be good, as well as a page describing the proper use of the script.
jasonsilver:
--- Quote from: Chronon on May 24, 2011, 09:17:06 AM ---Actually, you would do better to post this to the wiki.
--- End quote ---
Thanks, I'll see if I can figure out how to do that.
Chronon:
There are instructions on how to register at the main wiki page. You can post back here if you get stuck.
jasonsilver:
I tried to register, but got an error:
--- Code: ---Access Denied
Attention
Access check on Main.JasonSilver failed. Action "CHANGE": access not allowed on web.
If you recently registered here, have you asked to be added to the WikiUsersGroup yet? You cannot edit anything until you are. Ask, in IRC preferrably, an existing user to add you. This is done for spam reasons.
Contact bjorn@haxx.se if you have any questions.
--- End code ---
I am trying to access IRC now.
Jason
Navigation
[0] Message Index
[#] Next page
Go to full version