top of page
  • Writer's pictureMateusz Kaczyński

Receipts



okłada z biedronką




KB000011




Wstęp


Stworzenie jednego uniwersalnego kodu do każdego rodzaju udokumentowanego paragonu jest ogromnym wyzwaniem z uwagi na jakość, krzywizny, kąty, światło, odległość, format, metody wykonania oraz inne tego typu czynniki. Uzyskanie powtarzalnego procesu, wiąże się z długim okresem testowania, co znacznie oddala moment wdrożenia.


Wyzwaniem dla mnie było:


  • dostosowanie odpowiednich narzędzi, które obsłużą obraz w PDF

  • odpowiednie połączenie obrazów, dla PDF zawierających więcej niż jedną stronę

  • odpowiednie wyostrzenie uzyskanego PNG tak aby odczyt tekstu był jak najdokładniejszy

  • stworzenie algorytmu oczyszczającego plik tekstowy ze zbędnych informacji i uzyskanie powtarzalnej zawartości która zawsze w odpowiednim miejscu się zaczyna i kończy

  • przekonwertowanie linijek tekstu na obiekty bez utraty danych, w tym wyłączenie kolumny PTU, uwzględnienie rabatów rozłożonych na 3 linijki z pustymi przestrzeniami.

  • połączenie wszystkiego w jeden flow tak, aby finalnie uzyskać plik wynikowy z odpowiednio pogrupowanymi obiektami oraz posortowaną tablicą



Dlaczego w ogóle zająłem się tematem?


Historia tego projektu pochodzi sprzed kilku miesięcy, kiedy to wpadłem na pomysł, aby stworzyć mikro serwis do rejestrowania paragonów. Przeglądając popularne aplikacje takie jak "Pan Paragon", nie spotkałem się z automatycznym rozpoznawaniem danych. Najczęściej był to storage z formularzem i dashboardy do tego, co manualnie klient wprowadza. Jak się okazywało, funkcje premium ( a tak naprawdę bazowe/konieczne )

wiązały się z kosztami, a przecież ten sam efekt można było uzyskać, korzystając z Google Sheets, czy Excel a zdjęcia przechowywać w chmurze.


Sprawdziłem wówczas tzw "elektroniczny paragon" https://www.podatki.gov.pl/e-paragony/ ale okazało się, że niewiele punktów z tego korzysta.


Finalnie, pobrałem kilka aplikacji ( lidl, biedronka, żabka, rossman, McDonald ) - w żadnej nie znalazłem JSON. Co ciekawe, lidl udostępnia dane w PNG a biedronka OBRAZ w PDF - XD. O żabce, rossman, McDonald, nawet nie wspominam, bo tam nie ma czegoś takiego jak e-paragon. No comment....


Oczywiście, gdy zwróciłem się do Biedronki o to, aby udostępnili również format JSON, dostałem odmowę. Pingowałem temat przy różnych zgłoszeniach związnych z aplikacją, przemycając na różne sposoby uzasadnienia, prośby, no ale bezskutecznie.



Podsumowując


Jak widzisz, są takie firmy jak PKO Bank Polski S.A które umożliwiają pobranie historii w różnych formatach, i są takie firmy jak żabka, które tworzą aplikację typu "Dump component"

I to nie jest kwestia "kasy".



Rekomendacje


Jeśli jesteś PM-em, rozważ moją opinię. Aplikacja w sklepie spożywczym ma realizować dwa najważniejsze cele:


  • Historia zakupów

  • Punkt rabatowe


Klient chce zrealizować szybko swój cel, tak jak robi to w przypadku przelewu, blika, e-maila, i wyjść. Dlatego skupienie się na takich funkcjach jak:


  • Pobierz całą historię z okresu X - Y ( wybierz format pliku )

  • Dashboard typu: Na co wydałem | Ile wydałem w okresie X - Y

  • Ile mam punktów i na co mogę je przeznaczyć


