mirror of
https://github.com/gsi-upm/senpy
synced 2024-11-24 17:12:29 +00:00
Improved plugins (reload, imp)
This commit is contained in:
parent
bdf1992775
commit
ff8d12074b
4
app.py
4
app.py
@ -21,7 +21,7 @@ This class shows how to use the nif_server module to create custom services.
|
|||||||
'''
|
'''
|
||||||
import config
|
import config
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from senpy import Senpy
|
from senpy.extensions import Senpy
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@ -30,4 +30,4 @@ sp.init_app(app)
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.debug = config.DEBUG
|
app.debug = config.DEBUG
|
||||||
app.run()
|
app.run(use_reloader=False)
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
Sentiment analysis server in Python
|
Sentiment analysis server in Python
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
VERSION = "0.2.4"
|
||||||
|
|
||||||
import extensions
|
import extensions
|
||||||
import blueprints
|
import blueprints
|
||||||
import plugins
|
import plugins
|
||||||
|
@ -22,43 +22,13 @@ import json
|
|||||||
|
|
||||||
nif_blueprint = Blueprint("NIF Sentiment Analysis Server", __name__)
|
nif_blueprint = Blueprint("NIF Sentiment Analysis Server", __name__)
|
||||||
|
|
||||||
PARAMS = {"input": {"aliases": ["i", "input"],
|
BASIC_PARAMS = {
|
||||||
"required": True,
|
|
||||||
"help": "Input text"
|
|
||||||
},
|
|
||||||
"informat": {"aliases": ["f", "informat"],
|
|
||||||
"required": False,
|
|
||||||
"default": "text",
|
|
||||||
"options": ["turtle", "text"],
|
|
||||||
},
|
|
||||||
"intype": {"aliases": ["intype", "t"],
|
|
||||||
"required": False,
|
|
||||||
"default": "direct",
|
|
||||||
"options": ["direct", "url", "file"],
|
|
||||||
},
|
|
||||||
"outformat": {"aliases": ["outformat", "o"],
|
|
||||||
"default": "json-ld",
|
|
||||||
"required": False,
|
|
||||||
"options": ["json-ld"],
|
|
||||||
},
|
|
||||||
"algorithm": {"aliases": ["algorithm", "a", "algo"],
|
"algorithm": {"aliases": ["algorithm", "a", "algo"],
|
||||||
"required": False,
|
"required": False,
|
||||||
},
|
},
|
||||||
"language": {"aliases": ["language", "l"],
|
}
|
||||||
"required": False,
|
|
||||||
"options": ["es", "en"],
|
|
||||||
},
|
|
||||||
"urischeme": {"aliases": ["urischeme", "u"],
|
|
||||||
"required": False,
|
|
||||||
"default": "RFC5147String",
|
|
||||||
"options": "RFC5147String"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_algorithm(req):
|
def get_params(req, params=BASIC_PARAMS):
|
||||||
return get_params(req, params={"algorithm": PARAMS["algorithm"]})
|
|
||||||
|
|
||||||
def get_params(req, params=PARAMS):
|
|
||||||
indict = None
|
indict = None
|
||||||
if req.method == 'POST':
|
if req.method == 'POST':
|
||||||
indict = req.form
|
indict = req.form
|
||||||
@ -113,35 +83,44 @@ def basic_analysis(params):
|
|||||||
@nif_blueprint.route('/', methods=['POST', 'GET'])
|
@nif_blueprint.route('/', methods=['POST', 'GET'])
|
||||||
def home(entries=None):
|
def home(entries=None):
|
||||||
try:
|
try:
|
||||||
algo = get_algorithm(request)["algorithm"]
|
algo = get_params(request).get("algorithm", None)
|
||||||
specific_params = PARAMS.copy()
|
specific_params = current_app.senpy.parameters(algo)
|
||||||
specific_params.update(current_app.senpy.parameters(algo))
|
|
||||||
params = get_params(request, specific_params)
|
params = get_params(request, specific_params)
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
return ex.message
|
return ex.message
|
||||||
response = current_app.senpy.analyse(**params)
|
response = current_app.senpy.analyse(**params)
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
|
@nif_blueprint.route("/default")
|
||||||
|
def default():
|
||||||
|
return current_app.senpy.default_plugin
|
||||||
|
#return plugins(action="list", plugin=current_app.senpy.default_algorithm)
|
||||||
|
|
||||||
@nif_blueprint.route('/plugins/', methods=['POST', 'GET'])
|
@nif_blueprint.route('/plugins/', methods=['POST', 'GET'])
|
||||||
@nif_blueprint.route('/plugins/<plugin>', methods=['POST', 'GET'])
|
@nif_blueprint.route('/plugins/<plugin>', methods=['POST', 'GET'])
|
||||||
@nif_blueprint.route('/plugins/<plugin>/<action>', methods=['POST', 'GET'])
|
@nif_blueprint.route('/plugins/<plugin>/<action>', methods=['POST', 'GET'])
|
||||||
def plugins(plugin=None, action="list"):
|
def plugins(plugin=None, action="list"):
|
||||||
print current_app.senpy.plugins.keys()
|
filt = {}
|
||||||
if plugin:
|
if plugin:
|
||||||
plugs = {plugin:current_app.senpy.plugins[plugin]}
|
filt["name"] = plugin
|
||||||
else:
|
plugs = current_app.senpy.filter_plugins(**filt)
|
||||||
plugs = current_app.senpy.plugins
|
if plugin and not plugs:
|
||||||
|
return "Plugin not found", 400
|
||||||
if action == "list":
|
if action == "list":
|
||||||
dic = {plug:plugs[plug].jsonable(True) for plug in plugs}
|
with_params = request.args.get("params", "") == "1"
|
||||||
|
dic = {plug:plugs[plug].jsonable(with_params) for plug in plugs}
|
||||||
return jsonify(dic)
|
return jsonify(dic)
|
||||||
elif action == "disable":
|
if action == "disable":
|
||||||
plugs[plugin].enabled = False
|
current_app.senpy.disable_plugin(plugin)
|
||||||
return "Ok"
|
return "Ok"
|
||||||
elif action == "enable":
|
elif action == "enable":
|
||||||
plugs[plugin].enabled = True
|
current_app.senpy.enable_plugin(plugin)
|
||||||
|
return "Ok"
|
||||||
|
elif action == "reload":
|
||||||
|
current_app.senpy.reload_plugin(plugin)
|
||||||
return "Ok"
|
return "Ok"
|
||||||
else:
|
else:
|
||||||
return "action '{}' not allowed".format(action), 404
|
return "action '{}' not allowed".format(action), 400
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import config
|
import config
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import importlib
|
import imp
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
from collections import defaultdict
|
||||||
|
from .plugins import SentimentPlugin, EmotionPlugin
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from flask import _app_ctx_stack as stack
|
from flask import _app_ctx_stack as stack
|
||||||
@ -18,7 +20,7 @@ class Senpy(object):
|
|||||||
self.app = app
|
self.app = app
|
||||||
base_folder = os.path.join(os.path.dirname(__file__), "plugins")
|
base_folder = os.path.join(os.path.dirname(__file__), "plugins")
|
||||||
|
|
||||||
self.search_folders = (folder for folder in (base_folder, plugin_folder)
|
self.search_folders = (folder for folder in (base_folder, plugin_folder, '/tmp/plugins')
|
||||||
if folder and os.path.isdir(folder))
|
if folder and os.path.isdir(folder))
|
||||||
|
|
||||||
if app is not None:
|
if app is not None:
|
||||||
@ -41,24 +43,49 @@ class Senpy(object):
|
|||||||
app.register_blueprint(nif_blueprint)
|
app.register_blueprint(nif_blueprint)
|
||||||
|
|
||||||
def analyse(self, **params):
|
def analyse(self, **params):
|
||||||
|
algo = None
|
||||||
|
print("analysing with params: {}".format(params))
|
||||||
if "algorithm" in params:
|
if "algorithm" in params:
|
||||||
algo = params["algorithm"]
|
algo = params["algorithm"]
|
||||||
|
elif self.plugins:
|
||||||
|
algo = self.default_plugin
|
||||||
if algo in self.plugins and self.plugins[algo].enabled:
|
if algo in self.plugins and self.plugins[algo].enabled:
|
||||||
plug = self.plugins[algo]
|
plug = self.plugins[algo]
|
||||||
resp = plug.analyse(**params)
|
resp = plug.analyse(**params)
|
||||||
resp.analysis.append(plug.jsonable())
|
resp.analysis.append(plug.jsonable())
|
||||||
return resp
|
return resp
|
||||||
|
else:
|
||||||
return {"status": 500, "message": "No valid algorithm"}
|
return {"status": 500, "message": "No valid algorithm"}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_plugin(self):
|
||||||
|
if self.plugins:
|
||||||
|
candidate = self.filter_plugins(enabled=True).keys()[0]
|
||||||
|
print("Default: {}".format(candidate))
|
||||||
|
return candidate
|
||||||
|
else:
|
||||||
|
return Exception("No algorithm")
|
||||||
|
|
||||||
def parameters(self, algo):
|
def parameters(self, algo):
|
||||||
if algo in self.plugins:
|
return getattr(self.plugins.get(algo or self.default_plugin), "params", {})
|
||||||
if hasattr(self.plugins[algo], "parameters"):
|
|
||||||
return self.plugins[algo].parameters
|
def enable_plugin(self, plugin):
|
||||||
return {}
|
self.plugins[plugin].disable()
|
||||||
|
|
||||||
|
def disable_plugin(self, plugin):
|
||||||
|
self.plugins[plugin].disable()
|
||||||
|
|
||||||
|
def reload_plugin(self, plugin):
|
||||||
|
print("Reloading {}".format(plugin))
|
||||||
|
plug = self.plugins[plugin]
|
||||||
|
nplug = self._load_plugin(plug.module, plug.path)
|
||||||
|
del self.plugins[plugin]
|
||||||
|
self.plugins[nplug.name] = nplug
|
||||||
|
|
||||||
def _load_plugin(self, plugin, search_folder, enabled=True):
|
def _load_plugin(self, plugin, search_folder, enabled=True):
|
||||||
sys.path.append(search_folder)
|
sys.path.append(search_folder)
|
||||||
tmp = importlib.import_module(plugin).plugin
|
(fp, pathname, desc) = imp.find_module(plugin)
|
||||||
|
tmp = imp.load_module(plugin, fp, pathname, desc).plugin
|
||||||
sys.path.remove(search_folder)
|
sys.path.remove(search_folder)
|
||||||
tmp.path = search_folder
|
tmp.path = search_folder
|
||||||
try:
|
try:
|
||||||
@ -68,19 +95,19 @@ class Senpy(object):
|
|||||||
tmp.repo = None
|
tmp.repo = None
|
||||||
if not hasattr(tmp, "enabled"):
|
if not hasattr(tmp, "enabled"):
|
||||||
tmp.enabled = enabled
|
tmp.enabled = enabled
|
||||||
|
tmp.module = plugin
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
|
|
||||||
def _load_plugins(self):
|
def _load_plugins(self):
|
||||||
#print(sys.path)
|
|
||||||
#print(search_folder)
|
|
||||||
plugins = {}
|
plugins = {}
|
||||||
for search_folder in self.search_folders:
|
for search_folder in self.search_folders:
|
||||||
for item in os.listdir(search_folder):
|
for item in os.listdir(search_folder):
|
||||||
if os.path.isdir(os.path.join(search_folder, item)) \
|
if os.path.isdir(os.path.join(search_folder, item)) \
|
||||||
and os.path.exists(
|
and os.path.exists(
|
||||||
os.path.join(search_folder, item, "__init__.py")):
|
os.path.join(search_folder, item, "__init__.py")):
|
||||||
plugins[item] = self._load_plugin(item, search_folder)
|
plugin = self._load_plugin(item, search_folder)
|
||||||
|
plugins[plugin.name] = plugin
|
||||||
|
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
@ -105,6 +132,20 @@ class Senpy(object):
|
|||||||
self._plugins = self._load_plugins()
|
self._plugins = self._load_plugins()
|
||||||
return self._plugins
|
return self._plugins
|
||||||
|
|
||||||
|
def filter_plugins(self, **kwargs):
|
||||||
|
def matches(plug):
|
||||||
|
res = all(getattr(plug, k, None)==v for (k,v) in kwargs.items())
|
||||||
|
print("matching {} with {}: {}".format(plug.name, kwargs, res))
|
||||||
|
return res
|
||||||
|
if not kwargs:
|
||||||
|
return self.plugins
|
||||||
|
else:
|
||||||
|
return {n:p for n,p in self.plugins.items() if matches(p)}
|
||||||
|
|
||||||
|
def sentiment_plugins(self):
|
||||||
|
return (plugin for plugin in self.plugins if
|
||||||
|
isinstance(plugin, SentimentPlugin))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
@ -1,24 +1,67 @@
|
|||||||
|
PARAMS = {"input": {"aliases": ["i", "input"],
|
||||||
|
"required": True,
|
||||||
|
"help": "Input text"
|
||||||
|
},
|
||||||
|
"informat": {"aliases": ["f", "informat"],
|
||||||
|
"required": False,
|
||||||
|
"default": "text",
|
||||||
|
"options": ["turtle", "text"],
|
||||||
|
},
|
||||||
|
"intype": {"aliases": ["intype", "t"],
|
||||||
|
"required": False,
|
||||||
|
"default": "direct",
|
||||||
|
"options": ["direct", "url", "file"],
|
||||||
|
},
|
||||||
|
"outformat": {"aliases": ["outformat", "o"],
|
||||||
|
"default": "json-ld",
|
||||||
|
"required": False,
|
||||||
|
"options": ["json-ld"],
|
||||||
|
},
|
||||||
|
"language": {"aliases": ["language", "l"],
|
||||||
|
"required": False,
|
||||||
|
"options": ["es", "en"],
|
||||||
|
},
|
||||||
|
"urischeme": {"aliases": ["urischeme", "u"],
|
||||||
|
"required": False,
|
||||||
|
"default": "RFC5147String",
|
||||||
|
"options": "RFC5147String"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
class SenpyPlugin(object):
|
class SenpyPlugin(object):
|
||||||
def __init__(self, name=None, version=None, params=None):
|
def __init__(self, name=None, version=None, extraparams=None, params=None):
|
||||||
|
print("Initing {}".format(name))
|
||||||
self.name = name
|
self.name = name
|
||||||
self.version = version
|
self.version = version
|
||||||
self.params = params or []
|
if params:
|
||||||
|
self.params = params
|
||||||
|
else:
|
||||||
|
self.params = PARAMS.copy()
|
||||||
|
if extraparams:
|
||||||
|
self.params.update(extraparams)
|
||||||
|
self.extraparams = extraparams or {}
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
def analyse(self, *args, **kwargs):
|
def analyse(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def activate(self):
|
def enable(self):
|
||||||
pass
|
self.enabled = True
|
||||||
|
|
||||||
def deactivate(self):
|
def disable(self):
|
||||||
pass
|
self.enabled = False
|
||||||
|
|
||||||
def jsonable(self, parameters=False):
|
def jsonable(self, parameters=False):
|
||||||
resp = {
|
resp = {
|
||||||
"@id": "{}_{}".format(self.name, self.version),
|
"@id": "{}_{}".format(self.name, self.version),
|
||||||
|
"enabled": self.enabled,
|
||||||
}
|
}
|
||||||
|
if self.repo:
|
||||||
|
resp["repo"] = self.repo.remotes[0].url
|
||||||
if parameters:
|
if parameters:
|
||||||
resp["parameters"] = self.params,
|
resp["parameters"] = self.params
|
||||||
|
elif self.extraparams:
|
||||||
|
resp["extra_parameters"] = self.extraparams
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
class SentimentPlugin(SenpyPlugin):
|
class SentimentPlugin(SenpyPlugin):
|
||||||
|
@ -3,20 +3,20 @@ import json
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
print(sys.path)
|
|
||||||
from senpy.plugins import SentimentPlugin
|
from senpy.plugins import SentimentPlugin
|
||||||
from senpy.models import Response, Opinion, Entry
|
from senpy.models import Response, Opinion, Entry
|
||||||
|
|
||||||
class Sentiment140Plugin(SentimentPlugin):
|
class Sentiment140Plugin(SentimentPlugin):
|
||||||
parameters = {
|
EXTRA_PARAMS = {
|
||||||
"language": {"aliases": ["language", "l"],
|
"language": {"aliases": ["language", "l"],
|
||||||
"required": False,
|
"required": False,
|
||||||
"options": ["es", "en", "auto"],
|
"options": ["es", "en", "auto"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(Sentiment140Plugin, self).__init__(name="Sentiment140",
|
super(Sentiment140Plugin, self).__init__(name="sentiment140",
|
||||||
version="1.0",
|
version="2.0",
|
||||||
|
extraparams=self.EXTRA_PARAMS,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
def analyse(self, **params):
|
def analyse(self, **params):
|
||||||
@ -44,5 +44,4 @@ class Sentiment140Plugin(SentimentPlugin):
|
|||||||
response.entries.append(entry)
|
response.entries.append(entry)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
plugin = Sentiment140Plugin()
|
plugin = Sentiment140Plugin()
|
||||||
|
3
setup.py
3
setup.py
@ -1,9 +1,10 @@
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
import senpy
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = 'senpy',
|
name = 'senpy',
|
||||||
packages = ['senpy'], # this must be the same as the name above
|
packages = ['senpy'], # this must be the same as the name above
|
||||||
version = '0.2.3',
|
version = senpy.VERSION,
|
||||||
description = '''
|
description = '''
|
||||||
A sentiment analysis server implementation. Designed to be \
|
A sentiment analysis server implementation. Designed to be \
|
||||||
extendable, so new algorithms and sources can be used.
|
extendable, so new algorithms and sources can be used.
|
||||||
|
Loading…
Reference in New Issue
Block a user