#!/bin/bash
# -*- coding:utf-8 -*-
# file KLEIDER/web/src/favourites/evallogs
# 2017-06-08 Herbert Schiemann <h.schiemann@herbaer.de>
# 2017-09-14 --titles: Sprachen-Datei bei Bedarf erzeugen
# 2018-01-01 --undo: Letzte Auswertung rückgängig machen
# 2020-04-16 symlinks relativ: ln -s -r, ptniix, pooldir
# 2020-04-17 --upload: quit zum Ende
# 2020-05-11 xhtml_setlang.xslt entfernt

# Zähler, Variable, Aktionen
declare_vars ()
{
   # Zähler
   g_counters="  \
      verbose    \
      overwrite  ";

   # Variable
   g_variables=" \
      datadir    \
      newlogdir  \
      evlogdir   \
      docroot    \
      favdir     \
      srcbase    \
      srcdir     \
      pooldir    \
      trlang     \
      srclang    \
      ptnstory   \
      ptniix     \
      ptnlang    ";

   # Aktionen
   g_actions="   \
      undo       \
      eval       \
      reduce     \
      xslt       \
      xhtml      \
      langs      \
      titles     \
      upload     ";
} # declare_vars

# setzt Vorgabe-Werte
set_defaults ()
{
   local b=$(realpath $0);
   b=${b%/web/src/favourites/evallogs};
   [[ -n "$verbose"   ]] || verbose=1 ;
   [[ -n "$overwrite" ]] || overwrite=1 ;
   [[ -n "$datadir"   ]] || datadir=$b/web/visits ;
   [[ -n "$newlogdir" ]] || newlogdir=$datadir/newlogs ;
   [[ -n "$evlogdir"  ]] || evlogdir=$datadir/evallogs ;
   [[ -n "$docroot"   ]] || docroot=$b/web/docroot ;
   [[ -n "$favdir"    ]] || favdir=$docroot/f ;
   [[ -n "$srcbase"   ]] || srcbase=$b/web/src ;
   [[ -n "$srcdir"    ]] || srcdir=$srcbase/favourites ;
   [[ -n "$pooldir"   ]] || pooldir=$b/pool ;
   [[ -n "$trlang"    ]] || set_default_trlang ;
   [[ -n "$srclang"   ]] || srclang=de ;
   [[ -n "$ptnstory"  ]] || ptnstory="$docroot/s\${storyid}/story.xml.\${lang}."
   [[ -n "$ptnlang"   ]] || ptnlang="$datadir/langs/langs.xhtml.\${lang}"
   [[ -n "$ptniix"    ]] || ptniix="$b/web/imgix/imgix_\${storyid}.xml"
   [[ -n "$no_undo"   ]] || no_undo=1;
   [[ -n "$no_langs"  ]] || no_langs=1;
   [[ -n "$no_xslt"   ]] || no_xslt=1;
   [[ -n "$no_xhtml"  ]] || no_xhtml=1;
} # set_defaults

