Fabio Antonini Claude Sonnet 4.6 commited on
Commit
177fa16
·
1 Parent(s): f4abe67

Fix estrazione date in tab Datazione Documenti

Browse files

- extract_dates(): filtra date di nascita rilevando "nata/nato/nascita"
nel contesto precedente il match regex
- dating_rank(): rimuove preprocessing HTR (distrugge testo tipografico),
usa paragraph=False per catturare blocchi isolati, seleziona la data
più recente (dates[-1]) invece della più vecchia
- Aggiorna doc_contratto_2001.png e doc_testamento_2024.png (data più
prominente e senza date ambigue nel corpo testo)
- Aggiunge scripts/create_test_dated_docs.py per generare i 3 documenti
di test con date in formato italiano

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

app/grapholab_demo.py CHANGED
@@ -1331,8 +1331,12 @@ def extract_dates(text: str) -> list[tuple[str, _datetime]]:
1331
  found: list[tuple[str, _datetime]] = []
1332
 
1333
  # L1 — regex
 
1334
  for m in _DATE_RE.finditer(text):
1335
  raw = m.group(0).strip()
 
 
 
1336
  dt = _try_dateparser(raw)
1337
  if dt:
1338
  found.append((raw, dt))
@@ -1379,12 +1383,11 @@ def dating_rank(files: list) -> str:
1379
  try:
1380
  img = Image.open(path).convert("RGB")
1381
  img_np = np.array(img)
1382
- processed = _preprocess_for_htr(img_np)
1383
- ocr_lines = reader.readtext(processed, detail=0, paragraph=True)
1384
  text = "\n".join(ocr_lines)
1385
  dates = extract_dates(text)
1386
  if dates:
1387
- raw, dt = dates[0] # prima data plausibile
1388
  rows.append((name, raw, dt))
1389
  else:
1390
  rows.append((name, "— data non trovata", None))
 
1331
  found: list[tuple[str, _datetime]] = []
1332
 
1333
  # L1 — regex
1334
+ _BIRTH_KW = ("nata", "nato", "nascita", "nasc.", "nata il", "nato il")
1335
  for m in _DATE_RE.finditer(text):
1336
  raw = m.group(0).strip()
1337
+ context_before = text[max(0, m.start() - 35) : m.start()].lower()
1338
+ if any(kw in context_before for kw in _BIRTH_KW):
1339
+ continue # data di nascita — ignorala
1340
  dt = _try_dateparser(raw)
1341
  if dt:
1342
  found.append((raw, dt))
 
1383
  try:
1384
  img = Image.open(path).convert("RGB")
1385
  img_np = np.array(img)
1386
+ ocr_lines = reader.readtext(img_np, detail=0, paragraph=False)
 
1387
  text = "\n".join(ocr_lines)
1388
  dates = extract_dates(text)
1389
  if dates:
1390
+ raw, dt = dates[-1] # data più recente = data di redazione
1391
  rows.append((name, raw, dt))
1392
  else:
1393
  rows.append((name, "— data non trovata", None))
data/samples/doc_contratto_2001.png CHANGED

Git LFS Details

  • SHA256: 77a104110c682f559e86ab23e29e12a875907f9611952aeb5b5ec9afba089ffb
  • Pointer size: 130 Bytes
  • Size of remote file: 90.3 kB

Git LFS Details

  • SHA256: c8366668f2b33600a56621ea6345fee859c8266334081c995c03196b17d8d9ca
  • Pointer size: 130 Bytes
  • Size of remote file: 89.4 kB
data/samples/doc_testamento_2024.png CHANGED

Git LFS Details

  • SHA256: 0550c652d4ca4ac1cf45579d2074ca0d7c4b7a1359dcfe8b2c90043137cd9b1d
  • Pointer size: 130 Bytes
  • Size of remote file: 72.3 kB

Git LFS Details

  • SHA256: ee6a4d0bf15b209191e950f8d301f9e222ef326a564b352b195d52569732f595
  • Pointer size: 130 Bytes
  • Size of remote file: 74.1 kB
