Commit ff8d1207 authored by J. Fernando Sánchez's avatar J. Fernando Sánchez

Improved plugins (reload, imp)

parent bdf19927
......@@ -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
......
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__)
......
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()
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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment