Sammeln und kategorisieren von Weblinks in Orgmode
Tagwood ForestOb sie nun Lesezeichen, Favoriten oder Weblinks heissen. jeder von uns sammelt im Laufe der Zeit eine ganze Menge von ihnen an. Irgendwann kommt der Tag wo das ganze unübersichtlich wird. und aufgeräumt werden muss. Ich hab mir ein Python Tool dafyr geschrieben.
Was bisher geschah
Neu Weblinks entstehen bei mir immer aus meinem Browser heraus. Sei es der Firefox auf Desktop und Laptop oder auf meinem Handy. Ich könnte sie also einfach nur innerhalb von Firefox lassen. Damit kann ich sie zwischen den Browsern synchronisieren, und sie aufrufen. Was will man mehr? Nun ich habe in diesem Blog z.B eine Seite mit Links, die ich gerne öffentlich teilen möchte. Wäre doch schön, wenn ich das nicht getrennt machen müsste. Und so hab ich mich schon vor Jahren entschlossen keine der eingebauten. oder online verfügbaren Tools zu nutzen, um Weblinks zu verwalten. Stattdessen speichere ich alle meine Weblinks als Plaintext im Orgmode Format. Jeder Link ist dort eine Überschrift mit dem Titel der Seite die mit der entsprechenden Seite verlinkt ist. Falls ich Lust habe, schreibe ich mir noch eine kleine Anmerkung dazu, oder füge ein Zitat aus der Seite ein. Wenn ich auf Laptop oder Desktop arbeite erledigt das ein kleines AddOn (Org Capture) welches neue Links an meine Links.org Datei dranhängt. Dazu benutzt es das org-capture Protokoll das Emacs für Links zur Verfügung stellt. Auf dem Handy funktioniert dieses AddOn leider nicht, daher teile ich die Webseite dort einfach mit der Orgzly-revived App. mit der sich Orgmode Dateien auf Android bearbeiten lassen. Der Link landet dann ebenfalls in der Links.org Datei. Ich synchronisiere mein gesamtes org Verzeichnii zwischen allen meinen Geräten.
Die Links.org Datei im Detail
Zunächst einmal beginnt diese Datei mit einem Satz von Metadaten-Zeilen, die mein Webseiten Generator braucht, um aus der Datei direkt die Linkseite zu erstellen, die man hier im Blog sieht. Das sieht dann so aus
#+BEGIN_COMMENT .. title: Links .. description: All the links I can recommend .. slug: links .. date: 2019-01-01 00:00:00 UTC+01:00 .. tags: links, emacs, orgmode, qutebrowser, plaintext, pim .. previewimage: qrcodebus.png #+END_COMMENT #+OPTIONS: tags:nil
Die letzte Zeile sorgt dafür, dass die Tags, die ich den einzelnen Links gebe nicht nach HTML exportiert werden
In dieser Datei sind alle neuen Links zunächst mal eine Überschrifft der Stufe 2. In der obesten Stufe 1 gibt es genau vier Überschriften:
1. Quicklinks
Wie man sieht sind diese Links mit :noexport:
getaggt, und tauchen daher nicht in der öffentlichen Linkliste auf. Es sind Links. die ich selbst häüfig aufrufe. Deher stehen sie in der Datei ganz oben. und sind bereits sichtbar, wenn ich die Datei öffne
* ------ Quick Links ----- :noexport: :PROPERTIES: :VISIBILITY: children :END:
Social Media | Shopping | News | Finanzen | Haustechnik | Konten | Other |
---|---|---|---|---|---|---|
Mastodon | Idealo | Tagesschau | Volksbank | FritzBox | BibLoad | Thingiverse |
Feddit | Amazon | The Guardian | Barclays | Wechselrichter | Stadtbücherei | Cults |
eBay | Schw. Hall | Stromzähler | ||||
Codeberg | Otto | Wetter WAF | Paypal | Home Assistant | Oryx | |
Wallabag | Kaufland | Stat. Bundesamt | CUPS | |||
Dashboard DE | Syncthing | |||||
ChatGPT | Statista | Jenkins | ||||
Wiki | ||||||
2. Öffentliche Links
Dies sind alle Links, die im Blog sichtbar sind. Aus den Hierarchie-Ebenen generiert mein Layout mit ein wenig CSS-Magie dann eine dynamische Liste. Mein Python Tool lässt alles bis zum Ende dieses Abschnitts in Ruhe.
3. Private Links
Hier wird es nun interessant. Heute (2025-03-04) befinden sich hier etwa 450 Links. Diese Links sind mit bis zu vier Tags versehen, und wurden bisher einigermassen sortiert und kategoriesiert gehalten. Wobei man schon an das "einigermassen" nur niedrige Massstäbe anlegen darf. Ach wen lüg ich da an - es war ein Dumpster-Fire.
4. Die INBOX
Im Prinzip das gleiche Durcheinander, wie bei den privaten Links, nur, dass ich hier noch nicht einmal Tags vergeben habe.
Es sind also die Abschnitte 3 und 4 um die sich mein Tool kümmern soll,
Was ist das Ziel des Tools?
Es soll sich alle Weblinks aus den Abschnitten 3 und 4 schnappen, und sie anhand ihrer Tags in eine logische Baumstruktur bringen. Die Fallstricke im letzten Satz befinden sich in den Wörtern eine und logische.
Gibt es denn mehrere Bäume?
Natürlich alles andere wära ja auch zu einfach. Tags sind ihrer Natur entsprechend alle gleichwertig. Sie enthalten keine Information über eine Hierarchie. Mal angenommen ich habe den folgenden Link mit den Tags a and b:
* Link :a:b:
Dann kann ich daraus schon 2 mögliche Bäume bauen
├── a │ └── b │ └── Link └── b └── a └── Link
Sobald ich 3 Tags habe. kann ich das zusätzlich Tag in 3 möglichen Ebenen einfügen - in jeden der beiden Bäume - und erhalte damit schon 6 mögliche Bäume. die mathemathische Darstellung wäre 3!
(sprich: 3 zur Fakultät) Falls jemand noch exponentielles Wachstum aus der Covid Zeit kennt. Das ist gegen fakutatives Wachstum auf Kindergarten Nivau. Nehmen wir 6 Tags sind wir bereits bei 720 möglichen Bäumen und nur 8 Tags reichen für 40320 Bäume.
Und was mach ich nun mit den ganzen Bäumen
Der gängige Ansatz wäre, sie alle zu nutzen, Dann taucht ein Link mit 4 Tags eben 24 Mal in der Linkliste auf, aber es gibt eben auch 24 Wege ihn zu finden. Grob Überschlagen würde das bei meinen 450 Links etwa 3000 Links in der Ausgabedatei ergeben. Und das ist mir zu viel. Eine Menge der Pfade ergeben dabei nicht wirklich einen Sinn. Mal angenommen ich verlinke den Ort, an dem ich mein Auto zuletzt geparkt habe:
* Auto :germany:munic:mainstreet:
Dann denke ich ja nicht "Hmm ich weiss noch, das ich in der Mainstreet geparkt habe, also zeig mir mal alle Länder in denen es Städte mit einer Mainstreet gibt. damit ich mir daraus die passende aussuchen kann"
Also muss ich diejengen Bäume abholzen die keinen Sinn ergeben. Hier kommt das zweite entscheidende Wort hinzu. Es soll eine logische Baumstruktur werden. Aber nicht erst seit Trump sollte klar sein. das es die seltsamsten Varianten von Logik gibt. und da ist alternative Logik noch nicht mal berücksichtigt. Und bei der Suche noch etwas sinnvollen sind Computer nicht wirklich gut. Also behelfe ich mir mit einem anderen Kriterium, der Häufigkeit. Zählen können Computer nämlich recht gut.
Wenn ich in meiner Datei 200 mal das Tag emacs habe aber nur 100 mal das Tag orgmode, Dann sieht der Baum so aus
└── emacs └── orgmode └── Link
Das klappt dann schon ganz gut, Aber hin und wieder gibt es noch ein paar seltsame Hierarchien, besonders wenn es sich um Tags handelt die insgesamt selten genutzt werden. da kommt es dan auf eine Nutzung mehr oder weniger schon an, wenn es um die Sortierung geht
Und nun mogle ich auch noch
In den meisten Fällen kommt es bei den seltenen Tags sowieso nicht darauf an. Aber wenn mir das mit großen (häufigen) Tags passiert ist dass schon ärgerlich. Und deshalb mogle ich ein wenig. Um mal be dem Auto Beispiel zu bleiben - das würde ich in Wirklichkeit so schreiben:
* Auto :Germany:Munic:mainstreet:
Siehst du den Unterschied? Die ersten beiden Tags sind nun gross geschrieben. Für mein Tool ist das der Hinweis, dass Land und Ort hierarchisch wichtiger sind als die Straße. Es gibt damit immer noch 2 mögliche Bäume, aber 4 Bäume sind damit sicher verhindert. Ich kann damit nur ein zwingende Hierarchiestufe bestimmen, aber damit die Zweifelsfälle, die Auftreten könnten, ganz brauchbar abfangen.
Warum ist das gemogelt? Nun Tags tragen laut Lehrbuch nun mal keine hierarchische Information, und mein Pogrammiererherz fühlt sich auch ein wenig schmutzig an. Nun aber zum technischen Teil, falls das jemand in einer anderen Sprache nachbauen möchte:
Der Algorithmus
- Als allerstes sammle ich alle Links ein. die ich verarbeiten möchte und werfe sie in ein Dictionary. Der Key ist dabei ein Hashwert des eigentlichen Links. Damit elimimiere ich bereits versehentliche doppelte Links.
- Dann werden alle Permutationen der Tags gebildet. Dabei werden alle Permutationen. die laut meiner gemogelter Tag-Hierarchie verboten sind ausgelassen.
- Die Liste aller Links werfe ich nun in die eigentlich Funktion. die den Baum aufbaut. Weil es bei Bäumen einfach unglaublich praktisch ist, ist diese Funktion rekursiv. Das bedeutet sie erledigt die Aufgaben für ihre Ebene der Baumstruktur, und ruft sich dann für die tieferliegende Hierarchieebene einfach sebst auf. Jede Rekursion braucht eine Abbruchbedingung. Hier ist das "Es gibt keine weiteren Links die noch nicht an einem Tag hängen".
Was passiert in dieser Funktion nun?
- Der eigentlich Tag wird als Knoten in den Baum eingefügt.
- Zunächst einmal werden alle Links in den Baum gehängt, deren Tags exakt an diese Stelle im Baum passen, An Ebene 2 z.B also nur Links mit exakt 2 Tags die genau den Tags aus Ebene 1 und 2 entsprechen müssen
- Wenn das erledigt ist, und noch Links übrig sind geht es ins Voting.
- Hier geben alle Links Stimmen dafür ab welchen Tag sie in dieser Ebene des (Teil-)baums noch gebrauchen können.
- Dar Tag mit den meisten Stimmen gewinnt
- Es wird eine Liste aller Links erstellt. die für diesen Tag gevoted haben
- Mit dieser Liste und den Namen des Gewinner-Tags ruft sich die Funktion selbst auf, und baut somit den kompletten Baum unterhalb auf.
- Die Liste wird von den ursprüglichen Links abgezogen, und sollten dann noch Links übrig sein, geht es in eine weitere Abstimmungsrunde. Andernfalls ist die Ebene fertig und die Funktion wird beendet
Am Ende wird der ganze Baum als Orgmode Struktur wieder in die Datei geschrieben und fertig ist die Laube.
Das Ergebnis
In der Ausgabedatei sind die Tags nun hierarchisch und sortiert
- Die häufigsten Tags kommen zuerst
- Bei gleicher Häufigkeit alphabetisch
- Alle Links sind im Baum
- kein Link ist doppelt
Und ich habe damit bereits einen Link-Baum mit dem ich gut arbeiten kann. Natürlich entsteht damit niemals der ideala Baum der mit dem übereinstimmt. wie ich das per Han sortieren würde aber ich bin schon recht nah dran. Vor allem gibt es in dem Algoritmus noch reichlich Potential für Optimierungen.
Das Voting tweaken
Wer sich für Wahlrecht interresiert wird festgesteiit haben, dass es dutzende, wenn nicht hunderte verschiedene Wahlverfahren gibt. die alle zu leicht unterschiedlichen Ergebnissen führen,und auf verschiedene Ziele (feste Sitzanzahl, Gewichtung nach Aktienanteil, Minimierung der zu reitenden Tage bis zur Hauptstadt) optimiert sind.
Das gilt auch für das Voting in diesem Algorithmus. Aktuell voted bei mir jeder Link für jeden Tag der ihm irgendwie nutzt mit einer Stimme. Ebenso wären aber auch andere Varianten denkbar:
- Wenn ein Link 3 mögliche Bäume für Tag A sieht und 2 mögliche für Tag B, könnte er mit 3 bzw. 2 Stimmen für die jeweiligen Tags stimmen.
- Die Votes könnten danach gewichtet werden ob ein Tag unmittelbar in dieser Ebene nötig ist, oder möglicherweise erst tiefer im Baum.
- etc.
Alles in allem bin ich mit dem Ergebniss aber schon sehr zufrieden. Mein Repo mit dem aktuellen Stand des Tools befindet sich hier