Transformacja pliku tekstowego .txt na JSON

0

Witam,

chciałbym napisać skrypt w pythonie, który
-otworzy plik txt (raport ZUS przekonwertowany do pliku tekstowego)
-przeiteruje po każdej linii w poszukiwaniu danych osoby i okresów ubezpieczenia
-zapisze rekordy w json

Próbowałem to zrobić na różne sposoby, cały czas kończy się na tym samym problemie.
Po odpaleniu skrypt, tworzy mi pusty plik. Do tej pory myślałem, że problem leży w regexach, ale to chyba nie to.
Poniżej jedna z prób, oraz input.

Nie oczekuję gotowych rozwiązań tylko podpowiedzi jak ten temat ugryźć. :)

import re
import json

def parse_txt_to_json(txt_file):
    id_pattern = r"\bPESEL:\s+(\d+)\s+NAZWISKO:\s+([^\n]+)\s+IMIĘ PIERWSZE:\s+([^\n]+)"
    period_pattern = r"\|(\d+)\s+\|(\d{2}\.\d{2}\.\d{4})\s+\|(\d{2}\.\d{2}\.\d{4})\s+\|\d+\s+\|([TAK\s\|]+)\|"
    wniosek_pattern = r"WNIOSEK" #Pattern testowy <== nie dodaje się :(

    records = []
    current_record = {}
    with open(txt_file, "r", encoding="utf-8") as file:
        lines = file.readlines()
        for i, line in enumerate(lines):
            wniosek_match = re.search(wniosek_pattern, line)
            if wniosek_match:
                current_record["WNIOSEK"] = True

            id_match = re.search(id_pattern, line)
            if id_match:
                pesel, nazwisko, imie = id_match.groups()
                current_record = {
                    "PESEL": pesel,
                    "NAZWISKO": nazwisko,
                    "IMIĘ PIERWSZE": imie,
                    "okresy_ubezpieczenia": []
                }
                # Sprawdzenie kolejnych linii w poszukiwaniu okresów ubezpieczenia
                for j in range(i + 1, len(lines)):
                    period_match = re.search(period_pattern, lines[j])
                    if period_match:
                        kod_ubezpieczenia, data_zgloszenia, data_wyrejestrowania, podlega_ubezpieczeniom = period_match.groups()
                        okres_ubezpieczenia = {
                            "Kod ubezpieczenia": kod_ubezpieczenia,
                            "Data zgłoszenia": data_zgloszenia,
                            "Data wyrejestrowania": data_wyrejestrowania,
                            "Podlega ubezpieczeniom": [x.strip() for x in podlega_ubezpieczeniom.split("|")]
                        }
                        current_record["okresy_ubezpieczenia"].append(okres_ubezpieczenia)
                    else:
                        break
                records.append(current_record)

    json_data = json.dumps(records, ensure_ascii=False, indent=2)
    return json_data

plik_txt = "Baza_TEST_KOA.txt"
json_data = parse_txt_to_json(plik_txt)

with open("dane.json", "w", encoding="utf-8") as json_file:
    json_file.write(json_data)

Input txt

                               WNIOSEK O UPORZĄDKOWANIE OKRESÓW PODLEGANIA UBEZPIECZENIOM                               
                                         BEZPOŚREDNIO NA KONCIE UBEZPIECZONEGO                                          


       Dane identyfikacyjne płatnika:                                                                                   
       NIP:                     RODO                                                                         
       REGON:                   RODO                                                                                
       NAZWA SKRÓCONA:          RODO                                                              

       Proszę o uporządkowanie okresów podlegania ubezpieczeniom społecznym i ubezpieczeniu zdrowotnemu na koncie       
        ubezpieczonego:                                                                                                 

       Dane identyfikacyjne ubezpieczonego:                                                                             
       PESEL:             321321065                                                                                   
       NAZWISKO:          Testowy                                                                                       
       IMIĘ PIERWSZE:     Adam                                                                                         
       TYP oraz SERIA i NUMER dokumentu tożsamości:                                                                     
       Oświadczam, że ww. ubezpieczony podlegał ubezpieczeniom społecznym i/lub ubezpieczeniu zdrowotnemu zgodnie       
        z niżej wymienionymi danymi:                                                                                    
       ---------------------------------------------------------------------------------------------------------        
      |Kod tytułu   |Data        |Data        |Kod przyczyny   |             Podlega ubezpieczeniom             |       
      |ubezpiecz.   |zgłoszenia  |wyrej. od   |wyrejestrow.    |                                                |       
       ---------------------------------------------------------------------------------------------------------        
                                                               Emer.   |  Rent.   |  Chor.   |   Wyp.   |   Zdr.   |    
                                                            -------------------------------------------------------     
      |091100       | 01.01.1999 | 14.03.2003 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091100       | 11.08.2003 | 14.03.2005 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091100       | 09.11.2005 | 03.04.2006 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091100       | 03.10.2006 | 11.10.2006 |600             |    -    |    -    |    -    |    -    |   TAK   |      
      |091000       | 11.10.2006 | 16.10.2006 |100             |   TAK   |   TAK   |    -    |    -    |   TAK   |      
      |091000       | 03.04.2007 | 10.09.2007 |100             |   TAK   |   TAK   |    -    |    -    |   TAK   |      
      |091100       | 25.02.2008 | 03.07.2008 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091100       | 02.07.2009 | 28.10.2009 |600             |    -    |    -    |    -    |    -    |   TAK   |      
      |091101       | 28.10.2009 | 10.11.2009 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091101       | 21.12.2009 | 02.05.2011 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091101       | 06.09.2011 | 13.12.2011 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091101       | 17.04.2013 | 10.05.2013 |100             |    -    |    -    |    -    |    -    |   TAK   |      
      |091101       | 20.12.2013 | 05.02.2014 |100             |    -    |    -    |    -    |    -    |   TAK   |      
       ----------------------------------------------------------------------------------------------------------       


                                                               Podpis płatnika lub osoby upoważnionej                   



                                                               ............................. 13.07.2023                 
                                                               ....................                                     