spowoduje zwiększenie ruchu, ponieważ konsumenci zobaczą wartość. Rozważ także czy aby napewno potrzebujesz mieć aplikację w sklepie PLAY/AppStore, bo może warto pójść w ślady Facebook-a. Pamiętaj, że społeczeństwo staje się coraz bardziej zdigitalizowane. Na każdym kroku widzimy że jakaś aktywność podlega analizie ( głupie termometry mają aplikacje z historią i opcją pobrania danych do pliku csv ). Tworzenie narzędzia do wyświetlania "gazetki" i płacenie za "miejscówkę" przypomina absurd związany z aplikacjami typu "latarka".


Tyle tytułem wstępu. Co dalej?


W dalszej części, skupie się już tylko na omówieniu najważniejszych informacji, a rozpocznę od założeń:



  1. Posiadasz kartę stałego klienta Biedronka

  2. Nie zamierzasz prowadzić wnikniliwej analizy każdego produktu ( zmienność cen, ile razy był przeceniony, itp )

  3. Jesteś chętny rozwijać projekt według własnych potrzeb

  4. Nie przeszkadza ci konsolowy program ( ubóstwiam. GUI może nie istnieć )

  5. Nie jesteś wrażliwy na wzorce projektowe, sugar syntax itp.



[ WAŻNE ] OBSZARY DO POPRAWY


Ta sekcja jest dość istotna, dlatego że pokazuje niedociągnięcia, ale także potencjalne kierunki rozwoju.




NAZWY PRODUKTÓW


  • Część wartości przypisanych do "product" będzie wyglądać następująco:



[ PRZYKŁAD ]


zrzut ekranu - paragon

{
    "product": "OueenMaT0x10 A",
    "quantity": "1.000",
    "price": "4,49",
    "value": "4,49",
    "category": "",
    "date_time": "18.05.2024 09:04"
},
{
    "product": "Cnustl2UsztśWUueen A",
    "quantity": "1.000",
    "price": "6,29",
    "value": "6,29",
    "category": "",
    "date_time": "18.05.2024 09:04"
},


Sorry, ale często nawet ja mam problem, żeby odczytać co jest napisane, a co dopiero jakaś biblioteka, która oprócz przetwarzania tekstu z obrazu musi jeszcze uwzględnić polskie znaki. Obiecuje, że będę jeszcze pracował nad poprawą parametrów odczytu i doprecyzuje w jakimś stopniu te nazwy, niemniej, jak wspomniałem w punkcie o analizach, zmierzam w kierunku pracy na kategoriach, a nie na często zmieniających się nazwach produktów tak niedbale opisanych na dokumencie.


JAK AI RADZI SOBIE Z TAKIMI WPISAMI?



[ inserted PNG ]

zrzut ekranu - paragon

[ AI result ]

zrzut ekranu - chatgpt


Naprawdę nieźle!!!! Tylko to nie jest ten string. I na pewno da się to wyciągnąć. To kwestia zawzięcia.


[ Poprawny wynik ]


{
    "product": "Chust(chyba spacja)QueenMe10x10",
    "quantity": "1.000",
    "price": "4,49",
    "value": "4,49",
    "category": "",
    "date_time": "18.05.2024 09:04"
},
{
    "product": "Chust120szt3WQueen",
    "quantity": "1.000",
    "price": "6,29",
    "value": "6,29",
    "category": "",
    "date_time": "18.05.2024 09:04"
},



Czy teraz już rozumiesz dlaczego, udostępnienie formatu JSON przez sklepy spożywcze w ich aplikacjach to oszczędność czasu, środków oraz duże ułatwienie dla konsumentów?

A gdyby jeszcze dorzucić prosty REST API, ehh...


W ramach CI/CD zamierzam stworzyć interfejs umożliwiający poprawę tego typu pozycji, i powiązany z tym machine learning, który uzupełni kategorie, po zatwierdzeniu zmian nazw produktów ( co również będzie edytowalne na życzenie ).







  • Wdrożenie flow jako część procesu


