portfolio-performance / portfolio

Track and evaluate the performance of your investment portfolio across stocks, cryptocurrencies, and other assets.
http://www.portfolio-performance.info
Eclipse Public License 1.0
2.84k stars 588 forks source link

Definition eines PDF Extractor in JSON #1195

Closed buchen closed 4 years ago

buchen commented 5 years ago

Ziel

Definition in PDF Extraktoren (meist ja eine Sammlung von Regulären Ausdrücken) in einer JSON Datei die dynamisch geladen wird. Diese JSON Datein können von Benutzers einfacher editiert werden als Java Code zu schreiben.

Siehe Idee von @Ragas13 in #1184

buchen commented 5 years ago

Ich habe einen ersten Versuch in eine separaten Branch gepusht.

So könnte eine JSON Definition aussehen: https://github.com/buchen/portfolio/blob/99361be072e3f2c830be1ad55c3000b798c93d55/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/deutsche-bank-purchase.json

Es fehlt noch einiges, aber die Kaufbuchungen von maxblue kann ich so einlesen. In einer ersten Version können wir die User einfach die JSON Datei editieren lassen (wer sich mit regulären Ausdrücken auskennt, kann auch JSON editieren) - später könnte man einen Editor für JPDFExtractorDefinition bauen.

Das JSON wird in der Klasse JSONPDFExtractor eingelesen und in reguläre Ausdrücke umgewandelt. Es werden noch keine Werte validiert. Es werden nur Kauf/Buchungen unterstützt.

ghost commented 5 years ago

Ergänzend hierzu, da die Aktualität ja immer gewünscht wird, die Verteilung an alle Installationen könnte mE außerhalb der regulären Updates realisierbar sein. Die naheliegendste Lösung wäre die jeweiligen Filter über dein repository zu pushen und im Client die Dateien aus der GitHub get-tree API selektiv zu synchronisieren.

Die GitHub API hat derzeit ein Limit von 100.000 Dateien und Verzeichnissen. Für den Sync bietet sich der Job an, der die Kurse synchronisiert. Der Rest API Link für dieses repository wäre wie folgt:

buchen commented 5 years ago

Keine Ahnung was das rate limit auf das Github API ist, aber insgesamt sollten die Definitionen nicht sonderlich groß sein. Wenn wir ein separates Repository anlegen und dann automatisch den master zippen, ist diese ZIP Datei vermutlich so klein, dass Clients die jedesmal laden können.

Ich frage mich noch wie wir Testfälle einsammeln. Denn ohne Beispieldateien befürchte ich wird die Qualität leiden.

ghost commented 5 years ago

Ich frage mich noch wie wir Testfälle einsammeln. Denn ohne Beispieldateien befürchte ich wird die Qualität leiden.

Lassen sich hierfür nicht die vorhandenen Beispiele verwenden, die PP bereits im Repository kennt? Die vorhandenen Prüfungen kennen ja bereits das Soll-Ergebnis der Prüfung.

Auf der anderen Seite, wenn Anwender selbst JSON Konfigurationen f. den PDF Import schreiben können, kann nur auf Plausibilität und nicht auf reale Werte geprüft werden. Und, wenn neue JSON's ausgerollt werden sollen, lassen sich ja die Überprüfungen beliebig erweitern.

Mittelfristig kann man dann überlegen die existierenden Importer auf JSON umzustellen und die Überprüfung anzupassen, dass Ergebnis sollte ja unverändert bleiben :thinking:

lenucksi commented 5 years ago

Ansatz

Prima Ansatz. So kann man die einfachen Fälle, (und vielleicht auch CSV-Parser-Konfigurationen) und mit wenig Aufwand umsetzen und aktuell halten. :+1: dazu. Kompliziertere Ansätze die über Mapping und RegEx-Matching hinausgehen wären dann ja super für die interne Skripting-Engine oder im schlimmsten Fall die externen Extraktoren die was auch immer benutzen.

Updating der JSON-Konfigurationen

Testen

buchen commented 5 years ago

Siehe Commit:

Erwartete Test-Ergebnisse können als JSON Datei abgelegt werden (gleicher Dateiname, JSON als Dateiendung) und das Ergebnis des PDF Extraktors wird mit dem erwarteten Buchungen verglichen

buchen commented 5 years ago

Sofern wir öffentlich verfügbare Beispiel-PDFs oder CSVs haben könnte man diese mit Travis&Co nutzen um Pull Requests für Änderungen an den JSON-Konfigurationen oder Importer-Skripten auf ihre Plausibilität / Regressionen zu prüfen.

Aktuell liegen folgende Dateien vor:

...portfolio/.../pdf/deutsche-bank-purchase.json ...portfolio.tests/.../pdf/deutsche-bank/DeutscheBankKauf.txt ...portfolio.tests/.../pdf/deutsche-bank/DeutscheBankKauf.json

Eine ähnliche Struktur schwebt mir für das separate Github Repository vor. Dann kann man auch weitere Testfälle per PR hinzufügen. CI/CD würde dann ein ZIP bauen und als Release bei Github anhängen. Von da lädt PP die Konfigurationen.

