mirror of
https://github.com/gsi-upm/senpy
synced 2025-08-23 18:12:20 +00:00
Loads of changes!
* Added conversion plugins (API might change!) * Added conversion to the analysis pipeline * Changed behaviour of --default-plugins (it adds conversion plugins regardless) * Added emotionModel [sic] and emotionConversion models //TODO add conversion tests //TODO add conversion to docs
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
from senpy.plugins import SentimentPlugin
|
||||
from senpy.models import Results
|
||||
|
||||
|
||||
class DummyPlugin(SentimentPlugin):
|
||||
def analyse(self, *args, **kwargs):
|
||||
return Results()
|
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "Dummy",
|
||||
"module": "dummy",
|
||||
"description": "I am dummy",
|
||||
"author": "@balkian",
|
||||
"version": "0.1"
|
||||
}
|
7
tests/plugins/dummy_plugin/dummy.py
Normal file
7
tests/plugins/dummy_plugin/dummy.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from senpy.plugins import SentimentPlugin
|
||||
|
||||
|
||||
class DummyPlugin(SentimentPlugin):
|
||||
def analyse_entry(self, entry, params):
|
||||
entry.text = entry.text[::-1]
|
||||
yield entry
|
15
tests/plugins/dummy_plugin/dummy.senpy
Normal file
15
tests/plugins/dummy_plugin/dummy.senpy
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "Dummy",
|
||||
"module": "dummy",
|
||||
"description": "I am dummy",
|
||||
"author": "@balkian",
|
||||
"version": "0.1",
|
||||
"extra_params": {
|
||||
"example": {
|
||||
"@id": "example_parameter",
|
||||
"aliases": ["example", "ex"],
|
||||
"required": false,
|
||||
"default": 0
|
||||
}
|
||||
}
|
||||
}
|
14
tests/plugins/dummy_plugin/dummy_required.senpy
Normal file
14
tests/plugins/dummy_plugin/dummy_required.senpy
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "DummyRequired",
|
||||
"module": "dummy",
|
||||
"description": "I am dummy",
|
||||
"author": "@balkian",
|
||||
"version": "0.1",
|
||||
"extra_params": {
|
||||
"example": {
|
||||
"@id": "example_parameter",
|
||||
"aliases": ["example", "ex"],
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
from senpy.plugins import SenpyPlugin
|
||||
from senpy.models import Results
|
||||
from time import sleep
|
||||
|
||||
|
||||
@@ -7,6 +6,6 @@ class SleepPlugin(SenpyPlugin):
|
||||
def activate(self, *args, **kwargs):
|
||||
sleep(self.timeout)
|
||||
|
||||
def analyse(self, *args, **kwargs):
|
||||
sleep(float(kwargs.get("timeout", self.timeout)))
|
||||
return Results()
|
||||
def analyse_entry(self, entry, params):
|
||||
sleep(float(params.get("timeout", self.timeout)))
|
||||
yield entry
|
@@ -25,6 +25,8 @@ class BlueprintsTest(TestCase):
|
||||
self.dir = os.path.join(os.path.dirname(__file__), "..")
|
||||
self.senpy.add_folder(self.dir)
|
||||
self.senpy.activate_plugin("Dummy", sync=True)
|
||||
self.senpy.activate_plugin("DummyRequired", sync=True)
|
||||
self.senpy.default_plugin = 'Dummy'
|
||||
|
||||
def assertCode(self, resp, code):
|
||||
self.assertEqual(resp.status_code, code)
|
||||
@@ -34,12 +36,12 @@ class BlueprintsTest(TestCase):
|
||||
Calling with no arguments should ask the user for more arguments
|
||||
"""
|
||||
resp = self.client.get("/api/")
|
||||
self.assertCode(resp, 404)
|
||||
self.assertCode(resp, 400)
|
||||
js = parse_resp(resp)
|
||||
logging.debug(js)
|
||||
assert js["status"] == 404
|
||||
assert js["status"] == 400
|
||||
atleast = {
|
||||
"status": 404,
|
||||
"status": 400,
|
||||
"message": "Missing or invalid parameters",
|
||||
}
|
||||
assert check_dict(js, atleast)
|
||||
@@ -56,6 +58,28 @@ class BlueprintsTest(TestCase):
|
||||
assert "@context" in js
|
||||
assert "entries" in js
|
||||
|
||||
def test_analysis_extra(self):
|
||||
"""
|
||||
Extra params that have a default should
|
||||
"""
|
||||
resp = self.client.get("/api/?i=My aloha mohame&algo=Dummy")
|
||||
self.assertCode(resp, 200)
|
||||
js = parse_resp(resp)
|
||||
logging.debug("Got response: %s", js)
|
||||
assert "@context" in js
|
||||
assert "entries" in js
|
||||
|
||||
def test_analysis_extra_required(self):
|
||||
"""
|
||||
Extra params that have a required argument that does not
|
||||
have a default should raise an error.
|
||||
"""
|
||||
resp = self.client.get("/api/?i=My aloha mohame&algo=DummyRequired")
|
||||
self.assertCode(resp, 400)
|
||||
js = parse_resp(resp)
|
||||
logging.debug("Got response: %s", js)
|
||||
assert isinstance(js, models.Error)
|
||||
|
||||
def test_error(self):
|
||||
"""
|
||||
The dummy plugin returns an empty response,\
|
||||
@@ -102,7 +126,7 @@ class BlueprintsTest(TestCase):
|
||||
js = parse_resp(resp)
|
||||
logging.debug(js)
|
||||
assert "@id" in js
|
||||
assert js["@id"] == "Dummy_0.1"
|
||||
assert js["@id"] == "plugins/Dummy_0.1"
|
||||
|
||||
def test_default(self):
|
||||
""" Show only one plugin"""
|
||||
@@ -111,7 +135,7 @@ class BlueprintsTest(TestCase):
|
||||
js = parse_resp(resp)
|
||||
logging.debug(js)
|
||||
assert "@id" in js
|
||||
assert js["@id"] == "Dummy_0.1"
|
||||
assert js["@id"] == "plugins/Dummy_0.1"
|
||||
|
||||
def test_context(self):
|
||||
resp = self.client.get("/api/contexts/context.jsonld")
|
||||
|
@@ -9,9 +9,10 @@ from senpy.models import Results, Error
|
||||
|
||||
|
||||
class Call(dict):
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj.jsonld()
|
||||
self.status_code = 200
|
||||
self.content = self.json()
|
||||
|
||||
def json(self):
|
||||
return self.obj
|
||||
@@ -29,14 +30,14 @@ class ModelsTest(TestCase):
|
||||
with patch('requests.request', return_value=success) as patched:
|
||||
resp = client.analyse('hello')
|
||||
assert isinstance(resp, Results)
|
||||
patched.assert_called_with(url=endpoint + '/',
|
||||
method='GET',
|
||||
params={'input': 'hello'})
|
||||
patched.assert_called_with(
|
||||
url=endpoint + '/', method='GET', params={'input': 'hello'})
|
||||
error = Call(Error('Nothing'))
|
||||
with patch('requests.request', return_value=error) as patched:
|
||||
resp = client.analyse(input='hello', algorithm='NONEXISTENT')
|
||||
assert isinstance(resp, Error)
|
||||
patched.assert_called_with(url=endpoint + '/',
|
||||
method='GET',
|
||||
params={'input': 'hello',
|
||||
'algorithm': 'NONEXISTENT'})
|
||||
patched.assert_called_with(
|
||||
url=endpoint + '/',
|
||||
method='GET',
|
||||
params={'input': 'hello',
|
||||
'algorithm': 'NONEXISTENT'})
|
||||
|
@@ -16,7 +16,7 @@ from unittest import TestCase
|
||||
|
||||
class ExtensionsTest(TestCase):
|
||||
def setUp(self):
|
||||
self.app = Flask("test_extensions")
|
||||
self.app = Flask('test_extensions')
|
||||
self.dir = os.path.join(os.path.dirname(__file__))
|
||||
self.senpy = Senpy(plugin_folder=self.dir,
|
||||
app=self.app,
|
||||
@@ -45,7 +45,7 @@ class ExtensionsTest(TestCase):
|
||||
'requirements': ['noop'],
|
||||
'version': 0
|
||||
}
|
||||
root = os.path.join(self.dir, 'dummy_plugin')
|
||||
root = os.path.join(self.dir, 'plugins', 'dummy_plugin')
|
||||
name, module = self.senpy._load_plugin_from_info(info, root=root)
|
||||
assert name == 'TestPip'
|
||||
assert module
|
||||
@@ -55,7 +55,7 @@ class ExtensionsTest(TestCase):
|
||||
def test_installing(self):
|
||||
""" Enabling a plugin """
|
||||
self.senpy.activate_all(sync=True)
|
||||
assert len(self.senpy.plugins) == 2
|
||||
assert len(self.senpy.plugins) >= 3
|
||||
assert self.senpy.plugins["Sleep"].is_activated
|
||||
|
||||
def test_disabling(self):
|
||||
@@ -75,11 +75,12 @@ class ExtensionsTest(TestCase):
|
||||
def test_noplugin(self):
|
||||
""" Don't analyse if there isn't any plugin installed """
|
||||
self.senpy.deactivate_all(sync=True)
|
||||
self.assertRaises(Error, partial(self.senpy.analyse,
|
||||
input="tupni"))
|
||||
self.assertRaises(Error, partial(self.senpy.analyse,
|
||||
input="tupni",
|
||||
algorithm='Dummy'))
|
||||
self.assertRaises(Error, partial(self.senpy.analyse, input="tupni"))
|
||||
self.assertRaises(Error,
|
||||
partial(
|
||||
self.senpy.analyse,
|
||||
input="tupni",
|
||||
algorithm='Dummy'))
|
||||
|
||||
def test_analyse(self):
|
||||
""" Using a plugin """
|
||||
@@ -88,17 +89,20 @@ class ExtensionsTest(TestCase):
|
||||
r1 = self.senpy.analyse(
|
||||
algorithm="Dummy", input="tupni", output="tuptuo")
|
||||
r2 = self.senpy.analyse(input="tupni", output="tuptuo")
|
||||
assert r1.analysis[0].id[:5] == "Dummy"
|
||||
assert r2.analysis[0].id[:5] == "Dummy"
|
||||
assert r1.analysis[0] == "plugins/Dummy_0.1"
|
||||
assert r2.analysis[0] == "plugins/Dummy_0.1"
|
||||
assert r1.entries[0].text == 'input'
|
||||
|
||||
def test_analyse_error(self):
|
||||
mm = mock.MagicMock()
|
||||
mm.analyse.side_effect = Error('error on analysis', status=900)
|
||||
mm.analyse_entry.side_effect = Error('error on analysis', status=900)
|
||||
self.senpy.plugins['MOCK'] = mm
|
||||
resp = self.senpy.analyse(input='nothing', algorithm='MOCK')
|
||||
assert resp['message'] == 'error on analysis'
|
||||
assert resp['status'] == 900
|
||||
mm.analyse.side_effect = Exception('generic exception on analysis')
|
||||
mm.analyse_entry.side_effect = Exception(
|
||||
'generic exception on analysis')
|
||||
resp = self.senpy.analyse(input='nothing', algorithm='MOCK')
|
||||
assert resp['message'] == 'generic exception on analysis'
|
||||
assert resp['status'] == 500
|
||||
@@ -110,8 +114,7 @@ class ExtensionsTest(TestCase):
|
||||
assert self.senpy.filter_plugins(name="Dummy", is_activated=True)
|
||||
self.senpy.deactivate_plugin("Dummy", sync=True)
|
||||
assert not len(
|
||||
self.senpy.filter_plugins(
|
||||
name="Dummy", is_activated=True))
|
||||
self.senpy.filter_plugins(name="Dummy", is_activated=True))
|
||||
|
||||
def test_load_default_plugins(self):
|
||||
senpy = Senpy(plugin_folder=self.dir, default_plugins=True)
|
||||
|
@@ -3,20 +3,25 @@ import logging
|
||||
import jsonschema
|
||||
|
||||
import json
|
||||
import rdflib
|
||||
from unittest import TestCase
|
||||
from senpy.models import Entry, Results, Sentiment, EmotionSet, Error
|
||||
from senpy.models import (Emotion,
|
||||
EmotionAnalysis,
|
||||
EmotionSet,
|
||||
Entry,
|
||||
Error,
|
||||
Results,
|
||||
Sentiment)
|
||||
from senpy.plugins import SenpyPlugin
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
class ModelsTest(TestCase):
|
||||
def test_jsonld(self):
|
||||
prueba = {"id": "test",
|
||||
"analysis": [],
|
||||
"entries": []}
|
||||
prueba = {"id": "test", "analysis": [], "entries": []}
|
||||
r = Results(**prueba)
|
||||
print("Response's context: ")
|
||||
pprint(r.context)
|
||||
pprint(r._context)
|
||||
|
||||
assert r.id == "test"
|
||||
|
||||
@@ -30,14 +35,11 @@ class ModelsTest(TestCase):
|
||||
assert "id" not in j
|
||||
|
||||
r6 = Results(**prueba)
|
||||
e = Entry({
|
||||
"@id": "ohno",
|
||||
"nif:isString": "Just testing"
|
||||
})
|
||||
e = Entry({"@id": "ohno", "nif:isString": "Just testing"})
|
||||
r6.entries.append(e)
|
||||
logging.debug("Reponse 6: %s", r6)
|
||||
assert ("marl" in r6.context)
|
||||
assert ("entries" in r6.context)
|
||||
assert ("marl" in r6._context)
|
||||
assert ("entries" in r6._context)
|
||||
j6 = r6.jsonld(with_context=True)
|
||||
logging.debug("jsonld: %s", j6)
|
||||
assert ("@context" in j6)
|
||||
@@ -113,5 +115,35 @@ class ModelsTest(TestCase):
|
||||
s = str(r)
|
||||
assert "_testing" not in s
|
||||
|
||||
def test_frame_response(self):
|
||||
def test_turtle(self):
|
||||
"""Any model should be serializable as a turtle file"""
|
||||
ana = EmotionAnalysis()
|
||||
res = Results()
|
||||
res.analysis.append(ana)
|
||||
entry = Entry(text='Just testing')
|
||||
eSet = EmotionSet()
|
||||
emotion = Emotion()
|
||||
entry.emotions.append(eSet)
|
||||
res.entries.append(entry)
|
||||
eSet.onyx__hasEmotion.append(emotion)
|
||||
eSet.prov__wasGeneratedBy = ana.id
|
||||
triples = ('ana a :Analysis',
|
||||
'entry a :entry',
|
||||
' nif:isString "Just testing"',
|
||||
' onyx:hasEmotionSet eSet',
|
||||
'eSet a onyx:EmotionSet',
|
||||
' prov:wasGeneratedBy ana',
|
||||
' onyx:hasEmotion emotion',
|
||||
'emotion a onyx:Emotion',
|
||||
'res a :results',
|
||||
' me:AnalysisInvoloved ana',
|
||||
' prov:used entry')
|
||||
|
||||
t = res.serialize(format='turtle')
|
||||
print(t)
|
||||
g = rdflib.Graph().parse(data=t, format='turtle')
|
||||
assert len(g) == len(triples)
|
||||
|
||||
def test_convert_emotions(self):
|
||||
"""It should be possible to convert between different emotion models"""
|
||||
pass
|
||||
|
@@ -77,6 +77,7 @@ class PluginsTest(TestCase):
|
||||
})
|
||||
a.activate()
|
||||
|
||||
assert a.shelf_file == self.shelf_file
|
||||
res1 = a.analyse(input=1)
|
||||
assert res1.entries[0].nif__isString == 1
|
||||
res2 = a.analyse(input=1)
|
||||
@@ -103,3 +104,19 @@ class PluginsTest(TestCase):
|
||||
assert b.sh['a'] == 'fromA'
|
||||
b.sh['a'] = 'fromB'
|
||||
assert b.sh['a'] == 'fromB'
|
||||
|
||||
def test_extra_params(self):
|
||||
''' Should be able to set extra parameters'''
|
||||
a = ShelfDummyPlugin(info={
|
||||
'name': 'shelve',
|
||||
'version': 'test',
|
||||
'shelf_file': self.shelf_file,
|
||||
'extra_params': {
|
||||
'example': {
|
||||
'aliases': ['example', 'ex'],
|
||||
'required': True,
|
||||
'default': 'nonsense'
|
||||
}
|
||||
}
|
||||
})
|
||||
assert 'example' in a.extra_params
|
||||
|
Reference in New Issue
Block a user