Każdego, kto zamierza rozwijać ten projekt, wdrożyć go jako część innego procesu, zachęcam do przejrzenia kodu, dostosowania, poprawienia, uproszczenia. W tej materii przeważnie jest co usprawniać. To zawsze cieszy, gdy coś co zrobiłeś w ramach @openSource, zainspirowało kogoś do stworzenia czegoś jeszcze lepszego, z czego skorzysta więcej ludzi.







  • Wdrożenie AI


Chyba nie muszę nikogo informować o tym, że AI z paragonami radzi sobie równie dobrze, a nawet lepiej. AI oprócz samego zwróconego TXT/JSON/CSV/HTML może dodatkowo uzupełnić kategorie ( lepiej / gorzej, zawsze możesz dostosować ) czy przeprowadzić analizy ( nawet te bardziej skomplikowane ).


Dla chętnych udostępniam swojego asystenta AI, którego wciąż testuje i póki co, wyniki są zgodne z oczekiwaniami. Link znajdziesz na końcu artykułu.


[ KRÓTKA REKLAMA ]


Asystent AI powstał w oparciu, o pierwszy modół kursu "MAG SŁOWA" wydanego przez team MISTRZ AI. Zachęcam do sprawdzenia oferty. Bez zbędnego wysiłku, odpowiednio konfigurując parametry, jesteś wstanie uzyskać cały proces obejmujący tworzenie i walidację.


Więcej szczegółów na: https://mistrz.ai/#modules-1-4


A tu krótkie demo z kursu. Serdecznie zapraszam!





  • Wdrożenie pipenv


Nie ma to, jak dobre zarządzanie projektem. Warto o to zadbać.




RECEIPTS FLOW




Za kontrolę flow odpowiada plik main.py.



Status po uruchomieniu. NIE PRZERYWAJ JEŚLI NIE MUSISZ ! ! !


[ do not interupt ]

zrzut ekranu - progress bar


Status po wykonaniu całego flow.



[ final result ]

zrzut ekranu - result



>>> IMPORTS <<<



Libraries




  1. datetime

  2. os

  3. shutil

  4. pdf2image

  5. json

  6. subprocess

  7. pytesseract

  8. PIL (Image)

  9. cv2

  10. re

  11. glob

  12. collections (defaultdict)


chyba wszystkie... XD




>>> FLOW Controller description <<<





Sprawdza strukturę katalogów. Jeżeli coś się nie zgadza, to wyrzuca criticala i mamy stop process. Wykonanie core process następuje po uprzednim zweryfikowaniu zawartości katalogu z surowymi plikami OBRAZ w PDF. Jeżeli jest akceptacja, przenoszony jest plik z katalogu ' receitps/downloaded ' do ' input/ '. Po zakończeniu przenoszony jest plik JSON z

' output/json ' do ' output/collection '. Dalej, następuje oczyszczenie katalogów roboczych. Po czyszczeniu przenoszony jest plik z ' input/ ' do ' receipts/done ' i pętla zamyka obieg. Po wykonaniu wszystkich iteracji, wykonywany jest merge wszystkich obiektów z plików w

' output/collection ' do jednego pliku bazowego i zapis w katalogu ' output/target '. Następnie obiekty w pliku bazowym zostają pogrupowane zgodnie z datami i godzinami paragonów. Finalnie główna tablica jest sortowana od najstarszej daty a zmiany zostają zapisane i następuje zakończenie programu.





>>> Image processing description <<<





pdf_to_png.py




Odczytuje pojedynczy plik PDF z katalogu ' input/ ' pozyskując z niego pliki PNG. Pliki z rozszerzeniem *.png zostają umieszczone w katalogu ' extraced_png '.


combine_images.py




Odczytuje zawartość katalogu ' output/extraced_png ' i tworzy jeden plik PNG według określonego wzoru. Wynik w postaci jednego pliku *.png

zostaje zapisany w katalogu ' output/combined_png '.



prepare_raw_txtFile.py




Odczytuje pojedynczy plik PNG z katalogu ' output/combined_png ' i przetwarza tekst z PNG do postaci surowej. Wynikowy plik *.txt zapisywany jest w katalogu ' output/txt '



