This file is part of the pdr/pdx project.
Copyright (C) 2010 Torsten Mueller, Bern, Switzerland
This program is free software: you can redistribute it and/or modify it
under the
terms of the GNU General Public License as published by the Free
Software Foundation, either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
pdr/pdx 1.1.0 - Benutzerhandbuch
pdr ("personal data recorder")
und pdx ("personal data expert")
sind
freie
Programme
zur
Erfassung,
Verwaltung
und
Auswertung
von
persönlichen,
meist
numerischen
Daten.
Inhalt
1. Einleitung und
Grundlagen
2. Begriffe
3. Arbeitsweise
4. Aufruf
5. Konfiguration
1. Einleitung und
Grundlagen
Menschen arbeiten heute an Computern, oft mehrere Stunden
tagtäglich.
Praktisch jeder hat zudem in seiner computerfreien Zeit ein
einsatzbereites Mobiltelefon, einen Palmtop oder ein ähnlich
mobiles Gerät. Persönliche Daten, die im Laufe eines Tages
anfallen,
könnten mit Hilfe ganz verschiedener Infrastruktur einer
Auswertung zugeführt werden, sofern solche Wege nutzbar gemacht
würden. Der Benutzer muß
dasjenige
wählen und nutzen können, was er gerade
zur Verfügung hat. Das können je nach Ort, Tag und Tageszeit
durchaus ganz unterschiedliche Mittel sein: Auf einem
PC kann er seine Daten sowohl
direkt mit pdr eingeben oder über Betriebssystemgrenzen
hinweg eine e-mail-Nachricht schicken, vielleicht unter Benutzung eines
Kommandozeilenwerkzeugs wie sendmail
oder
aus einer Office-Anwendung heraus. Mit
seinem
Mobiltelefon
kann
er
ebenso e-mails oder SMS
versenden. Er benutzt aber vielleicht auch Meßgeräte, die
Daten in
privaten Speichern sammeln, so daß er sie später über
ein USB-Kabel, per Bluetooth, Infrarot
o.ä. auf einen Computer übertragen kann. Und
möglicherweise liefert bereits eine Software Daten in einem
XML-Format. All diese Wege
müssen
gleichwertig offenstehen.
Die Ausgangslage besteht in folgenden Annahmen:
- Für Daten im hier relevanten Sinne liegt mindestens ein
brauchbares Medium
vor,
über das
die Daten mit vertretbarem Aufwand auf einen Computer gelangen
können.
- Datenerfassung und -auswertung erfolgen nicht zeitgleich,
insbesondere
nicht in Echtzeit. Daten
fallen (ggf. viel) häufiger an, als sie ausgewertet werden
müssen.
- Die Datenerfassung muß deshalb schnell,
einfach
und
mobil sein. Das ist das wesentliche Kriterium
für ihre Akzeptanz.
- Bei der Datenauswertung ist der Zeitbedarf
hingegen unkritisch, dort spielen Leistungsumfang und
Konfigurierbarkeit die
wesentliche Rolle.
- Als Datenauswertung gilt ein generierter, statischer Bericht ggf.
mit
Diagrammen. Es ist zunächst keine interaktive Arbeit am
Datenbestand
erforderlich.
Hintergrund: Ursprünglich stand die Erfassung medizinischer Daten
im
Vordergrund (Blutzucker, Blutdruck, Temperatur, Puls, Gewicht sowie
auch notwendiger Medikamentierung). Insbesondere bei insulinpflichtigen
Diabetikern fällt pro Tag eine ganze Reihe solcher Daten an, die
zu
erfassen und mit Kommentaren zu versehen für sie (und z.B. auch
für
Ärzte) interessant
ist.
Die Applikationen sind allerdings nicht auf medizinische
Anwendungsfälle spezialisiert und festgelegt. Man könnte sie
auch für technische, Sport-, Wetter-, Umwelt- oder Finanzdaten
verwenden,
beispielsweise Strecken und Zeiten beim Joggen oder den Benzinverbrauch
eines Autos, dessen gefahrene
Strecken und die verursachten Kosten erfassen. Wichtig ist lediglich,
daß mehr oder weniger kontinuierlich Daten anfallen, die
erfaßt und statistisch ausgewertet werden sollen.
2. Begriffe
2.1. collections
Die Datenbank ist das zentrale Bindeglied zwischen pdr und pdx. Um ihre
interne Gestalt muß sich der Benutzer normalerweise nicht
kümmern. Der Benutzer arbeitet über die Programme pdr und pdx
fast ausschließlich mit sog. collections
(Meßreihen). Dies
ist ein Konzept: Eine collection
speichert alle Werte jeweils eines konkreten Meßparameters
zusammen mit je
einem
eindeutigen Zeitstempel:
[...]
2008-12-17 21:45:00 5.9
2008-12-18 05:00:00 6.1
2008-12-18 12:45:00 5.3
2008-12-18 18:45:00 5.3
2008-12-18 21:45:00 4.7
2008-12-19 05:00:00 5.2
2008-12-19 12:45:00 5.4
2008-12-19 18:45:00 4.7
2008-12-19 21:45:00 5.7
[...]
Wenn fünf verschiedene
Meßparameter aufzuzeichnen sind, werden auch fünf collections benötigt. Der
Benutzer kann mit
pdr solche collections
jederzeit anzeigen, nach Bedarf
und in beliebiger Anzahl anlegen und
auch wieder
löschen.
Jede collection trägt
einen
eindeutigen Namen. Dieser Name
ist eine Kombination aus folgenden
Zeichen:
A...Z
a...z
_
*
+
!
?
^
°
§
$
/
&
[
]
{
}
=
~
Groß- und Kleinschreibung werden unterschieden. Die Anzahl der collections und die Länge
ihres Namens sind nicht beschränkt.
Hinweis:
Aufgrund
dessen, daß diese Namen in
Ausdrücken
und deshalb sehr
häufig, nämlich bei jeder Eingabe, verwendet werden, sollten
sie möglichst kurz sein. Es
spricht nichts dagegen, einzelne Buchstaben zu vergeben.
Zwei collections haben feste
Namen: * und #. Erstere ist die sog. default collection,
sie
ist
vom
Typ
numeric.
Die andere ist die Kommentar-collection, sie
ist vom Typ text.
Diese beiden collections
müssen nicht explizit angelegt werden, sie existieren immer. Der
Grund der Existenz dieser beiden collections
ist ihre spezielle (namenlose) Benutzung in Ausdrücken. Man sollte
sie folglich beide für den jeweils häufigsten Anwendungsfall
benutzen.
collections haben je einen
konkreten Typ, der für
alle ihre Datenwerte zutrifft und beim
Anlegen vereinbart werden muß. Gemischte collections sind konzeptionell
nicht vorgesehen. Es gibt
drei mögliche Typen:
- numeric (Gleitkommazahlen
mit
doppelter
Genauigkeit)
- ratio (ein
Paar
von
zwei
Gleitkommazahlen,
gedacht
für
Blutdruckangaben)
- text (Zeichenkette
beliebiger
Länge,
für
Kommentare
o.ä.)
2.2. rejections
Bei Eingaben, die in die Datenbank gelangen, kann es vorkommen,
daß eine
solche nicht den Anforderungen genügt, möglicherweise weil
sie falsch geschrieben ist oder ein ungültiges Datum besitzt.
Diese Eingaben werden zurückgewiesen (rejected). Das bedeutet, sie
gelangen nicht in den gültigen Datenbestand, nicht in collections. Sie gelangen
allerdings in eine eigene Tabelle und sind nicht verloren.
Die Idee
dahinter ist, daß diese Datensätze später aufgelistet
und interaktiv nachbearbeitet werden können. Das ist besonders bei
der Eingabe per e-mail wichtig,
weil e-mail-Nachrichten auf dem Server nicht korrigiert werden
können, es aber auch keinen Sinn hat, sie unkorrigiert auf dem
Server zu belassen. Im Moment werden nur wenn nötig e-mail-Daten rejected, alle anderen Datenquellen
können korrigiert und müssen nicht in dieser Form behandelt
werden.
Zur Behandlung von rejections
besitzt pdr zwei eigene Kommandozeilenparameter.
2.3. Ausdrücke
pdr wertet bei der Eingabe über e-mail-Postfächer,
die
Kommandozeile oder Textdateien sog. Ausdrücke
(expressions) aus. Jede Zeile
wird
als
ein solcher Ausdruck betrachtet und interpretiert. Ein Ausdruck kann
verschiedene Meßparameter (also Datenwerte) enthalten,
weshalb man bekanntgeben muß, welcher Wert in welche collection gelangen soll.
Hierfür steht eine einfache Syntax zur Verfügung, der Name
der collection wird an den
Wert angehängt:
[Datum]
[Zeit]
(Wert[collection])* [; Kommentar]
Diese Definition besagt:
- die gesamte Zeile bildet einen Ausdruck
- Datum und Zeitangabe sind optional, erfolgt keine oder eine
teilweise Angabe, so
werden das aktuelle Datum und die aktuelle Uhrzeit benutzt, um die
Eingabe zu ergänzen
- Datum und Zeit gelten für den gesamten Ausdruck, alle
nachfolgenden Werte und auch eventueller Kommentar auf dieser Zeile
erhalten denselben
Zeitstempel
- es folgen 0 oder beliebig viele Wert-collection-Paare, d.h. ein Wert und
der Name einer collection
ohne
Leerraum dazwischen, der collection-Name
ist
optional,
wird
kein
collection-Name
angegeben,
so
wird
automatisch
die default
collection benutzt
- optional kann hinter einem Semikolon noch Kommentar angegeben
werden, als Kommentar gilt alles Nachfolgende bis zum Zeilenende
Hinweis:
wenn innerhalb ein und
desselben
Ausdrucks zwei Werte für ein und dieselbe collection angegeben sind, so wird
nur der hintere übernommen, denn es kann pro Zeitstempel in einer collection nur einen Wert geben.
Datum und Uhrzeit besitzen eine konkrete, nicht lokalisierte Syntax,
die strikt einzuhalten ist:
[CCYY-]MM-DD
sowie
hh:mm[:ss]
Beispiele
Angenommen, wir haben folgende numerische collections in der Datenbank
vereinbart: l, m, n, dazu ohnehin * und #. Folgende Ausdrücke
wären
somit korrekt:
5.2
(implizite Verwendung der default
collection)
5.2*
(explizite Verwendung der default collection)
5.2 8l 7n 1m
2009-08-16 12:34 5.3 9n ; dies
ist mein Kommentar
23:45 15l
; nur Kommentar
Es ist zu bemerken, daß das Primat hier klar auf leichter Eingabe
liegt, auch wenn es im ersten Moment ein wenig kryptisch anmutet. Diese
Ausdrücke (siehe die ersten drei Zeilen) sind auch
auf Mobiltelefonen wirklich leicht zu schreiben. Gelesen werden
müssen sie
nur von einer Maschine. Man kann solche Ausdrücke, falls
tatsächlich einmal keine Möglichkeit besteht, Daten zu
übermitteln, auch leicht in einen Texteditor eingeben oder selbst
auf einen Zettel schreiben und später abtippen.
2.4. Selektion
Eine Selektion ist ein
Ausschnitt aus einer collection. Der Begriff ist
spezifisch für pdx und die dort vorgenommenen Auswertungen und
Berechnungen. Man gewinnt eine Selektion durch einen Aufruf der Funktion select oder durch eine
Berechnung, die eine Selektion zurückliefert. Die
Einschränkung der collection
erfolgt, da eine collection
nur die Zeit als Dimension besitzt, immer zeitlich, d.h. durch
Auswählen bestimmter, einzelner Werte, die durch ihren Zeitstempel
eindeutig benannt werden. Diese Einzelwerte müssen dabei nicht
fortlaufend sein, eine Selektion darf auch Lücken haben. Man kann
beispielsweise für einen ganzen Monat die Werte eines jeden Tages
zwischen 8 und 9 Uhr selektieren.
3. Arbeitsweise
3.1. pdr
3.1.1. Funktionsweise
pdr sammelt und erfaßt Daten aus verschiedenen Quellen und
trägt diese in collections
der Datenbank ein:
Mobiltelefon
->
e-mail-Postfach
...
\
Meßgerät
-+->
pdr
-> Datenbank
...
/
XML-Datei |
Die Datenbank dient als Bindeglied zwischen pdr und pdx.
3.1.2. Datenquellen
und Transaktionen
Momentan
stehen folgende Typen von Datenquellen zur Verfügung:
|
diese drei Datenquellen arbeiten
mit Ausdrücken
|
|
diese beiden Datenquellen
arbeiten mit
verschiedenen, spezifischen Datenformaten
|
Die meisten Datenquellen (inputs)
müssen
einzeln
konfiguriert
werden.
Die Datenquellen werden beim Aufruf
in der konfigurierten Reihenfolge abgefragt in der
Annahme, daß
sich dort unverarbeitete Daten befinden.
pdr arbeitet mit Transaktionen,
um
die
Integrität
der
Datenbank
so
weit
wie
möglich
zu
gewährleisten.
Diese
Transaktionen
erstrecken
sich
vom
Aufruf
des
Programms
(d.h.
der
Entgegennahme
der Parameter) bis zum Einfügen
von Werten in die Datenbank. Es soll zuverlässig ausgeschlossen
werden, daß die Daten einer Datenquelle nur zum Teil
in die Datenbank gelangen und zum Teil nicht. Wenn bei der Verarbeitung
ein Fehler auftritt, kann die Datenquelle korrigiert und die
Verarbeitung erneut gestartet werden.
Hinweis:
E-mail-Postfächer
stellen
dahingehend
aufgrund
der
besonderen
Gestalt
ihrer
Daten
einen
Sonderfall
dar
(siehe
dort).
Konfigurierte Datenquellen werden jede in einer jeweils eigenen
Transaktion verarbeitet.
Datenquellen die auf der Kommandozeile angegeben werden, werden
je in einer eigenen Transaktion verarbeitet, sofern es sich um Dateien
handelt. Ausdrücke, die auf der
Kommandozeile angegeben werden, werden zusammengefaßt und alle in
einer gemeinsamen Transaktion verarbeitet.
3.1.3. Eingabe per
Kommandozeile
Die einfachste (und zugleich unkomfortabelste) Eingabemöglichkeit
ist die Kommandozeile, d.h. der Aufruf von pdr
selbst. Hierzu muß
nichts speziell konfiguriert
werden.
pdr besitzt den Kommandozeilenparameter -e bzw. --expression, der die Angabe
genau eines Ausdrucks
erlaubt. Dieser
Parameter ist mehrfach verwendbar. Ferner werden alle hinter pdr eingegebenen Zeichen, die
nicht Teil oder Argument eines Kommandozeilenparameters sind, zu einem
Ausdruck zusammengefaßt und gemeinsam ausgewertet (siehe dort).
Besitzt der angegebene Ausdruck keinen Zeitstempel werden die
aktuellen Werte für Datum und Uhrzeit benutzt.
Tritt bei der Verarbeitung ein Fehler auf, weil der Ausdruck nicht
korrekt war, so gibt pdr eine Meldung aus. Eine Übernahme des
Ausdrucks in die rejections erfolgt nicht.
3.1.4. Eingabe per Mail (POP3)
Bei der Abfrage von e-mail-Postfächern wird davon ausgegangen,
daß der Benutzer anfallende Daten mit einem anderen Programm oder
einem Mobiltelefon an ein e-mail-Postfach sendet
und diese dort liegenbleiben (d.h. nicht anderweitig abgeholt werden).
Diese e-mails müssen folgende Eigenschaften aufweisen:
- ein eindeutiges subject
- einen verwertbaren Zeitstempel (dafür sorgt normalerweise
der SMTP-Server beim Senden)
- reines Text-Format (kein HTML, RTF o.ä.)
- Text ausschließlich in Ausdrücken
Ist für pdr eine e-mail-Datenquelle
konfiguriert, so wird sie beim nächsten Aufruf automatisch
abgefragt. pdr schaut dann nach, ob sich mails im Postfach befinden,
prüft deren subject und
wertet passende e-mails der Reihe nach aus, wobei jede Zeile als
jeweils ein Ausdruck interpretiert wird. Besitzt eine
Zeile einen eigenen Zeitstempel, so besitzt dieser Priorität,
ansonsten gilt für alle Zeilen implizit der Zeitstempel der
e-mail-Nachricht. Das ist insofern praktisch, weil man
üblicherweise in einzeiligen e-mails nie einen Zeitstempel manuell
eingeben muß.
Hier einmal ein vollständiger e-mail-Quelltext:
From: Torsten
Mueller <MyMail@gmx.net>
To: MyMail@gmx.net
Subject: Q
Date: Thu, 04 Feb 2010 17:56:11 +0100
Message-ID: <87pr4ley8k.fsf@castor.ch>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
5.3 8i
Die meisten Werte in den Headerzeilen kann man in der Regel aus Listen
entnehmen. Date und Message-ID setzt der Server
hinzu, MIME-Version und
Content-Type stammen vom
verwendeten e-mail-Client. Die einzigen Texte,
die man wirklich tippen muß, sind das subject (es empfiehlt sich darum,
das subject so kurz wie
möglich zu vereinbaren, hier ein einzelner Buchstabe) sowie der
Inhalt der Nachricht (letzte Zeile).
Verarbeitete e-mails werden anschließend unabhängig vom
Erfolg der Verarbeitung automatisch auf dem
Server gelöscht, so daß sie später nicht ein zweites
Mal verarbeitet werden. Dieses automatische Löschen kann bei der
Konfiguration der Datenquelle unterbunden werden.
Tritt bei der Verarbeitung ein Fehler auf, weil
ein Ausdruck nicht korrekt war o.ä., so gelangen die betreffenden
Ausdrücke in die rejections und müssen
nachbearbeitet werden. pdr gibt in diesem Falle eine Meldung aus.
3.1.5. Eingabe
über Textdateien
Bei der Verwendung von Textdateien zur Eingabe wird jede Zeile als
Ausdruck interpretiert. Dieses Verfahren ist
nützlich, wenn man
über einen gewissen Zeitraum Daten sammeln, aber nicht online
übermitteln kann. Man schreibt die Ausdrücke einfach mit
einem Editor Ausdruck für Ausdruck, d.h. Zeile für Zeile in
eine Textdatei.
Zeilen, die innerhalb solcher Textdateien mit #
beginnen, werden nicht
ausgewertet.
Tritt bei der Verarbeitung ein Fehler auf, bleibt die gesamte Datei
unverarbeitet. Eine Übernahme in die rejections erfolgt nicht.
Erfolgreich verarbeitete Textdateien werden, sofern sie in der
Konfigurationsdatei als Datenquelle angegeben sind, automatisch
gelöscht, damit sie nicht ein zweites Mal verarbeitet werden.
Dieses Löschen kann bei der Konfiguration unterbunden werden.
3.1.6. Eingabe
über CSV-Dateien
Die Abkürzung CSV steht für "comma
separated
values", durch Komma getrennte Werte. Das Komma ist
nicht ganz wörtlich zu nehmen, pdr akzeptiert das Komma, das
Semikolon und den Tabulator als Separator zwischen den Werten.
Es gibt zwei Möglichkeiten, pdr mitzuteilen, welcher
kommaseparierter Datenwert in welche collection gelangen soll:
- über eine Steuerzeile in der CSV-Datei, die den Datenzeilen
vorangeht
- über eine Steuerzeile in der Konfigurationsdatei, die
für die gesamte CSV-Datei gilt
Im ersten Fall besitzt eine von pdr
verwendbare CSV-Datei folgende Struktur:
Steuerzeile
Datenzeile1
[...]
DatenzeileN
Steuerzeile
Datenzeile1
[...]
DatenzeileN
[...]
Diese Art der Verwendung von Steuerzeilen ist ungewöhnlich,
liefert aber die gewünschte Flexibilität und Offenheit. In
der Regel kann man sie sehr leicht einfügen, entweder von Hand
oder über ein Programm wie sed.
Im
zweiten
Fall
enthielte
die
CSV-Datei
wie
erwartet
nur
Datenzeilen.
Eine
Steuerzeile
hat
folgenden
Aufbau:
[#
pdr]
datetime
[Separator collection]+
z.B.
#
pdr datetime, *, n, l; h; q»p, #
(» soll ein Tabulator sein)
Dies ist eine Steuerzeile für nachfolgende Datenzeilen mit jeweils
einem Zeitstempel und sieben weiteren Werten für die collections *, n, l, h, q, p und #.
Jede Steuerzeile wird innerhalb einer CSV-Datei an
ihrer Einleitung # pdr
erkannt, bei Angabe der Steuerzeile in der Konfigurationsdatei ist
diese Kennung nicht erforderlich. Das folgende Schlüsselwort datetime kennzeichnet die Lage
des Zeitstempels auf den
Datenzeilen. Es muß nicht am Anfang stehen, jedoch
auf jeder Zeile enthalten sein - Datenwerte ohne Zeitstempel sind nicht
denkbar. Im Beispiel ist zu sehen, daß
verschiedene Separatoren innerhalb einer Zeile benutzt werden
können. Datenzeilen, die zu dieser Steuerzeile passen, hätten
damit folgendes Aussehen:
2008-10-11
12:31:38,
5.2,
7,
8;
42.3;
12»96,
erste Messung
2008-10-12 12:48:08, 6.1, , 8; 53.1; 16»93,
2008-10-13 12:43:57, 5.8, 7, 7; 34.2; 15»94,
dritte
Messung
In der zweiten Zeile fehlen Werte für die collections n und # - im Fall fehlender Werte
erfolgen einfach keine Eingaben.
Besitzt man CSV-Dateien, die mehr Werte enthalten, als man in collections importieren will, so
kann man auf der Steuerzeile Auslassungen vereinbaren:
#
pdr datetime, a, b, , , , c, d, e
Hier werden hinter dem Zeitstempel zwei collections gelesen, dann jeweils drei
Datenwerte
auf den Datenzeilen ausgelassen, und dann die restlichen drei collections gelesen.
Zeilen, die innerhalb von CSV-Dateien mit dem Symbol #
beginnen, werden als Kommentar betrachtet und nicht weiter ausgewertet.
Beim Einfügen von Werten in die Datenbank wird eine gesamte
CSV-Datei in einer einzigen Transaktion behandelt. Wenn dabei ein
Fehler auftritt, beispielsweise weil ein Datenwert nicht zum Typ der in
der Steuerzeile vereinbarten collection
paßt, so wird die gesamte Datei verworfen. Es kann nicht
passieren, daß eine Datei zur Hälfte eingelesen wird und zur
anderen Hälfte nicht und man hinterher gar keine rechte Kontrolle
mehr darüber hat. Eine Übernahme in die rejections erfolgt nicht.
Erfolgreich verarbeitete CSV-Dateien werden, sofern sie in der
Konfigurationsdatei als Datenquelle angegeben sind, automatisch
gelöscht, damit sie nicht ein zweites Mal verarbeitet werden.
Dieses Löschen kann bei der Konfiguration unterbunden werden.
3.1.7. Eingabe
über XML-Dateien
pdr bietet die Möglichkeit, XML-Dateien einzulesen. Diese Dateien
sind wohlstrukturiert, les- und editierbar, und sind damit ideal zum
Austausch von Daten zwischen verschiedenen Softwaresystemen geeignet.
pdr definiert zunächst ein eigenes, absichtlich sehr einfaches
Format. Der entsprechende Programmteil ist aber dafür ausgelegt,
nach entsprechender Erweiterung auch andere XML-Formate zu lesen.
3.1.7.1. Das pdr-XML-Format
Das Format ist vollständig abgelegt in der Datei pdr.xsd:
<?xml version="1.0"
encoding="iso-8859-1" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsd:annotation>
<xsd:documentation xml:lang="en">
pdr XML input file definition (C) T.M.
Bremgarten 2010-01-31
</xsd:documentation>
</xsd:annotation>
<xsd:element name="pdr">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="collection" type="collection" minOccurs="0" maxOccurs="unbounded"
/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="collection">
<xsd:sequence>
<xsd:element name="item"
minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="datetime" type="xs:string" />
<xsd:attribute name="value" type="xs:string" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xs:string" />
</xsd:complexType>
</xsd:schema>
Diese Definition erlaubt Dateien, die folgendes Aussehen haben:
<?xml version="1.0"
encoding="ISO-8859-1"?>
<pdr>
<collection
name="#" type="text">
<item datetime="2001-07-09
18:27:11" value="erste Messung"/>
<item date
time
="2001-
07
-10
07:52:01"
value="zweite
Messung"/>
<item date
time
="2001-
07
-10
10:07:00"
value="dritte
Messung"/>
[...]
</collection>
<collection
name="*" type="numeric">
<item date
time
="2001-
07
-12
13:57:01"
value="9.3"/>
<item date
time
="2001-
07
-12
14:46:45"
value="5.6"/>
<item date
time
="2001-
07
-12
18:25:36"
value="5.7"/>
[...]
</collection>
<collection
name="l" type="numeric">
<item date
time
="2001-
07
-03
21:41:58"
value="7"/>
<item date
time
="2001-
07
-04
21:48:43"
value="8"/>
<item date
time
="2001-
07
-05
21:50:49"
value="7"/>
[...]
</collection>
</pdr>
Das Format ist beinahe selbsterklärend. Die Daten der collections
werden direkt lesbar angegeben und genau so eingelesen.
Beim Einfügen von Werten in die Datenbank wird eine gesamte
XML-Datei in einer einzigen Transaktion behandelt. Wenn dabei ein
Fehler auftritt, beispielsweise weil ein Datenwert nicht zum Typ der
angegebenen collection
paßt, so wird die gesamte Datei verworfen. Es kann nicht
passieren, daß eine Datei zur Hälfte eingelesen wird und zur
anderen Hälfte nicht und man hinterher gar keine rechte Kontrolle
mehr darüber hat. Eine Übernahme in die rejections erfolgt nicht.
Erfolgreich verarbeitete XML-Dateien werden, sofern sie in der
Konfigurationsdatei als Datenquelle angegeben sind, automatisch
gelöscht, damit sie nicht ein zweites Mal verarbeitet werden.
Dieses Löschen kann bei der Konfiguration unterbunden werden.
3.1.7.2 (weitere XML-Formate)
...
3.1.8. Interaktiver Betrieb
pdr besitzt eine interaktive Betriebsart, die das Editieren,
Ergänzen und Löschen von bereits in der Datenbank
befindlichen Datenwerten erlaubt. Dies ist sehr nützlich
einerseits zur Korrektur von Fehlern, andererseits zum
nachträglichen Anbringen von Kommentaren und weit einfacher und
komfortabler als die manuelle Ausführung von SQL-Statements in der
Datenbank.
Die interaktive Betriebsart wird über den Kommandozeilenparameter -i eingeschaltet:
$ pdr -i
pdr 0.3.7, interactive mode (press ? for help)
2010-03-03 12:38:01 6.2 1m
6n
>
Das Symbol rechts ist ein Prompt. pdr wartet auf Eingaben. Auf der
linken Seite der Zeile wird die letzte, d.h. jüngste Datenzeile
ausgegeben, die der Benutzer zuvor über eine Eingabe erzeugt hat.
Man kann nun mit Hilfe von Navigationskommandos seinen Datenbestand
Zeile für Zeile durchlaufen und dabei Manipulationen an den Daten
vornehmen. Es stehen folgende Kommandos zur Verfügung:
-
|
zur vorhergehenden, nächst
älteren Zeile springen
|
+
|
zur nachfolgenden, nächst
jüngeren Zeile springen
|
^
|
zur ersten, ältesten Zeile
springen
|
$
|
zur letzten, jüngsten Zeile
springen
|
RET
|
die letzte Navigation wiederholen
|
D[collection+]
|
eine oder mehrere
Teilausdrücke auf der aktuellen Zeile löschen, die
Teilausdrücke werden in Form der collection-Namen
angegeben,
die
durch
Leerzeichen
getrennt werden, werden keine collection-Namen
angegeben, wird
die gesamte Zeile gelöscht, Beispiele:
D
D a b c
|
expression
|
die aktuelle Zeile um den
angegeben Ausdruck erweitern, besitzt die Zeile bereits Datenwerte in collections, die auch in dem neuen
Ausdruck vorkommen, so erfolgt einer Aktualisierung, d.h. ein
Überschreiben dieser Datenwerte, anderenfalls ein Einfügen,
der Zeitstempel der Zeile kann nicht geändert werden
|
TAB
|
den Ausdruck der aktuellen Zeile
vollständig in die Eingabe übernehmen, dies ist nützlich
zur Korrektur ggf. längerer Kommentare, die man nicht in voller
Länge neu eintippen möchte
|
?
|
einen Hilfebildschirm ausgeben
|
q
|
die interaktive Betriebsart und
pdr beenden
|
Beispielsitzung:
$
pdr -i
pdr
0.3.7, interactive mode (press q to quit)
2010-03-03 12:38:01 6.2 1m
6n
>
-
2010-03-03 08:13:32 5.7 1m 6n
7l
>
Dm
2010-03-03 08:13:32 5.7 6n
7l
>
;
Kommentar
2010-03-03 08:13:32 5.7 6n 7l ;
Kommentar
> D ; l n
2010-03-03
08:13:32
5.7
>
D
2010-03-02 22:10:56 5.8
10l
>
12l
80.3k
2010-03-02 22:10:56 5.8
12l
80.3k
>
+
2010-03-03 12:38:01 6.2 1m
6n
>
q
$
3.1.9. XML-Export
pdr besitzt mit dem Kommandozeilenparameter -X die Möglichkeit, die
Daten seiner Datenbank in eine XML-Datei zu schreiben. Das Format
dieser XML-Datei entspricht dem aus Abschnitt
3.1.7.1. Damit hat der Benutzer ein Werkzeug zum Datenaustausch
zwischen verschiedenen Datenbanken. Eine Einschränkung des
Datenumfangs vor dem Export ist nicht vorgesehen. Die XML-Datei kann
aber nachträglich leicht bearbeitet werden.
3.2. pdx
3.2.1. Funktionsweise
pdx wertet Daten aus collections
aus und erstellt daraus ggf. unter Anwendung
statistischer Funktionen Berichte, Tabellen und/oder Diagramme.
Für die Berichte werden Textvorlagen
(templates)
benutzt, die
zunächst Platzhalter
enthalten, welche später von pdx aufgelöst und ersetzt
werden. Es ist somit
möglich, Berichte in praktisch jedem beliebigen (Text-)Format zu
erstellen, z.B. ASCII, XML, HTML, RTF, CSV, SQL usw. Mit etwas Aufwand
wäre sogar ein Backend für ODF möglich. Für
Diagramme
wird momentan nur das SVG-Format unterstützt. (Weitere sind
denkbar.) pdx arbeitet nach folgendem Schema:
Berichtsvorlagen
Berichte
(HTML,
XML,
TXT)
\
/
Datenbank -+-> pdx -+
/
\
Diagrammdefinitionen
Diagramme (SVG, PNG) |
Die erzeugten Ausgaben müssen einzeln konfiguriert
werden. Die wesentliche Vorarbeit für den erfolgreichen Betrieb
von pdx
ist die
Erstellung der Berichtsvorlagen und Diagrammdefinitionen, wozu ein
wenig
Theorie notwendig wird.
3.2.2. Eingebaute
Funktionen
pdx besitzt einen umfangreichen Satz an eingebauten Funktionen zur
Gewinnung von Daten aus der Datenbank
und ihrer statistischen
Auswertung. Die Arbeitsweise wird damit sehr flexibel programmierbar.
Die Syntax ist stark an die funktionale Programmiersprache
Lisp angelehnt.
Hinweis:
Es
handelt
sich
nicht
um
einen
echten
Lisp-Interpreter.
Insbesondere
fehlt
das
Merkmal
der
Neudefinition
von
Funktionen
völlig.
Die
Funktionsabarbeitung
erfolgt
allerdings
wie
bei
Lisp
streng
funktional.
Der
Grund
für
die
Lisp-ähnliche Syntax liegt in der genial einfachen Struktur
dieser Anweisungen, die man augenblicklich durch Hinschauen versteht,
ohne erst komplizierte syntaktische Konstruktionen begreifen zu
müssen.
Hinweis:
Eine
Liste
aller
eingebauten
Funktionen
kann
im
interaktiven
Betrieb
mit
? angezeigt werden.
Die meisten
dieser Funktionen können im interaktiven Betrieb auch unmittelbar
ausprobiert
werden.
Die folgenden Abschnitte dokumentieren alle eingebauten Funktionen mit
einer einheitlichen Syntax:
(
Funktionsname Funktionsparameter* ) -> Ergebnistyp
Diese Zeile besagt folgendes:
- jeder Funktionsaufruf beginnt und endet mit einer runden Klammer
- jede Funktion hat einen Namen
- nach dem Namen folgen 0 oder beliebig viele Parameter
- jede Funktion hat einen Ergebistyp
Der Funktionsname allein
muß nicht eindeutig, er kann
überladen (overloaded)
sein. Eindeutigkeit muß dann anhand der angegebenen Parameter
(-anzahl und -typen) hergestellt werden.
Einige Funktionen haben eine offene
Parameterliste: ...
(ellipse). D.h. daß
weder die
Anzahl noch die Typen der Parameter festliegen. Diese Funktionen
können mit beliebigen Parametern aufgerufen werden.
Alle hier verwendeten Funktionen sind streng(stens)
typisiert. Der
Superlativ bezieht sich darauf, daß nicht einmal Typumwandlungen (type casts) existieren. Wenn also {int} dasteht, ist auch
ausschließlich {int}
gemeint, ein {double}-Wert
führt
zu
einem
Fehler.
Typen
werden
im folgenden zur Unterscheidung von Werten in
geschweiften
Klammern
geschrieben.
Typen tauchen in Funtionsdefinitionen auf, Werte bei Funktionsaufrufen.
Es
sind
folgende
Typen
möglich:
{int},
{double} |
vorzeichenbehaftete Zahlen |
5, 3.14
|
{string} |
Zeichenketten beliebiger
Länge
|
"Hugo"
|
{time} |
meist eine Zeitdauer, selten
tatsächlich eine
Uhrzeit |
09:13
|
{timestamp} |
ein konkreter Zeitpunkt mit
Datum und Uhrzeit |
2009-12-31 7:30:01
|
{selection} |
eine Menge von
Zeitstempel-Wert-Paaren |
|
{color} |
eine RGB-Farbe in hexadezimaler
Schreibweise
|
#00FF00
|
{nothing} |
nur für
Funktionsrückgabetypen: das Ergebnis
der Funktion ist leer und kann nicht weiter
ausgewertet werden |
|
3.2.2.1. Datums-
und Zeitfunktionen
Wo immer ein Wert vom Typ {time}
benötigt wird, kann eine der folgenden Funktionen benutzt werden:
(year)
->
{time}
(year {int})
-> {time}
(years {int})
-> {time}
(month)
->
{time}
(month {int}) ->
{time}
(months {int}) ->
{time}
(week)
->
{time}
(week {int})
-> {time}
(weeks {int})
-> {time}
(day)
->
{time}
(day
{int}) -> {time}
(days {int})
-> {time}
(hour)
->
{time}
(hour {int})
-> {time}
(hours {int}) ->
{time}
(minute)
->
{time}
(minute {int}) ->
{time}
(minutes {int}) -> {time}
(second)
->
{time}
(second {int}) ->
{time}
(seconds {int}) -> {time}
Diese Funktionen berechnen eine Zeitdauer mit klar definierter
Länge. Die
Namen sind selbsterklärend. Das {int}-Argument ist einfach ein
Faktor, d.h. die Anzahl solcher Zeiteinheiten. Fehlt es, so gilt
implizit der Wert 1.
Hinweis:
Ein Jahr und ein Monat sind in der EDV sehr problematisch, weil sie in
der Realität keine festen Längen haben. Für pdx sind die
Angaben (year) und (month) per definitionem identisch mit (days 365) bzw. (days 30).
Die Funktion now gibt
das aktuelle Datum und die aktuelle Uhrzeit zurück:
(now)
->
{timestamp}
Hinweis:
der Wert entspricht
bei Verwendung des
Kommandozeilenparameters -f
dem Zeitpunkt des Programmstarts, auch wenn seitdem aufgrund
längerer Berechnungen bereits ein paar
Sekunden vergangen sein mögen. Alle
now-Aufrufe liefern bei
Verwendung von
-f
denselben Zeitstempel.
Hinweis: der von der Funktion
now zurückgegebene Wert
kann mit dem
Kommandozeilenparameter -n explizit auf eine
konkrete Angabe gesetzt werden. Damit lassen sich bei geschickt
gestalteten Berichtsvorlagen und Diagrammdefinitionen Berichte und
Diagramme für beliebige Zeitpunkte in der Vergangenheit erstellen.
Beispiele siehe nächster Abschnitt.
Mit Hilfe der Funktionen + und - lassen sich auch relative Zeitpunkte
berechnen. Die Funktionen haben folgende Signaturen:
(+
{timestamp}
{time})
->
{timestamp}
(- {timestamp}
{time}) -> {timestamp}
Meist wird man für den ersten Parameter einen Aufruf von now verwenden, um einen
Zeitpunkt zu berechnen, der von jetzt aus gesehen eine Woche oder einen
Monat zurückliegt. Diese Funktionen sind gedacht für den
Einsatz im Zusammenhang mit folding.
3.2.2.2.
Funktionen zur
Datengewinnung
Die folgenden Funktionen holen einen Ausschnitt aus genau einer collection
und geben diesen als {selection}
zurück. Der {string}-Parameter
benennt
jeweils
die
collection, mit
den übrigen Parametern kann die Ergebnismenge zeitlich
eingeschränkt werden:
(select
{string})
->
{selection}
(select {string}
{timestamp})
->
{selection}
(select {string} {timestamp}
{timestamp}) -> {selection}
(select {string}
{time})
->
{selection}
(select {string} {time}
{timestamp}) -> {selection}
Die erste Implementierung holt einfach uneingeschränkt alle Daten
der angegebenen collection.
Das kann
ggf. lange dauern. Die zweite holt alle Daten ab dem angegebenen
Zeitpunkt,
die dritte alle Daten zwischen den beiden angegebenen Zeitpunkten,
wobei der zweite Zeitpunkt, das Ende, nicht mit zur Ergebnismenge
gehört. Die
vierte Implementierung holt alle Daten in der angegebenen Zeitdauer bis
jetzt, d.h. dem Zeitpunkt, den ein Aufruf von now liefern würde. Die
fünfte holt alle Daten in der angegebenen Zeitdauer bis zum
angegebenen Zeitpunkt.
Beispiele:
(select
"*")
alle Daten der default collection holen
(select "*" 2009-12-01-12:34)
Daten der default collection seit dem
01.12.2009 12:34 holen, Hinweis
beachten!
(select "n" 2009-01-01-0:00
2010-01-01-0:00)
alle Daten der collection n des Jahres 2009 holen
(select "l" (weeks 2))
alle Daten der collection l der letzten zwei Wochen holen
(select
"l"
(months
3)
2009-06-01-0:00)
alle Daten der collection l aus den drei Monaten vor dem
01.06.2009 holen
Hinweis:
In einigen Fällen
werden hier Zeitstempel als Parameter
verwendet. Zeitstempel besitzen eigentlich die Syntax CCYY-MM-DD hh:mm[:ss], haben
also eigentlich ein Leerzeichen in der Mitte. Damit hier beim
Funktionsaufruf klar ist, daß es sich bei diesen Zeitstempeln um einen und nicht um zwei Parameter
handelt, ist
hier ein - (Minuszeichen)
als Bindeglied
zwischen Datum und Uhrzeit erforderlich.
Mit Hilfe der Funktion merge
lassen sich Selektionen vereinigen. Das Ergebnis ist eine einzige,
reguläre Selektion:
(merge
keyword
...)
->
{selection}
mit keyword = avg, min, max, sum, first oder last
Die Funktion erwartet beim Aufruf anstelle der Ellipse Parameter von
Typ {selection}. Diese werden anhand ihrer Zeitstempel in eine einzige
Selektion sortiert. Dabei kann der Fall auftreten, dass zwei
Selektionen je einen Wert für ein und denselben Zeitstempel
besitzen. In diesem Fall erlangt keyword
Bedeutung: es kennzeichnet die Operation, die dann auf die beiden Werte
angewandt wird, um einen neuen Wert zu berechnen. avg berechtet den Durchschnitt
beider Werte, min nimmt
den kleineren, max den
grösseren, sum
addiert beide Werte, first
nimmt den der weiter links stehenden, last den der weiter rechts
stehenden Selektion. Einige Operationen ergeben nur bei numerischen
Daten einen Sinn. Alle beteiligten Selektionen müssen gleichen
Typs sein. Es können auf diese Weise auch problemlos drei oder
mehr Selektionen gemischt werden. Beispiel:
selection
a
selection
b
(merge
avg
(select
"a")
(select
"b"))
--------------------
--------------------
-------------------------------------
2009-11-17 12:38 9.3
2009-11-17 12:38 9.3
2009-12-01 13:01
5.2
2009-12-01
13:01 5.2
2009-12-02 13:02 5.7
2009-12-02 13:02 5.7
2009-12-03 13:03 3.2
2009-12-03 13:03 3.2
2009-12-03 19:17
8.4 2009-12-03
19:17
8.4
2009-12-04 13:04
4.8
2009-12-04
13:04 4.8
2009-12-05 13:05 5.7 2009-12-05
13:05 4.7 2009-12-05 13:05 5.2 <- avg!
2009-12-06 13:06 5.3
2009-12-06
13:06 5.3
Die Funktion fold
erlaubt
das "Falten" der Zeitachse einer Selektion. Man stelle sie sich auf
einem Streifen Papier vor und falte diesen in Gedanken ein paar Mal, so
daß Zeitabschnitte übereinanderliegen. Auf diese Weise wird
es möglich, beispielsweise Tage oder Monate miteinander zu
vergleichen. Die Funktion fold
besitzt folgenden Prototyp:
(fold
keyword1
keyword2
{selection})
->
{selection}
mit keyword1 = year, month, day, hour oder minute
und keyword2 = avg, min, max, sum, first oder last
keyword1 steuert das
Interval, anhand dessen die Faltung erfolgen soll. Wird beispielsweise
mit der Angabe day
gefaltet, so werden aus den Zeitstempeln der Selektion alle
Bestandteile bis hin zum Tag entfernt, d.h. es bleibt nur noch die
Uhrzeit übrig. Alle Werte liegen dann auf einer Zeitachse, die nur
noch 24 Stunden umfaßt. Beispiel:
selection
a
(fold
day
avg
(select
"a"))
--------------------
---------------------------
2009-12-01 13:01
5.2 9999-01-01
13:01 5.45 <- avg!
2009-12-02 13:02 5.7 9999-01-01 13:02 5.7
2009-12-03 13:03 3.2 9999-01-01 13:03 3.2
2009-12-04 13:04
4.8 9999-01-01
13:04 4.8
2009-12-05 13:01 5.7
2009-12-06 13:06 5.3
9999-01-01 13:06 5.3
Der Sinn dieser Funktion liegt darin, beispielsweise den Verlauf einer
durchschnittlichen Tageskurve zu ermitteln, jedoch auf Basis mehrer
Tage mit dann sehr vielen Meßwerten.
Hinweis:
auch
die
Selektion
im
Ergebnis
einer
fold-Operation
muß
gültige
Zeitstempel
enthalten.
Es kann jedoch bei der Faltung
eines Zeitbereichs kein absoluter Zeitstempel mehr ausgemacht werden,
mehrere liegen ja übereinander. Aus diesem Grund tragen die
Zeitstempel das Jahr 9999. Entsprechend dem benutzten Interval sind
zudem auch weitere Bestandteile dieser Zeitstempel (Monat, Tag usw.)
zwar syntaktisch gültig, aber inhaltlich nicht sinnvoll.
3.2.2.3.
Statistische Funktionen
Alle Funktionen dieses Abschnitts führen auf einer Selektion eine
statistische Zusammenfassung durch und geben grundsätzlich wieder
eine Selektion
zurück, in der jede Zeile einen zusammengefaßten Wert
enthält. Auf diese Weise ist es möglich, beispielsweise
über einen Monat den jeweiligen Tagesdurchschnitt o.ä.
auszurechnen.
Das Parameterschema ist bei allen statistischen Funktionen identisch:
- Die Implementierung (a) wendet die statistische Funktion auf die
gesamte Selektion an. Das Ergebnis ist eine einzelne Zeile.
- Die Implementierung (b) beschränkt die Daten der Selektion
auf die tägliche Zeit zwischen den beiden angegebenen Zeiten. Das
Ergebnis ist ebenfalls eine einzelne Zeile.
- Alle weiteren Implementationen verwenden ein Schlüsselwort (keyword),
das ein Aggregationsintervall
angibt. Dieses Schlüsselwort kann
lauten: year, month, day, hour, minute, second. Das Ergebnis ist dann
je eine Zeile pro Intervall. Verwendet man beispielsweise day, so erhält man also
eine Zeile pro in der ursprünglichen Selektion enthaltenem Tag.
- Die Implementierung (c) wendet die statistische Funktion auf
uneingeschränkt alle Daten im
Aggregationsintervall an.
- Die Implementierung (d) tut genau das gleiche, erlaubt aber im
speziellen Falle der Verwendung von day
noch die Angabe einer Uhrzeit, die den Tageswechsel kennzechnet. Es ist
ggf. (vor allem bei medizinischen Daten) nützlich, den
Tageswechsel nicht um
Mitternacht, sondern vielleicht erst um 2:00 stattfinden zu lassen.
Werte mit dem Zeitstempel 1:37 zählen dann noch zum vorherigen Tag.
- Die Implementierung (e) schränkt die Anwendung der
statistischen Funktion wieder nur auf Werte zwischen den beiden
angegebenen Uhrzeiten ein.
- Die Funktionen avg
und sdv besitzen noch je
eine sechste Implementierung (f), die gleitende
Berechnungen erlaubt.
Dabei wird Zeile für Zeile ein Fenster über die Selektion
geschoben, innerhalb dessen die statistische Berechnung erfolgt. Man
kann hier beispielsweise 5
und 5 angeben, um eine
Durchschnittberechnung über die jeweils fünf vorhergehenden,
den aktuellen und die fünf nachfolgenden Werte durchzuführen.
Die folgenden Funktionen berechnen den arithmetischen Durchschnitt (average):
(avg
{selection})
->
{selection}
(a)
(avg
{selection}
{time}
{time})
->
{selection}
(b)
(avg {selection}
keyword)
->
{selection}
(c)
(avg {selection} keyword
{time}) ->
{selection} (d)
(avg {selection} keyword {time}
{time}) -> {selection} (e)
(avg {selection} {int}
{int})
-> {selection} (f)
Standardabweichung (standard deviation):
(sdv
{selection})
->
{selection}
(sdv {selection} {time}
{time})
-> {selection}
(sdv {selection}
keyword)
->
{selection}
(sdv {selection} keyword
{time}) ->
{selection}
(sdv {selection} keyword {time}
{time}) -> {selection}
(sdv {selection} {int}
{int})
-> {selection}
Anzahl:
(count
{selection})
->
{selection}
(count {selection} {time}
{time}) ->
{selection}
(count {selection}
keyword)
-> {selection}
(count {selection} keyword
{time}) -> {selection}
(count {selection} keyword {time}
{time}) -> {selection}
Die count-Funktion
liefert immer eine Zeile mit einem {double}-Wert
im
Ergebnis,
unabhängig
vom
Typ
der Selektion.
Das arithmetische Maximum und Minimum:
(max
{selection})
->
{selection}
(max {selection} {time}
{time})
->
{selection}
(max {selection}
keyword)
->
{selection}
(max {selection} keyword
{time})
-> {selection}
(max {selection} keyword {time}
{time}) -> {selection}
(min
{selection})
->
{selection}
(min
{selection}
{time}
{time})
->
{selection}
(min {selection} keyword)
->
{selection}
(min {selection} keyword
{time})
-> {selection}
(min {selection} keyword {time}
{time}) -> {selection}
Die Funktionen max und min liefern neben dem Wert, der
als Maximum oder Minimum erkannt wurde, auch dessen Zeitstempel mit.
Die Summe:
(sum
{selection})
->
{selection}
(sum {selection} {time} {time})
-> {selection}
(sum {selection} keyword)
->
{selection}
(sum {selection} keyword {time})
-> {selection}
(sum {selection} keyword {time}
{time}) -> {selection}
Die jeweils erste oder letzte, d.h. älteste oder
jüngste
Zeile einer Selektion:
(first
{selection})
->
{selection}
(first {selection} {time}
{time})
->
{selection}
(first {selection}
keyword)
->
{selection}
(first {selection} keyword
{time})
-> {selection}
(first {selection} keyword {time}
{time}) -> {selection}
(last
{selection})
->
{selection}
(last {selection} {time}
{time})
->
{selection}
(last {selection}
keyword)
->
{selection}
(last {selection} keyword
{time})
-> {selection}
(last {selection} keyword {time}
{time}) -> {selection}
Die Funktionen first und last liefern neben dem Wert,
der als ältester oder jüngster erkannt wurde, auch dessen
Zeitstempel mit.
Hinweis:
die meisten dieser
Funtionen sind nur auf numerischen
Selektionen erlaubt, d.h. solche deren Werte numerischen Typs sind ({double}). count, first und last
funktionieren immer. sum
funktioniert auch auf Selektionen mit Zeichenketten, beispielsweise bei
Kommentaren.
Diese werden dann verkettet. avg
funktioniert auch auf Selektionen mit Ratio-Werten, wobei die
Durchschnittsberechnung
bei Zähler und Nenner getrennt durchgeführt wird.
Beispiele:
(avg
(select
"*"))
den Durchschnitt über alle Werte
der default collection bilden
(max (select "*") day 2:00)
das tägliche Maximum der default collection ermitteln,
annehmen, daß der Tageswechsel um 2:00 erfolgt
(sum
(select
"n"
3:30
9:00)
day)
die tägliche Summe über Werte
der collection n bilden, aber nur Werte
zwischen 3:30 und 9:00 berücksichtigen
(avg
(select
"l"
(month))
5
5)
den gleitenden Durchschnitt über
je 11 Werte der collection l des letzten Monats bilden
(first
(select
"*"
(month)) day 2:00)
die jeweils erste Zeile eines jeden
Tages des
letzten Monats aus der default
collection holen
(last
(select
"*"
(day)) hour)
die jeweils letzte Zeile einer Stunde
des letzten Tages aus der default
collection holen
3.2.2.4.
Arithmetische Funktionen
pdx besitzt einen bescheidenen Satz von arithmetischen Funktionen,
nämlich für die vier Grundrechenarten. Diese existieren
jeweils in drei verschiedenen Implementierungen:
(X
{double} {double}) ->
{selection} mit X = +, -, * oder /
(X {selection} {double}) -> {selection}
(X {selection} {selection}) -> {selection}
Die erste Form wendet die jeweilige Rechenoperation schlicht auf die
beiden numerischen Operanden an. Das Ergebnis enthält genau eine
Zeile. Die zweite Implementierung vollzieht die Operation mit jeder
Zeile des {selection}-Parameters
und
dem
{double}-Parameter.
Das Ergebnis enthält folglich genauso viele Zeilen und dieselben
Zeitstempel. Diese beiden Implementierungen sind vor allem zur
Konvertierung von Maßeinheiten gedacht.
Interessant ist die dritte Implementierung. Diese erlaubt die
zeilenweise arithmetische
Verknüpfung zweier Selektionen. Dabei
werden ihre Zeitstempel verglichen. Die Anzahlen der Zeilen in beiden
Selektionen müssen allerdings nicht gleich sein. Wenn in der
zweiten Selektion keine gleich alte Zeile wie in der ersten Selektion
vorhanden ist, wird die nächst ältere genommen. Das Ergebnis
hat so viele Zeilen wie der erste {selection}-Parameter:
selection
a
selection
b
(*
(select
"a")
(select
"b"))
--------------------
--------------------
-----------------------------
2009-11-17 12:38 9.3
2009-12-01 13:00
5.2
-> 5.2 * 9.3 =
2009-12-01
13:00 48.36
2009-12-02 13:00 5.7
->
5.7 * 9.3 = 2009-12-02 13:00 53.01
2009-12-03 13:00 3.2
->
3.2 * 9.3 = 2009-12-03 13:00 18.24
2009-12-03 19:17 8.4
2009-12-04 13:00
4.8
-> 4.8 * 8.4 =
2009-12-04
13:00 40.32
2009-12-05 13:00 5.7 2009-12-05
13:00 4.7 -> 5.7 * 4.7 =
2009-12-05 13:00 26.79
2009-12-06 13:00 5.3
-> 5.3 * 4.7 = 2009-12-06 13:00 30.21
Die Zeitstempel des Ergebnisses entsprechen ebenfalls dem ersten {selection}-Parameter. Wie man
jetzt leicht erkennt, ist wesentlich, daß auch für die erste
Zeile im ersten {selection}-Parameter
eine
brauchbare,
d.h.
gleich
alte
oder
ältere
Zeile
im
zweiten
{selection}-Parameter
erforderlich
ist.
pdx
gibt
einen
Fehler aus, wenn diese Bedingung nicht
erfüllt ist. - Nützlich ist diese Implementierung
insbesondere dann, wenn man zwei Selektionen hat, die Zähler und
Nenner eines Quotienten darstellen, wenn man also Werte hat, die auf
irgendeine Basis bezogen sind, d.h. spezifische Werte, z.B. ein
Benzinverbrauch pro 100km.
3.2.2.5. Funktionen
für Berichte
Die Funktionen dieses Abschnitts sind notwendig für die Erstellung
von Berichten. Sie geben jeweils eine Zeichenkette zurück, oft
einen umfangreichen Block Text. pdx liest die Textvorlage des Berichts,
trifft
darin auf eine format-Anweisung,
wertet
diese
augenblicklich
aus
und
ersetzt
sie
an
derselben
Position
im
Text
mit
deren
Ergebnis.
Auch
diese
Funktionen
können
interaktiv
getestet
werden.
Die format-Funktion ist
trotz ihres simplen Prototyps vergleichsweise komplex:
(format
...)
->
{string}
Sie erwartet eine beliebig lange Liste von Parametern, die aus Text,
Funktionsergebnissen, Formatierungsanweisungen und Schlüsselworten
bestehen können. Das Ergebnis kann ein ein- oder mehrzeiliges
Stück Text beliebiger Länge sein. Am einfachsten ist die
Funktion wahrscheinlich
anhand von Beispielen zu verstehen.
Beispiel 1:
(format
(avg (select "*" (days
7))) <1.2>
)
Dieser Aufruf erzeugt einen einzelnen, formatierten Wert. Das erste
Argument der format-Funktion
ist
hier
das
Ergebnis
einer
statistischen
Berechnung
(avg-Funktion),
die
eine
einzeilige Selektion mit einem numerischen
Wert liefert. Danach folgt
eine Formatierungsanweisung in spitzen Klammern, die besagt: das
Ergebnis soll
mindestens eine Vor-
und in jedem Falle zwei Nachkommastellen haben.
Wesentlich schwieriger ist es zu verstehen, wenn unter den Parametern
mehrere Selektionen
auftauchen, die vielleicht auch noch mehrere und womöglich
unterschiedlich viele (!) Ergebniszeilen liefern. Aber darin liegt
gerade die Mächtigkeit der format-Funktion.
Beispiel 2:
(format
"<tr>"
"<td>" datetime
"</td>"
"<td>" (select "*" (days 7))
<1.1> "</td>"
"<td>" (select "n" (days 7))
<1> "</td>"
"<td>" (select "l" (days 7))
<1> "</td>"
"<td>" (select "m" (days 7))
<1.0> "</td>"
"<td>" (select "x" (days 7))
<1.1> "</td>"
"<td>" (select "#" (days 7))
"</td>"
"</tr>"
newline
)
Dieser Aufruf erzeugt HTML-Zeilen für eine HTML-Tabelle mit
sämtlichen
Daten der collections
*, n, l, m, x und # der jeweils letzten sieben
Tage. (Die
Tabellendefinition steht außerhalb, sie ist nicht Teil der
hiesigen Betrachtung.)
Man erkennt auf den ersten Blick folgendes:
- im Wesentlichen wechseln sich Zeichenketten
und Selektionen als Parameter ab
- alle
Selektionen haben denselben
Zeitbereich, das ist wichtig, denn sie werden intern anhand ihrer
Zeitstempel zu einer "mehrspaltigen" Selektion, d.h. einer
(unsichtbaren) Tabelle verknüpft
- hinter den
Selektionen sind jeweils in spitzen Klammern Formatanweisungen
platziert,
Werte der default collection
sollen
hier mindestens eine Vor und eine Nachkommastelle haben, Werte der
collections n und l nur ganze Zahlen, Werte der collection m nur dann Nachkommastellen,
wenn diese
verschieden von 0 ist
- die Lage der einen gemeinsamen, nach der Vereinigung aller
Selektionen
noch in der Tabelle verbleibenden
Zeitstempel-Spalte wird über das Schlüsselwort datetime angegeben, würde
man es hier nicht verwenden, würden in der Ausgabe keine
Zeitstempel auftauchen
- das newline-Schlüsselwort
am
Ende
besagt,
daß
nach
jeder
ausgegebenen
Zeile
ein
Zeilenwechsel
eingefügt
wird
Die format-Funktion
analysiert nun zunächst die Selektionen und bildet daraus eine
unsichtbare
Tabelle. Dann gibt sie Zeile für Zeile dieser resultierenden
Tabelle aus, formatiert deren Werte und ordnet die Zeichenketten
zwischen den Spalten an.
Hinweis:
bei
derartigen
Konstrukten,
die
noch
weit
komplizierter
aussehen
können,
lohnt
es
sich,
sehr
sauber
zu
schreiben
und
viele
Leerzeichen
zu
verwenden,
um
Dinge
so
anzuordnen,
daß
man
die
Funktionsweise
erkennen kann.
Das Ergebnis eines solchen Funktionsaufrufs ist echtes HTML und sieht
so aus:
[...]
<tr><td>2009-01-17 18:58:13</td><td></td><td>6</td><td></td><td></td><td></td><td></td></tr>
<tr><td>2009-01-17 21:42:49</td><td>5.6</td><td></td><td>16</td><td></td><td></td><td></td></tr>
<tr><td>2009-01-18 05:54:41</td><td>6.8</td><td>7</td><td>8</td><td>1</td><td></td><td></td></tr>
<tr><td>2009-01-18 12:17:22</td><td>5.4</td><td>6</td><td></td><td>1</td><td></td><td></td></tr>
[...]
Die Anzahl der Zeilen wird allein durch die Selektionen bestimmt. Die
fett hervorgehobenen Werte stammen aus den Selektionen, alles andere
aus den Zeichenketten in der format-Funktion.
Enthält
eine
der
Selektionen
auf
einer
Zeile
keinen
Wert,
so
bleibt
das
Feld
leer,
d.h.
es
steht
dort
<td></td>,
was
übrigens
von
vielen
Browsern
oft
nicht
gut interpretiert wird,
besser wäre für leere Felder eine Angabe wie <td><br></td>.
Innerhalb einer format-Funktion
besteht
also
gelegentlich
das
Problem,
daß
man
in
der
Ausgabe
leere
Werte
kennzeichnen,
d.h.
sichtbar
machen
will
oder
muß.
Leere
Werte
entstehen
durch
die
Verknüpfung
mehrerer
Selektionen
zu
einer
mehrspaltigen
Tabelle
(outer
join). Mit Hilfe der empty-Funktion
(empty {string}) ->
{string}
kann man der umgebenden format-Funktion
mitteilen,
welche
Zeichenkette
anstelle
eines
leeren
Werts
in
die
Ausgabe
eingefügt
werden
soll.
Beispiel:
(format
(empty "nil")
[...]
)
Immer, wenn jetzt ein leerer Wert auftaucht, schreibt die format-Funktion die
Zeichenkette nil aus.
Die folgenden kleinen, parameterlosen Funktionen sind sehr einfach:
(build)
->
{string}
(version)
->
{string}
(database)
->
{string}
build gibt eine Zeichenkette mit pdx-Build-Informationen aus,
während version die
aktuelle Programmversion liefert. Diese Werte sind dafür gedacht,
in einen Bericht eingebaut zu werden, um anzuzeigen, von welcher
pdx-Version dieser stammt. database
zeigt die Version der aktuell geöffneten Datenbank an.
3.2.2.6.
Funktionen für Diagramme
Die folgenden Funktionen werden zur Erstellung
von
Diagrammen
verwendet. Sie geben nichts zurück, sie zeichnen ein Diagramm.
Dies ist auch der Grund dafür, warum sie nicht interaktiv getestet
werden können.
Die diagram-Funktion
dient als Container. Alle anderen Diagrammfunktionen dürfen nur
als Parameter der diagram-Funktion
aufgerufen
werden.
Wie
zu
erwarten
hat
sie
deshalb
eine
offene
Parameterliste:
(diagram
{int}
{int}
{color}
...)
->
{nothing}
Die beiden {int}-Parameter
sind
Breite
und
Höhe
des
Diagramms
in
Pixeln.
Hinweis:
diese Zahlen
beinhalten nicht die Beschriftung der Achsen,
sondern bezeichnen quasi das Achseninnere. Das erzeugte Diagramm ist um
diese Beschriftung größer. Dies hat interne Gründe, die
mit der nicht ganz unproblematischen Erzeugung einer Beschriftung in
SVG zusammenhängen.
Es folgt als dritter, fester Parameter die
Hintergrundfarbe.
Beispiel:
(diagram 400
300 #FDFDFD
[...]
)
Die axes-Funktion
zeichnet jeweils ein Koordinatensystem
(1.Quadrant):
(axes
{timestamp}
{timestamp}
{double}
{double}
{double}
{color})
->
{nothing}
(axes
{time}
{timestamp}
{double}
{double}
{double}
{color})
->
{nothing}
(axes
{time}
{double}
{double}
{double}
{color})
->
{nothing}
(axes
keyword
{double}
{double}
{double}
{color})
->
{nothing}
mit keyword = year, month, day, hour oder minute
Die erste Implementierung zeichnet die x-Achse vom ersten zum letzten
angegebenen Zeitstempel, die zweite über die angegebene Zeitdauer
vor dem angegebenen Zeitstempel, die dritte über die angegebene
Zeitdauer vor der aktuellen Zeit. Die vierte Implementierung ist
speziell für das Zeichnen von Daten, die aus einem Aufruf der
Funktion fold
stammen. Dabei ist dasselbe Interval anzugeben. Die Achse berechnet
daraus alles Nötige. Die Schrittweite der Beschriftung der
x-Achse wird automatisch festgelegt. Die folgenden drei {double}-Werte
sind Untergrenze, Obergrenze und Schrittweite der y-Achse. Der
{color}-Parameter benennt
die Farbe der Achsen und der Achsenbeschriftung.
Beispiel:
(axes
2009-08-01-0:00 2009-09-01-0:00 2.0 10.0
1.0
#000000)
(axes (months
3)
7.0
15.0
0.5
#101010)
Mit Hilfe der hline-Funktion
lassen
sich
sehr
nützliche,
horizontale
Linien
in
das
Diagramm
einfügen:
(hline
{double}
{color})
->
{nothing}
(hline {double} {double} {color})
-> {nothing}
Der erste {double}-Parameter
bezeichnet
die
Lage
der
Linie,
also
einen
y-Wert,
der
optionale
zweite
gibt
die
Strichdicke
an.
Die
Farbe
der
Linie
steht
im
{color}-Parameter.
Beispiel:
(hline
7.0
0.25
#101010)
Für vertikale Linien steht die vline-Funktion zur
Verfügung. Diese existiert in vier verschiedenen Varianten:
(vline
{timestamp}
{color})
(vline {timestamp}
{double} {color})
(vline {time}
{color})
(vline {time}
{double} {color})
Die ersten beiden Implementierungen benutzen einen absoluten
Zeitstempel, die letzten beiden einen relativen, d.h. wiederkehrenden
Zeitpunkt für gefaltete Zeitachsen. Der {double}-Parameter bezeichnet
die Strichdicke, der {color}-Parameter
die
Farbe
der
Linie.
Beispiel:
(vline
5:45
0.25
#101010)
gefaltete
Zeitachse!
Die letzte und wesentlichste Diagramm-Funktion ist die curve-Funktion. Mit ihr lassen
sich Kurven in verschiedenen
Stilen darstellen. Grundlage ist jeweils
eine Selektion:
(curve
{selection}
{color}
...)
->
{nothing}
Ohne weitere Parameter zeichnet die curve-Funktion eine
Zickzacklinie
in der angegebenen Farbe durch Verbinden der Datenwerte, die in der
Selektion übergeben werden. Durch zusätzliche Parameter kann
das Verhalten erheblich beeinflußt werden:
- Das Schlüsselwort bars
erzeugt vertikale Balken anstatt einer Zickzacklinie.
- Eine Zeichenkette der Form "+",
"|", "-", "x" oder "°" erzeugt keine Linie,
sondern einzelne, nicht verbundene Punkte, die durch das jeweilige
Symbol gekennzeichnet werden.
- Eine {double}-Zahl
ermöglicht die Angabe einer Strichdicke (nur bei
Zickzacklinie sinnvoll).
Bei Balkendarstellung können mit Hilfe von zwei {int}-Zahlen mehrere Balken pro
Aggregationsintervall dargestellt werden. Dies klingt schwierig, ist
aber anhand eines Beispiels leicht zu verstehen. Angenommen, wir haben
Werte an vier verschiedenen Tageszeiten, sagen wir morgens, mittags,
abends und spät. Und wir wollen jeden Tag durch vier
nebeneinanderstehende Balken
darstellen, die für diese Tageszeiten stehen. Dann wäre es
erforderlich, die Balken so zu
platzieren, daß sie sich nicht überdecken:
(curve
(sum
(select
"n"
(month
1))
day
3:30
9:30)
#FF1000
bars
1
4)
(curve (sum (select
"n" (month 1)) day 11:00 14:30)
#FF5000 bars 2 4)
(curve (sum (select
"n" (month 1)) day 17:30 20:30)
#FF9000 bars 3 4)
(curve (sum (select
"n" (month 1)) day 21:00 2:00)
#FFB000 bars 4 4)
Diese vier Zeilen unterscheiden sich in den Selektionen, den
Balkenfarben und dem ersten {int}-Parameter.
Dieser
benennt
die
Nummer
des
aktuellen
Balkens,
der
zweite
sagt,
wieviele
Balken
es
überhaupt
gibt.
Die
erste
Zeile
zeichnet
also
den
ersten
von
vier
Balken.
pdx
berechnet
dann,
wie
breit
die
Balken
gezeichnet
werden
müssen,
damit
sie
sauber
nebeneinander
kommen.
Im
Beispiel
hat
also
ein
Balken jeweils die Breite eines Viertels der Breite eines
Tages auf der x-Achse. Man kann damit etwas experimentieren. Man
muß nicht jeden Balken zeichnen, d.h. man könnte damit auch
Lücken erzeugen, Balken gruppieren usw.
3.2.2.7. Sonstige
Funktionen
pdx besitzt genau eine Funktion, die in kein Schema paßt und die
ausnahmsweise auch spezifisch für Diabetiker ist: die HbA1c-Funktion:
(HbA1c
{string})
->
{selection}
(HbA1c {string} {timestamp})
-> {selection}
(HbA1c {string} {timestamp}
{timestamp}) -> {selection}
(HbA1c {string} {time})
->
{selection}
(HbA1c {string} {time}
{timestamp}) -> {selection}
Diese Funktion berechnet den HbA1c-Wert
in
Prozent
durch
ein
Näherungsverfahren.
Man
benötigt
dazu
Blutzuckermeßwerte
über
einen
Zeitraum
von
mindestens
drei
Monaten,
d.h.
um
eine
Kurve
über
einen
Monat
zu
zeichnen
also
Meßdaten
von
mindestens
vier
Monaten.
Der
Absolutwert
ist
definitiv
nicht
sehr
genau,
aber
man
kann
am
Kurvenverlauf
Schwankungen
gut
erkennen.
Die
Parameter
entsprechen denen der select-Funktion.
Die erste Implementierung berechnet den Wert von heute, die zweite den
am angegebenen Datum.
Die HbA1c-Funktionen
existieren jeweils noch in einer zweiten Implementierung:
(HbA1c2
{string})
->
{selection}
(HbA1c2 {string} {timestamp})
-> {selection}
(HbA1c2 {string} {timestamp}
{timestamp}) -> {selection}
(HbA1c2 {string} {time})
->
{selection}
(HbA1c2 {string} {time}
{timestamp}) -> {selection}
Diese Implementierungen geben jüngeren Meßwerten ein
höheres Gewicht als älteren, in der Annahme, daß dies
dem natürlichen Verhalten näher kommt. Die Kurve schwankt
etwas stärker als die der obigen Implementierungen.
3.2.3. Interaktiver
Betrieb
pdx besitzt eine interaktive Betriebsart. Diese interaktive Betriebsart
erweist sich als sehr
nützlich beim Testen von Funktionsaufrufen,
bevor
man
sie
in
einer
Berichtsvorlage
oder
einer
Diagrammdefinition
niederschreibt.
Man
kann
zudem
sehr schnell kurze Abfragen ausführen, z.B. Wieviel
Werte gibt es überhaupt in collection
x? oder Was war das
Allzeit-Maximum? Die interaktive Betriebsart wird über den
Kommandozeilenparameter -i
veranlaßt. pdx gibt dann ein Promptsymbol aus und wartet auf
Eingaben:
$ pdx -i
pdx 0.3.1 (2010-01-03 16:43:29 on castor, GNU/Linux 2.6.32-ARCH x86_64)
>
An diesem Prompt gibt es zwei Befehle, ? und q, jede andere Eingabe wird als
Funktionsaufruf interpretiert.
Der Befehl ? listet
Implementierungen eingebauter
Funktionen auf. Ohne weiteren Parameter zeigt ? alle eingebauten Funktionen
mit ihren Parametertypen und ihrem jeweiligen Rückgabetyp an. ? akzeptiert als Parameter noch
einen regulären Ausdruck,
mit
dessen
Hilfe
die
Ergebnismenge
eingeschränkt
werden
kann:
>
?min
alle Implementierungen der min-Funktion anzeigen
> ?a.* alle mit a beginnenden Funktionen
anzeigen
> ?m.. Funktionen
anzeigen, die mit m
beginnen und genau zwei weitere Zeichen haben
Der Befehl q beendet den
interaktiven Betrieb und damit auch pdx. Der interaktive Betrieb kann
außerdem mit Ctrl-D
oder Ctrl-C beendet
werden.
Die Ausführung von Funktionen erfolgt unmittelbar. Dabei wird das
Ergebnis dargestellt. Ein Aufruf wie
>
(select
"*")
zeigt unmittelbar alle momentan verfügbaren Werte der default collection an.
3.2.4. Alles
zusammen: die Erzeugung von Berichten und Diagrammen
Berichte entstehen aus Textvorlagen.
pdx
sucht
innerhalb
einer
solchen
Vorlage
nach
einem
gekennzeichneten
Abschnitt
mit
Funktionsaufrufen,
meist
einer
format-Funktion.
Dieser
Abschnitt wird dann aus der Vorlage herausgeschnitten, ausgewertet und
durch das
Ergebnis ersetzt. In einer Vorlage können
beliebig viele Abschnitte mit Funktionsaufrufen angeordnet werden.
Textvorlagen sind entweder
einfacher ASCII-Text oder Text in einer
Formatierungssprache wie HTML oder XML bzw. einer Programmiersprache
wie C oder SQL, im Sinne dieser Abhandlung eine Wirtssprache. Worum es
sich dabei konkret handelt, soll absichtlich in keiner
Weise eingeschränkt werden. pdx muß aber wissen, woran die
Abschnitte mit den Funktionsaufrufen zu
erkennen sind. Deshalb werden solche Abschnitte generell in Kommentaren
der jeweiligen Wirtssprache angeordnet, in HTML oder XML also zwischen <!-- und -->, in C hingegen zwischen /* und */. Dabei bleibt die Vorlage
eine unvollständige, aber korrekte Datei ihres Typs, was den
Vorteil hat, daß man sie nach wie vor mit Werkzeugen (z.B.
HTML-Editoren) bearbeiten kann. Es ist klug, diese
pdx-Kommentarblöcke noch weiter zu kennzeichnen, um sie von
bereits existierenden, echten Kommentaren zu unterscheiden. Man
verwende also klugerweise z.B. <!---
und ---> oder /** und **/. Eine vollständige,
kleine Vorlage für eine HTML-Datei mit pdx-Anweisungen würde
also beispielsweise
wie folgt aussehen:
<!DOCTYPE
HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="de-ch">
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html;
charset=iso-8859-1">
<title>MyTitle</title>
</head>
<body style="direction: ltr;" lang="de-DE">
<!--- (now) --->,
<b>pdx</b> <!---
(version) ---> (<!---
(build) --->)
*
</body>
</html>
In der mit * markierten
Zeile stehen drei pdx-Abschnitte, in denen je eine der Funktionen now, version und build aufgerufen wird. Die
Ausgabe, die diese Zeile erzeugt, sieht ungefähr so aus:
2009-12-27
15:14:51,
pdx 0.3.0 (2009-12-27 10:28:14 on castor,
GNU/Linux 2.6.31-ARCH x86_64)
Man sieht, daß alle Bestandteile der Zeile, die nicht innerhalb
pdx-Abschnitten stehen, (sowie auch der gesamte umgebende Text)
unverändert in die Ausgabe übernommen werden. Diese Zeile
hätte übrigens unter Benutzung der format-Funktion
auch
anders formuliert werden können:
<!---
(format
(now)
",
<b>pdx</b>
"
(version)
"
("
(build)
")")
--->
Um nun in HTML eine komplette Tabelle anzuordnen, würde man
beispielsweise wie folgt vorgehen:
[...]
<table style="page-break-before: always; page-break-inside: avoid;
width: 800px;"
border="1" cellpadding="1" cellspacing="1">
<tbody>
<tr
valign="top">
<td>Datum/Zeit</td>
<td>*</td>
<td>n</td>
<td>l</td>
<td>m</td>
<td>x</td>
<td>Kommentar</td>
</tr>
<!---
(format
(empty
"<br>")
"<tr
valign=top>"
"<td>"
datetime
"</td>"
"<td>"
(select
"*"
(days
7))
<1.1>
"</td>"
"<td>"
(select
"n"
(days
7))
<1>
"</td>"
"<td>"
(select
"l"
(days
7))
<1>
"</td>"
"<td>"
(select
"m"
(days
7))
<1.0>
"</td>"
"<td>"
(select
"x"
(days
7))
<1.1>
"</td>"
"<td>"
(select
"#"
(days
7))
"</td>"
"</tr>"
newline
)
--->
</tbody>
</table>
[...]
Die Tabelle beginnt außerhalb des pdx-Abschnitts und endet
wiederum außerhalb. Die Zeilen der Tabelle mit Ausnahme der
Kopfzeile werden aber vollständig von pdx generiert. Diese Zeilen
besitzen alle das gleiche Aussehen.
Diagramme entstehen aus Diagrammdefinitionsdateien.
Diese
enthalten
immer
genau
eine
Diagrammdefinition,
d.h.
einen
Aufruf
der
diagram-Funktion. In
Diagrammdefinitionsdateien sind keine Kommentar-Abschnitte notwendig,
da es keine Wirtssprache gibt. Eine vollständige
Diagrammdefinition sieht beispielsweise so aus:
(diagram
400
300
#FFFDFD
(axes (month
1) 3.0 9.0 1.0 #0)
(hline 5.0
#C0C0C0)
(hline 6.0
#C0C0C0)
(hline 7.0
#C0C0C0)
(curve (sum
(select "*" (month 1)) day 3:30 9:30)
#FF0000)
(curve (sum
(select "*" (month 1)) day 11:00 14:30) #00FF00)
(curve (sum
(select "*" (month 1)) day 17:30 20:30) #0000FF)
(curve (sum
(select "*" (month 1)) day 21:00 2:00) #FFFF00)
(curve (avg
(select "*" (month 1)) day
2:00) #0 1.0)
)
Bei der Entwicklung neuer Berichtsvorlagen bzw. Diagrammdefinitionen
ist es klug, sich existierende Daten herzunehmen und leicht zu
modifizieren.
4. Aufruf
4.1. pdr
pdr akzeptiert Optionen und Argumente.
Hinweis:
Optionen können selbst Argumente haben, was nicht verwechselt
werden darf.
Optionen werden grundsätzlich durch ein Minuszeichen eingeleitet.
Ein zweites Minuszeichen kennzeichnet sog. lange Optionen.
4.1.1. Argumente
Alles, was hinter dem
Programmnamen
pdr auf der Kommandozeile
folgt und nicht mit einem Minuszeichen beginnt,
wird als Argument gewertet. Alle Argumente werden zu einem einzigen
Ausdruck zusammengefügt und gemeinsam
abgearbeitet:
$
pdr 5.2 5n 8l -v \; Kommentar bis zum
Ende der Zeile
Der resultierende Ausdruck lautet:
5.2 5n 8l ; Kommentar bis zum
Ende der Zeile
-v gehört nicht dazu,
dies ist eine erkennbare Option von pdr, sie wird nicht mit in den
Ausdruck übernommen. Der backslash
vor dem Semikolon ist eine Spezialität unter unixartigen
Betriebssystemen. Bei solchen wertet die shell
ihrerseits oft das Semikolon aus, was uns jedoch als
Kommentartrennzeichen dient. Um dies zu verhindern, muß man ihm
einen backslash voranstellen.
Dieser wird allerdings bereits von der jeweiligen shell entfernt und gelangt gar
nicht in die Eingabe von pdr.
4.1.2. Optionen
-?
|
obigen Hilfebilfschirm anzeigen
|
-V
|
die pdr-Versionsnummer anzeigen
|
-v
|
Ausgaben machen, ist diese
Option nicht gesetzt, macht pdr nur
dann Ausgaben, wenn Fehler
aufgetreten sind
|
-c
filename
|
die Datei filename als
Konfigurationsdatei benutzen, diese Option
hat Priorität
gegenüber der standardmäßig benutzten
Konfigurationsdatei ~/.pdrx
|
-l
|
die momentan verfügbaren collections
auflisten
|
-a
"name,type"
|
eine collection hinzufügen,
das Argument ist eine Zeichenkette,
die den Namen und den Typ der neuen collection
angibt, als
Typen
kommen in Frage n, r oder t (für numeric, ratio oder text)
|
-d name
|
eine collection löschen,
als Argument muß der Name übergeben
werden
|
-D
|
alle collections löschen,
die collections * und # werden nicht
gelöscht, sie werden nur
geleert
|
-r
|
alle aktuellen rejections
auflisten
|
-R
|
alle aktuellen rejections
löschen
|
-e "expr"
|
die als Argument übergebene
Zeichenkette aus Ausdruck
betrachten und auswerten
|
-t
filename
|
eine Textdatei einlesen
|
-C
filename
|
eine CSV-Datei einlesen
|
-x
filename
|
eine XML-Datei einlesen
|
-n
|
keine in der Konfigurationsdatei
vorkonfigurierten
Datenquellen
benutzen, nur die Kommandozeile
|
-i
|
die interaktive Betriebsart starten
|
-X filename
|
den Inhalt der gesamten
Datenbank in eine XML-Datei schreiben, das Format ist kompatible zum
Import über -x
|
4.1.2. Fallbeispiele
Wichtig sind die Optionen zur Handhabung von collections. -l
bzw. --list-collections
listet
zunächst alle vorhandenen collections
auf und liefert ein paar
statistische Daten:
$
pdr -l
name
type table recs
first
last
# text
C1 160 2008-11-25
18:45:00 2010-01-02 21:55:37
* numeric
C0 1636 2008-11-25
05:00:00 2010-01-03 12:10:00
h numeric
C6 1
2009-05-19 16:00:00 2009-05-19 16:00:00
l numeric
C3 707 2008-11-25
05:00:00 2010-01-03 06:26:01
m numeric
C4 612 2009-03-04
05:00:00 2010-01-03 06:26:01
n numeric
C2 1275 2008-11-25
05:00:00 2010-01-03 12:10:00
x numeric
C5 119 2009-03-22
09:28:09 2010-01-03 10:31:01
Diese Auflistung zeigt Name und Typ jeder collection, die physische
SQL-Tabelle in der Datenbank, die Anzahl
der dort befindlichen Zeilen,
d.h. Meßwerte sowie den Zeitstempel des ältesten und des
jüngsten Eintrags.
Mit -a bzw. --add-collection und einem
string-Argument läßt sich eine collection hinzugügen:
$ pdr -a "k,n"
$ pdr -l
name type table
recs
first
last
# text
C1 160 2008-11-25
18:45:00 2010-01-02 21:55:37
* numeric
C0 1636 2008-11-25
05:00:00 2010-01-03 12:10:00
h numeric
C6 1
2009-05-19 16:00:00 2009-05-19 16:00:00
k
numeric
C7
0
l numeric
C3 707 2008-11-25
05:00:00 2010-01-03 06:26:01
m numeric
C4 612 2009-03-04
05:00:00 2010-01-03 06:26:01
n numeric
C2 1275 2008-11-25
05:00:00 2010-01-03 12:10:00
x numeric
C5 119 2009-03-22
09:28:09 2010-01-03 10:31:01
Das Argument enthält den Namen der neuen collection, ein Komma und dahinter
den Typ in Form von n
(für numeric), r
(für ratio) oder t
(für text).
Eine nicht mehr benötigte collection
kann mit -d bzw. --delete-collection und dem
Namen der collection
gelöscht werden:
$
pdr -d k
Sämtliche collections
können mit -D bzw. --delete-all-collections
gelöscht werden. Die collections * und # bleiben dabei erhalten,
werden aber
geleert:
$
pdr -D
$ pdr -l
name
type table recs
first
last
# text
C1 0
* numeric
C0 0
Zwei Optionen dienen
der Behandlung von rejections,
d.h.
zurückgewiesenen
Daten,
bei
denen
ein erkennbares Problem vorlag,
aufgrunddessen die Daten nicht in die Datenbank übernommen werden
konnten. Diese Daten gelangen dann in eine Schattentabelle. pdr liefert
beim Auftreten solcher Fehler eine Ausschrift:
at least one expression has been rejected, try -r to list rejections
Mit -r bzw. --list-rejections können
diese Daten nachträglich angezeigt werden:
$
pdr -r
timestamp
expression
2010-01-03 17:46:20 12.0k
(Fehler:
die
collection
k existiert nicht)
Wenn tatsächlich ein Schreibfehler vorliegt, kann die Eingabe
wiederholt werden. Nach erfolgter Korrektur aller rejections kann die Schattentabelle
geleert werden:
$
pdr -R
Die Option -e bzw. --expression erlaubt die Angabe
jeweils eines Ausdrucks, der zur Eingabe
benutzt wird:
$
pdr -e "5.2"
-e "2009-12-31 17:28:03 7.9"
Die Option kann mehrfach verwendet werden, was jeweils einen neuen
Ausdruck erfordert. Die Ausdrücke bleiben untereinander
unabhängig und werden einzeln verarbeitet.
Die Option -n bzw. --none erlaubt das
Außerkraftsetzen aller konfigurierten Datenquellen. Dies dient
dem Fall, daß der Benutzer in kurzer
Zeit mehrfach hintereinander Aufrufe des Programms tätigt, z.B. um
Eingaben über die Kommandozeile durchzuführen. Viele mail server melden dann
beispielsweise, daß
pro Minute nur eine bestimmte Anzahl an Logins durchgeführt werden
darf o.ä. Das ständige Herstellen der Verbindung kostet zudem
spürbar Laufzeit. Mit -n
arbeitet man ganz privat einfach auf der lokalen Datenbank.
4.2. pdx
pdx besitzt keine Argumente, nur Optionen.
Optionen werden grundsätzlich durch ein Minuszeichen eingeleitet.
Ein zweites Minuszeichen kennzeichnet sog. lange Optionen.
4.2.1. Optionen
-?
|
obigen Hilfebildschirm anzeigen
|
-V
|
die pdx-Versionsnummer anzeigen
|
-v
|
Ausgaben machen, ist diese
Option nicht gesetzt, macht pdx
nur dann Ausgaben, wenn Fehler
aufgetreten sind
|
-c
filename
|
die Datei filename als
Konfigurationsdatei benutzen, diese Option
hat Priorität
gegenüber der standardmäßig benutzten
Konfigurationsdatei ~/.pdrx
|
-n
timestamp
|
den Wert der Funktion now auf timestamp festlegen
|
-i
|
die interaktive Betriebsart einschalten
|
-f
|
den schnellen Modus einschalten
|
4.2.2. Fallbeispiele
-n
bzw. --now
läßt die
Angabe des Wertes der Funktion now
zu. Überall, wo now
explizit oder implizit verwendet wird, wird dann statt des aktuellen
Zeitstempels der angegebene Wert verwendet. Das ist sehr praktisch, um
mit wenigen Handgriffen die gesamte Generierung von Berichten und/oder
Diagrammen für einen konkreten Zeitpunkt, etwa für den
letzten Monat o.ä. durchzuführen, ohne dies aufwendig erst
konfigurieren zu müssen. Vorausetzung dafür ist, daß
man in den Berichtsvorlagen und/oder Diagrammdefinitionen keine festen
Zeitstempelwerte konfiguriert. Zeitangaben im Argument sind optional.
Was fehlt, wird mit Nullen ergänzt - die folgenden Aufrufe sind
absolut gleichbedeutend:
$
pdx -n 2009-10-01
$ pdx -n 2009-10-01-00:00
$ pdx -n 2009-10-01-00:00:00
-f bzw.
--fast verbessert das
Laufzeitverhalten möglicherweise erheblich. Dazu werden einmal
berechnete Ergebnisse
intern gespeichert, so daß sie bei der nächsten Verwendung
augenblicklich zur Verfügung stehen. Dies kostet Arbeitsspeicher,
beschleunigt das Programm aber spürbar. Man sollte wenn immer
möglich mit -f
arbeiten, es am besten gleich in die Konfigurationsdatei ~/.pdrx eintragen. Einen
Einfluß auf die Ergebnisse hat -f selbstverständlich
nicht.
5. Konfiguration
pdr und pdx erfordern einige Konfiguration, da ihr Verhalten in weiten
Grenzen beeinflußbar ist. All diese Einstellungen werden in einer
lokalen Konfigurationsdatei mit dem Namen ~/.pdrx abgelegt. Diese Datei
hat vier Abschnitte:
- Generelle Optionen
- Datenbankoptionen
- Eingabeoptionen (d.h. pdr-spezifische Optionen)
- Ausgabeoptionen (d.h. pdx-spezifische Optionen)
Die Reihenfolge der Abschnitte oder Zeilen spielt keine Rolle.
5.1. Generelle Optionen
In diesem Abschnitt werden zwei Dinge konfiguriert:
- grundlegende Einstellungen
- die inputs und outputs sowie die Reihenfolge ihrer
Abarbeitung
Grundlegende Einstellungen entsprechen Kommandozeilenparametern. Nicht
jeder Kommandozeilenparameter ist hier verwendbar, aber einige:
verbose
=
true
Diese Zeile legt fest, daß pdr und pdx während ihrer Arbeit
grundsätzlich Ausgaben machen sollen, damit man die Arbeitsweise
verfolgen kann. Anderenfalls werden nur Fehlerausschriften gemacht.
(empfehlenswert)
fast
=
true
Diese Einstellung bewirkt, daß pdx im fast-Modus
läuft. (empfehlenswert)
Wer pdx nicht zum Generieren
von
Berichten
und
Diagrammen benutzt,
sondern lediglich interaktive
Abfragen auf den Daten ausführen
will, kann pdx mit der Zeile
interactive
=
true
grunsdätzlich in den interaktiven Betrieb versetzen.
Die Zeile
encoding
=
UTF-8
legt das Encoding fest, das immer dann benutzt wird, wenn keine
konkretere Angabe gemacht wird. Diese Option ist
dafür verantwortlich, daß Texte (z.B. Kommentare, man denke
insbesondere an deutsche Umlaute) korrekt in die Datenbank gelangen.
Auf modernen Systemen wird man UTF-8 oder ISO-8859-1 anwenden. pdr
erlaubt ASCII, UTF-8, UTF-16, ISO-8859-1, ISO-8859-15, Windows1252.
Wesentlich ist die Konfiguration von inputs
und outputs.
inputs
=
e-mail-postfach,
file1,
file2,
file3
outputs = report1, diagram1,
diagram2, diagram3, diagram4
Die erste Zeile definiert inputs
für pdr,
d.h. vier
Datenquellen,
nämlich e-mail-postfach,
file1, file2 und file3, die
nacheinander und zwar
in dieser Reihenfolge abgefragt werden. Worum es sich dabei konkret
handelt, wird später bei den Eingabeoptionen
konfiguriert. Die zweite Zeile benennt
gleichermaßen fünf outputs
für pdx,
nämlich einen
Bericht
und
vier
Diagramme. Ihre Konfiguration erfolgt
später bei den Ausgabeoptionen.
5.2. Datenbankoptionen
Hier werden Typ und Zugang zur verwendeten Datenbank
konfiguriert.
5.2.1. SQLite
database.type
=
sqlite
database.connect
=
~/local/share/my_data.db
Die erste Zeile besagt, daß die konkrete Datenbank eine
SQLite-Datenbank ist. Die
zweite Zeile enthält den vollständigen connect string zur Datenbank. Im
Fall von SQLite ist dies schlicht der Name der Datenbankdatei mit Pfad,
damit die Programme die Datenbank finden. Da es sich um
persönliche Applikationen handelt, d.h. es keinen
Mehrbenutzerbetrieb auf der Datenbank gibt, sollte diese in einem
lokalen Benutzerverzeichnis liegen. Das Erzeugen der physischen
Datenbank ist nicht Aufgabe von pdr oder
pdx. Der Benutzer muß dies mit den Mitteln des jeweiligen
Datenbank- bzw. Betriebssystems selbst vornehmen. Bei SQLite ist dies
ganz einfach:
$
cat > my_database.db (mit Ctrl-D beenden)
Dieser Aufruf erzeugt eine 0 Byte große Datei, die pdr als leere
Datenbank akzeptiert.
5.2.2. MySQL
database.type
=
mysql
database.connect
=
user=my_db_user_name;password=my_db_user_password;db=my_db_name;compress=true;auto-reconnect=true
Die erste Zeile besagt, daß die konkrete Datenbank eine
MySQL-Datenbank ist. Die
zweite Zeile enthält den vollständigen connect string zur Datenbank.
Dieser enthält Paare der Form Schlüssel=Wert, die
Schlüssel sind oben fett gemacht. Voraussetzungen zur Bildung
dieses connect strings sind
folgende:
- Die Datenbank muß existieren, d.h. sie muß zuvor von
einem Datenbankadministrator angelegt worden sein. Dieser vergibt dabei
einen Namen, der auf dem Datenbankserver eindeutig ist, z.B. pdrx. Es ist wahrscheinlich
empfehlenswert, auf Servern, an denen mehrere Benutzer arbeiten,
mehrere benutzerspezifische Datenbanken anzulegen und am Namen zu
unterscheiden.
- Der Benutzer (ein Benutzer des Datenbankservers, nicht des
Betriebssystems) muß existieren und das Recht besitzen, Tabellen
anzulegen, zu löschen, zu selektieren und zu manipulieren.
5.3. Eingabeoptionen
Im Fall der häufigen Verwendung immer gleicher Datenquellen zur
Eingabe können diese in die Konfigurationsdatei eingetragen und
administriert werden. Diese Datenquellen werden dann bei jedem Aufruf
von pdr automatisch abgefragt.
5.3.1. Konfiguration
eines
POP3-Postfachs
Die Konfiguration eines POP3-Postfachs
(e-mail-postfach)
erfordert
folgende
Einstellungen:
e-mail-postfach.type = pop3
e-mail-postfach.server
=
pop.gmx.net
e-mail-postfach.account =
MyAccount@gmx.net
e-mail-postfach.password = MyPassword
e-mail-postfach.subject = Z
e-mail-postfach.keep = yes
Die erste Zeile legt fest, dass es sich bei e-mail-postfach um ein Postfach
handelt, das über POP3 abgefragt wird. Die nächsten drei
Zeilen sind selbsterklärend. Die vierte Zeile
benennt die Betreffzeile (subject),
an
der
pdr
relevante
e-mails
in
diesem
Postfach
erkennt.
Nur
mails
mit
dieser
Betreffzeile
werden
bearbeitet,
alle
anderen
ignoriert.
Auf
diese
Weise
muß
man
nicht
zwingend
ein
neues
Postfach
anlegen,
man
kann
ein
existierendes
mitbenutzen.
Hinweis:
diese Betreffzeile muß bei
jedem mail angegeben werden. Wer viele Datenmails verschickt, muß
dies also ggf. sehr oft tun. Man halte sie deshalb möglichst kurz,
aber zweifelsfrei eindeutig. Die letzte Zeile legt fest, ob ene
e-mail-Nachricht nach erfolgreicher Verarbeitung gelöscht werden
soll. Die Option akzeptiert true,
false, yes und no. Im Fall von true oder yes bleibt die e-mail-Nachricht
auf dem Server bestehen. Fehlt diese Option, wird sie gelöscht.
5.3.2.
Konfiguration einer
Textdatei
Die Verwendung einer Textdatei
als Eingabe erfordert folgende Konfiguration:
file1.type
=
txt
file1.filename = ~/my_file.txt
file1.encoding = ISO-8859-1
file1.keep = true
Die erste Zeile legt fest, daß es sich bei file1 um eine Textdatei mit
interpretierbaren Ausdrücken handelt. Die zweite Zeile gibt den
Dateinamen bekannt, die dritte Zeile ein Encoding. Fehlt sie, wird default_encoding benutzt. Die
letzte Zeile legt fest, ob die Textdatei nach erfolgreicher
Verarbeitung gelöscht werden soll. Die Option akzeptiert true, false, yes und no. Im Fall von true oder yes bleibt die Datei bestehen.
Fehlt diese Option, wird die Datei gelöscht.
filename erlaubt die
Verwendung von wildcards (* und ?) um eine Datei mit nur
unvollständig bekanntem oder wechselndem Namen oder gleich eine
ganze Gruppe von Dateien zu verarbeiten. Der enthaltene Pfad muß
vollständig sein, im Dateinamen kann man jedoch eine Angabe wie *.txt machen, um alle Dateien
im angegebenen Verzeichnis zu verarbeiten.
5.3.3.
Konfiguration einer
CSV-Datei
Die Verwendung einer CSV-Datei
als Eingabe erfordert folgende Konfiguration:
file2.type
=
csv
file2.filename =
~/my_file.csv
file2.encoding = ISO-8859-1
file2.ctrl_line = datetime, x, y, z
file2.keep
=
false
Die erste Zeile legt fest, daß es sich bei file2
um eine CSV-Datei handelt. Die zweite
Zeile gibt den Dateinamen bekannt, die dritte Zeile ein Encoding. Fehlt
sie, wird die generelle Option encoding
benutzt. Die Option ctrl_line
legt bei Bedarf eine Steuerzeile für die CSV-Datei fest. Die
CSV-Datei selbst muß dann keine Steuerzeile enthalten. Die letzte
Zeile legt fest, ob die CSV-Datei nach
erfolgreicher Verarbeitung gelöscht werden soll. Die Option
akzeptiert true, false, yes und no. Im Fall von true oder yes bleibt die Datei bestehen.
Fehlt diese Option, wird die Datei gelöscht.
filename erlaubt die
Verwendung von wildcards (* und ?)
um eine Datei mit nur unvollständig bekanntem oder wechselndem
Namen oder gleich eine
ganze Gruppe von Dateien zu verarbeiten. Der enthaltene Pfad muß
vollständig sein, im Dateinamen kann man jedoch eine Angabe wie *.csv machen, um alle Dateien
im angegebenen Verzeichnis zu verarbeiten.
5.3.4.
Konfiguration einer
XML-Datei
Die Verwendung einer XML-Datei
als Eingabe erfordert folgende Konfiguration:
file3.type
=
xml
file3.filename = ~/my_file.xml
file3.keep = no
Die erste Zeile legt fest, daß es sich bei file3
um eine XML-Datei handelt. Die zweite
Zeile gibt den Dateinamen bekannt. Eine Angabe des Encodings fehlt
hier, da die XML-Datei diese Information in sich trägt. Die letzte
Zeile legt fest, ob die XML-Datei nach erfolgreicher Verarbeitung
gelöscht werden soll. Die Option akzeptiert true, false, yes und no. Im Fall von true oder yes bleibt die Datei bestehen.
Fehlt diese Option, wird die Datei gelöscht.
filename erlaubt die
Verwendung von wildcards (* und ?)
um eine Datei mit nur unvollständig bekanntem oder wechselndem
Namen oder gleich eine
ganze Gruppe von Dateien zu verarbeiten. Der enthaltene Pfad muß
vollständig sein, im Dateinamen kann man jedoch eine Angabe wie *.xml machen, um alle Dateien
im angegebenen Verzeichnis zu verarbeiten.
5.4. Ausgabeoptionen
5.4.1.
Konfiguration eines Berichts
Ein von pdx zu erzeugender Bericht erfordert folgende Konfiguration:
report1.type
=
report
report1.comment_begin
=
"<!---"
report1.comment_end =
"--->"
report1.input_file
=
input/report1.html
report1.output_file =
output/report1.html
report1.encoding
=
ISO-8859-1
Die erste Zeile besagt, daß report1
ein Bericht ist. Die nächsten beiden Zeilen benennen die beiden
Kommentarkennzeichnungen,
an denen pdx seine Anweisungen innerhalb der als Vorlage
verwendeten
Eingabedatei erkennt. Die vierte und fünfte Zeile benennen die
Ein- und
die
Ausgabedatei. Die letzte Zeile gibt bekannt, in welchem Zeichensatz die
Eingabedatei vorliegt und die Ausgabedatei erzeugt wird, notwendig
für Dateien, die das Encoding nicht in sich tragen.
5.4.2. Konfiguration
eines Diagramms
Die Konfiguration eines Diagramms ist ähnlich der eines Berichts,
aber etwas
einfacher:
diagram1.type = diagram
diagram1.input_file=input/diagram1.tmpl
diagram1.output_file=output/diagram1.png
diagram1.antialias=true;
Kommentarkennzeichnungen sind in
Diagrammdefinitionen nicht nötig. De letzte Option antialias ist nur für png-Dateien verfügbar.
Wird diese Option benutzt, so erzeugt die Cairo-Bibliothek Diagramme,
die in der Regel wesentlich besser aussehen, sofern sie Zickzack-Linien
enthalten.