Immer wieder habe ich zwischendrin mal eine Idee, was ich von meiner Plattenverwaltung noch gerne wissen möchte – so eine Art universelle Statistik. Zum Beispiel wissen, welche Platten noch kein Cover haben, wo noch kein Erscheinungsjahr hinterlegt ist usw. Völlig unabhängig davon entdeckte ich auf meiner virtuellen Maschine auf Arbeit einen Hinweis auf Microsoft Power BI. Wenn man das auflöst wird Business Intelligence daraus. In Kombination mit „Power“ klingt das richtig gruselig nach stundenlangen Besprechungen in den Fakten und Zahlen präsentiert werden. Fakt ist – genau dafür ist das Teil gedacht. Man verbindet sich mit einer Datenquelle, extrahiert die Daten – gruppiert, summiert und macht dann eine schicke Grafik daraus.

Also hab ich mich hingesetzt und der Power BI meine Plattenverwaltung zu futtern gegeben. Prima hat mir gefallen, dass man sofort das komplette Datenmodell sieht und die Beziehungen der Tabellen untereinander. Und genau entlang dieser Beziehungen kann man seine Abfragen aufbauen. Meine erste Frage an das System war: Wie oft höre ich Platten in Beziehung zu ihrem Erscheinungsdatum? Ich schnappte mir die Tabelle „PlayStats“, wo das Datum hinterlegt ist, wann eine Platte abgespielt wurde. Diese Tabelle verweist auf die Platte und diese wiederum auf die Veröffentlichung. Ich reduzierte das Abspieldatum auf Monat und Jahr, entfernte IDs und andere Daten, die nicht relevant sind und gruppierte dann nach dem Erscheinungsjahr, was eine neue Spalte hervorruft: Die Anzahl pro Monat und Erscheinungsjahr.

Jetzt kommt der optische Teil – bis jetzt starrt man nur auf langweilige Zahlenkolonnen, aber nachdem man die Daten fertig hat, kann man ähnlich wie in Excel Diagramme generieren und dabei live die Diagrammtypen ändern usw. Und fertig war mein erstes Diagramm: Eine Treemap, die zeigt, dass mehr als ein Drittel der Veröffentlichungen, die ich bisher (seit Einführung der Abspielfunktion) aus dem Jahr 2016 stammen und darunter – feiner aufgeteilt – die einzelnen Monate. Und auf die Schnelle noch ein zweites Diagramm: Anhand der Veröffentlichung sehen, wie sich die Verteilung des Mediums geändert hat.

Ich versuche mich ja – auch wenn ich einen iPod benutze – mich von den Statistiken und Informationen immer mehr unabhängig von iTunes bzw. von last.fm zu machen. Ein Feature, was ich an iTunes mag, ist die Information, wann welcher Titel gespielt wurde. So kann ich nachsehen, welche Scheibe ich lange nicht mehr gehört habe und mal wieder reinhören.

Mein Vorteil ist, dass ich in meiner Plattenverwaltung die Chartinformationen zurück bis 2004 vorliegen habe. Das heißt ich habe eine Datenbasis, die wesentlich umfangreicher ist, als die von iTunes. Also kann ich doch Nutzen daraus ziehen. Meine Herausforderung ist lediglich die, dass ich die Informationen in zwei Stufen vorliegen habe.

  1. Ich habe seit Oktober meine Charts, wo ich mir in einer separaten Tabelle zu jedem Tonträger das Datum gemerkt habe, an dem die Platte zuletzt gespielt wurde. Ganz einfache Abfrage – alle Tonträger ermitteln und dann nachschauen, wann das letzte Datum zu der jeweiligen Platte ist.
  2. Meine Jahrescharts – dort liegen die Informationen pro Tonträger, wie oft ich die Platte gespielt habe. Wie gesagt, diese Informationen reichen bis in das Jahr 2004 zurück. Auch hier ist die Abfrage einfach: Pro Tonträger den letzten Jahreseintrag heraussuchen ggf. in Betracht ziehen. Einziger Knackpunkt hier ist, dass ich beim Einlesen vielleicht den einen oder anderen Tonträger nicht bei den Charts zuordnen konnte und somit kein Eintrag in meiner SQL-Datenbank entstanden ist. Das hat zur Folge, dass die Notstufe greifen muss.
  3. Die Ausnahme: Die Platte muss vor 2004 das letzte Mal abgespielt worden sein oder ich habe keinen Eintrag gefunden. Hier ist auch Platz für potenziellen Unfug: Der Tonträger ist z.B. 2008 erschienen, ich konnte ihn nicht zuordnen und deshalb steht als letztes Abspieldatum 2003 drin. Gibt es aber zum Glück nicht.

