| import json |
| import os |
| import gradio as gr |
| import numpy |
| from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification |
|
|
| |
| |
| |
| print("π Caricamento modello...") |
| tokenizer = AutoTokenizer.from_pretrained( |
| "MilaNLProc/feel-it-italian-sentiment", |
| use_fast=False |
| ) |
|
|
| model = AutoModelForSequenceClassification.from_pretrained( |
| "MilaNLProc/feel-it-italian-sentiment" |
| ) |
|
|
| sentiment_pipeline = pipeline( |
| "text-classification", |
| model=model, |
| tokenizer=tokenizer, |
| truncation=True, |
| max_length=512 |
| ) |
| print("β
Modello caricato!") |
|
|
| |
| |
| |
| def chunk_text(text, max_chars=400): |
| if not text or not text.strip(): |
| return [] |
| |
| sentences = text.replace("\n", " ").split(". ") |
| chunks, current = [], "" |
| |
| for s in sentences: |
| if len(current) + len(s) <= max_chars: |
| current += s + ". " |
| else: |
| chunks.append(current.strip()) |
| current = s + ". " |
| |
| if current.strip(): |
| chunks.append(current.strip()) |
| |
| return chunks |
|
|
| |
| |
| |
| def analyze_article_sentiment(article): |
| text = article.get("content", "") |
| |
| if not text.strip(): |
| article["sentimentAnalysis"] = "UNKNOWN" |
| article["sentimentScore"] = None |
| article["chunksAnalyzed"] = 0 |
| return article |
| |
| chunks = chunk_text(text) |
| |
| if not chunks: |
| article["sentimentAnalysis"] = "UNKNOWN" |
| article["sentimentScore"] = None |
| article["chunksAnalyzed"] = 0 |
| return article |
| |
| results = sentiment_pipeline(chunks) |
| |
| scores = [] |
| for r in results: |
| label = r["label"].lower() |
| |
| if label == "negative": |
| scores.append(0.0) |
| elif label == "neutral": |
| scores.append(0.5) |
| elif label == "positive": |
| scores.append(1.0) |
| |
| if not scores: |
| article["sentimentAnalysis"] = "UNKNOWN" |
| article["sentimentScore"] = None |
| article["chunksAnalyzed"] = len(chunks) |
| return article |
| |
| avg_score = sum(scores) / len(scores) |
| |
| if avg_score <= 0.4: |
| sentiment = "NEGATIVE" |
| elif avg_score < 0.66: |
| sentiment = "NEUTRAL" |
| else: |
| sentiment = "POSITIVE" |
| |
| article["sentimentAnalysis"] = sentiment |
| article["sentimentScore"] = round(avg_score, 3) |
| article["chunksAnalyzed"] = len(chunks) |
| |
| return article |
|
|
| |
| |
| |
| def process_input_json(): |
| """ |
| Legge input.json dal repository, processa gli articoli e restituisce output.json |
| """ |
| input_file = "input.json" |
| output_file = "/tmp/output.json" |
| |
| |
| if not os.path.exists(input_file): |
| available_files = os.listdir() |
| error_msg = f"β File {input_file} non trovato!\n\nπ File disponibili nella directory:\n" |
| error_msg += "\n".join(f" - {f}" for f in available_files) |
| return None, error_msg |
| |
| try: |
| |
| with open(input_file, 'r', encoding='utf-8') as f: |
| articles = json.load(f) |
| |
| if not isinstance(articles, list): |
| return None, "β Errore: il file JSON deve contenere un array di articoli" |
| |
| log = f"π Trovati {len(articles)} articoli da analizzare\n\n" |
| |
| processed_articles = [] |
| |
| for i, article in enumerate(articles, 1): |
| title = article.get('title', 'N/A')[:60] |
| log += f"β³ [{i}/{len(articles)}] {title}...\n" |
| |
| processed_article = analyze_article_sentiment(article) |
| processed_articles.append(processed_article) |
| |
| log += f" β
{processed_article['sentimentAnalysis']} " |
| log += f"(score: {processed_article['sentimentScore']}, " |
| log += f"chunks: {processed_article['chunksAnalyzed']})\n\n" |
| |
| |
| with open(output_file, 'w', encoding='utf-8') as f: |
| json.dump(processed_articles, f, ensure_ascii=False, indent=2) |
| |
| log += f"\nβ
COMPLETATO! {len(processed_articles)} articoli processati.\n" |
| log += f"πΎ Output salvato e pronto per il download" |
| |
| return output_file, log |
| |
| except Exception as e: |
| import traceback |
| error_msg = f"β Errore durante l'elaborazione:\n{str(e)}\n\n" |
| error_msg += f"Traceback:\n{traceback.format_exc()}" |
| return None, error_msg |
|
|
|
|
| def clear_outputs(): |
| """ |
| Pulisce l'output file e il log |
| """ |
| return None, "π§Ή Interfaccia pulita! Puoi modificare input.json e riprocessare." |
|
|
| |
| |
| |
| with gr.Blocks(title="Italian Sentiment Analyzer", theme=gr.themes.Soft()) as demo: |
| gr.Markdown("# π§ Italian Sentiment Analyzer") |
| gr.Markdown("Analizza il sentiment di articoli italiani usando il modello Feel-IT") |
| gr.Markdown("### π Istruzioni:") |
| gr.Markdown("1. Assicurati che `input.json` sia presente nel repository") |
| gr.Markdown("2. Clicca il pulsante per avviare l'analisi") |
| gr.Markdown("3. Scarica `output.json` con i risultati") |
| |
| with gr.Row(): |
| process_btn = gr.Button("π Processa input.json", variant="primary", size="lg", scale=2) |
| clear_btn = gr.Button("π§Ή Clear", variant="secondary", size="lg", scale=1) |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| output_file = gr.File(label="πΎ Scarica output.json") |
| |
| with gr.Column(scale=2): |
| log_box = gr.Textbox( |
| label="π Log elaborazione", |
| lines=20, |
| max_lines=30 |
| ) |
| |
| process_btn.click( |
| fn=process_input_json, |
| inputs=None, |
| outputs=[output_file, log_box] |
| ) |
| clear_btn.click( |
| fn=clear_outputs, |
| inputs=None, |
| outputs=[output_file, log_box] |
| ) |
| |
| gr.Markdown("---") |
| gr.Markdown("### π Logica di classificazione:") |
| gr.Markdown("- **NEGATIVE** π΄: score β€ 0.4") |
| gr.Markdown("- **NEUTRAL** π‘: 0.4 < score < 0.66") |
| gr.Markdown("- **POSITIVE** π’: score β₯ 0.66") |
| |
| gr.Markdown("### π― Campi aggiunti nell'output:") |
| gr.Markdown("- **sentimentAnalysis**: POSITIVE / NEUTRAL / NEGATIVE / UNKNOWN") |
| gr.Markdown("- **sentimentScore**: valore numerico da 0.0 (negativo) a 1.0 (positivo)") |
| gr.Markdown("- **chunksAnalyzed**: numero di chunks in cui Γ¨ stato suddiviso il testo") |
| |
| |
|
|
| |
| |
| |
| if __name__ == "__main__": |
| demo.launch(server_name="0.0.0.0", server_port=7860) |