Rumah > pembangunan bahagian belakang > Tutorial Python > Pengekodan Hujung Minggu: Tukar Slip Gaji PDF Menjadi Laporan CSV Tunggal

Pengekodan Hujung Minggu: Tukar Slip Gaji PDF Menjadi Laporan CSV Tunggal

Susan Sarandon
Lepaskan: 2024-12-25 22:20:17
asal
120 orang telah melayarinya

Pernah diprogramkan dengan fail PDF? Tulis skrip Python dengan saya!

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

Seperti yang anda akan lihat dalam catatan blog akan datang, saya berada dalam era celik kewangan saya. Dengan penghujung tahun yang akan datang, saya ingin melihat nombor saya: Berapa banyak cukai yang saya bayar? Berapakah pendapatan saya untuk syif atas panggilan? Berbilang fail PDF bukanlah cara yang paling selesa untuk melihat data ini dan saya mahukan satu fail CSV yang boleh saya mainkan dalam Excel.

Seperti kebanyakan pembangun yang baik, saya terlalu malas untuk memasukkan nombor secara manual, jadi saya menulis skrip. Jika anda menikmati pengaturcaraan — sertai saya dalam pengembaraan! Dan jika anda tiada mood — saya akan tunjukkan kepada anda cara mengubah suai kod agar sepadan dengan struktur slip gaji anda :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

Rancangan

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
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kami akan bermula dengan menulis kod untuk membaca PDF, termasuk menentukan medan yang kami mahu dalam laporan kami. Ini adalah bahagian yang anda perlu ubah suai untuk memadankan struktur slip gaji anda. Setelah itu diketahui, kami akan mengulangi keseluruhan direktori slip gaji.

Dalam langkah ketiga, saya memilih untuk menambah langkah tambahan antara PDF dan CSV — laporan JSON. Sebaik sahaja kami melihat semuanya berfungsi, kami akan mengalih keluar penggunaan fail itu.

Akhir sekali, kami akan menterjemah data JSON itu ke dalam fail CSV. CSV itu kemudiannya boleh ditukar dengan mudah kepada Helaian Google (hanya klik "buka dengan") atau kepada Excel (arahan boleh didapati di sini).

Itu pelan bagus yang mudah, tetapi anda tahu bagaimana ia berjalan — cabaran ditemui sepanjang perjalanan... Bolehkah anda meneka di mana keadaan mungkin menjadi rumit?

Sebelum kita mula — nota penting: PASTIKAN SLIP GAJI ANDA SWASTA! Jika anda memuat naik projek anda ke GitHub — pastikan anda tidak berkongsi butiran peribadi tersebut! Anda boleh menggunakan .gitignore untuk ini:

/payslips_pdf
pdf_rows.txt
report.json
report.csv
Salin selepas log masuk
Salin selepas log masuk

Bolehkah kita mulakan?

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

Baca Fail PDF

Kami akan mulakan dengan membaca PDF dan mencetak semua baris. Dengan cara itu kita akan tahu apa yang muncul dalam setiap baris. Ini hanya perlu dilakukan sekali (manakala laporan mungkin akan dibuat sebulan sekali atau setahun sekali), dan ia bukan sebahagian daripada laporan -jadi kami akan menciptanya dalam fail yang berasingan.

Mulakan dengan mencipta fail Python baharu (saya memanggil saya pdf_to_txt.py) dan tulis fungsi yang membaca pdf dan mencetak hasilnya ke dalam fail .txt:



Ini akan berfungsi, tetapi terdapat 3 perubahan untuk menjadikannya lebih mesra pengguna:
  • Dapatkan laluan fail sebagai hujah baris arahan, supaya pengguna boleh menjalankannya tanpa menyentuh kod.
  • Tambahkan beberapa arahan yang menangkap ralat sekiranya pengguna menjalankan arahan yang salah. (Saya menambah warna amaran tetapi anda tidak perlu)

  • Kami akan membaca fail PDF dalam skrip utama juga, jadi mengalihkan fungsi ini ke sana adalah lebih baik.

Cubalah! Cuba jalankan arahan dengan hujah yang betul dan salah :)


Apabila anda menjalankan py ./main.py anda akan melihat fail baharu dalam direktori projek yang dipanggil pdf_rows.txt.
<script></script> <script></script> <script></script>
(Jangan risau, ini bukan data saya — ia adalah berdasarkan contoh slip gaji daripada imej di bawah)

Memproses Data PDF

