aboutsummaryrefslogtreecommitdiff
path: root/bin/events2semesterplan.pl
blob: 9d20957d3d987952755b9dd7774d66d2f44766c6 (plain)
  1. #!/usr/bin/perl
  2. use v5.36;
  3. use utf8;
  4. use open qw(:std :encoding(UTF-8));
  5. use Feature::Compat::Try;
  6. use FindBin qw($Bin);
  7. use lib "$Bin/../lib";
  8. use Getopt::Complete (
  9. 'quiet!' => undef,
  10. 'verbose!' => undef,
  11. 'debug!' => undef,
  12. 'trace!' => undef,
  13. 'output' => 'files',
  14. 'skeldir' => 'directories',
  15. 'username' => undef,
  16. 'password' => undef,
  17. 'locale' => undef,
  18. 'timezone' => undef,
  19. 'title' => undef,
  20. '<>' => undef,
  21. );
  22. use IO::Interactive::Tiny;
  23. use Log::Any qw($log);
  24. use Log::Any::Adapter;
  25. use URI;
  26. use DateTime;
  27. use Path::Tiny 0.119 qw(path tempdir);
  28. use Text::Xslate;
  29. use POSIX qw(locale_h); # resolve LC_TIME
  30. use locale;
  31. use DateTime::TimeZone;
  32. use LaTeX::Driver;
  33. use Object::Groupware::DAV;
  34. use Object::Groupware::Calendar;
  35. # collect settings from command-line options and defaults
  36. my $SKELDIR = $ARGS{skeldir} || $ENV{SKELDIR} || "$Bin/../templates";
  37. my $BASE_URI = $ARGS{'<>'}[0] || $ENV{CAL_DAV_URL_BASE};
  38. my $CALENDAR_URI = $ARGS{'<>'}[1] || $ENV{CAL_DAV_URL_CALENDAR};
  39. my $USERNAME = $ARGS{username} || $ENV{CAL_DAV_USER};
  40. my $PASSWORD = $ARGS{password} || $ENV{CAL_DAV_PASS};
  41. my $LOCALE = $ARGS{locale} || $ENV{CAL_LANG};
  42. my $TIME_ZONE = $ARGS{timezone};
  43. my $OUTPUT_FILE = $ARGS{output};
  44. # init logging
  45. my $LOGLEVEL = 'warning';
  46. $LOGLEVEL = 'critical' if $ARGS{quiet};
  47. $LOGLEVEL = 'warning' if defined $ARGS{verbose} and !$ARGS{verbose};
  48. $LOGLEVEL = 'info' if $ARGS{verbose};
  49. $LOGLEVEL = 'debug' if $ARGS{debug};
  50. $LOGLEVEL = 'trace' if $ARGS{trace};
  51. if ( IO::Interactive::Tiny::is_interactive() ) {
  52. Log::Any::Adapter->set( 'Screen', default_level => $LOGLEVEL );
  53. }
  54. else {
  55. use Log::Any::Adapter ( 'Stderr', default_level => $LOGLEVEL );
  56. }
  57. # init groupware settings
  58. my $dt_locale = DateTime::Locale->load( $LOCALE || setlocale(LC_TIME) );
  59. my %GROUPWARE_OPTIONS = (
  60. dt_locale => $dt_locale,
  61. dt_time_zone => DateTime::TimeZone->new(
  62. name => ( $ARGS{timezone} || 'local' ),
  63. ),
  64. );
  65. $log->infof(
  66. 'Will use locale %s and time zone %s',
  67. $GROUPWARE_OPTIONS{dt_locale}->code,
  68. $GROUPWARE_OPTIONS{dt_time_zone}->name,
  69. );
  70. # init calendar URIs
  71. $BASE_URI = URI->new($BASE_URI)
  72. or $log->fatal('failed to parse required base URI') && exit 2;
  73. $BASE_URI->scheme
  74. or $BASE_URI->scheme('file');
  75. # get calendar
  76. my $calendar;
  77. if ( $BASE_URI->scheme eq 'http' or $BASE_URI->scheme eq 'https' ) {
  78. $log->infof( 'will use base URI %s', $BASE_URI );
  79. $CALENDAR_URI = URI->new( $CALENDAR_URI || $BASE_URI );
  80. $CALENDAR_URI and $CALENDAR_URI->authority
  81. or $log->fatal('bad calendar URI: must be an internet URI') && exit 2;
  82. $BASE_URI->eq($CALENDAR_URI) and $CALENDAR_URI = undef
  83. or $log->infof( 'will use calendar URI %s', $CALENDAR_URI );
  84. my $session = Object::Groupware::DAV->new(
  85. user => $USERNAME,
  86. pass => $PASSWORD,
  87. uri => $BASE_URI,
  88. %GROUPWARE_OPTIONS,
  89. );
  90. $calendar = $session->get($CALENDAR_URI);
  91. }
  92. elsif ( $BASE_URI->scheme eq 'file' ) {
  93. defined $BASE_URI->file
  94. or $log->fatal('bad base URI: cannot open file') && exit 2;
  95. $log->infof( 'will use base URI %s', $BASE_URI );
  96. # parse local calendar data
  97. $log->debug('parse local calendar data...');
  98. my $path = path( $BASE_URI->file );
  99. if ( $path->is_file ) {
  100. $calendar = Object::Groupware::Calendar->new(
  101. filename => "$path",
  102. %GROUPWARE_OPTIONS,
  103. );
  104. }
  105. else {
  106. my $data;
  107. $path->visit( sub { $data .= $_->slurp_raw if $_->is_file } );
  108. $calendar = Object::Groupware::Calendar->new(
  109. data => $data,
  110. %GROUPWARE_OPTIONS,
  111. );
  112. }
  113. }
  114. # select subset of calendar events
  115. $log->debug('serialize calendar events...');
  116. my $start;
  117. if ( $ENV{CAL_DAV_NOW} ) {
  118. try { require DateTimeX::Easy }
  119. catch ($e) {
  120. $log->fatalf( 'failed parsing CAL_DAV_NOW: %s', $e ) && exit 2
  121. }
  122. $start = DateTimeX::Easy->new( $ENV{CAL_DAV_NOW} );
  123. $log->fatalf(
  124. 'failed parsing CAL_DAV_NOW: unknown start time "%s"',
  125. $ENV{CAL_DAV_NOW}
  126. )
  127. && exit 2
  128. unless defined $start;
  129. }
  130. $start ||= DateTime->now;
  131. my $end = $start->clone->add( months => 6 );
  132. my $span = DateTime::Span->from_datetimes( start => $start, end => $end );
  133. my @events = $calendar->events($span);
  134. # serialize calendar view
  135. my %vars;
  136. #$vars{metadata} = $calendar->metadata();
  137. $vars{name} = $ARGS{title} || '';
  138. for (@events) {
  139. next unless $_->summary;
  140. push @{ $vars{events} }, $_;
  141. }
  142. my %tmpl;
  143. $tmpl{plan} = path($SKELDIR)->child('semesterplan.tex')->slurp_utf8;
  144. $tmpl{list} = path($SKELDIR)->child('semesterplan-list.events')->slurp_utf8;
  145. my $template = Text::Xslate->new(
  146. path => \%tmpl,
  147. syntax => 'TTerse',
  148. type => 'text',
  149. );
  150. my $content_plan = $template->render( 'plan', \%vars );
  151. my $content_list = $template->render( 'list', \%vars );
  152. my $tempdir = tempdir( CLEANUP => !$log->is_debug );
  153. my $srcfile = $tempdir->child('plan.tex');
  154. my $pdffile = $tempdir->child('plan.pdf');
  155. if ( $log->is_debug ) {
  156. $log->warnf(
  157. '[debug] temporary directory %s will not be cleaned',
  158. $tempdir
  159. );
  160. }
  161. $srcfile->append_utf8($content_plan);
  162. $tempdir->child('list.events')->append_utf8($content_list);
  163. my $drv = LaTeX::Driver->new(
  164. source => "$srcfile",
  165. output => "$pdffile",
  166. format => 'pdf(lualatex)',
  167. DEBUG => $log->is_debug,
  168. -capture_stderr,
  169. );
  170. $drv->run
  171. or $log->fatalf( 'failed to generate PDF file: %s', $drv->stderr )
  172. && exit 2;
  173. if ($OUTPUT_FILE) {
  174. $OUTPUT_FILE = path($OUTPUT_FILE);
  175. $OUTPUT_FILE->parent->mkpath;
  176. $pdffile->copy($OUTPUT_FILE);
  177. }
  178. else {
  179. print $pdffile->slurp_raw;
  180. }
  181. 1;