diff --git a/docs/examples/example-basic.json b/docs/examples/example-basic.json new file mode 100644 index 0000000..c9f427b --- /dev/null +++ b/docs/examples/example-basic.json @@ -0,0 +1,18 @@ +{ + "@context": "http://mixedemotions-project.eu/ns/context.jsonld", + "@id": "http://example.com#NIFExample", + "analysis": [ + ], + "entries": [ + { + "@id": "http://example.org#char=0,40", + "@type": [ + "nif:RFC5147String", + "nif:Context" + ], + "nif:beginIndex": 0, + "nif:endIndex": 40, + "nif:isString": "My favourite actress is Natalie Portman" + } + ] +} diff --git a/docs/examples/example-complete.json b/docs/examples/example-complete.json new file mode 100644 index 0000000..420e7ab --- /dev/null +++ b/docs/examples/example-complete.json @@ -0,0 +1,86 @@ +{ + "@context": "http://mixedemotions-project.eu/ns/context.jsonld", + "@id": "me:Result1", + "analysis": [ + { + "@id": "me:SAnalysis1", + "@type": "marl:SentimentAnalysis", + "marl:maxPolarityValue": 1, + "marl:minPolarityValue": 0 + }, + { + "@id": "me:SgAnalysis1", + "@type": "me:SuggestionAnalysis" + }, + { + "@id": "me:EmotionAnalysis1", + "@type": "me:EmotionAnalysis" + }, + { + "@id": "me:NER1", + "@type": "me:NER" + } + ], + "entries": [ + { + "@id": "http://micro.blog/status1", + "@type": [ + "nif:RFC5147String", + "nif:Context" + ], + "nif:isString": "Dear Microsoft, put your Windows Phone on your newest #open technology program. You'll be awesome. #opensource", + "entities": [ + { + "@id": "http://micro.blog/status1#char=5,13", + "nif:beginIndex": 5, + "nif:endIndex": 13, + "nif:anchorOf": "Microsoft", + "me:references": "http://dbpedia.org/page/Microsoft", + "prov:wasGeneratedBy": "me:NER1" + }, + { + "@id": "http://micro.blog/status1#char=25,37", + "nif:beginIndex": 25, + "nif:endIndex": 37, + "nif:anchorOf": "Windows Phone", + "me:references": "http://dbpedia.org/page/Windows_Phone", + "prov:wasGeneratedBy": "me:NER1" + } + ], + "suggestions": [ + { + "@id": "http://micro.blog/status1#char=16,77", + "nif:beginIndex": 16, + "nif:endIndex": 77, + "nif:anchorOf": "put your Windows Phone on your newest #open technology program" + } + ], + "sentiments": [ + { + "@id": "http://micro.blog/status1#char=80,97", + "nif:beginIndex": 80, + "nif:endIndex": 97, + "nif:anchorOf": "You'll be awesome.", + "marl:hasPolarity": "marl:Positive", + "marl:polarityValue": 0.9, + "prov:wasGeneratedBy": "me:SAnalysis1" + } + ], + "emotions": [ + { + "@id": "http://micro.blog/status1#char=0,109", + "nif:anchorOf": "Dear Microsoft, put your Windows Phone on your newest #open technology program. You'll be awesome. #opensource", + "prov:wasGeneratedBy": "me:EAnalysis1", + "onyx:hasEmotion": [ + { + "onyx:hasEmotionCategory": "wna:liking" + }, + { + "onyx:hasEmotionCategory": "wna:excitement" + } + ] + } + ] + } + ] +} diff --git a/docs/examples/example-emotion.json b/docs/examples/example-emotion.json new file mode 100644 index 0000000..965d91a --- /dev/null +++ b/docs/examples/example-emotion.json @@ -0,0 +1,41 @@ +{ + "@context": "http://mixedemotions-project.eu/ns/context.jsonld", + "@id": "me:Result1", + "analysis": [ + { + "@id": "me:EmotionAnalysis1", + "@type": "onyx:EmotionAnalysis" + } + ], + "entries": [ + { + "@id": "http://micro.blog/status1", + "@type": [ + "nif:RFC5147String", + "nif:Context" + ], + "nif:isString": "Dear Microsoft, put your Windows Phone on your newest #open technology program. You'll be awesome. #opensource", + "entities": [ + ], + "suggestions": [ + ], + "sentiments": [ + ], + "emotions": [ + { + "@id": "http://micro.blog/status1#char=0,109", + "nif:anchorOf": "Dear Microsoft, put your Windows Phone on your newest #open technology program. You'll be awesome. #opensource", + "prov:wasGeneratedBy": "me:EmotionAnalysis1", + "onyx:hasEmotion": [ + { + "onyx:hasEmotionCategory": "wna:liking" + }, + { + "onyx:hasEmotionCategory": "wna:excitement" + } + ] + } + ] + } + ] +} diff --git a/docs/examples/example-ner.json b/docs/examples/example-ner.json new file mode 100644 index 0000000..27535a5 --- /dev/null +++ b/docs/examples/example-ner.json @@ -0,0 +1,44 @@ +{ + "@context": "http://mixedemotions-project.eu/ns/context.jsonld", + "@id": "me:Result1", + "analysis": [ + { + "@id": "me:NER1", + "@type": "me:NERAnalysis" + } + ], + "entries": [ + { + "@id": "http://micro.blog/status1", + "@type": [ + "nif:RFC5147String", + "nif:Context" + ], + "nif:isString": "Dear Microsoft, put your Windows Phone on your newest #open technology program. You'll be awesome. #opensource", + "entities": [ + { + "@id": "http://micro.blog/status1#char=5,13", + "nif:beginIndex": 5, + "nif:endIndex": 13, + "nif:anchorOf": "Microsoft", + "me:references": "http://dbpedia.org/page/Microsoft", + "prov:wasGeneratedBy": "me:NER1" + }, + { + "@id": "http://micro.blog/status1#char=25,37", + "nif:beginIndex": 25, + "nif:endIndex": 37, + "nif:anchorOf": "Windows Phone", + "me:references": "http://dbpedia.org/page/Windows_Phone", + "prov:wasGeneratedBy": "me:NER1" + } + ], + "suggestions": [ + ], + "sentiments": [ + ], + "emotionSets": [ + ] + } + ] +} diff --git a/docs/examples/example-sentiment.json b/docs/examples/example-sentiment.json new file mode 100644 index 0000000..ffdcc60 --- /dev/null +++ b/docs/examples/example-sentiment.json @@ -0,0 +1,39 @@ +{ + "@context": "http://mixedemotions-project.eu/ns/context.jsonld", + "@id": "me:Result1", + "analysis": [ + { + "@id": "me:SAnalysis1", + "@type": "marl:SentimentAnalysis", + "marl:maxPolarityValue": 1, + "marl:minPolarityValue": 0 + } + ], + "entries": [ + { + "@id": "http://micro.blog/status1", + "@type": [ + "nif:RFC5147String", + "nif:Context" + ], + "nif:isString": "Dear Microsoft, put your Windows Phone on your newest #open technology program. You'll be awesome. #opensource", + "entities": [ + ], + "suggestions": [ + ], + "sentiments": [ + { + "@id": "http://micro.blog/status1#char=80,97", + "nif:beginIndex": 80, + "nif:endIndex": 97, + "nif:anchorOf": "You'll be awesome.", + "marl:hasPolarity": "marl:Positive", + "marl:polarityValue": 0.9, + "prov:wasGeneratedBy": "me:SAnalysis1" + } + ], + "emotionSets": [ + ] + } + ] +} diff --git a/docs/examples/example-suggestion.json b/docs/examples/example-suggestion.json new file mode 100644 index 0000000..339be64 --- /dev/null +++ b/docs/examples/example-suggestion.json @@ -0,0 +1,35 @@ +{ + "@context": "http://mixedemotions-project.eu/ns/context.jsonld", + "@id": "me:Result1", + "analysis": [ + { + "@id": "me:SgAnalysis1", + "@type": "me:SuggestionAnalysis" + } + ], + "entries": [ + { + "@id": "http://micro.blog/status1", + "@type": [ + "nif:RFC5147String", + "nif:Context" + ], + "prov:wasGeneratedBy": "me:SAnalysis1", + "nif:isString": "Dear Microsoft, put your Windows Phone on your newest #open technology program. You'll be awesome. #opensource", + "entities": [ + ], + "suggestions": [ + { + "@id": "http://micro.blog/status1#char=16,77", + "nif:beginIndex": 16, + "nif:endIndex": 77, + "nif:anchorOf": "put your Windows Phone on your newest #open technology program" + } + ], + "sentiments": [ + ], + "emotionSets": [ + ] + } + ] +} diff --git a/docs/index.rst b/docs/index.rst index 70ae802..5a380b7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,9 +9,11 @@ Welcome to Senpy's documentation! Contents: .. toctree:: + senpy installation usage api + schema plugins demo :maxdepth: 2 diff --git a/docs/plugins.rst b/docs/plugins.rst index e2f35d1..2632cdd 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -1,31 +1,32 @@ Developing new plugins ---------------------- +Each plugin represents a different analysis process.There are two types of files that are needed by senpy for loading a plugin: -There are two types of files that were needed by senpy for loading a plugin: +- Definition file, has the ".senpy" extension. +- Code file, is a python file. -- *.senpy: this file is the builder of the plugin. -- *.py: this file is the interface of the plugin. +Plugins Definitions +=================== -Plugins Builder -================ - -The structure of this files is similar to a python dictionary, where the data representation consists on attribute-value pairs. +The definition file can be written in JSON or YAML, where the data representation consists on attribute-value pairs. The principal attributes are: * name: plugin name used in senpy to call the plugin. -* module: name of the file where the interface is written (*.py) +* module: indicates the module that will be loaded .. code:: python { "name" : "senpyPlugin", - "module" : "{python file}" + "module" : "{python code file}" } -You can use another attributes such as `description`, `author`, `version`, etc. - +.. code:: python + + name: senpyPlugin + module: {python code file} -Plugins Interface +Plugins Code ================= The basic methods in a plugin are: @@ -67,15 +68,57 @@ Training a classifier can be time time consuming. To avoid running the training You can speficy a 'shelf_file' in your .senpy file. By default the ShelfMixin creates a file based on the plugin name and stores it in that plugin's folder. +I want to implement my service as a plugin, How i can do it? +???????????????????????????????????????????????????????????? + +This example ilustrate how to implement the Sentiment140 service as a plugin in senpy + +.. code:: python + + class Sentiment140Plugin(SentimentPlugin): + def analyse(self, **params): + lang = params.get("language", "auto") + res = requests.post("http://www.sentiment140.com/api/bulkClassifyJson", + json.dumps({"language": lang, + "data": [{"text": params["input"]}] + } + ) + ) + + p = params.get("prefix", None) + response = Results(prefix=p) + polarity_value = self.maxPolarityValue*int(res.json()["data"][0] + ["polarity"]) * 0.25 + polarity = "marl:Neutral" + neutral_value = self.maxPolarityValue / 2.0 + if polarity_value > neutral_value: + polarity = "marl:Positive" + elif polarity_value < neutral_value: + polarity = "marl:Negative" + + entry = Entry(id="Entry0", + nif__isString=params["input"]) + sentiment = Sentiment(id="Sentiment0", + prefix=p, + marl__hasPolarity=polarity, + marl__polarityValue=polarity_value) + sentiment.prov__wasGeneratedBy = self.id + entry.sentiments = [] + entry.sentiments.append(sentiment) + entry.language = lang + response.entries.append(entry) + return response + + Where can I define extra parameters to be introduced in the request to my plugin? ????????????????????????????????????????????????????????????????????????????????? -You can add these parameters in the *.senpy file under the attribute "extra_params" : "{param_name}". The name of the parameter is going to act as another python dictionary with the next attributes: +You can add these parameters in the definition file under the attribute "extra_params" : "{param_name}". The name of the parameter has new attributes-value pairs. The basic attributes are: * aliases: the different names which can be used in the request to use the parameter. * required: this option is a boolean and indicates if the parameters is binding in operation plugin. * options: the different values of the paremeter. -* default: the default value which can have the parameter, this is useful in case the paremeter is required and you want to have a default value. +* default: the default value of the parameter, this is useful in case the paremeter is required and you want to have a default value. .. code:: python @@ -83,12 +126,12 @@ You can add these parameters in the *.senpy file under the attribute "extra_para "language": { "aliases": ["language", "l"], "required": true, - "options": ["es"], + "options": ["es","en"], "default": "es" } } -This example shows how to introduce a parameter associated language. +This example shows how to introduce a parameter associated with language. The extraction of this paremeter is used in the analyse method of the Plugin interface. .. code:: python @@ -98,7 +141,7 @@ The extraction of this paremeter is used in the analyse method of the Plugin int Where can I set up variables for using them in my plugin? ????????????????????????????????????????????????????????? -You can add these variables in the *.senpy with: {variable_name} : {variable_value}. +You can add these variables in the definition file with the extracture of attribute-value pair. Once you have added your variables, the next step is to extract them into the plugin. The plugin's __init__ method has a parameter called `info` where you can extract the values of the variables. This info parameter has the structure of a python dictionary. diff --git a/docs/schema.rst b/docs/schema.rst new file mode 100644 index 0000000..62879c3 --- /dev/null +++ b/docs/schema.rst @@ -0,0 +1,74 @@ +Schema Examples +=============== +All the examples in this page use the schema defined in :ref:`schema`. + +Simple NIF annotation +--------------------- +Description +........... +This example covers the basic example in the NIF documentation: ``_. + +Representation +.............. +.. literalinclude:: examples/example-basic.json + :language: json-ld + +Sentiment Analysis +--------------------- +Description +........... + +Representation +.............. + +.. literalinclude:: examples/example-sentiment.json + :emphasize-lines: 5-10,25-33 + :language: json-ld + +Suggestion Mining +----------------- +Description +........... + +Representation +.............. + +.. literalinclude:: examples/example-suggestion.json + :emphasize-lines: 5-8,22-27 + :language: json-ld + +Emotion Analysis +---------------- +Description +........... + +Representation +.............. + +.. literalinclude:: examples/example-emotion.json + :language: json-ld + :emphasize-lines: 5-8,25-37 + +Named Entity Recognition +------------------------ +Description +........... + +Representation +.............. + +.. literalinclude:: examples/example-ner.json + :emphasize-lines: 5-8,19-34 + :language: json-ld + +Complete example +---------------- +Description +........... +This example covers all of the above cases, integrating all the annotations in the same document. + +Representation +.............. + +.. literalinclude:: examples/example-complete.json + :language: json-ld diff --git a/docs/senpy-architecture.png b/docs/senpy-architecture.png new file mode 100644 index 0000000..dd1b642 Binary files /dev/null and b/docs/senpy-architecture.png differ diff --git a/docs/senpy.rst b/docs/senpy.rst new file mode 100644 index 0000000..bbf4a1d --- /dev/null +++ b/docs/senpy.rst @@ -0,0 +1,35 @@ +What is Senpy? +-------------- + +Senpy is an open source reference implementation of a linked data model for sentiment and emotion analysis services based on the vocabularies NIF, Marl and Onyx. + +The overall goal of the reference implementation Senpy is easing the adoption of the proposed linked data model for sentiment and emotion analysis services, so that services from different providers become interoperable. With this aim, the design of the reference implementation has focused on its extensibility and reusability. + +A modular approach allows organizations to replace individual components with custom ones developed in-house. Furthermore, organizations can benefit from reusing prepackages modules that provide advanced functionalities, such as algorithms for sentiment and emotion analysis, linked data publication or emotion and sentiment mapping between different providers. + +Specifications +============== + +The model used in Senpy is based on the following specifications: + +* Marl, a vocabulary designed to annotate and describe subjetive opinions expressed on the web or in information systems. +* Onyx, which is built one the same principles as Marl to annotate and describe emotions, and provides interoperability with Emotion Markup Language. +* NIF 2.0, which defines a semantic format and APO for improving interoperability among natural language processing services + +Architecture +============ + +The main component of a sentiment analysis service is the algorithm itself. However, for the algorithm to work, it needs to get the appropriate parameters from the user, format the results according to the defined API, interact with the user whn errors occur or more information is needed, etc. + +Senpy proposes a modular and dynamic architecture that allows: + +* Implementing different algorithms in a extensible way, yet offering a common interface. +* Offering common services that facilitate development, so developers can focus on implementing new and better algorithms. + +The framework consists of two main modules: Senpy core, which is the building block of the service, and Senpy plugins, which consist of the analysis algorithm. The next figure depicts a simplified version of the processes involved in an analysis with the Senpy framework. + +.. image:: senpy-architecture.png + :height: 400px + :width: 800px + :scale: 100 % + :align: center diff --git a/docs/usage.rst b/docs/usage.rst index e947446..3d510c2 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -19,7 +19,7 @@ This will create a server with any modules found in the current path. Useful command-line options =========================== -In case you want to load modules that are in different folders under the same path, use the next option. +In case you want to load modules, which are located in different folders under the root folder, use the next option. .. code:: bash @@ -35,7 +35,7 @@ Also, the host can be changed where senpy is deployed. The default value is `127 .. code:: bash - python -m senpy --host 125.1.2.3 + python -m senpy --host 0.0.0.0 For more options, see the `--help` page. @@ -46,4 +46,30 @@ Senpy server Once the server is launched, there is a basic endpoint in the server, which provides a playground to use the plugins that have been loaded. -In case you want to know the different endpoints of the server, there is more information available in the **NIF API setion**. +In case you want to know the different endpoints of the server, there is more information available in the NIF API section_. + +Video example +============= + +This video shows how to use senpy through command-line tool. + +https://asciinema.org/a/9uwef1ghkjk062cw2t4mhzpyk + +Request example in python +========================= + +This example shows how to make a request to a plugin. + +.. code:: python + + import requests + import json + + r = requests.get('http://127.0.0.1:5000/api/?algo=rand&i=Testing') + response = r.content.decode('utf-8') + response_json = json.loads(response) + + + +.. _section: http://senpy.readthedocs.org/en/latest/api.html +