Sekarang kita tahu struktur di mana PDF dibaca — kita boleh menangkap nilai yang diingini. Dalam kes saya — berikut ialah maklumat yang saya minati:

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

Perhatikan terdapat data dalam jadual (kategori yang mungkin berbeza setiap bulan) dan data di luar jadual.

Data di luar jadual:

Tempoh Gaji — Boleh didapati di baris 19

Bayar Kasar — Yang ini sukar untuk mencari peraturan kerana peraturan itu muncul selepas senarai pembayaran dan tidak mempunyai tajuk "Bayar Kasar".

Seperti yang dinyatakan sebelum ini, bayaran dan potongan boleh berbeza-beza, dan tidak setiap bulan adalah sama. Oleh itu, gaji kasar mungkin muncul dalam baris yang berbeza pada bulan yang berbeza.

Saya perasan ia muncul sejurus selepas nama pekerja — jadi itulah yang saya gunakan. Mulakan dengan menambahkannya berkod keras, dan kemudian kami akan mendapatkannya secara luaran.

Bayar Bersih: Yang ini mudah — ia muncul dalam baris 17.

Saya mengumpulkan nilai luar jadual tersebut ke dalam fungsi:


Data di dalam jadual

Butiran Pembayaran dan Potongan: Ini adalah bahagian yang menarik! Kita akan mulakan dengan memotong tatasusunan baris untuk menjimatkan beberapa milisaat dalam gelung for yang akan datang. Kemudian, saya perlu membezakan antara item senarai

dan baris lain.

Saya perhatikan bahawa dalam keseluruhan fail, item senarai adalah satu-satunya item yang sepadan dengan peraturan ini: Mulakan dengan aksara abjad dan berakhir dengan aksara angka dan mengandungi ruang (syarat terakhir ialah menapis baris yang salah dalam saya
slip gaji, anda mungkin tidak memerlukannya).

<script></script> <script></script>
Memandangkan kita mempunyai kumpulan baris, mari kita memprosesnya untuk disimpan dalam objek JSON. Pada ketika ini, kami tidak kisah sama ada ia adalah bayaran atau potongan.

Sebagai contoh, kita akan melihat item pencen:

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
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Saya tidak kisah tentang baki (nombor di sebelah kanan), tetapi saya mengambil berat tentang kod tersebut (G bermakna ia ditolak daripada gaji Kasar — ​​sebelum cukai — dan N bermakna ia ditolak daripada gaji Bersih — selepas cukai). Jadi idealnya, kita akan mempunyai json_obj["Pencen (G)"]=150.00.

Kami akan menggunakan ruang untuk membelah garisan. Ada baiknya terdapat ruang pendua — dengan cara itu kita boleh membezakan antara pemisahan ruang antara beberapa perkataan dan pemisahan ruang antara beberapa medan.

Penerangan:

Kami akan mencari ruang berkembar pertama dan dibelah olehnya.

Kod:

Jumlah ruang bergantung pada panjang perihalan, jadi kami tidak dapat mengetahui terlebih dahulu jumlah ruang — itulah sebabnya saya akan menggunakan lstrip() juga. Kini selebihnya baris bermula dengan aksara bukan ruang.

Bukan semua item senarai mempunyai kod, jadi kami ingin menyemak sama ada baris itu bermula dengan kod atau digit. Jika ia adalah kod — saya membungkusnya dalam () (termasuk ruang sebelum kurungan pembukaan) , dan melampirkannya pada rentetan penerangan. dan jika tidak — tambahkan apa-apa.

Jumlah:

Jika ada kod — kami akan mempunyai lebih banyak ruang untuk ditanggalkan. Jika tidak, talian kami mungkin mengandungi dua amaun: Bulanan dan baki.

Terdapat 4 kes yang saya perhatikan:

/payslips_pdf
pdf_rows.txt
report.json
report.csv
Salin selepas log masuk
Salin selepas log masuk

Selepas mengekstrak kategori dan kod, kami tinggal dengan:

PENSION     G   150.00   587.49
Salin selepas log masuk

Untuk menampung kes 2–3, kami akan mencari indeks ruang yang memisahkan jumlah dan memotong ekor. Ia juga berfungsi untuk kes pertama, di mana tiada ruang (aka tiada ekor).

Untuk menampung kes 4, saya bergantung pada perbezaan antara dua jenis kategori dengan amaun tunggal dalam baris: Yang pertama adalah seperti gaji — di mana kita ingin menyimpan amaun itu, dan jenis kedua adalah seperti penahanan cukai — di mana kita mahu mengabaikannya. Perbezaannya ialah hanya potongan yang menjejaki baki tahunan dalam jadual — jadi saya sedang menyemak -.

