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
|
||||
from flask import Flask
|
||||
from senpy import Senpy
|
||||
from senpy.extensions import Senpy
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@ -30,4 +30,4 @@ sp.init_app(app)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.debug = config.DEBUG
|
||||
app.run()
|
||||
app.run(use_reloader=False)
|
||||
|
@ -18,6 +18,8 @@
|
||||
Sentiment analysis server in Python
|
||||
'''
|
||||
|
||||
VERSION = "0.2.4"
|
||||
|
||||
import extensions
|
||||
import blueprints
|
||||
import plugins
|
||||
|
@ -22,43 +22,13 @@ import json
|
||||
|
||||
nif_blueprint = Blueprint("NIF Sentiment Analysis Server", __name__)
|
||||
|
||||
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"],
|
||||
},
|
||||
"algorithm": {"aliases": ["algorithm", "a", "algo"],
|
||||
"required": False,
|
||||
},
|
||||
"language": {"aliases": ["language", "l"],
|
||||
"required": False,
|
||||
"options": ["es", "en"],
|
||||
},
|
||||
"urischeme": {"aliases": ["urischeme", "u"],
|
||||
"required": False,
|
||||
"default": "RFC5147String",
|
||||
"options": "RFC5147String"
|
||||
},
|
||||
}
|
||||
BASIC_PARAMS = {
|
||||
"algorithm": {"aliases": ["algorithm", "a", "algo"],
|
||||
"required": False,
|
||||
},
|
||||
}
|
||||
|
||||
def get_algorithm(req):
|
||||
return get_params(req, params={"algorithm": PARAMS["algorithm"]})
|
||||
|
||||
def get_params(req, params=PARAMS):
|
||||
def get_params(req, params=BASIC_PARAMS):
|
||||
indict = None
|
||||
if req.method == 'POST':
|
||||
indict = req.form
|
||||
@ -113,35 +83,44 @@ def basic_analysis(params):
|
||||
@nif_blueprint.route('/', methods=['POST', 'GET'])
|
||||
def home(entries=None):
|
||||
try:
|
||||
algo = get_algorithm(request)["algorithm"]
|
||||
specific_params = PARAMS.copy()
|
||||
specific_params.update(current_app.senpy.parameters(algo))
|
||||
algo = get_params(request).get("algorithm", None)
|
||||
specific_params = current_app.senpy.parameters(algo)
|
||||
params = get_params(request, specific_params)
|
||||
except ValueError as ex:
|
||||
return ex.message
|
||||
response = current_app.senpy.analyse(**params)
|
||||
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/<plugin>', methods=['POST', 'GET'])
|
||||
@nif_blueprint.route('/plugins/<plugin>/<action>', methods=['POST', 'GET'])
|
||||
def plugins(plugin=None, action="list"):
|
||||
print current_app.senpy.plugins.keys()
|
||||
filt = {}
|
||||
if plugin:
|
||||
plugs = {plugin:current_app.senpy.plugins[plugin]}
|
||||
else:
|
||||
plugs = current_app.senpy.plugins
|
||||
filt["name"] = plugin
|
||||
plugs = current_app.senpy.filter_plugins(**filt)
|
||||
if plugin and not plugs:
|
||||
return "Plugin not found", 400
|
||||
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)
|
||||
elif action == "disable":
|
||||
plugs[plugin].enabled = False
|
||||
if action == "disable":
|
||||
current_app.senpy.disable_plugin(plugin)
|
||||
return "Ok"
|
||||
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"
|
||||
else:
|
||||
return "action '{}' not allowed".format(action), 404
|
||||
return "action '{}' not allowed".format(action), 400
|
||||
|
||||
if __name__ == '__main__':
|
||||
import config
|
||||
|
@ -1,8 +1,10 @@
|
||||
import os
|
||||
import sys
|
||||
import importlib
|
||||
import imp
|
||||
|
||||
from flask import current_app
|
||||
from collections import defaultdict
|
||||
from .plugins import SentimentPlugin, EmotionPlugin
|
||||
|
||||
try:
|
||||
from flask import _app_ctx_stack as stack
|
||||
@ -18,7 +20,7 @@ class Senpy(object):
|
||||
self.app = app
|
||||
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 app is not None:
|
||||
@ -41,24 +43,49 @@ class Senpy(object):
|
||||
app.register_blueprint(nif_blueprint)
|
||||
|
||||
def analyse(self, **params):
|
||||
algo = None
|
||||
print("analysing with params: {}".format(params))
|
||||
if "algorithm" in params:
|
||||
algo = params["algorithm"]
|
||||
if algo in self.plugins and self.plugins[algo].enabled:
|
||||
plug = self.plugins[algo]
|
||||
resp = plug.analyse(**params)
|
||||
resp.analysis.append(plug.jsonable())
|
||||
return resp
|
||||
return {"status": 500, "message": "No valid algorithm"}
|
||||
elif self.plugins:
|
||||
algo = self.default_plugin
|
||||
if algo in self.plugins and self.plugins[algo].enabled:
|
||||
plug = self.plugins[algo]
|
||||
resp = plug.analyse(**params)
|
||||
resp.analysis.append(plug.jsonable())
|
||||
return resp
|
||||
else:
|
||||
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):
|
||||
if algo in self.plugins:
|
||||
if hasattr(self.plugins[algo], "parameters"):
|
||||
return self.plugins[algo].parameters
|
||||
return {}
|
||||
return getattr(self.plugins.get(algo or self.default_plugin), "params", {})
|
||||
|
||||
def enable_plugin(self, plugin):
|
||||
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):
|
||||
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)
|
||||
tmp.path = search_folder
|
||||
try:
|
||||
@ -68,19 +95,19 @@ class Senpy(object):
|
||||
tmp.repo = None
|
||||
if not hasattr(tmp, "enabled"):
|
||||
tmp.enabled = enabled
|
||||
tmp.module = plugin
|
||||
return tmp
|
||||
|
||||
|
||||
def _load_plugins(self):
|
||||
#print(sys.path)
|
||||
#print(search_folder)
|
||||
plugins = {}
|
||||
for search_folder in self.search_folders:
|
||||
for item in os.listdir(search_folder):
|
||||
if os.path.isdir(os.path.join(search_folder, item)) \
|
||||
and os.path.exists(
|
||||
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
|
||||
|
||||
@ -105,6 +132,20 @@ class Senpy(object):
|
||||
self._plugins = self._load_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__':
|
||||
from flask import Flask
|
||||
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):
|
||||
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.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):
|
||||
pass
|
||||
|
||||
def activate(self):
|
||||
pass
|
||||
def enable(self):
|
||||
self.enabled = True
|
||||
|
||||
def deactivate(self):
|
||||
pass
|
||||
def disable(self):
|
||||
self.enabled = False
|
||||
|
||||
def jsonable(self, parameters=False):
|
||||
resp = {
|
||||
"@id": "{}_{}".format(self.name, self.version),
|
||||
"enabled": self.enabled,
|
||||
}
|
||||
if self.repo:
|
||||
resp["repo"] = self.repo.remotes[0].url
|
||||
if parameters:
|
||||
resp["parameters"] = self.params,
|
||||
resp["parameters"] = self.params
|
||||
elif self.extraparams:
|
||||
resp["extra_parameters"] = self.extraparams
|
||||
return resp
|
||||
|
||||
class SentimentPlugin(SenpyPlugin):
|
||||
|
@ -3,20 +3,20 @@ import json
|
||||
|
||||
import sys
|
||||
|
||||
print(sys.path)
|
||||
from senpy.plugins import SentimentPlugin
|
||||
from senpy.models import Response, Opinion, Entry
|
||||
|
||||
class Sentiment140Plugin(SentimentPlugin):
|
||||
parameters = {
|
||||
EXTRA_PARAMS = {
|
||||
"language": {"aliases": ["language", "l"],
|
||||
"required": False,
|
||||
"options": ["es", "en", "auto"],
|
||||
}
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
super(Sentiment140Plugin, self).__init__(name="Sentiment140",
|
||||
version="1.0",
|
||||
super(Sentiment140Plugin, self).__init__(name="sentiment140",
|
||||
version="2.0",
|
||||
extraparams=self.EXTRA_PARAMS,
|
||||
**kwargs)
|
||||
|
||||
def analyse(self, **params):
|
||||
@ -44,5 +44,4 @@ class Sentiment140Plugin(SentimentPlugin):
|
||||
response.entries.append(entry)
|
||||
return response
|
||||
|
||||
|
||||
plugin = Sentiment140Plugin()
|
||||
|
3
setup.py
3
setup.py
@ -1,9 +1,10 @@
|
||||
from setuptools import setup
|
||||
import senpy
|
||||
|
||||
setup(
|
||||
name = 'senpy',
|
||||
packages = ['senpy'], # this must be the same as the name above
|
||||
version = '0.2.3',
|
||||
version = senpy.VERSION,
|
||||
description = '''
|
||||
A sentiment analysis server implementation. Designed to be \
|
||||
extendable, so new algorithms and sources can be used.
|
||||
|
Loading…
Reference in New Issue
Block a user