1
0
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:
J. Fernando Sánchez 2014-11-04 21:31:41 +01:00
parent bdf1992775
commit ff8d12074b
7 changed files with 144 additions and 79 deletions

4
app.py
View File

@ -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)

View File

@ -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

View File

@ -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, "algorithm": {"aliases": ["algorithm", "a", "algo"],
"help": "Input text" "required": False,
}, },
"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"
},
}
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

View File

@ -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"]
if algo in self.plugins and self.plugins[algo].enabled: elif self.plugins:
plug = self.plugins[algo] algo = self.default_plugin
resp = plug.analyse(**params) if algo in self.plugins and self.plugins[algo].enabled:
resp.analysis.append(plug.jsonable()) plug = self.plugins[algo]
return resp resp = plug.analyse(**params)
return {"status": 500, "message": "No valid algorithm"} 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): 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__)

View File

@ -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):

View File

@ -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()

View File

@ -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.