Die Textdateien haben den Vorteil, dass man relativ einfach personenbezogenen Daten wie Name und Kontonummer entfernen kann. Man könnte aber auch PDF unterstützen (allerdings habe ich die Erfahrung, wenn man das PDF editiert, kann sich auch die Struktur ändern wie es in Text umgewandelt wird).

Lassen sich hierfür nicht die vorhandenen Beispiele verwenden, die PP bereits im Repository kennt? Die vorhandenen Prüfungen kennen ja bereits das Soll-Ergebnis der Prüfung.

Die Testfälle sollten auf jeden Fall nicht verloren gehen. Mir ging es aber darum, dass wir auch neue Testfälle bekommen. Ansonsten könnte es sein, dass die Importer sehr schnell "kaputt" gehen nur weil man sie auf das 2019 Format der Bank angepasst hat und 2018 gar nicht mehr testet.

Die GitHub API hat derzeit ein Limit von 100.000 Dateien und Verzeichnissen. Für den Sync bietet sich der Job an, der die Kurse synchronisiert.

Das ist eine interessant Option. Ich dachte zunächst an ein ZIP unter den "Releases" (durch die CI/CD pipeline / Github Action gebaut). Was ich mich aber frage ist wievielt "Last" wir auf den Server bringen können. Wenn ich ein Update veröffentliche, werden so ca. 14.000 Update in zwei Wochen gemacht, an den ersten Tagen teilweise über 4.000 Updates. Wenn man bei jedem Start nach neuen Definitionen suchen würde, produziert man eine ganze Menge Anfragen. Ich würde hoffen man kann einen HEAD Request zu den "Releases" machen und dann nur laden, wenn sich was geändert hat.

lenucksi commented 5 years ago

Aktuell liegen folgende Dateien vor:

...portfolio/.../pdf/deutsche-bank-purchase.json ...portfolio.tests/.../pdf/deutsche-bank/DeutscheBankKauf.txt ...portfolio.tests/.../pdf/deutsche-bank/DeutscheBankKauf.json

Eine ähnliche Struktur schwebt mir für das separate Github Repository vor. Dann kann man auch weitere Testfälle per PR hinzufügen. CI/CD würde dann ein ZIP bauen und als Release bei Github anhängen. Von da lädt PP die Konfigurationen.

Das sieht sinnvoll für mich aus. Hast du da schon eine CI aufgesetzt gehabt? Es gibt ja jetzt auch CI von GitHub via deren Actions.

Die Textdateien haben den Vorteil, dass man relativ einfach personenbezogenen Daten wie Name und Kontonummer entfernen kann. Man könnte aber auch PDF unterstützen (allerdings habe ich die Erfahrung, wenn man das PDF editiert, kann sich auch die Struktur ändern wie es in Text umgewandelt wird).

I see, ich hatte jetzt and die PDFs gedacht, weil die primäre Quelle darstellen. Im Fall der Text-Extrakte würde man sich ja den PDF-Extraktor in PP in einem definierten Changeset-Zustand festnageln in dem Sinne dass falls sich die Ausgaben des Extraktors in PP irgendwann mal ändern man dann evtl. Probleme mit den bereits gezogenen Extrakten bekommen könnte. Und in Fallen in dem man einen externen Extraktor für den Externes-Skripting Usecase hat, bräuchte man auch PDFs oder würde sich wieder auf den PP-Extraktor festlegen. Würdest Du den verschlüsselte+nur in der CI temporär entschlüsselte Original-PDFs Ansatz für unerreichbar aus dem Privatsphärengesichtspunkt halten mit Deiner Erfahrung and eingesandten PDFs?

Lassen sich hierfür nicht die vorhandenen Beispiele verwenden, die PP bereits im Repository kennt? Die vorhandenen Prüfungen kennen ja bereits das Soll-Ergebnis der Prüfung.

Die Testfälle sollten auf jeden Fall nicht verloren gehen. Mir ging es aber darum, dass wir auch neue Testfälle bekommen. Ansonsten könnte es sein, dass die Importer sehr schnell "kaputt" gehen nur weil man sie auf das 2019 Format der Bank angepasst hat und 2018 gar nicht mehr testet.

Ah, wichtiger Punkt. D.h. falls Menschen noch ihre 2018er, 2017er PDFs importieren wollen obwohl die Bank und der Importer im 2019er Zustand ist? Würde man in dem Fall dann beispielsweise einen Commerzbank-Importer-JSON für 2017, 2018, 2019 (als Beispiel annehmend dass sie in jedem Jahr ihr Format geändert hätten) haben wollen? D.h. man könnte das entweder in einem Importer (mit Annotation welche Version für welches Jahr taugt) unterbringen oder einem Importer für ein Jahr ein Metadatenfeld geben für welches Jahr / welche Version das taugen würde. Und dann entsprechend Testdaten und Tests für die jeweiligen Versionen vorhalten.

Die GitHub API hat derzeit ein Limit von 100.000 Dateien und Verzeichnissen. Für den Sync bietet sich der Job an, der die Kurse synchronisiert.

