[Apache Superset|https://superset.apache.org/] ist eine Webanwendung zur Business Intelligence (BI), d.h. zur Aufbereitung von Daten als Charts, Tabellen etc. Dabei erlaubt die Oberfläche eines BI-Tools nicht nur die Ausgabe von Daten (wie das z.B. auch JasperReports sehr schön macht, wenn man Ausdrucke haben möchte), sondern man kann auch mit den Daten "spielen", indem man schnell verschiedene Filter anwenden, Gruppieren, Metriken (z.B. Summen) ausgeben kann etc.

!Installation

Ich bin nach der [Installationsanweisung von Superset|https://superset.apache.org/docs/installation/docker-compose] vorgegangen und habe einen Container installiert. Läuft! :-) Die Installation hat allerdings eine ganze Weile gedauert. Also auf die Mittagspause timen oder in der Zeit langsam KAffee kochen. Ansonsten lief alles wie erwartet unter http://meindockerhost:8088/.

Bei der normalen Installation wird eine Beispieldatenbank mit einigen Datasets, Charts und Dashboards installiert, so das man direkt im Thema ist.

!Erste Schritte

Eigentlich ist der Zugang für den Anfänger sehr leicht. Man loggt sich mit admin/admin ein, geht auf die Liste der Dashboards und schaut sich diese an. Man sollte beachten, das einige Daten erscheinen, wenn man mit der Maus über ein Chart fährt und das man sehr viele Objekte in den Charts auch anklicken kann, um weiter zu filtern. Die Filter sind dann links in der Anzeige der Filter angezeigt (und oben rechts in jedem einzelnen Chart erhöht sich ein Zähler, falls der Filter auf dieses Chart anwendbar ist).

!Grundlagen

!!Dataset

Die Grundlage der Datenausgabe ist das __Dataset__. (Vorher konfiguriert man logischerweise zuerst eine Datenbankverbindung.) Ein Dataset ist im Prinzip ein SQL-Query, der eine Tabelle enthält. Die richtige Herangehensweise ist es hierbei, möglichst viele Spalten zur Kategorisierung in die Daten aufzunehmen, da es einem die Oberfläche hinterher erlaubt, einfach nach diesen sogenannten Dimensionen zu filtern und zu sortieren sowie auch unterschiedliche Daten zur Zusammenfassung zu haben.

Filterbedingungen nennt man __Dimensionen__. Typische Dimensionen sind Artikel, Kunden, Kundengruppe, Artikelgruppe, Art des Umsatzes, zuständiger Kundenbetreuer, Bestellweg, Auslieferfahrer, etc. Nach einer Dimension kann man gruppieren und filtern. Wer mitdenkt, merkt schnell, das es einem bei jeder dieser Dimensionen in den Fingern juckt, sich den Umsatz mal genau aufgeschlüsselt nach eben diesem Merkmal anzusehen. Wenn das so juckt, dann ist es eine Dimension. Quelle der Daten Mein Beispiel war eine Umsatztabelle.

Die eigentlichen __Datenwerte__, die man sodann hinterher in der gefilterten Auswertung irgendwie haben möchte, gehören ebenfalls mit in die Tabelle. Diese werden hinterher im Allgemeinen in irgendeiner Art zusammengefasst, das nennt man dann eine __Metrik__. Das kann als oder Umsatz sein, der Gewinn, Anzahl der Packstücke, Fahrtstrecke, etc. Für diese Werte macht es zumeist Sinn, über sogenannte Metriken nachzudenken. Die einfachste Metrik ist die Summe des Datenwertes. So erhält man vielleicht eine Auswertung, die z.B. die Summe des Umsatzes in den gefilterten Kundengruppen ausgibt. Weitere Metriken kann man hinterher im Datenset hinzufügen (wenn sie generell öfters interessant sind) oder auch in einem einzelnen Chart angeben, wenn man dort mal etwas besonderes machen möchte.

__Zeitwerte__ enthalten ein Datum und/oder eine Uhrzeit und erlauben es, Zahlenreihen über die Zeit in einem entsprechenden Diagramm zusammenzufassen. In diesen Diagrammtypen gibt es einige besondere Möglichkeiten wie die Auswahl von Zeiträumen oder auch einen Vergleich, z.B. dieser Monat mit dem Vormonat oder dem entsprechenden Monat des Vorjahres etc. Hat man also Daten, die irgendwie mit Zeiten zu tun haben, so hat es Sinn, diese Spalte einzufügen und nicht (nur) z.B. ein Feld mit der Jahreszahl oder einem Monatsnamen zu verwenden.

Für komplexere Ideen kann man auch __berechnete Spalten__ erzeugen, die in SQL andere Spalten zusammenfassen oder sonstwie Werte erzeugen. Im Prinzip habe ich den Eindruck, das man solche Werte auch direkt vorne im SQL Query einfügen könnte, aber je nach Wissensstand des Benutzers kann hier vielleicht jemand etwas addieren oder zwei Texte kombinieren, ohne durch einen komplexen SQL-Code abgeschreckt zu werden.

Ganz interessant zum Thema, wie man das in Superset macht, sind diese Blog-Artikel:
* https://docs.preset.io/docs/semantic-layer
* https://preset.io/blog/understanding-superset-semantic-layer/


!!Chart

Hat man ein Dataset erstellt, kann man nun darauf ein Chart aufbauen. Als erstes sollte man eine der über 40 verschiedenen Charttypen auswählen

!!Dashboard

Ein Dashboard enthält mehrere Charts, die in einem Layout auf der Dashboard-Seite verteilt werden können. Das Dashboard ist also das, wo der Benutzer letztlich mit arbeitet.

!! Filter

Links im Dashboard ist der Bereich der Filter. Hier stehen ggf. oben die Corssfilter, das sind Filter, die man aktiviert hat, indem man in einem Chart auf eine Kategorie geklickt hat. Aber man kann auch besondere, eigene Filter hier definieren, die dann zum Dashboard gehören.

Für jeden Filter kann man angeben, ob welche der angezeigten Charts er sich auswirkt. Das bedeutet, wenn ich einen Filter setze (z.B., weil ich in einem Chart auf eine Kundengruppe klicke), verändern sich sofort (ok... ggf. dauert es ein paar Sekunden) alle anderen Charts und zeigen nur noch Daten dieser Kundengruppe an.

Eine sehr tolle Einführung in Filter gibt in diesem Video: [Apache Superset Dashboard Filters 101|https://www.youtube.com/watch?v=cafjAk7t5MM]. Man muss nur die ersten ca. 20 Minuten anschauen. Die sind allerdings sehr interessant und dicht mit Informationen gepackt. Davon abgesehen ist das auch so ein schönes Video um zu sehen, was mit Superset so geht (weil das Filtern ja letztlich der Kern der ganzen Magie ist).

!!!Dashboard-Filter

Dashboard-Filter gehören sozusagen zum Dashboard und können dort fest mit einem Default-Wert versehen werden. Es ist möglich, einen Link zu erstellen, der ein Dashboard inklusive der eingestellten Filter definiert (sowohl als Permalink als auch über URL-Parameter) und ao einen besonderen Einblick, den ein gefiltertes Dashboard mir gibt, festzuhalten, später wieder aufzurufen oder auch per Mail zu versenden.

Die Dashboard-Filter haben ggf. Default-Werte. Ich sehe also z.B. immer zuerst nur den Umsatz vom aktuellen Jahr (Ja, man kann sowas einstellen wie "das aktuelle Jahr" - ich sagte doch, das Zeitwerte gesondert unterstützt werden).

!!!Crossfilter

Crossfilter können nicht persistent gemacht werden. Sie erlauben es, dynamisch durch klicken auf eine Kategorie innerhalb eines Charts weiter zu filtern.

!!Benutzer
Natürlich macht es Sinn, 

!Lokalisierung

Für eine ernsthafte Nutzung in Deutschland müssen wenigstens die Zahlenwerte im deutschen Format (also mit richtigem Komma) angezeigt werden. So eine richtige eindeutige Anleitung zum Verlinken habe ich nicht, aber am Ende habe ich diesen Teil hier in {{./docker/pythonpath_dev/superset_config.py}} eingetragen. Ich glaube, das ist nicht ganz die richtige Stelle, weil das bei Updates des Docker-Containers überschrieben werden könnte. Aber für den Anfang funktionierts. (Wer's besser weiss, bitte hier eintragen)

{{{
D3_FORMAT = {
    "decimal": ",",           # - decimal place string (e.g., ".").
    "thousands": ".",         # - group separator string (e.g., ",").
    "grouping": [3],          # - array of group sizes (e.g., [3]), cycled as 
    "currency": ["", "€"]     # - currency prefix/suffix strings (e.g., ["$", 
}

}}}

Zur kompletten Lokalisierung auf deutsch gibt es auch Infos, aber das habe ich bisher noch nicht gemacht. Soweit ich [diese Seite zum Thema|https://superset.apache.org/docs/contributing/howtos/#contributing-translations] verstanden habe, muss man die Likalisierung einschalten, damit man eine Sprache auswählen kann und dann gibt es gute oder nicht so gute Übersetzungen. Deutsch war nicht explizit erwähnt.

Mir war das Thema bisher nicht so wichtig. Wer es gemacht hat, kann hier gerne mehr erklären.

!Weitere Tipps

!! Time Series Charts

Wie oben bereits gesagt, sind bestimmte Arten von Charts dafür optimiert, Zeitreihen anzuzeigen. Eine gute Einführung ist hier: https://preset.io/blog/2020-06-26-timeseries-chart/ Darüber hinaus gibt es aber noch einige interessante Punkte.

Über Zeitcharts kann man ganz viel nachdenken. Möchte ich meine Umsatzzahlen je Monat anzeigen oder den Gesamtumsatz bis zu diesem Monat (was im Vergleich zum Vorjahr besser aussagt, wo ich stehe), möchte ich vielleicht nur die Steigerung je Monat anzeigen. (Oder natürlich je Woche oder je Tag). Möchte ich verschiedene Werte miteinander in Beziehung setzen (den Umsatz und die Personalkosten)? Hier gibt es viele Gedanken, die man sich machen kann.

Wie in dem genannten Blogartikel ganz am Ende gezeigt, kann man Zeitvergleiche innerhalb eines Charts anstellen. Also dieses Jahr mit dem Vorjahr. Das ist eine oft nachgefragte Funktion.

Außerdem gibt es auch ein [Mixed Time-Series Chart|https://preset.io/blog/mixed-time-series-charts-tutorial/]. Das erlaubt, zwei völlig unanhängige Werte in ein und das selbe Zeitchart einzufügen. Also z.B. den Umsatz und den Personalaufwand. Oder die Anzahl an Erkältungskranken und die Niederschlagsmenge. Oder so...

Man kann auch in einigen Chart-Typen über sogennante Annotations weitere Zahlenreihen in ein bestehendes Chart einfügen. Bisher habe ich das noch nicht ausprobiert, bin aber gespannt.

Natürlich kann man, wenn sich die Metrik aus verschiedenen Kategorien zusammensetzt, ein Diagramm mit mehreren Linien (oder Balken aus mehreren bunten Teilen) anzeigen, um die Verteilung des Gesamtwertes auf Teilkategorien zu verdeutlichen.

!!Wofür benutzt man Dashboards

Ich denke, das die Erstellung von Kennzahlen und die Darstellung derselben eine besonderer Zweig der Statistik oder auch der Betriebswirtschaft sind und schon viel darüber nachgedacht worden ist. Vielleicht findet ja jemand Zeit, hier ein paar gute Links einzufügen, die dabei helfen, die richtigen Dashboards für ein Problem zu finden.

Je mehr man versucht, alle Probleme mit einem einzigen Dashboard zu lösen, weil man hier noch ein Chart anfühgt und da noch ein weiteres Chart anders konfiguriert, umso unübersichtlicher und unfokussiert wird das Ganze dann irgendwann. Man sollte also mit klarem Fokus ein Dashboard zu einem Thema machen.

Eine grobe Einteilung aus einem Artikel in Vier Gruppe fand ich interessant:

* Analytisch: Betrachtet Zahlenreihen der Vergangenheit und erlaubt, diese nach allen Kategorien zu analysieren und zu betrachten, um herauszufinden, was denn nun wie war, wenn man es so oder so betrachtet. Das ist z.B. mein obiges Beispiel mit den Umsatzdaten der vergangenen Jahre.

* Operationell: Zeigt mir Dinge, die jetzt und hier wichtig sind, also z.B.ö einen Lagerbestand, mein Konto und meine Verbindlichkeiten, meinen Vertragsbestand oder auch das Wetter von heute.

* Planung: Erlaubt mir in der kurzfristigen Planung, Entscheidungen zu treffen. Wie viele BEstellungen habe ich für die nächsten Tage und habe ich dafür genug Ware, Material und Personal? Wer hat demnächst Urlaub und fehlt mir dann? (Interessant ist vielleicht auch, das es in Seuperset-Zeitreihen eine Funktion zur Prognose von Daten in die Zukunft gibt.)

* Strategie: Was sind meine langsfristigen Ziele und durch welche Kennzahlen kann ich die beschreiben?

Darüber hinaus gibt es auch viele Informationen zu sogenannten [Key Performance Indikatoren|https://de.wikipedia.org/wiki/Key-Performance-Indicator], das sind betriebswirtschaftliche Kennzahlen. Diese zu ermitteln, anzuzeigen, ins Verhältnis zu setzen und zu bewerten ist natürlich eine Standardanwendung von BI-Tools.

!! Verlinkung verschiedener Dashboards und externer Daten

Wie ich oben geschrieben habe, macht es Sinn, Dashboards auf ein Thema zu fokussieren, um sie nicht zu kompliziert werden zu lassen.

Im Charttyp Tabelle und auch im Charttyp Handlesets kann man HTM-Code eintragen, der entsprechend angezeigt wird. Das erlaubt, das man eine Textspalte in seinem Dataset hat, die einen Link erzeugt, also einen String der Form "<a ...". Tatsächlich ist das vielleicht auch ein guter Grund, eine "calculated column" einzusetzen, in der man mit den Stringfunktionen von SQL aus einem Datensatz einen HTML Tag für einen Link bauen kann.

Eine Anleitung zum Thema gibt es in diesem Artikel: https://docs.preset.io/docs/semantic-layer

Möchte man ein bestimmtes Dashboard wählen, kann man das über den Link zum Dashboard machen (enthält eine interne Dashboard-Nummer). Besser und schöner ist es allerdings, wenn man in den Dashboard-Einstellungen einen "Slug" angibt, das ist eine sinnvoll lesbare Ersetzung für die Nummer. Damit kann man z.B. ein Dashboard je Artikelgruppe machen und deren Namen dann in den Link einfügen. Man kann aber auch ein Dashboard "Artikel" anlegen und dann die entsprechende Artikelnummer als vordefinierten Filter in der URL übergeben. [Hier ein Artikel dazu|https://www.blef.fr/superset-filters-in-url/] und [Hier noch eine Stackoverflow-Frage zum Thema|https://stackoverflow.com/questions/73814452/apache-superset-how-to-customize-native-filters-in-an-url].

!!! Beispiel: Beiderseitige Verlinkung zwischen Superset und [iDempiere]

Auch eine Integration in unsere Warenwirtschaft war ganz einfach, weil [iDempiere] Links zu einzelnen Datensätzen erlaubt und ich dann einen solchen Link zusammengestellt habe. Wenn ich jetzt also in Superset auf eine Artikelnummer klicke, öffnet sich ein Fenster mit dem Stammdaten-Fenster von iDempiere. Ein kleiner Trick war übrigens noch nötig, damit die angezeigten Artikelnummern auch wie normale funktionierten: Klicke ich auf den Tabellenheader, so wurden die Links nach dem Link-String und damit nach der internen Datensatz-ID sortiert. Das ist natürlich Mist. Also habe ich im HTML-Quelltext das Sortierkriterium vorher in einen Kommentar geschrieben.

Also habe ich in mein Superset Query folgendes eingefügt, um eine Spalte p_link zu erhalten. Angezeigt wird die Kundennummer (das ist p.value in meiner Abfrage) und sortiert wird ebenfalls nach der Knudennummer. Außerdem angegeben ist der Tabellenname (also für Artikel in diesem Beispiel M_Product). Es öffnet sich dann immer das entsprechende Standardfenster zu dieser Tabelle (also in meinem Fall die Artikel-Stammdaten).

{{{
'<!--' || p.value || '-->' ||
    '<a href="https://idempiere.local/webui/index.zul?Action=Zoom&TableName=M_Product&Record_ID=' ||
    p.m_product_id || '"target=”_blank” >' || p.value || '</a>' as p_link,

}}}

Natürlich kann man einen solchen Ausdruck auch in einem View verewigen oder auch in Superset als "calculated column" aus dem bereits in einem Dataset vorhandenen value-Wert erzeugen.

Umgekehrt geht das in iDempiere z.B. über die QuickInfo-Funktion, die man als System-Benutzer im Fenster Status Line konfiguriert. Für einen Link aus dem Fenster für Geschäftspartner in mein Vertriebs-Dashboard trage ich folgende Zeile im Feld "Message-Text" meiner entsprechend neu angelegten Message ein. (Zu beachten ist noch, das diese Message übersetzt wird und man deshalb bei Änderungen immer auch die deutsche Übersetzung ändern muss.)

{{{
<a href="{1}" target="_blank">BI Kunde {0}</a>
}}}

Im Status Line Fenster lege ich ebenfalls passen einen neuen Datensatz ein und trage im SQL-Feld ein:

{{{
select @Value@, $$http://mysuperset.local:8088/superset/dashboard/Vertrieb/?native_filters=(NATIVE_FILTER-15UV7uwDjEXbWzt7FDmq-:(__cache:(label:'$$ || @Value@ || $$',validateStatus:!f,value:!('$$ || @Value@ || $$')),extraFormData:(filters:!((col:bp_value,op:IN,val:!('$$ || @Value@ || $$')))),filterState:(label:'$$ || @Value@ || $$',validateStatus:!f,value:!('$$ || @Value@ || $$')),id:NATIVE_FILTER-15UV7uwDjEXbWzt7FDmq-,ownState:()))$$}}}

Der URL wird so in SQL zusammengebaut (dabei wird der Value-Wert, also die Kundennummer, wie benötigt mehrfach eingesetzt) und das Ganze dann in der Message eingefügt, wo {0} steht. Tatsächlich könnte man überlegen, ob man die String-Zusammensetzung in der Status Line und damit in SQL oder in der Message und damit mit der iDempiere Template Syntax macht. Das ist im Grunde eine Geschmacksfrage, allerdings hat iDempiere einige Probleme mit dem Quoting von ' und ", weswegen es einfacher war, den Teil vorher in SQL zu machen.

! Drill Down

Funktioniert super. Am besten einen Blick auf das Kontextmenü werfen (also rechte Maustaste drücken), wenn man in einem Chart über einer Kategorie schwebt. Zum Beispiel im Pie-Chart (Kuchendiagramm) kann man so immer tiefer in die Daten schauen und in den Dimensionen immer weiter filtern. Gleichzeitig kann man in jeder Stufe die zugrundeliegenden Daten als Tabelle herauskopieren, so das man dann z.B. mit LibreOffice eine weitere Analyse machen könnte.

!Maps

Tatsächlich ist das ein super spannendes Thema, mit dem ich aber noch nicht wirklich viel experimentiert habe. Einerseits gibt es ein einfaches Mapbox-Chart, das Punkte per Geokoordinaten in eine Karte einzeichnen kann. Dazu benötigt man (weil das kommerzielle Mapbox das so verlangt) einen API-Key, der aber für einfache Anwendungen kostenlos zu bekommen ist. Dieser muss in docker/.env eingetragen werden, dann kann man das Chart nutzen.

Die richtig coolen Sachen gehen aber erst mit verschiedenen Charts, die auf deck.gl basieren. Man kann Mapbox als eine Ebene unterlegen, was dann Karten mit Strassen etc. bedeutet und dann per 2D- und 3D-Beschreibungen Dinge einzeichnen. Die Demos sehen sehr cool aus, eigene Erfahrungen habe ich noch nicht.

Scheinbar ist es nicht möglich, einen Punkt auf einer Karte anzuklicken und dann einen Crossfilter zu setzen. Eigentlich schade. Alles in allem scheint aber die deck.gl Einbindung relativ neu zu sein. Da tut sich vielleicht noch was.

Eine tolle Übersicht über Karten und wie man die in Superset nutzt, habe ich hier gefunden:
* https://openschoolmaps.ch/lehrmittel/einfuehrung_in_apache_superset/apache_superset_fuer_fortgeschrittene.pdf