scripts/create_test_dated_docs.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Crea 3 documenti fittizi con date in formato italiano per testare
3
+ il tab "Datazione Documenti" di GraphoLab.
4
+
5
+ Output:
6
+ data/samples/doc_lettera_1998.png — data: 3 marzo 1998
7
+ data/samples/doc_contratto_2001.png — data: 15/06/2001
8
+ data/samples/doc_testamento_2024.png — data: 10 gennaio 2024
9
+
10
+ Ordinamento atteso nella tab: 1998 → 2001 → 2024
11
+ """
12
+
13
+ from pathlib import Path
14
+ from PIL import Image, ImageDraw, ImageFont
15
+
16
+ ROOT = Path(__file__).parent.parent
17
+ OUT_DIR = ROOT / "data" / "samples"
18
+
19
+ FONTS_DIR = Path("C:/Windows/Fonts")
20
+ PAGE_W, PAGE_H = 1200, 900
21
+ MARGIN = 80
22
+ BG = (255, 255, 255)
23
+ INK = (30, 30, 30)
24
+ GRAY = (120, 120, 120)
25
+
26
+
27
+ def load_font(name: str, size: int) -> ImageFont.FreeTypeFont:
28
+ for path in [name, str(FONTS_DIR / name)]:
29
+ try:
30
+ return ImageFont.truetype(path, size)
31
+ except (IOError, OSError):
32
+ pass
33
+ return ImageFont.load_default()
34
+
35
+
36
+ def new_canvas() -> tuple[Image.Image, ImageDraw.ImageDraw]:
37
+ img = Image.new("RGB", (PAGE_W, PAGE_H), BG)
38
+ draw = ImageDraw.Draw(img)
39
+ # bordo sottile
40
+ draw.rectangle([10, 10, PAGE_W - 11, PAGE_H - 11], outline=(200, 200, 200), width=2)
41
+ return img, draw
42
+
43
+
44
+ def draw_text_block(draw: ImageDraw.ImageDraw, text: str, font: ImageFont.FreeTypeFont,
45
+ x: int, y: int, max_w: int, fill=INK, align="left") -> int:
46
+ """Disegna testo con a capo automatico. Restituisce la y finale."""
47
+ words = text.split()
48
+ line = ""
49
+ lines = []
50
+ for word in words:
51
+ test = (line + " " + word).strip()
52
+ bbox = draw.textbbox((0, 0), test, font=font)
53
+ if bbox[2] - bbox[0] > max_w and line:
54
+ lines.append(line)
55
+ line = word
56
+ else:
57
+ line = test
58
+ if line:
59
+ lines.append(line)
60
+
61
+ line_h = font.size + 6
62
+ for ln in lines:
63
+ if align == "right":
64
+ bbox = draw.textbbox((0, 0), ln, font=font)
65
+ lw = bbox[2] - bbox[0]
66
+ draw.text((x + max_w - lw, y), ln, font=font, fill=fill)
67
+ else:
68
+ draw.text((x, y), ln, font=font, fill=fill)
69
+ y += line_h
70
+ return y
71
+
72
+
73
+ # ──────────────────────────────────────────────
74
+ # Documento 1: Lettera formale — 3 marzo 1998
75
+ # ──────────────────────────────────────────────
76
+ def create_lettera(path: Path) -> None:
77
+ img, draw = new_canvas()
78
+ font_h = load_font("Inkfree.ttf", 28)
79
+ font_b = load_font("Inkfree.ttf", 20)
80
+ font_sm = load_font("Inkfree.ttf", 17)
81
+ font_dt = load_font("Inkfree.ttf", 22)
82
+
83
+ x0 = MARGIN
84
+ max_w = PAGE_W - 2 * MARGIN
85
+
86
+ # Data in alto a destra
87
+ draw.text((PAGE_W - MARGIN, MARGIN), "3 marzo 1998",
88
+ font=font_dt, fill=INK, anchor="ra")
89
+
90
+ # Mittente
91
+ y = MARGIN
92
+ draw.text((x0, y), "Dott. Mario Bianchi", font=font_b, fill=INK)
93
+ y += 28
94
+ draw.text((x0, y), "Via Roma 14, 20121 Milano", font=font_sm, fill=GRAY)
95
+ y += 70
96
+
97
+ # Intestazione
98
+ draw.text((x0, y), "LETTERA RACCOMANDATA", font=font_h, fill=INK)
99
+ y += 50
100
+
101
+ draw.line([(x0, y), (PAGE_W - MARGIN, y)], fill=(180, 180, 180), width=1)
102
+ y += 20
103
+
104
+ # Destinatario
105
+ draw.text((x0, y), "Egregio Avv. Luigi Ferri", font=font_b, fill=INK)
106
+ y += 28
107
+ draw.text((x0, y), "Studio Legale Ferri & Associati", font=font_sm, fill=GRAY)
108
+ y += 28
109
+ draw.text((x0, y), "Corso Vittorio Emanuele 88, 00186 Roma", font=font_sm, fill=GRAY)
110
+ y += 50
111
+
112
+ # Corpo lettera
113
+ corpo = (
114
+ "Con la presente mi pregio di informarLa che, in seguito alla nostra riunione "
115
+ "del mese scorso, ho provveduto a raccogliere tutta la documentazione necessaria "
116
+ "relativa alla pratica in oggetto. Allego pertanto le copie dei contratti stipulati "
117
+ "nonché le ricevute di pagamento degli ultimi tre anni fiscali. "
118
+ "Resto a Sua completa disposizione per qualsiasi chiarimento."
119
+ )
120
+ y = draw_text_block(draw, corpo, font_sm, x0, y, max_w)
121
+ y += 30
122
+
123
+ draw.text((x0, y), "Distinti saluti,", font=font_sm, fill=INK)
124
+ y += 30
125
+ draw.text((x0, y), "Mario Bianchi", font=font_b, fill=INK)
126
+ y += 28
127
+ draw.line([(x0, y), (x0 + 200, y)], fill=INK, width=1)
128
+
129
+ img.save(path, "PNG")
130
+ print(f" Creato: {path.name}")
131
+
132
+
133
+ # ──────────────────────────────────────────────
134
+ # Documento 2: Contratto di vendita — 15/06/2001
135
+ # ──────────────────────────────────────────────
136
+ def create_contratto(path: Path) -> None:
137
+ img, draw = new_canvas()
138
+ font_h = load_font("segoesc.ttf", 26)
139
+ font_b = load_font("segoesc.ttf", 19)
140
+ font_sm = load_font("segoesc.ttf", 16)
141
+ font_dt = load_font("segoesc.ttf", 21)
142
+
143
+ x0 = MARGIN
144
+ max_w = PAGE_W - 2 * MARGIN
145
+
146
+ y = MARGIN
147
+ # Titolo centrato
148
+ title = "CONTRATTO DI COMPRAVENDITA IMMOBILIARE"
149
+ bbox = draw.textbbox((0, 0), title, font=font_h)
150
+ tw = bbox[2] - bbox[0]
151
+ draw.text(((PAGE_W - tw) // 2, y), title, font=font_h, fill=INK)
152
+ y += 50
153
+
154
+ # Data e luogo
155
+ draw.text((x0, y), "Luogo e data: Torino, 15/06/2001", font=font_dt, fill=INK)
156
+ y += 45
157
+
158
+ draw.line([(x0, y), (PAGE_W - MARGIN, y)], fill=(160, 160, 160), width=2)
159
+ y += 25
160
+
161
+ # Parti contraenti
162
+ draw.text((x0, y), "TRA", font=font_b, fill=GRAY)
163
+ y += 28
164
+ draw.text((x0, y), "Sig. Carlo Esposito (venditore), C.F. ESPCRL60M15F205X", font=font_sm, fill=INK)
165
+ y += 25
166
+ draw.text((x0, y), "E", font=font_b, fill=GRAY)
167
+ y += 28
168
+ draw.text((x0, y), "Sig.ra Anna De Luca (acquirente), C.F. DLCNNA72E55L219Q", font=font_sm, fill=INK)
169
+ y += 40
170
+
171
+ # Articoli
172
+ articoli = [
173
+ ("Art. 1 — Oggetto", "Il venditore cede all'acquirente l'immobile sito in Via Garibaldi 5, Torino, "
174
+ "catastalmente identificato al Foglio 12, Particella 340, sub 8."),
175
+ ("Art. 2 — Prezzo", "Il prezzo pattuito ammonta a lire 280.000.000 (duecentoottantamilioni), "
176
+ "già interamente corrisposto alla data di firma del presente atto."),
177
+ ("Art. 3 — Consegna", "La consegna delle chiavi avverrà entro trenta giorni dalla firma, "
178
+ "libero da persone e cose."),
179
+ ]
180
+ for titolo, testo in articoli:
181
+ draw.text((x0, y), titolo, font=font_b, fill=INK)
182
+ y += 26
183
+ y = draw_text_block(draw, testo, font_sm, x0 + 20, y, max_w - 20)
184
+ y += 15
185
+
186
+ y += 20
187
+ col1, col2 = x0, PAGE_W // 2
188
+ draw.text((col1, y), "Il venditore", font=font_sm, fill=GRAY)
189
+ draw.text((col2, y), "L'acquirente", font=font_sm, fill=GRAY)
190
+ y += 50
191
+ draw.line([(col1, y), (col1 + 200, y)], fill=INK, width=1)
192
+ draw.line([(col2, y), (col2 + 200, y)], fill=INK, width=1)
193
+
194
+ img.save(path, "PNG")
195
+ print(f" Creato: {path.name}")
196
+
197
+
198
+ # ──────────────────────────────────────────────
199
+ # Documento 3: Testamento olografo — 10 gennaio 2024
200
+ # ──────────────────────────────────────────────
201
+ def create_testamento(path: Path) -> None:
202
+ img, draw = new_canvas()
203
+ font_h = load_font("segoepr.ttf", 26)
204
+ font_b = load_font("segoepr.ttf", 20)
205
+ font_sm = load_font("segoepr.ttf", 17)
206
+ font_dt = load_font("segoepr.ttf", 20)
207
+
208
+ x0 = MARGIN
209
+ max_w = PAGE_W - 2 * MARGIN
210
+
211
+ y = MARGIN
212
+ # Titolo
213
+ title = "TESTAMENTO OLOGRAFO"
214
+ bbox = draw.textbbox((0, 0), title, font=font_h)
215
+ tw = bbox[2] - bbox[0]
216
+ draw.text(((PAGE_W - tw) // 2, y), title, font=font_h, fill=INK)
217
+ y += 55
218
+
219
+ draw.line([(x0, y), (PAGE_W - MARGIN, y)], fill=(180, 180, 180), width=1)
220
+ y += 25
221
+
222
+ # Testatore
223
+ draw.text((x0, y), "Io sottoscritta, Giovanna Mancini, nata a Napoli il 12 aprile 1942,", font=font_sm, fill=INK)
224
+ y += 28
225
+ draw.text((x0, y), "residente in Via Caracciolo 22, 80122 Napoli, nel pieno possesso", font=font_sm, fill=INK)
226
+ y += 28
227
+ draw.text((x0, y), "delle mie facoltà mentali, dispongo delle mie ultime volontà:", font=font_sm, fill=INK)
228
+ y += 45
229
+
230
+ # Disposizioni
231
+ disposizioni = [
232
+ "1. Lascio al mio figlio primogenito, Dott. Roberto Mancini, "
233
+ "l'appartamento di Via Caracciolo 22 e tutti i beni mobili ivi contenuti.",
234
+ "2. Lascio alla mia nipote Sara Mancini la somma di euro 50.000 (cinquantamila) "
235
+ "depositata sul conto corrente n. 123456 presso la Banca di Napoli.",
236
+ "3. Il residuo del mio patrimonio dovrà essere suddiviso in parti uguali "
237
+ "tra i miei tre figli: Roberto, Elena e Marco Mancini.",
238
+ "4. Nomino esecutore testamentario il Notaio Dott. Enzo Vitale, "
239
+ "con studio in Via Toledo 80, Napoli.",
240
+ ]
241
+ for d in disposizioni:
242
+ y = draw_text_block(draw, d, font_sm, x0, y, max_w)
243
+ y += 18
244
+
245
+ y += 30
246
+ # Data in basso — font più grande e left-aligned per facilitare l'OCR
247
+ font_dt2 = load_font("segoepr.ttf", 26)
248
+ draw.text((x0, y), "Napoli, 10 gennaio 2024", font=font_dt2, fill=INK)
249
+ y += 50
250
+
251
+ draw.text((x0, y), "Firma autografa:", font=font_sm, fill=GRAY)
252
+ y += 35
253
+ draw.line([(x0, y), (x0 + 250, y)], fill=INK, width=1)
254
+ y += 10
255
+ draw.text((x0, y), "Giovanna Mancini", font=font_b, fill=INK)
256
+
257
+ img.save(path, "PNG")
258
+ print(f" Creato: {path.name}")
259
+
260
+
261
+ if __name__ == "__main__":
262
+ OUT_DIR.mkdir(parents=True, exist_ok=True)
263
+ print("Generazione documenti fittizi con date...")
264
+ create_lettera(OUT_DIR / "doc_lettera_1998.png")
265
+ create_contratto(OUT_DIR / "doc_contratto_2001.png")
266
+ create_testamento(OUT_DIR / "doc_testamento_2024.png")
267
+ print("Fatto. Carica i 3 file nel tab 'Datazione Documenti' di GraphoLab.")