-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
182 lines (160 loc) · 5.28 KB
/
server.py
File metadata and controls
182 lines (160 loc) · 5.28 KB
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
'''
NIL group basic NLP web service.
Usage:
server.py [options]
server.py --help
Options:
-h --help Show this screen.
--host=<h> Server address to bind to [default: 127.0.0.1].
--port=<p> Server port to bind to [default: 8080].
--model=<m> Spacy model to load [default: es].
--debug Reload the server on error or file change.
'''
from docopt import docopt
import bottle
import spacy
import json
import re
from nltk.corpus import wordnet
nlp_es = None
tag_split = re.compile('^([^_]+)__(.*)$')
feat_map = {
'Person': 'persona',
'Number': 'numero',
'Gender': 'genero',
}
val_map = {
'Sing': 'singular',
'Plur': 'plural',
'Fem': 'femenino',
'Masc': 'masculino'
}
pos_map = {
'NOUN': 'nombre',
'PROPN': 'nombre propio',
'VERB': 'verbo',
'AUX': 'verbo',
'ADJ': 'adjetivo',
'ADV': 'adverbio'
}
def tag_analisis (tag):
'''Convierte un análisis morfológico Ancora en un diccionario de
características.'''
res = tag_split.match(tag)
feats = res[2].split('|')
r = dict()
for f in feats:
cv = f.split('=')
if cv[0] in feat_map:
r[feat_map[cv[0]]] = val_map[cv[1]] if cv[1] in val_map else cv[1]
return r
def morfo (token):
'''Devuelve parte del análisis morfológico de una palabra.'''
pos = pos_map[token.pos_] if token.pos_ in pos_map else token.pos_
return {
'palabra': token.text,
'lema': token.lemma_,
'parte': pos,
**tag_analisis(token.tag_),
}
analisis_palabra_posibles = {
'morfologico': lambda token: morfo(token),
'sinonimos': lambda token: [ lema
for ss in wordnet.synsets(token.lemma_, lang='spa')
for lema in ss.lemma_names(lang='spa') ],
'synsets': lambda token: [ ss.name() for ss in
wordnet.synsets(token.lemma_, lang='spa') ],
}
analisis_palabra_defecto = [ 'morfologico' ]
@bottle.get('/analisis/<palabra>')
def analisis_palabra(palabra):
'''La ruta GET recibe un parámetro por URL que es una palabra de la que
calcular alguna de las siguientes propiedades:
- morfologico: análisis morfológico de cada palabra.
- synsets: synsets wordnet de la palabra (significados).
'''
operaciones = bottle.request.query.keys()
if len(operaciones)==0:
operaciones = analisis_palabra_defecto
try:
token = nlp_es(palabra)[0]
return {
tipo: analisis_palabra_posibles[tipo](token)
for tipo in operaciones
}
except KeyError:
bottle.abort(400, 'Petición json incorrecta.')
analisis_texto_posibles = {
'oraciones': lambda analisis: [ str(s) for s in analisis.sents ],
'entidades': lambda analisis: [ str(e) for e in analisis.ents ],
'sintagmas': lambda analisis: [ s.text for s in analisis.noun_chunks ],
}
analisis_texto_defecto = [ 'morfologico' ]
@bottle.post('/analisis')
def analisis_texto():
'''La ruta post recibe un objeto JSON con una propiedad "texto", que es el
texto a analizar. Además, por url recibe como parámetros los análisis que
devolver:
- oraciones: lista con las distintas oraciones del texto.
- entidades: lista de entidades nombradas detectadas en el texto.
- sintagmas: sintagmas nominales del texto.
- También puede recibir cualquiera de los análisis de palabra para
pasárselos a todo el texto.
'''
try :
operaciones = bottle.request.query.keys()
if len(operaciones)==0:
operaciones = analisis_texto_defecto
analisis = nlp_es(bottle.request.json["texto"])
return {
tipo: analisis_texto_posibles[tipo](analisis)
if tipo in analisis_texto_posibles else
[ analisis_palabra_posibles[tipo](palabra)
for palabra in analisis ]
for tipo in operaciones
}
except KeyError:
bottle.abort(400, 'Petición json incorrecta.')
@bottle.get('/synset/<ssid>')
def synset_ops(ssid):
'''Esta ruta recibe el id de un synset, y devuelve los lemas que pertenecen
a ese synset y la glosa.'''
try:
ss = wordnet.synset(ssid)
return {
'lemas': ss.lemma_names(lang='spa'),
'hiperonimos': [hyp.name() for hyp in ss.hypernyms()],
'hiponimos': [hyp.name() for hyp in ss.hyponyms()]
}
except KeyError:
bottle.abort(400, 'ID de synset inválida.')
@bottle.error(400)
def error_uso (error):
bottle.response.content_type = 'application/json'
return json.dumps({
'error': 'Error en la llamada al servicio.',
'mensaje': error.body
})
@bottle.error(405)
@bottle.error(404)
def error_rutas (error):
bottle.response.content_type = 'application/json'
return json.dumps({
'error': 'No existe el recurso solicitado o el método es incorrecto.'
})
@bottle.error(500)
def error_interno (error):
bottle.response.content_type = 'application/json'
print(error)
return json.dumps({
'error': 'Error interno del servidor.'
})
app = bottle.default_app()
if __name__ == "__main__":
arguments = docopt(__doc__)
nlp_es = spacy.load(arguments.get('--model'))
bottle.run(host=arguments.get('--host'),
port=arguments.get('--port'),
reloader=arguments.get('--debug'))
else:
nlp_es = spacy.load('es')