Kaum hatte ich das Feature implementiert und starte das erste Mal, musste ich zwei Erkenntnisse hinnehmen. Zum einen das erwartete „Stimmt, die hast du lange nicht mehr gehört“ und aber auch „Ich dachte, die Platte hätte ich verkauft?!“.

Heute geht es um Zweige und Quellcodeverwaltung. Der mitlesende Entwickler wird sich jetzt entspannt zurück lehnen und Bescheid wissen. Aber vielleicht ist es auch für Nicht-Entwickler mal interessant, wie so ein Entwickler es schafft, stabile Programme zu schreiben. Fangen wir mal mit der Quellcodeverwaltung an.

Falls uns mal die Bude abfackelt, lieben wir Entwickler es, wenn wir unseren Quellcode irgendwo ablegen können. Mit „abfackeln“ meine ich das weniger im wörtlichen Sinne, sondern auch wenn wir merken, dass wir uns beim Entwickeln verrannt haben und zu einem sicheren Punkt zurück kehren müssen, wo wir wussten, dass die Version noch lief. Was passiert dann in so einer Quellcodeverwaltung? Es gibt immer das Original auf dem Server und eine lokale Kopie. Sobald wir die lokale Kopie verändern, wird das von der Quellcodeverwaltung registriert und die Datei wird als „In Bearbeitung“ markiert. Ist man fertig mit seinen Änderungen, übergibt man der Quellcodeverwaltung seine Änderungen, wir sprechen dabei vom „Einchecken“. Oder man stellt fest, dass der Ansatz schlecht war und verwirft seine Änderungen und stellt den Originalzustand wieder her.

Wenn das schon zu kompliziert war, kommt jetzt der Teil, wo ihr komplett aussteigt. Stellt euch vor, dass viele Entwickler an dem Quellcode arbeiten, dass es immer möglich sein muss, stabile Versionen auszuliefern und trotzdem neue Features dazu kommen sollen. Schauen wir doch also mal, wie ein Feature in eine Version kommt…

Auf der Grafik sieht man, dass parallel mehrere Versionsstränge existieren, als Entwickler reden wir von sogenannten Zweigen oder Branches. Wenn mir also eine neue Idee kommt, wie z.B. den Musikstil zu einer Platte zu hinterlegen, dann lege ich einen neuen Zweig an. Jetzt beginne ich in kleinen Häppchen das neue Feature zu entwickeln. Datenbankänderung, Schnittstelle zum Lesen / Schreiben erweitern, Daten lesen / schreiben, Oberfläche erweitern und alles verknüpfen – jede von diesen Aktionen ist unabhängig von einander und kann somit einzeln eingecheckt werden (das sind die kleinen lila Punkte).

branches

Wenn mir jetzt mittendrin noch ein neues Feature einfällt, dann erzeuge ich einen neuen Zweig, der auch auf der Develop-Version basiert. Da mache ich auch wieder ein paar Änderungen. Und beide Features werden unabhängig von einander entwickelt und ich kann sie beliebig lange offen halten, ohne irgend etwas zu gefährden. Jetzt bin ich mit dem zweiten Feature zuerst fertig – kein Problem, es wird in die Develop-Version übernommen und kann jetzt nochmal auf Herz und Nieren getestet werden.

