Berichte Scripts

<< Klicken Sie, um das Inhaltsverzeichnis anzuzeigen >>

Navigation:  Scripts >

Berichte Scripts

Der Berichtegenerator von EMIL erzeugt Berichte aus Daten der Datenbank. Im Gegensatz zum Formular-Script wird ein Berichtscript nicht in einem einzigen Durchlauf abgearbeitet, sondern in drei Phasen aufgerufen. Dieses Kapitel beschreibt die Grundfunktion eines Berichtscripts, das Einbetten von SQL-Abfragen und enthält eine Befehlsreferenz mit Beispielen.

Die allgemeine Scriptsprache wird im Kapitel Scripting-Tutorial beschrieben - dieses Kapitel setzt das Grundwissen voraus.

Die drei Phasen eines Berichtscripts

Ein Berichtscript besteht aus einem äußeren begin/end-Block, der drei vom Berichtegenerator gerufene Prozeduren bzw. Funktionen definiert. Der eigentliche Aufruf der drei Routinen erfolgt durch EMIL:

1.DefineParameters - wird einmal vor der Anzeige der Parametereingabe-Maske gerufen. In dieser Phase registriert das Script über AddReportParam, welche Parameter der Benutzer eingeben soll.

2.CheckParameters - wird gerufen, sobald der Benutzer die Parameter ausgefüllt und auf Bericht erstellen geklickt hat. Liefert die Funktion einen leeren String zurück, gilt die Eingabe als gültig. Andernfalls wird der zurückgegebene Text als Warnmeldung angezeigt und der Benutzer muss die Parameter korrigieren.

3.CreateContent - wird gerufen, wenn alle Parameter gültig sind, und erzeugt den eigentlichen Bericht. Der erste Parameter ist der Titel des Berichts (kann ignoriert werden).

Skelett eines Berichtscripts:

begin

 

   procedure DefineParameters;

   begin

     AddReportParam('Von Datum', 'vondat', '01.01.' + IntToStr(YearOf(now)), 'D', nil);

     AddReportParam('Bis Datum', 'bisdat', 'heute', 'D', nil);

   end;

 

   function CheckParameters: String;

   begin

     if lowestDateFrom(GetParam('vondat')) > highestDateFrom(GetParam('bisdat')) then

       result := 'Das Von-Datum liegt nach dem Bis-Datum.'

     else

       result := '';

   end;

 

   procedure CreateContent(const aTitle: String);

   var

     ds: TDataset;

   begin

     initReport('Aufnahmen', 'Stand: ' + DateToStr(now), '', false);

     ds := getDataset(getSQLVar('sql1'),

       [lowestDateFrom(GetParam('vondat')), highestDateFrom(GetParam('bisdat'))]);

     while not ds.eof do

     begin

       PrintContent(ds['name'], DateToStr(ds['visit_date']), true);

       ds.next;

     end;

   end;

 

   { sql1 = select s.name, v.visit_date

           from subject s

           join visit v on v.subject_id = s.id

         where v.visit_date between :p1 and :p2

         order by v.visit_date }

 

end.

Einbetten von SQL-Abfragen

SQL-Statements im Berichtscript können auf zwei Wegen angegeben werden:

Direkt als Stringliteral in getDataset. Das ist einfach, wird aber bei längeren Abfragen schnell unleserlich.

Über benannte Kommentare. EMIL durchsucht den Scriptcode vor der Ausführung nach Pascal-Kommentaren in der Form { name = SQL-Statement }. Das gefundene SQL-Statement kann anschließend über getSQLVar(name) abgerufen werden. So bleibt der eigentliche Scriptcode lesbar und mehrzeilige SQL-Statements lassen sich frei formatieren.

Die SQL-Statements verwenden positional benannte Parameter :p1, :p2, ... (oder beliebige andere Namen). Im Aufruf von getDataset wird das Array aParams in der gleichen Reihenfolge übergeben:

ds := getDataset(getSQLVar('sql1'), [aVonDatum, aBisDatum]);

Innerhalb der SQL-Statements kann mit ProfileSQL der profilabhängige Filter eingefügt werden, der Akten anderer Indikationen ausblendet:

{ sql1 = select s.name from subject s where 1=1 } + ProfileSQL('s')

Hinweis: ProfileSQL liefert direkt das where-Fragment (z.B. and exists (...)), das mit String-Konkatenation an das SQL angehängt wird.

Parameter mit Auswahllisten

Listenparameter (Typ L oder M) erwarten eine TStringList mit Einträgen der Form Anzeige+#255+Code. Die Liste kann manuell aufgebaut oder über GetCodeList aus einer SQL-Abfrage gefüllt werden:

procedure DefineParameters;

begin

   AddReportParam('Indikation', 'indi', '', 'L',

     GetCodeList('select name||''''||chr(255)||''''||id from indication order by name'));

end;

Soll der ausgewählte Code per GetParam ausgelesen werden, liefert GetParamDecode zusätzlich den sprechenden Namen aus der Anzeigespalte - nützlich für den Berichtkopf.

 

Befehlsreferenz

Die folgenden Funktionen stehen in Berichtscripten zur Verfügung.

 

Allgemeine Berichtfunktionen

procedure initReport(const aHeader, aFooter, aSubHeader: String; landscape: Boolean);

Initialisiert die Berichteengine für einen neuen Bericht mit Kopf-/Fußzeile und optionaler Unterzeile. Mit landscape = true wird Querformat verwendet.

initReport('Patientenliste', 'Stand: ' + DateToStr(now), '', false);

 

procedure setTabs(const aTabs: array of byte);

Setzt Tabulatorpositionen (in Prozent der Seitenbreite) für tabellarische Berichte.

setTabs([20, 40, 60, 80]);

 

procedure printTabHeader(const Content: String);

Erzeugt eine Titelzeile im Seitenkopf für tabellarische Berichte. Die Spalten werden durch Tab-Zeichen (#9) getrennt und an den mit setTabs definierten Positionen ausgegeben.

printTabHeader('Name'#9'Geb.-Datum'#9'Diagnose'#9'Datum');

 

procedure PrintSubHeader(const Content: String; centered: Boolean);

Erzeugt eine Zwischenüberschrift zur Abtrennung von Unterbereichen im Bericht; mit centered = true wird zentriert ausgegeben.

PrintSubHeader('Abteilung Rheumatologie', false);

 

procedure PrintContent(const aLabel, aContent: String; AltColors: Boolean);

Gibt eine Druckzeile mit fettem Label und Inhalt aus. Mit AltColors = true wird ein gestreiftes Zebra-Layout zur besseren Lesbarkeit erzeugt.

PrintContent('Gewicht (kg)', Format(gewicht, 1), true);

 

procedure AddToWorkList(const aText: String; aId: Int64);

Fügt einen Eintrag zur Arbeitsliste hinzu. Die Liste wird mit jedem Berichtsaufruf geleert und neu befüllt, damit Patienten im Bericht durch Anklicken direkt aus der Liste geöffnet werden können.

AddToWorkList(ds['name'], ds['subject_id']);

 

function Format(f: Extended; x: Integer): String;

Formatiert eine Fließkommazahl f mit x Nachkommastellen und gibt sie als Zeichenkette zurück - direkt für die Ausgabe.

PrintContent('BMI', Format(bmi, 1), true);

 

function roundTo(f: Extended; x: Integer): Extended;

Rundet eine Fließkommazahl f auf die angegebene Stellenzahl. Achtung: um Nachkommastellen zu runden, muss ein negatives x angegeben werden: 1234.5678 wird bei x = 2 auf 1200 gerundet, bei x = -2 auf 1234.57.

summe := roundTo(summe, -2);

 

Berichtparameter

procedure AddReportParam(const aTitle, aVarname, aPreset: String; aType: Char; aList: TStringList);

Fügt einen Berichtsparameter hinzu, der im Berichtcode bei der Ausführung über GetParam mit dem in aVarname angegebenen Namen abgefragt werden kann. aTitle ist die Beschriftung, unter der der Parameter vom Benutzer abgefragt wird, aPreset ein Vorschlagswert. aType legt den Parametertyp fest:

'S' - String (Freitext)

'F' - Fließkommawert

'I' - Integer (Ganzzahl)

'D' - Datum (unscharf, siehe lowestDateFrom/highestDateFrom)

'L' - Einfachauswahl-Liste

'M' - Mehrfachauswahl-Liste

Bei Verwendung von Listen ist aList als TStringList mit Einträgen der Form Anzeige+#255+Code anzugeben oder kann per SQL mit GetCodeList bzw. GetTypeCodeList gefüllt werden. Bei anderen Typen ist nil zu übergeben.

AddReportParam('Von Datum', 'vondat', '01.01.'+IntToStr(YearOf(now)), 'D', nil);

AddReportParam('Geschlecht', 'gen', '', 'L', GetTypeCodeList(10001));

 

procedure SetNewList(const aParam: String; aList: TStringList);

Tauscht die aktuelle Liste eines bereits registrierten Parameters aus. Hilfreich, wenn sich Listen gegenseitig bedingen - z.B. wenn die Auswahl einer Abteilung die Liste der Ärzte einschränkt.

SetNewList('arzt', GetCodeList(...));

 

function GetParam(const aName: String): String;

Liefert bei der Berichtausführung den aktuellen Wert eines Parameters (bei Listenparametern: den Code, nicht die Anzeige). Bei Mehrfachauswahl werden die Codes durch Komma getrennt zurückgegeben.

aVonDatum := lowestDateFrom(GetParam('vondat'));

aGenderCode := GetParam('gen');

 

function GetParamDecode(const aName: String): String;

Ermittelt zu einem Listenparameter den sprechenden Namen aus der Anzeigespalte - z.B. für den Berichtkopf.

initReport('Patienten Geschlecht: ' + GetParamDecode('gen'), '', '', false);

 

function lowestDateFrom(const s: String): TDateTime;

Ermittelt das kleinste Datum aus einer unscharfen Angabe - z.B. '2000' → 1.1.2000 oder '5.2010' → 1.5.2010. Damit lassen sich vom Benutzer eingegebene Zeiträume sicher in SQL-Parameter umwandeln.

aVon := lowestDateFrom(GetParam('vondat'));

 

function highestDateFrom(const s: String): TDateTime;

Pendant zu lowestDateFrom: ermittelt das größte Datum aus einer unscharfen Angabe - '2000' → 31.12.2000, '5.2010' → 31.5.2010.

aBis := highestDateFrom(GetParam('bisdat'));

 

SQL und Datenzugriff

function getDataset(const aSQL: String; aParams: array of variant): TDataset;

Erzeugt und öffnet eine Datenmenge aus dem angegebenen SQL-Statement. Parameter im SQL (z.B. :p1, :p2) werden aus aParams in der angegebenen Reihenfolge gefüllt.

ds := getDataset('select * from subject where created_at > :p1', [aVon]);

while not ds.eof do begin ... ds.next; end;

 

function getSQLVar(const aName: String): String;

Liefert das SQL-Statement, das im Scriptcode als benannter Kommentar in der Form { name = SQL-Statement } hinterlegt wurde. So lassen sich lange SQL-Abfragen außerhalb des eigentlichen Codes lesbar formatieren und über getDataset verwenden.

{ sql1 = select s.name, v.visit_date

         from subject s join visit v on v.subject_id = s.id

       where v.visit_date between :p1 and :p2 }

...

ds := getDataset(getSQLVar('sql1'), [aVon, aBis]);

 

function GetCodeList(const aSQL: String): TStringList;

Führt ein SQL-Statement aus, das genau eine Spalte liefert, und gibt das Ergebnis als TStringList zurück - geeignet für AddReportParam mit Listentyp. Üblicherweise wird die Spalte in der SQL bereits als Anzeige||chr(255)||Code zusammengesetzt.

AddReportParam('Abteilung', 'abt', '', 'L',

   GetCodeList('select name||chr(255)||id from department order by name'));

 

function ProfileSQL(const aAlias: String): String;

Liefert eine SQL-where-Klausel, die Akten anderer Indikationen aus den Ergebnissen entfernt. aAlias ist der Tabellenalias der subject-Tabelle im SQL-Statement. Das Fragment kann per String-Konkatenation an ein SQL angehängt werden.

ds := getDataset('select s.* from subject s where 1=1 ' + ProfileSQL('s'), []);

 

Dictionary-Funktionen

function GetTypeUnitName(id: Int64): String;

Liefert den aktuell eingestellten Einheitenbezeichner für einen Dictionary-Datentyp.

PrintContent('Einheit', GetTypeUnitName(40001), true);

 

function GetTypeTitle(id: Int64): String;

Liefert den aktuell eingestellten Titel eines Dictionary-Datentyps.

PrintContent(GetTypeTitle(40001), Format(value, 2), true);

 

function GetTypeTitleWithUnit(id: Int64): String;

Wie GetTypeTitle, aber mit angehängter Einheit - z.B. 'Gewicht (kg)'.

PrintContent(GetTypeTitleWithUnit(40001), Format(value, 2), true);

 

function GetTypeCodeList(id: Int64): TStringList;

Ermittelt die Code-Liste für einen Dictionary-Datentyp (z.B. ein Drop-Down-Feld mit fester Werteliste). Direkt für AddReportParam als Listenquelle verwendbar.

AddReportParam('Diagnose', 'diag', '', 'L', GetTypeCodeList(45000));

 

Entwicklungssupport

procedure Debug(content: Variant);

Gibt einen Wert zu Entwicklungs- oder Fehlersuchzwecken in das Konsolenfenster des Berichtsdesigners aus. Im Produktivbetrieb wirkungslos.

debug('sql1 = ' + getSQLVar('sql1'));