analysis_tool / app.py
devusman's picture
all in italian
5a3f6ab
raw
history blame
13.3 kB
import os
from flask import Flask, request, jsonify
from flask_cors import CORS
import spacy
import traceback
# --- SEZIONE CARICAMENTO MODELLO ---
try:
# Carica il modello italiano di spaCy
nlp = spacy.load("it_core_news_sm")
except OSError:
raise RuntimeError(
"Impossibile trovare il modello 'it_core_news_sm'. "
"Assicurati che sia elencato e installato dal tuo file requirements.txt."
)
# --- FINE SEZIONE ---
# Inizializza l'app Flask
app = Flask(__name__)
CORS(app)
# --- INIZIO SEZIONE TRADUZIONI ---
SPIEGAZIONI_POS_IT = {
"ADJ": "Aggettivo", "ADP": "Preposizione", "ADV": "Avverbio", "AUX": "Ausiliare",
"CONJ": "Congiunzione", "CCONJ": "Congiunzione Coordinante", "SCONJ": "Congiunzione Subordinante",
"DET": "Determinante", "INTJ": "Interiezione", "NOUN": "Sostantivo", "NUM": "Numerale",
"PART": "Particella", "PRON": "Pronome", "PROPN": "Nome Proprio", "PUNCT": "Punteggiatura",
"SPACE": "Spazio", "SYM": "Simbolo", "VERB": "Verbo", "X": "Altro",
}
SPIEGAZIONI_ENT_IT = {
"PER": "Persona: Nomi di persone reali o fittizie.",
"LOC": "Luogo: Nomi di luoghi geografici come paesi, città, stati.",
"ORG": "Organizzazione: Nomi di aziende, istituzioni, governi.",
"MISC": "Miscellanea: Entità che non rientrano nelle altre categorie (es. eventi, nazionalità, prodotti)."
}
# NUOVA SEZIONE: Dizionario per le traduzioni morfologiche
TRADUZIONI_MORFOLOGIA = {
# Chiavi
"Gender": "Genere", "Number": "Numero", "Mood": "Modo", "Tense": "Tempo",
"Person": "Persona", "VerbForm": "Forma del Verbo", "PronType": "Tipo di Pronome",
"Clitic": "Clitico", "Definite": "Definitezza", "Degree": "Grado",
# Valori
"Masc": "Maschile", "Fem": "Femminile",
"Sing": "Singolare", "Plur": "Plurale",
"Ind": "Indicativo", "Sub": "Congiuntivo", "Cnd": "Condizionale", "Imp": "Imperativo", "Inf": "Infinito", "Part": "Participio", "Ger": "Gerundio",
"Pres": "Presente", "Past": "Passato", "Fut": "Futuro", "Imp": "Imperfetto",
"1": "1ª", "2": "2ª", "3": "3ª",
"Fin": "Finita", "Inf": "Infinito", "Part": "Participio", "Ger": "Gerundio",
"Prs": "Personale", "Rel": "Relativo", "Int": "Interrogativo", "Dem": "Dimostrativo", "Art": "Articolativo",
"Yes": "Sì",
"Ind": "Indeterminato", "Def": "Determinato",
"Abs": "Assoluto", "Cmp": "Comparativo",
}
def spiega_in_italiano(tag, tipo='pos'):
"""Fornisce una spiegazione in italiano per un tag POS o di entità."""
if tipo == 'pos':
return SPIEGAZIONI_POS_IT.get(tag, tag)
if tipo == 'ent':
return SPIEGAZIONI_ENT_IT.get(tag, tag)
return tag
def traduci_morfologia(morph_str):
"""Traduce la stringa morfologica in un formato leggibile in italiano."""
if not morph_str:
return "Non disponibile"
parti = morph_str.split('|')
parti_tradotte = []
for parte in parti:
if '=' in parte:
chiave, valore = parte.split('=', 1)
chiave_tradotta = TRADUZIONI_MORFOLOGIA.get(chiave, chiave)
valore_tradotto = TRADUZIONI_MORFOLOGIA.get(valore, valore)
parti_tradotte.append(f"{chiave_tradotta}: {valore_tradotto}")
else:
# Gestisce i casi in cui non c'è una coppia chiave=valore
parti_tradotte.append(TRADUZIONI_MORFOLOGIA.get(parte, parte))
return ", ".join(parti_tradotte)
# --- FINE SEZIONE TRADUZIONI ---
MAPPA_DEP = {
"nsubj": {"label": "Soggetto", "description": "Indica chi o cosa compie l'azione o si trova in un certo stato."},
"ROOT": {"label": "Predicato Verbale", "description": "Esprime l'azione o lo stato del soggetto."},
"obj": {"label": "Complemento Oggetto", "description": "Indica l'oggetto diretto dell'azione del verbo."},
"iobj": {"label": "Complemento di Termine", "description": "Indica a chi o a cosa è destinata l'azione."},
"obl": {"label": "Complemento Indiretto", "description": "Fornisce informazioni aggiuntive come luogo, tempo, modo, causa, etc."},
"nmod": {"label": "Complemento di Specificazione", "description": "Specifica o chiarisce il significato del nome a cui si riferisce."},
"amod": {"label": "Attributo", "description": "Aggettivo che qualifica un nome."},
"advmod": {"label": "Complemento Avverbiale", "description": "Modifica il significato di un verbo, aggettivo o altro avverbio."},
"appos": {"label": "Apposizione", "description": "Nome che ne chiarisce un altro."},
"acl:relcl": {"label": "Proposizione Subordinata Relativa", "description": "Frase che espande un nome, introdotta da un pronome relativo."},
"advcl": {"label": "Proposizione Subordinata Avverbiale", "description": "Frase che funziona come un avverbio, modificando il verbo della principale."},
"ccomp": {"label": "Proposizione Subordinata Oggettiva", "description": "Frase che funge da complemento oggetto del verbo della principale."},
"csubj": {"label": "Proposizione Subordinata Soggettiva", "description": "Frase che funge da soggetto del verbo della principale."}
}
def ottieni_tipo_complemento_con_dettagli(token):
preposizione = ""
for figlio in token.children:
if figlio.dep_ == "case":
preposizione = figlio.text.lower()
break
if not preposizione and token.head.dep_ == 'obl':
for figlio in token.head.children:
if figlio.dep_ == "case":
preposizione = figlio.text.lower()
break
if preposizione in ["di", "del", "dello", "della", "dei", "degli", "delle"]:
return {"label": "Complemento di Specificazione", "description": "Risponde alla domanda 'di chi?', 'di che cosa?'."}
if preposizione in ["a", "al", "allo", "alla", "ai", "agli", "alle"]:
return {"label": "Complemento di Termine", "description": "Risponde alla domanda 'a chi?', 'a che cosa?'."}
if preposizione in ["da", "dal", "dallo", "dalla", "dai", "dagli", "dalle"]:
if any(figlio.dep_ == 'aux:pass' for figlio in token.head.children):
return {"label": "Complemento d'Agente", "description": "Indica da chi è compiuta l'azione in una frase passiva."}
return {"label": "Complemento di Moto da Luogo", "description": "Indica il luogo da cui inizia un movimento."}
if preposizione in ["in", "nel", "nello", "nella", "nei", "negli", "nelle"]:
return {"label": "Complemento di Stato in Luogo", "description": "Indica il luogo in cui si svolge un'azione o ci si trova."}
if preposizione in ["con", "col", "coi"]:
return {"label": "Complemento di Compagnia o Mezzo", "description": "Indica la persona/animale con cui si compie l'azione o lo strumento utilizzato."}
if preposizione in ["su", "sul", "sullo", "sulla", "sui", "sugli", "sulle"]:
return {"label": "Complemento di Argomento o Luogo", "description": "Indica l'argomento di cui si parla o il luogo su cui si trova qualcosa."}
if preposizione in ["per"]:
return {"label": "Complemento di Fine o Causa", "description": "Indica lo scopo o la causa di un'azione."}
if preposizione in ["tra", "fra"]:
return {"label": "Complemento di Luogo o Tempo (Partitivo)", "description": "Indica una posizione intermedia o una scelta all'interno di un gruppo."}
return {"label": "Complemento Indiretto", "description": "Fornisce un'informazione generica non classificata in modo più specifico."}
def ottieni_testo_completo(token):
token_sintagma = [token] + sorted([t for t in token.children if t.dep_ in ('det', 'amod', 'case', 'advmod')], key=lambda x: x.i)
token_sintagma.sort(key=lambda x: x.i)
return " ".join(t.text for t in token_sintagma)
def costruisci_sintagmi_con_dettagli(lista_token):
mappa_sintagmi = {}
for token in lista_token:
if token.dep_ not in ['det', 'case', 'amod', 'punct', 'aux', 'cop', 'mark']:
mappa_sintagmi[token.i] = {
"text": ottieni_testo_completo(token),
"token_details": {
"lemma": token.lemma_,
"pos": f"{token.pos_}: {spiega_in_italiano(token.pos_, 'pos')}",
"tag": f"{token.tag_}: {spiega_in_italiano(token.tag_, 'pos')}",
"morph": traduci_morfologia(str(token.morph)) # AGGIORNATO
},
"label_info": {},
"token": token
}
risultato_analisi = []
indici_elaborati = set()
for indice, sintagma in sorted(mappa_sintagmi.items()):
if indice in indici_elaborati:
continue
token = sintagma['token']
dep = token.dep_
info_etichetta = {}
if dep == "ROOT":
e_nominale = any(c.dep_ == 'cop' for c in token.children)
if e_nominale:
copula = [c for c in token.children if c.dep_ == 'cop'][0]
nome_del_predicato = ottieni_testo_completo(token)
risultato_analisi.append({
"text": copula.text,
"label_info": {"label": "Copula", "description": "Verbo 'essere' che collega il soggetto alla parte nominale."},
"token_details": {
"lemma": copula.lemma_,
"pos": f"{copula.pos_}: {spiega_in_italiano(copula.pos_, 'pos')}",
"tag": f"{copula.tag_}: {spiega_in_italiano(copula.tag_, 'pos')}",
"morph": traduci_morfologia(str(copula.morph)) # AGGIORNATO
}
})
risultato_analisi.append({
"text": nome_del_predicato,
"label_info": {"label": "Parte Nominale del Predicato", "description": "Aggettivo o nome che descrive il soggetto."},
"token_details": sintagma["token_details"]
})
else:
info_etichetta = MAPPA_DEP.get(dep, {})
elif dep == 'obl':
info_etichetta = ottieni_tipo_complemento_con_dettagli(token)
elif dep in MAPPA_DEP:
info_etichetta = MAPPA_DEP[dep]
if info_etichetta:
sintagma_da_aggiungere = {
"text": sintagma['text'],
"label_info": info_etichetta
}
if sintagma.get("token_details"):
sintagma_da_aggiungere["token_details"] = sintagma["token_details"]
risultato_analisi.append(sintagma_da_aggiungere)
indici_elaborati.add(indice)
return risultato_analisi
def analizza_proposizione_con_dettagli(token_proposizione):
token_nella_proposizione = [t for t in token_proposizione if t.dep_ != 'mark']
return costruisci_sintagmi_con_dettagli(token_nella_proposizione)
@app.route("/")
def home():
return jsonify({"messaggio": "L'API per l'analisi logica è in esecuzione. Usa l'endpoint /api/analyze."})
@app.route('/api/analyze', methods=['POST'])
def analizza_frase():
try:
dati = request.get_json()
if not dati or 'sentence' not in dati:
return jsonify({"errore": "Frase non fornita"}), 400
frase = dati['sentence']
doc = nlp(frase)
proposizioni_subordinate = []
indici_subordinate = set()
for token in doc:
if token.dep_ in ["acl:relcl", "advcl", "ccomp", "csubj"]:
token_proposizione_subordinata = list(token.subtree)
for t in token_proposizione_subordinata:
indici_subordinate.add(t.i)
info_tipo_subordinata = MAPPA_DEP.get(token.dep_, {"label": "Proposizione Subordinata", "description": "Una frase che dipende da un'altra."})
marcatore = [figlio for figlio in token.children if figlio.dep_ == 'mark']
intro = marcatore[0].text if marcatore else ""
proposizioni_subordinate.append({
"type_info": info_tipo_subordinata,
"text": " ".join(t.text for t in token_proposizione_subordinata if not t.is_punct),
"intro": intro,
"analysis": analizza_proposizione_con_dettagli(token_proposizione_subordinata)
})
token_proposizione_principale = [token for token in doc if token.i not in indici_subordinate]
entita_nominate = [{
"text": ent.text,
"label": ent.label_,
"explanation": spiega_in_italiano(ent.label_, 'ent')
} for ent in doc.ents]
analisi_finale = {
"full_sentence": frase,
"main_clause": {
"text": " ".join(t.text for t in token_proposizione_principale if not t.is_punct),
"analysis": analizza_proposizione_con_dettagli(token_proposizione_principale)
},
"subordinate_clauses": proposizioni_subordinate,
"named_entities": entita_nominate
}
return jsonify(analisi_finale)
except Exception as e:
print(f"Errore durante l'analisi: {e}")
traceback.print_exc()
return jsonify({"errore": "Si è verificato un errore interno."}), 500
if __name__ == '__main__':
porta = int(os.environ.get("PORT", 8080))
app.run(host="0.0.0.0", port=porta, debug=True)