mirror of
https://github.com/gsi-upm/senpy
synced 2025-08-24 02:22:20 +00:00
WIP: working on a full refactor for v2.0
This is still not functional, because it involves a LOT of changes to the basic structure of the project. Some of the main changes can be seen in the CHANGELOG.md file, if you're interested, but it boils down to simplifying the logic of plugins (no more activation/deactivation shenanigans), more robust typing and use of schemas (pydantic) to avoid inconsistencies and user errors.
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
#
|
||||
# Copyright 2014 Grupo de Sistemas Inteligentes (GSI) DIT, UPM
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
from senpy import AnalysisPlugin
|
||||
|
||||
import multiprocessing
|
||||
|
||||
|
||||
def _train(process_number):
|
||||
return process_number
|
||||
|
||||
|
||||
class Async(AnalysisPlugin):
|
||||
'''An example of an asynchronous module'''
|
||||
author = '@balkian'
|
||||
version = '0.2'
|
||||
sync = False
|
||||
|
||||
def _do_async(self, num_processes):
|
||||
pool = multiprocessing.Pool(processes=num_processes)
|
||||
values = sorted(pool.map(_train, range(num_processes)))
|
||||
|
||||
return values
|
||||
|
||||
def activate(self):
|
||||
self.value = self._do_async(4)
|
||||
|
||||
def analyse_entry(self, entry, params):
|
||||
values = self._do_async(2)
|
||||
entry.async_values = values
|
||||
yield entry
|
||||
|
||||
test_cases = [
|
||||
{
|
||||
'input': 'any',
|
||||
'expected': {
|
||||
'async_values': [0, 1]
|
||||
}
|
||||
}
|
||||
]
|
@@ -24,8 +24,8 @@ import basic
|
||||
class BasicAnalyseEntry(plugins.SentimentPlugin):
|
||||
'''Equivalent to Basic, implementing the analyse_entry method'''
|
||||
|
||||
author = '@balkian'
|
||||
version = '0.1'
|
||||
author: str = '@balkian'
|
||||
version: str = '0.1'
|
||||
|
||||
mappings = {
|
||||
'pos': 'marl:Positive',
|
||||
@@ -43,7 +43,7 @@ class BasicAnalyseEntry(plugins.SentimentPlugin):
|
||||
entry.sentiments.append(s)
|
||||
yield entry
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'input': 'Hello :)',
|
||||
'polarity': 'marl:Positive'
|
||||
}, {
|
||||
|
@@ -24,8 +24,8 @@ import basic
|
||||
class BasicBox(SentimentBox):
|
||||
''' A modified version of Basic that also does converts annotations manually'''
|
||||
|
||||
author = '@balkian'
|
||||
version = '0.1'
|
||||
author: str = '@balkian'
|
||||
version: str = '0.1'
|
||||
|
||||
def predict_one(self, features, **kwargs):
|
||||
output = basic.get_polarity(features[0])
|
||||
@@ -35,7 +35,7 @@ class BasicBox(SentimentBox):
|
||||
return [0, 0, 1]
|
||||
return [0, 1, 0]
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'input': 'Hello :)',
|
||||
'polarity': 'marl:Positive'
|
||||
}, {
|
||||
|
@@ -25,8 +25,8 @@ import basic
|
||||
class Basic(SentimentBox):
|
||||
'''Provides sentiment annotation using a lexicon'''
|
||||
|
||||
author = '@balkian'
|
||||
version = '0.1'
|
||||
author: str = '@balkian'
|
||||
version: str = '0.1'
|
||||
|
||||
def predict_one(self, features, **kwargs):
|
||||
output = basic.get_polarity(features[0])
|
||||
@@ -36,7 +36,7 @@ class Basic(SentimentBox):
|
||||
return [0, 1, 0]
|
||||
return [0, 0, 1]
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'input': u'Hello :)',
|
||||
'polarity': 'marl:Positive'
|
||||
}, {
|
||||
|
@@ -25,8 +25,8 @@ import basic
|
||||
class Dictionary(plugins.SentimentPlugin):
|
||||
'''Sentiment annotation using a configurable lexicon'''
|
||||
|
||||
author = '@balkian'
|
||||
version = '0.2'
|
||||
author: str = '@balkian'
|
||||
version: str = '0.2'
|
||||
|
||||
dictionaries = [basic.emojis, basic.emoticons]
|
||||
|
||||
@@ -42,7 +42,7 @@ class Dictionary(plugins.SentimentPlugin):
|
||||
entry.sentiments.append(s)
|
||||
yield entry
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'input': 'Hello :)',
|
||||
'polarity': 'marl:Positive'
|
||||
}, {
|
||||
@@ -61,7 +61,7 @@ class EmojiOnly(Dictionary):
|
||||
'''Sentiment annotation with a basic lexicon of emojis'''
|
||||
dictionaries = [basic.emojis]
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'input': 'Hello :)',
|
||||
'polarity': 'marl:Neutral'
|
||||
}, {
|
||||
@@ -80,7 +80,7 @@ class EmoticonsOnly(Dictionary):
|
||||
'''Sentiment annotation with a basic lexicon of emoticons'''
|
||||
dictionaries = [basic.emoticons]
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'input': 'Hello :)',
|
||||
'polarity': 'marl:Positive'
|
||||
}, {
|
||||
@@ -102,7 +102,7 @@ class Salutes(Dictionary):
|
||||
'marl:Negative': ['Good bye', ]
|
||||
}]
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'input': 'Hello :)',
|
||||
'polarity': 'marl:Positive'
|
||||
}, {
|
||||
|
@@ -19,15 +19,15 @@ from senpy import AnalysisPlugin, easy
|
||||
|
||||
class Dummy(AnalysisPlugin):
|
||||
'''This is a dummy self-contained plugin'''
|
||||
author = '@balkian'
|
||||
version = '0.1'
|
||||
author: str = '@balkian'
|
||||
version: str = '0.1'
|
||||
|
||||
def analyse_entry(self, entry, params):
|
||||
entry['nif:isString'] = entry['nif:isString'][::-1]
|
||||
entry.text = entry.text[::-1]
|
||||
entry.reversed = entry.get('reversed', 0) + 1
|
||||
yield entry
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'entry': {
|
||||
'nif:isString': 'Hello',
|
||||
},
|
||||
|
@@ -19,9 +19,9 @@ from senpy import AnalysisPlugin, easy
|
||||
|
||||
class DummyRequired(AnalysisPlugin):
|
||||
'''This is a dummy self-contained plugin'''
|
||||
author = '@balkian'
|
||||
version = '0.1'
|
||||
extra_params = {
|
||||
author: str = '@balkian'
|
||||
version: str = '0.1'
|
||||
extra_params: dict = {
|
||||
'example': {
|
||||
'description': 'An example parameter',
|
||||
'required': True,
|
||||
@@ -30,11 +30,11 @@ class DummyRequired(AnalysisPlugin):
|
||||
}
|
||||
|
||||
def analyse_entry(self, entry, params):
|
||||
entry['nif:isString'] = entry['nif:isString'][::-1]
|
||||
entry.text = entry.text[::-1]
|
||||
entry.reversed = entry.get('reversed', 0) + 1
|
||||
yield entry
|
||||
|
||||
test_cases = [{
|
||||
test_cases: list[dict] = [{
|
||||
'entry': {
|
||||
'nif:isString': 'Hello',
|
||||
},
|
||||
|
@@ -22,11 +22,11 @@ from senpy.models import EmotionSet, Emotion, Entry
|
||||
|
||||
class EmoRand(EmotionPlugin):
|
||||
'''A sample plugin that returns a random emotion annotation'''
|
||||
name = 'emotion-random'
|
||||
author = '@balkian'
|
||||
version = '0.1'
|
||||
url = "https://github.com/gsi-upm/senpy-plugins-community"
|
||||
onyx__usesEmotionModel = "emoml:big6"
|
||||
name: str = 'emotion-random'
|
||||
author: str = '@balkian'
|
||||
version: str = '0.1'
|
||||
url: str = "https://github.com/gsi-upm/senpy-plugins-community"
|
||||
usesEmotionModel: str = "emoml:big6"
|
||||
|
||||
def analyse_entry(self, entry, activity):
|
||||
category = "emoml:big6happiness"
|
||||
|
63
example-plugins/moral/example.ttl
Normal file
63
example-plugins/moral/example.ttl
Normal file
@@ -0,0 +1,63 @@
|
||||
@prefix : <http://www.gsi.upm.es/ontologies/amor/examples#> .
|
||||
@prefix amor: <http://www.gsi.upm.es/ontologies/amor/ns#> .
|
||||
@prefix amor-bhv: <http://www.gsi.upm.es/ontologies/amor/models/bhv/ns#> .
|
||||
@prefix amor-mft: <http://www.gsi.upm.es/ontologies/amor/models/mft/ns#> .
|
||||
@prefix bhv: <http://www.gsi.upm.es/ontologies/bhv#> .
|
||||
@prefix mft: <http://www.gsi.upm.es/ontologies/mft/ns#> .
|
||||
@prefix mls: <http://www.w3.org/ns/mls#> .
|
||||
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
||||
@prefix prov: <http://www.w3.org/ns/prov#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix schema: <http://schema.org/> .
|
||||
|
||||
|
||||
:news1 a owl:NamedIndividual, schema:NewsArticle ;
|
||||
schema:articleBody "Director Comey says the probe into last year's US election would assess if crimes were committed."^^xsd:string ;
|
||||
schema:datePublished "2017-03-20T20:30:54+00:00"^^schema:Date ;
|
||||
schema:headline "Trump Russia claims: FBI's Comey confirms investigation of election 'interference'"^^xsd:string ;
|
||||
schema:image <http://ichef-1.bbci.co.uk/news/560/media/images/75306000/jpg/_75306515_line976.jpg>,
|
||||
<http://ichef.bbci.co.uk/news/560/cpsprodpb/8AB9/production/_95231553_comey2.jpg>,
|
||||
<http://ichef.bbci.co.uk/news/560/cpsprodpb/17519/production/_95231559_committee.jpg>,
|
||||
<http://ichef.bbci.co.uk/news/560/cpsprodpb/CC81/production/_95235325_f704a6dc-c017-4971-aac3-04c03eb097fb.jpg>,
|
||||
<http://ichef-1.bbci.co.uk/news/560/cpsprodpb/11AA1/production/_95235327_c0b59f9e-316e-4641-aa7e-3fec6daea62b.jpg>,
|
||||
<http://ichef.bbci.co.uk/news/560/cpsprodpb/0F99/production/_95239930_trumptweet.png>,
|
||||
<http://ichef-1.bbci.co.uk/news/560/cpsprodpb/10DFA/production/_95241196_mediaitem95241195.jpg>,
|
||||
<http://ichef.bbci.co.uk/news/560/cpsprodpb/2CA0/production/_95242411_comey.jpg>,
|
||||
<http://ichef.bbci.co.uk/news/560/cpsprodpb/11318/production/_95242407_mediaitem95242406.jpg>,
|
||||
<http://ichef-1.bbci.co.uk/news/560/cpsprodpb/BCED/production/_92856384_line976.jpg>,
|
||||
<http://ichef-1.bbci.co.uk/news/560/cpsprodpb/12B64/production/_95244667_mediaitem95244666.jpg> ;
|
||||
schema:mainEntityOfPage <http://www.bbc.com/news/world-us-canada-39324587> ;
|
||||
schema:publisher :bbc ;
|
||||
schema:url <http://www.bbc.com/news/world-us-canada-39324587> .
|
||||
|
||||
:bbc a schema:Organization ;
|
||||
schema:logo <http://www.bbc.co.uk/news/special/2015/newsspec_10857/bbc_news_logo.png?cb=1> ;
|
||||
schema:name "BBC News"^^xsd:string .
|
||||
|
||||
|
||||
:robot1 a prov:SoftwareAgent .
|
||||
|
||||
:model1 a mls:Model .
|
||||
|
||||
:logisticRegression a mls:Algorithm ;
|
||||
rdfs:label "Logistic Regression"@en ,
|
||||
"Regresión Logística"@es .
|
||||
|
||||
:run1 a mls:Run ;
|
||||
mls:executes :wekaLogistic ;
|
||||
mls:hasInput :credit-a ;
|
||||
mls:hasOutput :model1 ;
|
||||
mls:realizes :logisticRegression .
|
||||
|
||||
:analysis3 a amor:MoralValueAnalysis ;
|
||||
prov:wasAssociatedWith :robot1 ;
|
||||
amor:usedMoralValueModel amor-mft:MoralFoundationsTheory ;
|
||||
amor:analysed :news1 ;
|
||||
amor:usedMLModel :model1 ;
|
||||
prov:generated :annotation3 .
|
||||
|
||||
:annotation3 a amor:MoralValueAnnotation ;
|
||||
amor:hasMoralValueCategory mft:Authority ;
|
||||
amor:confidence "0.75"^^xsd:float ;
|
||||
amor-mft:hasPolarityIntensity "0.2"^^xsd:float ;
|
||||
amor:annotated :news1 .
|
10
example-plugins/moral/moral_plugin.py
Normal file
10
example-plugins/moral/moral_plugin.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from senpy.ns import amor, amor_bhv, amor_mft, prov
|
||||
from senpy.plugins import MoralityPlugin
|
||||
|
||||
|
||||
class DummyMoralityPlugin(MoralityPlugin):
|
||||
moralValueModel: str = amor_mft["MoralFoundationTHeory"]
|
||||
|
||||
def annotate(self, entry, activity, **kwargs):
|
||||
yield MoralAnnotation(amor_bhv['Conservation'],
|
||||
confidence=1.8)
|
@@ -21,7 +21,7 @@ from senpy.plugins import SentimentPlugin
|
||||
class NoOp(SentimentPlugin):
|
||||
'''This plugin does nothing. Literally nothing.'''
|
||||
|
||||
version = 0
|
||||
version: str = 0
|
||||
|
||||
def analyse_entry(self, entry, *args, **kwargs):
|
||||
yield entry
|
||||
|
@@ -24,11 +24,10 @@ import basic
|
||||
|
||||
class ParameterizedDictionary(plugins.SentimentPlugin):
|
||||
'''This is a basic self-contained plugin'''
|
||||
author: str = '@balkian'
|
||||
version: str = '0.2'
|
||||
|
||||
author = '@balkian'
|
||||
version = '0.2'
|
||||
|
||||
extra_params = {
|
||||
extra_params: dict = {
|
||||
'positive-words': {
|
||||
'description': 'Comma-separated list of words that are considered positive',
|
||||
'aliases': ['positive'],
|
||||
@@ -56,7 +55,7 @@ class ParameterizedDictionary(plugins.SentimentPlugin):
|
||||
entry.sentiments.append(s)
|
||||
yield entry
|
||||
|
||||
test_cases = [
|
||||
test_cases: list[dict] = [
|
||||
{
|
||||
'input': 'Hello :)',
|
||||
'polarity': 'marl:Positive',
|
||||
|
@@ -20,12 +20,12 @@ from senpy import SentimentPlugin, Sentiment, Entry
|
||||
|
||||
class RandSent(SentimentPlugin):
|
||||
'''A sample plugin that returns a random sentiment annotation'''
|
||||
name = 'sentiment-random'
|
||||
author = "@balkian"
|
||||
version = '0.1'
|
||||
url = "https://github.com/gsi-upm/senpy-plugins-community"
|
||||
marl__maxPolarityValue = '1'
|
||||
marl__minPolarityValue = "-1"
|
||||
name: str = 'sentiment-random'
|
||||
author: str = "@balkian"
|
||||
version: str = '0.1'
|
||||
url: str = "https://github.com/gsi-upm/senpy-plugins-community"
|
||||
maxPolarityValue: float = 1
|
||||
minPolarityValue: float = -1
|
||||
|
||||
def analyse_entry(self, entry, activity):
|
||||
polarity_value = max(-1, min(1, random.gauss(0.2, 0.2)))
|
||||
|
@@ -22,8 +22,8 @@ from mypipeline import pipeline
|
||||
class PipelineSentiment(SentimentBox):
|
||||
'''This is a pipeline plugin that wraps a classifier defined in another module
|
||||
(mypipeline).'''
|
||||
author = '@balkian'
|
||||
version = 0.1
|
||||
author: str = '@balkian'
|
||||
version: str = 0.1
|
||||
maxPolarityValue = 1
|
||||
minPolarityValue = -1
|
||||
|
||||
@@ -32,7 +32,7 @@ class PipelineSentiment(SentimentBox):
|
||||
return [1, 0, 0]
|
||||
return [0, 0, 1]
|
||||
|
||||
test_cases = [
|
||||
test_cases: list[dict] = [
|
||||
{
|
||||
'input': 'The sentiment for senpy should be positive :)',
|
||||
'polarity': 'marl:Positive'
|
||||
|
@@ -20,10 +20,10 @@ from time import sleep
|
||||
|
||||
class Sleep(AnalysisPlugin):
|
||||
'''Dummy plugin to test async'''
|
||||
author = "@balkian"
|
||||
version = "0.2"
|
||||
author: str = "@balkian"
|
||||
version: str = "0.2"
|
||||
timeout = 0.05
|
||||
extra_params = {
|
||||
extra_params: dict = {
|
||||
"timeout": {
|
||||
"@id": "timeout_sleep",
|
||||
"aliases": ["timeout", "to"],
|
||||
@@ -32,7 +32,8 @@ class Sleep(AnalysisPlugin):
|
||||
}
|
||||
}
|
||||
|
||||
def activate(self, *args, **kwargs):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
sleep(self.timeout)
|
||||
|
||||
def analyse_entry(self, entry, params):
|
||||
|
Reference in New Issue
Block a user