remove sync_cms
sync from google calendar is not used anymore
This commit is contained in:
@@ -1,18 +0,0 @@
|
|||||||
<config>
|
|
||||||
|
|
||||||
start_dir /home/calcms/agenda/admin/jobs/start/
|
|
||||||
log_dir /home/calcms/agenda/admin/jobs/logs/
|
|
||||||
|
|
||||||
<job>
|
|
||||||
title potsdam : sender berlin -> 88vier.de
|
|
||||||
name potsdam_to_88vier.de
|
|
||||||
command /home/radio/calcms/sync_cms/sync_jobs/calcms_to_google.sh -7 31 potsdam
|
|
||||||
</job>
|
|
||||||
|
|
||||||
<job>
|
|
||||||
title frb : sender berlin -> 88vier.de
|
|
||||||
name frb_to_88vier.de
|
|
||||||
command /home/radio/calcms/sync_cms/sync_jobs/calcms_to_google.sh -7 31 frb
|
|
||||||
</job>
|
|
||||||
|
|
||||||
</config>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<source>
|
|
||||||
<access>
|
|
||||||
hostname localhost
|
|
||||||
port 3306
|
|
||||||
database calcms
|
|
||||||
username calcms_read
|
|
||||||
password password
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
project 88vier
|
|
||||||
<projects>
|
|
||||||
<88vier>
|
|
||||||
name 88vier
|
|
||||||
title 88vier Studio Ansage
|
|
||||||
start_date 2010-05-01
|
|
||||||
end_date 2020-06-01
|
|
||||||
</88vier>
|
|
||||||
</projects>
|
|
||||||
location ansage
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
event_details_url http://senderberlin.org/programm/sendung/<TMPL_VAR event_id>.html
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</source>
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<source>
|
|
||||||
<access>
|
|
||||||
hostname localhost
|
|
||||||
port 3306
|
|
||||||
database calcms
|
|
||||||
username calcms_read
|
|
||||||
password password
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
project 88vier
|
|
||||||
<projects>
|
|
||||||
<88vier>
|
|
||||||
name 88vier
|
|
||||||
title 88vier Colaboradio
|
|
||||||
start_date 2010-05-01
|
|
||||||
end_date 2020-06-01
|
|
||||||
</88vier>
|
|
||||||
</projects>
|
|
||||||
location colabo
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
event_details_url http://senderberlin.org/programm/sendung/<TMPL_VAR event_id>.html
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</source>
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<source>
|
|
||||||
<access>
|
|
||||||
hostname localhost
|
|
||||||
port 3306
|
|
||||||
database calcms
|
|
||||||
username calcms_read
|
|
||||||
password password
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
project 88vier
|
|
||||||
<projects>
|
|
||||||
<88vier>
|
|
||||||
name 88vier
|
|
||||||
title 88vier FRB
|
|
||||||
start_date 2010-05-01
|
|
||||||
end_date 2020-06-01
|
|
||||||
</88vier>
|
|
||||||
</projects>
|
|
||||||
location frb
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
event_details_url http://senderberlin.org/programm/sendung/<TMPL_VAR event_id>.html
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</source>
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<source>
|
|
||||||
<access>
|
|
||||||
hostname localhost
|
|
||||||
port 3306
|
|
||||||
database calcms
|
|
||||||
username calcms_read
|
|
||||||
password password
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
project 88vier
|
|
||||||
<projects>
|
|
||||||
<88vier>
|
|
||||||
name 88vier
|
|
||||||
title 88vier PI-Radio
|
|
||||||
start_date 2010-05-01
|
|
||||||
end_date 2020-06-01
|
|
||||||
</88vier>
|
|
||||||
</projects>
|
|
||||||
location piradio
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
event_details_url http://piradio.de/programm/sendung/<TMPL_VAR event_id>.html
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</source>
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<source>
|
|
||||||
<access>
|
|
||||||
hostname localhost
|
|
||||||
port 3306
|
|
||||||
database calcms
|
|
||||||
username calcms_read
|
|
||||||
password password
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
project 88vier
|
|
||||||
<projects>
|
|
||||||
<88vier>
|
|
||||||
name 88vier
|
|
||||||
title 88vier Frrapo
|
|
||||||
start_date 2010-05-01
|
|
||||||
end_date 2020-06-01
|
|
||||||
</88vier>
|
|
||||||
</projects>
|
|
||||||
location potsdam
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
event_details_url http://senderberlin.org/programm/sendung/<TMPL_VAR event_id>.html
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</source>
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<target>
|
|
||||||
<access>
|
|
||||||
calendarId your-id
|
|
||||||
serviceAccount your-accounr@developer.gserviceaccount.com
|
|
||||||
serviceAccountKeyFile googleApi.key
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
title <TMPL_VAR location> : <TMPL_VAR series_name> - <TMPL_VAR title>
|
|
||||||
content <TMPL_VAR excerpt> <a href="<TMPL_VAR event_details_url>">mehr zur Sendung</a>
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<target>
|
|
||||||
<access>
|
|
||||||
calendarId your-calendar-id
|
|
||||||
serviceAccount your-account@developer.gserviceaccount.com
|
|
||||||
serviceAccountKeyFile googleApi.key
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
title <TMPL_VAR location> : <TMPL_VAR series_name> - <TMPL_VAR title>
|
|
||||||
content <TMPL_VAR excerpt> <a href="<TMPL_VAR event_details_url>">mehr zur Sendung</a>
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<target>
|
|
||||||
<access>
|
|
||||||
calendarId your-id@group.calendar.google.com
|
|
||||||
serviceAccount your-account@developer.gserviceaccount.com
|
|
||||||
serviceAccountKeyFile googleApi.key
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
title <TMPL_VAR location> : <TMPL_VAR series_name> - <TMPL_VAR title>
|
|
||||||
content <TMPL_VAR excerpt> <a href="<TMPL_VAR event_details_url>">mehr zur Sendung</a>
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<target>
|
|
||||||
<access>
|
|
||||||
calendarId your-id@group.calendar.google.com
|
|
||||||
serviceAccount your-account@developer.gserviceaccount.com
|
|
||||||
serviceAccountKeyFile googleApi.key
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
title <TMPL_VAR location> - <TMPL_VAR series_name> - <TMPL_VAR title>
|
|
||||||
content <TMPL_VAR excerpt> <a href="<TMPL_VAR event_details_url>">mehr zur Sendung</a>
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<target>
|
|
||||||
type google_calendar2
|
|
||||||
|
|
||||||
<access>
|
|
||||||
calendarId 8nh18f858098u4ji4qrsmfrcr4@group.calendar.google.com
|
|
||||||
serviceAccount 433089473368-bv26eveq03b7nhb9p62nu3ts7htgb4g3@developer.gserviceaccount.com
|
|
||||||
serviceAccountKeyFile /home/radio/googleApi.key
|
|
||||||
</access>
|
|
||||||
|
|
||||||
<date>
|
|
||||||
time_zone Europe/Berlin
|
|
||||||
</date>
|
|
||||||
|
|
||||||
<mapping>
|
|
||||||
title <TMPL_VAR location> : <TMPL_VAR series_name> - <TMPL_VAR title>
|
|
||||||
content <TMPL_VAR excerpt> <a href="<TMPL_VAR event_details_url>">mehr zur Sendung</a>
|
|
||||||
</mapping>
|
|
||||||
|
|
||||||
<system>
|
|
||||||
debug 1
|
|
||||||
</system>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
package CalcmsEvents;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use Common ('info','error');
|
|
||||||
use DateTime;
|
|
||||||
use Data::Dumper;
|
|
||||||
|
|
||||||
use creole_wiki;
|
|
||||||
use events;
|
|
||||||
use time;
|
|
||||||
#use config;
|
|
||||||
|
|
||||||
my $settings = {};
|
|
||||||
|
|
||||||
sub init($) {
|
|
||||||
$settings = shift || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub set($$) {
|
|
||||||
my $key = shift;
|
|
||||||
my $value = shift;
|
|
||||||
$settings->{$key} = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get($) {
|
|
||||||
my $key = shift;
|
|
||||||
return $settings->{$key};
|
|
||||||
}
|
|
||||||
|
|
||||||
# return a list of start_min, start_max request parameters.
|
|
||||||
sub splitRequest($$$) {
|
|
||||||
my $from = shift;
|
|
||||||
my $till = shift;
|
|
||||||
my $timeZone = shift;
|
|
||||||
|
|
||||||
return undef unless defined $from;
|
|
||||||
return undef unless defined $till;
|
|
||||||
return undef if $from eq '';
|
|
||||||
return undef if $till eq '';
|
|
||||||
|
|
||||||
my $dates = [];
|
|
||||||
|
|
||||||
my $start = time::get_datetime( $from, $timeZone );
|
|
||||||
my $end = time::get_datetime( $till, $timeZone );
|
|
||||||
|
|
||||||
#build a list of dates
|
|
||||||
my $date = $start;
|
|
||||||
my @dates = ();
|
|
||||||
while ( $date < $end ) {
|
|
||||||
push @dates, $date;
|
|
||||||
$date = $date->clone->add( days => 7 );
|
|
||||||
}
|
|
||||||
my $duration = $end - $date;
|
|
||||||
|
|
||||||
push @dates, $end->clone if $duration->delta_seconds <= 0;
|
|
||||||
|
|
||||||
#build a list of parameters from dates
|
|
||||||
$start = shift @dates;
|
|
||||||
for my $end (@dates) {
|
|
||||||
push @$dates,
|
|
||||||
{
|
|
||||||
from => $start,
|
|
||||||
till => $end
|
|
||||||
};
|
|
||||||
$start = $end;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dates;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#get a hash with per-day-lists days of a google calendar, given by its url defined at $calendar_name
|
|
||||||
sub getEvents($$) {
|
|
||||||
my $from = shift;
|
|
||||||
my $till = shift;
|
|
||||||
|
|
||||||
my $last_update = get('last_update');
|
|
||||||
info "getEvents from $from till $till";
|
|
||||||
|
|
||||||
my $request_parameters = {
|
|
||||||
from_date => $from,
|
|
||||||
till_date => $till,
|
|
||||||
project => get('project'),
|
|
||||||
archive => 'all',
|
|
||||||
template => 'no'
|
|
||||||
};
|
|
||||||
my $location = get('location') || '';
|
|
||||||
$request_parameters->{location} = $location if $location ne '';
|
|
||||||
|
|
||||||
my $config = $settings;
|
|
||||||
my %params = ();
|
|
||||||
my $request = {
|
|
||||||
url => $ENV{QUERY_STRING},
|
|
||||||
params => {
|
|
||||||
original => \%params,
|
|
||||||
checked => events::check_params( $config, $request_parameters, $settings ),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
my $sourceEvents = events::get( $config, $request, $settings );
|
|
||||||
|
|
||||||
#return events by date
|
|
||||||
my $eventsByDate = {};
|
|
||||||
for my $source (@$sourceEvents) {
|
|
||||||
$source->{calcms_start} = $source->{start};
|
|
||||||
my $key = substr( $source->{start}, 0, 10 );
|
|
||||||
push @{ $eventsByDate->{$key} }, $source;
|
|
||||||
}
|
|
||||||
return $eventsByDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub mapToSchema {
|
|
||||||
my $event = shift;
|
|
||||||
|
|
||||||
#override settings by source map filter
|
|
||||||
for my $key ( keys %{ get('mapping') } ) {
|
|
||||||
$event->{$key} = get('mapping')->{$key};
|
|
||||||
}
|
|
||||||
|
|
||||||
#resolve variables set in mapped values
|
|
||||||
for my $mkey ( keys %{ get('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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $event;
|
|
||||||
}
|
|
||||||
|
|
||||||
#do not delete last line
|
|
||||||
1;
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package Common;
|
|
||||||
use warnings;
|
|
||||||
use strict;
|
|
||||||
|
|
||||||
use Fcntl ':flock';
|
|
||||||
|
|
||||||
use base 'Exporter';
|
|
||||||
our @EXPORT_OK = ( 'info', 'error' );
|
|
||||||
|
|
||||||
sub checkSingleInstance() {
|
|
||||||
open my $self, '<', $0 or die "Couldn't open self: $!";
|
|
||||||
flock $self, LOCK_EX | LOCK_NB or die "This script $0 is already running";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub loadFile($) {
|
|
||||||
my $filename = shift;
|
|
||||||
|
|
||||||
my $content = '';
|
|
||||||
open my $file, '<', $filename || die("cannot load $filename");
|
|
||||||
while (<$file>) {
|
|
||||||
$content .= $_;
|
|
||||||
}
|
|
||||||
close $file;
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub saveFile($$) {
|
|
||||||
my $filename = shift;
|
|
||||||
my $content = shift;
|
|
||||||
open my $file, ">:utf8", $filename || die("cannot write $filename");
|
|
||||||
print $file $content;
|
|
||||||
close $file;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sub getModifiedAt {
|
|
||||||
my $file = shift;
|
|
||||||
my @stats = stat $file;
|
|
||||||
return 0 if scalar @stats == 0;
|
|
||||||
my $modifiedAt = $stats[9];
|
|
||||||
return $modifiedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub execute($) {
|
|
||||||
my $command = shift;
|
|
||||||
print "EXEC:\t$command\n";
|
|
||||||
my $result = `$command`;
|
|
||||||
my $exitCode = ( $? >> 8 );
|
|
||||||
print "ERROR! exitCode=$?\n" if $exitCode > 0;
|
|
||||||
return ( $exitCode, $result );
|
|
||||||
}
|
|
||||||
|
|
||||||
my $debug = 0;
|
|
||||||
|
|
||||||
sub debug($$) {
|
|
||||||
my $level = shift;
|
|
||||||
my $message = shift;
|
|
||||||
print $message. "\n" if $debug > $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub error ($) {
|
|
||||||
print "\nERROR: $_[0]\nsee $0 --help for help";
|
|
||||||
exit 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub info ($) {
|
|
||||||
my $message = shift;
|
|
||||||
if ( $message =~ /^\n/ ) {
|
|
||||||
$message =~ s/^\n//g;
|
|
||||||
print "\n";
|
|
||||||
}
|
|
||||||
print "INFO:\t$message\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
package GoogleCalendar;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use Data::Dumper;
|
|
||||||
|
|
||||||
use lib '../calcms/';
|
|
||||||
use Common ( 'info', 'error' );
|
|
||||||
use GoogleCalendarApi();
|
|
||||||
use time();
|
|
||||||
|
|
||||||
my $settings = {};
|
|
||||||
my $cal = undef;
|
|
||||||
my $debug = 1;
|
|
||||||
|
|
||||||
sub set($$) {
|
|
||||||
my $key = shift;
|
|
||||||
my $value = shift;
|
|
||||||
$settings->{$key} = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get($) {
|
|
||||||
my $key = shift;
|
|
||||||
return $settings->{$key};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub init($) {
|
|
||||||
$settings = shift || {};
|
|
||||||
|
|
||||||
my $access = get('access');
|
|
||||||
|
|
||||||
# 1. create service account at https://console.developers.google.com/
|
|
||||||
# 2. enable Calendar API
|
|
||||||
# 3. share calendar with service account for update permissions
|
|
||||||
|
|
||||||
# see http://search.cpan.org/~shigeta/Google-API-Client-0.13/lib/Google/API/Client.pm
|
|
||||||
|
|
||||||
my $serviceAccount = $access->{serviceAccount};
|
|
||||||
my $serviceAccountKeyFile = $access->{serviceAccountKeyFile};
|
|
||||||
my $calendarId = $access->{calendarId};
|
|
||||||
|
|
||||||
my $serviceAccountKey = Common::loadFile($serviceAccountKeyFile);
|
|
||||||
my $calendar = GoogleCalendarApi->new(
|
|
||||||
{
|
|
||||||
'serviceAccount' => $serviceAccount,
|
|
||||||
'privateKey' => $serviceAccountKey,
|
|
||||||
'calendarId' => $calendarId,
|
|
||||||
'debug' => 0
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$cal = $calendar;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map event schema to target schema
|
|
||||||
sub mapToSchema {
|
|
||||||
my $event = shift;
|
|
||||||
|
|
||||||
#clone event
|
|
||||||
my $targetEvent = {};
|
|
||||||
for my $key ( keys %{$event} ) {
|
|
||||||
$targetEvent->{$key} = $event->{$key};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( defined $event->{recurrence} && ref( $event->{recurrence} ) eq 'HASH' ) {
|
|
||||||
$targetEvent->{reference} .= '[' . $event->{recurrence}->{number} . ']' if ( $event->{recurrence}->{number} > 0 );
|
|
||||||
$targetEvent->{recurrence} = $event->{recurrence}->{number} + 0;
|
|
||||||
}
|
|
||||||
$targetEvent->{rating} = 0;
|
|
||||||
$targetEvent->{visibility} = 0;
|
|
||||||
|
|
||||||
#set project by project's date range
|
|
||||||
my $projects = get('projects');
|
|
||||||
if ( ref($projects) eq 'HASH' ) {
|
|
||||||
for my $projectName ( keys %$projects ) {
|
|
||||||
my $project = get('projects')->{$projectName};
|
|
||||||
my $start = substr( $event->{start}, 0, 10 );
|
|
||||||
if ( $start ge $project->{start_date} && $start le $project->{end_date} ) {
|
|
||||||
$targetEvent->{project} = $project->{name};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#override settings by target map filter
|
|
||||||
for my $key ( keys %{ get('mapping') } ) {
|
|
||||||
$targetEvent->{$key} = get('mapping')->{$key};
|
|
||||||
}
|
|
||||||
|
|
||||||
#resolve variables set in mapped values
|
|
||||||
for my $mkey ( keys %{ get('mapping') } ) {
|
|
||||||
for my $key ( sort keys %{$targetEvent} ) {
|
|
||||||
my $val = $targetEvent->{$key};
|
|
||||||
$val = $event->{$key} if $mkey eq $key;
|
|
||||||
$targetEvent->{$mkey} =~ s/<TMPL_VAR $key>/$val/g;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$targetEvent->{title} =~ s/\s+$//g;
|
|
||||||
$targetEvent->{title} =~ s/\s*\#$//g;
|
|
||||||
$targetEvent->{title} =~ s/\s*\-\s*$//g;
|
|
||||||
|
|
||||||
my $schema = { event => $targetEvent };
|
|
||||||
|
|
||||||
return $schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
#this is done before sync and allows to delete old events before adding new
|
|
||||||
sub getEvents {
|
|
||||||
my $event = shift;
|
|
||||||
|
|
||||||
return undef if get('date')->{'time_zone'} eq '';
|
|
||||||
return undef if $event->{start} eq '';
|
|
||||||
return undef if $event->{end} eq '';
|
|
||||||
|
|
||||||
#delete a span of dates
|
|
||||||
my $timeZone = get('date')->{'time_zone'};
|
|
||||||
my $start = time::get_datetime( $event->{start}, $timeZone );
|
|
||||||
my $end = time::get_datetime( $event->{end}, $timeZone );
|
|
||||||
|
|
||||||
info( "search target for events from " . $start . " to " . $end );
|
|
||||||
|
|
||||||
#search datetime with same timezone
|
|
||||||
my $events = $cal->getEvents(
|
|
||||||
{
|
|
||||||
timeMin => $cal->getDateTime( $start->datetime, $timeZone ),
|
|
||||||
timeMax => $cal->getDateTime( $end->datetime, $timeZone ),
|
|
||||||
maxResults => 50,
|
|
||||||
singleEvents => 'true',
|
|
||||||
orderBy => 'startTime'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return $events;
|
|
||||||
}
|
|
||||||
|
|
||||||
# insert a new event
|
|
||||||
sub insertEvent {
|
|
||||||
my $event = shift;
|
|
||||||
my $entity = $event->{event};
|
|
||||||
|
|
||||||
$entity->{'html_content'} = markup::creole_to_html( $entity->{'content'} );
|
|
||||||
|
|
||||||
my $timeZone = get('date')->{'time_zone'};
|
|
||||||
|
|
||||||
my $start = $cal->getDateTime( $entity->{start}, $timeZone );
|
|
||||||
my $end = $cal->getDateTime( $entity->{end}, $timeZone );
|
|
||||||
|
|
||||||
#info "insert event\t$start\t$entity->{title}";
|
|
||||||
my $entry = {
|
|
||||||
start => $start,
|
|
||||||
end => $end,
|
|
||||||
summary => $entity->{title},
|
|
||||||
description => $entity->{content},
|
|
||||||
location => $entity->{location},
|
|
||||||
transparency => 'transparent',
|
|
||||||
status => 'confirmed'
|
|
||||||
};
|
|
||||||
|
|
||||||
my $result = $cal->insertEvent($entry);
|
|
||||||
my $id = $result->{id};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub deleteEvent {
|
|
||||||
my $event = shift;
|
|
||||||
|
|
||||||
#info "delete event";
|
|
||||||
$cal->deleteEvent( $event->{id} );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub fixFields {
|
|
||||||
my $event = shift;
|
|
||||||
|
|
||||||
#lower case for upper case titles longer than 4 characters
|
|
||||||
for my $attr ( 'series_name', 'title' ) {
|
|
||||||
my $val = $event->{$attr};
|
|
||||||
my $c = 0;
|
|
||||||
while ( $val =~ /\b([A-Z]{5,99})\b/ && $c < 10 ) {
|
|
||||||
my $word = $1;
|
|
||||||
my $lower = lc $word;
|
|
||||||
$lower =~ s/^([a-z])/\u$1/gi;
|
|
||||||
$val =~ s/$word/$lower/g;
|
|
||||||
$c++;
|
|
||||||
}
|
|
||||||
$event->{$attr} = $val if $event->{$attr} ne $val;
|
|
||||||
}
|
|
||||||
|
|
||||||
for my $attr ( 'series_name', 'title', 'excerpt', 'content' ) {
|
|
||||||
my $val = $event->{$attr};
|
|
||||||
$val =~ s/^\s*(.*?)\s*$/$1/g;
|
|
||||||
$val =~ s/^[ \t]/ /g;
|
|
||||||
$event->{$attr} = $val if $event->{$attr} ne $val;
|
|
||||||
}
|
|
||||||
return $event;
|
|
||||||
}
|
|
||||||
|
|
||||||
#do not delete last line
|
|
||||||
1;
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
package GoogleCalendarApi;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use JSON;
|
|
||||||
use JSON::WebToken;
|
|
||||||
use LWP::UserAgent;
|
|
||||||
use HTML::Entities;
|
|
||||||
use URI::Escape;
|
|
||||||
use Data::Dumper;
|
|
||||||
use DateTime;
|
|
||||||
use Time::HiRes qw(sleep);
|
|
||||||
|
|
||||||
use Common ( 'info', 'error' );
|
|
||||||
|
|
||||||
sub new {
|
|
||||||
my $class = shift;
|
|
||||||
my $params = shift;
|
|
||||||
|
|
||||||
#print Dumper($class);
|
|
||||||
my $self = {};
|
|
||||||
for my $attr ( 'calendarId', 'debug' ) {
|
|
||||||
$self->{$attr} = $params->{$attr} if defined $params->{$attr};
|
|
||||||
}
|
|
||||||
$self->{debug} = 1;
|
|
||||||
|
|
||||||
my $instance = bless $self, $class;
|
|
||||||
if ( ( defined $params->{serviceAccount} ) && ( defined $params->{privateKey} ) ) {
|
|
||||||
$instance->login( $params->{serviceAccount}, $params->{privateKey} );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub setCalendar {
|
|
||||||
my $self = shift;
|
|
||||||
my $calendarId = shift;
|
|
||||||
$self->{calendarId} = $calendarId;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub getBasicUrl {
|
|
||||||
my $self = shift;
|
|
||||||
return 'https://www.googleapis.com/calendar/v3/calendars/' . encode_entities( $self->{calendarId} );
|
|
||||||
}
|
|
||||||
|
|
||||||
#https://developers.google.com/google-apps/calendar/v3/reference/events/list
|
|
||||||
|
|
||||||
#returns {
|
|
||||||
# 'timeZone' => 'Europe/Berlin',
|
|
||||||
# 'description' => "Radioprogramm von Pi Radio f\x{fc}r 88vier.de",
|
|
||||||
# 'defaultReminders' => [],
|
|
||||||
# 'accessRole' => 'owner',
|
|
||||||
# 'etag' => '"1415821582086000"',
|
|
||||||
# 'kind' => 'calendar#events',
|
|
||||||
# 'summary' => '88vier.de Pi Radio (Programm)',
|
|
||||||
# 'updated' => '2014-11-12T19:46:22.086Z',
|
|
||||||
# 'items' => [...]
|
|
||||||
# }
|
|
||||||
sub getEvents {
|
|
||||||
my $self = shift;
|
|
||||||
my $params = shift;
|
|
||||||
|
|
||||||
my $url = '/events?';
|
|
||||||
for my $param (
|
|
||||||
'iCalUID', 'alwaysIncludeEmail', 'maxAttendees', 'maxResults',
|
|
||||||
'orderBy', 'pageToken', 'privateExtendedProperty', 'q',
|
|
||||||
'sharedExtendedProperty', 'showDeleted', 'showHiddenInvitations', 'singleEvents',
|
|
||||||
'syncToken', 'timeZone'
|
|
||||||
)
|
|
||||||
{
|
|
||||||
$url .= '&' . $param . '=' . uri_escape( $params->{$param} ) if defined $params->{$param};
|
|
||||||
}
|
|
||||||
for my $param ( 'timeMin', 'timeMax', 'updatedMin' ) {
|
|
||||||
$url .= '&' . $param . '=' . uri_escape( $self->formatDateTime( $params->{$param} ) ) if defined $params->{$param};
|
|
||||||
}
|
|
||||||
my $result = $self->httpRequest( 'GET', $url );
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
# sleep 0.25 seconds to prevent hitting the 5.0 requests/second/user rate
|
|
||||||
#sub sleep{
|
|
||||||
# my $this=shift;
|
|
||||||
# my $duration=shift;
|
|
||||||
# $duration=1 unless defined $duration;
|
|
||||||
# select(undef, undef, undef, $duration);
|
|
||||||
#}
|
|
||||||
|
|
||||||
#https://developers.google.com/google-apps/calendar/v3/reference/events/delete
|
|
||||||
sub deleteEvent {
|
|
||||||
my $self = shift;
|
|
||||||
my $eventId = shift;
|
|
||||||
my $url = '/events/' . $eventId;
|
|
||||||
|
|
||||||
#DELETE https://www.googleapis.com/calendar/v3/calendars/calendarId/events/eventId
|
|
||||||
my $result = $self->httpRequest( 'DELETE', $url );
|
|
||||||
|
|
||||||
#$self->sleep();
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#https://developers.google.com/google-apps/calendar/v3/reference/events/insert
|
|
||||||
sub insertEvent {
|
|
||||||
my $self = shift;
|
|
||||||
my $params = shift;
|
|
||||||
|
|
||||||
my $event = {
|
|
||||||
start => {
|
|
||||||
dateTime => $self->formatDateTime( $params->{start} )
|
|
||||||
},
|
|
||||||
end => {
|
|
||||||
dateTime => $self->formatDateTime( $params->{end} )
|
|
||||||
},
|
|
||||||
summary => $params->{summary} || '',
|
|
||||||
description => $params->{description} || '',
|
|
||||||
location => $params->{location} || '',
|
|
||||||
status => $params->{confirmed} || 'confirmed'
|
|
||||||
};
|
|
||||||
$event = encode_json $event;
|
|
||||||
|
|
||||||
#POST https://www.googleapis.com/calendar/v3/calendars/calendarId/events
|
|
||||||
my $url = '/events';
|
|
||||||
my $result = $self->httpRequest( 'POST', $url, $event );
|
|
||||||
|
|
||||||
#$self->sleep();
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
# send a HTTP request
|
|
||||||
sub httpRequest {
|
|
||||||
my $self = shift;
|
|
||||||
my $method = shift;
|
|
||||||
my $url = shift;
|
|
||||||
my $content = shift || '';
|
|
||||||
|
|
||||||
sleep 0.3;
|
|
||||||
print STDERR "$method " . $url . "\n" if $self->{debug};
|
|
||||||
|
|
||||||
die("missing url") unless defined $url;
|
|
||||||
die("calendarId not set") unless defined $self->{calendarId};
|
|
||||||
die("not logged in ") unless defined $self->{api};
|
|
||||||
|
|
||||||
#prepend basic url including calendar id
|
|
||||||
$url = $self->getBasicUrl() . $url;
|
|
||||||
print STDERR "$method " . $url . "\n" if $self->{debug};
|
|
||||||
|
|
||||||
my $response = undef;
|
|
||||||
if ( $method eq 'GET' ) {
|
|
||||||
$response = $self->{api}->get($url);
|
|
||||||
} elsif ( ( $method eq 'POST' ) || ( $method eq 'PUT' ) ) {
|
|
||||||
#return;
|
|
||||||
print STDERR $content . "\n" if $self->{debug};
|
|
||||||
my $request = HTTP::Request->new( $method, $url );
|
|
||||||
$request->header( 'Content-Type' => 'application/json' );
|
|
||||||
$request->content($content);
|
|
||||||
$response = $self->{api}->request($request);
|
|
||||||
} elsif ( $method eq 'DELETE' ) {
|
|
||||||
#return;
|
|
||||||
$response = $self->{api}->delete($url);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $response->is_success ) {
|
|
||||||
my $content = $response->content;
|
|
||||||
return {} if $content eq '';
|
|
||||||
return decode_json($content);
|
|
||||||
} else {
|
|
||||||
print "ERROR:\n";
|
|
||||||
print "Code: " . $response->code . "\n";
|
|
||||||
print "Message: " . $response->message . "\n";
|
|
||||||
print $response->content . "\n";
|
|
||||||
die;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# write datetime object to string
|
|
||||||
sub formatDateTime {
|
|
||||||
my $self = shift;
|
|
||||||
my $dt = shift;
|
|
||||||
|
|
||||||
my $datetime = $dt->format_cldr("yyyy-MM-ddTHH:mm:ssZZZZZ");
|
|
||||||
print STDERR "$dt -> $datetime\n" if $self->{debug};
|
|
||||||
return $datetime;
|
|
||||||
}
|
|
||||||
|
|
||||||
# parse datetime from string to object
|
|
||||||
sub getDateTime {
|
|
||||||
my $self = shift;
|
|
||||||
my $datetime = shift;
|
|
||||||
my $timezone = shift;
|
|
||||||
|
|
||||||
return if ( !defined $datetime ) or ( $datetime eq '' );
|
|
||||||
my @l = split /[\-\;T\s\:\+\.]/, $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;
|
|
||||||
}
|
|
||||||
|
|
||||||
# login with serviceAccount and webToken (from privateKey)
|
|
||||||
sub login {
|
|
||||||
my $self = shift;
|
|
||||||
my $serviceAccount = shift;
|
|
||||||
my $privateKey = shift;
|
|
||||||
|
|
||||||
# https://developers.google.com/accounts/docs/OAuth2ServiceAccount
|
|
||||||
my $time = time;
|
|
||||||
|
|
||||||
#create JSON Web Token
|
|
||||||
my $jwt = JSON::WebToken->encode(
|
|
||||||
{
|
|
||||||
iss => $serviceAccount,
|
|
||||||
scope => 'https://www.googleapis.com/auth/calendar',
|
|
||||||
aud => 'https://accounts.google.com/o/oauth2/token',
|
|
||||||
exp => $time + 3600,
|
|
||||||
iat => $time,
|
|
||||||
},
|
|
||||||
$privateKey,
|
|
||||||
'RS256',
|
|
||||||
{ typ => 'JWT' }
|
|
||||||
);
|
|
||||||
|
|
||||||
#send JSON web token to authentication service
|
|
||||||
$self->{auth} = LWP::UserAgent->new();
|
|
||||||
my $response = $self->{auth}->post(
|
|
||||||
'https://accounts.google.com/o/oauth2/token',
|
|
||||||
{
|
|
||||||
grant_type => encode_entities('urn:ietf:params:oauth:grant-type:jwt-bearer'),
|
|
||||||
assertion => $jwt
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
die( $response->code, "\n", $response->content, "\n" ) unless $response->is_success();
|
|
||||||
my $data = decode_json( $response->content );
|
|
||||||
|
|
||||||
#create a new user agent and set token to bearer
|
|
||||||
$self->{api} = LWP::UserAgent->new();
|
|
||||||
$self->{api}->default_header( Authorization => 'Bearer ' . $data->{access_token} );
|
|
||||||
|
|
||||||
print STDERR "login successful\n" if $self->{debug};
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#! /usr/bin/perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use Data::Dumper;
|
|
||||||
use FindBin;
|
|
||||||
use lib "$FindBin::Bin/lib";
|
|
||||||
use lib "$FindBin::Bin/../calcms";
|
|
||||||
|
|
||||||
use Common ( 'info', 'error' );
|
|
||||||
|
|
||||||
use config();
|
|
||||||
use time();
|
|
||||||
use log();
|
|
||||||
|
|
||||||
$| = 1;
|
|
||||||
|
|
||||||
sub runJobs {
|
|
||||||
my $jobs = shift;
|
|
||||||
my $startDir = shift;
|
|
||||||
my $logDir = shift;
|
|
||||||
|
|
||||||
for my $job (@$jobs) {
|
|
||||||
|
|
||||||
my $startFile = $startDir . '/' . $job->{name} . '.start.txt';
|
|
||||||
my $startAge = Common::getModifiedAt($startFile);
|
|
||||||
next if $startAge == 0;
|
|
||||||
|
|
||||||
my $logFile = $logDir . '/' . $job->{name} . '.log';
|
|
||||||
my $logAge = Common::getModifiedAt($logFile);
|
|
||||||
next if $startAge < $logAge;
|
|
||||||
|
|
||||||
# read parameters form start file
|
|
||||||
my $content = log::load_file($startFile);
|
|
||||||
|
|
||||||
#execute command
|
|
||||||
my $command = $job->{command} . ' 2>&1 > ' . $logFile;
|
|
||||||
my ( $exitCode, $result ) = Common::execute($command);
|
|
||||||
error "exitCode=$exitCode on $command" if $exitCode != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub check() {
|
|
||||||
my $configFile = shift @ARGV;
|
|
||||||
error qq{cannot read $configFile "$configFile"} unless -e $configFile;
|
|
||||||
|
|
||||||
my $config = config::get($configFile);
|
|
||||||
|
|
||||||
my $startDir = $config->{start_dir} || '';
|
|
||||||
error 'missing configuration of jobs/start_dir!' if $startDir eq '';
|
|
||||||
error "job dir does not exist '$startDir'" unless -e $startDir;
|
|
||||||
error "cannot read from job dir '$startDir'. Please check permissions!" unless -w $startDir;
|
|
||||||
|
|
||||||
my $logDir = $config->{log_dir} || '';
|
|
||||||
error 'missing configuration of jobs/log_dir' if $logDir eq '';
|
|
||||||
error "job log dir does not exist '$logDir'" unless -e $logDir;
|
|
||||||
error "cannot read from job log dir '$logDir'. Please check permissions!" unless -r $logDir;
|
|
||||||
error "cannot write to job log dir '$logDir'. Please check permissions!" unless -w $logDir;
|
|
||||||
|
|
||||||
my $jobs = $config->{job};
|
|
||||||
error "no jobs defined!" if scalar @$jobs == 0;
|
|
||||||
|
|
||||||
return ( $jobs, $startDir, $logDir );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub main() {
|
|
||||||
|
|
||||||
info "INIT\t" . time::time_to_datetime();
|
|
||||||
Common::checkSingleInstance();
|
|
||||||
my ( $jobs, $startDir, $logDir ) = check();
|
|
||||||
|
|
||||||
#exit after a at most 10 minute timeout in case of hanging process
|
|
||||||
local $SIG{ALRM} = sub { die "ERROR: exit due to synchronization hangs\n" };
|
|
||||||
alarm 10 * 60;
|
|
||||||
|
|
||||||
runJobs( $jobs, $startDir, $logDir );
|
|
||||||
info "DONE\t" . time::time_to_datetime();
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
#!/usr/bin/perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use Data::Dumper;
|
|
||||||
use Getopt::Long;
|
|
||||||
use Config::General;
|
|
||||||
use DateTime;
|
|
||||||
use DateTime::Duration;
|
|
||||||
use IO::Socket::INET;
|
|
||||||
use Fcntl ':flock';
|
|
||||||
|
|
||||||
use FindBin;
|
|
||||||
use lib "$FindBin::Bin/lib";
|
|
||||||
use lib "$FindBin::Bin/../../calcms";
|
|
||||||
|
|
||||||
use Common ( 'info', 'error' );
|
|
||||||
use GoogleCalendar;
|
|
||||||
use CalcmsEvents;
|
|
||||||
|
|
||||||
Common::checkSingleInstance();
|
|
||||||
|
|
||||||
BEGIN {
|
|
||||||
$ENV{LANG} = "en_US.UTF-8";
|
|
||||||
}
|
|
||||||
|
|
||||||
$| = 1;
|
|
||||||
|
|
||||||
my $sourceConfigFile = '';
|
|
||||||
my $targetConfigFile = '';
|
|
||||||
my $debug = 1;
|
|
||||||
|
|
||||||
my $from = undef;
|
|
||||||
my $till = undef;
|
|
||||||
GetOptions(
|
|
||||||
"from=s" => \$from,
|
|
||||||
"till=s" => \$till,
|
|
||||||
"source=s" => \$sourceConfigFile,
|
|
||||||
"target=s" => \$targetConfigFile,
|
|
||||||
);
|
|
||||||
|
|
||||||
#source and taget settings are loaded from config files
|
|
||||||
my $settings = {};
|
|
||||||
|
|
||||||
error "set one of folling parameters: --from, --till" unless $from || $till;
|
|
||||||
|
|
||||||
init();
|
|
||||||
sync();
|
|
||||||
info "$0 done.";
|
|
||||||
exit 0;
|
|
||||||
|
|
||||||
#sync all events, splitting multi-day-requests into multiple 1-day-requests to avoid large result sets
|
|
||||||
sub sync {
|
|
||||||
my $timeZone = CalcmsEvents::get('date')->{time_zone};
|
|
||||||
my $from = CalcmsEvents::get('start_min');
|
|
||||||
my $till = CalcmsEvents::get('start_max');
|
|
||||||
|
|
||||||
info "sync from $from till $till at $timeZone";
|
|
||||||
|
|
||||||
#prepare target
|
|
||||||
info "last update: " . ( CalcmsEvents::get('last_update') || '' );
|
|
||||||
|
|
||||||
if ( my $days = CalcmsEvents::splitRequest( $from, $till, $timeZone ) ) {
|
|
||||||
for my $date (@$days) {
|
|
||||||
syncTimespan( $date->{from}, $date->{till} );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
syncTimespan( $from, $till );
|
|
||||||
}
|
|
||||||
|
|
||||||
info "\nset last-update time: $settings->{event}->{update_start}";
|
|
||||||
setLastUpdateTime( $sourceConfigFile, $targetConfigFile, $settings->{event}->{update_start} );
|
|
||||||
}
|
|
||||||
|
|
||||||
#sync all events of a given source timespan
|
|
||||||
sub syncTimespan {
|
|
||||||
my $from = shift;
|
|
||||||
my $till = shift;
|
|
||||||
|
|
||||||
#get a list of all days and their events
|
|
||||||
my $sourceEvents = CalcmsEvents::getEvents( $from, $till );
|
|
||||||
|
|
||||||
my @dates = keys %$sourceEvents;
|
|
||||||
if ( scalar @dates == 0 ) {
|
|
||||||
info "\nno entries found.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sort lists of date and time (same time events should be preserved)
|
|
||||||
for my $date ( sort { $a cmp $b } @dates ) {
|
|
||||||
syncEvents( $sourceEvents->{$date} );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#syncronize a list of source events to target events
|
|
||||||
sub syncEvents($) {
|
|
||||||
my $sourceEvents = shift;
|
|
||||||
|
|
||||||
my @sourceEvents = sort { $a->{calcms_start} cmp $b->{calcms_start} } @$sourceEvents;
|
|
||||||
$sourceEvents = \@sourceEvents;
|
|
||||||
my $start = $sourceEvents->[0]->{start};
|
|
||||||
my $end = $sourceEvents->[-1]->{end};
|
|
||||||
|
|
||||||
my $targetEvents = GoogleCalendar::getEvents(
|
|
||||||
{
|
|
||||||
start => $start,
|
|
||||||
end => $end
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$targetEvents = $targetEvents->{items};
|
|
||||||
info "google:" . scalar(@$targetEvents) . " vs " . scalar(@$sourceEvents);
|
|
||||||
|
|
||||||
# mark all known target events
|
|
||||||
my $targetEventsByKey = {};
|
|
||||||
for my $event (@$targetEvents) {
|
|
||||||
#print Dumper($event);
|
|
||||||
next if $event->{status} eq 'canceled';
|
|
||||||
my $key = getGoogleEventToString($event);
|
|
||||||
$targetEventsByKey->{$key} = $event;
|
|
||||||
}
|
|
||||||
|
|
||||||
# mark all knwon source events
|
|
||||||
my $sourceEventsByKey = {};
|
|
||||||
for my $event (@$sourceEvents) {
|
|
||||||
$event = CalcmsEvents::mapToSchema($event);
|
|
||||||
$event = GoogleCalendar::mapToSchema($event);
|
|
||||||
my $key = getCalcmsEventToString($event);
|
|
||||||
$sourceEventsByKey->{$key} = $event;
|
|
||||||
}
|
|
||||||
|
|
||||||
# delete target entries without matching source entries
|
|
||||||
for my $key ( keys %$targetEventsByKey ) {
|
|
||||||
next if defined $sourceEventsByKey->{$key};
|
|
||||||
my $event = $targetEventsByKey->{$key};
|
|
||||||
info "delete $key ";
|
|
||||||
print Dumper($event);
|
|
||||||
GoogleCalendar::deleteEvent($event);
|
|
||||||
}
|
|
||||||
|
|
||||||
# insert source entries without matching target entries
|
|
||||||
for my $key ( keys %$sourceEventsByKey ) {
|
|
||||||
if ( defined $targetEventsByKey->{$key} ) {
|
|
||||||
info "$key is up to date";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
my $event = $sourceEventsByKey->{$key};
|
|
||||||
info "insert $key";
|
|
||||||
GoogleCalendar::insertEvent($event);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sub getGoogleEventToString {
|
|
||||||
my $event = shift;
|
|
||||||
my $result = "\n";
|
|
||||||
$result .= "start: " . substr( $event->{start}->{dateTime}, 0, 19 ) . "\n";
|
|
||||||
$result .= "end : " . substr( $event->{end}->{dateTime}, 0, 19 ) . "\n";
|
|
||||||
$result .= "title: $event->{summary}\n";
|
|
||||||
$result .= "desc : $event->{description}\n";
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub getCalcmsEventToString {
|
|
||||||
my $event = shift;
|
|
||||||
my $result = "\n";
|
|
||||||
$result .= "start: " . substr( $event->{event}->{start_datetime}, 0, 19 ) . "\n";
|
|
||||||
$result .= "end : " . substr( $event->{event}->{end_datetime}, 0, 19 ) . "\n";
|
|
||||||
$result .= "title: $event->{event}->{title}\n";
|
|
||||||
$result .= "desc : $event->{event}->{content}\n";
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#import requested source and target libs
|
|
||||||
sub init {
|
|
||||||
binmode STDOUT, ":utf8";
|
|
||||||
|
|
||||||
{
|
|
||||||
#require target config file
|
|
||||||
error "missing target parameter!" unless $targetConfigFile =~ /\S/;
|
|
||||||
error "target file: '$targetConfigFile' does not exist" unless -e $targetConfigFile;
|
|
||||||
error "cannot read target file: '$targetConfigFile'" unless -r $targetConfigFile;
|
|
||||||
my $config = new Config::General($targetConfigFile);
|
|
||||||
$config = $config->{DefaultConfig}->{target};
|
|
||||||
GoogleCalendar::init($config);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
#require source config file
|
|
||||||
error "missing source parameter!" unless $sourceConfigFile =~ /\S/;
|
|
||||||
error "source file: '$sourceConfigFile' does not exist" unless -e $sourceConfigFile;
|
|
||||||
error "cannot read source file: '$sourceConfigFile'" unless -r $sourceConfigFile;
|
|
||||||
my $config = new Config::General($sourceConfigFile);
|
|
||||||
$config = $config->{DefaultConfig}->{source};
|
|
||||||
$config->{last_update} = getLastUpdateTime( $sourceConfigFile, $targetConfigFile );
|
|
||||||
CalcmsEvents::init($config);
|
|
||||||
}
|
|
||||||
|
|
||||||
$from .= 'T00:00' if $from =~ /^\d\d\d\d\-\d\d\-\d\d$/;
|
|
||||||
$till .= 'T23:59' if $till =~ /^\d\d\d\d\-\d\d\-\d\d$/;
|
|
||||||
|
|
||||||
if ( $from =~ /^([-+]?\d+$)/ ) {
|
|
||||||
my $days = $1;
|
|
||||||
my $duration = new DateTime::Duration( days => $days );
|
|
||||||
$from = DateTime->today->add_duration($duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $till =~ /^([-+]?\d+$)/ ) {
|
|
||||||
my $days = $1 + 1;
|
|
||||||
my $duration = new DateTime::Duration( days => $days );
|
|
||||||
$till = DateTime->today->add_duration($duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
CalcmsEvents::set( 'start_min', $from ) if defined $from;
|
|
||||||
CalcmsEvents::set( 'start_max', $till ) if defined $till;
|
|
||||||
|
|
||||||
my $now = time();
|
|
||||||
$now = time::time_to_datetime($now);
|
|
||||||
$settings->{event} = {
|
|
||||||
update_start => time::time_to_datetime( time() ),
|
|
||||||
modified_at => $now,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#output usage on error or --help parameter
|
|
||||||
sub usage {
|
|
||||||
print qq{
|
|
||||||
update all/modified events from source at target.
|
|
||||||
|
|
||||||
USAGE: sync_cms.pl [--read,--update] [--modified,--all] --source s --target t
|
|
||||||
|
|
||||||
on using --from and --till requests will be processed as multiple single-day-requests.
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
--read show all events without updating database
|
|
||||||
--update update target database with source events
|
|
||||||
|
|
||||||
--source source configuration file
|
|
||||||
--target target configuration file
|
|
||||||
|
|
||||||
--from start of date range: datetime (YYYY-MM-DDTHH:MM::SS) or days from today (e.g. -1 for yesterday, +1 for tomorrow)
|
|
||||||
--till end of date range: datetime (YYYY-MM-DDTHH:MM::SS) or days from today (e.g. -1 for yesterday, +1 for tomorrow)
|
|
||||||
|
|
||||||
examples:
|
|
||||||
update modified
|
|
||||||
perl sync_cms.pl --update --source=config/source/program.cfg --target=config/target/calcms.cfg
|
|
||||||
update a given time range
|
|
||||||
perl sync_cms.pl --update --all --from=2009-09-01T00:00:00 --till=2009-11-22T23:59:59 --source=config/source/program.cfg --target=config/target/calcms.cfg
|
|
||||||
update from last 2 days until next 3 days
|
|
||||||
perl sync_cms.pl --update --from=-2 --till=+3 --source=config/source/program.cfg --target=config/target/calcms.cfg
|
|
||||||
};
|
|
||||||
exit 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#load last update time out of sync.data
|
|
||||||
sub getLastUpdateTime {
|
|
||||||
my $source = shift;
|
|
||||||
my $target = shift;
|
|
||||||
|
|
||||||
my $date = undef;
|
|
||||||
return undef unless -r "sync.data";
|
|
||||||
my $content = Common::loadFile("sync.data");
|
|
||||||
if ( $content =~ /$source\s+\->\s+$target\s+:\s+(\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2})/ ) {
|
|
||||||
$date = $1;
|
|
||||||
}
|
|
||||||
return $date;
|
|
||||||
}
|
|
||||||
|
|
||||||
#save last update time to sync.data
|
|
||||||
sub setLastUpdateTime {
|
|
||||||
my $source = shift;
|
|
||||||
my $target = shift;
|
|
||||||
my $date = shift;
|
|
||||||
|
|
||||||
my $data = '';
|
|
||||||
if ( -r "sync.data" ) {
|
|
||||||
$data = Common::loadFile("sync.data");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $data =~ /$source\s+\->\s+$target\s+:\s+(\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2})/ ) {
|
|
||||||
$data =~ s/($source\s+\->\s+$target\s+:)\s+\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}/$1\t$date/gi;
|
|
||||||
} else {
|
|
||||||
$data .= "$source\t\->\t$target\t:\t$date\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$data =~ s/[\r\n]+/\n/g;
|
|
||||||
Common::saveFile( "sync.data", $data );
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
from=$1
|
|
||||||
till=$2
|
|
||||||
project=$3
|
|
||||||
|
|
||||||
export LC_ALL="de_DE.utf8"
|
|
||||||
export LANGUAGE="de_DE.utf8"
|
|
||||||
|
|
||||||
set -x
|
|
||||||
cd /home/radio/calcms/sync_cms
|
|
||||||
nice -n 10 perl sync_cms.pl --from=$from --till=$till --source=config/source/calcms_$project.cfg --target=config/target/88vier_$project.cfg 2>&1
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#/bin/sh
|
|
||||||
|
|
||||||
./sync_project.sh "$1" "$2" piradio
|
|
||||||
./sync_project.sh "$1" "$2" potsdam
|
|
||||||
./sync_project.sh "$1" "$2" ansage
|
|
||||||
./sync_project.sh "$1" "$2" collabo
|
|
||||||
./sync_project.sh "$1" "$2" frb
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
file=/home/radio/piradio.de/agenda/admin/jobs/start/ansage_to_88vier.de.start.txt
|
|
||||||
touch $file
|
|
||||||
chown radio:www-data $file
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
file=/home/radio/piradio.de/agenda/admin/jobs/start/colabo_to_88vier.de.start.txt
|
|
||||||
touch $file
|
|
||||||
chown radio:www-data $file
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
file=/home/radio/piradio.de/agenda/admin/jobs/start/frb_to_88vier.de.start.txt
|
|
||||||
touch $file
|
|
||||||
chown radio:www-data $file
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
file=/home/radio/piradio.de/agenda/admin/jobs/start/piradio_to_88vier.de.start.txt
|
|
||||||
touch $file
|
|
||||||
chown radio:www-data $file
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
file=/home/radio/piradio.de/agenda/admin/jobs/start/potsdam_to_88vier.de.start.txt
|
|
||||||
touch $file
|
|
||||||
chown radio:www-data $file
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user