Ich mache es immer so: Wenn ich ein neues Feature entwickle und teste, starte ich die Plattenverwaltung immer aus der Entwicklungsumgebung heraus. Sollte ein Absturz auftreten, bleibe ich sofort an der entsprechenden Stelle im Quellcode hängen und kann sie korrigieren. Läuft das neue Feature stabil, baue ich eine Version und arbeite ganz normal damit. Knallt es jetzt nochmal, dann ziehe ich das Verhalten in der Entwicklungsumgebung nach und korrigiere den Fehler in der Develop-Version. Ist dann auch hier alles in Ordnung, kommt jetzt das Feature von der Develop-Version in die Master-Version. Hier kommt wirklich nur das stabile Zeug rein. Dort wird auch nichts mehr geändert.

Was, wenn jetzt das nächste Feature fertig wird? Unter Umständen habe ich durch die zwei Features Änderungen an den selben Stellen im Quellcode gemacht und die passen nicht mehr zusammen. Damit würde der Develop-Zweig instabil werden. Ganz schlecht, denn hier sollen nur kleine Bugs gefixt werden. Also passiert was, dass wir „Forward Integration“ nennen. Dafür gibt es kein schönes deutsches Wort. Es bedeutet, dass ich den Stand der Develop-Version (also mit Feature 2) in die Feature-Version übernehme. Jetzt wird alles korrigiert und stabilisiert, bis es passt und dann kann auch das neue Feature in die Develop-Version.

Ist ein Feature abgeschlossen, wird der entsprechende Zweig gelöscht, sobald er in der Develop-Version aufgegangen ist. Nur die Develop- und die Master-Zweige bleiben bestehen.

Auslöser – so könnte man sagen – ist das Diagramm von XKCD. Es zeigt, wann es sich lohnt, etwas zu automatisieren und wie viel Zeit man investieren sollte. Ich habe mal so als Richtwert 10 Minuten pro Monat genommen, die ich in die Eingabe meiner neuen Platten investiere. Laut Diagramm hätte ich also 10 Stunden investieren können, um eine automatisierte Anpassung vorzunehmen.

Da ich meine Musikdateien ohnehin nur noch in digitaler Form erwerbe, war mein Ansatz, dass ich eine Programmerweiterung schreibe, die über die Dateien drüber geht, die Tags aus der Datei ausliest und für die Plattenverwaltung erfasst. Also hab ich erstmal nach einer sinnvollen Bibliothek gesucht, die mir eine gute Funktionalität bietet und bin auf TagLib gestoßen. Was jetzt nur noch gemacht werden musste, war heraus zu finden, wie man an die gewünschten Daten kommt, dass entsprechende Verzeichnis auszuwählen, die Dateien ein zu lesen und die passende Datenstruktur zu füllen.

Nach knapp 3 Stunden hatte ich das Ergebnis in der Hand – alle Titel waren da und auch ein paar Daten bezüglich des gesamten Tonträgers. Ich freue mich schon, wenn ich jetzt eine Compilation kaufe, die 30 Titel hat und statt 10 Minuten verzweifelt Copy & Paste zu spielen, ist die Liste in 5 Sekunden eingelesen.

Klick auf den Button, Verzeichnis suchen und schon sind 13 Titel eingelesen

Klick auf den Button, Verzeichnis suchen und schon sind 13 Titel eingelesen

