mirror of
https://github.com/gsi-upm/senpy
synced 2025-09-17 12:02:21 +00:00
Compare commits
9 Commits
0.8.0-fix1
...
0.8.1
Author | SHA1 | Date | |
---|---|---|---|
|
d8b59d06a4 | ||
|
453b9f3257 | ||
|
5fb858f5fc | ||
|
bd984a1437 | ||
|
e741b565a1 | ||
|
668a803d89 | ||
|
9daae8dda7 | ||
|
c72094b94b | ||
|
15d456d048 |
@@ -6,12 +6,11 @@ image: gsiupm/dockermake:latest
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay
|
||||
DOCKERFILE: Dockerfile
|
||||
VERSION: $CI_BUILD_REF
|
||||
IMAGENAME: $CI_REGISTRY_IMAGE
|
||||
|
||||
stages:
|
||||
- test
|
||||
- images
|
||||
- release
|
||||
- push
|
||||
- clean
|
||||
|
||||
.test: &test_definition
|
||||
@@ -31,38 +30,29 @@ test-2.7:
|
||||
|
||||
|
||||
.image: &image_definition
|
||||
stage: images
|
||||
stage: push
|
||||
before_script:
|
||||
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
|
||||
script:
|
||||
- make -e push-$PYTHON_VERSION
|
||||
only:
|
||||
- tags
|
||||
- master
|
||||
- triggers
|
||||
|
||||
image-3.5:
|
||||
push-3.5:
|
||||
<<: *image_definition
|
||||
variables:
|
||||
PYTHON_VERSION: "3.5"
|
||||
VERSION: $CI_BUILD_TAG
|
||||
IMAGENAME: $CI_REGISTRY_IMAGE
|
||||
|
||||
image-2.7:
|
||||
push-2.7:
|
||||
<<: *image_definition
|
||||
variables:
|
||||
PYTHON_VERSION: "2.7"
|
||||
VERSION: $CI_BUILD_TAG
|
||||
IMAGENAME: $CI_REGISTRY_IMAGE
|
||||
|
||||
image-latest:
|
||||
stage: release
|
||||
push-latest:
|
||||
<<: *image_definition
|
||||
variables:
|
||||
IMAGENAME: $CI_REGISTRY_IMAGE
|
||||
before_script:
|
||||
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
|
||||
script:
|
||||
- make -e push-latest
|
||||
PYTHON_VERSION: latest
|
||||
only:
|
||||
- master
|
||||
- triggers
|
||||
@@ -70,4 +60,6 @@ image-latest:
|
||||
clean :
|
||||
stage: clean
|
||||
script:
|
||||
- make -e clean
|
||||
- make -e clean
|
||||
only:
|
||||
- master
|
17
.travis.yml
17
.travis.yml
@@ -1,8 +1,13 @@
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
install: "pip install -r requirements.txt"
|
||||
|
||||
env:
|
||||
- PYV=2.7
|
||||
- PYV=3.4
|
||||
- PYV=3.5
|
||||
# run nosetests - Tests
|
||||
script: nosetests
|
||||
script: make test-$PYV
|
||||
|
34
Makefile
34
Makefile
@@ -4,7 +4,8 @@ NAME=senpy
|
||||
REPO=gsiupm
|
||||
VERSION=$(shell git describe --tags --dirty 2>/dev/null)
|
||||
TARNAME=$(NAME)-$(VERSION).tar.gz
|
||||
IMAGENAME=$(REPO)/$(NAME):$(VERSION)
|
||||
IMAGENAME=$(REPO)/$(NAME)
|
||||
IMAGEWTAG=$(IMAGENAME):$(VERSION)
|
||||
action="test-${PYMAIN}"
|
||||
|
||||
all: build run
|
||||
@@ -35,24 +36,24 @@ quick_build: $(addprefix build-, $(PYMAIN))
|
||||
build: $(addprefix build-, $(PYVERSIONS))
|
||||
|
||||
build-%: version Dockerfile-%
|
||||
docker build -t '$(IMAGENAME)-python$*' -f Dockerfile-$* .;
|
||||
docker build -t '$(IMAGEWTAG)-python$*' -f Dockerfile-$* .;
|
||||
|
||||
quick_test: $(addprefix test-,$(PYMAIN))
|
||||
|
||||
dev-%:
|
||||
@docker start $(NAME)-dev || (\
|
||||
@docker start $(NAME)-dev$* || (\
|
||||
$(MAKE) build-$*; \
|
||||
docker run -d -w /usr/src/app/ -v $$PWD:/usr/src/app --entrypoint=/bin/bash -p 5000:5000 -ti --name $(NAME)-dev '$(IMAGENAME)-python$*'; \
|
||||
docker run -d -w /usr/src/app/ -v $$PWD:/usr/src/app --entrypoint=/bin/bash -ti --name $(NAME)-dev$* '$(IMAGEWTAG)-python$*'; \
|
||||
)\
|
||||
|
||||
docker exec -ti $(NAME)-dev bash
|
||||
docker exec -ti $(NAME)-dev$* bash
|
||||
|
||||
dev: dev-$(PYMAIN)
|
||||
|
||||
test-all: $(addprefix test-,$(PYVERSIONS))
|
||||
|
||||
test-%: build-%
|
||||
docker run --rm --entrypoint /usr/local/bin/python -w /usr/src/app $(IMAGENAME)-python$* setup.py test
|
||||
docker run --rm --entrypoint /usr/local/bin/python -w /usr/src/app $(IMAGEWTAG)-python$* setup.py test
|
||||
|
||||
test: test-$(PYMAIN)
|
||||
|
||||
@@ -66,15 +67,6 @@ pip_test-%: sdist
|
||||
|
||||
pip_test: $(addprefix pip_test-,$(PYVERSIONS))
|
||||
|
||||
upload-%: test-%
|
||||
docker push '$(IMAGENAME)-python$*'
|
||||
|
||||
upload: test $(addprefix upload-,$(PYVERSIONS))
|
||||
docker tag '$(IMAGENAME)-python$(PYMAIN)' '$(IMAGENAME)'
|
||||
docker tag '$(IMAGENAME)-python$(PYMAIN)' '$(REPO)/$(NAME)'
|
||||
docker push '$(IMAGENAME)'
|
||||
docker push '$(REPO)/$(NAME)'
|
||||
|
||||
clean:
|
||||
@docker ps -a | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[0] != "${VERSION}"){ print $$1;}}' | xargs docker rm -v 2>/dev/null|| true
|
||||
@docker images | awk '/$(REPO)\/$(NAME)/{ split($$2, vers, "-"); if(vers[0] != "${VERSION}"){ print $$1":"$$2;}}' | xargs docker rmi 2>/dev/null|| true
|
||||
@@ -96,18 +88,20 @@ pip_upload:
|
||||
pip_test: $(addprefix pip_test-,$(PYVERSIONS))
|
||||
|
||||
run-%: build-%
|
||||
docker run --rm -p 5000:5000 -ti '$(IMAGENAME)-python$(PYMAIN)' --default-plugins
|
||||
docker run --rm -p 5000:5000 -ti '$(IMAGEWTAG)-python$(PYMAIN)' --default-plugins
|
||||
|
||||
run: run-$(PYMAIN)
|
||||
|
||||
push-latest: build-$(PYMAIN)
|
||||
docker tag $(IMAGENAME)-python$(PYMAIN) $(IMAGENAME)
|
||||
docker push $(IMAGENAME)
|
||||
docker tag '$(IMAGEWTAG)-python$(PYMAIN)' '$(IMAGEWTAG)'
|
||||
docker tag '$(IMAGEWTAG)-python$(PYMAIN)' '$(IMAGENAME)'
|
||||
docker push '$(IMAGENAME)'
|
||||
docker push '$(IMAGEWTAG)'
|
||||
|
||||
push-%: build-%
|
||||
docker push $(IMAGENAME)-python$*
|
||||
docker push $(IMAGENAME):$(VERSION)-python$*
|
||||
|
||||
ci:
|
||||
gitlab-runner exec docker --docker-volumes /var/run/docker.sock:/var/run/docker.sock --env CI_PROJECT_NAME=$(NAME) ${action}
|
||||
|
||||
.PHONY: test test-% test-all build-% build test pip_test run yapf dev ci version .FORCE
|
||||
.PHONY: test test-% test-all build-% build test pip_test run yapf push-main push-% dev ci version .FORCE
|
||||
|
@@ -13,10 +13,11 @@ from .api import API_PARAMS, NIF_PARAMS, parse_params
|
||||
from threading import Thread
|
||||
|
||||
import os
|
||||
import copy
|
||||
import fnmatch
|
||||
import inspect
|
||||
import sys
|
||||
import imp
|
||||
import importlib
|
||||
import logging
|
||||
import traceback
|
||||
import yaml
|
||||
@@ -180,7 +181,7 @@ class Senpy(object):
|
||||
newentries = []
|
||||
for i in resp.entries:
|
||||
if output == "full":
|
||||
newemotions = i.emotions.copy()
|
||||
newemotions = copy.deepcopy(i.emotions)
|
||||
else:
|
||||
newemotions = []
|
||||
for j in i.emotions:
|
||||
@@ -288,7 +289,7 @@ class Senpy(object):
|
||||
|
||||
def install_deps(self):
|
||||
for i in self.plugins.values():
|
||||
self._install_deps(i._info)
|
||||
self._install_deps(i)
|
||||
|
||||
@classmethod
|
||||
def _install_deps(cls, info=None):
|
||||
@@ -302,6 +303,13 @@ class Senpy(object):
|
||||
logger.info('Installing requirements: ' + str(requirements))
|
||||
pip.main(pip_args)
|
||||
|
||||
@classmethod
|
||||
def _load_module(cls, name, root):
|
||||
sys.path.append(root)
|
||||
tmp = importlib.import_module(name)
|
||||
sys.path.remove(root)
|
||||
return tmp
|
||||
|
||||
@classmethod
|
||||
def _load_plugin_from_info(cls, info, root):
|
||||
if not cls.validate_info(info):
|
||||
@@ -309,11 +317,10 @@ class Senpy(object):
|
||||
return None, None
|
||||
module = info["module"]
|
||||
name = info["name"]
|
||||
sys.path.append(root)
|
||||
(fp, pathname, desc) = imp.find_module(module, [root, ])
|
||||
|
||||
cls._install_deps(info)
|
||||
tmp = imp.load_module(module, fp, pathname, desc)
|
||||
sys.path.remove(root)
|
||||
tmp = cls._load_module(module, root)
|
||||
|
||||
candidate = None
|
||||
for _, obj in inspect.getmembers(tmp):
|
||||
if inspect.isclass(obj) and inspect.getmodule(obj) == tmp:
|
||||
|
@@ -7,7 +7,7 @@ import pickle
|
||||
import logging
|
||||
import tempfile
|
||||
import copy
|
||||
from . import models
|
||||
from .. import models
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
0
senpy/plugins/conversion/__init__.py
Normal file
0
senpy/plugins/conversion/__init__.py
Normal file
52
senpy/plugins/conversion/centroids.py
Normal file
52
senpy/plugins/conversion/centroids.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from senpy.plugins import EmotionConversionPlugin
|
||||
from senpy.models import EmotionSet, Emotion, Error
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CentroidConversion(EmotionConversionPlugin):
|
||||
|
||||
def _forward_conversion(self, original):
|
||||
"""Sum the VAD value of all categories found."""
|
||||
res = Emotion()
|
||||
for e in original.onyx__hasEmotion:
|
||||
category = e.onyx__hasEmotionCategory
|
||||
if category in self.centroids:
|
||||
for dim, value in self.centroids[category].iteritems():
|
||||
try:
|
||||
res[dim] += value
|
||||
except Exception:
|
||||
res[dim] = value
|
||||
return res
|
||||
|
||||
def _backwards_conversion(self, original):
|
||||
"""Find the closest category"""
|
||||
dimensions = list(self.centroids.values())[0]
|
||||
|
||||
def distance(e1, e2):
|
||||
return sum((e1[k] - e2.get(self.aliases[k], 0)) for k in dimensions)
|
||||
|
||||
emotion = ''
|
||||
mindistance = 10000000000000000000000.0
|
||||
for state in self.centroids:
|
||||
d = distance(self.centroids[state], original)
|
||||
if d < mindistance:
|
||||
mindistance = d
|
||||
emotion = state
|
||||
result = Emotion(onyx__hasEmotionCategory=emotion)
|
||||
return result
|
||||
|
||||
def convert(self, emotionSet, fromModel, toModel, params):
|
||||
|
||||
cf, ct = self.centroids_direction
|
||||
logger.debug('{}\n{}\n{}\n{}'.format(emotionSet, fromModel, toModel, params))
|
||||
e = EmotionSet()
|
||||
if fromModel == cf:
|
||||
e.onyx__hasEmotion.append(self._forward_conversion(emotionSet))
|
||||
elif fromModel == ct:
|
||||
for i in emotionSet.onyx__hasEmotion:
|
||||
e.onyx__hasEmotion.append(self._backwards_conversion(i))
|
||||
else:
|
||||
raise Error('EMOTION MODEL NOT KNOWN')
|
||||
yield e
|
@@ -1,56 +0,0 @@
|
||||
from senpy.plugins import EmotionConversionPlugin
|
||||
from senpy.models import EmotionSet, Emotion, Error
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import math
|
||||
|
||||
|
||||
class WNA2VAD(EmotionConversionPlugin):
|
||||
|
||||
def _ekman_to_vad(self, ekmanSet):
|
||||
potency = 0
|
||||
arousal = 0
|
||||
dominance = 0
|
||||
for e in ekmanSet.onyx__hasEmotion:
|
||||
category = e.onyx__hasEmotionCategory
|
||||
centroid = self.centroids[category]
|
||||
potency += centroid['V']
|
||||
arousal += centroid['A']
|
||||
dominance += centroid['D']
|
||||
e = Emotion({'emoml:potency': potency,
|
||||
'emoml:arousal': arousal,
|
||||
'emoml:dominance': dominance})
|
||||
return e
|
||||
|
||||
def _vad_to_ekman(self, VADEmotion):
|
||||
V = VADEmotion['emoml:valence']
|
||||
A = VADEmotion['emoml:potency']
|
||||
D = VADEmotion['emoml:dominance']
|
||||
emotion = ''
|
||||
value = 10000000000000000000000.0
|
||||
for state in self.centroids:
|
||||
valence = V - self.centroids[state]['V']
|
||||
arousal = A - self.centroids[state]['A']
|
||||
dominance = D - self.centroids[state]['D']
|
||||
new_value = math.sqrt((valence**2) +
|
||||
(arousal**2) +
|
||||
(dominance**2))
|
||||
if new_value < value:
|
||||
value = new_value
|
||||
emotion = state
|
||||
result = Emotion(onyx__hasEmotionCategory=emotion)
|
||||
return result
|
||||
|
||||
def convert(self, emotionSet, fromModel, toModel, params):
|
||||
logger.debug('{}\n{}\n{}\n{}'.format(emotionSet, fromModel, toModel, params))
|
||||
e = EmotionSet()
|
||||
if fromModel == 'emoml:big6':
|
||||
e.onyx__hasEmotion.append(self._ekman_to_vad(emotionSet))
|
||||
elif fromModel == 'emoml:fsre-dimensions':
|
||||
for i in emotionSet.onyx__hasEmotion:
|
||||
e.onyx__hasEmotion.append(self._vad_to_ekman(e))
|
||||
else:
|
||||
raise Error('EMOTION MODEL NOT KNOWN')
|
||||
yield e
|
@@ -1,13 +1,13 @@
|
||||
---
|
||||
name: Ekman2VAD
|
||||
module: ekman2vad
|
||||
module: senpy.plugins.conversion.centroids
|
||||
description: Plugin to convert emotion sets from Ekman to VAD
|
||||
version: 0.1
|
||||
onyx:doesConversion:
|
||||
- onyx:conversionFrom: emoml:big6
|
||||
onyx:conversionTo: emoml:fsre-dimensions
|
||||
- onyx:conversionFrom: emoml:fsre-dimensions
|
||||
onyx:conversionTo: wna:WNAModel
|
||||
onyx:conversionTo: emoml:big6
|
||||
centroids:
|
||||
emoml:big6anger:
|
||||
A: 6.95
|
||||
@@ -29,7 +29,10 @@ centroids:
|
||||
A: 5.21
|
||||
D: 2.82
|
||||
V: 2.21
|
||||
centroids_direction:
|
||||
- emoml:big6
|
||||
- emoml:fsre-dimensions
|
||||
aliases:
|
||||
A: emoml:arousal
|
||||
V: emoml:potency
|
||||
V: emoml:valence
|
||||
D: emoml:dominance
|
@@ -9,4 +9,6 @@ test=pytest
|
||||
ignore = E402
|
||||
max-line-length = 100
|
||||
[bdist_wheel]
|
||||
universal=1
|
||||
universal=1
|
||||
[tool:pytest]
|
||||
addopts = --cov=senpy --cov-report term-missing
|
@@ -1,5 +1,6 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
|
||||
try:
|
||||
@@ -9,7 +10,7 @@ except ImportError:
|
||||
|
||||
from functools import partial
|
||||
from senpy.extensions import Senpy
|
||||
from senpy.models import Error
|
||||
from senpy.models import Error, Results, Entry, EmotionSet, Emotion
|
||||
from flask import Flask
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -52,6 +53,7 @@ class ExtensionsTest(TestCase):
|
||||
assert module
|
||||
import noop
|
||||
dir(noop)
|
||||
self.senpy.install_deps()
|
||||
|
||||
def test_installing(self):
|
||||
""" Enabling a plugin """
|
||||
@@ -120,3 +122,42 @@ class ExtensionsTest(TestCase):
|
||||
def test_load_default_plugins(self):
|
||||
senpy = Senpy(plugin_folder=self.dir, default_plugins=True)
|
||||
assert len(senpy.plugins) > 1
|
||||
|
||||
def test_convert_emotions(self):
|
||||
self.senpy.activate_all()
|
||||
plugin = {
|
||||
'id': 'imaginary',
|
||||
'onyx:usesEmotionModel': 'emoml:fsre-dimensions'
|
||||
}
|
||||
eSet1 = EmotionSet()
|
||||
eSet1['onyx:hasEmotion'].append(Emotion({
|
||||
'emoml:arousal': 1,
|
||||
'emoml:potency': 0,
|
||||
'emoml:valence': 0
|
||||
}))
|
||||
response = Results({
|
||||
'entries': [Entry({
|
||||
'text': 'much ado about nothing',
|
||||
'emotions': [eSet1]
|
||||
})]
|
||||
})
|
||||
params = {'emotionModel': 'emoml:big6',
|
||||
'conversion': 'full'}
|
||||
r1 = deepcopy(response)
|
||||
self.senpy.convert_emotions(r1,
|
||||
plugin,
|
||||
params)
|
||||
assert len(r1.entries[0].emotions) == 2
|
||||
params['conversion'] = 'nested'
|
||||
r2 = deepcopy(response)
|
||||
self.senpy.convert_emotions(r2,
|
||||
plugin,
|
||||
params)
|
||||
assert len(r2.entries[0].emotions) == 1
|
||||
assert r2.entries[0].emotions[0]['prov:wasDerivedFrom'] == eSet1
|
||||
params['conversion'] = 'filtered'
|
||||
r3 = deepcopy(response)
|
||||
self.senpy.convert_emotions(r3,
|
||||
plugin,
|
||||
params)
|
||||
assert len(r3.entries[0].emotions) == 1
|
||||
|
@@ -143,7 +143,3 @@ class ModelsTest(TestCase):
|
||||
print(t)
|
||||
g = rdflib.Graph().parse(data=t, format='turtle')
|
||||
assert len(g) == len(triples)
|
||||
|
||||
def test_convert_emotions(self):
|
||||
"""It should be possible to convert between different emotion models"""
|
||||
pass
|
||||
|
Reference in New Issue
Block a user