mirror of
https://github.com/gsi-upm/senpy
synced 2024-11-22 00:02:28 +00:00
Improved schema validation
* Added debug Dockerfile/Makefile * Validation of examples in docs
This commit is contained in:
parent
bc1f9e4cf5
commit
b543a4614e
5
Dockerfile-debug-3.4
Normal file
5
Dockerfile-debug-3.4
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from python:3.4
|
||||||
|
|
||||||
|
RUN pip install pytest
|
||||||
|
ADD requirements.txt /usr/src/app/
|
||||||
|
RUN pip install -r /usr/src/app/requirements.txt
|
10
Makefile
10
Makefile
@ -20,12 +20,17 @@ buildall: $(addprefix build-, $(PYVERSIONS))
|
|||||||
build-%: Dockerfile-%
|
build-%: Dockerfile-%
|
||||||
docker build -t '$(REPO)/$(NAME):$(VERSION)-python$*' -f Dockerfile-$* .;
|
docker build -t '$(REPO)/$(NAME):$(VERSION)-python$*' -f Dockerfile-$* .;
|
||||||
|
|
||||||
|
build-debug-%:
|
||||||
|
docker build -t '$(NAME)-debug' -f Dockerfile-debug-$* .;
|
||||||
|
|
||||||
test: $(addprefix test-,$(PYMAIN))
|
test: $(addprefix test-,$(PYMAIN))
|
||||||
|
|
||||||
testall: $(addprefix test-,$(PYVERSIONS))
|
testall: $(addprefix test-,$(PYVERSIONS))
|
||||||
|
|
||||||
debug-%: build-%
|
debug-%: build-debug-%
|
||||||
docker run --rm -w /usr/src/app/ -v $$PWD:/usr/src/app --entrypoint=/bin/bash -ti '$(REPO)/$(NAME):$(VERSION)-python$*' ;
|
docker run --rm -w /usr/src/app/ -v $$PWD:/usr/src/app --entrypoint=/bin/bash -ti $(NAME)-debug ;
|
||||||
|
|
||||||
|
debug: debug-$(PYMAIN)
|
||||||
|
|
||||||
test-%: build-%
|
test-%: build-%
|
||||||
docker run --rm -w /usr/src/app/ --entrypoint=/usr/local/bin/python -ti '$(REPO)/$(NAME):$(VERSION)-python$*' setup.py test --addopts "-vvv -s" ;
|
docker run --rm -w /usr/src/app/ --entrypoint=/usr/local/bin/python -ti '$(REPO)/$(NAME):$(VERSION)-python$*' setup.py test --addopts "-vvv -s" ;
|
||||||
@ -51,6 +56,7 @@ upload: testall $(addprefix upload-,$(PYVERSIONS))
|
|||||||
clean:
|
clean:
|
||||||
@docker ps -a | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[1] != "${VERSION}"){ print $$1;}}' | xargs docker rm 2>/dev/null|| true
|
@docker ps -a | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[1] != "${VERSION}"){ print $$1;}}' | xargs docker rm 2>/dev/null|| true
|
||||||
@docker images | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[1] != "${VERSION}"){ print $$1":"$$2;}}' | xargs docker rmi 2>/dev/null|| true
|
@docker images | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[1] != "${VERSION}"){ print $$1":"$$2;}}' | xargs docker rmi 2>/dev/null|| true
|
||||||
|
@docker rmi $(NAME)-debug 2>/dev/null || true
|
||||||
|
|
||||||
upload_git:
|
upload_git:
|
||||||
git commit -a
|
git commit -a
|
||||||
|
4
docs/bad-examples/plugins/noplugins.json
Normal file
4
docs/bad-examples/plugins/noplugins.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
]
|
||||||
|
}
|
18
docs/bad-examples/results/example-basic-FAIL.json
Normal file
18
docs/bad-examples/results/example-basic-FAIL.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
|
"@id": "http://example.com#NIFExample",
|
||||||
|
"@type": "results",
|
||||||
|
"analysis": [
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"@type": [
|
||||||
|
"nif:RFC5147String",
|
||||||
|
"nif:Context"
|
||||||
|
],
|
||||||
|
"nif:beginIndex": 0,
|
||||||
|
"nif:endIndex": 40,
|
||||||
|
"nif:isString": "My favourite actress is Natalie Portman"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
docs/examples/plugins/noplugins.json
Normal file
5
docs/examples/plugins/noplugins.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"@type": "plugins",
|
||||||
|
"plugins": [
|
||||||
|
]
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
"@id": "http://example.com#NIFExample",
|
"@id": "http://example.com#NIFExample",
|
||||||
|
"@type": "results",
|
||||||
"analysis": [
|
"analysis": [
|
||||||
],
|
],
|
||||||
"entries": [
|
"entries": [
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
"@id": "me:Result1",
|
"@id": "me:Result1",
|
||||||
|
"@type": "results",
|
||||||
"analysis": [
|
"analysis": [
|
||||||
{
|
{
|
||||||
"@id": "me:SAnalysis1",
|
"@id": "me:SAnalysis1",
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
"@id": "me:Result1",
|
"@id": "me:Result1",
|
||||||
|
"@type": "results",
|
||||||
"analysis": [
|
"analysis": [
|
||||||
{
|
{
|
||||||
"@id": "me:EmotionAnalysis1",
|
"@id": "me:EmotionAnalysis1",
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
"@id": "me:Result1",
|
"@id": "me:Result1",
|
||||||
|
"@type": "results",
|
||||||
"analysis": [
|
"analysis": [
|
||||||
{
|
{
|
||||||
"@id": "me:NER1",
|
"@id": "me:NER1",
|
46
docs/examples/results/example-pad.json
Normal file
46
docs/examples/results/example-pad.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
|
{
|
||||||
|
"emovoc": "http://www.gsi.dit.upm.es/ontologies/onyx/vocabularies/emotionml/ns#"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@id": "me:Result1",
|
||||||
|
"@type": "results",
|
||||||
|
"analysis": [
|
||||||
|
{
|
||||||
|
"@id": "me:HesamsAnalysis",
|
||||||
|
"@type": "onyx:EmotionAnalysis",
|
||||||
|
"onyx:usesEmotionModel": "emovoc:pad-dimensions"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"@id": "Entry1",
|
||||||
|
"@type": [
|
||||||
|
"nif:RFC5147String",
|
||||||
|
"nif:Context"
|
||||||
|
],
|
||||||
|
"nif:isString": "This is a test string",
|
||||||
|
"entities": [
|
||||||
|
],
|
||||||
|
"suggestions": [
|
||||||
|
],
|
||||||
|
"sentiments": [
|
||||||
|
],
|
||||||
|
"emotions": [
|
||||||
|
{
|
||||||
|
"@id": "Entry1#char=0,21",
|
||||||
|
"nif:anchorOf": "This is a test string",
|
||||||
|
"prov:wasGeneratedBy": "me:HesamAnalysis",
|
||||||
|
"onyx:hasEmotion": [
|
||||||
|
{
|
||||||
|
"emovoc:pleasure": 0.5,
|
||||||
|
"emovoc:arousal": 0.7
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
"@id": "me:Result1",
|
"@id": "me:Result1",
|
||||||
|
"@type": "results",
|
||||||
"analysis": [
|
"analysis": [
|
||||||
{
|
{
|
||||||
"@id": "me:SAnalysis1",
|
"@id": "me:SAnalysis1",
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
"@context": "http://mixedemotions-project.eu/ns/context.jsonld",
|
||||||
"@id": "me:Result1",
|
"@id": "me:Result1",
|
||||||
|
"@type": "results",
|
||||||
"analysis": [
|
"analysis": [
|
||||||
{
|
{
|
||||||
"@id": "me:SgAnalysis1",
|
"@id": "me:SgAnalysis1",
|
@ -1 +1 @@
|
|||||||
0.6.2
|
pre-0.7.0
|
||||||
|
@ -36,7 +36,6 @@ def read_schema(schema_file, absolute=False):
|
|||||||
return jsonref.load(f, base_uri=schema_uri)
|
return jsonref.load(f, base_uri=schema_uri)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
base_schema = read_schema(DEFINITIONS_FILE)
|
base_schema = read_schema(DEFINITIONS_FILE)
|
||||||
logging.debug(base_schema)
|
logging.debug(base_schema)
|
||||||
|
|
||||||
@ -157,18 +156,16 @@ class SenpyModel(SenpyMixin, dict):
|
|||||||
|
|
||||||
temp = dict(*args, **kwargs)
|
temp = dict(*args, **kwargs)
|
||||||
|
|
||||||
|
for obj in [self.schema,]+self.schema.get('allOf', []):
|
||||||
|
for k, v in obj.get('properties', {}).items():
|
||||||
|
if 'default' in v:
|
||||||
|
temp[k] = copy.deepcopy(v['default'])
|
||||||
|
|
||||||
for i in temp:
|
for i in temp:
|
||||||
nk = self._get_key(i)
|
nk = self._get_key(i)
|
||||||
if nk != i:
|
if nk != i:
|
||||||
temp[nk] = temp[i]
|
temp[nk] = temp[i]
|
||||||
del temp[i]
|
del temp[i]
|
||||||
|
|
||||||
reqs = self.schema.get('required', [])
|
|
||||||
for i in reqs:
|
|
||||||
if i not in temp:
|
|
||||||
prop = self.schema['properties'][i]
|
|
||||||
if 'default' in prop:
|
|
||||||
temp[i] = copy.deepcopy(prop['default'])
|
|
||||||
if 'context' in temp:
|
if 'context' in temp:
|
||||||
context = temp['context']
|
context = temp['context']
|
||||||
del temp['context']
|
del temp['context']
|
||||||
@ -226,12 +223,21 @@ class EmotionSet(SenpyModel):
|
|||||||
class Emotion(SenpyModel):
|
class Emotion(SenpyModel):
|
||||||
schema = read_schema('emotion.json')
|
schema = read_schema('emotion.json')
|
||||||
|
|
||||||
|
class EmotionModel(SenpyModel):
|
||||||
|
schema = read_schema('emotionModel.json')
|
||||||
|
|
||||||
class Suggestion(SenpyModel):
|
class Suggestion(SenpyModel):
|
||||||
schema = read_schema('suggestion.json')
|
schema = read_schema('suggestion.json')
|
||||||
|
|
||||||
class PluginModel(SenpyModel):
|
class PluginModel(SenpyModel):
|
||||||
schema = read_schema('plugin.json')
|
schema = read_schema('plugin.json')
|
||||||
|
|
||||||
|
class EmotionPluginModel(SenpyModel):
|
||||||
|
schema = read_schema('plugin.json')
|
||||||
|
|
||||||
|
class SentimentPluginModel(SenpyModel):
|
||||||
|
schema = read_schema('plugin.json')
|
||||||
|
|
||||||
class Plugins(SenpyModel):
|
class Plugins(SenpyModel):
|
||||||
schema = read_schema('plugins.json')
|
schema = read_schema('plugins.json')
|
||||||
|
|
||||||
|
9
senpy/schemas/dimensions.json
Normal file
9
senpy/schemas/dimensions.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string"},
|
||||||
|
"maxValue": {"type": "number"},
|
||||||
|
"minValue": {"type": "number"}
|
||||||
|
},
|
||||||
|
"required": ["name", "maxValue", "minValue"]
|
||||||
|
}
|
18
senpy/schemas/emotionAnalysis.json
Normal file
18
senpy/schemas/emotionAnalysis.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"description": "Senpy Emotion analysis",
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "analysis.json"},
|
||||||
|
{"properties":
|
||||||
|
{
|
||||||
|
"onyx:hasEmotionModel": {
|
||||||
|
"anyOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"$ref": "emotionModel.json"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["onyx:hasEmotionModel"]
|
||||||
|
}]
|
||||||
|
}
|
@ -8,17 +8,20 @@
|
|||||||
"description": "Piece of context that contains the Sentiment",
|
"description": "Piece of context that contains the Sentiment",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"onyx:hasEmotion": {
|
"onyx:hasDimension": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "dimensions.json"
|
||||||
|
},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"onyx:hasEmotionCategory": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "emotion.json"
|
"$ref": "emotion.json"
|
||||||
},
|
},
|
||||||
"default": []
|
"default": []
|
||||||
},
|
|
||||||
"prov:wasGeneratedBy": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ID of the analysis that generated this Emotion. The full object should be included in the \"analysis\" property of the root object"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["@id", "prov:wasGeneratedBy", "onyx:hasEmotion"]
|
"required": ["@id", "onyx:hasEmotion"]
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "response.json"},
|
||||||
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "plugin.json"
|
"$ref": "plugin.json"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"@type": {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
"type": "object"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"@type": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["@type"]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"allOf": [
|
||||||
|
{"$ref": "response.json"},
|
||||||
|
{
|
||||||
"title": "Results",
|
"title": "Results",
|
||||||
"description": "The results of an analysis",
|
"description": "The results of an analysis",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -7,6 +10,9 @@
|
|||||||
"@context": {
|
"@context": {
|
||||||
"$ref": "context.json"
|
"$ref": "context.json"
|
||||||
},
|
},
|
||||||
|
"@type": {
|
||||||
|
"default": "results"
|
||||||
|
},
|
||||||
"@id": {
|
"@id": {
|
||||||
"description": "ID of the analysis",
|
"description": "ID of the analysis",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -29,3 +35,5 @@
|
|||||||
},
|
},
|
||||||
"required": ["@id", "analysis", "entries"]
|
"required": ["@id", "analysis", "entries"]
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
52
tests/test_schemas.py
Normal file
52
tests/test_schemas.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import json
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
from os import path
|
||||||
|
from fnmatch import fnmatch
|
||||||
|
|
||||||
|
import pyld
|
||||||
|
from jsonschema import validate, RefResolver, Draft4Validator, ValidationError
|
||||||
|
|
||||||
|
root_path = path.join(path.dirname(path.realpath(__file__)), '..')
|
||||||
|
schema_folder = path.join(root_path, 'senpy', 'schemas')
|
||||||
|
examples_path = path.join(root_path, 'docs', 'examples')
|
||||||
|
bad_examples_path = path.join(root_path, 'docs', 'bad-examples')
|
||||||
|
|
||||||
|
class JSONSchemaTests(unittest.TestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def do_create_(jsfile, success):
|
||||||
|
def do_expected(self):
|
||||||
|
with open(jsfile) as f:
|
||||||
|
js = json.load(f)
|
||||||
|
try:
|
||||||
|
assert '@type' in js
|
||||||
|
schema_name = js['@type']
|
||||||
|
with open(os.path.join(schema_folder, schema_name+".json")) as file_object:
|
||||||
|
schema = json.load(file_object)
|
||||||
|
resolver = RefResolver('file://' + schema_folder + '/', schema)
|
||||||
|
validator = Draft4Validator(schema, resolver=resolver)
|
||||||
|
validator.validate(js)
|
||||||
|
except (AssertionError, ValidationError, KeyError) as ex:
|
||||||
|
if success:
|
||||||
|
raise
|
||||||
|
return do_expected
|
||||||
|
|
||||||
|
def add_examples(dirname, success):
|
||||||
|
for dirpath, dirnames, filenames in os.walk(dirname):
|
||||||
|
for i in filenames:
|
||||||
|
if fnmatch(i, '*.json'):
|
||||||
|
filename = path.join(dirpath, i)
|
||||||
|
test_method = do_create_(filename, success)
|
||||||
|
test_method.__name__ = 'test_file_%s_success_%s' % (filename, success)
|
||||||
|
test_method.__doc__ = '%s should %svalidate' % (filename, '' if success else 'not' )
|
||||||
|
setattr(JSONSchemaTests, test_method.__name__, test_method)
|
||||||
|
del test_method
|
||||||
|
|
||||||
|
add_examples(examples_path, True)
|
||||||
|
add_examples(bad_examples_path, False)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user