Semua bersama-sama, begitulah rupanya:

Tulis ke Fail JSON


Ini bukan langkah wajib — kita boleh bekerja dengan objek JSON tanpa mengeksport nilai. Saya lebih suka melihat rupanya, sekurang-kurangnya untuk peringkat pengekodan.

Skalakan kepada Berbilang Fail PDF

Satu-satunya sebab langkah ini mendapat bahagian khusus adalah kerana membungkus pdf_to_dict dalam gelung for mendedahkan kejutan yang tidak menyenangkan. Untuk menunjukkannya, saya mencipta fungsi yang dipanggil iterate_over_pdfs():
<script></script> <script></script> <script></script>
Ini berlaku kerana senarai fail diisih mengikut susunan abjad, jadi 10 muncul sebelum 2. Mempunyai entri laporan dalam susunan kronologi boleh dianggap penting, dan bukan sekadar ciri yang bagus untuk dimiliki. Oleh itu, kita perlu memperbaikinya!

Pada asalnya, saya fikir saya perlu menamakan semula fail (Payslip1.pdf -> Payslip01.pdf), tetapi ada penyelesaian yang lebih baik:


Sebaik sahaja kami mengisih senarai nama fail mengikut panjang, 10 akan muncul selepas 2. Pada baris terakhir, saya menyahkod nama untuk menghilangkan b'' struktur lalai.

Buat Laporan CSV

Oleh kerana item dalam pembayaran dan potongan mungkin berbeza dari slip gaji ke slip gaji, bahagian ini lebih daripada terjemahan langsung. CSV ialah set data hubungan, yang bermaksud kita perlu mengetahui terlebih dahulu semua kategori dalam pembayaran dan potongan dan memastikan kemasukan itu kosong untuk slip gaji yang tidak wujud. JSON, sebaliknya, tidak berkaitan dan setiap entri menyatakan kuncinya.

Dengan itu, langkah pertama dalam laporan CSV kami ialah mengumpulkan kategori. Semua kategori.

Kumpulkan kategori:


Sekarang, pada pandangan pertama, anda mungkin terfikir untuk menggunakan Set untuk itu — kerana kami mahu semua kategori muncul sekali sahaja. Saya telah mencuba itu. Masalahnya ialah set tidak tersenarai, dan saya rasa penting untuk memadankan susunan item yang muncul dalam slip gaji asal. Apabila menggunakan senarai, jangan lupa untuk menyemak sama ada item itu wujud dalam senarai sebelum menambahkannya:


Sekarang setelah kami mengetahui perkara ini: Ingat dahulu apabila kami berkata kami tidak mengambil berat tentang senarai item mana yang merupakan pembayaran dan yang mana potongan? Nah, kami peduli sekarang! Kami tidak

perlu
berpisah, tetapi saya menjangkakan laporan slip gaji mempunyai semua pembayaran di sebelah kanan dan semua potongan di sebelah kiri, tidak bercampur.

Walaupun setiap slip gaji mungkin mempunyai item senarai yang berbeza — sesetengahnya akan sentiasa wujud (kerana anda akan sentiasa membayar cukai anda ;) ). Kami boleh menggunakan ini untuk faedah kami — dan tandakan PAYE sebagai permulaan potongan! (Saya agak pasti PAYE hanya di Ireland, jadi anda perlu menukarnya agar sepadan dengan slip gaji anda)

Akhir sekali, saya mengembalikan satu senarai, kerana tidak ada gunanya mengasingkan pembayaran daripada potongan — perpecahan itu adalah untuk memastikan pembayaran akan muncul di sebelah kanan dan potongan akan muncul di sebelah kiri.

Isi jadual CSV:


Sekarang kita mempunyai kategori, kita boleh mula mengisi jadual CSV:

Setiap penyata gaji akan menjadi satu baris dan setiap baris akan mempunyai medan dalam susunan tertentu yang dipisahkan dengan koma. Saya mendapati lebih mudah untuk menyusun medan dalam senarai, dan kemudian menyertainya. Medan yang muncul dalam kategori tetapi tidak dalam slip gaji — akan kekal kosong:


<script></script> Akhir sekali, kami akan menulis ke fail CSV:<script></script> <script></script> <script></script> <script></script>
Selepas ini, anda akan mendapat laporan CSV yang bagus dengan semua slip gaji anda!

Anda boleh menjadikannya lebih mudah untuk dibaca dengan memuat turun sambungan VS RainbowCSV (atau mana-mana selari dengan IDE lain)