4

Odpal debugera i zobacz co tam zie dzieje

2

Otwierasz plik i dzielisz na linie, przez to masz w 3 różnych liniach imię, nazwisko i pesel, a twój regex wszystkie trzy elementy na raz sprawdza, więc albo rzucisz to dla całego pliku bez zbędnej pętli i iterowania linia za linią lub poprawisz sobie pattern, gdzie ten pattern i tak nie usuwa białych znaków, ale powinien działać jako tako.

W debuggrze jak wstawisz sobie conditional breakpoint na 16 linii z i == 14, to możesz sobie dynamicznie podejrzeć.

debugg.png

0

@GodOfCode: Dzięki za podpowiedź, rozwiązałem już problem. Faktycznie chodziło o źle dopasowany pattern.
Napisałem to od nowa, teraz skrypt dzieli tekst na bloki (wnioski), pozbywa się spacji, szuka i zapisuje potrzebne dane.

kod:

import re
import tkinter as tk
from tkinter import filedialog
import json

class IdentificationDataExtractor:
    def __init__(self):
        self.file = None
        self.blocks = []
        self.identification_data_list = []

    def open_file(self):
        file_path = filedialog.askopenfilename()
        if file_path:
            self.file = open(file_path, "r",encoding="utf=8")
            self.extract_blocks()
            self.extract_identification_data()

    def extract_blocks(self):
        if self.file is not None:
            try:
                content = self.file.read()
                self.blocks = content.split("WNIOSEK")[1:]
                self.blocks = ["WNIOSEK" + block.replace(" ", "") for block in self.blocks]
            except Exception as e:
                print(f"Error: An error occurred while processing the file: {e}")
        else:
            print("Error: File is not open. Call 'open_file()' first to open the file.")

    def extract_identification_data(self):
        self.identification_data_list = []

        for block in self.blocks:
            match = re.search(r'PESEL:(\d+)\nNAZWISKO:(\w+)\nIMIĘPIERWSZE:(\w+)', block)
            if match:
                pesel = match.group(1)
                last_name = match.group(2)
                first_name = match.group(3)

                # Extracting OKRESY_UBEZPIECZENIA using a regular expression pattern
                okresy_ubezpieczenia_pattern = r"\|(\d+)\|(\d{2}\.\d{2}\.\d{4})\|(\d{2}\.\d{2}\.\d{4})\|(\d+)\|([^\|]*)\|([^\|]*)\|([^\|]*)\|([^\|]*)\|(\w+)\|"
                okresy_ubezpieczenia_matches = re.findall(okresy_ubezpieczenia_pattern, block)
                okresy_ubezpieczenia = []
                for match in okresy_ubezpieczenia_matches:
                    numer = match[0]
                    data_od = match[1]
                    data_do = match[2]
                    kod_przyczyny = match[3]
                    emer = match[4]
                    rent = match[5]
                    chor = match[6]
                    wyp = match[7]
                    zdrow = match[8]

                    okres_ubezpieczenia = {
                        "NUMER": numer,
                        "DATA_OD": data_od,
                        "DATA_DO": data_do,
                        "KOD_PRZYCZYNY": kod_przyczyny,
                        "emer": emer,
                        "rent": rent,
                        "chor": chor,
                        "wyp": wyp,
                        "zdrow": zdrow
                    }
                    okresy_ubezpieczenia.append(okres_ubezpieczenia)

                identification_data = {
                    "PESEL": pesel,
                    "NAZWISKO": last_name,
                    "IMIĘPIERWSZE": first_name,
                    "OKRESY_UBEZPIECZENIA": okresy_ubezpieczenia
                }
                self.identification_data_list.append(identification_data)

    def save_to_json(self):
        if self.identification_data_list:
            output_file_path = filedialog.asksaveasfilename(defaultextension=".json")
            if output_file_path:
                with open(output_file_path, "w") as output_file:
                    json.dump(self.identification_data_list, output_file, indent=4)
                print("Data saved to JSON successfully.")
            else:
                print("Save operation canceled.")
        else:
            print("Error: No data to save. Call 'extract_blocks()' first to process the file.")

def main():
    root = tk.Tk()
    root.title("Identification Data Extractor")

    extractor = IdentificationDataExtractor()

    frame = tk.Frame(root)
    frame.pack(padx=20, pady=20)

    open_file_button = tk.Button(frame, text="Open File", command=extractor.open_file)
    open_file_button.pack(side=tk.LEFT)

    save_to_json_button = tk.Button(frame, text="Save to JSON", command=extractor.save_to_json)
    save_to_json_button.pack(side=tk.LEFT)

    root.mainloop()

if __name__ == "__main__":
    main()


Mam jeszcze jeden mniejszy problem.
Jak zrobić, żeby w output, były polskie znaki?
input.txt jest wygenerowany z kodowaniem cp1250, ale w skrypcie mogę go otworzyć tylko przed encoding="utf-8".
Teoretycznie to i utf-8 i cp1250 powinny obsługiwać polskie znaki, ale w output, jakoś źle odczytuje.

1 użytkowników online, w tym zalogowanych: 0, gości: 1