Spaces:
Sleeping
Sleeping
File size: 12,503 Bytes
42693f7 31edf0b 42693f7 96e4672 42693f7 96e4672 870c988 96e4672 870c988 96e4672 42693f7 96e4672 42693f7 4f5a1e9 96e4672 42693f7 e90e953 96e4672 31edf0b 42693f7 96e4672 42693f7 96e4672 4f5a1e9 42693f7 96e4672 31edf0b 96e4672 31edf0b 96e4672 31edf0b 96e4672 31edf0b 96e4672 31edf0b 96e4672 31edf0b 96e4672 31edf0b 96e4672 31edf0b 42693f7 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 31edf0b e90e953 31edf0b 4f5a1e9 96e4672 4f5a1e9 e90e953 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 4f5a1e9 31edf0b e90e953 31edf0b 4f5a1e9 96e4672 31edf0b 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 31edf0b 96e4672 31edf0b 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 4f5a1e9 42693f7 96e4672 42693f7 96e4672 42693f7 96e4672 42693f7 96e4672 42693f7 96e4672 e90e953 4f5a1e9 42693f7 4f5a1e9 96e4672 e90e953 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 e90e953 4f5a1e9 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 31edf0b e90e953 31edf0b 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 4f5a1e9 42693f7 96e4672 42693f7 96e4672 4f5a1e9 96e4672 4f5a1e9 96e4672 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | 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__)
# Abilita la Condivisione delle Risorse tra Origini Diverse (CORS)
CORS(app)
# --- INIZIO SEZIONE TRADUZIONI ---
# Mappe per le traduzioni in italiano delle spiegazioni di spaCy
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)."
}
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)
# Ritorna il tag originale se non trova una spiegazione
return tag
# --- FINE SEZIONE TRADUZIONI ---
# Mappatura delle etichette di dipendenza di spaCy alle nostre etichette di analisi logica con spiegazioni
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):
"""Affina il tipo di complemento basandosi sulla preposizione precedente e fornisce dettagli."""
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):
"""Costruisce ricorsivamente il testo completo di un sintagma, partendo da un token principale."""
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):
"""Aggrega i token in sintagmi grammaticali significativi con spiegazioni dettagliate."""
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')}", # Usa 'pos' per la spiegazione del tag più generica
"morph": str(token.morph) if token.morph else "Non disponibile"
},
"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": str(copula.morph) if copula.morph else "Non disponibile"
}
})
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):
"""Analizza una singola proposizione (principale o subordinata) con dettagli."""
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():
"""Restituisce un semplice messaggio di benvenuto per la radice dell'API."""
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():
"""Endpoint principale per ricevere una frase e restituire l'analisi logica completa con dettagli."""
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) |