mirror of
https://github.com/gsi-upm/senpy
synced 2025-08-24 02:22:20 +00:00
New schema for parameters
* Add parameters as an entity in the schema * Update examples to include parameters * Change the API for processing plugins, params is a parameter again, instead of only adding the request. * Update tests
This commit is contained in:
@@ -3,7 +3,7 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from unittest import TestCase
|
||||
from senpy.api import (boolean, parse_params, get_extra_params, parse_extra_params,
|
||||
from senpy.api import (boolean, parse_params, get_extra_params, parse_analysis,
|
||||
API_PARAMS, NIF_PARAMS, WEB_PARAMS)
|
||||
from senpy.models import Error, Plugin
|
||||
|
||||
@@ -91,7 +91,7 @@ class APITest(TestCase):
|
||||
assert 'input' in p
|
||||
assert p['input'] == 'Aloha my friend'
|
||||
|
||||
def test_parse_extra_params(self):
|
||||
def test_parse_analysis(self):
|
||||
'''The API should parse user parameters and return them in a format that plugins can use'''
|
||||
plugins = [
|
||||
Plugin({
|
||||
@@ -161,10 +161,11 @@ class APITest(TestCase):
|
||||
}
|
||||
|
||||
]
|
||||
p = parse_extra_params(call, plugins)
|
||||
p = parse_analysis(call, plugins)
|
||||
for i, arg in enumerate(expected):
|
||||
params = p[i].params
|
||||
for k, v in arg.items():
|
||||
assert p[i][k] == v
|
||||
assert params[k] == v
|
||||
|
||||
def test_get_extra_params(self):
|
||||
'''The API should return the list of valid parameters for a set of plugins'''
|
||||
@@ -216,13 +217,11 @@ class APITest(TestCase):
|
||||
]
|
||||
|
||||
expected = {
|
||||
# Each plugin's parameters
|
||||
'0.param0': plugins[0]['extra_params']['param0'],
|
||||
'0.param1': plugins[0]['extra_params']['param1'],
|
||||
'0.param2': plugins[0]['extra_params']['param2'],
|
||||
'1.param0': plugins[1]['extra_params']['param0'],
|
||||
'1.param1': plugins[1]['extra_params']['param1'],
|
||||
'1.param3': plugins[1]['extra_params']['param3'],
|
||||
# Overlapping parameters
|
||||
'plugin1.param0': plugins[0]['extra_params']['param0'],
|
||||
'plugin1.param1': plugins[0]['extra_params']['param1'],
|
||||
'plugin2.param0': plugins[1]['extra_params']['param0'],
|
||||
'plugin2.param1': plugins[1]['extra_params']['param1'],
|
||||
|
||||
# Non-overlapping parameters
|
||||
'param2': plugins[0]['extra_params']['param2'],
|
||||
|
@@ -26,8 +26,7 @@ class BlueprintsTest(TestCase):
|
||||
cls.senpy.init_app(cls.app)
|
||||
cls.dir = os.path.join(os.path.dirname(__file__), "..")
|
||||
cls.senpy.add_folder(cls.dir)
|
||||
cls.senpy.activate_plugin("Dummy", sync=True)
|
||||
cls.senpy.activate_plugin("DummyRequired", sync=True)
|
||||
cls.senpy.activate_all()
|
||||
cls.senpy.default_plugin = 'Dummy'
|
||||
|
||||
def setUp(self):
|
||||
@@ -139,16 +138,27 @@ class BlueprintsTest(TestCase):
|
||||
# Calling dummy twice, should return the same string
|
||||
self.assertCode(resp, 200)
|
||||
js = parse_resp(resp)
|
||||
assert len(js['analysis']) == 1
|
||||
assert len(js['analysis']) == 2
|
||||
assert js['entries'][0]['nif:isString'] == 'My aloha mohame'
|
||||
|
||||
resp = self.client.get("/api/Dummy+Dummy?i=My aloha mohame")
|
||||
# Same with pluses instead of slashes
|
||||
self.assertCode(resp, 200)
|
||||
js = parse_resp(resp)
|
||||
assert len(js['analysis']) == 1
|
||||
assert len(js['analysis']) == 2
|
||||
assert js['entries'][0]['nif:isString'] == 'My aloha mohame'
|
||||
|
||||
def test_analysis_chain_required(self):
|
||||
"""
|
||||
If a parameter is required and duplicated (because two plugins require it), specifying
|
||||
it once should suffice
|
||||
"""
|
||||
resp = self.client.get("/api/DummyRequired/DummyRequired?i=My aloha mohame&example=a")
|
||||
js = parse_resp(resp)
|
||||
assert len(js['analysis']) == 2
|
||||
assert js['entries'][0]['nif:isString'] == 'My aloha mohame'
|
||||
assert js['entries'][0]['reversed'] == 2
|
||||
|
||||
def test_requirements_chain_help(self):
|
||||
'''The extra parameters of each plugin should be merged if they are in a chain '''
|
||||
resp = self.client.get("/api/split/DummyRequired?help=true")
|
||||
@@ -157,6 +167,7 @@ class BlueprintsTest(TestCase):
|
||||
assert 'valid_parameters' in js
|
||||
vp = js['valid_parameters']
|
||||
assert 'example' in vp
|
||||
assert 'delimiter' in vp
|
||||
|
||||
def test_requirements_chain_repeat_help(self):
|
||||
'''
|
||||
@@ -168,10 +179,14 @@ class BlueprintsTest(TestCase):
|
||||
js = parse_resp(resp)
|
||||
assert 'valid_parameters' in js
|
||||
vp = js['valid_parameters']
|
||||
assert '0.delimiter' in vp
|
||||
assert '1.delimiter' in vp
|
||||
assert 'delimiter' in vp
|
||||
|
||||
resp = self.client.get("/api/split/split?help=true&verbose=false")
|
||||
js = parse_resp(resp)
|
||||
vp = js['valid_parameters']
|
||||
assert len(vp.keys()) == 1
|
||||
|
||||
|
||||
def test_requirements_chain(self):
|
||||
"""
|
||||
It should be possible to specify different parameters for each step in the chain.
|
||||
|
@@ -11,14 +11,15 @@ except ImportError:
|
||||
from functools import partial
|
||||
from senpy.extensions import Senpy
|
||||
from senpy import plugins
|
||||
from senpy.models import Error, Results, Entry, EmotionSet, Emotion, Plugin
|
||||
from senpy.models import Analysis, Error, Results, Entry, EmotionSet, Emotion, Plugin
|
||||
from senpy import api
|
||||
from flask import Flask
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
def analyse(instance, **kwargs):
|
||||
request = api.parse_call(kwargs)
|
||||
basic = api.parse_params(kwargs, api.API_PARAMS)
|
||||
request = api.parse_call(basic)
|
||||
return instance.analyse(request)
|
||||
|
||||
|
||||
@@ -49,9 +50,9 @@ class ExtensionsTest(TestCase):
|
||||
'''Should be able to add and delete new plugins. '''
|
||||
new = plugins.Analysis(name='new', description='new', version=0)
|
||||
self.senpy.add_plugin(new)
|
||||
assert new in self.senpy.plugins()
|
||||
assert new in self.senpy.plugins(is_activated=False)
|
||||
self.senpy.delete_plugin(new)
|
||||
assert new not in self.senpy.plugins()
|
||||
assert new not in self.senpy.plugins(is_activated=False)
|
||||
|
||||
def test_adding_folder(self):
|
||||
""" It should be possible for senpy to look for plugins in more folders. """
|
||||
@@ -60,7 +61,7 @@ class ExtensionsTest(TestCase):
|
||||
default_plugins=False)
|
||||
assert not senpy.analysis_plugins
|
||||
senpy.add_folder(self.examples_dir)
|
||||
assert senpy.analysis_plugins
|
||||
assert senpy.plugins(plugin_type=plugins.AnalysisPlugin, is_activated=False)
|
||||
self.assertRaises(AttributeError, senpy.add_folder, 'DOES NOT EXIST')
|
||||
|
||||
def test_installing(self):
|
||||
@@ -121,8 +122,8 @@ class ExtensionsTest(TestCase):
|
||||
# Leaf (defaultdict with __setattr__ and __getattr__.
|
||||
r1 = analyse(self.senpy, algorithm="Dummy", input="tupni", output="tuptuo")
|
||||
r2 = analyse(self.senpy, input="tupni", output="tuptuo")
|
||||
assert r1.analysis[0].id == "endpoint:plugins/Dummy_0.1"
|
||||
assert r2.analysis[0].id == "endpoint:plugins/Dummy_0.1"
|
||||
assert r1.analysis[0].algorithm == "endpoint:plugins/Dummy_0.1"
|
||||
assert r2.analysis[0].algorithm == "endpoint:plugins/Dummy_0.1"
|
||||
assert r1.entries[0]['nif:isString'] == 'input'
|
||||
|
||||
def test_analyse_empty(self):
|
||||
@@ -130,7 +131,7 @@ class ExtensionsTest(TestCase):
|
||||
senpy = Senpy(plugin_folder=None,
|
||||
app=self.app,
|
||||
default_plugins=False)
|
||||
self.assertRaises(Error, senpy.analyse, Results())
|
||||
self.assertRaises(Error, senpy.analyse, Results(), [])
|
||||
|
||||
def test_analyse_wrong(self):
|
||||
""" Trying to analyse with a non-existent plugin should raise an error."""
|
||||
@@ -156,29 +157,32 @@ class ExtensionsTest(TestCase):
|
||||
r2 = analyse(self.senpy,
|
||||
input="tupni",
|
||||
output="tuptuo")
|
||||
assert r1.analysis[0].id == "endpoint:plugins/Dummy_0.1"
|
||||
assert r2.analysis[0].id == "endpoint:plugins/Dummy_0.1"
|
||||
assert r1.analysis[0].algorithm == "endpoint:plugins/Dummy_0.1"
|
||||
assert r2.analysis[0].algorithm == "endpoint:plugins/Dummy_0.1"
|
||||
assert r1.entries[0]['nif:isString'] == 'input'
|
||||
|
||||
def test_analyse_error(self):
|
||||
mm = mock.MagicMock()
|
||||
mm.id = 'magic_mock'
|
||||
mm.name = 'mock'
|
||||
mm.is_activated = True
|
||||
mm.process.side_effect = Error('error in analysis', status=500)
|
||||
self.senpy.add_plugin(mm)
|
||||
class ErrorPlugin(plugins.Analysis):
|
||||
author = 'nobody'
|
||||
version = 0
|
||||
ex = Error()
|
||||
|
||||
def process(self, *args, **kwargs):
|
||||
raise self.ex
|
||||
|
||||
m = ErrorPlugin(ex=Error('error in analysis', status=500))
|
||||
self.senpy.add_plugin(m)
|
||||
try:
|
||||
analyse(self.senpy, input='nothing', algorithm='MOCK')
|
||||
analyse(self.senpy, input='nothing', algorithm='ErrorPlugin')
|
||||
assert False
|
||||
except Error as ex:
|
||||
assert 'error in analysis' in ex['message']
|
||||
assert ex['status'] == 500
|
||||
|
||||
ex = Exception('generic exception on analysis')
|
||||
mm.process.side_effect = ex
|
||||
m.ex = Exception('generic exception on analysis')
|
||||
|
||||
try:
|
||||
analyse(self.senpy, input='nothing', algorithm='MOCK')
|
||||
analyse(self.senpy, input='nothing', algorithm='ErrorPlugin')
|
||||
assert False
|
||||
except Exception as ex:
|
||||
assert 'generic exception on analysis' in str(ex)
|
||||
@@ -194,7 +198,7 @@ class ExtensionsTest(TestCase):
|
||||
|
||||
def test_load_default_plugins(self):
|
||||
senpy = Senpy(plugin_folder=self.examples_dir, default_plugins=True)
|
||||
assert len(senpy.plugins()) > 1
|
||||
assert len(senpy.plugins(is_activated=False)) > 1
|
||||
|
||||
def test_convert_emotions(self):
|
||||
self.senpy.activate_all(sync=True)
|
||||
|
@@ -5,7 +5,8 @@ import jsonschema
|
||||
import json
|
||||
import rdflib
|
||||
from unittest import TestCase
|
||||
from senpy.models import (Emotion,
|
||||
from senpy.models import (Analysis,
|
||||
Emotion,
|
||||
EmotionAnalysis,
|
||||
EmotionSet,
|
||||
Entry,
|
||||
@@ -61,7 +62,7 @@ class ModelsTest(TestCase):
|
||||
def test_id(self):
|
||||
""" Adding the id after creation should overwrite the automatic ID
|
||||
"""
|
||||
r = Entry()
|
||||
r = Entry(_auto_id=True)
|
||||
j = r.jsonld()
|
||||
assert '@id' in j
|
||||
r.id = "test"
|
||||
@@ -189,6 +190,19 @@ class ModelsTest(TestCase):
|
||||
assert isinstance(js['plugins'], list)
|
||||
assert js['plugins'][0]['@type'] == 'sentimentPlugin'
|
||||
|
||||
def test_parameters(self):
|
||||
'''An Analysis should contain the algorithm and the list of parameters to be used'''
|
||||
a = Analysis()
|
||||
a.params = {'param1': 1, 'param2': 2}
|
||||
assert len(a.parameters) == 2
|
||||
for param in a.parameters:
|
||||
if param.name == 'param1':
|
||||
assert param.value == 1
|
||||
elif param.name == 'param2':
|
||||
assert param.value == 2
|
||||
else:
|
||||
raise Exception('Unknown value %s' % param)
|
||||
|
||||
def test_from_string(self):
|
||||
results = {
|
||||
'@type': 'results',
|
||||
|
Reference in New Issue
Block a user