copy current state of medienstaatsvertrag.org, to be verified

This commit is contained in:
Milan
2017-12-18 10:58:50 +01:00
parent 8b35e7c5c2
commit 69e5d0e4c6
401 changed files with 74197 additions and 0 deletions

567
tools/sync_cms/sync_cms.pl Executable file
View File

@@ -0,0 +1,567 @@
#!/usr/bin/perl
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'');
#add calcms libs
unshift(@INC,$dir.'/../calcms/');
}
use Data::Dumper;
use Getopt::Long;
use Config::General;
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;
our $from='';
our $till='';
my $read_only=0;
our $output_type='text';
our $debug=0;
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,
"block_number:i" => \$block_number,
"block_size:i" => \$block_size,
"output_type=s" => \$output_type,
);
$|=1;
BEGIN {
our $utf8dbi=1;
$ENV{LANG}="en_US.UTF-8";
}
#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_info("enter update mode");
}elsif($read_mode){
#default
$db::write=0;
# print_info("enter read-only mode");
}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();
sync();
print_info("$0 done.");
exit 0;
#sync all events, splitting multi-day-requests into multiple 1-day-requests to avoid large result sets
sub sync{
#prepare target
print_info("$0 inited");
print_info("last update: $settings->{source}->{last_update}");
if (my $days=source::split_request()){
#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("\nclean up old database entries...");
target::clean_up();
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});
my @dates=(keys %$source_events);
#print "2\n";
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{
print "<table>" if ($output_type eq 'html');
#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);
}
print "</table>" if ($output_type eq 'html');
}
}
#syncronize a list of source events to target events
sub sync_events{
my $source_events=shift;
my $settings=shift;
# my $source_settings =$settings->{source};
# my $target_settings =$settings->{target};
my $event_settings =$settings->{event};
my $c=0;
$c=$source::settings->{start_index}+0 if (defined $source::settings->{start_index});
# print "<events>\n";
print html_table_header() if ($output_type eq 'html');
#order processing by start time (TODO: order by last-modified date)
for my $event (sort{$a->{calcms_start} cmp $b->{calcms_start}} @$source_events){
target::pre_sync({
start =>$source_events->[0]->{start},
end =>$source_events->[-1]->{end}
});
print "<tr><td>"if ($output_type eq 'html');
#read event
$event=source::get_event_attributes($event);
#convert to calcms schema
$event=source::map_to_schema($event);
#map event to target schema
$event=target::map_to_schema($event);
#deprecated: override defined attributes by configuration
if ((defined $source::settings->{override}) && (ref($source::settings->{override})eq 'HASH')){
for my $key (keys %{$source::settings->{override}}){
my $value=$source::settings->{override}->{$key};
if ($source::settings->{override} ne ''){
print_info("override '$key'='$value'");
$event->{event}->{$key}=$value;
}
}
}
if ($output_type eq'html'){
print_event_html("[".($c+1)."]",$event);
}else{
print_event_text("[".($c+1)."]",$event);
}
if ($event->{event}->{start} eq '' || $event->{event}->{end} eq ''){
print ('WARNING: Cannot read start or end of event');
print "\n";
}else{
# print Dumper($event);
sync_event($event);
}
# last;
$event=undef;
$c++;
print "</td></tr>"if ($output_type eq 'html');
}
# print "\n</events>\n";
}
#syncronize a single source event with target
sub sync_event{
my $event=shift;
#look if target_event exists by reference id incl. recurrence counter
#print Dumper($event);
my $target_event=target::get_event_by_reference_id($event->{event}->{reference});
#if target_event exists
if (defined $target_event){
#delete canceled events
if ($event->{event}->{status}eq'canceled'){
print cell("delete canceled event:".qq{$target_event});
# target::delete($target_event->{id});
return;
}
$event->{event_id}=$target_event->{id};
target::update_event($event,$target_event);
print cell("(ref. update)");
}else{
#find by date, time and title
$target_event=target::find_event($event);
if (defined $target_event){
target::update_event($event,$target_event);
#print Dumper($event);
$event->{event_id}=$target_event->{id};
print cell("(update)");
}else{
target::insert_event($event);
#print Dumper($event);
$target_event=target::get_event_by_reference_id($event->{event}->{reference});
#print Dumper($target_event);
$event->{event_id}=$target_event->{id};
print cell("(new)");
}
}
print "\n";
for my $category (@{$event->{categories}}){
target::assign_category_to_event($category,$event);
}
for my $meta (@{$event->{meta}}){
target::assign_meta_to_event($meta,$event);
}
# print Dumper($event);
}
#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);
$configuration = new Config::General($target_config_file);
$settings->{target}=$configuration->{DefaultConfig}->{target};
#$settings->{target}=require $target_config_file;
#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';
# print "from:$from\t";
}
if ($till=~/^\d\d\d\d\-\d\d\-\d\d$/){
$till.='T23:59';
# print "till:$till\t";
}
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});
target::init($settings->{target});
}
# print date/time, title and excerpt of an calendar event
# TODO: replace by output filter (text, html)
sub print_event_text{
my $header=shift;
my $event=shift;
my $s=$header;
$s=$s." "x (8-length($s));
my $start=$event->{event}->{start}||'';
$start=~s/T/ /g;
$start=~s/\:00$//g;
if (defined $event->{event}->{program}){
$s.="$start $event->{event}->{program}";
$s=$s." "x (45-length($s));
}
if (defined $event->{event}->{series_name}){
$s.=" : $event->{event}->{series_name}";
$s=$s." "x (75-length($s));
}
if (defined $event->{event}->{title}){
$s.=" - $event->{event}->{title}";
$s=$s." "x (110-length($s));
}
if ($event->{categories}){
$s.= "(".join(", ",(@{$event->{categories}})).")";
}
$s=$s." "x (135-length($s));
my $status=$event->{event}->{status};
$s.=$status.' ' if (defined $status);
$s=$s." "x (140-length($s));
my $reference=$event->{event}->{reference};
$s.=substr($reference,length($reference)-25) if (defined $reference);
print $s;
}
sub print_event_html{
my $header=shift;
my $event=shift;
#close error block
my $s='</td>';
my $start=$event->{event}->{start}||'';
$start=~s/T/ /g;
$start=~s/\:00$//g;
$s.=cell($start);
$s.=cell($event->{event}->{program});
$s.=cell($event->{event}->{series_name});
$s.=cell($event->{event}->{title});
if ($event->{categories}){
$s.=cell( join(", " , ( @{$event->{categories}} ) ) );
}
my $status=$event->{event}->{status};
$s.=cell($status) if (defined $status);
my $reference=$event->{event}->{reference};
$reference=substr($reference,length($reference)-25) if (defined $reference);
$s.=cell($reference);
$s.="<td>";
print $s;
}
sub cell{
if ($output_type eq 'html'){
return "<td>$_[0]</td>";
}else{
return "\t".$_[0];
};
}
#output usage on error or --help parameter
sub print_usage{
print qq{
update all/modified events from source at target.
USAGE: sync_cms.pl [--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)
--output_type log output format [text,html]
--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:
update modified
perl sync_cms.pl --update --modified --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 --all --from=-2 --till=+3 --source=config/source/program.cfg --target=config/target/calcms.cfg
};
exit 1;
};
#default error handling
sub print_error{
print "\nERROR: $_[0]\n" ;
print_usage();
}
sub print_info{
my $message=shift;
if ($message=~/^\n/){
$message=~s/^\n//g;
print "\n";
}
if ($output_type eq 'html'){
print "$message<br/>";
}else{
print "INFO:\t$message\n";
}
}
sub html_table_header{
return qq{
<tr>
<th> </th>
<th>start date</th>
<th>project</th>
<th>series</th>
<th>title</th>
<th>category</th>
<th>status</th>
<th>id</th>
<th> </th>
<th>action</th>
</tr>
};
};
#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;
}
#avoid to run more than one sync process in parallel
sub check_running_processes{
my $cmd="ps -afex 2>/dev/null | grep sync_cms.pl | grep -v nice | grep -v grep ";
my $ps=`$cmd`;
# print "$ps";
my @lines=(split(/\n/,$ps));
if (@lines>1){
print "ERROR: another ".@lines." synchronization processes 'sync_cms.pl' instances are running!".qq{
$cmd
$ps
-> program will exit
};
exit;
}
}