1
0
mirror of https://github.com/gsi-upm/senpy synced 2025-09-18 04:22:21 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
J. Fernando Sánchez
d7acf3d67a Fix schema issues and parameter validation 2018-05-16 11:15:13 +02:00
24 changed files with 181 additions and 386 deletions

View File

@@ -18,8 +18,6 @@ before_script:
stage: test stage: test
script: script:
- make -e test-$PYTHON_VERSION - make -e test-$PYTHON_VERSION
except:
- tags # Avoid unnecessary double testing
test-3.5: test-3.5:
<<: *test_definition <<: *test_definition

View File

@@ -1,14 +1,5 @@
ifndef IMAGENAME IMAGENAME?=$(NAME)
ifdef CI_REGISTRY_IMAGE
IMAGENAME=$(CI_REGISTRY_IMAGE)
else
IMAGENAME=$(NAME)
endif
endif
IMAGEWTAG?=$(IMAGENAME):$(VERSION) IMAGEWTAG?=$(IMAGENAME):$(VERSION)
DOCKER_FLAGS?=$(-ti)
DOCKER_CMD?=
docker-login: ## Log in to the registry. It will only be used in the server, or when running a CI task locally (if CI_BUILD_TOKEN is set). docker-login: ## Log in to the registry. It will only be used in the server, or when running a CI task locally (if CI_BUILD_TOKEN is set).
ifeq ($(CI_BUILD_TOKEN),) ifeq ($(CI_BUILD_TOKEN),)
@@ -28,19 +19,6 @@ else
@docker logout @docker logout
endif endif
docker-run: ## Build a generic docker image
docker run $(DOCKER_FLAGS) $(IMAGEWTAG) $(DOCKER_CMD)
docker-build: ## Build a generic docker image
docker build . -t $(IMAGEWTAG)
docker-push: docker-login ## Push a generic docker image
docker push $(IMAGEWTAG)
docker-latest-push: docker-login ## Push the latest image
docker tag $(IMAGEWTAG) $(IMAGENAME)
docker push $(IMAGENAME)
login:: docker-login login:: docker-login
clean:: docker-clean clean:: docker-clean

View File

@@ -14,7 +14,7 @@ push-github: ## Push the code to github. You need to set up GITHUB_DEPLOY_KEY
ifeq ($(GITHUB_DEPLOY_KEY),) ifeq ($(GITHUB_DEPLOY_KEY),)
else else
$(eval KEY_FILE := "$(shell mktemp)") $(eval KEY_FILE := "$(shell mktemp)")
@printf '%b' '$(GITHUB_DEPLOY_KEY)' > $(KEY_FILE) @echo "$(GITHUB_DEPLOY_KEY)" > $(KEY_FILE)
@git remote rm github-deploy || true @git remote rm github-deploy || true
git remote add github-deploy $(GITHUB_REPO) git remote add github-deploy $(GITHUB_REPO)
-@GIT_SSH_COMMAND="ssh -i $(KEY_FILE)" git fetch github-deploy $(CI_COMMIT_REF_NAME) -@GIT_SSH_COMMAND="ssh -i $(KEY_FILE)" git fetch github-deploy $(CI_COMMIT_REF_NAME)

View File

@@ -13,7 +13,7 @@
KUBE_CA_TEMP=false KUBE_CA_TEMP=false
ifndef KUBE_CA_PEM_FILE ifndef KUBE_CA_PEM_FILE
KUBE_CA_PEM_FILE:=$$PWD/.ca.crt KUBE_CA_PEM_FILE:=$$PWD/.ca.crt
CREATED:=$(shell printf '%b\n' '$(KUBE_CA_BUNDLE)' > $(KUBE_CA_PEM_FILE)) CREATED:=$(shell echo -e "$(KUBE_CA_BUNDLE)" > $(KUBE_CA_PEM_FILE))
endif endif
KUBE_TOKEN?="" KUBE_TOKEN?=""
KUBE_NAMESPACE?=$(NAME) KUBE_NAMESPACE?=$(NAME)

View File

@@ -26,7 +26,6 @@ Dockerfile-%: Dockerfile.template ## Generate a specific dockerfile (e.g. Docke
quick_build: $(addprefix build-, $(PYMAIN)) quick_build: $(addprefix build-, $(PYMAIN))
build: $(addprefix build-, $(PYVERSIONS)) ## Build all images / python versions build: $(addprefix build-, $(PYVERSIONS)) ## Build all images / python versions
docker tag $(IMAGEWTAG)-python$(PYMAIN) $(IMAGEWTAG)
build-%: version Dockerfile-% ## Build a specific version (e.g. build-2.7) build-%: version Dockerfile-% ## Build a specific version (e.g. build-2.7)
docker build -t '$(IMAGEWTAG)-python$*' -f Dockerfile-$* .; docker build -t '$(IMAGEWTAG)-python$*' -f Dockerfile-$* .;
@@ -76,9 +75,8 @@ pip_upload: pip_test ## Upload package to pip
push-latest: $(addprefix push-latest-,$(PYVERSIONS)) ## Push the "latest" tag to dockerhub push-latest: $(addprefix push-latest-,$(PYVERSIONS)) ## Push the "latest" tag to dockerhub
docker tag '$(IMAGEWTAG)-python$(PYMAIN)' '$(IMAGEWTAG)' docker tag '$(IMAGEWTAG)-python$(PYMAIN)' '$(IMAGEWTAG)'
docker tag '$(IMAGEWTAG)-python$(PYMAIN)' '$(IMAGENAME):latest' docker tag '$(IMAGEWTAG)-python$(PYMAIN)' '$(IMAGENAME)'
docker push '$(IMAGENAME):latest' docker push '$(IMAGENAME):latest'
docker push '$(IMAGEWTAG)'
push-latest-%: build-% ## Push the latest image for a specific python version push-latest-%: build-% ## Push the latest image for a specific python version
docker tag $(IMAGENAME):$(VERSION)-python$* $(IMAGENAME):python$* docker tag $(IMAGENAME):$(VERSION)-python$* $(IMAGENAME):python$*

