File size: 6,999 Bytes
24675b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0f09876
24675b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

import pandas as pd
import re
import PyPDF2

import unicodedata

class DocumentProcessor:
    """
    Classe per elaborare il testo dei documenti:
    - Rimuove righe inutili basandosi su ESCLUDI_RE.
    - Unisce righe spezzate in paragrafi coerenti, mantenendo gli elenchi puntati al paragrafo precedente.
    - Suddivide il testo in paragrafi ben formattati.
    """

    # 🔹 Lista di espressioni regolari per rimuovere righe indesiderate
    ESCLUDI_RE = [
        r'Pagina\s+\d+\s+di\s+\d+',  # "Pagina x di y"
        r'^Foglio\s+\d+',             # "Foglio 3"
        r'^\s*$',                     # Righe vuote
        r'^Codice\s+Documento:\s+\w+', # "Codice Documento: ABC123"
        r'^Firma\s+Digitale',          # "Firma Digitale"
    ]
    @staticmethod
    def normalizza_testo_avanzato(testo):
        if pd.isna(testo):
            return ""
        testo = str(testo).strip()
        testo = testo.replace("’", "'").replace("‘", "'")  # Sostituire tipi diversi di apostrofi
        testo = unicodedata.normalize("NFKD", testo)  # Normalizza Unicode (es: accenti, simboli)
        return " ".join(testo.split())  # Rimuove spazi multipli

    @staticmethod
    def spezza_in_frammenti(testo: str, numero_frammenti : int = 1)  ->list :
        if numero_frammenti <= 0:
            raise ValueError("Il numero di frammenti deve essere un intero positivo.")

        lunghezza_testo = len(testo)
        if numero_frammenti > lunghezza_testo:
            return []  # Restituisce una lista vuota se non è possibile dividere

        lunghezza_frammento = lunghezza_testo // numero_frammenti  # Divisione intera
        resto = lunghezza_testo % numero_frammenti  # Calcola il resto
        frammenti = []
        inizio = 0
        for i in range(numero_frammenti):
            fine = inizio + lunghezza_frammento + (1 if i < resto else 0)  # Gestisce il resto
            frammenti.append(testo[inizio:fine])
            inizio = fine

        return frammenti
    
    def estrai_da_pdf(self, pdf_file_path) :  
       with open(pdf_file_path, "rb") as f:
            reader = PyPDF2.PdfReader(f)
            full_text = ""
            for page in reader.pages:
                page_text = page.extract_text() or ""
                full_text += page_text 

       return full_text

    def chunk_text_by_paragraph(self,text: str):
        """
        Suddivide il testo in paragrafi basandosi su newline.
        Mantiene uniti gli elenchi puntati e numerati con il paragrafo precedente.
        """
        paragraphs = text.split("\n")
        docs = []
        for i, para in enumerate(paragraphs):
            para = para.strip()
            if para:
                docs.append({"id": str(i), "text": para})
        return docs

    
    
    def scomponi_in_frammenti(self, testo:str, numero_frammenti: int = 1):
        raise NotImplementedError("Questo metodo deve essere implementato nelle sottoclassi.")
    
    def unify_lines(self, text):
        """
        Metodo da implementare nelle sottoclassi per suddividere il testo in paragrafi.
        """
        return  "\n".join(self.dividi_in_paragrafi(text))
    
    def dividi_in_paragrafi(self,pdf_text: str) :

        """
        Unisce righe spezzate in paragrafi coerenti, evitando di separare gli elenchi puntati dal paragrafo precedente.
        Filtra le righe che corrispondono a qualsiasi espressione regolare contenuta in ESCLUDI_RE.
        """
        lines = pdf_text.splitlines()
        paragraphs = []
        current_line = ""
        end_punctuations = (".", "?", "!", ":", ";")
        inside_list = False  # 🟢 Indica se stiamo dentro un elenco puntato

        for line in lines:
            line = line.strip()

            # 🛑 Rimuove le righe che corrispondono a una delle regex in ESCLUDI_RE
            if any(re.search(pattern, line, re.IGNORECASE) for pattern in DocumentProcessor.ESCLUDI_RE):
                continue  # ❌ Salta la riga


            # 🟢 Riconosce un elemento di un elenco puntato (es. "- testo", "• testo", "1. testo")
            is_list_item = re.match(r"^(\d+\.\s+|[-•*]\s+).+", line)

            # 🟢 Se la riga è vuota e abbiamo testo nel buffer, chiudiamo il paragrafo
            if not line:
                if current_line:
                    paragraphs.append(current_line.strip())
                    current_line = ""
                    inside_list = False  # 🛑 Reset della modalità elenco
                continue

            # 🟢 Se è un elemento di elenco, lo aggiungiamo direttamente al paragrafo precedente
            if is_list_item:
                inside_list = True  # 🟢 Indichiamo che siamo dentro un elenco
                current_line += " " + line  # 🔄 Aggiunge la riga all'elenco senza creare un nuovo paragrafo
            elif inside_list:
                # 🛑 Se eravamo dentro un elenco e ora la riga NON fa parte dell'elenco
                current_line += " " + line  # ✅ Manteniamo tutto unito
                inside_list = False  # 🔄 Reset della modalità elenco
            elif line.endswith(end_punctuations):
                current_line += " " + line
                paragraphs.append(current_line.strip())
                current_line = ""
            else:
                current_line += " " + line  # ✅ Unisce righe normali

        if current_line.strip():
            paragraphs.append(current_line.strip())
        #i = 0
        #for par in paragraphs:
        #    print(f"Paragrafo {i} - {par}")
        #    i = i+1
        return paragraphs
    
class ParagraphDocumentProcessor(DocumentProcessor):

    def scomponi_in_frammenti(self, testo:str, numero_frammenti: int = 1):
        return self.dividi_in_paragrafi(testo)
    
class WholeTextDocumentProcessor(DocumentProcessor) :
        def scomponi_in_frammenti(self, testo:str, numero_frammenti: int = 1):
            return [testo]
        
class SmallFragmentDocumentProcessor(DocumentProcessor):
    def scomponi_in_frammenti(self, testo:str, numero_frammenti: int = 1):
         return self.dividi_testo_in_frammenti(testo)
    
    def dividi_testo_in_frammenti(self,testo, lunghezza_massima=1000):
        frammenti = []
        inizio = 0

        while inizio < len(testo):
            fine = inizio + lunghezza_massima

            # Se siamo alla fine del testo, aggiungiamo e usciamo
            if fine >= len(testo):
                frammenti.append(testo[inizio:].strip())
                break

            # Cerca l'ultimo spazio prima del limite per evitare di tagliare la parola
            fine_corretto = testo.rfind(" ", inizio, fine)
            if fine_corretto == -1 or fine_corretto <= inizio:
                # Se non troviamo spazi, tagliamo brutalmente
                fine_corretto = fine

            frammento = testo[inizio:fine_corretto].strip()
            frammenti.append(frammento)
            inizio = fine_corretto

        return frammenti