mirror of
https://github.com/gsi-upm/senpy
synced 2025-09-17 03:52:22 +00:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a82e4ed440 | ||
|
c939b095de | ||
|
6dd4a44924 | ||
|
4291c5eabf | ||
|
7c7a815d1a | ||
|
a3eb8f196c | ||
|
00ffbb3804 | ||
|
13cf0c71c5 | ||
|
e5662d482e | ||
|
61181db199 | ||
|
a1663a3f31 | ||
|
83b23dbdf4 | ||
|
4675d9acf1 | ||
|
6832a2816d | ||
|
7a8abf1823 | ||
|
a21ce0d90e | ||
|
a964e586d7 | ||
|
bce42b5bb4 | ||
|
1313853788 |
@@ -18,6 +18,8 @@ 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
|
||||||
|
@@ -1,5 +1,14 @@
|
|||||||
IMAGENAME?=$(NAME)
|
ifndef IMAGENAME
|
||||||
|
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),)
|
||||||
@@ -19,6 +28,19 @@ 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
|
||||||
|
@@ -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)")
|
||||||
@echo "$(GITHUB_DEPLOY_KEY)" > $(KEY_FILE)
|
@printf '%b' '$(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)
|
||||||
|
@@ -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 echo -e "$(KUBE_CA_BUNDLE)" > $(KUBE_CA_PEM_FILE))
|
CREATED:=$(shell printf '%b\n' '$(KUBE_CA_BUNDLE)' > $(KUBE_CA_PEM_FILE))
|
||||||
endif
|
endif
|
||||||
KUBE_TOKEN?=""
|
KUBE_TOKEN?=""
|
||||||
KUBE_NAMESPACE?=$(NAME)
|
KUBE_NAMESPACE?=$(NAME)
|
||||||
|
@@ -26,6 +26,7 @@ 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-$* .;
|
||||||
@@ -75,8 +76,9 @@ 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)'
|
docker tag '$(IMAGEWTAG)-python$(PYMAIN)' '$(IMAGENAME):latest'
|
||||||
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$*
|
||||||
|
10
docker-compose.dev.yml
Normal file
10
docker-compose.dev.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
senpy:
|
||||||
|
image: "${IMAGENAME-gsiupm/senpy}:${VERSION-latest}"
|
||||||
|
entrypoint: ["/bin/bash"]
|
||||||
|
working_dir: "/senpy-plugins"
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
volumes:
|
||||||
|
- ".:/usr/src/app/"
|
9
docker-compose.test.yml
Normal file
9
docker-compose.test.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
test:
|
||||||
|
image: "${IMAGENAME-gsiupm/senpy}:${VERSION-dev}"
|
||||||
|
entrypoint: ["py.test"]
|
||||||
|
volumes:
|
||||||
|
- ".:/usr/src/app/"
|
||||||
|
command:
|
||||||
|
[]
|
11
docker-compose.yml
Normal file
11
docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
senpy:
|
||||||
|
image: "${IMAGENAME-gsiupm/senpy}:${VERSION-dev}"
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile${PYVERSION--2.7}
|
||||||
|
ports:
|
||||||
|
- 5001:5000
|
||||||
|
volumes:
|
||||||
|
- "./data:/data"
|
@@ -11,7 +11,7 @@ class Async(AnalysisPlugin):
|
|||||||
'''An example of an asynchronous module'''
|
'''An example of an asynchronous module'''
|
||||||
author = '@balkian'
|
author = '@balkian'
|
||||||
version = '0.2'
|
version = '0.2'
|
||||||
async = True
|
sync = False
|
||||||
|
|
||||||
def _do_async(self, num_processes):
|
def _do_async(self, num_processes):
|
||||||
pool = multiprocessing.Pool(processes=num_processes)
|
pool = multiprocessing.Pool(processes=num_processes)
|
||||||
|
@@ -43,7 +43,6 @@ 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 = [{
|
||||||
|
@@ -12,3 +12,4 @@ rdflib-jsonld
|
|||||||
numpy
|
numpy
|
||||||
scipy
|
scipy
|
||||||
scikit-learn
|
scikit-learn
|
||||||
|
responses
|
||||||
|
@@ -78,10 +78,15 @@ def main():
|
|||||||
help='Do not run a server, only install plugin dependencies')
|
help='Do not run a server, only install plugin dependencies')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--only-test',
|
'--only-test',
|
||||||
'-t',
|
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help='Do not run a server, just test all plugins')
|
help='Do not run a server, just test all plugins')
|
||||||
|
parser.add_argument(
|
||||||
|
'--test',
|
||||||
|
'-t',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Test all plugins before launching the server')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--only-list',
|
'--only-list',
|
||||||
'--list',
|
'--list',
|
||||||
@@ -99,12 +104,24 @@ def main():
|
|||||||
action='store_false',
|
action='store_false',
|
||||||
default=True,
|
default=True,
|
||||||
help='Run a threaded server')
|
help='Run a threaded server')
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-deps',
|
||||||
|
'-n',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Skip installing dependencies')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--version',
|
'--version',
|
||||||
'-v',
|
'-v',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help='Output the senpy version and exit')
|
help='Output the senpy version and exit')
|
||||||
|
parser.add_argument(
|
||||||
|
'--allow-fail',
|
||||||
|
'--fail',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Do not exit if some plugins fail to activate')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.version:
|
if args.version:
|
||||||
print('Senpy version {}'.format(senpy.__version__))
|
print('Senpy version {}'.format(senpy.__version__))
|
||||||
@@ -119,19 +136,27 @@ def main():
|
|||||||
data_folder=args.data_folder)
|
data_folder=args.data_folder)
|
||||||
if args.only_list:
|
if args.only_list:
|
||||||
plugins = sp.plugins()
|
plugins = sp.plugins()
|
||||||
maxwidth = max(len(x.id) for x in plugins)
|
maxname = max(len(x.name) for x in plugins)
|
||||||
|
maxversion = max(len(x.version) for x in plugins)
|
||||||
|
print('Found {} plugins:'.format(len(plugins)))
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
import inspect
|
import inspect
|
||||||
fpath = inspect.getfile(plugin.__class__)
|
fpath = inspect.getfile(plugin.__class__)
|
||||||
print('{: <{width}} @ {}'.format(plugin.id, fpath, width=maxwidth))
|
print('\t{: <{maxname}} @ {: <{maxversion}} -> {}'.format(plugin.name,
|
||||||
|
plugin.version,
|
||||||
|
fpath,
|
||||||
|
maxname=maxname,
|
||||||
|
maxversion=maxversion))
|
||||||
return
|
return
|
||||||
sp.install_deps()
|
if not args.no_deps:
|
||||||
|
sp.install_deps()
|
||||||
if args.only_install:
|
if args.only_install:
|
||||||
return
|
return
|
||||||
sp.activate_all()
|
sp.activate_all(allow_fail=args.allow_fail)
|
||||||
if args.only_test:
|
if args.test or args.only_test:
|
||||||
easy_test(sp.plugins())
|
easy_test(sp.plugins(), debug=args.debug)
|
||||||
return
|
if args.only_test:
|
||||||
|
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,
|
||||||
args.port))
|
args.port))
|
||||||
|
23
senpy/api.py
23
senpy/api.py
@@ -3,6 +3,10 @@ from .models import Error, Results, Entry, from_string
|
|||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
boolean = [True, False]
|
||||||
|
|
||||||
|
|
||||||
API_PARAMS = {
|
API_PARAMS = {
|
||||||
"algorithm": {
|
"algorithm": {
|
||||||
"aliases": ["algorithms", "a", "algo"],
|
"aliases": ["algorithms", "a", "algo"],
|
||||||
@@ -13,14 +17,14 @@ API_PARAMS = {
|
|||||||
"expanded-jsonld": {
|
"expanded-jsonld": {
|
||||||
"@id": "expanded-jsonld",
|
"@id": "expanded-jsonld",
|
||||||
"aliases": ["expanded"],
|
"aliases": ["expanded"],
|
||||||
"options": "boolean",
|
"options": boolean,
|
||||||
"required": True,
|
"required": True,
|
||||||
"default": False
|
"default": False
|
||||||
},
|
},
|
||||||
"with_parameters": {
|
"with_parameters": {
|
||||||
"aliases": ['withparameters',
|
"aliases": ['withparameters',
|
||||||
'with-parameters'],
|
'with-parameters'],
|
||||||
"options": "boolean",
|
"options": boolean,
|
||||||
"default": False,
|
"default": False,
|
||||||
"required": True
|
"required": True
|
||||||
},
|
},
|
||||||
@@ -29,14 +33,14 @@ API_PARAMS = {
|
|||||||
"aliases": ["o"],
|
"aliases": ["o"],
|
||||||
"default": "json-ld",
|
"default": "json-ld",
|
||||||
"required": True,
|
"required": True,
|
||||||
"options": ["json-ld", "turtle"],
|
"options": ["json-ld", "turtle", "ntriples"],
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"@id": "help",
|
"@id": "help",
|
||||||
"description": "Show additional help to know more about the possible parameters",
|
"description": "Show additional help to know more about the possible parameters",
|
||||||
"aliases": ["h"],
|
"aliases": ["h"],
|
||||||
"required": True,
|
"required": True,
|
||||||
"options": "boolean",
|
"options": boolean,
|
||||||
"default": False
|
"default": False
|
||||||
},
|
},
|
||||||
"emotionModel": {
|
"emotionModel": {
|
||||||
@@ -83,7 +87,7 @@ WEB_PARAMS = {
|
|||||||
"aliases": ["headers"],
|
"aliases": ["headers"],
|
||||||
"required": True,
|
"required": True,
|
||||||
"default": False,
|
"default": False,
|
||||||
"options": "boolean"
|
"options": boolean
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +136,7 @@ NIF_PARAMS = {
|
|||||||
"aliases": ["u"],
|
"aliases": ["u"],
|
||||||
"required": False,
|
"required": False,
|
||||||
"default": "RFC5147String",
|
"default": "RFC5147String",
|
||||||
"options": "RFC5147String"
|
"options": ["RFC5147String", ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +163,7 @@ def parse_params(indict, *specs):
|
|||||||
wrong_params[param] = spec[param]
|
wrong_params[param] = spec[param]
|
||||||
continue
|
continue
|
||||||
if "options" in options:
|
if "options" in options:
|
||||||
if options["options"] == "boolean":
|
if options["options"] == boolean:
|
||||||
outdict[param] = outdict[param] in [None, True, 'true', '1']
|
outdict[param] = outdict[param] in [None, True, 'true', '1']
|
||||||
elif outdict[param] not in options["options"]:
|
elif outdict[param] not in options["options"]:
|
||||||
wrong_params[param] = spec[param]
|
wrong_params[param] = spec[param]
|
||||||
@@ -172,7 +176,7 @@ def parse_params(indict, *specs):
|
|||||||
errors=wrong_params)
|
errors=wrong_params)
|
||||||
raise message
|
raise message
|
||||||
if 'algorithm' in outdict and not isinstance(outdict['algorithm'], list):
|
if 'algorithm' in outdict and not isinstance(outdict['algorithm'], list):
|
||||||
outdict['algorithm'] = outdict['algorithm'].split(',')
|
outdict['algorithm'] = list(outdict['algorithm'].split(','))
|
||||||
return outdict
|
return outdict
|
||||||
|
|
||||||
|
|
||||||
@@ -190,7 +194,8 @@ def parse_call(params):
|
|||||||
params = parse_params(params, NIF_PARAMS)
|
params = parse_params(params, NIF_PARAMS)
|
||||||
if params['informat'] == 'text':
|
if params['informat'] == 'text':
|
||||||
results = Results()
|
results = Results()
|
||||||
entry = Entry(nif__isString=params['input'])
|
entry = Entry(nif__isString=params['input'],
|
||||||
|
id='#') # Use @base
|
||||||
results.entries.append(entry)
|
results.entries.append(entry)
|
||||||
elif params['informat'] == 'json-ld':
|
elif params['informat'] == 'json-ld':
|
||||||
results = from_string(params['input'], cls=Results)
|
results = from_string(params['input'], cls=Results)
|
||||||
|
@@ -18,15 +18,15 @@
|
|||||||
Blueprints for Senpy
|
Blueprints for Senpy
|
||||||
"""
|
"""
|
||||||
from flask import (Blueprint, request, current_app, render_template, url_for,
|
from flask import (Blueprint, request, current_app, render_template, url_for,
|
||||||
jsonify)
|
jsonify, redirect)
|
||||||
from .models import Error, Response, Help, Plugins, read_schema, dump_schema, Datasets
|
from .models import Error, Response, Help, Plugins, read_schema, dump_schema, Datasets
|
||||||
from . import api
|
from . import api
|
||||||
from .version import __version__
|
from .version import __version__
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
import json
|
import json
|
||||||
|
import base64
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -34,6 +34,24 @@ api_blueprint = Blueprint("api", __name__)
|
|||||||
demo_blueprint = Blueprint("demo", __name__, template_folder='templates')
|
demo_blueprint = Blueprint("demo", __name__, template_folder='templates')
|
||||||
ns_blueprint = Blueprint("ns", __name__)
|
ns_blueprint = Blueprint("ns", __name__)
|
||||||
|
|
||||||
|
_mimetypes_r = {'json-ld': ['application/ld+json'],
|
||||||
|
'turtle': ['text/turtle'],
|
||||||
|
'ntriples': ['application/n-triples'],
|
||||||
|
'text': ['text/plain']}
|
||||||
|
|
||||||
|
MIMETYPES = {}
|
||||||
|
|
||||||
|
for k, vs in _mimetypes_r.items():
|
||||||
|
for v in vs:
|
||||||
|
if v in MIMETYPES:
|
||||||
|
raise Exception('MIMETYPE {} specified for two formats: {} and {}'.format(v,
|
||||||
|
v,
|
||||||
|
MIMETYPES[v]))
|
||||||
|
MIMETYPES[v] = k
|
||||||
|
|
||||||
|
DEFAULT_MIMETYPE = 'application/ld+json'
|
||||||
|
DEFAULT_FORMAT = 'json-ld'
|
||||||
|
|
||||||
|
|
||||||
def get_params(req):
|
def get_params(req):
|
||||||
if req.method == 'POST':
|
if req.method == 'POST':
|
||||||
@@ -45,22 +63,60 @@ def get_params(req):
|
|||||||
return indict
|
return indict
|
||||||
|
|
||||||
|
|
||||||
|
def encoded_url(url=None, base=None):
|
||||||
|
code = ''
|
||||||
|
if not url:
|
||||||
|
if request.method == 'GET':
|
||||||
|
url = request.full_path[1:] # Remove the first slash
|
||||||
|
else:
|
||||||
|
hash(frozenset(request.parameters.items()))
|
||||||
|
code = 'hash:{}'.format(hash)
|
||||||
|
|
||||||
|
code = code or base64.urlsafe_b64encode(url.encode()).decode()
|
||||||
|
|
||||||
|
if base:
|
||||||
|
return base + code
|
||||||
|
return url_for('api.decode', code=code, _external=True)
|
||||||
|
|
||||||
|
|
||||||
|
def decoded_url(code, base=None):
|
||||||
|
if code.startswith('hash:'):
|
||||||
|
raise Exception('Can not decode a URL for a POST request')
|
||||||
|
base = base or request.url_root
|
||||||
|
path = base64.urlsafe_b64decode(code.encode()).decode()
|
||||||
|
return base + path
|
||||||
|
|
||||||
|
|
||||||
@demo_blueprint.route('/')
|
@demo_blueprint.route('/')
|
||||||
def index():
|
def index():
|
||||||
return render_template("index.html", version=__version__)
|
ev = str(get_params(request).get('evaluation', False))
|
||||||
|
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')
|
||||||
def context(entity="context"):
|
def context(entity="context"):
|
||||||
context = Response._context
|
context = Response._context
|
||||||
context['@vocab'] = url_for('ns.index', _external=True)
|
context['@vocab'] = url_for('ns.index', _external=True)
|
||||||
|
context['endpoint'] = url_for('api.api_root', _external=True)
|
||||||
return jsonify({"@context": context})
|
return jsonify({"@context": context})
|
||||||
|
|
||||||
|
|
||||||
|
@api_blueprint.route('/d/<code>')
|
||||||
|
def decode(code):
|
||||||
|
try:
|
||||||
|
return redirect(decoded_url(code))
|
||||||
|
except Exception:
|
||||||
|
return Error('invalid URL').flask()
|
||||||
|
|
||||||
|
|
||||||
@ns_blueprint.route('/') # noqa: F811
|
@ns_blueprint.route('/') # noqa: F811
|
||||||
def index():
|
def index():
|
||||||
context = Response._context
|
context = Response._context.copy()
|
||||||
context['@vocab'] = url_for('.ns', _external=True)
|
context['endpoint'] = url_for('api.api_root', _external=True)
|
||||||
return jsonify({"@context": context})
|
return jsonify({"@context": context})
|
||||||
|
|
||||||
|
|
||||||
@@ -76,7 +132,7 @@ def basic_api(f):
|
|||||||
default_params = {
|
default_params = {
|
||||||
'inHeaders': False,
|
'inHeaders': False,
|
||||||
'expanded-jsonld': False,
|
'expanded-jsonld': False,
|
||||||
'outformat': 'json-ld',
|
'outformat': None,
|
||||||
'with_parameters': True,
|
'with_parameters': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,42 +151,52 @@ def basic_api(f):
|
|||||||
request.parameters = params
|
request.parameters = params
|
||||||
response = f(*args, **kwargs)
|
response = f(*args, **kwargs)
|
||||||
except (Exception) as ex:
|
except (Exception) as ex:
|
||||||
if current_app.debug:
|
if current_app.debug or current_app.config['TESTING']:
|
||||||
raise
|
raise
|
||||||
if not isinstance(ex, Error):
|
if not isinstance(ex, Error):
|
||||||
msg = "{}:\n\t{}".format(ex,
|
msg = "{}".format(ex)
|
||||||
traceback.format_exc())
|
|
||||||
ex = Error(message=msg, status=500)
|
ex = Error(message=msg, status=500)
|
||||||
logger.exception('Error returning analysis result')
|
|
||||||
response = ex
|
response = ex
|
||||||
response.parameters = raw_params
|
response.parameters = raw_params
|
||||||
logger.error(ex)
|
logger.exception(ex)
|
||||||
|
|
||||||
if 'parameters' in response and not params['with_parameters']:
|
if 'parameters' in response and not params['with_parameters']:
|
||||||
del response.parameters
|
del response.parameters
|
||||||
|
|
||||||
logger.info('Response: {}'.format(response))
|
logger.info('Response: {}'.format(response))
|
||||||
|
mime = request.accept_mimetypes\
|
||||||
|
.best_match(MIMETYPES.keys(),
|
||||||
|
DEFAULT_MIMETYPE)
|
||||||
|
|
||||||
|
mimeformat = MIMETYPES.get(mime, DEFAULT_FORMAT)
|
||||||
|
outformat = params['outformat'] or mimeformat
|
||||||
|
|
||||||
return response.flask(
|
return response.flask(
|
||||||
in_headers=params['inHeaders'],
|
in_headers=params['inHeaders'],
|
||||||
headers=headers,
|
headers=headers,
|
||||||
prefix=url_for('.api_root', _external=True),
|
prefix=params.get('prefix', encoded_url()),
|
||||||
context_uri=url_for('api.context',
|
context_uri=url_for('api.context',
|
||||||
entity=type(response).__name__,
|
entity=type(response).__name__,
|
||||||
_external=True),
|
_external=True),
|
||||||
outformat=params['outformat'],
|
outformat=outformat,
|
||||||
expanded=params['expanded-jsonld'])
|
expanded=params['expanded-jsonld'])
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
@api_blueprint.route('/', methods=['POST', 'GET'])
|
@api_blueprint.route('/', defaults={'plugin': None}, methods=['POST', 'GET'])
|
||||||
|
@api_blueprint.route('/<path:plugin>', methods=['POST', 'GET'])
|
||||||
@basic_api
|
@basic_api
|
||||||
def api_root():
|
def api_root(plugin):
|
||||||
if request.parameters['help']:
|
if request.parameters['help']:
|
||||||
dic = dict(api.API_PARAMS, **api.NIF_PARAMS)
|
dic = dict(api.API_PARAMS, **api.NIF_PARAMS)
|
||||||
response = Help(valid_parameters=dic)
|
response = Help(valid_parameters=dic)
|
||||||
return response
|
return response
|
||||||
req = api.parse_call(request.parameters)
|
req = api.parse_call(request.parameters)
|
||||||
|
if plugin:
|
||||||
|
plugin = plugin.replace('+', '/')
|
||||||
|
plugin = plugin.split('/')
|
||||||
|
req.parameters['algorithm'] = plugin
|
||||||
return current_app.senpy.analyse(req)
|
return current_app.senpy.analyse(req)
|
||||||
|
|
||||||
|
|
||||||
@@ -160,7 +226,7 @@ def plugins():
|
|||||||
|
|
||||||
@api_blueprint.route('/plugins/<plugin>/', methods=['POST', 'GET'])
|
@api_blueprint.route('/plugins/<plugin>/', methods=['POST', 'GET'])
|
||||||
@basic_api
|
@basic_api
|
||||||
def plugin(plugin=None):
|
def plugin(plugin):
|
||||||
sp = current_app.senpy
|
sp = current_app.senpy
|
||||||
return sp.get_plugin(plugin)
|
return sp.get_plugin(plugin)
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ class Client(object):
|
|||||||
return self.request('/', method=method, input=input, **kwargs)
|
return self.request('/', method=method, input=input, **kwargs)
|
||||||
|
|
||||||
def evaluate(self, input, method='GET', **kwargs):
|
def evaluate(self, input, method='GET', **kwargs):
|
||||||
return self.request('/evaluate', method = method, input=input, **kwargs)
|
return self.request('/evaluate', method=method, input=input, **kwargs)
|
||||||
|
|
||||||
def plugins(self, *args, **kwargs):
|
def plugins(self, *args, **kwargs):
|
||||||
resp = self.request(path='/plugins').plugins
|
resp = self.request(path='/plugins').plugins
|
||||||
@@ -24,7 +24,7 @@ class Client(object):
|
|||||||
return {d.name: d for d in resp}
|
return {d.name: d for d in resp}
|
||||||
|
|
||||||
def request(self, path=None, method='GET', **params):
|
def request(self, path=None, method='GET', **params):
|
||||||
url = '{}{}'.format(self.endpoint, path)
|
url = '{}{}'.format(self.endpoint.rstrip('/'), path)
|
||||||
response = requests.request(method=method, url=url, params=params)
|
response = requests.request(method=method, url=url, params=params)
|
||||||
try:
|
try:
|
||||||
resp = models.from_dict(response.json())
|
resp = models.from_dict(response.json())
|
||||||
|
@@ -18,14 +18,9 @@ import errno
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
from . import gsitk_compat
|
||||||
|
|
||||||
try:
|
logger = logging.getLogger(__name__)
|
||||||
from gsitk.datasets.datasets import DatasetManager
|
|
||||||
GSITK_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
logger.warn('GSITK is not installed. Some functions will be unavailable.')
|
|
||||||
GSITK_AVAILABLE = False
|
|
||||||
|
|
||||||
|
|
||||||
class Senpy(object):
|
class Senpy(object):
|
||||||
@@ -95,7 +90,7 @@ class Senpy(object):
|
|||||||
if plugin in self._plugins:
|
if plugin in self._plugins:
|
||||||
return self._plugins[plugin]
|
return self._plugins[plugin]
|
||||||
|
|
||||||
results = self.plugins(id='plugins/{}'.format(name))
|
results = self.plugins(id='endpoint:plugins/{}'.format(name))
|
||||||
|
|
||||||
if not results:
|
if not results:
|
||||||
return Error(message="Plugin not found", status=404)
|
return Error(message="Plugin not found", status=404)
|
||||||
@@ -158,7 +153,6 @@ class Senpy(object):
|
|||||||
yield i
|
yield i
|
||||||
return
|
return
|
||||||
plugin = plugins[0]
|
plugin = plugins[0]
|
||||||
self._activate(plugin) # Make sure the plugin is activated
|
|
||||||
specific_params = api.parse_extra_params(req, plugin)
|
specific_params = api.parse_extra_params(req, plugin)
|
||||||
req.analysis.append({'plugin': plugin,
|
req.analysis.append({'plugin': plugin,
|
||||||
'parameters': specific_params})
|
'parameters': specific_params})
|
||||||
@@ -167,8 +161,7 @@ class Senpy(object):
|
|||||||
yield i
|
yield i
|
||||||
|
|
||||||
def install_deps(self):
|
def install_deps(self):
|
||||||
for plugin in self.plugins(is_activated=True):
|
plugins.install_deps(*self.plugins())
|
||||||
plugins.install_deps(plugin)
|
|
||||||
|
|
||||||
def analyse(self, request):
|
def analyse(self, request):
|
||||||
"""
|
"""
|
||||||
@@ -203,16 +196,14 @@ class Senpy(object):
|
|||||||
raise Error(
|
raise Error(
|
||||||
status=404,
|
status=404,
|
||||||
message="The dataset '{}' is not valid".format(dataset))
|
message="The dataset '{}' is not valid".format(dataset))
|
||||||
dm = DatasetManager()
|
dm = gsitk_compat.DatasetManager()
|
||||||
datasets = dm.prepare_datasets(datasets_name)
|
datasets = dm.prepare_datasets(datasets_name)
|
||||||
return datasets
|
return datasets
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def datasets(self):
|
def datasets(self):
|
||||||
if not GSITK_AVAILABLE:
|
|
||||||
raise Exception('GSITK is not available. Install it to use this function.')
|
|
||||||
self._dataset_list = {}
|
self._dataset_list = {}
|
||||||
dm = DatasetManager()
|
dm = gsitk_compat.DatasetManager()
|
||||||
for item in dm.get_datasets():
|
for item in dm.get_datasets():
|
||||||
for key in item:
|
for key in item:
|
||||||
if key in self._dataset_list:
|
if key in self._dataset_list:
|
||||||
@@ -223,8 +214,6 @@ class Senpy(object):
|
|||||||
return self._dataset_list
|
return self._dataset_list
|
||||||
|
|
||||||
def evaluate(self, params):
|
def evaluate(self, params):
|
||||||
if not GSITK_AVAILABLE:
|
|
||||||
raise Exception('GSITK is not available. Install it to use this function.')
|
|
||||||
logger.debug("evaluating request: {}".format(params))
|
logger.debug("evaluating request: {}".format(params))
|
||||||
results = AggregatedEvaluation()
|
results = AggregatedEvaluation()
|
||||||
results.parameters = params
|
results.parameters = params
|
||||||
@@ -318,10 +307,15 @@ class Senpy(object):
|
|||||||
else:
|
else:
|
||||||
self._default = self._plugins[value.lower()]
|
self._default = self._plugins[value.lower()]
|
||||||
|
|
||||||
def activate_all(self, sync=True):
|
def activate_all(self, sync=True, allow_fail=False):
|
||||||
ps = []
|
ps = []
|
||||||
for plug in self._plugins.keys():
|
for plug in self._plugins.keys():
|
||||||
ps.append(self.activate_plugin(plug, sync=sync))
|
try:
|
||||||
|
self.activate_plugin(plug, sync=sync)
|
||||||
|
except Exception as ex:
|
||||||
|
if not allow_fail:
|
||||||
|
raise
|
||||||
|
logger.error('Could not activate {}: {}'.format(plug, ex))
|
||||||
return ps
|
return ps
|
||||||
|
|
||||||
def deactivate_all(self, sync=True):
|
def deactivate_all(self, sync=True):
|
||||||
@@ -346,6 +340,7 @@ class Senpy(object):
|
|||||||
logger.info(msg)
|
logger.info(msg)
|
||||||
success = True
|
success = True
|
||||||
self._set_active(plugin, success)
|
self._set_active(plugin, success)
|
||||||
|
return success
|
||||||
|
|
||||||
def activate_plugin(self, plugin_name, sync=True):
|
def activate_plugin(self, plugin_name, sync=True):
|
||||||
plugin_name = plugin_name.lower()
|
plugin_name = plugin_name.lower()
|
||||||
@@ -356,8 +351,8 @@ class Senpy(object):
|
|||||||
|
|
||||||
logger.info("Activating plugin: {}".format(plugin.name))
|
logger.info("Activating plugin: {}".format(plugin.name))
|
||||||
|
|
||||||
if sync or 'async' in plugin and not plugin.async:
|
if sync or not getattr(plugin, 'async', True) or getattr(plugin, 'sync', False):
|
||||||
self._activate(plugin)
|
return self._activate(plugin)
|
||||||
else:
|
else:
|
||||||
th = Thread(target=partial(self._activate, plugin))
|
th = Thread(target=partial(self._activate, plugin))
|
||||||
th.start()
|
th.start()
|
||||||
@@ -379,7 +374,7 @@ class Senpy(object):
|
|||||||
|
|
||||||
self._set_active(plugin, False)
|
self._set_active(plugin, False)
|
||||||
|
|
||||||
if sync or 'async' in plugin and not plugin.async:
|
if sync or not getattr(plugin, 'async', True) or not getattr(plugin, 'sync', False):
|
||||||
self._deactivate(plugin)
|
self._deactivate(plugin)
|
||||||
else:
|
else:
|
||||||
th = Thread(target=partial(self._deactivate, plugin))
|
th = Thread(target=partial(self._deactivate, plugin))
|
||||||
|
31
senpy/gsitk_compat.py
Normal file
31
senpy/gsitk_compat.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pkg_resources import parse_version, get_distribution, DistributionNotFound
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
MSG = 'GSITK is not (properly) installed.'
|
||||||
|
IMPORTMSG = '{} Some functions will be unavailable.'.format(MSG)
|
||||||
|
RUNMSG = '{} Install it to use this function.'.format(MSG)
|
||||||
|
|
||||||
|
|
||||||
|
def raise_exception(*args, **kwargs):
|
||||||
|
raise Exception(RUNMSG)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
gsitk_distro = get_distribution("gsitk")
|
||||||
|
GSITK_VERSION = parse_version(gsitk_distro.version)
|
||||||
|
GSITK_AVAILABLE = GSITK_VERSION > parse_version("0.1.9.1") # Earlier versions have a bug
|
||||||
|
except DistributionNotFound:
|
||||||
|
GSITK_AVAILABLE = False
|
||||||
|
GSITK_VERSION = ()
|
||||||
|
|
||||||
|
if GSITK_AVAILABLE:
|
||||||
|
from gsitk.datasets.datasets import DatasetManager
|
||||||
|
from gsitk.evaluation.evaluation import Evaluation as Eval
|
||||||
|
from sklearn.pipeline import Pipeline
|
||||||
|
modules = locals()
|
||||||
|
else:
|
||||||
|
logger.warning(IMPORTMSG)
|
||||||
|
DatasetManager = Eval = Pipeline = raise_exception
|
@@ -138,7 +138,7 @@ class BaseModel(with_metaclass(BaseMeta, CustomDict)):
|
|||||||
@property
|
@property
|
||||||
def id(self):
|
def id(self):
|
||||||
if '@id' not in self:
|
if '@id' not in self:
|
||||||
self['@id'] = ':{}_{}'.format(type(self).__name__, time.time())
|
self['@id'] = '_:{}_{}'.format(type(self).__name__, time.time())
|
||||||
return self['@id']
|
return self['@id']
|
||||||
|
|
||||||
@id.setter
|
@id.setter
|
||||||
@@ -146,7 +146,7 @@ class BaseModel(with_metaclass(BaseMeta, CustomDict)):
|
|||||||
self['@id'] = value
|
self['@id'] = value
|
||||||
|
|
||||||
def flask(self,
|
def flask(self,
|
||||||
in_headers=True,
|
in_headers=False,
|
||||||
headers=None,
|
headers=None,
|
||||||
outformat='json-ld',
|
outformat='json-ld',
|
||||||
**kwargs):
|
**kwargs):
|
||||||
@@ -176,20 +176,22 @@ class BaseModel(with_metaclass(BaseMeta, CustomDict)):
|
|||||||
|
|
||||||
def serialize(self, format='json-ld', with_mime=False, **kwargs):
|
def serialize(self, format='json-ld', with_mime=False, **kwargs):
|
||||||
js = self.jsonld(**kwargs)
|
js = self.jsonld(**kwargs)
|
||||||
|
content = json.dumps(js, indent=2, sort_keys=True)
|
||||||
if format == 'json-ld':
|
if format == 'json-ld':
|
||||||
content = json.dumps(js, indent=2, sort_keys=True)
|
|
||||||
mimetype = "application/json"
|
mimetype = "application/json"
|
||||||
elif format in ['turtle', ]:
|
elif format in ['turtle', 'ntriples']:
|
||||||
logger.debug(js)
|
logger.debug(js)
|
||||||
content = json.dumps(js, indent=2, sort_keys=True)
|
base = kwargs.get('prefix')
|
||||||
g = Graph().parse(
|
g = Graph().parse(
|
||||||
data=content,
|
data=content,
|
||||||
format='json-ld',
|
format='json-ld',
|
||||||
base=kwargs.get('prefix'),
|
base=base,
|
||||||
context=self._context)
|
context=[self._context,
|
||||||
|
{'@base': base}])
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Parsing with prefix: {}'.format(kwargs.get('prefix')))
|
'Parsing with prefix: {}'.format(kwargs.get('prefix')))
|
||||||
content = g.serialize(format='turtle').decode('utf-8')
|
content = g.serialize(format=format,
|
||||||
|
base=base).decode('utf-8')
|
||||||
mimetype = 'text/{}'.format(format)
|
mimetype = 'text/{}'.format(format)
|
||||||
else:
|
else:
|
||||||
raise Error('Unknown outformat: {}'.format(format))
|
raise Error('Unknown outformat: {}'.format(format))
|
||||||
@@ -203,24 +205,23 @@ class BaseModel(with_metaclass(BaseMeta, CustomDict)):
|
|||||||
context_uri=None,
|
context_uri=None,
|
||||||
prefix=None,
|
prefix=None,
|
||||||
expanded=False):
|
expanded=False):
|
||||||
ser = self.serializable()
|
|
||||||
|
|
||||||
result = jsonld.compact(
|
result = self.serializable()
|
||||||
ser,
|
|
||||||
self._context,
|
|
||||||
options={
|
|
||||||
'base': prefix,
|
|
||||||
'expandContext': self._context,
|
|
||||||
'senpy': prefix
|
|
||||||
})
|
|
||||||
if context_uri:
|
|
||||||
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})[0]
|
||||||
if not with_context:
|
if not with_context:
|
||||||
del result['@context']
|
try:
|
||||||
|
del result['@context']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
elif context_uri:
|
||||||
|
result['@context'] = context_uri
|
||||||
|
else:
|
||||||
|
result['@context'] = self._context
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def validate(self, obj=None):
|
def validate(self, obj=None):
|
||||||
@@ -323,7 +324,10 @@ def _add_class_from_schema(*args, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
for i in [
|
for i in [
|
||||||
|
'aggregatedEvaluation',
|
||||||
'analysis',
|
'analysis',
|
||||||
|
'dataset',
|
||||||
|
'datasets',
|
||||||
'emotion',
|
'emotion',
|
||||||
'emotionConversion',
|
'emotionConversion',
|
||||||
'emotionConversionPlugin',
|
'emotionConversionPlugin',
|
||||||
@@ -331,19 +335,17 @@ 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',
|
||||||
'aggregatedEvaluation',
|
'topic',
|
||||||
'evaluation',
|
|
||||||
'metric',
|
|
||||||
'dataset',
|
|
||||||
'datasets',
|
|
||||||
|
|
||||||
]:
|
]:
|
||||||
_add_class_from_schema(i)
|
_add_class_from_schema(i)
|
||||||
|
@@ -3,6 +3,7 @@ standard_library.install_aliases()
|
|||||||
|
|
||||||
|
|
||||||
from future.utils import with_metaclass
|
from future.utils import with_metaclass
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import os
|
import os
|
||||||
@@ -18,21 +19,16 @@ 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
|
||||||
|
from .. import gsitk_compat
|
||||||
|
from .. import testing
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
|
||||||
from gsitk.evaluation.evaluation import Evaluation as Eval
|
|
||||||
from sklearn.pipeline import Pipeline
|
|
||||||
GSITK_AVAILABLE = True
|
|
||||||
except ImportError:
|
|
||||||
logger.warn('GSITK is not installed. Some functions will be unavailable.')
|
|
||||||
GSITK_AVAILABLE = False
|
|
||||||
|
|
||||||
|
|
||||||
class PluginMeta(models.BaseMeta):
|
class PluginMeta(models.BaseMeta):
|
||||||
_classes = {}
|
_classes = {}
|
||||||
@@ -91,10 +87,30 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)):
|
|||||||
if info:
|
if info:
|
||||||
self.update(info)
|
self.update(info)
|
||||||
self.validate()
|
self.validate()
|
||||||
self.id = 'plugins/{}_{}'.format(self['name'], self['version'])
|
self.id = 'endpoint:plugins/{}_{}'.format(self['name'], self['version'])
|
||||||
self.is_activated = False
|
self.is_activated = False
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
self.data_folder = data_folder or os.getcwd()
|
self._directory = os.path.abspath(os.path.dirname(inspect.getfile(self.__class__)))
|
||||||
|
|
||||||
|
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 = []
|
||||||
@@ -123,19 +139,28 @@ 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)
|
||||||
logger.debug('Test case passed:\n{}'.format(pprint.pformat(case)))
|
self.log.debug('Test case passed:\n{}'.format(pprint.pformat(case)))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warn('Test case failed:\n{}'.format(pprint.pformat(case)))
|
self.log.warn('Test case failed:\n{}'.format(pprint.pformat(case)))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def test_case(self, case):
|
def test_case(self, case, mock=testing.MOCK_REQUESTS):
|
||||||
entry = models.Entry(case['entry'])
|
entry = models.Entry(case['entry'])
|
||||||
given_parameters = case.get('params', case.get('parameters', {}))
|
given_parameters = case.get('params', case.get('parameters', {}))
|
||||||
expected = case.get('expected', None)
|
expected = case.get('expected', None)
|
||||||
should_fail = case.get('should_fail', False)
|
should_fail = case.get('should_fail', False)
|
||||||
|
responses = case.get('responses', [])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
params = api.parse_params(given_parameters, self.extra_params)
|
params = api.parse_params(given_parameters, self.extra_params)
|
||||||
res = list(self.analyse_entries([entry, ], params))
|
|
||||||
|
method = partial(self.analyse_entries, [entry, ], params)
|
||||||
|
|
||||||
|
if mock:
|
||||||
|
res = list(method())
|
||||||
|
else:
|
||||||
|
with testing.patch_all_requests(responses):
|
||||||
|
res = list(method())
|
||||||
|
|
||||||
if not isinstance(expected, list):
|
if not isinstance(expected, list):
|
||||||
expected = [expected]
|
expected = [expected]
|
||||||
@@ -148,10 +173,22 @@ class Plugin(with_metaclass(PluginMeta, models.Plugin)):
|
|||||||
raise
|
raise
|
||||||
assert not should_fail
|
assert not should_fail
|
||||||
|
|
||||||
def open(self, fpath, *args, **kwargs):
|
def find_file(self, fname):
|
||||||
if not os.path.isabs(fpath):
|
for p in self._data_paths:
|
||||||
fpath = os.path.join(self.data_folder, fpath)
|
alternative = os.path.join(p, fname)
|
||||||
return open(fpath, *args, **kwargs)
|
if os.path.exists(alternative):
|
||||||
|
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)
|
||||||
@@ -186,7 +223,7 @@ class Analysis(Plugin):
|
|||||||
|
|
||||||
def analyse_entries(self, entries, parameters):
|
def analyse_entries(self, entries, parameters):
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
logger.debug('Analysing entry with plugin {}: {}'.format(self, entry))
|
self.log.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:
|
||||||
@@ -299,7 +336,7 @@ class Box(AnalysisPlugin):
|
|||||||
return self.transform(X)
|
return self.transform(X)
|
||||||
|
|
||||||
def as_pipe(self):
|
def as_pipe(self):
|
||||||
pipe = Pipeline([('plugin', self)])
|
pipe = gsitk_compat.Pipeline([('plugin', self)])
|
||||||
pipe.name = self.name
|
pipe.name = self.name
|
||||||
return pipe
|
return pipe
|
||||||
|
|
||||||
@@ -375,7 +412,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):
|
||||||
logger.warning('{} has a corrupted shelf file!'.format(self.id))
|
self.log.warning('Corrupted shelf file: {}'.format(self.shelf_file))
|
||||||
if not self.get('force_shelf', False):
|
if not self.get('force_shelf', False):
|
||||||
raise
|
raise
|
||||||
return self._sh
|
return self._sh
|
||||||
@@ -402,32 +439,31 @@ class ShelfMixin(object):
|
|||||||
self._shelf_file = value
|
self._shelf_file = value
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
logger.debug('saving pickle')
|
self.log.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, **kwargs):
|
def pfilter(plugins, plugin_type=Analysis, **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('ptype {}'.format(ptype))
|
logger.debug('plugin_type {}'.format(plugin_type))
|
||||||
if ptype:
|
if plugin_type:
|
||||||
if isinstance(ptype, PluginMeta):
|
if isinstance(plugin_type, PluginMeta):
|
||||||
ptype = ptype.__name__
|
plugin_type = plugin_type.__name__
|
||||||
try:
|
try:
|
||||||
ptype = ptype[0].upper() + ptype[1:]
|
plugin_type = plugin_type[0].upper() + plugin_type[1:]
|
||||||
pclass = globals()[ptype]
|
pclass = globals()[plugin_type]
|
||||||
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(ptype))
|
raise models.Error('{} is not a valid type'.format(plugin_type))
|
||||||
else:
|
else:
|
||||||
candidates = plugins
|
candidates = plugins
|
||||||
|
|
||||||
@@ -462,6 +498,7 @@ 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:
|
||||||
@@ -476,7 +513,10 @@ 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")
|
raise models.Error("Dependencies not properly installed: {}".format(pip_args))
|
||||||
|
nltk_resources |= set(info.get('nltk_resources', []))
|
||||||
|
|
||||||
|
installed |= nltk.download(list(nltk_resources))
|
||||||
return installed
|
return installed
|
||||||
|
|
||||||
|
|
||||||
@@ -494,7 +534,7 @@ def find_plugins(folders):
|
|||||||
yield fpath
|
yield fpath
|
||||||
|
|
||||||
|
|
||||||
def from_path(fpath, **kwargs):
|
def from_path(fpath, install_on_fail=False, **kwargs):
|
||||||
logger.debug("Loading plugin from {}".format(fpath))
|
logger.debug("Loading plugin from {}".format(fpath))
|
||||||
if fpath.endswith('.py'):
|
if fpath.endswith('.py'):
|
||||||
# We asume root is the dir of the file, and module is the name of the file
|
# We asume root is the dir of the file, and module is the name of the file
|
||||||
@@ -504,7 +544,7 @@ def from_path(fpath, **kwargs):
|
|||||||
yield instance
|
yield instance
|
||||||
else:
|
else:
|
||||||
info = parse_plugin_info(fpath)
|
info = parse_plugin_info(fpath)
|
||||||
yield from_info(info, **kwargs)
|
yield from_info(info, install_on_fail=install_on_fail, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def from_folder(folders, loader=from_path, **kwargs):
|
def from_folder(folders, loader=from_path, **kwargs):
|
||||||
@@ -515,7 +555,7 @@ def from_folder(folders, loader=from_path, **kwargs):
|
|||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
|
|
||||||
def from_info(info, root=None, **kwargs):
|
def from_info(info, root=None, install_on_fail=True, **kwargs):
|
||||||
if any(x not in info for x in ('module',)):
|
if any(x not in info for x in ('module',)):
|
||||||
raise ValueError('Plugin info is not valid: {}'.format(info))
|
raise ValueError('Plugin info is not valid: {}'.format(info))
|
||||||
module = info["module"]
|
module = info["module"]
|
||||||
@@ -523,7 +563,12 @@ def from_info(info, root=None, **kwargs):
|
|||||||
if not root and '_path' in info:
|
if not root and '_path' in info:
|
||||||
root = os.path.dirname(info['_path'])
|
root = os.path.dirname(info['_path'])
|
||||||
|
|
||||||
return one_from_module(module, root=root, info=info, **kwargs)
|
fun = partial(one_from_module, module, root=root, info=info, **kwargs)
|
||||||
|
try:
|
||||||
|
return fun()
|
||||||
|
except (ImportError, LookupError):
|
||||||
|
install_deps(info)
|
||||||
|
return fun()
|
||||||
|
|
||||||
|
|
||||||
def parse_plugin_info(fpath):
|
def parse_plugin_info(fpath):
|
||||||
@@ -570,14 +615,8 @@ def _instances_in_module(module):
|
|||||||
yield obj
|
yield obj
|
||||||
|
|
||||||
|
|
||||||
def _from_module_name(module, root, info=None, install=True, **kwargs):
|
def _from_module_name(module, root, info=None, **kwargs):
|
||||||
try:
|
module = load_module(module, root)
|
||||||
module = load_module(module, root)
|
|
||||||
except ImportError:
|
|
||||||
if not install or not info:
|
|
||||||
raise
|
|
||||||
install_deps(info)
|
|
||||||
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):
|
||||||
yield plugin
|
yield plugin
|
||||||
|
|
||||||
@@ -590,12 +629,9 @@ def _from_loaded_module(module, info=None, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def evaluate(plugins, datasets, **kwargs):
|
def evaluate(plugins, datasets, **kwargs):
|
||||||
if not GSITK_AVAILABLE:
|
ev = gsitk_compat.Eval(tuples=None,
|
||||||
raise Exception('GSITK is not available. Install it to use this function.')
|
datasets=datasets,
|
||||||
|
pipelines=[plugin.as_pipe() for plugin in plugins])
|
||||||
ev = Eval(tuples=None,
|
|
||||||
datasets=datasets,
|
|
||||||
pipelines=[plugin.as_pipe() for plugin in plugins])
|
|
||||||
ev.evaluate()
|
ev.evaluate()
|
||||||
results = ev.results
|
results = ev.results
|
||||||
evaluations = evaluations_to_JSONLD(results, **kwargs)
|
evaluations = evaluations_to_JSONLD(results, **kwargs)
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
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
|
|
@@ -5,13 +5,27 @@ from nltk.tokenize.simple import LineTokenizer
|
|||||||
import nltk
|
import nltk
|
||||||
|
|
||||||
|
|
||||||
class SplitPlugin(AnalysisPlugin):
|
class Split(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":
|
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
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
|
|
@@ -4,12 +4,31 @@ import json
|
|||||||
from senpy.plugins import SentimentPlugin
|
from senpy.plugins import SentimentPlugin
|
||||||
from senpy.models import Sentiment
|
from senpy.models import Sentiment
|
||||||
|
|
||||||
|
ENDPOINT = 'http://www.sentiment140.com/api/bulkClassifyJson'
|
||||||
|
|
||||||
class Sentiment140Plugin(SentimentPlugin):
|
|
||||||
|
class Sentiment140(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(ENDPOINT,
|
||||||
json.dumps({
|
json.dumps({
|
||||||
"language": lang,
|
"language": lang,
|
||||||
"data": [{
|
"data": [{
|
||||||
@@ -31,23 +50,10 @@ class Sentiment140Plugin(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
|
||||||
|
|
||||||
def test(self, *args, **kwargs):
|
|
||||||
'''
|
|
||||||
To avoid calling the sentiment140 API, we will mock the results
|
|
||||||
from requests.
|
|
||||||
'''
|
|
||||||
from senpy.test import patch_requests
|
|
||||||
expected = {"data": [{"polarity": 4}]}
|
|
||||||
with patch_requests(expected) as (request, response):
|
|
||||||
super(Sentiment140Plugin, self).test(*args, **kwargs)
|
|
||||||
assert request.called
|
|
||||||
assert response.json.called
|
|
||||||
|
|
||||||
test_cases = [
|
test_cases = [
|
||||||
{
|
{
|
||||||
'entry': {
|
'entry': {
|
||||||
@@ -61,6 +67,9 @@ class Sentiment140Plugin(SentimentPlugin):
|
|||||||
'marl:hasPolarity': 'marl:Positive',
|
'marl:hasPolarity': 'marl:Positive',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
'responses': [{'url': ENDPOINT,
|
||||||
|
'method': 'POST',
|
||||||
|
'json': {'data': [{'polarity': 4}]}}]
|
||||||
}
|
}
|
||||||
]
|
]
|
@@ -10,8 +10,10 @@
|
|||||||
"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": "dc:subject"
|
"@id": "nif:topic",
|
||||||
|
"@container": "@set"
|
||||||
},
|
},
|
||||||
"entities": {
|
"entities": {
|
||||||
"@id": "me:hasEntities"
|
"@id": "me:hasEntities"
|
||||||
|
@@ -167,3 +167,36 @@ 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); }
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,7 @@ 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);
|
||||||
@@ -30,7 +31,10 @@ function hashchanged(){
|
|||||||
|
|
||||||
|
|
||||||
function get_plugins(response){
|
function get_plugins(response){
|
||||||
plugins = response.plugins;
|
for(ix in response.plugins){
|
||||||
|
plug = response.plugins[ix];
|
||||||
|
plugins[plug.name] = plug;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_datasets(response){
|
function get_datasets(response){
|
||||||
@@ -83,10 +87,32 @@ 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
|
||||||
document.getElementsByClassName('plugin')[0].innerHTML = html;
|
plugin_lists = document.getElementsByClassName('plugin')
|
||||||
document.getElementsByClassName('plugin')[1].innerHTML = html;
|
for (element in plugin_lists){
|
||||||
|
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');
|
||||||
|
|
||||||
@@ -105,6 +131,13 @@ 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=\"";
|
||||||
@@ -118,16 +151,20 @@ 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_datasets();
|
draw_plugin_description();
|
||||||
|
|
||||||
|
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();
|
||||||
@@ -144,17 +181,34 @@ 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 = document.getElementsByClassName('plugin')[0].options[document.getElementsByClassName('plugin')[0].selectedIndex].value;
|
var plugin = get_selected_plugin();
|
||||||
get_parameters();
|
get_parameters();
|
||||||
|
|
||||||
var extra_params = document.getElementById("extra_params");
|
var extra_params = document.getElementById("extra_params");
|
||||||
extra_params.innerHTML = params_div(plugins_params[plugin]);
|
var params = {};
|
||||||
|
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(){
|
||||||
@@ -261,6 +315,15 @@ 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";
|
||||||
@@ -269,7 +332,9 @@ function load_JSON(){
|
|||||||
rawcontainer.innerHTML = '';
|
rawcontainer.innerHTML = '';
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
var plugin = document.getElementsByClassName("plugin")[0].options[document.getElementsByClassName("plugin")[0].selectedIndex].value;
|
var plugin = get_pipeline_arg();
|
||||||
|
$(".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
|
||||||
@@ -280,27 +345,27 @@ function load_JSON(){
|
|||||||
url += add_param(key, params[key]);
|
url += add_param(key, params[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = $.ajax({type: "GET", url: url , async: false}).responseText;
|
$.ajax({type: "GET", url: url}).always(function(response){
|
||||||
rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
|
document.getElementById("results-div").style.display = 'block';
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("input_request").innerHTML = "<a href='"+url+"'>"+url+"</a>"
|
rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
|
||||||
document.getElementById("results-div").style.display = 'block';
|
document.getElementById("input_request").innerHTML = "<a href='"+url+"'>"+url+"</a>"
|
||||||
try {
|
|
||||||
response = JSON.parse(response);
|
$(".loading").removeClass("loader");
|
||||||
var options = {
|
$("#preview").show();
|
||||||
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(){
|
||||||
@@ -347,40 +412,53 @@ function evaluate_JSON(){
|
|||||||
|
|
||||||
url += "?algo="+plugin+"&dataset="+datasets
|
url += "?algo="+plugin+"&dataset="+datasets
|
||||||
|
|
||||||
var response = $.ajax({type: "GET", url: url , async: false, dataType: 'json'}).responseText;
|
$('#doevaluate').attr("disabled", true);
|
||||||
rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
|
$.ajax({type: "GET", url: url, dataType: 'json'}).always(function(resp) {
|
||||||
|
$('#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';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
|
catch(err){
|
||||||
|
console.log("Error decoding JSON (got turtle?)");
|
||||||
|
$('#evaluate-div a[href="#evaluate-raw"]').click();
|
||||||
|
// location.hash = 'raw';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
new_tbody = create_body_metrics(response.evaluations)
|
function draw_plugin_description(){
|
||||||
table.replaceChild(new_tbody, table.lastElementChild)
|
var plugin = plugins[get_selected_plugin()];
|
||||||
|
$("#plugdescription").text(plugin.description);
|
||||||
|
console.log(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
var editor = new JSONEditor(container, options, response);
|
function plugin_selected(){
|
||||||
editor.expandAll();
|
draw_extra_parameters();
|
||||||
// $('#results-div a[href="#viewer"]').tab('show');
|
draw_plugin_description();
|
||||||
$('#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';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -5,6 +5,9 @@
|
|||||||
<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">
|
||||||
@@ -32,7 +35,9 @@
|
|||||||
<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>
|
||||||
|
|
||||||
@@ -61,6 +66,14 @@
|
|||||||
</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 ">
|
||||||
@@ -70,8 +83,6 @@
|
|||||||
</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>
|
||||||
@@ -82,9 +93,6 @@
|
|||||||
<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>
|
||||||
@@ -96,17 +104,28 @@
|
|||||||
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">
|
||||||
<a data-toggle="collapse" class="deco-none" href="#basic_params">
|
<div class="panel-heading">
|
||||||
|
<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>
|
||||||
@@ -118,6 +137,8 @@ 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>
|
||||||
@@ -129,6 +150,7 @@ 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>
|
||||||
@@ -155,6 +177,8 @@ 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">
|
||||||
@@ -169,7 +193,7 @@ I cannot believe it!</textarea>
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a id="preview" class="btn btn-lg btn-primary" onclick="evaluate_JSON()">Evaluate Plugin!</a>
|
<a id="doevaluate" 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>
|
||||||
@@ -216,6 +240,7 @@ 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>
|
||||||
|
@@ -1,36 +0,0 @@
|
|||||||
try:
|
|
||||||
from unittest.mock import patch, MagicMock
|
|
||||||
except ImportError:
|
|
||||||
from mock import patch, MagicMock
|
|
||||||
|
|
||||||
from past.builtins import basestring
|
|
||||||
|
|
||||||
|
|
||||||
import json
|
|
||||||
from contextlib import contextmanager
|
|
||||||
|
|
||||||
from .models import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def patch_requests(value, code=200):
|
|
||||||
success = MagicMock()
|
|
||||||
if isinstance(value, BaseModel):
|
|
||||||
value = value.jsonld()
|
|
||||||
if not isinstance(value, basestring):
|
|
||||||
data = json.dumps(value)
|
|
||||||
else:
|
|
||||||
data = value
|
|
||||||
|
|
||||||
success.json.return_value = value
|
|
||||||
|
|
||||||
success.status_code = code
|
|
||||||
success.content = data
|
|
||||||
success.text = data
|
|
||||||
|
|
||||||
method_mocker = MagicMock()
|
|
||||||
method_mocker.return_value = success
|
|
||||||
with patch.multiple('requests', request=method_mocker,
|
|
||||||
get=method_mocker, post=method_mocker):
|
|
||||||
yield method_mocker, success
|
|
||||||
assert method_mocker.called
|
|
31
senpy/testing.py
Normal file
31
senpy/testing.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from past.builtins import basestring
|
||||||
|
|
||||||
|
import os
|
||||||
|
import responses as requestmock
|
||||||
|
|
||||||
|
from .models import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
MOCK_REQUESTS = os.environ.get('MOCK_REQUESTS', '').lower() in ['no', 'false']
|
||||||
|
|
||||||
|
|
||||||
|
def patch_all_requests(responses):
|
||||||
|
|
||||||
|
patched = requestmock.RequestsMock()
|
||||||
|
|
||||||
|
for response in responses or []:
|
||||||
|
args = response.copy()
|
||||||
|
if 'json' in args and isinstance(args['json'], BaseModel):
|
||||||
|
args['json'] = args['json'].jsonld()
|
||||||
|
args['method'] = getattr(requestmock, args.get('method', 'GET'))
|
||||||
|
patched.add(**args)
|
||||||
|
return patched
|
||||||
|
|
||||||
|
|
||||||
|
def patch_requests(url, response, method='GET', status=200):
|
||||||
|
args = {'url': url, 'method': method, 'status': status}
|
||||||
|
if isinstance(response, basestring):
|
||||||
|
args['body'] = response
|
||||||
|
else:
|
||||||
|
args['json'] = response
|
||||||
|
return patch_all_requests([args])
|
@@ -1,6 +1,7 @@
|
|||||||
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__)
|
||||||
@@ -32,8 +33,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(indict),
|
'\tFound: {}').format(pprint.pformat(template),
|
||||||
pprint.pformat(template)))
|
pprint.pformat(indict)))
|
||||||
|
|
||||||
|
|
||||||
def convert_dictionary(original, mappings):
|
def convert_dictionary(original, mappings):
|
||||||
@@ -67,18 +68,23 @@ def easy_load(app=None, plugin_list=None, plugin_folder=None, **kwargs):
|
|||||||
return sp, app
|
return sp, app
|
||||||
|
|
||||||
|
|
||||||
def easy_test(plugin_list=None):
|
def easy_test(plugin_list=None, debug=True):
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
if not plugin_list:
|
try:
|
||||||
import __main__
|
if not plugin_list:
|
||||||
logger.info('Loading classes from {}'.format(__main__))
|
import __main__
|
||||||
from . import plugins
|
logger.info('Loading classes from {}'.format(__main__))
|
||||||
plugin_list = plugins.from_module(__main__)
|
from . import plugins
|
||||||
for plug in plugin_list:
|
plugin_list = plugins.from_module(__main__)
|
||||||
plug.test()
|
for plug in plugin_list:
|
||||||
logger.info('The tests for {} passed!'.format(plug.name))
|
plug.test()
|
||||||
logger.info('All tests passed!')
|
plug.log.info('My tests passed!')
|
||||||
|
logger.info('All tests passed for {} plugins!'.format(len(plugin_list)))
|
||||||
|
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):
|
||||||
|
@@ -21,7 +21,6 @@ class BlueprintsTest(TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
"""Set up only once, and re-use in every individual test"""
|
"""Set up only once, and re-use in every individual test"""
|
||||||
cls.app = Flask("test_extensions")
|
cls.app = Flask("test_extensions")
|
||||||
cls.app.debug = False
|
|
||||||
cls.client = cls.app.test_client()
|
cls.client = cls.app.test_client()
|
||||||
cls.senpy = Senpy(default_plugins=True)
|
cls.senpy = Senpy(default_plugins=True)
|
||||||
cls.senpy.init_app(cls.app)
|
cls.senpy.init_app(cls.app)
|
||||||
@@ -31,17 +30,21 @@ class BlueprintsTest(TestCase):
|
|||||||
cls.senpy.activate_plugin("DummyRequired", sync=True)
|
cls.senpy.activate_plugin("DummyRequired", sync=True)
|
||||||
cls.senpy.default_plugin = 'Dummy'
|
cls.senpy.default_plugin = 'Dummy'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.app.config['TESTING'] = True # Tell Flask not to catch Exceptions
|
||||||
|
|
||||||
def assertCode(self, resp, code):
|
def assertCode(self, resp, code):
|
||||||
self.assertEqual(resp.status_code, code)
|
self.assertEqual(resp.status_code, code)
|
||||||
|
|
||||||
def test_playground(self):
|
def test_playground(self):
|
||||||
resp = self.client.get("/")
|
resp = self.client.get("/")
|
||||||
assert "main.js" in resp.data.decode()
|
assert "main.js" in resp.get_data(as_text=True)
|
||||||
|
|
||||||
def test_home(self):
|
def test_home(self):
|
||||||
"""
|
"""
|
||||||
Calling with no arguments should ask the user for more arguments
|
Calling with no arguments should ask the user for more arguments
|
||||||
"""
|
"""
|
||||||
|
self.app.config['TESTING'] = False # Errors are expected in this case
|
||||||
resp = self.client.get("/api/")
|
resp = self.client.get("/api/")
|
||||||
self.assertCode(resp, 400)
|
self.assertCode(resp, 400)
|
||||||
js = parse_resp(resp)
|
js = parse_resp(resp)
|
||||||
@@ -64,10 +67,24 @@ class BlueprintsTest(TestCase):
|
|||||||
logging.debug("Got response: %s", js)
|
logging.debug("Got response: %s", js)
|
||||||
assert "@context" in js
|
assert "@context" in js
|
||||||
assert "entries" in js
|
assert "entries" in js
|
||||||
|
assert len(js['analysis']) == 1
|
||||||
|
|
||||||
|
def test_analysis_post(self):
|
||||||
|
"""
|
||||||
|
The results for a POST request should be the same as for a GET request.
|
||||||
|
"""
|
||||||
|
resp = self.client.post("/api/", data={'i': 'My aloha mohame',
|
||||||
|
'with_parameters': True})
|
||||||
|
self.assertCode(resp, 200)
|
||||||
|
js = parse_resp(resp)
|
||||||
|
logging.debug("Got response: %s", js)
|
||||||
|
assert "@context" in js
|
||||||
|
assert "entries" in js
|
||||||
|
assert len(js['analysis']) == 1
|
||||||
|
|
||||||
def test_analysis_extra(self):
|
def test_analysis_extra(self):
|
||||||
"""
|
"""
|
||||||
Extra params that have a default should
|
Extra params that have a default should use it
|
||||||
"""
|
"""
|
||||||
resp = self.client.get("/api/?i=My aloha mohame&algo=Dummy&with_parameters=true")
|
resp = self.client.get("/api/?i=My aloha mohame&algo=Dummy&with_parameters=true")
|
||||||
self.assertCode(resp, 200)
|
self.assertCode(resp, 200)
|
||||||
@@ -81,7 +98,7 @@ class BlueprintsTest(TestCase):
|
|||||||
Extra params that have a required argument that does not
|
Extra params that have a required argument that does not
|
||||||
have a default should raise an error.
|
have a default should raise an error.
|
||||||
"""
|
"""
|
||||||
self.app.debug = False
|
self.app.config['TESTING'] = False # Errors are expected in this case
|
||||||
resp = self.client.get("/api/?i=My aloha mohame&algo=DummyRequired")
|
resp = self.client.get("/api/?i=My aloha mohame&algo=DummyRequired")
|
||||||
self.assertCode(resp, 400)
|
self.assertCode(resp, 400)
|
||||||
js = parse_resp(resp)
|
js = parse_resp(resp)
|
||||||
@@ -92,12 +109,50 @@ class BlueprintsTest(TestCase):
|
|||||||
resp = self.client.get("/api/?i=My aloha mohame&algo=DummyRequired&example=a")
|
resp = self.client.get("/api/?i=My aloha mohame&algo=DummyRequired&example=a")
|
||||||
self.assertCode(resp, 200)
|
self.assertCode(resp, 200)
|
||||||
|
|
||||||
|
def test_analysis_url(self):
|
||||||
|
"""
|
||||||
|
The algorithm can also be specified as part of the URL
|
||||||
|
"""
|
||||||
|
self.app.config['TESTING'] = False # Errors are expected in this case
|
||||||
|
resp = self.client.get("/api/DummyRequired?i=My aloha mohame")
|
||||||
|
self.assertCode(resp, 400)
|
||||||
|
js = parse_resp(resp)
|
||||||
|
logging.debug("Got response: %s", js)
|
||||||
|
assert isinstance(js, models.Error)
|
||||||
|
resp = self.client.get("/api/DummyRequired?i=My aloha mohame&example=notvalid")
|
||||||
|
self.assertCode(resp, 400)
|
||||||
|
resp = self.client.get("/api/DummyRequired?i=My aloha mohame&example=a")
|
||||||
|
self.assertCode(resp, 200)
|
||||||
|
|
||||||
|
def test_analysis_chain(self):
|
||||||
|
"""
|
||||||
|
More than one algorithm can be specified. Plugins will then be chained
|
||||||
|
"""
|
||||||
|
resp = self.client.get("/api/Dummy?i=My aloha mohame")
|
||||||
|
js = parse_resp(resp)
|
||||||
|
assert len(js['analysis']) == 1
|
||||||
|
assert js['entries'][0]['nif:isString'] == 'My aloha mohame'[::-1]
|
||||||
|
|
||||||
|
resp = self.client.get("/api/Dummy/Dummy?i=My aloha mohame")
|
||||||
|
# Calling dummy twice, should return the same string
|
||||||
|
self.assertCode(resp, 200)
|
||||||
|
js = parse_resp(resp)
|
||||||
|
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']) == 2
|
||||||
|
assert js['entries'][0]['nif:isString'] == 'My aloha mohame'
|
||||||
|
|
||||||
def test_error(self):
|
def test_error(self):
|
||||||
"""
|
"""
|
||||||
The dummy plugin returns an empty response,\
|
The dummy plugin returns an empty response,\
|
||||||
it should contain the context
|
it should contain the context
|
||||||
"""
|
"""
|
||||||
self.app.debug = False
|
self.app.config['TESTING'] = False # Errors are expected in this case
|
||||||
resp = self.client.get("/api/?i=My aloha mohame&algo=DOESNOTEXIST")
|
resp = self.client.get("/api/?i=My aloha mohame&algo=DOESNOTEXIST")
|
||||||
self.assertCode(resp, 404)
|
self.assertCode(resp, 404)
|
||||||
js = parse_resp(resp)
|
js = parse_resp(resp)
|
||||||
@@ -139,7 +194,7 @@ class BlueprintsTest(TestCase):
|
|||||||
js = parse_resp(resp)
|
js = parse_resp(resp)
|
||||||
logging.debug(js)
|
logging.debug(js)
|
||||||
assert "@id" in js
|
assert "@id" in js
|
||||||
assert js["@id"] == "plugins/Dummy_0.1"
|
assert js["@id"] == "endpoint:plugins/Dummy_0.1"
|
||||||
|
|
||||||
def test_default(self):
|
def test_default(self):
|
||||||
""" Show only one plugin"""
|
""" Show only one plugin"""
|
||||||
@@ -148,7 +203,7 @@ class BlueprintsTest(TestCase):
|
|||||||
js = parse_resp(resp)
|
js = parse_resp(resp)
|
||||||
logging.debug(js)
|
logging.debug(js)
|
||||||
assert "@id" in js
|
assert "@id" in js
|
||||||
assert js["@id"] == "plugins/Dummy_0.1"
|
assert js["@id"] == "endpoint:plugins/Dummy_0.1"
|
||||||
|
|
||||||
def test_context(self):
|
def test_context(self):
|
||||||
resp = self.client.get("/api/contexts/context.jsonld")
|
resp = self.client.get("/api/contexts/context.jsonld")
|
||||||
@@ -172,5 +227,6 @@ class BlueprintsTest(TestCase):
|
|||||||
assert "help" in js["valid_parameters"]
|
assert "help" in js["valid_parameters"]
|
||||||
|
|
||||||
def test_conversion(self):
|
def test_conversion(self):
|
||||||
|
self.app.config['TESTING'] = False # Errors are expected in this case
|
||||||
resp = self.client.get("/api/?input=hello&algo=emoRand&emotionModel=DOES NOT EXIST")
|
resp = self.client.get("/api/?input=hello&algo=emoRand&emotionModel=DOES NOT EXIST")
|
||||||
self.assertCode(resp, 404)
|
self.assertCode(resp, 404)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from senpy.test import patch_requests
|
from senpy.testing 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
|
||||||
@@ -14,22 +14,28 @@ class ModelsTest(TestCase):
|
|||||||
def test_client(self):
|
def test_client(self):
|
||||||
endpoint = 'http://dummy/'
|
endpoint = 'http://dummy/'
|
||||||
client = Client(endpoint)
|
client = Client(endpoint)
|
||||||
with patch_requests(Results()) as (request, response):
|
with patch_requests('http://dummy/', Results()):
|
||||||
resp = client.analyse('hello')
|
resp = client.analyse('hello')
|
||||||
assert isinstance(resp, Results)
|
assert isinstance(resp, Results)
|
||||||
request.assert_called_with(
|
with patch_requests('http://dummy/', Error('Nothing')):
|
||||||
url=endpoint + '/', method='GET', params={'input': 'hello'})
|
|
||||||
with patch_requests(Error('Nothing')) as (request, response):
|
|
||||||
try:
|
try:
|
||||||
client.analyse(input='hello', algorithm='NONEXISTENT')
|
client.analyse(input='hello', algorithm='NONEXISTENT')
|
||||||
raise Exception('Exceptions should be raised. This is not golang')
|
raise Exception('Exceptions should be raised. This is not golang')
|
||||||
except Error:
|
except Error:
|
||||||
pass
|
pass
|
||||||
request.assert_called_with(
|
|
||||||
url=endpoint + '/',
|
def test_client_post(self):
|
||||||
method='GET',
|
endpoint = 'http://dummy/'
|
||||||
params={'input': 'hello',
|
client = Client(endpoint)
|
||||||
'algorithm': 'NONEXISTENT'})
|
with patch_requests('http://dummy/', Results()):
|
||||||
|
resp = client.analyse('hello')
|
||||||
|
assert isinstance(resp, Results)
|
||||||
|
with patch_requests('http://dummy/', Error('Nothing'), method='POST'):
|
||||||
|
try:
|
||||||
|
client.analyse(input='hello', method='POST', algorithm='NONEXISTENT')
|
||||||
|
raise Exception('Exceptions should be raised. This is not golang')
|
||||||
|
except Error:
|
||||||
|
pass
|
||||||
|
|
||||||
def test_plugins(self):
|
def test_plugins(self):
|
||||||
endpoint = 'http://dummy/'
|
endpoint = 'http://dummy/'
|
||||||
@@ -37,11 +43,8 @@ class ModelsTest(TestCase):
|
|||||||
plugins = Plugins()
|
plugins = Plugins()
|
||||||
p1 = AnalysisPlugin({'name': 'AnalysisP1', 'version': 0, 'description': 'No'})
|
p1 = AnalysisPlugin({'name': 'AnalysisP1', 'version': 0, 'description': 'No'})
|
||||||
plugins.plugins = [p1, ]
|
plugins.plugins = [p1, ]
|
||||||
with patch_requests(plugins) as (request, response):
|
with patch_requests('http://dummy/plugins', plugins):
|
||||||
response = client.plugins()
|
response = client.plugins()
|
||||||
assert isinstance(response, dict)
|
assert isinstance(response, dict)
|
||||||
assert len(response) == 1
|
assert len(response) == 1
|
||||||
assert "AnalysisP1" in response
|
assert "AnalysisP1" in response
|
||||||
request.assert_called_with(
|
|
||||||
url=endpoint + '/plugins', method='GET',
|
|
||||||
params={})
|
|
||||||
|
@@ -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.Plugin(name='new', description='new', version=0)
|
new = plugins.Analysis(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)
|
||||||
@@ -121,8 +121,8 @@ class ExtensionsTest(TestCase):
|
|||||||
# Leaf (defaultdict with __setattr__ and __getattr__.
|
# Leaf (defaultdict with __setattr__ and __getattr__.
|
||||||
r1 = analyse(self.senpy, algorithm="Dummy", input="tupni", output="tuptuo")
|
r1 = analyse(self.senpy, algorithm="Dummy", input="tupni", output="tuptuo")
|
||||||
r2 = analyse(self.senpy, input="tupni", output="tuptuo")
|
r2 = analyse(self.senpy, input="tupni", output="tuptuo")
|
||||||
assert r1.analysis[0] == "plugins/Dummy_0.1"
|
assert r1.analysis[0] == "endpoint:plugins/Dummy_0.1"
|
||||||
assert r2.analysis[0] == "plugins/Dummy_0.1"
|
assert r2.analysis[0] == "endpoint:plugins/Dummy_0.1"
|
||||||
assert r1.entries[0]['nif:isString'] == 'input'
|
assert r1.entries[0]['nif:isString'] == 'input'
|
||||||
|
|
||||||
def test_analyse_empty(self):
|
def test_analyse_empty(self):
|
||||||
@@ -156,8 +156,8 @@ class ExtensionsTest(TestCase):
|
|||||||
r2 = analyse(self.senpy,
|
r2 = analyse(self.senpy,
|
||||||
input="tupni",
|
input="tupni",
|
||||||
output="tuptuo")
|
output="tuptuo")
|
||||||
assert r1.analysis[0] == "plugins/Dummy_0.1"
|
assert r1.analysis[0] == "endpoint:plugins/Dummy_0.1"
|
||||||
assert r2.analysis[0] == "plugins/Dummy_0.1"
|
assert r2.analysis[0] == "endpoint:plugins/Dummy_0.1"
|
||||||
assert r1.entries[0]['nif:isString'] == 'input'
|
assert r1.entries[0]['nif:isString'] == 'input'
|
||||||
|
|
||||||
def test_analyse_error(self):
|
def test_analyse_error(self):
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
#!/bin/env python
|
#!/bin/env python
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import pickle
|
import pickle
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -10,6 +9,7 @@ from unittest import TestCase, skipIf
|
|||||||
from senpy.models import Results, Entry, EmotionSet, Emotion, Plugins
|
from senpy.models import Results, Entry, EmotionSet, Emotion, Plugins
|
||||||
from senpy import plugins
|
from senpy import plugins
|
||||||
from senpy.plugins.conversion.emotion.centroids import CentroidConversion
|
from senpy.plugins.conversion.emotion.centroids import CentroidConversion
|
||||||
|
from senpy.gsitk_compat import GSITK_AVAILABLE
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
@@ -312,9 +312,7 @@ class PluginsTest(TestCase):
|
|||||||
res = c._backwards_conversion(e)
|
res = c._backwards_conversion(e)
|
||||||
assert res["onyx:hasEmotionCategory"] == "c2"
|
assert res["onyx:hasEmotionCategory"] == "c2"
|
||||||
|
|
||||||
@skipIf(sys.version_info < (3, 0),
|
def _test_evaluation(self):
|
||||||
reason="requires Python3")
|
|
||||||
def test_evaluation(self):
|
|
||||||
testdata = []
|
testdata = []
|
||||||
for i in range(50):
|
for i in range(50):
|
||||||
testdata.append(["good", 1])
|
testdata.append(["good", 1])
|
||||||
@@ -348,6 +346,16 @@ class PluginsTest(TestCase):
|
|||||||
smart_metrics = results[0].metrics[0]
|
smart_metrics = results[0].metrics[0]
|
||||||
assert abs(smart_metrics['accuracy'] - 1) < 0.01
|
assert abs(smart_metrics['accuracy'] - 1) < 0.01
|
||||||
|
|
||||||
|
@skipIf(not GSITK_AVAILABLE, "GSITK is not available")
|
||||||
|
def test_evaluation(self):
|
||||||
|
self._test_evaluation()
|
||||||
|
|
||||||
|
@skipIf(GSITK_AVAILABLE, "GSITK is available")
|
||||||
|
def test_evaluation_unavailable(self):
|
||||||
|
with self.assertRaises(Exception) as context:
|
||||||
|
self._test_evaluation()
|
||||||
|
self.assertTrue('GSITK ' in str(context.exception))
|
||||||
|
|
||||||
|
|
||||||
def make_mini_test(fpath):
|
def make_mini_test(fpath):
|
||||||
def mini_test(self):
|
def mini_test(self):
|
||||||
|
@@ -2,31 +2,32 @@ from unittest import TestCase
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
from senpy.test import patch_requests
|
from senpy.testing import patch_requests
|
||||||
from senpy.models import Results
|
from senpy.models import Results
|
||||||
|
|
||||||
|
ENDPOINT = 'http://example.com'
|
||||||
|
|
||||||
|
|
||||||
class TestTest(TestCase):
|
class TestTest(TestCase):
|
||||||
def test_patch_text(self):
|
def test_patch_text(self):
|
||||||
with patch_requests('hello'):
|
with patch_requests(ENDPOINT, 'hello'):
|
||||||
r = requests.get('http://example.com')
|
r = requests.get(ENDPOINT)
|
||||||
assert r.text == 'hello'
|
assert r.text == 'hello'
|
||||||
assert r.content == 'hello'
|
|
||||||
|
|
||||||
def test_patch_json(self):
|
def test_patch_json(self):
|
||||||
r = Results()
|
r = Results()
|
||||||
with patch_requests(r):
|
with patch_requests(ENDPOINT, r):
|
||||||
res = requests.get('http://example.com')
|
res = requests.get(ENDPOINT)
|
||||||
assert res.content == json.dumps(r.jsonld())
|
assert res.text == json.dumps(r.jsonld())
|
||||||
js = res.json()
|
js = res.json()
|
||||||
assert js
|
assert js
|
||||||
assert js['@type'] == r['@type']
|
assert js['@type'] == r['@type']
|
||||||
|
|
||||||
def test_patch_dict(self):
|
def test_patch_dict(self):
|
||||||
r = {'nothing': 'new'}
|
r = {'nothing': 'new'}
|
||||||
with patch_requests(r):
|
with patch_requests(ENDPOINT, r):
|
||||||
res = requests.get('http://example.com')
|
res = requests.get(ENDPOINT)
|
||||||
assert res.content == json.dumps(r)
|
assert res.text == json.dumps(r)
|
||||||
js = res.json()
|
js = res.json()
|
||||||
assert js
|
assert js
|
||||||
assert js['nothing'] == 'new'
|
assert js['nothing'] == 'new'
|
||||||
|
Reference in New Issue
Block a user