Wie Sie in den kommenden Blogbeiträgen sehen werden, befinde ich mich in meiner Ära der Finanzkompetenz. Da das Jahresende naht, wollte ich einen Blick auf meine Zahlen werfen: Wie viel Steuern habe ich gezahlt? Wie viel habe ich für Bereitschaftsdienste verdient? Mehrere PDF-Dateien sind nicht die bequemste Möglichkeit, diese Daten anzuzeigen, und ich wollte eine einzige CSV-Datei, mit der ich in Excel spielen kann.
Wie viele gute Entwickler war ich zu faul, die Zahlen manuell einzugeben, also habe ich ein Skript geschrieben. Wenn Sie Spaß am Programmieren haben, begleiten Sie mich auf einem Abenteuer! Und wenn Sie keine Lust haben, zeige ich Ihnen, wie Sie den Code an Ihre Gehaltsabrechnungsstruktur anpassen können :D
This script receives a directory with payslip PDFs and returns a CSV file with the desired data |
main.py: # translate 1 pdf to 1 dict # loop over the pdf dir # save all dicts to 1 json file # translate json report to csv report
Wir beginnen damit, den Code zum Lesen einer PDF-Datei zu schreiben, einschließlich der Entscheidung, welche Felder wir in unserem Bericht haben möchten. Dies ist der Teil, den Sie anpassen müssen, um ihn an die Struktur Ihrer Gehaltsabrechnung anzupassen. Sobald das herausgefunden ist, werden wir das gesamte Gehaltsabrechnungsverzeichnis durchgehen.
Im dritten Schritt habe ich beschlossen, einen zusätzlichen Schritt zwischen der PDF- und der CSV-Datei hinzuzufügen – einen JSON-Bericht. Sobald wir sehen, dass alles funktioniert, werden wir die Verwendung dieser Datei entfernen.
Zuletzt übersetzen wir diese JSON-Daten in eine CSV-Datei. Diese CSV-Datei kann dann einfach in Google Sheets (klicken Sie einfach auf „Öffnen mit“) oder in Excel (Anweisungen finden Sie hier) konvertiert werden.
Das ist ein einfacher, schöner Plan, aber Sie wissen, wie er abläuft – unterwegs werden Herausforderungen entdeckt ... Können Sie erraten, wo es komplex werden könnte?
Bevor wir beginnen – ein wichtiger Hinweis: Bewahren Sie Ihre Gehaltsabrechnungen geheim auf! Wenn Sie Ihr Projekt auf GitHub hochladen, geben Sie diese persönlichen Daten unbedingt nicht weiter! Sie können hierfür .gitignore verwenden:
/payslips_pdf pdf_rows.txt report.json report.csv
Sollen wir anfangen?
Wir beginnen damit, das PDF zu lesen und alle Zeilen auszudrucken. Auf diese Weise wissen wir, was in jeder Zeile erscheint. Dies muss nur einmal durchgeführt werden (während ein Bericht wahrscheinlich einmal im Monat oder einmal im Jahr erstellt wird) und ist nicht Teil des Berichts – wir werden ihn also in einer separaten Datei erstellen.
Erstellen Sie zunächst eine neue Python-Datei (ich habe meine pdf_to_txt.py genannt) und schreiben Sie eine Funktion, die das PDF liest und das Ergebnis in eine TXT-Datei druckt:
Wir werden auch PDF-Dateien im Hauptskript lesen, daher ist es besser, diese Funktion dorthin zu verschieben.
Da wir nun die Struktur kennen, in der das PDF gelesen wird, können wir die gewünschten Werte herausfischen. In meinem Fall sind hier die Informationen, die mich interessierten:
Beachten Sie, dass es Daten innerhalb der Tabelle (Kategorien, die jeden Monat variieren können) und Daten außerhalb der Tabelle gibt.
Daten außerhalb der Tabelle:
Zahlungszeitraum – Zu finden in Zeile 19
Bruttolohn – Für diese Regel war es schwierig, eine Regel zu finden, da sie nach der Zahlungsliste erscheint und nicht den Titel „Bruttolohn“ trägt.
Wie bereits erwähnt, können Zahlungen und Abzüge variieren und nicht jeder Monat ist gleich. Daher kann der Bruttolohn in verschiedenen Monaten in einer anderen Zeile erscheinen.
Mir ist aufgefallen, dass es direkt nach dem Namen des Mitarbeiters erscheint – also habe ich es verwendet. Beginnen Sie damit, es fest codiert hinzuzufügen, und später erhalten wir es extern.
Nett Pay: Dies ist einfach – es erscheint in Zeile 17.
Ich habe diese Werte außerhalb der Tabelle in einer Funktion zusammengefasst:
Zahlungs- und Abzugsdetails: Das ist der interessante Teil! Wir beginnen damit, das Zeilen-Array zu kürzen, um in der kommenden for-Schleife ein paar Millisekunden zu sparen. Dann musste ich zwischen Listenelementen
und anderen Zeilen unterscheiden
Mir ist aufgefallen, dass in der gesamten Datei nur Listenelemente dieser Regel entsprechen: Beginnen Sie mit einem alphabetischen Zeichen undenden Sie mit einem numerischen Zeichen und enthalten ein Leerzeichen (die letzte Bedingung besteht darin, falsche Zeilen in mein
herauszufiltern Gehaltsabrechnung, das brauchen Sie vielleicht nicht).
Wir schauen uns zum Beispiel den Rentenposten an:
main.py: # translate 1 pdf to 1 dict # loop over the pdf dir # save all dicts to 1 json file # translate json report to csv report
Mir ist der Saldo (die Zahl rechts) egal, aber der Code ist mir wichtig (G bedeutet, dass er vom Bruttolohn abgezogen wird – vor Steuern). – und N bedeutet, dass es vom Nettolohn abgezogen wird – nach Steuern). Im Idealfall haben wir also json_obj["Pension (G)"]=150,00.
Wir verwenden die Leerzeichen, um die Zeile zu teilen. Es ist gut, dass es doppelte Leerzeichen gibt – so können wir zwischen der Aufteilung von Leerzeichen zwischen ein paar Wörtern und der Aufteilung von Leerzeichen zwischen einigen Feldern unterscheiden.
Die Beschreibung:
Wir finden das erste doppelte Leerzeichen und teilen es auf.
Der Code:
Die Anzahl der Leerzeichen hängt von der Länge der Beschreibung ab, daher können wir nicht im Voraus wissen, wie viele es sind – deshalb verwende ich auch lstrip(). Nun beginnt der Rest der Zeile mit einem Nicht-Leerzeichen.
Nicht alle Listenelemente haben einen Code, daher möchten wir prüfen, ob die Zeile mit einem Code oder einer Ziffer beginnt. Wenn es sich um einen Code handelt, schließe ich ihn in () ein (einschließlich eines Leerzeichens vor der öffnenden Klammer) und hänge ihn an die Beschreibungszeichenfolge an. und wenn nicht, fügen Sie nichts hinzu.
Der Betrag:
Wenn es Code gäbe, hätten wir mehr Leerzeichen zum Entfernen. Wenn nicht, könnte unsere Zeile zwei Beträge enthalten: den Monatsbetrag und den Restbetrag.
Es gibt 4 Fälle, die mir aufgefallen sind:
/payslips_pdf pdf_rows.txt report.json report.csv
Nach dem Extrahieren der Kategorie und des Codes bleibt Folgendes übrig:
PENSION G 150.00 587.49
Um die Fälle 2–3 abzudecken, suchen wir den Index der Leerzeichen, die die Beträge trennen, und schneiden den Schwanz ab. Es funktioniert auch für den ersten Fall, bei dem kein Leerzeichen vorhanden ist (auch bekannt als „kein Schwanz“).
Um Fall 4 abzudecken, verlasse ich mich auf den Unterschied zwischen zwei Arten von Kategorien mit einem einzigen Betrag in der Zeile: Die erste ist wie das Gehalt – wo wir den Betrag sparen wollen, und die zweite Art ist wie die Steuereinbehalte – wo wir sie ignorieren wollen. Der Unterschied besteht darin, dass nur Abzüge den Jahressaldo in der Tabelle verfolgen – ich überprüfe also auf -.
Alles in allem sieht es so aus:
Dies ist kein obligatorischer Schritt – wir können mit einem JSON-Objekt arbeiten, ohne die Werte zu exportieren. Ich schaue mir lieber an, wie es aussieht, zumindest für die Codierungsphase.
Auf mehrere PDF-Dateien skalieren
Ursprünglich dachte ich, ich müsste die Dateien umbenennen (Payslip1.pdf -> Payslip01.pdf), aber es gibt eine bessere Lösung:
Da die Posten in Zahlungen und Abzügen von Gehaltsabrechnung zu Gehaltsabrechnung variieren können, handelt es sich bei diesem Abschnitt um mehr als nur eine direkte Übersetzung. CSV ist ein relationaler Datensatz, was bedeutet, dass wir alle Kategorien in Zahlungen und Abzügen im Voraus kennen müssen und den Eintrag für eine Gehaltsabrechnung leer lassen müssen, wenn er nicht vorhanden ist. JSON hingegen ist nicht relational und jeder Eintrag gibt seine Schlüssel an.
Vor diesem Hintergrund besteht der erste Schritt in unserem CSV-Bericht darin, die Kategorien zu sammeln. Alle Kategorien.
Sammeln Sie die Kategorien:
Auf den ersten Blick könnte man denken, dass man dafür Set verwenden sollte – denn wir wollen, dass alle Kategorien nur einmal auftauchen. Das habe ich versucht. Das Problem dabei ist, dass die Sets nicht aufgeführt sind und ich finde es wichtig, die Reihenfolge der Artikel in den Original-Gehaltsabrechnungen einzuhalten. Vergessen Sie bei der Verwendung von Listen nicht zu prüfen, ob das Element in der Liste vorhanden ist, bevor Sie es anhängen:
müssen
nicht trennen, aber ich erwarte, dass in einem Gehaltsabrechnungsbericht rechts alle Zahlungen und links alle Abzüge aufgeführt sind, nicht gemischt.
Abschließend gebe ich eine einzelne Liste zurück, da es keinen Sinn macht, die Zahlungen von den Abzügen zu trennen. Die Aufteilung sollte sicherstellen, dass die Zahlungen rechts und die Abzüge links angezeigt werden.
Befüllen Sie die CSV-Tabelle:
Da wir nun die Kategorien haben, können wir mit dem Füllen der CSV-Tabelle beginnen:
Sie können das Lesen erleichtern, indem Sie die VS-Erweiterung RainbowCSV (oder eine Parallele einer anderen IDE) herunterladen
Sobald wir wissen, dass alles funktioniert, müssen wir nicht mehr in die JSON-Datei schreiben und aus ihr lesen – wir können das JSON-Objekt direkt verwenden:
Anstatt json_object zu verwenden (wie in json_object = json.dumps(json_payslips)) – verwenden wir json_payslips direkt:
Schreiben: Es ist nicht nötig, in report.json zu schreiben – wir können diesen Abschnitt entfernen.
Lesen Sie: Übergeben Sie json_payslips direkt an die Funktion json_to_csv():
Sobald Sie Ihr Skript fertig haben, möchten Sie es mit Ihren Kollegen und Freunden teilen! Für ein angenehmes Benutzererlebnis exportieren wir den Mitarbeiternamen so, dass er aus der Befehlszeile stammt, anstatt ihn aufzufordern, den Code zu öffnen.
Argument lesen
Wir beginnen mit dem glücklichen Weg – vorausgesetzt, der Benutzer hat den Namen des Mitarbeiters eingegeben – und fügen Code hinzu, der ihn verwendet:
Anstatt EMPLOYEE_NAME = „IFAT NEUMANN“ hart zu codieren, lesen wir es in pdf_to_dict() aus den Argumenten: Employee_name = sys.argv[1]. Vergessen Sie nicht, sys zu importieren!
Jetzt denken wir über andere Szenarien nach:
Es wurde kein Mitarbeitername angegeben
Was passiert, wenn der Benutzer keinen Mitarbeiternamen eingegeben hat? Wir möchten es so schnell wie möglich bemerken und sie benachrichtigen!
Daher fügen wir in der ersten Zeile der Hauptfunktion ein Häkchen ein. Nun besteht die Intuition darin, die Variable „employee_name“ dort zu initialisieren – aber das führt zu einer Blasenbildung in den Funktionseigenschaften, bis die Funktion erreicht wird, die diese Variable verwendet – und ich halte das für keinen sehr sauberen Ansatz.
Zuletzt versuche ich einfach, auf dieses Feld zuzugreifen – und fange es ab, wenn es nicht da ist:
main.py: # translate 1 pdf to 1 dict # loop over the pdf dir # save all dicts to 1 json file # translate json report to csv report
Beachten Sie, dass das Hinzufügen von Ausnahmen dazu führt, dass die Funktion print_warning() nach main.py verschoben wird. Andernfalls erhalten Sie eine Fehlermeldung:
Mitarbeitername ohne Anführungszeichen
Sie können die Zitatanforderungen überspringen und Argumente durchgehen und alle Teile des Benutzernamens sammeln – aber ich finde, dass dieser Ansatz die Komplexität unnötig erhöht.
Der Name des Mitarbeiters erscheint nicht auf der Gehaltsabrechnung
Wir können den Bruttolohn nicht finden, wenn uns der Name des Mitarbeiters, wie er auf der Gehaltsabrechnung erscheint, nicht vorliegt.
Zuletzt fangen wir den Fehler wieder in der Hauptfunktion ab:
Hier ist der vollständige Code, den Sie zum Spielen mit Ihren Gehaltsabrechnungen verwenden können:
https://cupofcode.blog/ |
Das obige ist der detaillierte Inhalt vonWochenend-Codierung: Verwandeln Sie PDF-Gehaltsabrechnungen in einen einzigen CSV-Bericht. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!