2026-04-30 15:26:52 +02:00
|
|
|
"""Orchestratore: applica le trasformazioni in ordine semantico."""
|
|
|
|
|
import re
|
|
|
|
|
from functools import partial
|
|
|
|
|
|
|
|
|
|
from ._encoding import (
|
|
|
|
|
_t_fix_symbol_font, _t_fix_accents,
|
|
|
|
|
_t_fix_multiplication, _t_fix_micro,
|
|
|
|
|
)
|
|
|
|
|
from ._artifacts import (
|
|
|
|
|
_t_remove_images, _t_fix_br, _t_fix_tabsep, _t_remove_footnotes,
|
|
|
|
|
_t_remove_formula_labels, _t_remove_dotleaders, _t_remove_recurring_lines,
|
|
|
|
|
_t_fix_math_symbols, _t_remove_watermarks, _t_remove_urls,
|
2026-05-07 16:12:50 +02:00
|
|
|
_t_remove_page_markers, _t_remove_page_numbers, _t_remove_separators,
|
2026-04-30 15:26:52 +02:00
|
|
|
)
|
|
|
|
|
from ._headers import (
|
|
|
|
|
_t_fix_header_concat, _t_extract_capitolo,
|
|
|
|
|
_t_normalize_numbered_headings, _t_normalize_header_levels,
|
2026-05-07 16:21:02 +02:00
|
|
|
_t_remove_header_bold, _t_normalize_allcaps_headers, _t_demote_h1,
|
2026-04-30 15:26:52 +02:00
|
|
|
)
|
|
|
|
|
from ._structure import (
|
|
|
|
|
_t_remove_toc, _t_remove_orphan_toc, _t_allcaps_to_headers,
|
2026-05-07 16:12:50 +02:00
|
|
|
_t_numbered_sections, _t_promote_chapter_headers,
|
|
|
|
|
_t_extract_math, _t_extract_articles,
|
2026-04-30 15:26:52 +02:00
|
|
|
)
|
|
|
|
|
from ._text import (
|
|
|
|
|
_t_merge_paragraphs, _t_normalize_whitespace, _t_collapse_blank_lines,
|
|
|
|
|
_t_restore_poetry_lines, _t_demote_verse_headers,
|
|
|
|
|
)
|
|
|
|
|
from ._finish import (
|
|
|
|
|
_t_remove_empty_headers, _t_merge_title_headers,
|
|
|
|
|
_t_remove_garbage_headers, _t_math_header_demotion,
|
|
|
|
|
_t_remove_frontmatter,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-05-07 14:30:41 +02:00
|
|
|
def apply_transforms(text: str, on_step=None) -> tuple[str, dict]:
|
2026-04-30 15:26:52 +02:00
|
|
|
"""
|
|
|
|
|
Applica le trasformazioni strutturali al Markdown grezzo.
|
|
|
|
|
Restituisce (testo_modificato, statistiche).
|
|
|
|
|
L'ordine è semantico: encoding → artefatti → struttura header →
|
|
|
|
|
costruzione struttura → testo → rifinitura.
|
2026-05-07 14:30:41 +02:00
|
|
|
|
|
|
|
|
on_step(i, total, label) — callback opzionale chiamato dopo ogni step.
|
2026-04-30 15:26:52 +02:00
|
|
|
"""
|
|
|
|
|
_has_ex = bool(re.search(r"\b(Esercizi|Exercises|Problems|Homework)\b", text, re.IGNORECASE))
|
|
|
|
|
|
2026-05-07 14:30:41 +02:00
|
|
|
# (stat_key, fn, label)
|
|
|
|
|
_transforms: list[tuple[str | None, object, str]] = [
|
2026-04-30 15:26:52 +02:00
|
|
|
# 1. Encoding
|
2026-05-07 14:30:41 +02:00
|
|
|
("n_simboli_pua_corretti", _t_fix_symbol_font, "encoding PUA Symbol"),
|
|
|
|
|
("n_accenti_corretti", _t_fix_accents, "accenti backtick LaTeX"),
|
|
|
|
|
("n_moltiplicazioni_corrette", _t_fix_multiplication, "simbolo moltiplicazione"),
|
|
|
|
|
("n_micro_corretti", _t_fix_micro, "simbolo micro SI"),
|
2026-04-30 15:26:52 +02:00
|
|
|
# 2. Pulizia artefatti
|
2026-05-07 16:12:50 +02:00
|
|
|
("n_page_markers_rimossi", _t_remove_page_markers, "rimozione page markers PDF"),
|
|
|
|
|
("n_separatori_rimossi", _t_remove_separators, "rimozione separatori underscore"),
|
2026-05-07 14:30:41 +02:00
|
|
|
("n_immagini_rimosse", _t_remove_images, "rimozione immagini"),
|
|
|
|
|
("n_br_rimossi", _t_fix_br, "fix <br> inline"),
|
|
|
|
|
("n_tabsep_rimossi", _t_fix_tabsep, "fix separatori tabella"),
|
|
|
|
|
("n_note_rimosse", _t_remove_footnotes, "rimozione footnote"),
|
|
|
|
|
("n_simboli_math_rimossi", _t_fix_math_symbols, "rimozione box math"),
|
|
|
|
|
("n_formule_rimossi", _t_remove_formula_labels, "rimozione label formula"),
|
|
|
|
|
("n_dotleader_rimossi", _t_remove_dotleaders, "rimozione dot-leader TOC"),
|
|
|
|
|
("n_righe_ricorrenti_rimosse", _t_remove_recurring_lines, "rimozione righe ricorrenti"),
|
2026-05-07 16:12:50 +02:00
|
|
|
("n_numeri_pagina_rimossi", _t_remove_page_numbers, "rimozione numeri pagina isolati"),
|
2026-04-30 15:26:52 +02:00
|
|
|
# 3. Struttura header
|
2026-05-07 14:30:41 +02:00
|
|
|
("n_header_concat_fixati", _t_fix_header_concat, "fix header+corpo concatenati"),
|
|
|
|
|
(None, _t_extract_capitolo, "estrazione Capitolo inline"),
|
|
|
|
|
("n_header_numerati_normalizzati", _t_normalize_numbered_headings, "normalizzazione livelli numerati"),
|
|
|
|
|
(None, _t_normalize_header_levels, "normalizzazione livelli ####→###"),
|
2026-05-07 16:21:02 +02:00
|
|
|
(None, _t_demote_h1, "demozione #→## (sezioni principali)"),
|
2026-05-07 14:30:41 +02:00
|
|
|
(None, _t_remove_header_bold, "rimozione bold negli header"),
|
|
|
|
|
(None, _t_normalize_allcaps_headers, "normalizzazione ALL-CAPS header"),
|
2026-04-30 15:26:52 +02:00
|
|
|
# 4. Costruzione struttura
|
2026-05-07 14:30:41 +02:00
|
|
|
("toc_rimosso", _t_remove_toc, "rimozione TOC"),
|
|
|
|
|
("n_toc_orfani_rimossi", _t_remove_orphan_toc, "rimozione voci TOC orfane"),
|
|
|
|
|
("n_header_allcaps", _t_allcaps_to_headers, "ALL-CAPS → ##"),
|
|
|
|
|
("n_sezioni_numerate", partial(_t_numbered_sections, has_exercises=_has_ex), "sezioni numerate → ###"),
|
2026-05-07 16:12:50 +02:00
|
|
|
("n_capitoli_promossi", _t_promote_chapter_headers, "promozione capitoli ### → ##"),
|
2026-05-07 14:30:41 +02:00
|
|
|
("n_ambienti_matematici", _t_extract_math, "estrazione ambienti matematici"),
|
|
|
|
|
("n_articoli_estratti", _t_extract_articles, "estrazione articoli → ###"),
|
2026-04-30 15:26:52 +02:00
|
|
|
# 5. Testo
|
2026-05-07 14:30:41 +02:00
|
|
|
("n_paragrafi_uniti", _t_merge_paragraphs, "merge paragrafi spezzati"),
|
|
|
|
|
(None, _t_normalize_whitespace, "normalizzazione whitespace"),
|
|
|
|
|
(None, _t_collapse_blank_lines, "riduzione righe vuote"),
|
|
|
|
|
("n_versi_ripristinati", _t_restore_poetry_lines, "ripristino versi poesia"),
|
|
|
|
|
("n_header_verso_demotati", _t_demote_verse_headers, "demozione header-verso"),
|
|
|
|
|
("n_url_rimossi", _t_remove_urls, "rimozione URL"),
|
2026-04-30 15:26:52 +02:00
|
|
|
# 6. Rifinitura
|
2026-05-07 16:12:50 +02:00
|
|
|
(None, lambda t: (re.sub(r"(?m)^(#{1,6}.+?)\s+pag\.\s*\d{1,4}\s*$", r"\1", t), 0), "strip pag.N dagli header"),
|
2026-05-07 14:30:41 +02:00
|
|
|
(None, _t_remove_empty_headers, "rimozione header vuoti"),
|
|
|
|
|
("n_titoli_uniti", _t_merge_title_headers, "merge titoli isolati"),
|
|
|
|
|
(None, lambda t: (re.sub(r"(?m)^(#{1,6}.+?)\s*\|\s*\d{1,3}\s*$", r"\1", t), 0), "fix header|pagina"),
|
|
|
|
|
("n_garbage_headers_rimossi", _t_remove_garbage_headers, "rimozione garbage header"),
|
|
|
|
|
("n_formula_headers_demotati", _t_math_header_demotion, "demozione formula-header"),
|
|
|
|
|
("n_frontmatter_rimossi", _t_remove_frontmatter, "rimozione frontmatter"),
|
|
|
|
|
("n_watermark_rimossi", _t_remove_watermarks, "rimozione watermark"),
|
2026-04-30 15:26:52 +02:00
|
|
|
]
|
|
|
|
|
|
2026-05-07 14:30:41 +02:00
|
|
|
total = len(_transforms)
|
2026-04-30 15:26:52 +02:00
|
|
|
stats: dict = {}
|
2026-05-07 14:30:41 +02:00
|
|
|
for i, (stat_key, fn, label) in enumerate(_transforms, 1):
|
2026-04-30 15:26:52 +02:00
|
|
|
text, n = fn(text)
|
|
|
|
|
if stat_key:
|
|
|
|
|
stats[stat_key] = stats.get(stat_key, 0) + n
|
2026-05-07 14:30:41 +02:00
|
|
|
if on_step:
|
|
|
|
|
on_step(i, total, label)
|
2026-04-30 15:26:52 +02:00
|
|
|
|
|
|
|
|
stats["toc_rimosso"] = bool(stats.get("toc_rimosso", 0))
|
|
|
|
|
return text, stats
|