chartsVon Zeit zu Zeit kam ich dazu, noch etwas Feintuning an meiner Statistik vorzunehmen. Es kamen viele Kleinigkeiten zusammen…

  • Zum einen stellte ich von einem einfachen Liniendiagramm auf ein Flächendiagramm um. Sieht schon mal schöner aus.
  • Nachdem ich eine Compilation mit 100 Titeln abspielte und noch 4-5 Alben kam ich auf eine Abspielzeit von 36 Stunden. Das sieht in einer Tagesstatistik schon ziemlich merkwürdig aus. Also ersetzte ich die Tagesstatistik mit einer Jahresstatistik. Die ist zwar noch nicht aussagekräftig, aber wenn das Programm jetzt wieder 10+ Jahre läuft, dann kommt schon was dabei rum.
  • Dritter und letzter Punkt ist die Achsenbeschriftung. Lange hab ich damit herumgekämpft, jetzt habe ich es geschafft eine schöne Formatierung einzustellen, die sich entsprechend des eingestellten Wertes verhält.

Als ich letztens meine Historie zu meiner Plattenverwaltung ansah, kam mir die Idee eines „About“-Dialogs. Worum geht es denn eigentlich bei dem Programm und wie ist es gewachsen. Eigentlich ist es ja schon irgendwie unsinnig, da ich sowieso der Einzige bin, der das Programm nutzt. Aber trotzdem wollte ich etwas Schickes einbauen. Initial ging mir so ein einfacher Scrolldialog durch
den Kopf, aber dann musste ich unwillkürlich an Star Wars denken. Und schon war die Idee geboren. Logisch, dass ich nicht der Erste bin, der sowas in WPF baut, deswegen habe ich gleich mehrere Lösungen gefunden. Ich verglich ein paar Versionen miteinander, entschied mich für eine Lösung und musste noch etwas an den Parametern drehen, da mein Text doch etwas länger war, wie sich der Entwickler das ursprünglich gedacht hatte. Außerdem parametrisierte ich noch die Texte und schon war er fertig – mein „About“-Dialog.

Jetzt sind einige Wochen vergangen und meine Plattenverwaltung konnte ein paar Daten sammeln. Damit konnte ich mich an die Auswertung der Daten machen. Als erstes suchte ich mir eine Bibliothek zum Zeichnen von Diagrammen und wurde relativ schnell auf OxyPlot aufmerksam. Ich lud mir das Package runter, band es ein und baute ein Control, was den Beispielcode ausführte. Klappte wunderbar und sehr einfach.

Es konnte der nächste Schritt folgen: Das Auslesen der Daten. Schon im Vorfeld hatte ich mir überlegt, was ich gerne sehen möchte. Ich zitiere:

Ich könnte auch eine Grafik erstellen, wo ich den Zeitraum einstellen kann (Tag, Woche, Monat) und dann den zeitlichen Verlauf der abgespielten Tonträger / Titel.

Also warum nicht das alles? Ich begann aber mit dem einfachsten Fall – die Anzahl der Tonträger pro Tag. Die Daten waren schnell da und nun stolperte ich über das erste Problem. Wie aktualisiere ich das bereits angezeigt Diagramm. Nur neue Daten unterschieben reicht offensichtlich nicht. Nein, das Control möchte auch nochmal explizit einen Aufruf, dass es sich aktualisieren soll, auch wenn das darunter liegende Model ein ChangeEvent auslöst. Ganz unsauber! Zumindest konnte ich es reibungsfrei in meine Architektur integrieren und schon tauchte das erste Diagramm auf.

Ich experimentierte noch mit anderen Diagrammtypen und stieß noch auf weitere Unschärfen (Das ViewModel muss die Farbe des Graphen definieren?). Aber das ließ ich erstmal außen vor. Jetzt wurden noch die anderen Darstellungsarten eingebaut (pro Woche, pro Monat) und anschließend die Skalierung entsprechend angepasst, da ja pro Monat mehr Platten gespielt werden als pro Tag.

Wie man sieht, sieht es schon ganz ansprechend aus. Ob ich jetzt nochmal anfange und eine neue Bibliothek suche oder über eine Erweiterung nachdenke, weiß ich noch nicht. Denn momentan hätte ich gerne noch zusätzlich die gespielte Zeitdauer…