# setzt die Default-Sprachen
set_default_trlang () {
   trlng="" ;
   local f;
   local l;
   for f in $docroot/local/local.xml.*.; do
      l=${f#$docroot/local/local.xml.};
      l=${l%%.*};
      trlang="$trlang $l";
   done;
} # set_default_trlang

# Zeigt eine kurze Hilfe an
show_help ()
{
   local cmd=${0#*/} ;
   set_defaults ;
   cat << .HELP ;
$cmd --version
$cmd --help
$cmd ([Aktion] | [Option])*

Aktionen
--undo            letzte Auswertung rückgängig machen
--eval            wertet Log-Dateien aus
--reduce          Besuchsdaten für den Webserver bereitstellen
--xslt            XSLT-Dateien für den Webserver
--xhtml           XHTML-Vorlagen für den Webserver
--langs           Sprachbezeichnungen
--titles          Titel-Dateien
--upload          Dateien hochladen

Optionen
--[no_]verbose        Erhöht den Umfang der Ausgabe des Scripts ($verbose)
--[no_]overwrite      Existierende Dateien überschreiben ($overwrite)
--datadir   DATADIR   Datenverzeichnis
                      ($datadir)
--newlogdir NEWLOGDIR Verzeichnis der neuen Log-Dateien
                      ($newlogdir)
--evlogdir  EVLOGDIR  Verzeichnis der ausgewerteten Log-Dateien
--docroot   DOCROOT   Document Root des lokalen HTTP-Servers
                      ($docroot)
--favdir    FAVDIR    Web-Unterverzeichnis für die Auswertung
--srcdir    SRCDIR    Skript-Verzeichnis
                      ($srcdir)
--pooldir   POOLDIR   Verzeichnis gemeinsam genutzter XSLT-Dateien
                      ($pooldir)
--trlang    TRLANG    Komma-getrennte Liste der Sprachen ($trlang)
--srclang   SRCLANG   Quell-Sprache ($srclang)
--ptnstory  PTNSTORY  Dateipfad einer Bildergeschichte mit Platzhaltern
                      ($ptnstory)
--ptniix    PTNIIX    Dateipfad einer Bildbewertungsdatei mit Platzhaltern
                      ($ptniix)
--ptnlang   PTNLANG   Dateipfad einer Sprachendatein mit Platzhaltern
                      ($ptnlang)
.HELP
} # show_help

# Zeigt die Version an
show_version ()
{
   cat << .VERSION ;
KLEIDER/web/src/favourites/evallogs
Logdateien auswerten
2017-06-08 Herbert Schiemann <h.schiemann@herbaer.de>
GPL Version 2 oder neuer
.VERSION
} # show_version

# Variable und Zähler initialisieren
init_vars () {
   local v;
   declare_vars ;
   for v in $g_counters $g_variables $g_actions; do
      eval "$v=" ;
   done;
} # init_vars

# Argumente verarbeiten
read_args ()
{
   local wd ;
   local lastwd ;
   local var ;
   local ok ;

   has_actions=0 ;
   for wd in "$@"; do
      if [[ "$lastwd" = "--" ]]; then
          _argv="$_argv $wd";
      elif [[ -n "$lastwd" ]]; then
         if [[ "$wd" =~ ^[\ a-zA-Z0-9./_#-]+$ ]]; then
            if [[ "$lastwd" == "rc" && -n "" ]]; then
               exit 10 ;
            else
               ok=0 ;
               for var in $g_variables; do
                  if [[ "$var" == "$lastwd" ]]; then
                     (( ++ok )) ;
                     eval "$var=\"$wd\"" ;
                     break ;
                  fi ;
               done ;
               if (( ! ok )); then
                  (( verbose )) && echo "Unbekannte Option --$lastwd $wd" ;
                  exit 11 ;
               fi ;
            fi ;
         else
            (( verbose )) && echo "Ungültiger Optionswert --$lastwd $wd" ;
            exit 12;
         fi;
         lastwd= ;
      else
         case "$wd" in
            --version )
               show_version ;
               exit 0 ;
               ;;
            --help )
               show_version ;
               show_help ;
               exit 0 ;
               ;;
            -- )
               if [[ -n "$_argv" ]]; then
                  lastwd=--;
                  continue;
               else
                  (( verbose )) && echo "Ungültige Option $wd" ;
                  exit 13 ;
               fi ;
               ;;
            --* )
               if [[ "$wd" =~ ^--[a-z][a-z0-9_]*$ ]]; then
                  lastwd=${wd#--} ;
                  ok=0 ;
                  for var in $g_counters ; do
                     if   [[ "$lastwd" == $var    ]]; then
                        eval "(( ++$lastwd ))" ;
                     elif [[ "$lastwd" == "no_$var" ]]; then
                        eval "${lastwd#no_}=0" ;
                     else
                        continue;
                     fi;
                     (( ++ok )) ;
                     break ;
                  done;
                  if (( !ok )); then
                     for var in $g_actions; do
                        if [[ "$lastwd" == "$var" ]]; then
                           eval "(( ++$var ))" ;
                           (( ++ok ));
                           has_actions=1;
                           break;
                        elif [[ "$lastwd" == "no_$var" ]]; then
                           eval "(( ++no_$var ))" ;
                           (( ++ok ));
                           break;
                        fi;
                     done;
                  fi;
                  (( ok )) && lastwd=;
               else
                  (( verbose )) && echo "Ungültige Option $wd" ;
                  exit 14 ;
               fi ;
               ;;
            * )
               if [[ -n $_argv ]]; then
                   _argv="$_argv $wd";
               else
                   (( verbose )) && echo "Ungültige Option $wd" ;
                   exit 15 ;
               fi;
               ;;
         esac ;
      fi ;
   done ;
   if [[ -n $lastwd && "$lastwd" != "--" ]]; then
      (( verbose )) && echo "Unverarbeitete Option --$lastwd";
      exit 16 ;
   fi ;
   [[ "$_argv" =~ ^[[:space:]]+$ ]] && _argv="" ;
} # read_args

# Aktionen ausführen
run_actions ()
{
   local act ;
   for act in $g_actions; do
     eval "(( ! has_actions && ! no_$act || $act )) && process_$act";
   done;
} # run_actions

# show_variables VARNAME1 VARNAME2
# Werte der Variablen anzeigen
show_variables ()
{
   local v ;
   for v in $g_counters $g_variables $g_actions $1; do
      eval "echo \"$v = \$$v\"" ;
   done;
} # show_variables

# Können die Eingabedateien gelesen werden?
# check_infiles first/path/to/file path/to/second_file ;
check_infiles ()
{
   local f ;
   for f in "$@"; do
      if [[ ! -f "$f" ]]; then
         (( verbose )) && echo "\"$f\" ist keine gewöhnliche Datei";
         return 1;
      fi;
      if [[ ! -s "$f" ]]; then
         (( verbose )) && echo "\"$f\" ist leer";
         return 1;
      fi;
      if [[ ! -r "$f" ]]; then
         (( verbose )) && echo "Kann Datei \"$f\" nicht lesen";
         return 1;
      fi;
   done;
   return 0;
} # check_infiles

# Können die Ausgabedateien erstellt werden?
# erstellt fehlende Verzeichnisse und löscht existierende Dateien
# nach Maßgabe der Variablen overwrite
# check_outfiles first/path/to/file path/to/second_file ;
check_outfiles ()
{
   local fp;
   local dir;
   local verb;
   (( verbose )) && verb=--verbose ;
   for fp in "$@"; do
      if [[ ! -e $fp ]]; then
         dir=${fp%/*};
         if [[ -n $dir && ! -e $dir ]]; then
            mkdir -p $verb $dir ;
            if [[ ! -d $dir ]]; then
               (( verbose )) && echo "$dir ist kein Verzeichnis";
               return 1;
            fi;
         fi;
      elif [[ -d $fp ]]; then
         (( verbose )) && echo "$fp ist ein Verzeichnis";
         return 1;
      elif (( overwrite )); then
         (( verbose )) && echo "lösche $fp";
         rm $fp;
      else
         (( verbose )) && echo "$fp existiert";
         return 1;
      fi;
      (( verbose )) && echo "$fp";
   done;
   return 0;
} # check_outfiles

# Sind die Dateien ausführbar?
# check_executeable first/path/to/script path/to/second_srcipt ;
check_executeable ()
{
   local f ;
   for f in "$@"; do
      if [[ ! -f "$f" ]]; then
         (( verbose )) && echo "$f\" ist keine gewöhnliche Datei";
         return 1;
      fi;
      if [[ ! -x "$f" ]]; then
         (( verbose )) && echo "$f\" ist keine ausführbare Datei";
         return 1;
      fi;
   done;
   return 0;
} # check_executeable

# letzte Auswertung rückgängig machen
process_undo ()
{
   (( verbose )) && echo "process_undo" ;
   local verb=;
   (( verbose )) && verb=--verbose;
   check_infiles $srcdir/visits_logs.xslt || return;
   if ! [[ -L $datadir/visits.xml ]]; then
      (( verbose )) && echo "$datadir/visits.xml ist kein Symlink";
      exit 101;
   fi;
   local v=$(realpath $datadir/visits.xml); # letztes visits-File
   local ts=${v#$datadir/visits.xml.};      # letzter Zeitstempel
   (( verbose )) && echo "letzter Zeitstempel $ts";
   local d=$datadir/detailed.xml.$ts        # letztes detailed-File
   local e=$datadir/finished.xml.$ts        # letztes finished-File
   check_infiles $d $e || return;
   local fs=$(xsltproc $srcdir/visits_logs.xslt $d);
   local f;
   for f in $fs; do
      check_infiles $evlogdir/$f || return;
   done;
   local ots;  # alter Zeitstempel
   local ov=;  # vorige visits-Datei
   for f in $datadir/visits.xml.*; do
      [[ "$f" == "$v" ]] && continue;
      [[ $f -nt $v ]] && continue;
      if [[ -z "$ov" ]]; then
         ov=$f;
         continue;
      fi;
      [[ $f -nt $ov ]] && ov=$f;
   done;
   local ots;
   [[ -n "$ov" ]] && ots=${ov#$datadir/visits.xml.};
   echo "früherer Zeitstempel $ots";
   check_infiles                 \
      $datadir/visits.xml.$ots   \
      $datadir/finished.xml.$ots \
      $datadir/detailed.xml.$ots \
      || return;
   for f in $fs; do
      mv $verb $evlogdir/$f $newlogdir/$f;
   done;
   for f in visits open; do
      [[ -L $datadir/$f.xml      ]] && rm $verb $datadir/$f.xml;
      [[ -f $datadir/$f.xml.$ts  ]] && rm $verb $datadir/$f.xml.$ts ;
      [[ -f $datadir/$f.xml.$ots ]] && ln -s -r $datadir/$f.xml.$ots $datadir/$f.xml ;
   done;
   rm $verb $d ;
   rm $verb $e ;
} # process_undo

# Logdateien auswerten
process_eval ()
{
   (( verbose )) && echo "process_eval" ;
   local ts=$(date +%Y%m%d%H%M%S%N) ;
   check_outfiles                \
      $datadir/open.xml.$ts      \
      $datadir/finished.xml.$ts  \
      $datadir/detailed.xml.$ts  \
      $datadir/visits.xml.$ts    \
   || return;
   check_executeable $srcdir/logs.pl $srcdir/sums.pl || return;
   check_infiles $srcdir/visits_logs.xslt $srcdir/visits_num.xslt || return ;
   $srcdir/logs.pl --timestamp $ts --srcdir $srcdir \
      --datadir $datadir --newlogs $newlogdir       ;
   local f;
   if [[ ! -f $datadir/finished.xml.$ts ]]; then
      [[ -f $datadir/detailed.xml.$ts ]] && rm $datadir/detailed.xml.$ts;
      (( verbose )) && echo "Keine neuen Besuche";
      exit 0;
   fi;
   {
      xsltproc $srcdir/visits_num.xslt $datadir/finished.xml.$ts;
      [[ -r $datadir/visits.xml ]] \
      && xsltproc $srcdir/visits_num.xslt $datadir/visits.xml;
   } | $srcdir/sums.pl > $datadir/visits.xml.$ts;
   [[ -f $datadir/visits.xml.$ts ]] || return;
   for f in $(xsltproc $srcdir/visits_logs.xslt $datadir/detailed.xml.$ts); do
      check_outfiles $evlogdir/$f || continue ;
      mv $newlogdir/$f $evlogdir/$f;
   done;
   for f in open visits; do
      [[ -L $datadir/$f.xml ]] && rm $datadir/$f.xml;
      [[ -f $datadir/$f.xml.$ts ]] || continue ;
      ln -s -r $datadir/$f.xml.$ts $datadir/$f.xml ;
   done;
} # process_eval

# gzip-komprimierte Datei(en) hinzufügen
add_gzip ()
{
   local f;
   for f in "$@"; do
      [[ -f $f ]] || continue;
      [[ -f ${f}gz ]] && rm ${f}gz;
      [[ -e ${f}gz ]] && continue;
      (( verbose )) && echo "erstelle ${f}gz";
      gzip --best --stdout $f > ${f}gz ;
   done ;
} # add_gzip

# Daten für die Website reduzieren
process_reduce ()
{
   (( verbose )) && echo "process_reduce" ;
   local s=$datadir/visits.xml ;
   local t1=$srcdir/visits_reduce.xslt ;
   local t2=$srcdir/vis_add_details.xslt ;
   local t3=$pooldir/xml_minimize.xslt ;
   local o=$favdir/f.xml ;
   check_infiles $s $t1 $t2 $t3 || return;
   check_outfiles $o.           || return;
   local p1=${ptnstory%\$\{lang\}*};
   local p2=${ptnstory#*\$\{lang\}};
   local p=${p1}de${p2};
   xsltproc $t1 $s                         \
   | xsltproc                              \
     --stringparam p_ptn_storypath "$p"    \
     --stringparam p_ptn_iixpath "$ptniix" \
      $t2 -                                \
   | xsltproc $t3 - > $o.                  ;
   add_gzip $o. ;
} # process_reduce

# XSLT-Dateien für den Webserver
process_xslt ()
{
   (( verbose )) && echo "process_xslt" ;
   local f;
   local s;
   local d;
   local t=/pool/xslt_minimize.xslt;
   check_infiles $t || continue;
   for f in v; do
      s=$srcdir/$f.xslt;
      d=$favdir/$f.xslt;
      check_infiles  $s  || continue;
      check_outfiles $d. || continue;
      xsltproc $t $s > $d. ;
      add_gzip $d. ;
   done;
} # process_xslt

# XHTML-Vorlagen für den Webserver
process_xhtml ()
{
   (( verbose )) && echo "process_xhtml" ;
   local s;
   local d;
   local t=/pool/xhtml_minimize.xslt ;
   check_infiles $t || return ;
   for s in $srcdir/*.xhtml.de; do
      d=$favdir/${s#$srcdir/}.;
      check_outfiles $d;
      xsltproc $t $s > $d ;
      add_gzip $d;
   done;
} # process_xhtml

# erstellt die Dateien langs.xhml.$lang
process_langs ()
{
   (( verbose )) && echo "process_langs" ;
   local s=$srcbase/kalender/langcodes.dbd ;
   local t=$srcdir/langcodes_xhtml.xslt ;
   check_outfiles                    \
      $datadir/langs/langs.xhtml.de  \
      $docroot/langcodes.xhtml.de    \
   || return ;
   check_infiles $s $t || return ;
   xsltproc $t $s > $docroot/langcodes.xhtml.de. ;
   check_infiles $docroot/langcodes.xhtml.de. || return ;
   $srcbase/localization/localize --no_upload --no_zipfiles langcodes.xhtml ;
   for s in $docroot/langcodes.xhtml.*. ; do
      t=${s#$docroot/langcodes.} ;
      t=$datadir/langs/langs.${t%.} ;
      mv $s $t;
   done;
} # process_langs

# erstellt die Dateien t.xml.$lang
process_titles ()
{
   (( verbose )) && echo "process_titles" ;
   local l ; # Sprache
   local o ; # Ausgabedatei
   local f=$favdir/f.xml. ;
   local t=$srcdir/vis_titles.xslt ;
   local s ; # Sprachen-Datei
   check_infiles $f $t || return ;
   for l in $trlang; do
      o=$favdir/t.xml.$l. ;
      check_outfiles $o || continue ;
      s=$datadir/langs/langs.xhtml.$l ;
      check_infiles $s || process_langs ;
      check_infiles $s || continue ;
      xsltproc                                     \
        --stringparam p_lang          $l           \
        --stringparam p_srclang       $srclang     \
        --stringparam p_ptn_storypath "$ptnstory"  \
        --stringparam p_ptn_langpath  "$ptnlang"   \
        $t $f > $o                                 ;
      add_gzip $o ;
   done;
} # process_titles

# Dateien hochladen
process_upload ()
{
   (( verbose )) && echo "process_upload" ;
   local verb;
   (( verbose )) && verb=--verbose
   local f;  # Kennung einer Bildergeschichte
   {
      for f in $favdir/f.xml.* $favdir/t.xml.*; do
         echo "putnewer ${f#$docroot/}";
      done;
      echo "quit";
   } | $srcbase/localization/ftp.pl $verb;
} # process_upload

# Sicherheit
export PATH=/bin:/usr/bin ;
IFS=$' \t\n' ; 
init_vars ;
set -o noclobber ;   # existierende Dateien werden nicht überschrieben
shopt -s extglob nullglob ;
read_args "$@" ;
set_defaults ;
(( verbose > 1 )) && show_variables ;
run_actions ;
exit 0;
# end of file KLEIDER/web/src/favourites/evallogs
