Files
racalmas/tools/sync_cms/lib/source/google_calendar.pl

340 lines
10 KiB
Perl
Executable File

#use markup;
use creole_wiki;
use DateTime;
use Net::Google::Calendar;
use DateTime::Format::ICal;
package source;
#do 'time.pl';
use Data::Dumper;
my $settings={};
sub init{
$source::settings=shift;
}
#return a list of start_min, start_max request parameters.
#list is defined as timespan given by start_min and start_max in source::settings
sub split_request{
return undef if (
(!(defined $source::settings->{start_min})) || ($source::settings->{start_min} eq'')
||(!(defined $source::settings->{start_max})) || ($source::settings->{start_max} eq'')
);
my $dates=[];
my $start =get_datetime($source::settings->{start_min},$source::settings->{date}->{time_zone});
my $end =get_datetime($source::settings->{start_max},$source::settings->{date}->{time_zone});
my $date =$start;
#build a list of dates
my @dates=();
while ($date < $end){
push @dates,$date;
$date=$date->clone->add(days=>7);
}
my $duration=$end-$date;
# print "sec:".($duration->delta_seconds/(60*60))."\n";
if ($duration->delta_seconds <= 0){
# pop @dates;
push @dates,$end->clone;
}
#build a list of parameters from dates
my $start=shift @dates;
for my $end (@dates){
push @$dates,{
start_min => $start,
start_max => $end
};
$start=$end;
}
# for $day(@$dates){print "$day->{start_min} - $day->{start_max}\n";}
return $dates;
}
#get a hash with per-day-lists days of a google calendar, given by its url defined at $calendar_name
sub get_events{
# print Dumper($source::settings);
my $url =$source::settings->{access}->{url};
my $email =$source::settings->{access}->{email};
my $password =$source::settings->{access}->{password};
my $block_number =$source::settings->{block_number};
my $block_size =$source::settings->{block_size};
my $last_update =$source::settings->{last_update};
my $parameters={};
my $start_index=undef;
my $stop_index=undef;
if ($source::settings->{read_blocks}==1){
my $start_index=$block_number*$block_size+1 ;
my $stop_index=$start_index+$block_size-1;
$parameters->{"start-index"} = $start_index;
$parameters->{"max-results"} = $block_size;
$source::settings->{start_index}=$start_index;
$source::settings->{stop_index}=$stop_index;
}else{
$parameters->{"max-results"} = 10000;
}
#see http://code.google.com/intl/de/apis/calendar/data/2.0/reference.html
$parameters->{singleevents}='true';
$parameters->{orderby}='lastmodified';
my $more='modified' if (defined $last_update && $source::settings->{modified_events}==1);
main::print_info("read $more events from google calendar: '".substr($url,0,40)."...".substr($url,length($url)-8)."'");
# print "\nblock '$block_number' (events ".$start_index."..".$stop_index.") \n" if (defined $block_number || defined $start_index || defined $stop_index);
# http://search.cpan.org/~simonw/Net-Google-Calendar-0.97/lib/Net/Google/Calendar.pm#get_events_[_%opts_]
my $cal = Net::Google::Calendar->new( url => $url );
#main::print_info("new\n");
if ($email ne'' && $password ne''){
$cal->login($email, $password) ;
# $cal->auth($email, $password) if ($email ne'' && $password ne'');
# main::print_info("login $email $password");
}
#print Dumper($cal);
#set UTF-8
$XML::Atom::ForceUnicode = 1;
$XML::Atom::DefaultVersion = "1.0";
# my $xml=$cal->get_xml();
# $xml=~s/<content/\n<content/gi;
# print $xml."\n";
# exit;
#set updated-min (using UTC)
if ((defined $last_update) && ($source::settings->{modified_events}==1)){
my $datetime=$last_update;
$datetime=source::get_datetime($datetime,$source::settings->{date}->{time_zone}) if (ref($datetime)eq'');
$datetime->set_time_zone('UTC');
$parameters->{"updated-min"} = $datetime->datetime;
#print "last update\n";
}
#set start min (using UTC)
if ((defined $source::settings->{start_min}) && ($source::settings->{start_min}ne'')){
my $datetime=$source::settings->{start_min};
$datetime=source::get_datetime($datetime,$source::settings->{date}->{time_zone}) if (ref($datetime)eq'');
$datetime->set_time_zone('UTC');
$parameters->{"start-min"} = $datetime->datetime;
$parameters->{"recurrence-expansion-start"}= $datetime->datetime;
}
#set start max (using UTC)
if ((defined $source::settings->{start_max})&&($source::settings->{start_max} ne'')){
my $datetime=$source::settings->{start_max};
$datetime=source::get_datetime($datetime,$source::settings->{date}->{time_zone}) if (ref($datetime)eq'');
$datetime->set_time_zone('UTC');
$parameters->{"start-max"} = $datetime->datetime;
$parameters->{"recurrence-expansion-end"}= $datetime->datetime;
}
# print Dumper($parameters);
my @events=();
my @source_events=$cal->get_events(%$parameters);
main::print_info("found ".@source_events." events");
# print Dumper($parameters);
# print Dumper($source::settings);
# exit;
for my $source(@source_events) {
(my $start,my $end)=$source->when;
$start= $start->set_time_zone($source::settings->{date}->{time_zone})->datetime if (defined $start);
$end= $end->set_time_zone ($source::settings->{date}->{time_zone})->datetime if (defined $end);
$source->{calcms_start} = $start;
$source->{calcms_end} = $end;
$source->{status} = $source->status;
}
#return events by date
my $sources_by_date={};
my $old_start='';
# for my $source (sort{$a->{calcms_start} cmp $b->{calcms_start} }@source_events){
for my $source (@source_events){
# if ($source->{status}eq'confirmed'){
my $key=substr($source->{calcms_start},0,10);
# if ($old_start eq $source->{calcms_start}){
# my $source=pop (@{$sources_by_date->{$key}});
# print STDERR "WARNING: ignore canceled entry in google calendar: ".$source->{calcms_start}."\t".$source->{title}."\t".$source->{id}."\n";
# }
#
push @{$sources_by_date->{$key}},$source;
#
# $old_start=$source->{calcms_start};
# }
}
return $sources_by_date;
}
sub map_to_schema{
my $event=shift;
my $params={
title => $event->{title},
content => $event->{content},
local_media_url => '<TMPL_VAR local_media_url>'
};
$params->{content}=~s/\x0A\x20/\n/g;
#print Dumper($params);
#open FILE,">/tmp/test";
#print FILE Dumper($params);
#close FILE;
#decode event
$event=creole_wiki::extractEventFromWikiText($params, $event);
#exit;
#override settings by source map filter
for my $key (keys %{$source::settings->{mapping}}){
$event->{$key}=$source::settings->{mapping}->{$key};
}
#resolve variables set in mapped values
for my $mkey (keys %{$source::settings->{mapping}}){
for my $key (keys %{$event}){
my $val=$event->{$key};
$val=$event->{$key} if($mkey eq $key);
$event->{$mkey}=~s/<TMPL_VAR $key>/$val/g;
}
}
#print Dumper($event);
return $event;
}
sub get_event_attributes{
my $source=shift;
#print @source_events." ".Dumper($source)."\n";
#use Data::Dumper;print Dumper($source->when);
#create an hash with calendar event settings
my $event={
start => $source->{calcms_start},
end => $source->{calcms_end},
status => $source->{status},
# recurrence => $source->{recurrence},
reference => $source->id,
# program => $program,
# series_name => $series_name,
title => $source->title,
content => $source->content->body,
author_name => $source->author->name,
author_uri => $source->author->uri,
author_email => $source->author->email,
transparency => $source->transparency,
visibility => $source->visibility,
location => $source->location,
# podcast_url => $podcast_url,
# media_url => $media_url,
# comments => $source->comments
# who_name => $source->who->name,
# who_email => $source->who->email,
# who_attendee_status => $source->who->attendee_status,
};
#print Dumper($event);
# if ($source->recurrence){
# $event->{recurrence}=get_event_recurrence($source,$event);
# }
return $event;
}
sub get_event_recurrence{
my $source=shift;
my $event=shift;
#print Dumper();
my $event_recurrence=$source->recurrence;
my $properties = $event_recurrence->properties;
# print Dumper($properties);
my $dtstart = $properties->{dtstart}->[0]->{value};
my $timezone = $properties->{dtstart}->[0]->{_parameters}->{TZID};
my $dtend = $properties->{dtend}->[0]->{value};
my $rrule = $properties->{rrule}->[0]->{value};
# print $rrule."\n";
#convert timezone from "until=<datetime>" to same datetime as in dtstart
if ($rrule=~/UNTIL=([\dT]+Z?)/){
my $ical=$1;
#convert timezone at ical format
my $datetime= DateTime::Format::ICal->parse_datetime($ical);
$datetime=$datetime->set_time_zone($timezone);
$ical=DateTime::Format::ICal->format_datetime($datetime);
#remove TZID=... from ical, since not implemented at format_datetime
$ical=~s/[^\:]+\://;
$rrule=~s/(UNTIL\=)([\dT]+Z?)/$1$ical/g;
# print $datetime->datetime." --> $ical --> $rrule\n";
}
$dtstart = DateTime::Format::ICal->parse_datetime($dtstart);
$dtend = DateTime::Format::ICal->parse_datetime($dtend);#->add(seconds=>3600)->set_time_zone('UTC');
my $recurrence={
dtstart => $dtstart,
dtend => $dtend,
rrule => $rrule
};
#calc duration of the event
my $duration=$dtend-$dtstart;
my $duration_min=$duration->delta_minutes;
# print Dumper($duration_min);
#print Dumper($recurrence);
my $recurrence_start = DateTime::Format::ICal->parse_recurrence(
recurrence =>$rrule,
dtstart =>$dtstart
);
#step through recurrent events and mark if event matchs
my $start_iter = $recurrence_start->iterator;
$c=1;
while (my $start = $start_iter->next ){
# print "$start eq $event->{start}, $end\n";
$recurrence->{number}=$c if ($start eq $event->{start});
# push @dates,{
# start => $start->set_time_zone($source::settings->{time_zone})->datetime,
# end => $start->set_time_zone($source::settings->{time_zone})->add(minutes=>$duration_min)->datetime
# };
$c++;
}
$event->{recurrence}=$recurrence;
#print Dumper($event->{recurrence});
}
sub get_datetime{
my $datetime=shift;
my $timezone=shift;
return if((!defined $datetime) or ($datetime eq ''));
my @l=@{time::datetime_to_array($datetime)};
$datetime=DateTime->new(
year =>$l[0],
month =>$l[1],
day =>$l[2],
hour =>$l[3],
minute =>$l[4],
second =>$l[5],
time_zone=> $timezone
);
return $datetime;
}
eof;