Das ist eine interessant Option. Ich dachte zunächst an ein ZIP unter den "Releases" (durch die CI/CD pipeline / Github Action gebaut). Was ich mich aber frage ist wievielt "Last" wir auf den Server bringen können. Wenn ich ein Update veröffentliche, werden so ca. 14.000 Update in zwei Wochen gemacht, an den ersten Tagen teilweise über 4.000 Updates. Wenn man bei jedem Start nach neuen Definitionen suchen würde, produziert man eine ganze Menge Anfragen. Ich würde hoffen man kann einen HEAD Request zu den "Releases" machen und dann nur laden, wenn sich was geändert hat.

Ich denke, dass GitHub auch noch größere Projekte kennt. Dementsprechend sollte die Last auf Releases in dem Ausmaß kein Problem darstellem. Und falls doch wird GitHub vermutlich auf einen zukommen und man kann immer noch zu S3 und dergleichen gehen...

ghost commented 5 years ago

Ich habe gestern auch schon einmal mit dem ING Extractor experimentiert. Der einfache Kauf/Verkauf funktioniert, nur bei Gemeinschaftskonten klemmt es noch.

Da bräuchte es irgendwie folgende Logik:

                {
                    "context": "UNIT",
                    "isOptional": true,
                    "pattern": [
                        "Solidarit.tszuschlag \\d+,\\d+ ?% (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)"
                    ],
                    "attributes": {
                        "type": "FEE"
                    }
                },

                {
                    "context": "UNIT",
                    "isOptional": true,
                    "pattern": [
                        "Solidarit.tszuschlag \\d+,\\d+ ?% (?<currency>\\w{3}+) (?<amount>[\\d.]+,\\d+)",
                        "KapSt anteilig 50,00 %.*"
                    ],
                    "attributes": {
                        "type": "FEE"
                    }
                },

Entweder habe ich da einen Regex Fehler, oder aber ich muss die Bedingung ändern.

buchen commented 5 years ago

Im Fall der Text-Extrakte würde man sich ja den PDF-Extraktor in PP in einem definierten Changeset-Zustand festnageln in dem Sinne dass falls sich die Ausgaben des Extraktors in PP irgendwann mal ändern man dann evtl. Probleme mit den bereits gezogenen Extrakten bekommen könnte.

Ja, das ist so. Darum bleibe ich (noch) bei Apache PDFBox 1.x. Es sieht zwar nicht so aus als würde PDFBox 2.x signifikant andere Ergebnisse liefern, aber warum updaten.

Würdest Du den verschlüsselte+nur in der CI temporär entschlüsselte Original-PDFs Ansatz für unerreichbar aus dem Privatsphärengesichtspunkt halten mit Deiner Erfahrung and eingesandten PDFs?

Außer meinen eigenen PDF Dokumenten habe ich in den letzten Jahren noch genau zwei andere PDF Originaldateien zugesendet bekommen.

Würde man in dem Fall dann beispielsweise einen Commerzbank-Importer-JSON für 2017, 2018, 2019 (als Beispiel annehmend dass sie in jedem Jahr ihr Format geändert hätten) haben wollen?

Man will auf jeden Fall alle alten Dokumente importieren können - z.B. wenn man PP neu aufsetzt und die alten Buchungen importiert.

Ob das jeweils ein komplett neuer Extrator ist, oder die Pattern nur hier und da leicht angepasst sind, hängt von der Änderung im PDF an. Mein Punkt war: wir brauchen die Testfälle, damit man den Extractor nicht aus Versehen so ändert, dass die alten Dokumente nicht mehr importiert werden.

buchen commented 5 years ago

@Ragas13 schreibt:

Ich habe gestern auch schon einmal mit dem ING Extractor experimentiert. Der einfache Kauf/Verkauf funktioniert, nur bei Gemeinschaftskonten klemmt es noch.

Super. Da sieht man direkt wie weit man mit dem JSON kommt - oder auch nicht...

Blöd ist, dass man hier ein "Anti-Pattern" braucht - wenn es das Muster gefunden wird, dann dürfen die Gebühren nicht gesetzt werden.

Welche Optionen haben wir?

  1. der section "antipattern" hinzufügen
  2. globale pattern/antipattern erlauben (ähnlich wie der ContextProvider bei der ING Diba)
  3. Diese Art von Importern eben noch nur per Code ermöglichen (auch Degiro Kontoauszüge sind verdammt kompliziert, dass das wohl weiter nur mit Code möglich ist - siehe #1198)

Würde denn 1) überhaupt funktionieren? Pushe doch ggf. man Deinen Zwischenstand als PR damit ich mir das anschauen kann.

fmms commented 4 years ago

Ist dies schon in der aktuell releasten Version (0.46.0) vorhanden und könnte für die Umsetzung von #1487 verwendet werden?

ghost commented 4 years ago

@fmms Jup, sollte möglich sein.

Du kannst dich bspw an https://github.com/buchen/portfolio/commit/894da75953a219f258b0b2856cf7bc28d7b3d32c#diff-771248eb0f318292af45651d58bec198 orientieren.