Reporting
Grundsätzlich gehen wir dazu von folgendem Vorgehen aus:
- Initial-Objekte: Die Objekte zu denen ein Chart erstellt werden soll, müssen bereitgestellt werden
- Aufbereitung der Daten: Beschreibt, wie die Initialobjekte einzuteilen sind (z.B. Aufteilung auf verschiedene Balken in einem Balkendiagramm) und zu bewerten sind (z.B. Anzahl der so eingeteilten Objekte oder Summe bestimmter Attributwerte).
- Darstellung: Wie sollen die Daten angezeigt werden, z.B. Kuchendiagramm, Balkendiagramm, …
Aus der gewünschten Darstellung ergeben sich Einschränkungen für bzw. Anforderungen an die Aufbereitung. Je nachdem nach wie vielen Kriterien die Eingangsobjekte klassifiziert werden sollen bieten sich folgende Darstellungsmöglichkeiten an:
- Kuchendiagramm (
PieChart
) - eine Einteilung - Balken/Säulen-diagramm (
BarChart
) - zwei Einteilungen - Gruppierte Balken (
GroupedStackedBarChart
) - drei Einteilungen
Konfigurierte Charts
Typisierte Konfiguration
com.top_logic.reporting.chart.configuration.ChartConfig
Entsprechend der Beschreibung oben besteht die Chart-Konfiguration im wesentlichen aus 3 Teilen:
Beschreibung der Eingangs-Daten (data-source)
Hier kann eine Klasse hinterlegt werden, welche die Eingangsobjekte für den Chart liefert. Zum Beispiel alle Objekte zu einem Typ. Diese Objekte sollen ggf. noch gefiltert werden oder transformiert.
Aufbereitung der Daten (model-preparation)
Es können Funktionen (PartitionFunction) zur Einteilung der Daten hinterlegt werden und Funktionen zur Berechnung des Wertes einer Partition (AggregationFunction)
Darstellung (chart-builder)
Die Diagrammart kann festgelegt werden und weitere Informationen zur Darstellung können hinterlegt werde. Zum Beispiel Titel und Achsen-Beschriftungen oder Farben. Außerdem muss es ja nach Ausgabemedium möglich sein weitere Informationen unterzubringen, z.B. Tooltips und Links.
Eine solche Konfiguration enthält alle notwendigen Informationen darüber, welche Daten wie anzuzeigen sind. Es wird eine Default-Implementierung einer Chart-Component bereitgestellt, welche ein Chart zu einer gegebenen Konfiguration darstellen kann. Es sollte in der Regel nicht notwendig sein, eigene Chart-Komponenten zu implementieren bzw. abzuleiten.
Interaktive konfigurierte Charts
Typisierte Konfiguration
com.top_logic.reporting.chart.configuration.gui.InteractiveChartBuilder.Config
Die interaktive Chart-Konfigurationen ist ganz ähnlich der normalen Chart-Konfiguration aufgebaut. Am Ende muss eine Chart-Konfiguration vorliegen, die auf dem Standard-Weg (siehe oben - Default-Implementierung der Chart-Component) dargestellt werden kann. Variable Anteile werden an der GUI zur Anzeige gebracht, so dass der Nutzer eine entsprechende Parametrisierung vornehmen kann.
Ausgehend davon, dass die variablen Anteile valide vorbelegt sind, kann man aus einer interaktiven Konfiguration immer direkt eine valide ChartConfig ableiten ohne dass der Nutzer eine Eingabe vornehmen muss.
Konfigurationsteile
Datenquelle
public interface ChartDataSource<C extends DataContext> {
Collection<? extends Object> getRawData(C context);
}
Die Datenquelle liefert die Initialobjekte für die Chart-Darstellung. Dazu muss die Methode getRawData(DataContext) implementiert werden. Ob Kontextinformationen zum bereitstellen der Initialobjekte notwendig sind, ist von der jeweiligen Implementierung abhängig. Die Ableitung
ComponentDataContext extends DataContext
kann verwendet werden, falls der Chart ausschließlich durch eine entsprechende LayoutComponent zur Anzeige gebracht wird (das ist der Standardfall). In diesem Fall kann Bezug auf die Komponente und deren Modell genommen werden. Z.b. wenn sich die Initialobjekte aus der Master-Selektion ableiten.
Beispiele
- Datenquelle liefert (kontextfrei) alle Objekte zu einem gegeben (konfigurierten) KO-Typ
com.top_logic.reporting.chart.configuration.datasource.KOTypeProducer
- Datenquelle liefert das Ergebnis eines gegebenen (konfigurierten) ListModelBuilder zum aktuellen Modell der Kontext-Komponente
com.top_logic.reporting.chart.configuration.datasource.ListModelBuilderProducer
Aufbereitung
public interface ModelPreparation {
public ChartTree prepare(Collection<? extends Object> rawData);
}
Es gibt die Default-Implementierung
com.top_logic.reporting.chart.configuration.model.DefaultModelPreparation
welche kofiguriert werden kann. Es sollte in der Regel nicht notwendig sein, weitere Implementierungen zu erstellen. Ziel der ModelPreparation ist es, aus den Initialobjekten aus der Datenquelle einen ChartTree zu erstellen. Der ChartTree ist das allgemeine interne Modell welches unabhängig von der späteren Darstellung alle notwendigen Informationen hält. Der ChartTree muss also außer den visualisierten "Zahlen"werten auch noch die zugrundeliegenden Objekte kennen. Dies wird verwendet um zum Beispiel Tooltips und URLs in Charts zu hinterlegen. Welche Zusatzinformationen hinterlegt werden können, hängt vom gewählten Ausgabemedium und Darstellungsform ab. Der ChartTree muss unabhängig davon die Informationen bereithalten.
Statt eine eigene ModelPreparation zu implementieren, sollte die Einteilung und Wertberechung konfiguriert werden. Die Standardimplementierung kann dafür mit Partitionierungsfunktionen und Aggregationsfunktionen konfiguriert werden.
Beispiele
- Die Initialobjekte werden anhand ihres Wertes eines klassifikationswertigen Attributes eingeteilt
com.top_logic.reporting.chart.configuration.partition.ClassificationPartition
Darstellung
Die ersten Konfigurationsteile (Datenquelle und Aufbereitung) sind allgemein. Der Darstellungsteil hingegen ist auf JFreeChart ausgelegt da dies bisher die auschließlich verwendete Darstellungsform ist. Eine Standard-Komponente wird bereitgestellt, um aus den aufbereiteten Daten JFreeCharts zu erstellen und mit allen Standard-Features zur Anzeige zu bringen (Export, Links).
com.top_logic.reporting.layout.flexreporting.component.ConfiguredChartComponent
Soll auf eine andere Rendering-Engine gewechselt werden oder zusätzlich angeboten werden, müssen entsprechende Klassen bereitgestellt werden.
public interface JFreeChartBuilder<D extends Dataset> extends ConfiguredInstance {
public DatasetBuilder<D> getDatasetBuilder();
public Class<D> getRequiredDatasetType();
public JFreeChart createChart(ChartContext context, ChartData<D> chartData);
}
Der JFreeChartBuilder erstellt aus dem ChartTree ein JFreeChart. JFreeChart unterstützt unterschiedliche Darstellungen. Abhängig von der Darstellung, wird ein eigenes internes Modell (Dataset) verwendet. Der DatasetBuilder ist dafür zuständig, passend zum JFreeChart-Builder die Daten in das notwendige JFreeChart-Modell umzuwandeln. Die Standard-Komponente erstellt mit Hilfe des hinterlegten DatasetBuilder das notwendige JFreeChart-Modell und ruft die Methode createChart(ChartContext, ChartData). ChartData enthält außer dem JFreeChart-Modell (Dataset) noch das interne Modell (ChartTree) so dass bei Bedarf zusätzliche Informationen bereitgestellt werden können:
Im Dataset steht z.b. nur, das für Spalte S1 und Reihe R1 der Wert 4711 gilt. Im ChartTree sind die Objekte die zu diesem Wert geführt haben hinterlegt. So kann z.B. ein Link hinterlegt werden, der zu den jeweiligen Objekten führt oder ein Tooltip kann Auskunft darüber geben wie der Wert zustande kommt.
Der ChartContext wird verwendet um URLs zu generieren.
Beispiele
- Balkendiagramme
com.top_logic.reporting.chart.configuration.chartbuilder.BarChartBuilder
- Kuchendiagramme
com.top_logic.reporting.chart.configuration.chartbuilder.PieChartBuilder
- Zeitverlauf
com.top_logic.reporting.chart.configuration.chartbuilder.TimeSeriesChartBuilder
Idealfall
Im folgenden wird der Idealfall beschrieben, der sehr viele einfache Standardauswertungen umfasst. In diesem Fall ist es nicht notwendig eigene Klassen zu implementieren, alles kann einfach in der Konfiguration hinterlegt werden:
Eine Datenquelle liefert Initialobjekte (z.B. alle Risiken) und eine konfigurierte Liste von Partitionierungsfunktionen teilt die Menge ein (z.B. nach Status). Eine geeignete Darstellungsart aus den vorhandenen JFreeChartBuildern wird verwendet (Balkendiagramm).
Die Konfiguration sieht wie folgt aus:
<chart xmlns:config="http://www.top-logic.com/ns/config/6.0" >
<data-source class="com.top_logic.reporting.chart.configuration.datasource.KOTypeProducer" object-type="Risk" />
<model-preparation class="com.top_logic.reporting.chart.configuration.model.DefaultModelPreparation">
<partitions>
<partition class="com.top_logic.reporting.chart.configuration.partition.ClassificationPartition" attribute-name="state" meta-element-name="riskItem.risk" />
</partitions>
</model-preparation>
<chart-builder class="com.top_logic.reporting.chart.configuration.chartbuilder.BarChartBuilder" />
</chart>
Aus der Datenquelle ergeben sich die Initialobjekte, ein Standard-Export kann also zusätzlich zum Diagramm auch eine Liste der Objekte (hier Risiken) exportieren. Da eine Einteilung nach Status vorgenommen wurde, kann für den Export abgeleitet werden, das diese Information in der Liste der Objekte enthalten sein muss. Jeder Balken repräsentiert eine Menge von Risiken. Standardmäßig werden URLs gerendert, so dass ein Klick auf einen Balken die Liste der entsprechenden Objekte zur Detailansicht öffnen kann.
Abweichungen vom Standard
Einige Darstellungsoptionen können direkt über die JFreeChartBuilder-Konfiguration gesteuert werden, z.B. Titel, Achsen-Bezeichnungen oder ob Tooltips / URLs gerendert werden sollen. Zum Beispiel kann es vorkommen, dass keine Fachobjekte hinter den angezeigten Daten liegen, so dass es keinen Sinn ergibt URLs zu generieren. Unter Umständen ergeben sich die relevanten Objekte auch erst im Zuge der ModelPreparation und haben nur indirekt mit den Initialobjekten aus der Datenquelle zu tun. In diesem Fall ist es nicht sinnvoll, diese Objekte in einem Export aufzuführen. In der Standardkomponente lassen sich die export-relevanten Objekte konfigurieren:
- Initialobjekte - Es werden die Objekte aus der Datenquelle in einer Detailtabelle exportiert
- Blatt-Objekte im ChartTree - Es werden die Objekte die im internen Modell hinter den Blatt-Knoten liegen in einer Detailtabelle exportiert
- Keine - Es wird keine Detailtabelle exportiert
Der Export setzt momentan voraus, dass die zu exportierenden Objekte Wrapper sind.
(Good) practices
Vorberechnung
Aus Performance-Gründen ist es manchmal nicht möglich oder sinnvoll den Standard-Weg zu gehen. Unter Umständen müssen viele Daten vorberechnet werden um überhaupt die reportrelevanten Daten zusammenzustellen. Evtl. gibt es auch schon Berechnungscode, der weiterverwendet werden soll.
Um trotzdem möglichst viel des Standards zu verwenden müssen die Daten in einen ChartTree aufbereitet werden. Die Standardimplementierungen des JFreeChartBuilder können diesen dann an der GUI darstellen.
Beispiel "Soll/Ist-Vergleich" in Project: Pro definiertem Zeitpunkt werden Säulen für Plan und Ist angezeigt, jede Säule enthält Werte für verschiedene Klassifikationen. Diese Art der Darstellung ist in JFreeChart ein GroupedStackedBarChart. Der bisherige Code hat ein CategoryDataset für JFreeChart erstellt. Dabei werden Einteilungen gruppiert und später beim generieren des Charts werden die Gruppierungen wieder aufgelöst. Die Fachlichkeit geht hier verloren. In der Baumdarstellung im ChartTree bleibt die Struktur und damit die Fachlichkeit erhalten:
- Baumebene: Zeitpunkt
- Baumebene: Plan oder Ist
- Baumebene: Klassifikation
Der bisherige Code musste nur geringfügig angepasst werden um statt einem CategoryDataset eine verschachtelte Map zu liefern, welche die Baumstruktur repräsentiert. Als Partitionierungsfunktion wird die
com.top_logic.reporting.chart.configuration.partition.KeySetPartition
verwendet. Danach kann ein Standard JFreeChartBuilder für GroupedStackedBarCharts verwendet werden.
Raw-Data
Nicht immer entsprechen die Initialdaten den Daten, die letztendlich in den ChartTree-Blättern den dargestellten Werten zugrundeliegen. Was mit den Initialdaten passiert, liegt an der (ersten) verwendeten Partitionierungsfunktion. Falls die Partitionierungsfunktion selbst die relevanten Objekte bereitstellt, ist die Datenquelle irrelevant.
Eine andere mögliche Verwendung der Datenquelle ist es, ein Initialmodell zu liefern, aus dem dann durch spezielle Partitionierungsfunktionen die relevanten Objekte abgeleitet werden. Zum Beispiel ist das Initialobjekt ein Modell, die report-relevanten Objekte sind aber zugehörigen Risiken. Eine solche Datenquelle kann dann Kontext-abhängig leicht ausgetauscht werden. So kann im Kompoenenten-Kontext z.B. eine Datenquelle verwendet werden, welche die Selektion in einem Projektbaum liefert. An anderer Stelle soll der gleiche Chart für ein fixes Projekt erstellt werden, ohne dass es einen Navigationbaum gibt. In diesem Fall müsste man nur die Datenquelle austauschen.
Interaktion
Die Teile der interaktiven Chart-Konfiguration, die einen Beitrag zum GUI liefern, müssen das folgende Interface implementieren:
public interface InteractiveFormBuilder<R, A> extends ConfiguredInstance {
void fillContainer(FormContainer container, A arg);
R create(FormContainer container);
}
Die konfigurierten Instanzen füttern in fillContainer den FormContext um die relevanten Anteile an und müssen in create daraus das Ergebnis ableiten können. Die interaktive Chart-Konfiguration ist ganz analog zur ChartConfig aufgebaut. Statt dass die jeweiligen Anteile direkt konfiguriert werden, werden Implementierungen von InteractiveFormBuilder geliefert, die in ihrer create-Methode dann die jeweilgen Konfigurationsanteile liefern.
public interface Config extends PolymorphicConfiguration<InteractiveChartBuilder> {
@InstanceFormat
InteractiveFormBuilder<ChartDataSource<?>, ChartContextObserver> getDataSource();
@InstanceFormat
InteractiveFormBuilder<DefaultModelPreparation, ChartContextObserver> getModelPreparation();
@InstanceFormat
JFreeChartBuilder<?> getChartBuilder();
}
Die Standardkomponente zur Darstellung der variablen Anteile ist die
com.top_logic.reporting.layout.flexreporting.component.ChartConfigComponent
Falls dynamische GUI-Anteile auf Modelländerungen dieser Komponente reagieren sollen, können sie sich am ChartContextObserver registrieren. Interaktive Anteile können in ihrer Konfiguration selbst wieder interaktive Anteile enthälten. Um name-clashes zu vermeiden, empfiehlt es sich, pro property einen FormContainer anzulegen, der vom jeweiligen InteractiveFormBuilder befüllt wird.
Im folgenden Beispiel werden InteractiveFormBuilder von Datenquellen verschachtelt verwendet:
- ListModelBuilderProducer - liefert die Initialobjekte und benötigt keine GUI-Interaktion
- TransformedDataSourceBuilder - Zeigt ein Auswahlfeld für Zeitpunkte (z.B. Berichtsabschlüsse) und transformiert die Daten aus der inneren Datenquelle (1.) in die gewählte Revision
- FilteredDataSourceBuilder - Zeigt konfigurierte Filter (z.B. nach Status) und filtert Daten aus der inneren Datenquelle (2.) entsprechend der Einstellung…
<data-source class="com.top_logic.reporting.chart.configuration.datasource.FilteredDataSourceBuilder">
<data-source class="com.top_logic.reporting.chart.configuration.datasource.TransformedDataSourceBuilder">
<transformer class="com.top_logic.project.pos.layout.filter.version.transform.TaggedReportTransformerBuilder" master="projectView_navigationTree" />
<data-source class="com.top_logic.reporting.chart.configuration.datasource.ListModelBuilderProducer"
master="projectView_navigationTree"
list-model-builder="com.top_logic.risk.layout.risk.RiskListModelBuilder" >
</data-source>
</data-source>
<filters>
<filter
class="com.top_logic.reporting.chart.configuration.datasource.StaticAttributeFilter"
meta-element="riskItem.risk" attribute="state" />
<filter
class="com.top_logic.reporting.chart.configuration.datasource.StaticAttributeFilter"
meta-element="riskItem.risk" attribute="itemclass" />
</filters>
</data-source>