ausschnittNachdem Apple beschlossen hat ihren iPod classic in die Schrottecke zu stellen und keinen Nachfolger außer ihre Touch-Serie in die Spur zu schicken, wird bei mir wohl über kurz oder lang ein anderer Player den iPod verdrängen. Und wenn der iPod erstmal weg ist, bröckelt das iTunes mit ihm weg und auch das Scrobbeln der Titel, die ich mit dem iPod abgespielt habe. Der gute alte WinAmp wird wieder ausgegraben und dafür sorgen, dass ich weiter Musik hören kann.

Aber als Musikliebhaber stehe ich auf meine Statistiken. Was liegt da näher, wie eine eigene Statistik in die Plattenverwaltung einzubauen. In der Quellcodeverwaltung ist schon der Zweig für das neue Feature gezogen und die Umsetzung erfolgt in zwei Schritten.

1. Daten speichern

Bisher merke ich mir die Anzahl, wie oft ein Tonträger abgespielt wurde. Und zwar auf zwei Wegen. Einmal für das aktuelle Jahr und einmal insgesamt. Für eine Statistik wird das aber nicht ausreichen. Deswegen wird in eine neue Tabelle aufgenommen, zu welchem Datum ein Tonträger abgespielt wurde. Und zwar wird jedes Mal ein neuer Datensatz erzeugt, wenn ich abspiele. Bisher war es ja so, dass ich nur das Datum des letzten Laufes aktualisiert habe. Wenn das erste Feature umgesetzt ist, lasse ich das erstmal 1-2 Monate laufen, um genügend Daten zu haben. Danach folgt Phase 2!

2. Auswertung

Jetzt kann ich eine Maske erstellen, wo ich einstellen kann, welches Zeitfenster ich betrachten will und was ich sehen will: abgespielte Tonträger oder abgespielte Titel. Ich könnte auch eine Grafik erstellen, wo ich den Zeitraum einstellen kann (Tag, Woche, Monat) und dann den zeitlichen Verlauf der abgespielten Tonträger / Titel. Aber da wird noch etwas Zeit ins Land gehen.

Was mich immer wieder an meiner Plattenverwaltung gestört hat ist, dass sie nicht portabel ist. D.h. um was an einem anderen Rechner auszuprobieren, müssen folgende Kriterien erfüllt sein:

  • (von Runtimes und .NET-Frameworks rede ich mal nicht)
  • SQL-Datenbank muss installiert sein
  • Die Coverbilder müssen genau unter dem Pfad zu finden sein

Letzteres war diese Woche der Fall, dass mein Programm schlicht und ergreifend abstürzte. Ein kurzes Debugging zeigte, dass der Versuch, eine nicht vorhandene Bilddatei zu laden, zu einer Exception führte und dann beendete sich das Programm unschön.

Der Bugfix war schnell gemacht, aber das Problem war ja eigentlich, dass ich die Bilder nicht – oder nur schlecht – immer mitnehmen kann. Lösungsmöglichkeiten gibt es da ja viele. Entweder ich lebe damit, kein Bild zu sehen oder ich mache die Datenbank beweglich und schiebe die Bilder in die Datenbank. Die Datenbank mobil zu machen oder online zu haben, nennt man in diesen Zeiten „in der Cloud“.

Ich habe mich also mal bei Microsoft umgesehen, die mit Azure jegliche Art von Cloudaktivität anbieten, unter anderem auch DaaS (Database as a Service). Nur das Bezahlkonzept blieb mir etwas verschleiert, weil einmal hieß es ca. 5 Euro im Monat für die kleinste Stufe, auf der anderen Seite gab es transaktionsbasiertes Bezahlen. Aber um dahin zu kommen, brauch ich erstmal den ersten Schritt: Das Cover in der Datenbank speichern.

