"""Anonimizzatore Gare d'Appalto — UI Gradio.""" import gradio as gr from config import MODES, DEFAULT_MIN_SCORE, SEVERITY_COLORS, SEVERITY_LABELS, SEVERITY_ORDER from pipeline import anonymize from renderers import render_highlighted_text, render_anonymized_text, render_categorized_report from utils import extract_pdf_text from demo_text import DEMO_TEXT # --------------------------------------------------------------------------- # Handlers # --------------------------------------------------------------------------- def _process(text: str, mode: str, min_score: float, use_regex: bool): if not text or not text.strip(): e = '
Inserisci del testo per iniziare.
' return e, e, e anon_text, entities = anonymize(text, mode, min_score, use_regex) return ( render_highlighted_text(text, entities), render_anonymized_text(anon_text), render_categorized_report(text, entities), ) def handle_text(text, mode, min_score, use_regex): return _process(text, mode, min_score, use_regex) def handle_pdf(pdf_file, mode, min_score, use_regex): def _err(msg): e = f'
{msg}
' return e, e, e if pdf_file is None: return _err("Carica un PDF per iniziare.") raw = extract_pdf_text(pdf_file) if not raw.strip(): return _err("⚠️ Impossibile estrarre testo dal PDF (scansionato?).") return _process(raw, mode, min_score, use_regex) # --------------------------------------------------------------------------- # Legenda gravità # --------------------------------------------------------------------------- def _legend_html() -> str: badges = [] for sev in SEVERITY_ORDER: c = SEVERITY_COLORS[sev] badges.append( f'Gravità {SEVERITY_LABELS[sev]}' ) return ( '
' + "".join(badges) + '
' ) # --------------------------------------------------------------------------- # CSS # --------------------------------------------------------------------------- _CSS = """ .gradio-container { max-width: 1200px !important; margin: 0 auto !important; font-family: Arial, Helvetica, sans-serif !important; } .gradio-container * { font-family: Arial, Helvetica, sans-serif !important; } .header-block h1 { margin-bottom: 4px !important; font-weight: 700; } .section-label { font-size: 0.78em !important; font-weight: 700 !important; text-transform: uppercase; letter-spacing: 0.06em; color: #6b7280 !important; margin-bottom: 6px !important; } button.primary { font-weight: 600 !important; } """ # --------------------------------------------------------------------------- # UI # --------------------------------------------------------------------------- with gr.Blocks( title="Anonimizzatore Gare d'Appalto", theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate", font=["Arial", "Helvetica", "sans-serif"]), css=_CSS, ) as demo: with gr.Row(elem_classes=["header-block"]): gr.Markdown( "# 🔒 Anonimizzatore Gare d'Appalto\n" "Pipeline a 3 livelli: **Regex** → **NER** (Italian_NER_XXL_v2) → " "**GLiNER** (PII_ITA zero-shot), con doppio passaggio regex finale." ) gr.Markdown("### Impostazioni", elem_classes=["section-label"]) mode_radio = gr.Radio( choices=list(MODES.keys()), value=list(MODES.keys())[0], label="Modalità di offuscamento", ) with gr.Row(equal_height=True): score_slider = gr.Slider( minimum=0.0, maximum=1.0, value=DEFAULT_MIN_SCORE, step=0.05, label="Score minimo di confidenza", scale=3, ) use_regex_chk = gr.Checkbox( value=True, label="Livello 1: regex appalti", scale=1, ) gr.Markdown("### Input", elem_classes=["section-label"]) with gr.Tabs(): with gr.TabItem("📝 Testo"): txt_in = gr.Textbox( label=None, lines=10, placeholder="Incolla il testo del documento di gara…", show_label=False, ) with gr.Row(): txt_btn = gr.Button("Anonimizza", variant="primary", size="lg", scale=3) demo_btn = gr.Button("📋 Carica esempio", variant="secondary", size="lg", scale=1) with gr.TabItem("📄 PDF"): pdf_in = gr.File(label="Carica un PDF", file_types=[".pdf"]) pdf_btn = gr.Button("Anonimizza", variant="primary", size="lg") gr.HTML(_legend_html()) gr.Markdown("### Risultato", elem_classes=["section-label"]) with gr.Tabs(): with gr.TabItem("🎨 Evidenziato"): out_highlighted = gr.HTML() with gr.TabItem("🔒 Anonimizzato"): out_anonymized = gr.HTML() with gr.TabItem("📊 Report"): out_report = gr.HTML() outputs = [out_highlighted, out_anonymized, out_report] inputs_common = [mode_radio, score_slider, use_regex_chk] txt_btn.click(handle_text, inputs=[txt_in, *inputs_common], outputs=outputs) pdf_btn.click(handle_pdf, inputs=[pdf_in, *inputs_common], outputs=outputs) demo_btn.click(lambda: DEMO_TEXT, inputs=None, outputs=txt_in) demo.launch()