View File

@@ -43,6 +43,7 @@ class Dictionary(plugins.SentimentPlugin):
class EmojiOnly(Dictionary): class EmojiOnly(Dictionary):
'''Sentiment annotation with a basic lexicon of emojis''' '''Sentiment annotation with a basic lexicon of emojis'''
description = 'A plugin'
dictionaries = [basic.emojis] dictionaries = [basic.emojis]
test_cases = [{ test_cases = [{

View File

@@ -130,7 +130,7 @@ def main():
return return
sp.activate_all() sp.activate_all()
if args.only_test: if args.only_test:
easy_test(sp.plugins(), debug=args.debug) easy_test(sp.plugins())
return return
print('Senpy version {}'.format(senpy.__version__)) print('Senpy version {}'.format(senpy.__version__))
print('Server running on port %s:%d. Ctrl+C to quit' % (args.host, print('Server running on port %s:%d. Ctrl+C to quit' % (args.host,

View File

@@ -47,12 +47,7 @@ def get_params(req):
@demo_blueprint.route('/') @demo_blueprint.route('/')
def index(): def index():
ev = str(get_params(request).get('evaluation', False)) return render_template("index.html", version=__version__)
evaluation_enabled = ev.lower() not in ['false', 'no', 'none']
return render_template("index.html",
evaluation=evaluation_enabled,
version=__version__)
@api_blueprint.route('/contexts/<entity>.jsonld') @api_blueprint.route('/contexts/<entity>.jsonld')

View File

@@ -203,27 +203,24 @@ class BaseModel(with_metaclass(BaseMeta, CustomDict)):
context_uri=None, context_uri=None,
prefix=None, prefix=None,
expanded=False): expanded=False):
ser = self.serializable()
result = self.serializable() result = jsonld.compact(
if context_uri or with_context: ser,
result['@context'] = context_uri or self._context self._context,
options={
# result = jsonld.compact(result, 'base': prefix,
# self._context, 'expandContext': self._context,
# options={ 'senpy': prefix
# 'base': prefix, })
# 'expandContext': self._context, if context_uri:
# 'senpy': prefix result['@context'] = context_uri
# })
if expanded: if expanded:
result = jsonld.expand( result = jsonld.expand(
result, options={'base': prefix, result, options={'base': prefix,
'expandContext': self._context}) 'expandContext': self._context})
if not with_context: if not with_context:
try: del result['@context']
del result['@context']
except KeyError:
pass
return result return result
def validate(self, obj=None): def validate(self, obj=None):
@@ -326,10 +323,7 @@ def _add_class_from_schema(*args, **kwargs):
for i in [ for i in [
'aggregatedEvaluation',
'analysis', 'analysis',
'dataset',
'datasets',
'emotion', 'emotion',
'emotionConversion', 'emotionConversion',
'emotionConversionPlugin', 'emotionConversionPlugin',
@@ -337,17 +331,19 @@ for i in [
'emotionModel', 'emotionModel',
'emotionPlugin', 'emotionPlugin',
'emotionSet', 'emotionSet',
'evaluation',
'entity', 'entity',
'help', 'help',
'metric',
'plugin', 'plugin',
'plugins', 'plugins',
'response', 'response',
'results', 'results',
'sentimentPlugin', 'sentimentPlugin',
'suggestion', 'suggestion',
'topic', 'aggregatedEvaluation',
'evaluation',
'metric',
'dataset',
'datasets',
]: ]:
_add_class_from_schema(i) _add_class_from_schema(i)

View File

@@ -18,7 +18,6 @@ import subprocess
import importlib import importlib
import yaml import yaml
import threading import threading
import nltk
from .. import models, utils from .. import models, utils
from .. import api from .. import api
@@ -95,27 +94,7 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)):
self.id = 'plugins/{}_{}'.format(self['name'], self['version']) self.id = 'plugins/{}_{}'.format(self['name'], self['version'])
self.is_activated = False self.is_activated = False
self._lock = threading.Lock() self._lock = threading.Lock()
self._directory = os.path.abspath(os.path.dirname(inspect.getfile(self.__class__))) self.data_folder = data_folder or os.getcwd()
data_folder = data_folder or os.getcwd()
subdir = os.path.join(data_folder, self.name)
self._data_paths = [
data_folder,
subdir,
self._directory,
os.path.join(self._directory, 'data'),
]
if os.path.exists(subdir):
data_folder = subdir
self.data_folder = data_folder
self._log = logging.getLogger('{}.{}'.format(__name__, self.name))
@property
def log(self):
return self._log
def validate(self): def validate(self):
missing = [] missing = []
@@ -144,9 +123,9 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)):
for case in test_cases: for case in test_cases:
try: try:
self.test_case(case) self.test_case(case)
self.log.debug('Test case passed:\n{}'.format(pprint.pformat(case))) logger.debug('Test case passed:\n{}'.format(pprint.pformat(case)))
except Exception as ex: except Exception as ex:
self.log.warn('Test case failed:\n{}'.format(pprint.pformat(case))) logger.warn('Test case failed:\n{}'.format(pprint.pformat(case)))
raise raise
def test_case(self, case): def test_case(self, case):
@@ -169,22 +148,10 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)):
raise raise
assert not should_fail assert not should_fail
def find_file(self, fname): def open(self, fpath, *args, **kwargs):
for p in self._data_paths: if not os.path.isabs(fpath):
alternative = os.path.join(p, fname) fpath = os.path.join(self.data_folder, fpath)
if os.path.exists(alternative): return open(fpath, *args, **kwargs)
return alternative
raise IOError('File does not exist: {}'.format(fname))
def open(self, fpath, mode='r'):
if 'w' in mode:
# When writing, only use absolute paths or data_folder
if not os.path.isabs(fpath):
fpath = os.path.join(self.data_folder, fpath)
else:
fpath = self.find_file(fpath)
return open(fpath, mode=mode)
def serve(self, debug=True, **kwargs): def serve(self, debug=True, **kwargs):
utils.easy(plugin_list=[self, ], plugin_folder=None, debug=debug, **kwargs) utils.easy(plugin_list=[self, ], plugin_folder=None, debug=debug, **kwargs)
@@ -219,7 +186,7 @@ class Analysis(Plugin):
def analyse_entries(self, entries, parameters): def analyse_entries(self, entries, parameters):
for entry in entries: for entry in entries:
self.log.debug('Analysing entry with plugin {}: {}'.format(self, entry)) logger.debug('Analysing entry with plugin {}: {}'.format(self, entry))
results = self.analyse_entry(entry, parameters) results = self.analyse_entry(entry, parameters)
if inspect.isgenerator(results): if inspect.isgenerator(results):
for result in results: for result in results:
@@ -408,7 +375,7 @@ class ShelfMixin(object):
with self.open(self.shelf_file, 'rb') as p: with self.open(self.shelf_file, 'rb') as p:
self._sh = pickle.load(p) self._sh = pickle.load(p)
except (IndexError, EOFError, pickle.UnpicklingError): except (IndexError, EOFError, pickle.UnpicklingError):
self.log.warning('Corrupted shelf file: {}'.format(self.shelf_file)) logger.warning('{} has a corrupted shelf file!'.format(self.id))
if not self.get('force_shelf', False): if not self.get('force_shelf', False):
raise raise
return self._sh return self._sh
@@ -435,31 +402,32 @@ class ShelfMixin(object):
self._shelf_file = value self._shelf_file = value
def save(self): def save(self):
self.log.debug('Saving pickle') logger.debug('saving pickle')
if hasattr(self, '_sh') and self._sh is not None: if hasattr(self, '_sh') and self._sh is not None:
with self.open(self.shelf_file, 'wb') as f: with self.open(self.shelf_file, 'wb') as f:
pickle.dump(self._sh, f) pickle.dump(self._sh, f)
def pfilter(plugins, plugin_type=Analysis, **kwargs): def pfilter(plugins, **kwargs):
""" Filter plugins by different criteria """ """ Filter plugins by different criteria """
if isinstance(plugins, models.Plugins): if isinstance(plugins, models.Plugins):
plugins = plugins.plugins plugins = plugins.plugins
elif isinstance(plugins, dict): elif isinstance(plugins, dict):
plugins = plugins.values() plugins = plugins.values()
ptype = kwargs.pop('plugin_type', Plugin)
logger.debug('#' * 100) logger.debug('#' * 100)
logger.debug('plugin_type {}'.format(plugin_type)) logger.debug('ptype {}'.format(ptype))
if plugin_type: if ptype:
if isinstance(plugin_type, PluginMeta): if isinstance(ptype, PluginMeta):
plugin_type = plugin_type.__name__ ptype = ptype.__name__
try: try:
plugin_type = plugin_type[0].upper() + plugin_type[1:] ptype = ptype[0].upper() + ptype[1:]
pclass = globals()[plugin_type] pclass = globals()[ptype]
logger.debug('Class: {}'.format(pclass)) logger.debug('Class: {}'.format(pclass))
candidates = filter(lambda x: isinstance(x, pclass), candidates = filter(lambda x: isinstance(x, pclass),
plugins) plugins)
except KeyError: except KeyError:
raise models.Error('{} is not a valid type'.format(plugin_type)) raise models.Error('{} is not a valid type'.format(ptype))
else: else:
candidates = plugins candidates = plugins
@@ -494,7 +462,6 @@ def _log_subprocess_output(process):
def install_deps(*plugins): def install_deps(*plugins):
installed = False installed = False
nltk_resources = set()
for info in plugins: for info in plugins:
requirements = info.get('requirements', []) requirements = info.get('requirements', [])
if requirements: if requirements:
@@ -509,10 +476,7 @@ def install_deps(*plugins):
exitcode = process.wait() exitcode = process.wait()
installed = True installed = True
if exitcode != 0: if exitcode != 0:
raise models.Error("Dependencies not properly installed: {}".format(pip_args)) raise models.Error("Dependencies not properly installed")
nltk_resources |= set(info.get('nltk_resources', []))
installed |= nltk.download(list(nltk_resources))
return installed return installed
@@ -609,14 +573,12 @@ def _instances_in_module(module):
def _from_module_name(module, root, info=None, install=True, **kwargs): def _from_module_name(module, root, info=None, install=True, **kwargs):
try: try:
module = load_module(module, root) module = load_module(module, root)
except (ImportError, LookupError): except ImportError:
if not install or not info: if not install or not info:
raise raise
install_deps(info) install_deps(info)
module = load_module(module, root) module = load_module(module, root)
for plugin in _from_loaded_module(module=module, root=root, info=info, **kwargs): for plugin in _from_loaded_module(module=module, root=root, info=info, **kwargs):
if install:
install_deps(plugin)
yield plugin yield plugin

View File

@@ -5,27 +5,13 @@ from nltk.tokenize.simple import LineTokenizer
import nltk import nltk
class Split(AnalysisPlugin): class SplitPlugin(AnalysisPlugin):
'''description: A sample plugin that chunks input text''' '''description: A sample plugin that chunks input text'''
author = ["@militarpancho", '@balkian']
version = '0.2'
url = "https://github.com/gsi-upm/senpy"
extra_params = {
'delimiter': {
'aliases': ['type', 't'],
'required': False,
'default': 'sentence',
'options': ['sentence', 'paragraph']
},
}
def activate(self): def activate(self):
nltk.download('punkt') nltk.download('punkt')
def analyse_entry(self, entry, params): def analyse_entry(self, entry, params):
yield entry
chunker_type = params["delimiter"] chunker_type = params["delimiter"]
original_text = entry['nif:isString'] original_text = entry['nif:isString']
if chunker_type == "sentence": if chunker_type == "sentence":

View File

@@ -0,0 +1,19 @@
---
name: split
module: senpy.plugins.misc.split
description: A sample plugin that chunks input text
author: "@militarpancho"
version: '0.2'
url: "https://github.com/gsi-upm/senpy"
requirements:
- nltk
extra_params:
delimiter:
aliases:
- type
- t
required: false
default: sentence
options:
- sentence
- paragraph

View File

@@ -5,25 +5,8 @@ from senpy.plugins import SentimentPlugin
from senpy.models import Sentiment from senpy.models import Sentiment
class Sentiment140(SentimentPlugin): class Sentiment140Plugin(SentimentPlugin):
'''Connects to the sentiment140 free API: http://sentiment140.com''' '''Connects to the sentiment140 free API: http://sentiment140.com'''
author = "@balkian"
version = '0.2'
url = "https://github.com/gsi-upm/senpy-plugins-community"
extra_params = {
'language': {
"@id": 'lang_sentiment140',
'aliases': ['language', 'l'],
'required': False,
'default': 'auto',
'options': ['es', 'en', 'auto']
}
}
maxPolarityValue = 1
minPolarityValue = 0
def analyse_entry(self, entry, params): def analyse_entry(self, entry, params):
lang = params["language"] lang = params["language"]
res = requests.post("http://www.sentiment140.com/api/bulkClassifyJson", res = requests.post("http://www.sentiment140.com/api/bulkClassifyJson",
@@ -48,6 +31,7 @@ class Sentiment140(SentimentPlugin):
marl__hasPolarity=polarity, marl__hasPolarity=polarity,
marl__polarityValue=polarity_value) marl__polarityValue=polarity_value)
sentiment.prov__wasGeneratedBy = self.id sentiment.prov__wasGeneratedBy = self.id
entry.sentiments = []
entry.sentiments.append(sentiment) entry.sentiments.append(sentiment)
entry.language = lang entry.language = lang
yield entry yield entry
@@ -57,10 +41,10 @@ class Sentiment140(SentimentPlugin):
To avoid calling the sentiment140 API, we will mock the results To avoid calling the sentiment140 API, we will mock the results
from requests. from requests.
''' '''
from senpy.testing import patch_requests from senpy.test import patch_requests
expected = {"data": [{"polarity": 4}]} expected = {"data": [{"polarity": 4}]}
with patch_requests(expected) as (request, response): with patch_requests(expected) as (request, response):
super(Sentiment140, self).test(*args, **kwargs) super(Sentiment140Plugin, self).test(*args, **kwargs)
assert request.called assert request.called
assert response.json.called assert response.json.called

View File

@@ -0,0 +1,22 @@
---
name: sentiment140
module: sentiment140
description: "Connects to the sentiment140 free API: http://sentiment140.com"
author: "@balkian"
version: '0.2'
url: "https://github.com/gsi-upm/senpy-plugins-community"
extra_params:
language:
"@id": lang_sentiment140
aliases:
- language
- l
required: false
options:
- es
- en
- auto
default: auto
requirements: {}
maxPolarityValue: 1
minPolarityValue: 0

View File

@@ -10,10 +10,8 @@
"wna": "http://www.gsi.dit.upm.es/ontologies/wnaffect/ns#", "wna": "http://www.gsi.dit.upm.es/ontologies/wnaffect/ns#",
"emoml": "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/emotionml/ns#", "emoml": "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/emotionml/ns#",
"xsd": "http://www.w3.org/2001/XMLSchema#", "xsd": "http://www.w3.org/2001/XMLSchema#",
"fam": "http://vocab.fusepool.info/fam#",
"topics": { "topics": {
"@id": "nif:topic", "@id": "dc:subject"
"@container": "@set"
}, },
"entities": { "entities": {
"@id": "me:hasEntities" "@id": "me:hasEntities"

View File

@@ -167,36 +167,3 @@ textarea{
color: inherit; color: inherit;
text-decoration: inherit; text-decoration: inherit;
} }
.collapsed .collapseicon {
display: none !important;
}
.collapsed .expandicon {
display: inline-block !important;
}
.expandicon {
display: none !important;
}
.collapseicon {
display: inline-block !important;
}
.loader {
border: 6px solid #f3f3f3; /* Light grey */
border-top: 6px solid blue;
border-bottom: 6px solid blue;
border-radius: 50%;
width: 3em;
height: 3em;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

View File

@@ -4,7 +4,6 @@ var plugins_params = default_params = {};
var plugins = []; var plugins = [];
var defaultPlugin = {}; var defaultPlugin = {};
var gplugins = {}; var gplugins = {};
var pipeline = [];
function replaceURLWithHTMLLinks(text) { function replaceURLWithHTMLLinks(text) {
console.log('Text: ' + text); console.log('Text: ' + text);
@@ -31,10 +30,7 @@ function hashchanged(){
function get_plugins(response){ function get_plugins(response){
for(ix in response.plugins){ plugins = response.plugins;
plug = response.plugins[ix];
plugins[plug.name] = plug;
}
} }
function get_datasets(response){ function get_datasets(response){
@@ -87,32 +83,10 @@ function draw_plugins_selection(){
html += "</optgroup>" html += "</optgroup>"
// Two elements with plugin class // Two elements with plugin class
// One from the evaluate tab and another one from the analyse tab // One from the evaluate tab and another one from the analyse tab
plugin_lists = document.getElementsByClassName('plugin') document.getElementsByClassName('plugin')[0].innerHTML = html;
for (element in plugin_lists){ document.getElementsByClassName('plugin')[1].innerHTML = html;
plugin_lists[element].innerHTML = html;
}
draw_plugin_pipeline();
} }
function draw_plugin_pipeline(){
var pipeHTML = "";
console.log("Drawing pipeline: ", pipeline);
for (ix in pipeline){
plug = pipeline[ix];
pipeHTML += '<span onclick="remove_plugin_pipeline(\'' + plug + '\')" class="btn btn-primary"><span ><i class="fa fa-minus"></i></span> ' + plug + '</span> <i class="fa fa-arrow-right"></i> ';
}
console.log(pipeHTML);
$("#pipeline").html(pipeHTML);
}
function remove_plugin_pipeline(name){
console.log("Removing plugin: ", name);
var index = pipeline.indexOf(name);
pipeline.splice(index, 1);
draw_plugin_pipeline();
}
function draw_plugins_list(){ function draw_plugins_list(){
var availablePlugins = document.getElementById('availablePlugins'); var availablePlugins = document.getElementById('availablePlugins');
@@ -131,13 +105,6 @@ function draw_plugins_list(){
} }
} }
function add_plugin_pipeline(){
var selected = get_selected_plugin();
pipeline.push(selected);
console.log("Adding ", selected);
draw_plugin_pipeline();
}
function draw_datasets(){ function draw_datasets(){
html = ""; html = "";
repeated_html = "<input class=\"checks-datasets\" type=\"checkbox\" value=\""; repeated_html = "<input class=\"checks-datasets\" type=\"checkbox\" value=\"";
@@ -151,20 +118,16 @@ function draw_datasets(){
$(document).ready(function() { $(document).ready(function() {
var response = JSON.parse($.ajax({type: "GET", url: "/api/plugins/" , async: false}).responseText); var response = JSON.parse($.ajax({type: "GET", url: "/api/plugins/" , async: false}).responseText);
defaultPlugin= JSON.parse($.ajax({type: "GET", url: "/api/plugins/default" , async: false}).responseText); defaultPlugin= JSON.parse($.ajax({type: "GET", url: "/api/plugins/default" , async: false}).responseText);
var response2 = JSON.parse($.ajax({type: "GET", url: "/api/datasets/" , async: false}).responseText);
get_plugins(response); get_plugins(response);
get_default_parameters(); get_default_parameters();
get_datasets(response2);
draw_plugins_list(); draw_plugins_list();
draw_plugins_selection(); draw_plugins_selection();
draw_parameters(); draw_parameters();
draw_plugin_description(); draw_datasets();
if (evaluation_enabled) {
var response2 = JSON.parse($.ajax({type: "GET", url: "/api/datasets/" , async: false}).responseText);
get_datasets(response2);
draw_datasets();
}
$(window).on('hashchange', hashchanged); $(window).on('hashchange', hashchanged);
hashchanged(); hashchanged();
@@ -181,34 +144,17 @@ function get_default_parameters(){
} }
function get_selected_plugin(){
return document.getElementsByClassName('plugin')[0].options[document.getElementsByClassName('plugin')[0].selectedIndex].value;
}
function draw_default_parameters(){ function draw_default_parameters(){
var basic_params = document.getElementById("basic_params"); var basic_params = document.getElementById("basic_params");
basic_params.innerHTML = params_div(default_params); basic_params.innerHTML = params_div(default_params);
} }
function update_params(params, plug){
ep = plugins_params[plug];
for(k in ep){
params[k] = ep[k];
}
return params
}
function draw_extra_parameters(){ function draw_extra_parameters(){
var plugin = get_selected_plugin(); var plugin = document.getElementsByClassName('plugin')[0].options[document.getElementsByClassName('plugin')[0].selectedIndex].value;
get_parameters(); get_parameters();
var extra_params = document.getElementById("extra_params"); var extra_params = document.getElementById("extra_params");
var params = {}; extra_params.innerHTML = params_div(plugins_params[plugin]);
for (sel in pipeline){
update_params(params, pipeline[sel]);
}
update_params(params, plugin);
extra_params.innerHTML = params_div(params);
} }
function draw_parameters(){ function draw_parameters(){
@@ -315,15 +261,6 @@ function add_param(key, value){
return "&"+key+"="+value; return "&"+key+"="+value;
} }
function get_pipeline_arg(){
arg = "";
for (ix in pipeline){
arg = arg + pipeline[ix] + ",";
}
arg = arg + get_selected_plugin();
return arg;
}
function load_JSON(){ function load_JSON(){
url = "/api"; url = "/api";
@@ -332,9 +269,7 @@ function load_JSON(){
rawcontainer.innerHTML = ''; rawcontainer.innerHTML = '';
container.innerHTML = ''; container.innerHTML = '';
var plugin = get_pipeline_arg(); var plugin = document.getElementsByClassName("plugin")[0].options[document.getElementsByClassName("plugin")[0].selectedIndex].value;
$(".loading").addClass("loader");
$("#preview").hide();
var input = encodeURIComponent(document.getElementById("input").value); var input = encodeURIComponent(document.getElementById("input").value);
url += "?algo="+plugin+"&i="+input url += "?algo="+plugin+"&i="+input
@@ -345,27 +280,27 @@ function load_JSON(){
url += add_param(key, params[key]); url += add_param(key, params[key]);
} }
$.ajax({type: "GET", url: url}).always(function(response){ var response = $.ajax({type: "GET", url: url , async: false}).responseText;
document.getElementById("results-div").style.display = 'block'; rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
if(typeof response=="object") {
var options = {
mode: 'view'
};
var editor = new JSONEditor(container, options, response);
editor.expandAll();
$('#results-div a[href="#viewer"]').click();
response = JSON.stringify(response, null, 4);
} else {
console.log("Got turtle?");
$('#results-div a[href="#raw"]').click();
}
rawcontainer.innerHTML = replaceURLWithHTMLLinks(response); document.getElementById("input_request").innerHTML = "<a href='"+url+"'>"+url+"</a>"
document.getElementById("input_request").innerHTML = "<a href='"+url+"'>"+url+"</a>" document.getElementById("results-div").style.display = 'block';
try {
$(".loading").removeClass("loader"); response = JSON.parse(response);
$("#preview").show(); var options = {
}); mode: 'view'
};
var editor = new JSONEditor(container, options, response);
editor.expandAll();
// $('#results-div a[href="#viewer"]').tab('show');
$('#results-div a[href="#viewer"]').click();
// location.hash = 'raw';
}
catch(err){
console.log("Error decoding JSON (got turtle?)");
$('#results-div a[href="#raw"]').click();
// location.hash = 'raw';
}
} }
function get_datasets_from_checkbox(){ function get_datasets_from_checkbox(){
@@ -412,53 +347,40 @@ function evaluate_JSON(){
url += "?algo="+plugin+"&dataset="+datasets url += "?algo="+plugin+"&dataset="+datasets
$('#doevaluate').attr("disabled", true); var response = $.ajax({type: "GET", url: url , async: false, dataType: 'json'}).responseText;
$.ajax({type: "GET", url: url, dataType: 'json'}).done(function(resp) { rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
$('#doevaluate').attr("disabled", false);
response = resp.responseText;
rawcontainer.innerHTML = replaceURLWithHTMLLinks(response); document.getElementById("input_request_eval").innerHTML = "<a href='"+url+"'>"+url+"</a>"
document.getElementById("evaluate-div").style.display = 'block';
document.getElementById("input_request_eval").innerHTML = "<a href='"+url+"'>"+url+"</a>"
document.getElementById("evaluate-div").style.display = 'block';
try {
response = JSON.parse(response);
var options = {
mode: 'view'
};
//Control the single response results
if (!(Array.isArray(response.evaluations))){
response.evaluations = [response.evaluations]
}
new_tbody = create_body_metrics(response.evaluations)
table.replaceChild(new_tbody, table.lastElementChild)
var editor = new JSONEditor(container, options, response);
editor.expandAll();
// $('#results-div a[href="#viewer"]').tab('show');
$('#evaluate-div a[href="#evaluate-table"]').click();
// location.hash = 'raw';
try {
response = JSON.parse(response);
var options = {
mode: 'view'
};
//Control the single response results
if (!(Array.isArray(response.evaluations))){
response.evaluations = [response.evaluations]
} }
catch(err){
console.log("Error decoding JSON (got turtle?)");
$('#evaluate-div a[href="#evaluate-raw"]').click();
// location.hash = 'raw';
}
})
}
function draw_plugin_description(){ new_tbody = create_body_metrics(response.evaluations)
var plugin = plugins[get_selected_plugin()]; table.replaceChild(new_tbody, table.lastElementChild)
$("#plugdescription").text(plugin.description);
console.log(plugin); var editor = new JSONEditor(container, options, response);
} editor.expandAll();
// $('#results-div a[href="#viewer"]').tab('show');
$('#evaluate-div a[href="#evaluate-table"]').click();
// location.hash = 'raw';
}
catch(err){
console.log("Error decoding JSON (got turtle?)");
$('#evaluate-div a[href="#evaluate-raw"]').click();
// location.hash = 'raw';
}
function plugin_selected(){
draw_extra_parameters();
draw_plugin_description();
} }

View File

@@ -5,9 +5,6 @@
<title>Playground {{version}}</title> <title>Playground {{version}}</title>
</head> </head>
<script>
this.evaluation_enabled = {% if evaluation %}true{%else %}false{%endif%};
</script>
<script src="static/js/jquery-2.1.1.min.js" ></script> <script src="static/js/jquery-2.1.1.min.js" ></script>
<!--<script src="jquery.autosize.min.js"></script>--> <!--<script src="jquery.autosize.min.js"></script>-->
<link rel="stylesheet" href="static/css/bootstrap.min.css"> <link rel="stylesheet" href="static/css/bootstrap.min.css">
@@ -35,9 +32,7 @@
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" ><a class="active" href="#about">About</a></li> <li role="presentation" ><a class="active" href="#about">About</a></li>
<li role="presentation"class="active"><a class="active" href="#test">Test it</a></li> <li role="presentation"class="active"><a class="active" href="#test">Test it</a></li>
{% if evaluation %}
<li role="presentation"><a class="active" href="#evaluate">Evaluate Plugins</a></li> <li role="presentation"><a class="active" href="#evaluate">Evaluate Plugins</a></li>
{% endif %}
</ul> </ul>
@@ -66,14 +61,6 @@
</ul> </ul>
</p> </p>
<p>Senpy is a research project. If you use it in your research, please cite:
<pre>
Senpy: A Pragmatic Linked Sentiment Analysis Framework.
Sánchez-Rada, J. F., Iglesias, C. A., Corcuera, I., & Araque, Ó.
In Data Science and Advanced Analytics (DSAA),
2016 IEEE International Conference on (pp. 735-742). IEEE.
</pre>
</p>
</div> </div>
<div class="col-lg-6 "> <div class="col-lg-6 ">
@@ -83,6 +70,8 @@ In Data Science and Advanced Analytics (DSAA),
</div> </div>
<div class="panel-body"><ul id=availablePlugins></ul></div> <div class="panel-body"><ul id=availablePlugins></ul></div>
</div> </div>
</div>
<div class="col-lg-6 ">
<a href="http://senpy.readthedocs.io"> <a href="http://senpy.readthedocs.io">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-book"></i> If you are new to senpy, you might want to read senpy's documentation</div> <div class="panel-heading"><i class="fa fa-book"></i> If you are new to senpy, you might want to read senpy's documentation</div>
@@ -93,6 +82,9 @@ In Data Science and Advanced Analytics (DSAA),
<div class="panel-heading"><i class="fa fa-sign-in"></i> Feel free to follow us on GitHub</div> <div class="panel-heading"><i class="fa fa-sign-in"></i> Feel free to follow us on GitHub</div>
</div> </div>
</a> </a>
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-child"></i> Enjoy.</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -104,28 +96,17 @@ In Data Science and Advanced Analytics (DSAA),
whilst this text makes me happy and surprised at the same time. whilst this text makes me happy and surprised at the same time.
I cannot believe it!</textarea> I cannot believe it!</textarea>
</div> </div>
<div>
<label>Select the plugin:</label>
<select id="plugins" name="plugins" class=plugin onchange="draw_extra_parameters()">
</select>
</div>
<!-- PARAMETERS --> <!-- PARAMETERS -->
<div class="panel-group" id="parameters"> <div class="panel-group" id="parameters">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <a data-toggle="collapse" class="deco-none" href="#basic_params">
<h4 class="panel-title">
Select the plugin.
</h4>
</div>
<div id="plugin_selection" class="panel-collapse panel-body">
<span id="pipeline"></span>
<select name="plugins" class="plugin" onchange="plugin_selected()">
</select>
<span onclick="add_plugin_pipeline()"><span class="btn"><i class="fa fa-plus" title="Add more plugins to the pipeline. Processing order is left to right. i.e. the results of the leftmost plugin will be used as input for the second leftmost, and so on."></i></span></span>
<label class="help-block " id="plugdescription"></label>
</div>
</div>
<div class="panel panel-default">
<a data-toggle="collapse" class="deco-none collapsed" href="#basic_params">
<div class="panel-heading"> <div class="panel-heading">
<h4 class="panel-title"> <h4 class="panel-title">
<i class="fa fa-chevron-right pull-left expandicon"></i>
<i class="fa fa-chevron-down pull-left collapseicon"></i>
Basic API parameters Basic API parameters
</h4> </h4>
</div> </div>
@@ -137,8 +118,6 @@ I cannot believe it!</textarea>
<a data-toggle="collapse" class="deco-none" href="#extra_params"> <a data-toggle="collapse" class="deco-none" href="#extra_params">
<div class="panel-heading"> <div class="panel-heading">
<h4 class="panel-title"> <h4 class="panel-title">
<i class="fa fa-chevron-right pull-left expandicon"></i>
<i class="fa fa-chevron-down pull-left collapseicon"></i>
Plugin extra parameters Plugin extra parameters
</h4> </h4>
</div> </div>
@@ -150,7 +129,6 @@ I cannot believe it!</textarea>
<!-- END PARAMETERS --> <!-- END PARAMETERS -->
<a id="preview" class="btn btn-lg btn-primary" onclick="load_JSON()">Analyse!</a> <a id="preview" class="btn btn-lg btn-primary" onclick="load_JSON()">Analyse!</a>
<div id="loading-results" class="loading"></div>
<!--<button id="visualise" name="type" type="button">Visualise!</button>--> <!--<button id="visualise" name="type" type="button">Visualise!</button>-->
</form> </form>
</div> </div>
@@ -177,8 +155,6 @@ I cannot believe it!</textarea>
</div> </div>
</div> </div>
{% if evaluation %}
<div class="tab-pane" id="evaluate"> <div class="tab-pane" id="evaluate">
<div class="well"> <div class="well">
<form id="form" class="container" onsubmit="return getPlugins();" accept-charset="utf-8"> <form id="form" class="container" onsubmit="return getPlugins();" accept-charset="utf-8">
@@ -193,7 +169,7 @@ I cannot believe it!</textarea>
</select> </select>
</div> </div>
<a id="doevaluate" class="btn btn-lg btn-primary" onclick="evaluate_JSON()">Evaluate Plugin!</a> <a id="preview" class="btn btn-lg btn-primary" onclick="evaluate_JSON()">Evaluate Plugin!</a>
<!--<button id="visualise" name="type" type="button">Visualise!</button>--> <!--<button id="visualise" name="type" type="button">Visualise!</button>-->
</form> </form>
</div> </div>
@@ -240,7 +216,6 @@ I cannot believe it!</textarea>
</div> </div>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
<a href="http://www.gsi.dit.upm.es" target="_blank"><img class="center-block" src="static/img/gsi.png"/> </a> <a href="http://www.gsi.dit.upm.es" target="_blank"><img class="center-block" src="static/img/gsi.png"/> </a>

View File

@@ -1,7 +1,6 @@
from . import models, __version__ from . import models, __version__
from collections import MutableMapping from collections import MutableMapping
import pprint import pprint
import pdb
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -33,8 +32,8 @@ def check_template(indict, template):
if indict != template: if indict != template:
raise models.Error(('Differences found.\n' raise models.Error(('Differences found.\n'
'\tExpected: {}\n' '\tExpected: {}\n'
'\tFound: {}').format(pprint.pformat(template), '\tFound: {}').format(pprint.pformat(indict),
pprint.pformat(indict))) pprint.pformat(template)))
def convert_dictionary(original, mappings): def convert_dictionary(original, mappings):
@@ -68,23 +67,18 @@ def easy_load(app=None, plugin_list=None, plugin_folder=None, **kwargs):
return sp, app return sp, app
def easy_test(plugin_list=None, debug=True): def easy_test(plugin_list=None):
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
try: if not plugin_list:
if not plugin_list: import __main__
import __main__ logger.info('Loading classes from {}'.format(__main__))
logger.info('Loading classes from {}'.format(__main__)) from . import plugins
from . import plugins plugin_list = plugins.from_module(__main__)
plugin_list = plugins.from_module(__main__) for plug in plugin_list:
for plug in plugin_list: plug.test()
plug.test() logger.info('The tests for {} passed!'.format(plug.name))
plug.log.info('My tests passed!') logger.info('All tests passed!')
logger.info('All tests passed!')
except Exception:
if not debug:
raise
pdb.post_mortem()
def easy(host='0.0.0.0', port=5000, debug=True, **kwargs): def easy(host='0.0.0.0', port=5000, debug=True, **kwargs):

View File

@@ -36,7 +36,7 @@ class BlueprintsTest(TestCase):
def test_playground(self): def test_playground(self):
resp = self.client.get("/") resp = self.client.get("/")
assert "main.js" in resp.get_data(as_text=True) assert "main.js" in resp.data.decode()
def test_home(self): def test_home(self):
""" """

View File

@@ -1,6 +1,6 @@
from unittest import TestCase from unittest import TestCase
from senpy.testing import patch_requests from senpy.test import patch_requests
from senpy.client import Client from senpy.client import Client
from senpy.models import Results, Plugins, Error from senpy.models import Results, Plugins, Error
from senpy.plugins import AnalysisPlugin from senpy.plugins import AnalysisPlugin

View File

@@ -47,7 +47,7 @@ class ExtensionsTest(TestCase):
def test_add_delete(self): def test_add_delete(self):
'''Should be able to add and delete new plugins. ''' '''Should be able to add and delete new plugins. '''
new = plugins.Analysis(name='new', description='new', version=0) new = plugins.Plugin(name='new', description='new', version=0)
self.senpy.add_plugin(new) self.senpy.add_plugin(new)
assert new in self.senpy.plugins() assert new in self.senpy.plugins()
self.senpy.delete_plugin(new) self.senpy.delete_plugin(new)

View File

@@ -2,7 +2,7 @@ from unittest import TestCase
import requests import requests
import json import json
from senpy.testing import patch_requests from senpy.test import patch_requests
from senpy.models import Results from senpy.models import Results