clear_data_in_txt.py




Odczytuje pojedyńczy plik TXT z katalogu ' output/txt ' i oczyszcza go z nie potrzebnych danych uzyskując zawsze ten sam zakres tekstu. Ten moduł modyfikuje plik źródłowy.






Odczytuje pojedynczy plik TXT z katalogu ' output/txt ' i przetwarza tekst na obiekty JSON. Plik oznaczony datą i godziną paragonu zostaje zapisany w katalogu ' output/json '



find_invalid_objects.py




Odczytuje pojedynczy plik JSON z katalogu ' output/json ' i sprawdza, czy znajduje się w nim obiekt, który posiada pewne słowa kluczowe ( nagłówki tabeli ).

Jeżeli tak, modyfikuje plik źródłowy





>>> Error handling <<<








Funkcja zapisuje dane do pliku logs.txt, który znajduje się w katalogu ' logs/ '. Zapisywany log to: ${date & time} | ${file_name} | ${function_name} | ${message}. W konsoli mogą pojawić się dwa rodzaje komunikatów: "Check logs" oraz błąd podczas sprawdzania struktury katalogów.





>>> Additional information <<<




Zadbałem oto aby obieg dokumentacji można było wykorzystać do różnych scenariuszy. Mimo to jeszcze raz zachęcam do refaktoryzacji. Poniżej opis stanu po obiegu:

Proces pozostawia:




  • W katalogu ' receitps/done ': pliki źródłowe

  • W katalogu ' output/collection ': JSON's z każdego paragonu ( opisane datą i godziną z paragonu [ FORMAT : ISO ] )

  • W katalogu ' output/target ': plik wynikowy JSON zawierający całą kolekcję.









Jeżeli potrzebujesz pomocy w zainstalowaniu i konfiguracji napisz do mnie:






TRZY KROKI DO STARTU:




[ STEP 1 ]




Pobierz projekt z GitHub. Upewnij się, że masz wszystkie biblioteki.

[ WAŻNE ] Przeczytaj plik README.md




[ STEP 2 ]




Wejdź na https://moja.biedronka.pl/, zaloguj się na swoje konto, i zaimportuj paragony

( flow przetwarza wiele plików )




zrzut ekranu - biedronka




[ STEP 3 ]




Uruchom plik main.py. Obserwuj progress bar w terminalu.

Plik wynikowy znajdziesz w ' output/target '.







RECOMMENDED TOOLS




JetBrains IDEs






[ WAŻNE ] NIE ZAPOMNIJ O PORÓWNANIU SUMY KONTROLNEJ!!!!
zrzut ekranu - pycharm








Canonical Ubuntu. Dzięki temu systemowi możesz skupić się na tym co najważniejsze. Polecam go do pracy, nauki i rozwoju osobistego.



logo ubuntu












OpenAI | LLM. Skrócona wersja użycia mojego asystenta:



  • Wklej PNG ( WAŻNE: nie obraz w PDF )

  • W wiadomości napisz tylko słowo kluczowe "Start"





W wyniku otrzymasz dane na wzór:



zrzut ekranu - chatgpt


Osoby zainteresowane budową modelu, również zachęcam do kontaktu. Krótki opis poniżej.



METAWARSTWY:

  • Uzgodniono rzeczywistość

  • Podano kontekst sytuacyjny

  • Opisano problem

  • Wskazano rolę

  • Podano rozmówcę

  • Opisano zadanie, rezultat oraz strukturę



Temperatura: nie określone

Ilość tokenów: nie określone



ZAŁOŻENIA WYNIKOWE:

Rabat = 1 else 0

Data = dd.mm.yyyy w cudzysłów

Podsumowanie = 0

Ilość = zaokrąglenie do całości 0 do 1 = 1; analogicznie > 1


STAŁE:

Załącznik = PNG > 0

Sklep = 'biedronka'

Kategoria = 'undefined'









PDF to PNG. Jeżeli korzystasz tylko z LLM, na pewno przyda Ci się to narzędzie










Komentarze


bottom of page