Kapitel 5 - Anwendung: »Die Rote Liste im WWW«

Die vorgefertigten Anbindungen aus dem Oracle-WWW-Interface-Kit waren teilweise auf der am Lehrstuhl vorhandenen Hardware nicht zum Laufen zu bekommen oder sie waren zu allgemein gehalten, so daß das Wissen, über die Zusammenhänge der Daten nicht eingebrac ht werden konnte. So wurden zur Bereitstellung der »Roten Liste« über das WWW drei perl-Skripte geschrieben, die nach erfolgter DB-Anfrage die HTML-Seiten so aufbereiten, daß dadurch weitere Informationen bekommen werden konnten.

5.1 Was ist perl ?

perl (practical extraction and report language) ist eine interpretierte Kommandosprache, die seit 1986 von Larry Wall entwickelt und immer weiter verbessert wird [PRL01], [PRL02]. Der Quellcode von Perl ist frei verfügbar und so wurde Perl auf viele Hardwa replattformen portiert (Diverse UNIX, MS-DOS, Windows-NT, OS/2, Atari usw.).

Die Sprache hat (neben vielen anderen!) vor allem zwei Vorteile, die es für automatisierte Textbearbeitung zum hervorragenden Werkzeug machen:

  1. Programme müssen keine Variablen deklarieren oder für dynamische Strukturen Speicherplatz anfordern. Perl kennt String-Arrays, in denen sowohl die Länge der Strings, als auch die Anzahl der Array-Felder vollkommen dynamisch von Perl selber verwaltet wird.
  2. Ein Mechanismus zur Verwendung von komplexen regulären Ausdrücken ist bereits enthalten. So kann man Zeilen mit bestimmten Mustern erkennen, aussortieren oder (wie bei der vorliegenden Aufgabe) um diese Muster andere Texte (z.B. HTML-Tags) gruppieren und d en Text dann wieder ausgeben.

5.2 Worauf man achten sollte...

Bei der WWW-Anbindung der »Roten Liste« sind vor allem vier Punkte aufgefallen, die generell bei jeder Datenbank/WWW Aufgabe beachtet werden sollten:

  1. Der http-Dämon (also der Web-Server) läuft unter dem Account und damit auch mit den Rechten eines bestimmten Unix-Benutzers (meist: »http«). Dieser User muß sowohl das Recht haben, das CGI-Programm im cgi-bin Verzeichnis zu starten, als auch (über das CGI- Programm) Zugriff auf die Oracle-Tools haben (im vorliegenden Fall das Programm SQL*DBA).
  2. Kompilierte CGI-Gateways sollten nicht direkt, sondern über ein Shell-Skript gestartet werden, damit hier entsprechende Umgebungsvariablen gesetzt werden können, ohne die ein Datenbankbetrieb in der Regel nicht möglich ist:
    set path=~oracle /bin:$path
    setenv ORACLE_HOME /usr/users/oracle
    setenv ORACLE_SID adria
    setenv ORACLE_LPPROG /usr/ucb/lpr
    setenv ORACLE_PAGER /usr/ucb/more
    setenv ORACLE_LPSTAT /usr/ucb/lpq
    I n perl kann jedoch auch innerhalb des perl-Skriptes direkt in des Umgebungs-Variablen-Array (%ENV) geschrieben werden.
  3. Das CGI-Programm sollte - auch im Fehlerfall - einen korrekten CGI-Header zurückliefern (Also zum Beispiel »Content-type: text/html« gefolgt von einer Leerzeile), sonst überträgt der Web-Server keine sonst vom CGI-Programm gelieferten Daten sondern nur ein e Fehlermeldung.
  4. Der Web-Server schreibt in seinem Verzeichnis in das Unterverzeichnis »logs« eine Datei »error_log«, in der Zugriffsfehler protokolliert werden. Bei Problemen läßt sich hier oft die Beschreibung der Ursache finden.

5.3 Die Implementierung

Für den Benutzer wird über die cgi-perl-Skripte folgende Funktionalität bereitgestellt:

Zunächst wurde ein HTML-Form entworfen, mit dem der User seine Anfrage formulieren kann:

<H3>3c.)Oracle-Anfrage in der Roten Liste (in PERL)</H3>
<IMG SRC="rotlistk.gif">
<FORM METHOD="GET" ACTION=
  "http://galway.informatik.uni-wuerzburg.de/cgi-bin/rote_liste_suchen.pl">
