aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/events2semesterplan.pl202
-rw-r--r--templates/semesterplan-list.events7
-rw-r--r--templates/semesterplan.tex12
3 files changed, 221 insertions, 0 deletions
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;
diff --git a/templates/semesterplan-list.events b/templates/semesterplan-list.events
new file mode 100644
index 0000000..89313de
--- /dev/null
+++ b/templates/semesterplan-list.events
@@ -0,0 +1,7 @@
+% Annually recurring events should contain the macro \year.
+
+[% FOREACH e IN events -%]
+\event*{[% e.start_date %]}{[% e.summary %]}
+[%- END %]
+
+\endinput
diff --git a/templates/semesterplan.tex b/templates/semesterplan.tex
new file mode 100644
index 0000000..8b873ad
--- /dev/null
+++ b/templates/semesterplan.tex
@@ -0,0 +1,12 @@
+\documentclass{tikz-kalender}
+\setup{%
+,lang=danish%
+,year=2024%
+,title={[% name %]}
+,showweeknumbers%
+,events={list}% events and periods (files with ending ".events")
+}
+
+\begin{document}
+ \makeKalender
+\end{document}