mirror of
https://github.com/gsi-upm/senpy
synced 2025-08-24 02:22:20 +00:00
WIP: working on a full refactor for v2.0
This is still not functional, because it involves a LOT of changes to the basic structure of the project. Some of the main changes can be seen in the CHANGELOG.md file, if you're interested, but it boils down to simplifying the logic of plugins (no more activation/deactivation shenanigans), more robust typing and use of schemas (pydantic) to avoid inconsistencies and user errors.
This commit is contained in:
@@ -21,7 +21,8 @@ logger = logging.getLogger(__name__)
|
||||
from unittest import TestCase
|
||||
from senpy.api import (boolean, parse_params, get_extra_params, parse_analyses,
|
||||
API_PARAMS, NIF_PARAMS, WEB_PARAMS)
|
||||
from senpy.models import Error, Plugin
|
||||
from senpy.errors import Error
|
||||
from senpy.models import Plugin
|
||||
|
||||
|
||||
class APITest(TestCase):
|
||||
@@ -72,7 +73,7 @@ class APITest(TestCase):
|
||||
in2 = {
|
||||
'apikey': 25
|
||||
}
|
||||
extra_params = {
|
||||
extra_params: dict = {
|
||||
"apikey": {
|
||||
"aliases": [
|
||||
"apikey",
|
||||
@@ -110,7 +111,7 @@ class APITest(TestCase):
|
||||
def test_parse_analyses(self):
|
||||
'''The API should parse user parameters and return them in a format that plugins can use'''
|
||||
plugins = [
|
||||
Plugin({
|
||||
Plugin.parse_obj({
|
||||
'name': 'plugin1',
|
||||
'extra_params': {
|
||||
# Incompatible parameter
|
||||
@@ -133,7 +134,7 @@ class APITest(TestCase):
|
||||
'options': ['value2_1', 'value2_2', 'value3_3']
|
||||
}
|
||||
}
|
||||
}), Plugin({
|
||||
}), Plugin.parse_obj({
|
||||
'name': 'plugin2',
|
||||
'extra_params': {
|
||||
'param0': {
|
||||
@@ -186,7 +187,7 @@ class APITest(TestCase):
|
||||
def test_get_extra_params(self):
|
||||
'''The API should return the list of valid parameters for a set of plugins'''
|
||||
plugins = [
|
||||
Plugin({
|
||||
Plugin.parse_obj({
|
||||
'name': 'plugin1',
|
||||
'extra_params': {
|
||||
# Incompatible parameter
|
||||
@@ -208,7 +209,7 @@ class APITest(TestCase):
|
||||
'options': ['value2_1', 'value2_2', 'value3_3']
|
||||
}
|
||||
}
|
||||
}), Plugin({
|
||||
}), Plugin.parse_obj({
|
||||
'name': 'plugin2',
|
||||
'extra_params': {
|
||||
'param0': {
|
||||
@@ -234,14 +235,14 @@ class APITest(TestCase):
|
||||
|
||||
expected = {
|
||||
# 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'],
|
||||
'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'],
|
||||
'param3': plugins[1]['extra_params']['param3'],
|
||||
'param2': plugins[0].extra_params['param2'],
|
||||
'param3': plugins[1].extra_params['param3'],
|
||||
|
||||
# Intersection of overlapping parameters
|
||||
'param1': {
|
||||
|
@@ -38,11 +38,8 @@ class BlueprintsTest(TestCase):
|
||||
"""Set up only once, and re-use in every individual test"""
|
||||
cls.app = Flask("test_extensions")
|
||||
cls.client = cls.app.test_client()
|
||||
cls.senpy = Senpy(default_plugins=True, strict=False) # Ignore any optional plugins
|
||||
cls.senpy.init_app(cls.app)
|
||||
cls.dir = os.path.join(os.path.dirname(__file__), "..")
|
||||
cls.senpy.add_folder(cls.dir)
|
||||
cls.senpy.activate_all()
|
||||
cls.senpy = Senpy(default_plugins=True, app=cls.app, plugin_folders=[cls.dir, "."], strict=False) # Ignore any optional plugins
|
||||
cls.senpy.default_plugin = 'Dummy'
|
||||
|
||||
def setUp(self):
|
||||
|
@@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
from unittest import TestCase
|
||||
from senpy.cli import main_function
|
||||
from senpy.models import Error
|
||||
from senpy.errors import Error
|
||||
|
||||
|
||||
class CLITest(TestCase):
|
||||
|
@@ -18,7 +18,8 @@ from unittest import TestCase
|
||||
|
||||
from senpy.testing import patch_requests
|
||||
from senpy.client import Client
|
||||
from senpy.models import Results, Plugins, Error
|
||||
from senpy.models import Results, Plugins
|
||||
from senpy.errors import Error
|
||||
from senpy.plugins import AnalysisPlugin
|
||||
|
||||
|
||||
|
@@ -23,7 +23,8 @@ import logging
|
||||
from functools import partial
|
||||
from senpy.extensions import Senpy
|
||||
from senpy import plugins, config, api
|
||||
from senpy.models import Error, Results, Entry, EmotionSet, Emotion, Plugin
|
||||
from senpy.models import Results, Entry, EmotionSet, Emotion, Plugin
|
||||
from senpy.errors import Error
|
||||
from flask import Flask
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -41,8 +42,7 @@ class ExtensionsTest(TestCase):
|
||||
self.senpy = Senpy(plugin_folder=self.examples_dir,
|
||||
app=self.app,
|
||||
default_plugins=False)
|
||||
self.senpy.deactivate_all()
|
||||
self.senpy.activate_plugin("Dummy", sync=True)
|
||||
self.senpy.default_plugin = "Dummy"
|
||||
self.app.config['TESTING'] = True # Tell Flask not to catch Exceptions
|
||||
|
||||
def test_init(self):
|
||||
@@ -62,9 +62,9 @@ class ExtensionsTest(TestCase):
|
||||
'''Should be able to add and delete new plugins. '''
|
||||
new = plugins.Analyser(name='new', description='new', version=0)
|
||||
self.senpy.add_plugin(new)
|
||||
assert new in self.senpy.plugins(is_activated=False)
|
||||
assert new in self.senpy.plugins()
|
||||
self.senpy.delete_plugin(new)
|
||||
assert new not in self.senpy.plugins(is_activated=False)
|
||||
assert new not in self.senpy.plugins()
|
||||
|
||||
def test_adding_folder(self):
|
||||
""" It should be possible for senpy to look for plugins in more folders. """
|
||||
@@ -74,7 +74,7 @@ class ExtensionsTest(TestCase):
|
||||
default_plugins=False)
|
||||
assert not senpy.analysis_plugins()
|
||||
senpy.add_folder(self.examples_dir)
|
||||
assert senpy.plugins(plugin_type=plugins.Analyser, is_activated=False)
|
||||
assert senpy.plugins(plugin_type=plugins.Analyser)
|
||||
self.assertRaises(AttributeError, senpy.add_folder, 'DOES NOT EXIST')
|
||||
|
||||
def test_installing(self):
|
||||
@@ -94,9 +94,8 @@ class ExtensionsTest(TestCase):
|
||||
|
||||
def test_enabling(self):
|
||||
""" Enabling a plugin """
|
||||
self.senpy.activate_all(sync=True)
|
||||
assert len(self.senpy.plugins()) >= 3
|
||||
assert self.senpy.get_plugin("Sleep").is_activated
|
||||
assert self.senpy.get_plugin("Sleep")
|
||||
|
||||
def test_installing_nonexistent(self):
|
||||
""" Fail if the dependencies cannot be met """
|
||||
@@ -110,23 +109,14 @@ class ExtensionsTest(TestCase):
|
||||
with self.assertRaises(Error):
|
||||
plugins.install_deps(info)
|
||||
|
||||
def test_disabling(self):
|
||||
""" Disabling a plugin """
|
||||
self.senpy.deactivate_all(sync=True)
|
||||
assert not self.senpy.get_plugin("dummy").is_activated
|
||||
assert not self.senpy.get_plugin("sleep").is_activated
|
||||
|
||||
def test_default(self):
|
||||
""" Default plugin should be set """
|
||||
assert self.senpy.default_plugin
|
||||
assert self.senpy.default_plugin.name == "dummy"
|
||||
self.senpy.deactivate_all(sync=True)
|
||||
logging.debug("Default: {}".format(self.senpy.default_plugin))
|
||||
assert self.senpy.default_plugin is None
|
||||
|
||||
def test_noplugin(self):
|
||||
""" Don't analyse if there isn't any plugin installed """
|
||||
self.senpy.deactivate_all(sync=True)
|
||||
nosenpy = Senpy(default_plugins=False, plugin_folders=[])
|
||||
self.assertRaises(Error, partial(analyse, self.senpy, input="tupni"))
|
||||
|
||||
def test_analyse(self):
|
||||
@@ -177,7 +167,7 @@ class ExtensionsTest(TestCase):
|
||||
|
||||
def test_analyse_error(self):
|
||||
class ErrorPlugin(plugins.Analyser):
|
||||
author = 'nobody'
|
||||
author: str = 'nobody'
|
||||
version = 0
|
||||
ex = Error()
|
||||
|
||||
@@ -205,17 +195,12 @@ class ExtensionsTest(TestCase):
|
||||
""" Filtering plugins """
|
||||
assert len(self.senpy.plugins(name="Dummy")) > 0
|
||||
assert not len(self.senpy.plugins(name="NotDummy"))
|
||||
assert self.senpy.plugins(name="Dummy", is_activated=True)
|
||||
self.senpy.deactivate_plugin("Dummy", sync=True)
|
||||
assert not len(self.senpy.plugins(name="Dummy",
|
||||
is_activated=True))
|
||||
|
||||
def test_load_default_plugins(self):
|
||||
senpy = Senpy(plugin_folder=self.examples_dir, default_plugins=True)
|
||||
assert len(senpy.plugins(is_activated=False)) > 1
|
||||
assert len(senpy.plugins()) > 1
|
||||
|
||||
def test_convert_emotions(self):
|
||||
self.senpy.activate_all(sync=True)
|
||||
plugin = Plugin({
|
||||
'id': 'imaginary',
|
||||
'onyx:usesEmotionModel': 'emoml:fsre-dimensions'
|
||||
|
@@ -26,14 +26,14 @@ from senpy.models import (Analysis,
|
||||
EmotionAnalysis,
|
||||
EmotionSet,
|
||||
Entry,
|
||||
Error,
|
||||
ErrorResponse,
|
||||
Results,
|
||||
Sentiment,
|
||||
SentimentPlugin,
|
||||
Plugins,
|
||||
from_string,
|
||||
from_dict,
|
||||
subtypes)
|
||||
from_dict)
|
||||
from senpy.errors import Error
|
||||
from senpy import plugins
|
||||
from pprint import pprint
|
||||
|
||||
@@ -117,7 +117,7 @@ class ModelsTest(TestCase):
|
||||
|
||||
def test_plugins(self):
|
||||
self.assertRaises(Error, plugins.Plugin)
|
||||
p = plugins.SentimentPlugin({"name": "dummy",
|
||||
p = plugins.SentimentPlugin.parse_obj({"name": "dummy",
|
||||
"description": "I do nothing",
|
||||
"version": 0,
|
||||
"extra_params": {
|
||||
@@ -152,11 +152,6 @@ class ModelsTest(TestCase):
|
||||
s = str(r)
|
||||
assert "_testing" not in s
|
||||
|
||||
def test_serialize(self):
|
||||
for k, v in subtypes().items():
|
||||
e = v()
|
||||
e.serialize()
|
||||
|
||||
def test_turtle(self):
|
||||
"""Any model should be serializable as a turtle file"""
|
||||
ana = EmotionAnalysis()
|
||||
|
@@ -35,24 +35,23 @@ ROOT = os.path.join(os.path.dirname(__file__), '..')
|
||||
|
||||
class ShelfDummyPlugin(plugins.SentimentPlugin, plugins.ShelfMixin):
|
||||
'''Dummy plugin for tests.'''
|
||||
name = 'Shelf'
|
||||
version = 0
|
||||
author = 'the senpy community'
|
||||
name: str = 'Shelf'
|
||||
version: str = '0'
|
||||
author: str = 'the senpy community'
|
||||
|
||||
def activate(self, *args, **kwargs):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if 'counter' not in self.sh:
|
||||
self.sh['counter'] = 0
|
||||
self.save()
|
||||
|
||||
def deactivate(self, *args, **kwargs):
|
||||
self.save()
|
||||
|
||||
def analyse(self, *args, **kwargs):
|
||||
self.sh['counter'] = self.sh['counter'] + 1
|
||||
e = Entry()
|
||||
e.nif__isString = self.sh['counter']
|
||||
r = Results()
|
||||
r.entries.append(e)
|
||||
self.save()
|
||||
return r
|
||||
|
||||
|
||||
@@ -82,7 +81,6 @@ class PluginsTest(TestCase):
|
||||
info={'name': 'default_shelve_file',
|
||||
'description': 'Dummy plugin for tests',
|
||||
'version': 'test'})
|
||||
a.activate()
|
||||
assert os.path.isfile(a.shelf_file)
|
||||
os.remove(a.shelf_file)
|
||||
|
||||
@@ -114,8 +112,6 @@ class PluginsTest(TestCase):
|
||||
'version': 'test',
|
||||
'shelf_file': newfile
|
||||
})
|
||||
assert a.sh == {}
|
||||
a.activate()
|
||||
assert a.sh == {'counter': 0}
|
||||
assert a.shelf_file == newfile
|
||||
|
||||
@@ -137,12 +133,10 @@ class PluginsTest(TestCase):
|
||||
'shelf_file': self.shelf_file,
|
||||
'version': 'test'
|
||||
})
|
||||
a.activate()
|
||||
|
||||
assert a.shelf_file == self.shelf_file
|
||||
res1 = a.analyse(input=1)
|
||||
assert res1.entries[0].nif__isString == 100
|
||||
a.deactivate()
|
||||
del a
|
||||
|
||||
with open(self.shelf_file, 'rb') as f:
|
||||
@@ -190,7 +184,6 @@ class PluginsTest(TestCase):
|
||||
'version': 'test',
|
||||
'shelf_file': self.shelf_file
|
||||
})
|
||||
a.activate()
|
||||
print('Shelf file: %s' % a.shelf_file)
|
||||
a.sh['a'] = 'fromA'
|
||||
a.save()
|
||||
@@ -201,7 +194,6 @@ class PluginsTest(TestCase):
|
||||
'version': 'test',
|
||||
'shelf_file': self.shelf_file
|
||||
})
|
||||
b.activate()
|
||||
assert b.sh['a'] == 'fromA'
|
||||
b.sh['a'] = 'fromB'
|
||||
assert b.sh['a'] == 'fromB'
|
||||
@@ -228,8 +220,8 @@ class PluginsTest(TestCase):
|
||||
class MyBox(plugins.Box):
|
||||
''' Vague description'''
|
||||
|
||||
author = 'me'
|
||||
version = 0
|
||||
author: str = 'me'
|
||||
version: str = 0
|
||||
|
||||
def to_features(self, entry, **kwargs):
|
||||
return entry.text.split()
|
||||
@@ -243,7 +235,7 @@ class PluginsTest(TestCase):
|
||||
entry.myAnnotation = 'DETECTED'
|
||||
return entry
|
||||
|
||||
test_cases = [
|
||||
test_cases: list[dict] = [
|
||||
{
|
||||
'input': "nothing here",
|
||||
'expected': {'myAnnotation': 'DETECTED'},
|
||||
@@ -260,8 +252,8 @@ class PluginsTest(TestCase):
|
||||
class SentimentBox(plugins.SentimentBox):
|
||||
''' Vague description'''
|
||||
|
||||
author = 'me'
|
||||
version = 0
|
||||
author: str = 'me'
|
||||
version: str = 0
|
||||
|
||||
def predict_one(self, features, **kwargs):
|
||||
text = ' '.join(features)
|
||||
@@ -269,7 +261,7 @@ class PluginsTest(TestCase):
|
||||
return [1, 0, 0]
|
||||
return [0, 0, 1]
|
||||
|
||||
test_cases = [
|
||||
test_cases: list[dict] = [
|
||||
{
|
||||
'input': 'a happy face :)',
|
||||
'polarity': 'marl:Positive'
|
||||
@@ -355,7 +347,7 @@ class PluginsTest(TestCase):
|
||||
|
||||
class DummyPlugin(plugins.SentimentBox):
|
||||
description = 'Plugin to test evaluation'
|
||||
version = 0
|
||||
version: str = 0
|
||||
|
||||
classes = ['marl:Positive', 'marl:Negative']
|
||||
|
||||
@@ -365,7 +357,7 @@ class PluginsTest(TestCase):
|
||||
|
||||
class SmartPlugin(plugins.SentimentBox):
|
||||
description = 'Plugin to test evaluation'
|
||||
version = 0
|
||||
version: str = 0
|
||||
|
||||
classes = ['marl:Positive', 'marl:Negative']
|
||||
|
||||
|
@@ -1,80 +0,0 @@
|
||||
#
|
||||
# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import json
|
||||
import unittest
|
||||
import os
|
||||
from os import path
|
||||
from fnmatch import fnmatch
|
||||
|
||||
from jsonschema import RefResolver, Draft4Validator, ValidationError
|
||||
|
||||
from senpy.models import read_schema
|
||||
|
||||
root_path = path.join(path.dirname(path.realpath(__file__)), '..')
|
||||
schema_folder = path.join(root_path, 'senpy', 'schemas')
|
||||
examples_path = path.join(root_path, 'docs', 'examples')
|
||||
bad_examples_path = path.join(root_path, 'docs', 'bad-examples')
|
||||
|
||||
|
||||
class JSONSchemaTests(unittest.TestCase):
|
||||
def test_definitions(self):
|
||||
read_schema('definitions.json')
|
||||
|
||||
|
||||
def do_create_(jsfile, success):
|
||||
def do_expected(self):
|
||||
with open(jsfile) as f:
|
||||
js = json.load(f)
|
||||
try:
|
||||
assert '@type' in js
|
||||
schema_name = js['@type']
|
||||
with open(os.path.join(schema_folder, schema_name +
|
||||
".json")) as file_object:
|
||||
schema = json.load(file_object)
|
||||
resolver = RefResolver('file://' + schema_folder + '/', schema)
|
||||
validator = Draft4Validator(schema, resolver=resolver)
|
||||
validator.validate(js)
|
||||
except (AssertionError, ValidationError, KeyError) as ex:
|
||||
if success:
|
||||
raise
|
||||
return
|
||||
assert success
|
||||
return do_expected
|
||||
|
||||
|
||||
def add_examples(dirname, success):
|
||||
for dirpath, dirnames, filenames in os.walk(dirname):
|
||||
for i in filenames:
|
||||
if fnmatch(i, '*.json'):
|
||||
filename = path.join(dirpath, i)
|
||||
test_method = do_create_(filename, success)
|
||||
test_method.__name__ = 'test_file_%s_success_%s' % (filename,
|
||||
success)
|
||||
test_method.__doc__ = '%s should %svalidate' % (filename, ''
|
||||
if success else
|
||||
'not')
|
||||
setattr(JSONSchemaTests, test_method.__name__, test_method)
|
||||
del test_method
|
||||
|
||||
|
||||
add_examples(examples_path, True)
|
||||
add_examples(bad_examples_path, False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@@ -42,12 +42,10 @@ class SemanticsTest(TestCase):
|
||||
"""Set up only once, and re-use in every individual test"""
|
||||
cls.app = Flask("test_extensions")
|
||||
cls.client = cls.app.test_client()
|
||||
cls.senpy = Senpy(default_plugins=True)
|
||||
cls.senpy = Senpy(plugin_folder=None, default_plugins=True)
|
||||
cls.senpy.init_app(cls.app)
|
||||
cls.dir = os.path.join(os.path.dirname(__file__), "..")
|
||||
cls.senpy.add_folder(cls.dir)
|
||||
cls.senpy.activate_all()
|
||||
cls.senpy.default_plugin = 'Dummy'
|
||||
#cls.dir = os.path.join(os.path.dirname(__file__), "..")
|
||||
#cls.senpy.add_folder(cls.dir)
|
||||
|
||||
def setUp(self):
|
||||
self.app.config['TESTING'] = True # Tell Flask not to catch Exceptions
|
||||
@@ -57,10 +55,10 @@ class SemanticsTest(TestCase):
|
||||
|
||||
def test_sentiment(self):
|
||||
"""
|
||||
A sentiment analysis call in JSON-LD
|
||||
a sentiment analysis call in json-ld
|
||||
"""
|
||||
# We use expanded JSON-LD and ignore the context, because in general
|
||||
# the context is a URIS to the service and that URI is not
|
||||
# we use expanded json-ld and ignore the context, because in general
|
||||
# the context is a uris to the service and that uri is not
|
||||
# available outside of self.client
|
||||
params = {
|
||||
'input': 'hello',
|
||||
@@ -69,28 +67,28 @@ class SemanticsTest(TestCase):
|
||||
'expanded': True,
|
||||
'prefix': 'http://default.example/#'
|
||||
}
|
||||
resp = self.client.get("/api/basic?{}".format(urlencode(params)))
|
||||
resp = self.client.get("/api/sentiment-basic?{}".format(urlencode(params)))
|
||||
self.assertCode(resp, 200)
|
||||
g = parse_resp(resp, fmt='json-ld')
|
||||
print('Got this graph: ', g.serialize(format='ttl'))
|
||||
assert g
|
||||
qres = g.query("""
|
||||
PREFIX prov: <http://www.w3.org/ns/prov#>
|
||||
PREFIX marl: <http://www.gsi.upm.es/ontologies/marl/ns#>
|
||||
PREFIX nif: <http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#>
|
||||
PREFIX onyx: <http://www.gsi.upm.es/ontologies/onyx/ns#>
|
||||
PREFIX senpy: <http://www.gsi.upm.es/onto/senpy/ns#>
|
||||
prefix prov: <http://www.w3.org/ns/prov#>
|
||||
prefix marl: <http://www.gsi.upm.es/ontologies/marl/ns#>
|
||||
prefix nif: <http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#>
|
||||
prefix onyx: <http://www.gsi.upm.es/ontologies/onyx/ns#>
|
||||
prefix senpy: <http://www.gsi.upm.es/ontologies/senpy/ns#>
|
||||
|
||||
SELECT DISTINCT ?entry ?text ?sentiment
|
||||
WHERE {
|
||||
?entry a senpy:Entry .
|
||||
?entry marl:hasOpinion ?o .
|
||||
?entry nif:isString ?text .
|
||||
?o marl:hasPolarity ?sentiment .
|
||||
}""")
|
||||
assert len(qres) == 1
|
||||
SELECT distinct ?entry ?text ?sentiment
|
||||
WHERE {
|
||||
?entry a senpy:Entry .
|
||||
?entry marl:hasOpinion ?o .
|
||||
?entry nif:isString ?text .
|
||||
?o marl:hasPolarity ?sentiment .
|
||||
}""")
|
||||
assert len(qres) == 1, "There should only be one result"
|
||||
entry, text, sentiment = list(qres)[0]
|
||||
assert entry
|
||||
assert str(text) == 'hello'
|
||||
assert str(text) == 'hello', "The returned text does not match the input text."
|
||||
assert str(sentiment) in ['marl:Positive', 'marl:Neutral', 'marl:Negative']
|
||||
|
||||
def test_sentiment_turtle(self):
|
||||
@@ -104,25 +102,75 @@ class SemanticsTest(TestCase):
|
||||
'expanded': True,
|
||||
'prefix': 'http://default.example/#'
|
||||
}
|
||||
resp = self.client.get("/api/basic?{}".format(urlencode(params)))
|
||||
resp = self.client.get("/api/sentiment-basic?{}".format(urlencode(params)))
|
||||
self.assertCode(resp, 200)
|
||||
g = parse_resp(resp, 'ttl')
|
||||
print('Got this graph: ', g.serialize(format='ttl'))
|
||||
qres = g.query("""
|
||||
PREFIX prov: <http://www.w3.org/ns/prov#>
|
||||
PREFIX marl: <http://www.gsi.upm.es/ontologies/marl/ns#>
|
||||
PREFIX nif: <http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#>
|
||||
PREFIX onyx: <http://www.gsi.upm.es/ontologies/onyx/ns#>
|
||||
PREFIX senpy: <http://www.gsi.upm.es/onto/senpy/ns#>
|
||||
PREFIX senpy: <http://www.gsi.upm.es/ontologies/senpy/ns#>
|
||||
|
||||
SELECT DISTINCT ?entry ?text ?sentiment
|
||||
WHERE {
|
||||
?entry a senpy:Entry .
|
||||
?entry marl:hasOpinion ?o .
|
||||
?entry nif:isString ?text .
|
||||
?o marl:hasPolarity ?sentiment .
|
||||
?entry a senpy:Entry ;
|
||||
nif:isString ?text ;
|
||||
marl:hasOpinion [
|
||||
marl:hasPolarity ?sentiment
|
||||
] .
|
||||
}""")
|
||||
assert len(qres) == 1, "There should only be one row in the result"
|
||||
entry, text, sentiment = list(qres)[0]
|
||||
assert str(text) == 'hello', "Returned text does not match input text"
|
||||
assert str(sentiment) in ['marl:Positive', 'marl:Neutral', 'marl:Negative']
|
||||
|
||||
def test_moral(self):
|
||||
"""
|
||||
An example of a moral analysis, adapted from the examples for the AMOR project:
|
||||
http://www.gsi.upm.es/ontologies/amor/examples
|
||||
"""
|
||||
# we use expanded json-ld and ignore the context, because in general
|
||||
# the context is a uris to the service and that uri is not
|
||||
# available outside of self.client
|
||||
params = {
|
||||
'input': 'hello',
|
||||
'in-headers': True,
|
||||
'outformat': 'json-ld',
|
||||
'expanded': True,
|
||||
'prefix': 'http://default.example/#'
|
||||
}
|
||||
resp = self.client.get("/api/sentiment-basic?{}".format(urlencode(params)))
|
||||
self.assertCode(resp, 200)
|
||||
g = parse_resp(resp, fmt='json-ld')
|
||||
print('Got this graph: ', g.serialize(format='ttl'))
|
||||
assert g
|
||||
qres = g.query("""
|
||||
prefix : <http://www.gsi.upm.es/ontologies/amor/examples#>
|
||||
prefix amor: <http://www.gsi.upm.es/ontologies/amor/ns#>
|
||||
prefix amor-bhv: <http://www.gsi.upm.es/ontologies/amor/models/bhv/ns#>
|
||||
prefix amor-mft: <http://www.gsi.upm.es/ontologies/amor/models/mft/ns#>
|
||||
prefix bhv: <http://www.gsi.upm.es/ontologies/bhv#>
|
||||
prefix mft: <http://www.gsi.upm.es/ontologies/mft/ns#>
|
||||
prefix mls: <http://www.w3.org/ns/mls#>
|
||||
prefix owl: <http://www.w3.org/2002/07/owl#>
|
||||
prefix prov: <http://www.w3.org/ns/prov#>
|
||||
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
prefix schema: <http://schema.org/>
|
||||
|
||||
SELECT ?analysis ?agent ?model ?annotation ?origin ?category
|
||||
WHERE {
|
||||
?analysis a amor:MoralValueAnalysis ;
|
||||
prov:wasAssociatedWith ?agent ;
|
||||
amor:usedMoralValueModel ?model ;
|
||||
amor:analysed ?origin ;
|
||||
prov:generated ?annotation .
|
||||
?annotation a amor:MoralValueAnnotation ;
|
||||
amor:hasMoralValueCategory ?category .
|
||||
}""")
|
||||
assert len(qres) == 1
|
||||
entry, text, sentiment = list(qres)[0]
|
||||
assert entry
|
||||
assert str(text) == 'hello'
|
||||
assert str(sentiment) in ['marl:Positive', 'marl:Neutral', 'marl:Negative']
|
||||
assert str(sentiment) in ['marl:positive', 'marl:neutral', 'marl:negative']
|
||||
|
Reference in New Issue
Block a user