diff --git a/src/_posts/parking-expenses-automation/2025-03-02-parking-expenses-automation.md b/src/_posts/parking-expenses-automation/2025-03-02-parking-expenses-automation.md deleted file mode 100644 index b450921..0000000 --- a/src/_posts/parking-expenses-automation/2025-03-02-parking-expenses-automation.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: post -title: Automating a Parking Expenses Report -date: 2025-03-02 14:38:00 Europe/Amsterdam -categories: paperless paperless-ngx nix python python3 ---- - -For the past year, I was consulting at a place where I could request -reimbursement of parking expenses. This had to be claimed using a standard Excel -template. - -Doing this manually every month seemed boring, so I automated a significant part -of that: - -- Fetch parking costs for a certain month from my Paperless-ngx instance -- Calculate costs after taxes and a grand total -- Fill in an Excel sheet with the details -- Convert the Excel sheet to PDF and append photos of parking costs - -Note: I don't expect anybody to have the same requirements as me here, but -hopefully pieces can be useful to some. The open-sourced project can be found -[here](https://git.kun.is/pim/parking-expenses). - -# Organizing the Parking Costs - -Each time I would park, I would accumulate a parking ticket. This ticket -contains the following information: - -- Date and time -- Car license plate -- Price -- VAT - -In order to automate the report, I had to have this information in a structured -way. Therefore, I turned to [Paperless-ngx](https://docs.paperless-ngx.com/). -Paperless-ngx is an self-hostable open-source service that helps digitalizing -paper documents. Third-party mobile apps help quickly scanning documents and -uploading them to the server. This is exactly what I did with the parking -tickets as well: each time I received a ticket, I would scan it with the -Paperless app. - -Paperless-ngx uses Optical Character Recognition (OCR) to cleverly extract the -date of the ticket automatically. I also created a custom label to indicate a -parking ticket, and I added custom fields to indicate a ticket's cost and VAT -amount. You can see this information in the screenshot below. - -![foo](ticket.png) _Translated from Dutch: aanmaakdatum means creation date, -parkeerkaart means parking ticket, BTW means VAT and bedrag means cost_ - -Paperless-ngx will also automatically learn to label the tickets with the -"parking ticket" label, based on the document's content. That just leaves the -VAT and price data points. Unfortunately I found the OCR to be too unreliable to -extract that text from the documents. Therefore, I had to manually set those two -data points for each ticket. - -Neat, we now have all parking tickets organized on Paperless-ngx! In order to -create a report, we can just query the Paperless-ngx API! - -# Using the Paperless-ngx API - -The remaining part of this post will be code explanations. You can find the full -open-sourced Python code -[here](https://git.kun.is/pim/parking-expenses/src/commit/645fd8b4e4e46bc8f3d8ba831ca28261cd3edb39/main.py). - -Paperless-ngx has [an API](https://docs.paperless-ngx.com/api/) we can now use -to query our parking tickets. I found the filtering logic pretty hard to -understand. Therefore I simply looked at what API calls the web UI makes with -the query I needed. This turned out to be something like this: - -```python -response = requests.get( - f"{paperless_ngx_url}/api/documents/?page_size=50&query=created:[{start_date} TO {end_date}]&tags__id__all={FILTER_TAG_ID}&correspondent__id__in={FILTER_CORRESPONDENT_ID}", - headers={"Authorization": f"Token {token}"}, -) -``` - -In the above code snippet, `FILTER_TAG_ID` is the ID of the "Parking ticket" -label and `FILTER_CORRESPONDENT_ID` is the ID of the correspondent (I included -this to filter out potential unrelated parking tickets). - -Perfect, this returns a JSON list of parking tickets we can use to create the -report! - -# Creating the Report - -All that's left is to fill in an Excel template now. This actually proved to be -the most annoying part but also the most boring part. I used the -[OpenPyXL](https://openpyxl.readthedocs.io/en/stable/) Python library to -manipulate the Excel sheets and basically just add a row for each parking -ticket. For code, check -[here](https://git.kun.is/pim/parking-expenses/src/commit/645fd8b4e4e46bc8f3d8ba831ca28261cd3edb39/main.py#L104). - -To convert the Excel sheet to a PDF, I used the (apparently now deprecated) -[unoconv](https://github.com/unoconv/unoconv) utility. It uses LibreOffice under -the hood for the conversion, which adds a 2GiB dependency for the project 🫠. - -Finally, to merge the report PDF with photos of the parking tickets, I used the -[PyPDF2](https://pypdf2.readthedocs.io/en/3.x/) Python library. - -I would now show some example output, but it contains quite some sensitive -information so unfortunately I can't... But I never had any complaints from the -finance department, so it worked great! diff --git a/src/_posts/parking-expenses-automation/ticket.png b/src/_posts/parking-expenses-automation/ticket.png deleted file mode 100644 index 2665ac3..0000000 Binary files a/src/_posts/parking-expenses-automation/ticket.png and /dev/null differ diff --git a/src/now.md b/src/now.md index 34e4efd..f0e58dd 100644 --- a/src/now.md +++ b/src/now.md @@ -6,12 +6,7 @@ excerpt: Things I am working on now comments: false --- -_Last updated: 2025-06-07_ +*Last updated: 2024-05-11* -- I'm **hacking** on my [k3s](https://k3s.io/) cluster using - [❄️ NixOS ❄️](https://nixos.org/) and [Kubenix](https://kubenix.org/) -- I'm **playing** Hades - -What games did I finish in 2025? - -- Celeste +- I'm **hacking** on my [k3s](https://k3s.io/) cluster using [❄️ NixOS ❄️](https://nixos.org/) and [Kubenix](https://kubenix.org/) +- I'm **playing** [🧩 The Talos Principle 2 🤖](https://store.steampowered.com/app/835960/The_Talos_Principle_2/)