Berichte Scripts

<< Klicken Sie, um das Inhaltsverzeichnis anzuzeigen >>

Navigation:  Scripts >

Berichte Scripts

Der Berichtegenerator von EMIL erzeugt Berichte aus Daten der Datenbank. Anders als ein Formular-Script wird ein Berichtscript in drei Phasen aufgerufen. Dieses Kapitel beschreibt den Aufbau eines Berichtscripts, das Einbetten von SQL-Abfragen und die Befehlsreferenz. Vorausgesetzt werden die Scripting-Grundlagen.

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 können direkt als Stringliteral an getDataset übergeben werden. Längere Abfragen schreiben Sie besser als benannten Kommentar in der Form { name = SQL-Statement } in den Scriptcode, wie im Skelett oben. EMIL liest diese Kommentare vor der Ausführung aus; getSQLVar(name) liefert dann das Statement. So bleibt der Code lesbar und mehrzeiliges SQL lässt sich frei formatieren.

Die SQL-Statements verwenden positionell 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]);

Den Profilfilter hängen Sie per String-Verkettung an. ProfileSQL liefert eine geklammerte Bedingung ohne führendes and; im benannten Kommentar endet das SQL deshalb mit where oder and:

{ sql2 = select s.name from subject s where }

ds := getDataset(getSQLVar('sql2') + ProfileSQL('s'), []);

Parameter mit Auswahllisten

Listenparameter (Typ L oder M) erwarten eine TStringList mit Einträgen der Form Anzeige+#255+Code, die Sie manuell aufbauen oder per GetCodeList aus einer SQL-Abfrage füllen:

procedure DefineParameters;

begin

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

     GetCodeList('select name, id from indication order by name'));

end;

GetParam liefert den ausgewählten Code, GetParamDecode den Anzeigetext, z.B. für den Berichtkopf.

Befehlsreferenz

Zusätzlich zu den Standardfunktionen stehen in Berichtscripts diese Funktionen zur Verfügung. Format und roundTo sind bei den Standardfunktionen beschrieben.

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']);

 

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 in SQL-Parameter umwandeln. Zu unscharfen Datumsangaben siehe Fuzzy Dates.

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 { name = SQL-Statement } hinterlegt ist (siehe Einbetten von SQL-Abfragen).

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

 

function GetCodeList(const aSQL: String): TStringList;

Führt ein SQL-Statement aus, das genau zwei Spalten liefert - erste Spalte Anzeigetext, zweite Spalte Code - und gibt das Ergebnis als TStringList im Format Anzeige+#255+Code zurück, geeignet für AddReportParam mit Listentyp. Die Verkettung mit chr(255) übernimmt die Funktion selbst, sie gehört nicht ins SQL.

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

   GetCodeList('select name, id from department order by name'));

 

Profilfunktionen im Überblick

Funktion

Filtert / liefert

Verwendung

ProfileSQL(alias)

Akten (subject.profile_codes)

Bedingung für die Tabelle subject: blendet Akten anderer Indikationen aus.

ActiveProfileSQL(alias)

Dictionary (type.active_profile_codes)

Bedingung für die Tabelle type: nur im Benutzerprofil freigeschaltete Einträge.

CurrentProfile

Profilcode des Benutzers (ein Zeichen)

Parameter für type_profile/type_group_profile oder VTYPE.

ActiveProfiles

alle freigeschalteten Profilcodes

Parameter für VTYPE, nur bei aktivem Rhadar oder Rabbit nötig.

Hinweis: Das Feld type.enabled_flag wird nicht mehr gepflegt. Berichte, die noch darauf filtern, blenden neu angelegte Dictionary-Einträge fälschlich aus. Verwenden Sie stattdessen ActiveProfileSQL oder VTYPE mit CurrentProfile/ActiveProfiles.

function ProfileSQL(const aAlias: String): String;

Liefert eine geklammerte Bedingung ohne führendes and, die Akten anderer Indikationen ausblendet (Prüfung über subject.profile_codes). aAlias ist der Tabellenalias der subject-Tabelle im SQL-Statement.

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

 

function ActiveProfileSQL(const aAlias: String): String;

Liefert eine geklammerte Bedingung, die Dictionary-Einträge (Tabelle type) auf die im aktuellen Benutzerprofil freigeschalteten Einträge einschränkt (Prüfung über type.active_profile_codes). aAlias ist der Tabellenalias der type-Tabelle.

sl := GetCodeList('select t.title, t.id from type t ' +

   'where ' + ActiveProfileSQL('t') + ' order by 1');

 

function CurrentProfile: String;

Liefert den Profilcode des angemeldeten Benutzers (ein Zeichen, z.B. r für Erwachsenenrheumatologie), etwa als SQL-Parameter beim Join auf profilabhängige Bezeichnungen in type_profile bzw. type_group_profile.

ds := getDataset('select coalesce(tp.title, t.title) from type t ' +

   'left join type_profile tp on tp.type_id=t.id and tp.profile_code=:p1 ' +

   'where ' + ActiveProfileSQL('t'), [CurrentProfile]);

 

function ActiveProfiles: String;

Liefert alle Profilcodes, unter denen für den angemeldeten Benutzer etwas freigeschaltet sein kann: das eigene Profil (an erster Stelle) plus c (kundenspezifisch, immer), z bei aktivem Rhadar und x bei aktivem Rabbit.

CurrentProfile und ActiveProfiles sind als Parameter für die Datenbankprozedur VTYPE gedacht. Sie liefert eine für ein Profil aufbereitete Sicht auf das Dictionary mit ACTIVE_FLAG, profilabhängigen Titeln (PROFILE_TITLE, TITLE_UNIT) und Gruppen (GROUP_TITLE, AREA), standardmäßig nur mit freigeschalteten Einträgen. Bei mehreren Profilcodes bestimmt das erste Zeichen, aus welchem Profil Titel und Gruppen stammen. Hat ein Benutzer wie üblich ein Profil und ist weder Rhadar noch Rabbit aktiv, genügt CurrentProfile, denn c ergänzt VTYPE selbst:

sl := GetCodeList('select title_unit, id from vtype(''' + CurrentProfile +

   ''') where type_ind in (''slist'',''mlist'') order by 1');

 

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'));