CoolFace
Modelpublic

pemix09/paperstack_document_data_retrieval

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes11downloads
clean_scans.py259 linesDownload Raw Back to root
1import os2import shutil3import ollama4from pathlib import Path5 6# --- KONFIGURACJA ---7ROOT_FOLDER = "scans"8REJECTED_FOLDER = "_ODRZUCONE"9HISTORY_FILE = "clean_scans_processed.txt"10MODEL_NAME = "llama3.2-vision"11IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.webp', '.heic'}12 13# Foldery, których NIE ruszać (bezpieczne)14SAFE_FOLDERS = {'documentScan', 'other'}15 16# --- SZCZEGÓŁOWE KRYTERIA DLA KAŻDEGO TYPU ---17# AI otrzyma instrukcję: "Szukaj [Tytuł]. Wymagane cechy: [Cechy]"18DOCUMENT_TYPES = {19    # --- PODATKI (PIT) - WYMÓG SYMBOLU W NAGŁÓWKU ---20    'pit11': ('Deklaracja PIT-11',21              'Wyraźny symbol "PIT-11" (zazwyczaj lewy górny róg), tabela z przychodami, dane płatnika i podatnika'),22    'pit37': ('Zeznanie PIT-37',23              'Wyraźny symbol "PIT-37" (duży druk w nagłówku/rogu), biało-zielony lub biały formularz, pola na PESEL'),24    'pit36': ('Zeznanie PIT-36', 'Wyraźny symbol "PIT-36" w nagłówku formularza, sekcje działalności gospodarczej'),25    'pit36L': ('Zeznanie PIT-36L', 'Wyraźny symbol "PIT-36L" (podatek liniowy) w nagłówku'),26    'pit28': ('Zeznanie PIT-28', 'Wyraźny symbol "PIT-28" (ryczałt) w nagłówku formularza'),27    'pit38': ('Zeznanie PIT-38', 'Wyraźny symbol "PIT-38" (kapitały pieniężne) w nagłówku'),28    'pit39': ('Zeznanie PIT-39', 'Wyraźny symbol "PIT-39" (nieruchomości) w nagłówku'),29    'pit5': ('Deklaracja PIT-5', 'Symbol "PIT-5" widoczny na formularzu'),30    'pit8C': ('Informacja PIT-8C', 'Symbol "PIT-8C" widoczny w nagłówku formularza'),31    'vat7': ('Deklaracja VAT-7 / JPK', 'Symbol "VAT-7" lub nagłówek JPK_V7, tabela rozliczenia podatku VAT'),32    'cit8': ('Zeznanie CIT-8', 'Symbol "CIT-8" w nagłówku, dotyczy osób prawnych'),33    'pcc3': ('Deklaracja PCC-3', 'Symbol "PCC-3" (podatek od czynności cywilnoprawnych) w nagłówku'),34 35    # --- FINANSE ---36    'invoice': ('Faktura VAT',37                'Słowo "Faktura" lub "Invoice", tabela z kolumnami netto/vat/brutto, dane sprzedawcy i nabywcy'),38    'proformaInvoice': ('Faktura Proforma',39                        'Wyraźny napis "Proforma" lub "Zamówienie", brak skutków księgowych (wygląda jak faktura)'),40    'receipt': ('Paragon fiskalny',41                'Wąski wydruk z drukarki fiskalnej, logo sklepu na górze, stawki PTU na dole, data i godzina'),42    'utilityBill': ('Rachunek za media',43                    'Logo dostawcy (prąd/gaz/woda/internet), wykres zużycia, kwota "do zapłaty", numer konta'),44    'bankStatement': ('Wyciąg bankowy', 'Logo banku, lista operacji z datami i kwotami, saldo początkowe i końcowe'),45    'loanAgreement': ('Umowa kredytowa',46                      'Tytuł "Umowa kredytu" lub "Umowa pożyczki", harmonogram spłat, pieczęci banku'),47    'insurancePolicy': ('Polisa ubezpieczeniowa',48                        'Tytuł "Polisa", numer polisy, okres ubezpieczenia, przedmiot ubezpieczenia (auto/dom)'),49 50    # --- PRAWO ---51    'notarialDeed': ('Akt notarialny',52                     'Godło państwowe (orzeł), pieczęć notariusza, charakterystyczny sznurek (repetytorium), tytuł "Akt Notarialny"'),53    'courtJudgment': ('Wyrok sądu',54                      'Godło państwowe, nagłówek "Wyrok w imieniu Rzeczypospolitej Polskiej", sygnatura akt'),55    'powerOfAttorney': ('Pełnomocnictwo',56                        'Tytuł "Pełnomocnictwo" lub "Upoważnienie", dane mocodawcy i pełnomocnika, podpis'),57    'employmentContract': ('Umowa o pracę',58                           'Tytuł "Umowa o pracę", określenie stanowiska, wynagrodzenia, wymiaru etatu'),59    'mandateContract': ('Umowa zlecenie',60                        'Tytuł "Umowa zlecenie", określenie czynności do wykonania, stawka godzinowa/miesięczna'),61    'taskContract': ('Umowa o dzieło', 'Tytuł "Umowa o dzieło", określenie konkretnego rezultatu/dzieła'),62    'b2bContract': ('Kontrakt B2B', 'Umowa współpracy biznesowej, dane dwóch firm (NIP), określenie zasad współpracy'),63    'nonCompeteAgreement': ('Zakaz konkurencji',64                            'Umowa lub aneks o zakazie konkurencji, określenie kar umownych i okresu obowiązywania'),65    'lawsuit': ('Pozew sądowy', 'Pismo procesowe, nagłówek "Pozew", oznaczenie sądu i stron, uzasadnienie'),66 67    # --- OSOBISTE ---68    'idCard': ('Dowód osobisty', 'Plastikowa karta, zdjęcie twarzy, godło, napis "Rzeczpospolita Polska"'),69    'passport': ('Paszport', 'Strona z danymi, zdjęcie, hologramy, dolny pasek maszynowy (<<<)'),70    'birthCertificate': ('Akt urodzenia', 'Odpis aktu stanu cywilnego, godło, pieczęć urzędu stanu cywilnego (USC)'),71    'marriageCertificate': ('Akt małżeństwa', 'Odpis aktu małżeństwa, dane małżonków, pieczęć USC'),72    'deathCertificate': ('Akt zgonu', 'Odpis aktu zgonu, czarna ramka lub standardowy druk USC, pieczęć'),73    'peselConfirmation': ('Zaświadczenie PESEL',74                          'Biały druk urzędowy, potwierdzenie nadania numeru PESEL, pieczęć gminy/urzędu'),75    'drivingLicense': ('Prawo jazdy', 'Różowa plastikowa karta, zdjęcie, ikony pojazdów na rewersie'),76    'schoolCertificate': ('Świadectwo szkolne',77                          'Gilosz (ozdobne tło), godło, nazwa szkoły, oceny, czerwony pasek (opcjonalnie)'),78    'universityDiploma': ('Dyplom studiów',79                          'Ozdobny papier, godło uczelni, tytuł zawodowy (licencjat/magister/inżynier), pieczęć sucha lub tuszowa'),80    'professionalCertificate': ('Certyfikat zawodowy',81                                'Nazwa kursu/szkolenia, imię i nazwisko uczestnika, podpis organizatora'),82    'cv': ('CV / Życiorys', 'Układ sekcyjny: Doświadczenie, Edukacja, Umiejętności, często zdjęcie, dane kontaktowe'),83 84    # --- ZDROWIE ---85    'sickLeave': ('Zwolnienie L4', 'Formularz ZUS ZLA (zielony/biały) lub wydruk e-ZLA, dane pacjenta i lekarza'),86    'prescription': ('Recepta', 'Kod kreskowy (góra/dół), "Recepta", lista leków, dane świadczeniodawcy'),87    'medicalResults': ('Wyniki badań',88                       'Wydruk laboratoryjny, nazwy parametrów (morfologia, glukoza itp.), normy i wyniki'),89    'referral': ('Skierowanie', 'Tytuł "Skierowanie", rozpoznanie (kod ICD-10), pieczęć lekarza kierującego'),90    'medicalHistory': ('Historia choroby/Wypis', 'Karta informacyjna leczenia szpitalnego, epikryza, zalecenia'),91    'vaccinationCard': ('Karta szczepień', 'Książeczka lub karta, tabela z datami szczepień i nazwami preparatów'),92    'sanitaryBooklet': ('Książeczka sanepidowska',93                        'Mała książeczka, wpisy badań na nosicielstwo, pieczątki stacji sanitarno-epidemiologicznej'),94 95    # --- NIERUCHOMOŚCI / AUTO ---96    'propertyDeed': ('Akt własności', 'Akt notarialny dotyczący przeniesienia własności nieruchomości'),97    'landRegistry': ('Księga wieczysta', 'Wydruk z EKW (Elektroniczne Księgi Wieczyste), działy I-IV'),98    'rentalAgreement': ('Umowa najmu', 'Tytuł "Umowa najmu lokalu", określenie czynszu, kaucji, adres lokalu'),99    'registrationCertificate': ('Dowód rejestracyjny',100                                'Składany dokument (błękitno-żółty), hologram, pola z kodami A, B, C'),101    'vehicleHistory': ('Karta pojazdu', 'Czerwona książeczka (stary typ) lub wydruk historii z CEPiK'),102    'landMap': ('Mapa geodezyjna', 'Rysunek techniczny terenu, granice działek, numery działek, pieczęć starostwa'),103    'technicalInspection': ('Przegląd techniczny',104                            'Zaświadczenie ze stacji kontroli pojazdów lub pieczątka w dowodzie rejestracyjnym'),105 106    # --- INNE ---107    'application': ('Wniosek/Podanie', 'Nagłówek "Wniosek" lub "Podanie", adresat (urząd/firma), prośba, podpis'),108    'certificate': ('Zaświadczenie', 'Tytuł "Zaświadczenie", potwierdzenie faktu przez instytucję, pieczęć'),109    'authorization': ('Upoważnienie', 'Tytuł "Upoważnienie", dane osoby upoważnianej do czynności, podpis')110}111 112DEFAULT_CRITERIA = "Oficjalny dokument z czytelnym tekstem i pieczęciami."113 114 115# --- LOGIKA ---116 117def load_history():118    if not os.path.exists(HISTORY_FILE):119        return set()120    with open(HISTORY_FILE, 'r', encoding='utf-8') as f:121        return set(line.strip() for line in f if line.strip())122 123 124def mark_as_done(rel_path):125    with open(HISTORY_FILE, 'a', encoding='utf-8') as f:126        f.write(f"{rel_path}\n")127 128 129def check_document_strict(file_path, doc_name, criteria):130    """131    Wysyła zapytanie do Llama Vision z BARDZO rygorystycznymi wymogami.132    """133    print(f" (Weryfikacja: {doc_name} -> {criteria[:30]}...)", end="", flush=True)134 135    prompt = f"""136    Działaj jako rygorystyczny audytor dokumentów. Twoim zadaniem jest potwierdzenie autentyczności typu dokumentu.137 138    OBRAZ: {file_path}139    OCZEKIWANY TYP: {doc_name}140 141    KRYTYCZNE WYMAGANIA WIZUALNE (MUST HAVE):142    - {criteria}143 144    NATYCHMIASTOWE ODRZUCENIE (REJECT IF):145    1. To jest zrzut ekranu (widać paski przeglądarki, kursor, interfejs telefonu).146    2. To jest zdjęcie ekranu monitora (widać piksele/morę).147    3. Dokument jest nieczytelny, rozmazany lub ucięty w sposób uniemożliwiający identyfikację.148    4. Brakuje kluczowych elementów wymienionych w wymaganiach (np. brak napisu "PIT-11" na rzekomym PIT-11).149    5. To jest zdjęcie przedmiotu, zwierzęcia lub osoby (selfie), a nie skan dokumentu.150 151    DECYZJA:152    Czy obraz spełnia wszystkie kryteria dla {doc_name}?153    Odpowiedz TYLKO jednym słowem: TAK lub NIE.154    """155 156    try:157        response = ollama.chat(158            model=MODEL_NAME,159            messages=[{160                'role': 'user',161                'content': prompt,162                'images': [str(file_path)]163            }]164        )165        # Czyszczenie odpowiedzi (np. "TAK." -> "TAK")166        answer = response['message']['content'].strip().upper().replace('.', '')167 168        if "TAK" in answer or "YES" in answer:169            return True170        return False171 172    except Exception as e:173        print(f" ❌ Błąd API: {e}")174        return None175 176 177def main():178    base_path = Path(ROOT_FOLDER)179    rejected_path = base_path / REJECTED_FOLDER180 181    if not base_path.exists():182        print(f"❌ Folder '{ROOT_FOLDER}' nie istnieje!")183        return184 185    if not rejected_path.exists():186        rejected_path.mkdir()187 188    processed_files = load_history()189    print(f"📂 Historia: {len(processed_files)} plików pominiętych.")190    print(f"🚀 Start audytu wizualnego (Model: {MODEL_NAME})...")191 192    # Pobieramy listę folderów w katalogu scans193    subdirs = [d for d in base_path.iterdir() if d.is_dir()]194 195    for folder in subdirs:196        folder_name = folder.name197 198        # 1. Pomijanie folderów specjalnych199        if folder_name == REJECTED_FOLDER:200            continue201 202        # 2. Pomijanie folderów "bezpiecznych" (np. documentScan - szybki zrzut)203        if folder_name in SAFE_FOLDERS:204            # print(f"⏩ Pomijam bezpieczny folder: {folder_name}")205            continue206 207        # Pobieranie kryteriów z mapy208        if folder_name in DOCUMENT_TYPES:209            doc_name, doc_criteria = DOCUMENT_TYPES[folder_name]210        else:211            # Jeśli folderu nie ma w słowniku, można go pominąć lub użyć domyślnych212            # print(f"⏩ Folder nieznany w systemie: {folder_name} (pomijam)")213            continue214 215        files = [f for f in folder.iterdir() if f.suffix.lower() in IMAGE_EXTENSIONS]216        if not files:217            continue218 219        print(f"\n📂 Audyt folderu: [{folder_name}]")220 221        for file_path in files:222            rel_path_str = str(file_path.relative_to(base_path))223 224            # Sprawdzenie historii225            if rel_path_str in processed_files:226                continue227 228            print(f"  👁️  Plik: {file_path.name}...", end="", flush=True)229 230            is_valid = check_document_strict(file_path, doc_name, doc_criteria)231 232            if is_valid is True:233                print(" ✅ OK")234                mark_as_done(rel_path_str)235 236            elif is_valid is False:237                print(" 🗑️  ODRZUCONY")238 239                # Przenoszenie240                target_dir = rejected_path / folder_name241                if not target_dir.exists():242                    target_dir.mkdir(parents=True)243 244                try:245                    shutil.move(str(file_path), str(target_dir / file_path.name))246                    mark_as_done(rel_path_str)  # Oznaczamy jako przetworzony (usunięty)247                except Exception as e:248                    print(f"     [!] Błąd przenoszenia: {e}")249            else:250                print(" ⚠️ Błąd modelu (spróbujemy ponownie).")251 252    print("\n✨ Zakończono.")253 254 255if __name__ == "__main__":256    try:257        main()258    except KeyboardInterrupt:259        print("\n🛑 Zatrzymano.")