From 270d40236d161c204646da4f8be53f467e108fc6 Mon Sep 17 00:00:00 2001 From: Jonas Smedegaard Date: Sun, 1 Sep 2024 23:21:10 +0200 Subject: add script events2semesterplan --- bin/events2semesterplan.pl | 202 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100755 bin/events2semesterplan.pl (limited to 'bin') diff --git a/bin/events2semesterplan.pl b/bin/events2semesterplan.pl new file mode 100755 index 0000000..68e695b --- /dev/null +++ b/bin/events2semesterplan.pl @@ -0,0 +1,202 @@ +#!/usr/bin/perl + +use v5.36; +use utf8; +use open qw(:std :encoding(UTF-8)); +use Feature::Compat::Try; + +use FindBin qw($Bin); +use lib "$Bin/../lib"; + +use Getopt::Complete ( + 'quiet!' => undef, + 'verbose!' => undef, + 'debug!' => undef, + 'trace!' => undef, + 'output' => 'files', + 'skeldir' => 'directories', + 'username' => undef, + 'password' => undef, + 'locale' => undef, + 'timezone' => undef, + 'title' => undef, + '<>' => undef, +); +use IO::Interactive::Tiny; +use Log::Any qw($log); +use Log::Any::Adapter; +use URI; +use DateTime; +use Path::Tiny 0.119 qw(path tempdir); +use Text::Xslate; +use POSIX qw(locale_h); # resolve LC_TIME +use locale; +use DateTime::TimeZone; +use LaTeX::Driver; + +use Object::Groupware::DAV; +use Object::Groupware::Calendar; + +# collect settings from command-line options and defaults +my $SKELDIR = $ARGS{skeldir} || $ENV{SKELDIR} || "$Bin/../templates"; +my $BASE_URI = $ARGS{'<>'}[0] || $ENV{CAL_DAV_URL_BASE}; +my $CALENDAR_URI = $ARGS{'<>'}[1] || $ENV{CAL_DAV_URL_CALENDAR}; +my $USERNAME = $ARGS{username} || $ENV{CAL_DAV_USER}; +my $PASSWORD = $ARGS{password} || $ENV{CAL_DAV_PASS}; +my $LOCALE = $ARGS{locale} || $ENV{CAL_LANG}; +my $TIME_ZONE = $ARGS{timezone}; +my $OUTPUT_FILE = $ARGS{output}; + +# init logging +my $LOGLEVEL = 'warning'; +$LOGLEVEL = 'critical' if $ARGS{quiet}; +$LOGLEVEL = 'warning' if defined $ARGS{verbose} and !$ARGS{verbose}; +$LOGLEVEL = 'info' if $ARGS{verbose}; +$LOGLEVEL = 'debug' if $ARGS{debug}; +$LOGLEVEL = 'trace' if $ARGS{trace}; +if ( IO::Interactive::Tiny::is_interactive() ) { + Log::Any::Adapter->set( 'Screen', default_level => $LOGLEVEL ); +} +else { + use Log::Any::Adapter ( 'Stderr', default_level => $LOGLEVEL ); +} + +# init groupware settings +my $dt_locale = DateTime::Locale->load( $LOCALE || setlocale(LC_TIME) ); +my %GROUPWARE_OPTIONS = ( + dt_locale => $dt_locale, + dt_time_zone => DateTime::TimeZone->new( + name => ( $ARGS{timezone} || 'local' ), + ), +); +$log->infof( + 'Will use locale %s and time zone %s', + $GROUPWARE_OPTIONS{dt_locale}->code, + $GROUPWARE_OPTIONS{dt_time_zone}->name, +); + +# init calendar URIs +$BASE_URI = URI->new($BASE_URI) + or $log->fatal('failed to parse required base URI') && exit 2; +$BASE_URI->scheme + or $BASE_URI->scheme('file'); + +# get calendar +my $calendar; +if ( $BASE_URI->scheme eq 'http' or $BASE_URI->scheme eq 'https' ) { + $log->infof( 'will use base URI %s', $BASE_URI ); + $CALENDAR_URI = URI->new( $CALENDAR_URI || $BASE_URI ); + $CALENDAR_URI and $CALENDAR_URI->authority + or $log->fatal('bad calendar URI: must be an internet URI') && exit 2; + $BASE_URI->eq($CALENDAR_URI) and $CALENDAR_URI = undef + or $log->infof( 'will use calendar URI %s', $CALENDAR_URI ); + + my $session = Object::Groupware::DAV->new( + user => $USERNAME, + pass => $PASSWORD, + uri => $BASE_URI, + %GROUPWARE_OPTIONS, + ); + $calendar = $session->get($CALENDAR_URI); +} +elsif ( $BASE_URI->scheme eq 'file' ) { + defined $BASE_URI->file + or $log->fatal('bad base URI: cannot open file') && exit 2; + $log->infof( 'will use base URI %s', $BASE_URI ); + + # parse local calendar data + $log->debug('parse local calendar data...'); + my $path = path( $BASE_URI->file ); + if ( $path->is_file ) { + $calendar = Object::Groupware::Calendar->new( + filename => "$path", + %GROUPWARE_OPTIONS, + ); + } + else { + my $data; + $path->visit( sub { $data .= $_->slurp_raw if $_->is_file } ); + $calendar = Object::Groupware::Calendar->new( + data => $data, + %GROUPWARE_OPTIONS, + ); + } +} + +# select subset of calendar events +$log->debug('serialize calendar events...'); +my $start; +if ( $ENV{CAL_DAV_NOW} ) { + try { require DateTimeX::Easy } + catch ($e) { + $log->fatalf( 'failed parsing CAL_DAV_NOW: %s', $e ) && exit 2 + } + $start = DateTimeX::Easy->new( $ENV{CAL_DAV_NOW} ); + $log->fatalf( + 'failed parsing CAL_DAV_NOW: unknown start time "%s"', + $ENV{CAL_DAV_NOW} + ) + && exit 2 + unless defined $start; +} +$start ||= DateTime->now; +my $end = $start->clone->add( months => 6 ); +my $span = DateTime::Span->from_datetimes( start => $start, end => $end ); +my @events = $calendar->events($span); + +# serialize calendar view +my %vars; + +#$vars{metadata} = $calendar->metadata(); +$vars{name} = $ARGS{title} || ''; +for (@events) { + next unless $_->summary; + push @{ $vars{events} }, $_; +} + +my %tmpl; +$tmpl{plan} = path($SKELDIR)->child('semesterplan.tex')->slurp_utf8; +$tmpl{list} = path($SKELDIR)->child('semesterplan-list.events')->slurp_utf8; + +my $template = Text::Xslate->new( + path => \%tmpl, + syntax => 'TTerse', + type => 'text', +); + +my $content_plan = $template->render( 'plan', \%vars ); +my $content_list = $template->render( 'list', \%vars ); + +my $tempdir = tempdir( CLEANUP => !$log->is_debug ); +my $srcfile = $tempdir->child('plan.tex'); +my $pdffile = $tempdir->child('plan.pdf'); +if ( $log->is_debug ) { + $log->warnf( '[debug] temporary directory %s will not be cleaned', + $tempdir ); +} + +$srcfile->append_utf8($content_plan); +$tempdir->child('list.events')->append_utf8($content_list); + +my $drv = LaTeX::Driver->new( + source => "$srcfile", + output => "$pdffile", + format => 'pdf(lualatex)', + DEBUG => $log->is_debug, + -capture_stderr, +); + +$drv->run + or $log->fatalf( 'failed to generate PDF file: %s', $drv->stderr ) + && exit 2; + +if ($OUTPUT_FILE) { + $OUTPUT_FILE = path($OUTPUT_FILE); + $OUTPUT_FILE->parent->mkpath; + $pdffile->copy($OUTPUT_FILE); +} +else { + print $pdffile->slurp_raw; +} + +1; -- cgit v1.2.3