Alih keluar penggunaan fail .json

Setelah kami mengetahui perkara berfungsi, kami tidak perlu menulis dan membaca daripada fail JSON — kami boleh menggunakan objek JSON secara langsung:

Daripada menggunakan json_object (seperti dalam json_object = json.dumps(json_payslips)) – kami akan menggunakan json_payslips secara langsung:

  1. Tulis: Tidak perlu menulis untuk melaporkan.json - kami boleh mengalih keluar bahagian ini.

  2. Baca: Hantar json_payslips terus ke fungsi json_to_csv():

Dapatkan nama pekerja sebagai hujah

Setelah anda menyediakan skrip anda — anda pasti mahu berkongsi dengan rakan sekerja dan rakan anda! Untuk pengalaman pengguna yang bagus, kami akan mengeksport nama pekerja untuk datang daripada baris arahan, dan bukannya meminta mereka membuka kod.

Baca hujah

Kami akan bermula dengan laluan gembira — dengan andaian pengguna memasukkan nama pekerja — dan tambah kod yang menggunakannya:

Dalam pdf_to_dict(), bukannya pengekodan keras EMPLOYEE_NAME = "IFAT NEUMANN", kami akan membacanya daripada hujah: employee_name = sys.argv[1]. Jangan lupa import sys!

Sekarang kita akan memikirkan senario lain:

Tiada nama pekerja diberikan

Bagaimana jika pengguna tidak memasukkan sebarang nama pekerja? Kami ingin menangkapnya secepat mungkin dan memberitahu mereka!

Oleh itu, kami akan menambah semakan dalam baris pertama fungsi utama. Sekarang, gerak hati adalah untuk memulakan pembolehubah nama_pekerja di sana - tetapi ini akan menyebabkan sifat fungsi menggelegak sehingga ia mencapai fungsi yang menggunakan pembolehubah ini - dan saya tidak mendapati pendekatan itu sangat bersih.


Akhir sekali, saya hanya akan cuba mengakses medan ini — dan tangkap jika ia tiada:


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
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Ambil perhatian bahawa menambah pengecualian bermakna fungsi print_warning() berpindah ke main.py. Jika tidak, anda akan mendapat ralat:

Nama pekerja tanpa petikan


Kami meminta pengguna untuk membungkus nama dengan petikan kerana argumen baris arahan dipisahkan oleh ruang. Satu-satunya hujah yang kami jangkakan ialah nama pekerja, jadi jika ada hujah lain — kami tahu mereka tidak menggunakan petikan dan kami boleh memberitahu mereka:



Anda boleh melangkau keperluan petikan dan mengulangi argumen, mengumpulkan semua bahagian nama pengguna — tetapi saya dapati pendekatan ini menambahkan kerumitan yang tidak perlu.

Nama pekerja tidak tertera pada slip gaji


Kami tidak akan dapat mencari gaji kasar jika kami tidak mempunyai nama pekerja seperti yang tertera pada slip gaji.

Tempat awal untuk mengenal pasti ketidakpadanan adalah apabila kita membaca pdf. Oleh itu — tambahkan cek pada permulaan fungsi pdf_to_dict():
<script></script> <script></script> <script></script> <script></script>
Ambil perhatian bahawa saya mengalihkan employee_name = sys.argv[1] ke fungsi ini kerana ia lebih mudah dibaca (daripada jika sys.argv[1] tiada dalam teks). Selepas itu, saya menghantarnya ke get_fixed_values().

Akhir sekali, kami menangkap semula ralat dalam fungsi utama:


Dan untuk menyelesaikan semuanya — jangan lupa untuk mengemas kini fail README anda dengan arahan baharu.

Skrip penuh


Berikut ialah kod penuh yang boleh anda gunakan untuk bermain dengan slip gaji anda:


Weekend Coding: Turn PDF Payslips Into a Single CSV Report
https://cupofcode.blog/
Harap anda menikmati yang ini! Berminat dengan projek pengekodan hujung minggu yang lain? Lihat catatan blog penghantaran e-mel automatik saya!

Blogging adalah hobi saya, jadi saya gembira menghabiskan masa dan wang untuknya. Jika anda menikmati catatan blog ini, meletakkan 1 euro dalam balang tip saya akan memberitahu saya :) Terima kasih atas sokongan anda! <script></script> <script></script>

Atas ialah kandungan terperinci Pengekodan Hujung Minggu: Tukar Slip Gaji PDF Menjadi Laporan CSV Tunggal. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan