Heim > Backend-Entwicklung > Python-Tutorial > Wochenend-Codierung: Verwandeln Sie PDF-Gehaltsabrechnungen in einen einzigen CSV-Bericht

Wochenend-Codierung: Verwandeln Sie PDF-Gehaltsabrechnungen in einen einzigen CSV-Bericht

Susan Sarandon
Freigeben: 2024-12-25 22:20:17
Original
120 Leute haben es durchsucht

Haben Sie schon einmal mit PDF-Dateien programmiert? Schreiben Sie mit mir ein Python-Skript!

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

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

Weekend Coding: Turn PDF Payslips Into a Single CSV Report
This script receives a directory with payslip PDFs and returns a CSV file with the desired data

Der Plan

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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

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
Nach dem Login kopieren
Nach dem Login kopieren

Sollen wir anfangen?

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

PDF-Datei lesen

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:



Das wird funktionieren, aber es gibt drei Änderungen, um es benutzerfreundlicher zu machen:
  • Rufen Sie den Dateipfad als Befehlszeilenargument ab, damit der Benutzer ihn ausführen kann, ohne den Code zu berühren.
  • Fügen Sie einige Fehlerabfangfunktionen mit Anweisungen hinzu, falls der Benutzer den Befehl falsch ausführt. (Ich habe eine Warnfarbe hinzugefügt, aber das ist nicht notwendig)

  • Wir werden auch PDF-Dateien im Hauptskript lesen, daher ist es besser, diese Funktion dorthin zu verschieben.

Probieren Sie es aus! Versuchen Sie, den Befehl mit den richtigen und den falschen Argumenten auszuführen :)


Wenn Sie py ./main.py ausführen, wird im Projektverzeichnis eine neue Datei mit dem Namen pdf_rows.txt angezeigt.
<script></script> <script></script> <script></script>
(Keine Sorge, das sind nicht meine Daten – sie basieren auf der Beispiel-Gehaltsabrechnung aus dem Bild unten)

Verarbeiten Sie die PDF-Daten

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:

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

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:


Daten in der Tabelle

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).

<script></script> <script></script>
Nachdem wir nun die Zeilengruppe haben, verarbeiten wir sie, um sie im JSON-Objekt zu speichern. An dieser Stelle ist es uns egal, ob es sich um eine Zahlung oder einen Abzug handelt.

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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

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 Login kopieren
Nach dem Login kopieren

Nach dem Extrahieren der Kategorie und des Codes bleibt Folgendes übrig:

PENSION     G   150.00   587.49
Nach dem Login kopieren

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:

In JSON-Datei schreiben


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

Der einzige Grund, warum dieser Schritt einen eigenen Abschnitt erhält, besteht darin, dass das Einbinden von pdf_to_dict in eine for-Schleife eine unangenehme Überraschung mit sich bringt. Um es zu demonstrieren, habe ich eine Funktion namens iterate_over_pdfs():
erstellt
<script></script> <script></script> <script></script>
Dies liegt daran, dass die Liste der Dateien in alphabetischer Reihenfolge sortiert ist, sodass 10 vor 2 erscheint. Die chronologische Reihenfolge der Berichtseinträge kann als entscheidend und nicht nur als nette Funktion angesehen werden. Deshalb müssen wir es beheben!

Ursprünglich dachte ich, ich müsste die Dateien umbenennen (Payslip1.pdf -> Payslip01.pdf), aber es gibt eine bessere Lösung:


Sobald wir die Liste der Dateinamen nach Länge sortiert haben, erscheint 10 nach 2. In der letzten Zeile habe ich die Namen dekodiert, um das b'' zu entfernen. Standardstruktur.

Erstellen Sie den CSV-Bericht

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:


Nun, da wir das herausgefunden haben: Erinnern Sie sich vorhin, als wir sagten, dass es uns egal ist, welche Artikelliste eine Zahlung und welche ein Abzug ist? Nun, es ist uns jetzt egal! Wir

müssen
nicht trennen, aber ich erwarte, dass in einem Gehaltsabrechnungsbericht rechts alle Zahlungen und links alle Abzüge aufgeführt sind, nicht gemischt.

Obwohl jede Gehaltsabrechnung unterschiedliche Listenelemente enthalten kann, werden einige immer vorhanden sein (weil Sie immer Ihre Steuern zahlen ;)). Wir können dies zu unserem Vorteil nutzen – und PAYE als Beginn der Abzüge kennzeichnen! (Ich bin mir ziemlich sicher, dass es PAYE nur in Irland gibt, daher müssen Sie es ändern, damit es mit Ihrer Gehaltsabrechnung übereinstimmt)

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:

Jede Gehaltsabrechnung ist eine Zeile und jede Zeile enthält die Felder in einer bestimmten Reihenfolge, getrennt durch ein Komma. Ich finde es einfacher, die Felder in einer Liste zu organisieren und sie dann zusammenzufügen. Felder, die in den Kategorien, aber nicht in der Lohnabrechnung erscheinen, bleiben leer:


<script></script> Zuletzt schreiben wir in die CSV-Datei:<script></script> <script></script> <script></script> <script></script>
Danach erhalten Sie einen schönen CSV-Bericht mit all Ihren Gehaltsabrechnungen!

Sie können das Lesen erleichtern, indem Sie die VS-Erweiterung RainbowCSV (oder eine Parallele einer anderen IDE) herunterladen

Entfernen Sie die Verwendung der .json-Datei

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:

  1. Schreiben: Es ist nicht nötig, in report.json zu schreiben – wir können diesen Abschnitt entfernen.

  2. Lesen Sie: Übergeben Sie json_payslips direkt an die Funktion json_to_csv():

Holen Sie sich den Namen des Mitarbeiters als Argument

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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

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


Wir bitten den Benutzer, den Namen in Anführungszeichen zu setzen, da Befehlszeilenargumente durch Leerzeichen getrennt werden. Das einzige Argument, das wir erwarten, ist der Name des Mitarbeiters. Wenn es also ein anderes Argument gibt, wissen wir, dass keine Anführungszeichen verwendet wurden, und können ihn benachrichtigen:



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.

Der frühere Zeitpunkt, um die Nichtübereinstimmung zu erkennen, ist, wenn wir das PDF lesen. Fügen Sie daher das Häkchen am Anfang der Funktion pdf_to_dict() hinzu:
<script></script> <script></script> <script></script> <script></script>
Beachten Sie, dass ich „employee_name = sys.argv[1]“ in diese Funktion verschoben habe, weil sie einfacher zu lesen ist (anstatt „sys.argv[1] nicht im Text“ zu lesen). Danach übergebe ich es an get_fixed_values().

Zuletzt fangen wir den Fehler wieder in der Hauptfunktion ab:


Und zum Abschluss: Vergessen Sie nicht, Ihre README-Datei mit den neuen Anweisungen zu aktualisieren.

Das vollständige Drehbuch


Hier ist der vollständige Code, den Sie zum Spielen mit Ihren Gehaltsabrechnungen verwenden können:


Weekend Coding: Turn PDF Payslips Into a Single CSV Report
https://cupofcode.blog/
Ich hoffe, es hat Ihnen gefallen! Interessiert an einem weiteren Wochenend-Codierungsprojekt? Schauen Sie sich meinen Blogbeitrag zum automatisierten E-Mail-Versand an!

Bloggen ist mein Hobby, deshalb investiere ich gerne Zeit und Geld dafür. Wenn Ihnen dieser Blogbeitrag gefallen hat, lassen Sie es mich wissen, indem Sie 1 Euro in mein Trinkgeldglas stecken :) Vielen Dank für Ihre Unterstützung! <script></script> <script></script>

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!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage