488 lines
13 KiB
Perl
488 lines
13 KiB
Perl
#!/usr/bin/perl -I ../lib #-w
|
|
|
|
BEGIN{
|
|
my $dir='';
|
|
$ENV{SCRIPT_FILENAME}||'' if ($dir eq'');
|
|
$dir=~s/(.*\/)[^\/]+/$1/ if ($dir ne '');
|
|
$dir=$ENV{PWD} if ($dir eq'');
|
|
$dir=`pwd` if ($dir eq'');
|
|
|
|
#local perl installation libs
|
|
unshift(@INC,$dir.'/../../perl/lib/');
|
|
|
|
#calcms libs + configuration
|
|
unshift(@INC,$dir.'/../calcms/');
|
|
}
|
|
|
|
#use utf8;
|
|
use Data::Dumper;
|
|
#require 'time.pl';
|
|
use Getopt::Long;
|
|
use time;
|
|
use DateTime;
|
|
use DateTime::Duration;
|
|
use strict;
|
|
use warnings;
|
|
|
|
check_running_processes();
|
|
|
|
|
|
my $read_mode='';
|
|
my $update_mode='';
|
|
my $all_events='';
|
|
my $modified_events='';
|
|
my $source_config_file='';
|
|
my $target_config_file='';
|
|
my $block_number=0;
|
|
my $block_size=2000;
|
|
my $from='';
|
|
my $till='';
|
|
my $read_only=0;
|
|
my $project='';
|
|
|
|
GetOptions(
|
|
"read" => \$read_mode,
|
|
"update" => \$update_mode,
|
|
"all" => \$all_events,
|
|
"modified" => \$modified_events,
|
|
"from=s" => \$from,
|
|
"till=s" => \$till,
|
|
"source=s" => \$source_config_file,
|
|
"target=s" => \$target_config_file,
|
|
"project=s" => \$project,
|
|
"block_number:i" => \$block_number,
|
|
"block_size:i" => \$block_size
|
|
);
|
|
|
|
$|=1;
|
|
|
|
BEGIN {
|
|
our $utf8dbi=1;
|
|
$ENV{LANG}="en_US.UTF-8";
|
|
# print Dumper(\%ENV);
|
|
}
|
|
|
|
#source and taget settings are loaded from config files
|
|
our $settings={
|
|
};
|
|
|
|
#user interface
|
|
our $ask_before_insert=0;
|
|
our $ask_before_update=0;
|
|
|
|
# end of configuration
|
|
|
|
if ($update_mode){
|
|
$db::write=1;
|
|
# print "enter update mode\n";
|
|
}elsif($read_mode){
|
|
#default
|
|
$db::write=0;
|
|
# print "enter read-only mode\n";
|
|
}else{
|
|
print_error("set parameter >read< or >update<");
|
|
}
|
|
|
|
unless ($modified_events || $all_events || $from || $till){
|
|
print_error("set one of folling parameters: --modified, --from, --till");
|
|
}
|
|
|
|
init();
|
|
my $project_target=$source::settings->{sources}->{$project};
|
|
unless (defined $project){
|
|
print_error("missing parameter --project") unless(defined $project_target);
|
|
print_error("cant find project configuration '$project_target'") unless (-f $project_target);
|
|
print_error("cant read project configuration '$project_target'") unless (-r $project_target);
|
|
}
|
|
|
|
my $events=[];
|
|
print "TIME_GATE: READ ALL CALENDARS\n";
|
|
sync();
|
|
$events=compress_events($events);
|
|
my $c=0;
|
|
if ($project eq ''){
|
|
for my $event (@$events){
|
|
print_event("[".($c+1)."]",$event);
|
|
print "\n";
|
|
$c++;
|
|
}
|
|
}else{
|
|
my $source=$source::settings->{sources}->{$project};
|
|
my $target='config/target/calcms.cfg';
|
|
|
|
for my $event (@$events){
|
|
my $from=$event->{start};
|
|
#print Dumper($event->{end});
|
|
#remove a second
|
|
my $till=source::get_datetime($event->{end}, $source::settings->{date}->{time_zone})->add(seconds=>-1)->datetime();
|
|
print_event("STATION TIMESLOT [".($c+1)."]\t",$event);
|
|
print "\n";
|
|
$c++;
|
|
my $command="perl sync_cms.pl --update --all --from=$from --till=$till --source $source --target $target ";
|
|
print_info($command);
|
|
print `$command`;
|
|
#exit;
|
|
}
|
|
|
|
}
|
|
print "\ndone.\n";
|
|
exit 0;
|
|
|
|
sub compress_events{
|
|
my $events=shift;
|
|
|
|
my @results=();
|
|
my $old_event={end=>'', start=>'', title=>''};
|
|
for my $event(sort {$a->{start} gt $b->{start}} @$events){
|
|
# print "$event->{start}\t$event->{end}\t$event->{title}\n";
|
|
if (
|
|
# (defined $event) && (defined $event->{start}) && (defined $event->{end}) && (defined $event->{title})
|
|
( #station continues
|
|
($event->{start} eq $old_event->{end})
|
|
|| (#multiple entries for same event
|
|
($event->{start} ge $old_event->{start})
|
|
&& ($event->{end} eq $old_event->{end})
|
|
)
|
|
)
|
|
&& ($event->{title} eq $old_event->{title})
|
|
&& (@results>0)
|
|
){
|
|
$results[-1]->{end}=$event->{end};
|
|
# print @results."\tmerge \n";
|
|
}else{
|
|
push @results,{
|
|
start => $event->{start},
|
|
end => $event->{end},
|
|
title => $event->{title},
|
|
};
|
|
# print @results."\tinsert \n";
|
|
}
|
|
$old_event=$results[-1];
|
|
}
|
|
# print Dumper(\@results);
|
|
|
|
return \@results;
|
|
}
|
|
|
|
#sync all events, splitting multi-day-requests into multiple 1-day-requests to avoid large result sets
|
|
sub sync{
|
|
#prepare target
|
|
target::init($settings->{target});
|
|
print_info("last update: $settings->{source}->{last_update}");
|
|
|
|
if (my $days=source::split_request($settings->{source})){
|
|
#set 1-day start-min and start-max parameters, requires --from and --till values
|
|
for my $date (@$days){
|
|
for my $key(keys %$date){
|
|
$settings->{source}->{$key}=$date->{$key};
|
|
}
|
|
print "\nrequest ".$settings->{source}->{"start_min"}." to ".$settings->{source}->{"start_max"}."\n";
|
|
sync_timespan();
|
|
}
|
|
}else{
|
|
#update without time span (e.g. --modified)
|
|
sync_timespan();
|
|
}
|
|
|
|
print_info("\nset last-update time: $settings->{event}->{update_start}");
|
|
set_last_update_time($source_config_file,$target_config_file,$settings->{event}->{update_start});
|
|
}
|
|
|
|
#sync all events of a given source timespan
|
|
sub sync_timespan{
|
|
#get a list of all days and their events
|
|
#print Dumper($settings->{source});
|
|
my $source_events=source::get_events($settings->{source},$settings->{target});
|
|
#print Dumper($source_events);
|
|
my @dates=(keys %$source_events);
|
|
|
|
if (@dates==0){
|
|
my $more='';
|
|
if ((defined $settings->{source}->{block_number}) && ($settings->{source}->{block_number} ne '0')){
|
|
$more='more ';
|
|
}elsif ($modified_events){
|
|
$more.='modified ';
|
|
}
|
|
print_info("\n".'no '.$more."entries found.");
|
|
}else{
|
|
#sort lists of date and time (same time events should be preserved)
|
|
for my $date(sort {$a cmp $b} @dates){
|
|
# for my $date(@dates){
|
|
# print "\n$date:\n";
|
|
sync_events($source_events->{$date}, $settings);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#syncronize a list of source events to target events
|
|
sub sync_events{
|
|
my $source_events=shift;
|
|
my $settings=shift;
|
|
|
|
my $c=0;
|
|
$c=$source::settings->{start_index}+0 if (defined $source::settings->{start_index});
|
|
|
|
# print "<events>\n";
|
|
|
|
#order processing by start time (TODO: order by last-modified date)
|
|
for my $event (sort{$a->{calcms_start} cmp $b->{calcms_start}} @$source_events){
|
|
#read event attributes
|
|
$event=source::get_event_attributes($event);
|
|
|
|
$event->{title}=~s/\s//g;
|
|
|
|
$event->{event}={
|
|
title => $event->{title},
|
|
start => $event->{start},
|
|
end => $event->{end},
|
|
status => $event->{status},
|
|
};
|
|
|
|
# print "\n";
|
|
#print_event("[".($c+1)."]",$event);
|
|
#print "\n".$event->{event}->{title}." ".$project."\n";
|
|
|
|
if ($event->{event}->{status}eq'canceled'){
|
|
print "canceled event:".qq{$event};
|
|
}elsif ($event->{event}->{start} eq ''){
|
|
print ('WARNING: Cannot read start of event'."\n");
|
|
}elsif ($event->{event}->{end} eq ''){
|
|
print ('WARNING: Cannot read start of end'."\n");
|
|
}elsif ($event->{event}->{title} eq ''){
|
|
print ('WARNING: Cannot read start of title'."\n");
|
|
}elsif ($project ne ''){
|
|
if ($event->{event}->{title} eq $project){
|
|
push @$events, $event->{event};
|
|
}
|
|
}else{
|
|
push @$events, $event->{event};
|
|
}
|
|
$event=undef;
|
|
$c++;
|
|
}
|
|
}
|
|
|
|
|
|
#import requested source and target libs
|
|
sub init{
|
|
binmode STDOUT, ":utf8";
|
|
|
|
#require source config file
|
|
print_error ("missing source parameter!") unless ($source_config_file=~/\S/);
|
|
print_error ("source file: '$source_config_file' does not exist") unless (-e $source_config_file);
|
|
print_error ("cannot read source file: '$source_config_file'") unless (-r $source_config_file);
|
|
#$settings->{source}=require $source_config_file;
|
|
my $configuration = new Config::General($source_config_file);
|
|
$settings->{source}=$configuration->{DefaultConfig}->{source};
|
|
|
|
#require source import lib from config file
|
|
my $source_import_lib='lib/source/'.$settings->{source}->{type}.'.pl';
|
|
print_error ("missing 'type' in 'source' config ") unless ($settings->{source}->{type}=~/\S/);
|
|
print_error ("cannot read source type import lib: '$source_import_lib'")unless (-r $source_import_lib);
|
|
require $source_import_lib;
|
|
|
|
#require target config file
|
|
print_error ("missing target parameter!") unless ($target_config_file=~/\S/);
|
|
print_error ("target file: '$target_config_file' does not exist") unless (-e $target_config_file);
|
|
print_error ("cannot read target file: '$target_config_file'") unless (-r $target_config_file);
|
|
#$settings->{target}=require $target_config_file;
|
|
$configuration = new Config::General($target_config_file);
|
|
$settings->{target}=$configuration->{DefaultConfig}->{target};
|
|
|
|
#require target import lib from config file
|
|
my $target_import_lib='lib/target/'.$settings->{target}->{type}.'.pl';
|
|
print_error ("missing 'type' in 'target' config ") unless ($settings->{target}->{type}=~/\S/);
|
|
print_error ("cannot read target type import lib: '$target_import_lib'")unless (-r $target_import_lib);
|
|
require $target_import_lib;
|
|
|
|
#print Dumper($settings);
|
|
if ((defined $settings->{source}->{read_blocks}) && ($settings->{source}->{read_blocks}==1)){
|
|
$settings->{source}->{block_number} =$block_number;
|
|
$settings->{source}->{block_size} =$block_size;
|
|
}
|
|
$settings->{source}->{last_update} =get_last_update_time($source_config_file,$target_config_file);
|
|
$settings->{source}->{modified_events} =$modified_events;
|
|
|
|
if ($from=~/^\d\d\d\d\-\d\d\-\d\d$/){
|
|
$from.='T00:00';
|
|
}
|
|
|
|
if ($till=~/^\d\d\d\d\-\d\d\-\d\d$/){
|
|
$till.='T23:59';
|
|
}
|
|
|
|
if ($from=~/^([-+]?\d+$)/){
|
|
my $days=$1;
|
|
my $duration=new DateTime::Duration(days=>$days);
|
|
$from=DateTime->today->add_duration($duration);
|
|
# print "from:$from\t";
|
|
}
|
|
if ($till=~/^([-+]?\d+$)/){
|
|
my $days=$1+1;
|
|
my $duration=new DateTime::Duration(days=>$days);
|
|
$till=DateTime->today->add_duration($duration);
|
|
# print "till:$till\t";
|
|
|
|
}
|
|
|
|
|
|
$settings->{source}->{start_min} =$from if defined ($from);
|
|
$settings->{source}->{start_max} =$till if defined ($till);
|
|
|
|
my $gmt_difference =0;#*=3600;
|
|
my $now =time();
|
|
my $now_gmt =$now-$gmt_difference;
|
|
$now =time::time_to_datetime($now);
|
|
$now_gmt =time::time_to_datetime($now_gmt);
|
|
|
|
$settings->{event}={
|
|
update_start => time::time_to_datetime(time()),
|
|
modified_at => $now,
|
|
modified_at_gmt => $now_gmt
|
|
};
|
|
source::init($settings->{source});
|
|
|
|
}
|
|
|
|
# print date/time, title and excerpt of an calendar event
|
|
# TODO: replace by output filter (text, html)
|
|
sub print_event{
|
|
my $header=shift;
|
|
my $event=shift;
|
|
|
|
my $s=$header;
|
|
$s=$s." "x (8-length($s));
|
|
|
|
# print Dumper($event);
|
|
my $start=$event->{start}||'';
|
|
$start=~s/T/ /g;
|
|
$start=~s/\:00$//g;
|
|
|
|
my $end=$event->{end}||'';
|
|
$end=~s/T/ /g;
|
|
$end=~s/\:00$//g;
|
|
|
|
$s.="$start\t$end\t'$event->{title}'";
|
|
|
|
# print Dumper($event->{event});
|
|
print $s;
|
|
#excerpt: >$event->{excerpt}<
|
|
#content: >$event->{content}<
|
|
#content: >$event->{content}<
|
|
|
|
}
|
|
|
|
#output usage on error or --help parameter
|
|
sub print_usage{
|
|
print qq{
|
|
update all/modified events from source at target.
|
|
|
|
USAGE: $0 [--read,--update] [--modified,--all] --source s --target t [--block_number b] [--block_size s]
|
|
|
|
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
|
|
|
|
--modified process only modified events.
|
|
--all' process all 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)
|
|
|
|
--block_number which block is to be syncronized [0..n]. To split up processing into multiple blocks (for machines with small memory resources).
|
|
--block_size size of a block, default=20 events
|
|
|
|
examples:
|
|
perl $0 --update --modified --source=config/source/einheit.cfg --target=config/target/calcms.cfg
|
|
perl $0 --update --all --from=2009-09-01T00:00:00 --till=2009-11-22T23:59:59 --source=config/source/einheit.cfg --target=config/target/calcms.cfg
|
|
};
|
|
exit 1;
|
|
};
|
|
|
|
#load last update time out of sync.data
|
|
sub get_last_update_time{
|
|
my $source=shift;
|
|
my $target=shift;
|
|
|
|
my $date=undef;
|
|
return undef unless(-r "sync.data");
|
|
|
|
open my $DATA, "<:utf8","sync.data" || die ('cannot read update timestamp');
|
|
while (<$DATA>){
|
|
my $line=$_;
|
|
if ($line=~/$source\s+\->\s+$target\s+:\s+(\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2})/){
|
|
$date=$1;
|
|
last;
|
|
}
|
|
}
|
|
close $DATA;
|
|
return $date;
|
|
}
|
|
|
|
#save last update time to sync.data
|
|
sub set_last_update_time{
|
|
my $source =shift;
|
|
my $target =shift;
|
|
my $date =shift;
|
|
|
|
my $data='';
|
|
if (-r "sync.data"){
|
|
open my $DATA, "<:utf8","sync.data";
|
|
$data=join("\n",(<$DATA>));
|
|
close $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;
|
|
|
|
open my $DATA2, ">:utf8","sync.data" || die ('cannot write update timestamp');
|
|
print $DATA2 $data;
|
|
close $DATA2;
|
|
|
|
# print $data;
|
|
}
|
|
|
|
#default error handling
|
|
sub print_error{
|
|
print "\nERROR:\t$_[0]\n" ;
|
|
print_usage();
|
|
}
|
|
|
|
sub print_info{
|
|
my $message=shift;
|
|
if ($message=~/^\n/){
|
|
$message=~s/^\n//g;
|
|
print "\n";
|
|
}
|
|
print "INFO:\t$message\n";
|
|
}
|
|
|
|
#avoid to run more than one sync process simultaniously
|
|
sub check_running_processes{
|
|
my $cmd="ps -afex 2>/dev/null | grep $0.pl | grep -v nice | grep -v grep ";
|
|
my $ps=`$cmd`;
|
|
# print "$ps";
|
|
my @lines=(split(/\n/,$ps));
|
|
if (@lines>1){
|
|
print "ERROR:\tanother ".@lines." synchronization processes '$0.pl' instances are running!".qq{
|
|
|
|
$cmd
|
|
$ps
|
|
-> program will exit
|
|
};
|
|
exit;
|
|
}
|
|
|
|
}
|