Also fing ich an, das Datenmodell anzupassen. Danach suchte ich nach Wegen, wie ich das Bild im passenden Format geladen und gespeichert bekomme. Danach nur noch von der Oberfläche bis zum Datenbankzugriff die Daten durchreichen (ich musste von dem einfachen Pfad auf ein Objekt umstellen) und schon war eine neue Version fertig.

pv45

Ziemlich lange blieb ich noch an einem Bug hängen, warum das Bild bei Tonträgern, die aus mehreren Platten bestehen und wo jeder Tonträger sein eigenes Cover hat, nicht geändert wurde. Letztendlich fand ich den Schlingel und nun arbeite ich wieder stabil. Momentan arbeite ich noch mit den Backup, d.h. wenn ich das Bild nicht in der Datenbank finde, dann lade ich es vom hinterlegten Pfad, aber das werde ich über kurz oder lang auch entfernen, damit ich die entsprechenden Bilder in die Datenbank bekomme.

Mit meiner letzten Version habe ich es ermöglicht, dass ich die Charts nicht nur eines Jahres anzeigen kann. Dabei fiel mir auf, dass ich seit 2004 meine Charts bei Jahreswechsel auf die Platte gedumpt habe. Dort standen dann mit Komma getrennt, Interpret, Titel und die Anzahl, wie oft die Platte gespielt wurde, drin. Und da wurde mir klar, dass man beides gut miteinander kombinieren kann.

Also begann der lange, steinige Weg zur Version 4.4. Erstmal habe ich mein Projekt, dass bei BitBucket gehostet wird, auf den Git Flow umgestellt. Vorher war das eher Kraut und Rüben. Da ich ganz allein an dem Projekt arbeite ist, stellt mir meine Quellcode-Verwaltung das Branching Model als Linie dar, da nie Änderungen auf mehreren Zweigen erfolgen.

Ich begann ganz zaghaft mit einer Oberfläche, dann habe ich das Einlesen und Zerlegen der alten Charts-Dateien in Angriff genommen und diese dann über die Datenbankschnittstelle finden lassen. Dabei entstehen zwei Töpfchen: gefunden und nicht gefunden. „Nicht gefunden“ kann heißen, dass ich die Bezeichnung geändert / verbessert habe oder die Platte verkauft wurde. Also gab es zwei Optionen: „Verwerfen“ – das ging schnell und „Zuordnen“ – das hat den Großteil der Zeit gekostet.

Für das Zuordnen musste ich erstmal jede Menge Komponenten, die ich schon für die „normale“ Plattenverwaltung gebraucht habe und eine gemeinsam genutzte Komponente umziehen, Aufrufe korrigieren und dann konnte das Zuordnen beginnen. Nächste Hürde: Bisher hatte ich nie das permanente Wechseln zwischen zwei Screen (Übersicht und Suchmaske) in meinem Prozess. Die Datenbank rebellierte etwas, weil die Verbindung bereits geöffnet war oder der Garbage Collector sie mittendrin mal wegräumte.

Heute kam dann der letzte und entscheidende Schritt: Ich hatte alles soweit fertig, las die Datei ein, ordnete zu und lies mir nochmal die eingelesene Zeile und das zugeordnete Datenbankobjekt rausdumpen. Und irgendwie passte bei den manuell zugeordneten Sätzen zu Zuordnung nicht hin. Ursache war schnell gefunden: Die Charts basieren auf den einzelnen physischen Tonträger, die Suche für die Zuordnung aber auf den Veröffentlichungen. Klingt jetzt komisch, oder? Beispiel: Doppel-CD – die besteht aus zwei CDs, ist aber eine Veröffentlichung. Beides habe ich jetzt verwechselt. Jetzt musste ich nur schnell das komplette Programm mal kurz umbauen, damit sie Suche mit beiden Kriterien arbeiten kann und schon funktionierte alles.

chartseinlesen