<INPUT TYPE=checkbox VALUE="1"   NAME="startpercent">
  Beliebige Zeichen <U>vor</U> Suchbegriff erlaubt (%Suchbegriff)<BR> 
Suchbegriff f&uuml;r Medikamentnamen:  
<INPUT TYPE=text     VALUE="Asp" NAME="medikament"><BR>
<INPUT TYPE=checkbox VALUE="1"   NAME="endpercent" checked>
  Beliebige Zeichen <U>nach</U> Suchbegriff erlaubt (Suchbegriff%)<BR> 
<INPUT TYPE=submit VALUE="Anfrage !"> </FORM>

Nach Drücken des Anfrage-Knopfes wird rote_liste_suchen.pl ausgeführt. Hier wird nach dem Setzen der Umgebungsvariablen der QUERY_STRING zerlegt und ein entsprechender SQL-String generiert. Dieser SQL-String wird dann an SQL*DBA übergeben und dessen Ausgaben in eine temporäre Datei gelenkt. Die temporäre Datei wird anschließend eingelesen und daraus eine HTML-Seite mit Verweisen generiert. Das Dokument wird dann auf STDOUT ausgegeben:


#!/usr/local/bin/perl

srand(time|$$);
$tempfile = "/usr/tmp/rl_" . int(rand(100000)) . ".tmp";

print "Content-type: text/html\n\n";

$ENV{"PATH"}="/usr/users/oracle/bin:".$ENV{"PATH"};
$ENV{"ORACLE_HOME"}="/usr/users/oracle";
$ENV{"ORACLE_SID"}="adria";
$ENV{"ORACLE_LPPROG"}="/usr/ucb/lpr";
$ENV{"ORACLE_PAGER"}="/usr/ucb/more";
$ENV{"ORACLE_LPSTAT"}="/usr/ucb/lpq";
$ENV{"TERM"}="vt100";
$ENV{"APIPATH"}="/usr/users/oracle/rdbms/admin/terminal";

