devusman commited on
Commit
e90e953
·
1 Parent(s): 96e4672
Files changed (1) hide show
  1. app.py +38 -34
app.py CHANGED
@@ -21,6 +21,33 @@ app = Flask(__name__)
21
  # Abilita la Condivisione delle Risorse tra Origini Diverse (CORS)
22
  CORS(app)
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  # Mappatura delle etichette di dipendenza di spaCy alle nostre etichette di analisi logica con spiegazioni
25
  MAPPA_DEP = {
26
  "nsubj": {"label": "Soggetto", "description": "Indica chi o cosa compie l'azione o si trova in un certo stato."},
@@ -41,12 +68,10 @@ MAPPA_DEP = {
41
  def ottieni_tipo_complemento_con_dettagli(token):
42
  """Affina il tipo di complemento basandosi sulla preposizione precedente e fornisce dettagli."""
43
  preposizione = ""
44
- # Cerca una preposizione ('case') come figlio del token
45
  for figlio in token.children:
46
  if figlio.dep_ == "case":
47
  preposizione = figlio.text.lower()
48
  break
49
- # Soluzione alternativa per alcune strutture dove la preposizione è un fratello
50
  if not preposizione and token.head.dep_ == 'obl':
51
  for figlio in token.head.children:
52
  if figlio.dep_ == "case":
@@ -58,7 +83,6 @@ def ottieni_tipo_complemento_con_dettagli(token):
58
  if preposizione in ["a", "al", "allo", "alla", "ai", "agli", "alle"]:
59
  return {"label": "Complemento di Termine", "description": "Risponde alla domanda 'a chi?', 'a che cosa?'."}
60
  if preposizione in ["da", "dal", "dallo", "dalla", "dai", "dagli", "dalle"]:
61
- # Controlla la costruzione passiva per il Complemento d'Agente
62
  if any(figlio.dep_ == 'aux:pass' for figlio in token.head.children):
63
  return {"label": "Complemento d'Agente", "description": "Indica da chi è compiuta l'azione in una frase passiva."}
64
  return {"label": "Complemento di Moto da Luogo", "description": "Indica il luogo da cui inizia un movimento."}
@@ -73,14 +97,11 @@ def ottieni_tipo_complemento_con_dettagli(token):
73
  if preposizione in ["tra", "fra"]:
74
  return {"label": "Complemento di Luogo o Tempo (Partitivo)", "description": "Indica una posizione intermedia o una scelta all'interno di un gruppo."}
75
 
76
- # Valore predefinito se non viene trovata una preposizione specifica
77
  return {"label": "Complemento Indiretto", "description": "Fornisce un'informazione generica non classificata in modo più specifico."}
78
 
79
  def ottieni_testo_completo(token):
80
  """Costruisce ricorsivamente il testo completo di un sintagma, partendo da un token principale."""
81
- # Raccoglie il token principale e i modificatori direttamente correlati (determinanti, aggettivi, preposizioni)
82
  token_sintagma = [token] + sorted([t for t in token.children if t.dep_ in ('det', 'amod', 'case', 'advmod')], key=lambda x: x.i)
83
- # Ordina tutti i token in base alla loro posizione nella frase per ottenere l'ordine corretto
84
  token_sintagma.sort(key=lambda x: x.i)
85
  return " ".join(t.text for t in token_sintagma)
86
 
@@ -88,17 +109,14 @@ def costruisci_sintagmi_con_dettagli(lista_token):
88
  """Aggrega i token in sintagmi grammaticali significativi con spiegazioni dettagliate."""
89
  mappa_sintagmi = {}
90
 
91
- # Crea una mappa di token importanti (le teste dei sintagmi)
92
  for token in lista_token:
93
- # Esclude i token non importanti che verranno uniti in seguito
94
  if token.dep_ not in ['det', 'case', 'amod', 'punct', 'aux', 'cop', 'mark']:
95
  mappa_sintagmi[token.i] = {
96
  "text": ottieni_testo_completo(token),
97
- # Aggiunge informazioni grammaticali dettagliate con spiegazioni
98
  "token_details": {
99
  "lemma": token.lemma_,
100
- "pos": f"{token.pos_}: {spacy.explain(token.pos_)}",
101
- "tag": f"{token.tag_}: {spacy.explain(token.tag_)}",
102
  "morph": str(token.morph) if token.morph else "Non disponibile"
103
  },
104
  "label_info": {},
@@ -108,7 +126,7 @@ def costruisci_sintagmi_con_dettagli(lista_token):
108
  risultato_analisi = []
109
  indici_elaborati = set()
110
 
111
- for indice, sintagma in mappa_sintagmi.items():
112
  if indice in indici_elaborati:
113
  continue
114
 
@@ -117,45 +135,37 @@ def costruisci_sintagmi_con_dettagli(lista_token):
117
  info_etichetta = {}
118
 
119
  if dep == "ROOT":
120
- # Controlla la presenza di un predicato nominale (es. "è bello")
121
  e_nominale = any(c.dep_ == 'cop' for c in token.children)
122
  if e_nominale:
123
  copula = [c for c in token.children if c.dep_ == 'cop'][0]
124
  nome_del_predicato = ottieni_testo_completo(token)
125
- # Aggiunge la copula separatamente
126
  risultato_analisi.append({
127
  "text": copula.text,
128
  "label_info": {"label": "Copula", "description": "Verbo 'essere' che collega il soggetto alla parte nominale."},
129
  "token_details": {
130
  "lemma": copula.lemma_,
131
- "pos": f"{copula.pos_}: {spacy.explain(copula.pos_)}",
132
- "tag": f"{copula.tag_}: {spacy.explain(copula.tag_)}",
133
  "morph": str(copula.morph) if copula.morph else "Non disponibile"
134
  }
135
  })
136
- # Aggiunge la parte nominale del predicato
137
  risultato_analisi.append({
138
  "text": nome_del_predicato,
139
  "label_info": {"label": "Parte Nominale del Predicato", "description": "Aggettivo o nome che descrive il soggetto."},
140
  "token_details": sintagma["token_details"]
141
  })
142
  else:
143
- # È un predicato verbale
144
  info_etichetta = MAPPA_DEP.get(dep, {})
145
  elif dep == 'obl':
146
- # Usa la funzione speciale per determinare il tipo di complemento indiretto
147
  info_etichetta = ottieni_tipo_complemento_con_dettagli(token)
148
  elif dep in MAPPA_DEP:
149
- # Recupera l'etichetta e la descrizione dalla mappa
150
  info_etichetta = MAPPA_DEP[dep]
151
 
152
- # Aggiunge il sintagma analizzato alla lista dei risultati
153
  if info_etichetta:
154
  sintagma_da_aggiungere = {
155
  "text": sintagma['text'],
156
  "label_info": info_etichetta
157
  }
158
- # Aggiunge i dettagli del token se esistono
159
  if sintagma.get("token_details"):
160
  sintagma_da_aggiungere["token_details"] = sintagma["token_details"]
161
  risultato_analisi.append(sintagma_da_aggiungere)
@@ -166,7 +176,6 @@ def costruisci_sintagmi_con_dettagli(lista_token):
166
 
167
  def analizza_proposizione_con_dettagli(token_proposizione):
168
  """Analizza una singola proposizione (principale o subordinata) con dettagli."""
169
- # Rimuove le congiunzioni (marcatori) dall'analisi dei sintagmi stessi
170
  token_nella_proposizione = [t for t in token_proposizione if t.dep_ != 'mark']
171
  return costruisci_sintagmi_con_dettagli(token_nella_proposizione)
172
 
@@ -186,38 +195,35 @@ def analizza_frase():
186
  frase = dati['sentence']
187
  doc = nlp(frase)
188
 
189
- token_proposizione_principale = []
190
  proposizioni_subordinate = []
 
191
 
192
- # Identifica e separa le proposizioni subordinate
193
  for token in doc:
194
  if token.dep_ in ["acl:relcl", "advcl", "ccomp", "csubj"]:
195
  token_proposizione_subordinata = list(token.subtree)
 
 
 
196
  info_tipo_subordinata = MAPPA_DEP.get(token.dep_, {"label": "Proposizione Subordinata", "description": "Una frase che dipende da un'altra."})
197
 
198
- # Trova la parola introduttiva (es. "che", "quando", "perché")
199
  marcatore = [figlio for figlio in token.children if figlio.dep_ == 'mark']
200
  intro = marcatore[0].text if marcatore else ""
201
 
202
  proposizioni_subordinate.append({
203
  "type_info": info_tipo_subordinata,
204
- "text": " ".join(t.text for t in token_proposizione_subordinata),
205
  "intro": intro,
206
  "analysis": analizza_proposizione_con_dettagli(token_proposizione_subordinata)
207
  })
208
 
209
- # Determina i token della proposizione principale escludendo quelli delle subordinate
210
- indici_subordinate = {token.i for prop in proposizioni_subordinate for token in nlp(prop["text"])}
211
  token_proposizione_principale = [token for token in doc if token.i not in indici_subordinate]
212
 
213
- # Estrae le Entità Nominate (Named Entities) con spiegazione
214
  entita_nominate = [{
215
  "text": ent.text,
216
  "label": ent.label_,
217
- "explanation": spacy.explain(ent.label_) # Fornisce la spiegazione
218
  } for ent in doc.ents]
219
 
220
- # Compone l'analisi finale
221
  analisi_finale = {
222
  "full_sentence": frase,
223
  "main_clause": {
@@ -231,12 +237,10 @@ def analizza_frase():
231
  return jsonify(analisi_finale)
232
 
233
  except Exception as e:
234
- # Gestione migliorata degli errori
235
  print(f"Errore durante l'analisi: {e}")
236
  traceback.print_exc()
237
  return jsonify({"errore": "Si è verificato un errore interno."}), 500
238
 
239
  if __name__ == '__main__':
240
- # Ottiene la porta dalle variabili d'ambiente per facilitare il deployment
241
  porta = int(os.environ.get("PORT", 8080))
242
  app.run(host="0.0.0.0", port=porta, debug=True)
 
21
  # Abilita la Condivisione delle Risorse tra Origini Diverse (CORS)
22
  CORS(app)
23
 
24
+ # --- INIZIO SEZIONE TRADUZIONI ---
25
+ # Mappe per le traduzioni in italiano delle spiegazioni di spaCy
26
+ SPIEGAZIONI_POS_IT = {
27
+ "ADJ": "Aggettivo", "ADP": "Preposizione", "ADV": "Avverbio", "AUX": "Ausiliare",
28
+ "CONJ": "Congiunzione", "CCONJ": "Congiunzione Coordinante", "SCONJ": "Congiunzione Subordinante",
29
+ "DET": "Determinante", "INTJ": "Interiezione", "NOUN": "Sostantivo", "NUM": "Numerale",
30
+ "PART": "Particella", "PRON": "Pronome", "PROPN": "Nome Proprio", "PUNCT": "Punteggiatura",
31
+ "SPACE": "Spazio", "SYM": "Simbolo", "VERB": "Verbo", "X": "Altro",
32
+ }
33
+
34
+ SPIEGAZIONI_ENT_IT = {
35
+ "PER": "Persona: Nomi di persone reali o fittizie.",
36
+ "LOC": "Luogo: Nomi di luoghi geografici come paesi, città, stati.",
37
+ "ORG": "Organizzazione: Nomi di aziende, istituzioni, governi.",
38
+ "MISC": "Miscellanea: Entità che non rientrano nelle altre categorie (es. eventi, nazionalità, prodotti)."
39
+ }
40
+
41
+ def spiega_in_italiano(tag, tipo='pos'):
42
+ """Fornisce una spiegazione in italiano per un tag POS o di entità."""
43
+ if tipo == 'pos':
44
+ return SPIEGAZIONI_POS_IT.get(tag, tag)
45
+ if tipo == 'ent':
46
+ return SPIEGAZIONI_ENT_IT.get(tag, tag)
47
+ # Ritorna il tag originale se non trova una spiegazione
48
+ return tag
49
+ # --- FINE SEZIONE TRADUZIONI ---
50
+
51
  # Mappatura delle etichette di dipendenza di spaCy alle nostre etichette di analisi logica con spiegazioni
52
  MAPPA_DEP = {
53
  "nsubj": {"label": "Soggetto", "description": "Indica chi o cosa compie l'azione o si trova in un certo stato."},
 
68
  def ottieni_tipo_complemento_con_dettagli(token):
69
  """Affina il tipo di complemento basandosi sulla preposizione precedente e fornisce dettagli."""
70
  preposizione = ""
 
71
  for figlio in token.children:
72
  if figlio.dep_ == "case":
73
  preposizione = figlio.text.lower()
74
  break
 
75
  if not preposizione and token.head.dep_ == 'obl':
76
  for figlio in token.head.children:
77
  if figlio.dep_ == "case":
 
83
  if preposizione in ["a", "al", "allo", "alla", "ai", "agli", "alle"]:
84
  return {"label": "Complemento di Termine", "description": "Risponde alla domanda 'a chi?', 'a che cosa?'."}
85
  if preposizione in ["da", "dal", "dallo", "dalla", "dai", "dagli", "dalle"]:
 
86
  if any(figlio.dep_ == 'aux:pass' for figlio in token.head.children):
87
  return {"label": "Complemento d'Agente", "description": "Indica da chi è compiuta l'azione in una frase passiva."}
88
  return {"label": "Complemento di Moto da Luogo", "description": "Indica il luogo da cui inizia un movimento."}
 
97
  if preposizione in ["tra", "fra"]:
98
  return {"label": "Complemento di Luogo o Tempo (Partitivo)", "description": "Indica una posizione intermedia o una scelta all'interno di un gruppo."}
99
 
 
100
  return {"label": "Complemento Indiretto", "description": "Fornisce un'informazione generica non classificata in modo più specifico."}
101
 
102
  def ottieni_testo_completo(token):
103
  """Costruisce ricorsivamente il testo completo di un sintagma, partendo da un token principale."""
 
104
  token_sintagma = [token] + sorted([t for t in token.children if t.dep_ in ('det', 'amod', 'case', 'advmod')], key=lambda x: x.i)
 
105
  token_sintagma.sort(key=lambda x: x.i)
106
  return " ".join(t.text for t in token_sintagma)
107
 
 
109
  """Aggrega i token in sintagmi grammaticali significativi con spiegazioni dettagliate."""
110
  mappa_sintagmi = {}
111
 
 
112
  for token in lista_token:
 
113
  if token.dep_ not in ['det', 'case', 'amod', 'punct', 'aux', 'cop', 'mark']:
114
  mappa_sintagmi[token.i] = {
115
  "text": ottieni_testo_completo(token),
 
116
  "token_details": {
117
  "lemma": token.lemma_,
118
+ "pos": f"{token.pos_}: {spiega_in_italiano(token.pos_, 'pos')}",
119
+ "tag": f"{token.tag_}: {spiega_in_italiano(token.tag_, 'pos')}", # Usa 'pos' per la spiegazione del tag più generica
120
  "morph": str(token.morph) if token.morph else "Non disponibile"
121
  },
122
  "label_info": {},
 
126
  risultato_analisi = []
127
  indici_elaborati = set()
128
 
129
+ for indice, sintagma in sorted(mappa_sintagmi.items()):
130
  if indice in indici_elaborati:
131
  continue
132
 
 
135
  info_etichetta = {}
136
 
137
  if dep == "ROOT":
 
138
  e_nominale = any(c.dep_ == 'cop' for c in token.children)
139
  if e_nominale:
140
  copula = [c for c in token.children if c.dep_ == 'cop'][0]
141
  nome_del_predicato = ottieni_testo_completo(token)
 
142
  risultato_analisi.append({
143
  "text": copula.text,
144
  "label_info": {"label": "Copula", "description": "Verbo 'essere' che collega il soggetto alla parte nominale."},
145
  "token_details": {
146
  "lemma": copula.lemma_,
147
+ "pos": f"{copula.pos_}: {spiega_in_italiano(copula.pos_, 'pos')}",
148
+ "tag": f"{copula.tag_}: {spiega_in_italiano(copula.tag_, 'pos')}",
149
  "morph": str(copula.morph) if copula.morph else "Non disponibile"
150
  }
151
  })
 
152
  risultato_analisi.append({
153
  "text": nome_del_predicato,
154
  "label_info": {"label": "Parte Nominale del Predicato", "description": "Aggettivo o nome che descrive il soggetto."},
155
  "token_details": sintagma["token_details"]
156
  })
157
  else:
 
158
  info_etichetta = MAPPA_DEP.get(dep, {})
159
  elif dep == 'obl':
 
160
  info_etichetta = ottieni_tipo_complemento_con_dettagli(token)
161
  elif dep in MAPPA_DEP:
 
162
  info_etichetta = MAPPA_DEP[dep]
163
 
 
164
  if info_etichetta:
165
  sintagma_da_aggiungere = {
166
  "text": sintagma['text'],
167
  "label_info": info_etichetta
168
  }
 
169
  if sintagma.get("token_details"):
170
  sintagma_da_aggiungere["token_details"] = sintagma["token_details"]
171
  risultato_analisi.append(sintagma_da_aggiungere)
 
176
 
177
  def analizza_proposizione_con_dettagli(token_proposizione):
178
  """Analizza una singola proposizione (principale o subordinata) con dettagli."""
 
179
  token_nella_proposizione = [t for t in token_proposizione if t.dep_ != 'mark']
180
  return costruisci_sintagmi_con_dettagli(token_nella_proposizione)
181
 
 
195
  frase = dati['sentence']
196
  doc = nlp(frase)
197
 
 
198
  proposizioni_subordinate = []
199
+ indici_subordinate = set()
200
 
 
201
  for token in doc:
202
  if token.dep_ in ["acl:relcl", "advcl", "ccomp", "csubj"]:
203
  token_proposizione_subordinata = list(token.subtree)
204
+ for t in token_proposizione_subordinata:
205
+ indici_subordinate.add(t.i)
206
+
207
  info_tipo_subordinata = MAPPA_DEP.get(token.dep_, {"label": "Proposizione Subordinata", "description": "Una frase che dipende da un'altra."})
208
 
 
209
  marcatore = [figlio for figlio in token.children if figlio.dep_ == 'mark']
210
  intro = marcatore[0].text if marcatore else ""
211
 
212
  proposizioni_subordinate.append({
213
  "type_info": info_tipo_subordinata,
214
+ "text": " ".join(t.text for t in token_proposizione_subordinata if not t.is_punct),
215
  "intro": intro,
216
  "analysis": analizza_proposizione_con_dettagli(token_proposizione_subordinata)
217
  })
218
 
 
 
219
  token_proposizione_principale = [token for token in doc if token.i not in indici_subordinate]
220
 
 
221
  entita_nominate = [{
222
  "text": ent.text,
223
  "label": ent.label_,
224
+ "explanation": spiega_in_italiano(ent.label_, 'ent')
225
  } for ent in doc.ents]
226
 
 
227
  analisi_finale = {
228
  "full_sentence": frase,
229
  "main_clause": {
 
237
  return jsonify(analisi_finale)
238
 
239
  except Exception as e:
 
240
  print(f"Errore durante l'analisi: {e}")
241
  traceback.print_exc()
242
  return jsonify({"errore": "Si è verificato un errore interno."}), 500
243
 
244
  if __name__ == '__main__':
 
245
  porta = int(os.environ.get("PORT", 8080))
246
  app.run(host="0.0.0.0", port=porta, debug=True)