###################### QUERY_STRING var=wert Paare erzeugen
@query_string = split (/&/, $ENV{"QUERY_STRING"} );
for ($i=0 ;  $i <= $#query_string; $i++)
{
   ($variable, $wert) = split (/=/, $query_string[$i]);
   eval "\$$variable=\"$wert\"";
}

##################### medikament-Variable leer ?
if (length($medikament)==0) 
{
  print "FEHLER: Kein Medikament uebergeben !\n";
  exit;
}


##################### Prozentzeichen anfuegen...
if ($startpercent) {$medikament = "%".$medikament;}
if ($endpercent) {$medikament = $medikament."%";}

##################### Rote-Liste nach Medikamenten Namen durchsuchen
open (WOK, "|sqldba > $tempfile");
print WOK "connect www/w3oracle\n";
$sqlstring  = "select num || '\@' || nam";
$sqlstring .= " from infox.rlpridnumnam";
$sqlstring .= " where nam like '$medikament'";
$sqlstring .= " order by nam;";
# print $sqlstring;
print WOK $sqlstring;

#################### Oracle-Output lesen und formatieren
open (WOK, "$tempfile");

$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;
$muell = <WOK>;

@temp = <WOK>;
close WOK;
$#temp = $#temp - 3;

for ($i=0; $i<=$#temp; $i++)
{
   chop $temp[$i];
   while (ord(substr ($temp[$i], -1)) == 32)  { chop ($temp[$i]); }
   while (ord(substr ($temp[$i], 0, 1)) == 32)  
   { $temp[$i] = substr ($temp[$i], 1); }

   ($med_infofile[$i], $med_name[$i]) = split (/@/, $temp[$i], 2); 
}

# ################################## Ergebnis als HTML formatieren
print "<HTML><HEAD><TITLE>Suchergebnis der Roten Liste</TITLE></HEAD>\n";
print "<BODY>\n";
print "<H1>Die Rote Liste antwortet:</H1>\n";
# print "<IMG SRC=\"/var/adm/httpd/htdocs/rotlistk.gif\">\n";
if ($#med_name == 1) 
   {print "<H4>$#med_name Eintrag gefunden.</H4>\n";}
elsif ($#med_name == -1) 
   {print "<H4>Keine Einträge gefunden.</H4>\n";}
else
   {print "<H4>", $#med_name+1, " Einträge gefunden.</H4>\n";}
print "<HR><UL>\n";

for ($i=0; $i<=$#med_name; $i++)
    { print "<LI><A HREF=\"http://galway.informatik.uni-wuerzburg.de".
            "/cgi-bin/rote_liste_anzeigen.pl?$med_infofile[$i]\">".
            "$med_name[$i]</A><BR>\n";
    }

print "</UL><HR>\n";
print "</BODY></HTML>\n";

################################## Temp-Datei loeschen;
open (WOK, "rm $tempfile |"); while (<WOK>) {}


Die HTML-Seite, die der User nach der Anfrage »Asp%« durch das obige perl-Skript bekommt sieht dann so aus:

Wird hier nun auf »Aspirin Plus C« geklickt, dann wird das Skript »rote_liste_anzeigen.pl« ausgeführt und bekommt in QUERY_STRING die Medikamentennummer aus der obigen SQL-Anfrage. Diese Nummer zeigt auf eine außerhalb der Datenbank liegenden Text-Datei, die dann als formatiertes HTML-Dokument auf STDOUT ausgegeben wird. Dabei werden mit regulären Ausdrücken im Text die Verweise auf Gegenanzeigen u.s.w. erkannt und durch Hyperlinks ersetzt:


#!/usr/local/bin/perl

print "Content-type: text/html\n\n";

###################### QUERY_STRING enthaelt Dateinamen
$med_infofile = $ENV{"QUERY_STRING"};

#################### HTML - Header
print "<HTML><HEAD><TITLE>Suchergebnis der Roten Liste</TITLE></HEAD>\n";
print "<BODY>\n";

##################### Kein Name uebergeben  ?
if (length($med_infofile)==0) 
{
  print "FEHLER: Keine Text-ID (RLTID) uebergeben !\n";
  print "</BODY></HTML>\n";
  exit;
}

$med_infofile = "/usr/users/rl/data/afpraeps/".$med_infofile;
open (WOK, "$med_infofile");
@zeilen = <WOK>;
chop @zeilen;
if ($#zeilen == -1) 
{
      $zeilen[0]="FEHLER: Datei $med_infofile konnte nicht gelesen werden !"; 
}
$zeilen[0] =~ s/\|o/ö/g;   # deutsche Umlaute ersetzen
$zeilen[0] =~ s/\|O/Ö/g;
$zeilen[0] =~ s/\|u/ü/g;
$zeilen[0] =~ s/\|U/Ü/g;
$zeilen[0] =~ s/\|a/ä/g;
$zeilen[0] =~ s/\|A/Ä/g;
$zeilen[0] =~ s/\|s/ß/g;

print "<H2>$zeilen[0]</H2>";
print "<HR>\n";
print "<PRE>\n";

for ($i=0; $i<=$#zeilen; $i++)
{
  $zeilen[$i] =~ s/\|o/ö/g;   # deutsche Umlaute ersetzen
  $zeilen[$i] =~ s/\|O/Ö/g;
  $zeilen[$i] =~ s/\|u/ü/g;
  $zeilen[$i] =~ s/\|U/Ü/g;
  $zeilen[$i] =~ s/\|a/ä/g;
  $zeilen[$i] =~ s/\|A/Ä/g;
  $zeilen[$i] =~ s/\|s/ß/g;
  
  if (ord(substr ($zeilen[$i], 0, 1)) == 32)
  {   
      $_ = $zeilen[$i];
      if (/(   )([^ :]*:)( .*)/)             ##### Anw.: Dos.: usw.
         {  $_ = "$1<B>$2</B>$3";}
      $found = 1;
      while ($found)
      {
          if (/(   .* )(Gr {1,2}[0-9]{3,3}|La {1,2}[0-9]{3,3}|[A-Z] {1,2}[0-9]{3,3})([ \,\.].*|$)/
                 && substr($2, 0, 2) ne "E ")  # Farbstoffe "E nnn" aussieben
             { $vorA  = $1;  

               $inA   = $2;
               $nachA = $3;
               $href = $2;
               $href =~ s/ /\+/g;
               $href = "rote_liste_hinweis.pl?".$href;
               $_ = "$vorA<A HREF=\"$href\">$inA</A>$nachA";}
          elsif (/(   .* )(Gr {1,2}[0-9]{2,2}|La {1,2}[0-9]{2,2}|[A-Z] {1,2}[0-9]{2,2})([ \,\.].*|$)/)
             { $vorA  = $1;
               $inA   = $2;
               $nachA = $3;
               $href = $2;
               $href =~ s/ /\+/g;
               $href = "rote_liste_hinweis.pl?".$href;
               $_ = "$vorA<A HREF=\"$href\">$inA</A>$nachA";}
          elsif (/(   .* )(Gr {1,2}[0-9]{1,1}|La {1,2}[0-9]{1,1}|[A-Z] {1,2}[0-9]{1,1})([ \,\.].*|$)/)
             { $vorA  = $1;
               $inA   = $2;
               $nachA = $3;
               $href = $2;
               $href =~ s/ /\+/g;
               $href = "rote_liste_hinweis.pl?".$href;
               $_ = "$vorA<A HREF=\"$href\">$inA</A>$nachA";}
          else {$found = 0;}
      }
      if (/  .*\.\.\..*/)            ##### Packungsgroessen
         {  $_ = "<I>$_</I>";}
      print $_, "\n";
  } 
  else { print "</PRE><BR><PRE><H3>$zeilen[$i]</H3>\n";}   ##### Ueberschrift 
}

print "<PRE>\n";
print "<HR>\n";
print "</BODY></HTML>\n";

Nach dem Klick auf »Aspirin Plus C« (siehe letzte Grafik) erscheint die folgende HTML-Seite (durch obiges perl-Skript generiert):

In obigem Dokument tauchen also die mit Links versehenen Abkürzungen auf. Wird nun auf die Abkürzung »A 5« geklickt, wird das dritte perl-Skript gestartet, daß den Text zu der Abkürzung anzeigt:


#!/usr/local/bin/perl

# print "Content-type: text/plain\n\n";
print "Content-type: text/html\n\n";

###################### QUERY_STRING enthaelt Dateinamen
$med_hinweisfile = $ENV{"QUERY_STRING"};

#################### HTML - Header
print "<HTML><HEAD><TITLE>Suchergebnis der Roten Liste</TITLE></HEAD>\n";
print "<BODY>\n";

##################### Kein Name uebergeben  ?
if (length($med_hinweisfile)==0) 
{
  print "FEHLER: Keine Hinweis-ID  uebergeben !\n";
  print "</BODY></HTML>\n";
  exit;
}

$med_hinweisfile =~ s/\+/ /g;
$med_hinweisfile = "/usr/users/rl/data/gnw/".$med_hinweisfile;
open (WOK, "$med_hinweisfile");
@zeilen = <WOK>;
chop @zeilen;

if ($#zeilen == -1) 
{
      $zeilen[0]="FEHLER: Datei $med_hinweisfile konnte nicht gelesen werden !"; 
}
$zeilen[0] =~ s/\|o/ö/g;   # deutsche Umlaute ersetzen
$zeilen[0] =~ s/\|O/Ö/g;
$zeilen[0] =~ s/\|u/ü/g;
$zeilen[0] =~ s/\|U/Ü/g;
$zeilen[0] =~ s/\|a/ä/g;
$zeilen[0] =~ s/\|A/Ä/g;
$zeilen[0] =~ s/\|s/ß/g;

print "<H2>$zeilen[0]</H2>";
print "<HR>\n";
print "<PRE>\n";

for ($i=1; $i<=$#zeilen; $i++)
{
  $zeilen[$i] =~ s/\|o/ö/g;   # deutsche Umlaute ersetzen
  $zeilen[$i] =~ s/\|O/Ö/g;
  $zeilen[$i] =~ s/\|u/ü/g;
  $zeilen[$i] =~ s/\|U/Ü/g;
  $zeilen[$i] =~ s/\|a/ä/g;
  $zeilen[$i] =~ s/\|A/Ä/g;
  $zeilen[$i] =~ s/\|s/ß/g;
  
  if (ord(substr ($zeilen[$i], 0, 1)) == 32)
  {   
      $_ = $zeilen[$i];
      if (/ {9,}[^ ]{1,1}.*/ || length ($_) > 40)
         {print $_, "\n";}
      else
         {print "<B>$_</B>\n";}
  }
  elsif ($zeilen[$i] ne "") 
  {
      print $zeilen[$i], "\n";
  } 
}

print "<PRE>\n";
print "<HR>\n";
print "</BODY></HTML>\n";

Hier nun das formatierte Ergebnis:


Zum Kapitel 4 Zum Kapitel 6 Zum Inhalt