From 83b23dbdf4ead956017e611d5d6dfcbf4ca80c06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=2E=20Fernando=20S=C3=A1nchez?=
Date: Mon, 18 Jun 2018 16:18:58 +0200
Subject: [PATCH] UI improvements
* Add option to add multiple plugins
* Improve UI hints for collapsed parameters
* Refactored plugins without requirements
* Hide evaluation tab for the moment. You can see it by adding "?evaluation" to
the URL.
---
example-plugins/configurable_plugin.py | 1 -
senpy/blueprints.py | 7 +-
senpy/plugins/__init__.py | 2 +-
senpy/plugins/misc/split.senpy | 19 --
.../misc/{split.py => split_plugin.py} | 16 +-
.../sentiment/sentiment140/sentiment140.senpy | 22 --
...sentiment140.py => sentiment140_plugin.py} | 21 +-
senpy/static/css/main.css | 33 +++
senpy/static/js/main.js | 204 ++++++++++++------
senpy/templates/index.html | 49 +++--
tests/test_blueprints.py | 2 +-
11 files changed, 253 insertions(+), 123 deletions(-)
delete mode 100644 senpy/plugins/misc/split.senpy
rename senpy/plugins/misc/{split.py => split_plugin.py} (83%)
delete mode 100644 senpy/plugins/sentiment/sentiment140/sentiment140.senpy
rename senpy/plugins/sentiment/sentiment140/{sentiment140.py => sentiment140_plugin.py} (80%)
diff --git a/example-plugins/configurable_plugin.py b/example-plugins/configurable_plugin.py
index 73af6ee..f857000 100644
--- a/example-plugins/configurable_plugin.py
+++ b/example-plugins/configurable_plugin.py
@@ -43,7 +43,6 @@ class Dictionary(plugins.SentimentPlugin):
class EmojiOnly(Dictionary):
'''Sentiment annotation with a basic lexicon of emojis'''
- description = 'A plugin'
dictionaries = [basic.emojis]
test_cases = [{
diff --git a/senpy/blueprints.py b/senpy/blueprints.py
index d5dec56..26fd330 100644
--- a/senpy/blueprints.py
+++ b/senpy/blueprints.py
@@ -47,7 +47,12 @@ def get_params(req):
@demo_blueprint.route('/')
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/.jsonld')
diff --git a/senpy/plugins/__init__.py b/senpy/plugins/__init__.py
index 1510fd4..314f2fb 100644
--- a/senpy/plugins/__init__.py
+++ b/senpy/plugins/__init__.py
@@ -509,7 +509,7 @@ def install_deps(*plugins):
exitcode = process.wait()
installed = True
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))
diff --git a/senpy/plugins/misc/split.senpy b/senpy/plugins/misc/split.senpy
deleted file mode 100644
index 399d683..0000000
--- a/senpy/plugins/misc/split.senpy
+++ /dev/null
@@ -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
diff --git a/senpy/plugins/misc/split.py b/senpy/plugins/misc/split_plugin.py
similarity index 83%
rename from senpy/plugins/misc/split.py
rename to senpy/plugins/misc/split_plugin.py
index c7cea73..4c11f3a 100644
--- a/senpy/plugins/misc/split.py
+++ b/senpy/plugins/misc/split_plugin.py
@@ -5,13 +5,27 @@ from nltk.tokenize.simple import LineTokenizer
import nltk
-class SplitPlugin(AnalysisPlugin):
+class Split(AnalysisPlugin):
'''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):
nltk.download('punkt')
def analyse_entry(self, entry, params):
+ yield entry
chunker_type = params["delimiter"]
original_text = entry['nif:isString']
if chunker_type == "sentence":
diff --git a/senpy/plugins/sentiment/sentiment140/sentiment140.senpy b/senpy/plugins/sentiment/sentiment140/sentiment140.senpy
deleted file mode 100644
index 2b38283..0000000
--- a/senpy/plugins/sentiment/sentiment140/sentiment140.senpy
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/senpy/plugins/sentiment/sentiment140/sentiment140.py b/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py
similarity index 80%
rename from senpy/plugins/sentiment/sentiment140/sentiment140.py
rename to senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py
index e97e73c..5b3ae71 100644
--- a/senpy/plugins/sentiment/sentiment140/sentiment140.py
+++ b/senpy/plugins/sentiment/sentiment140/sentiment140_plugin.py
@@ -5,8 +5,25 @@ from senpy.plugins import SentimentPlugin
from senpy.models import Sentiment
-class Sentiment140Plugin(SentimentPlugin):
+class Sentiment140(SentimentPlugin):
'''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):
lang = params["language"]
res = requests.post("http://www.sentiment140.com/api/bulkClassifyJson",
@@ -44,7 +61,7 @@ class Sentiment140Plugin(SentimentPlugin):
from senpy.testing import patch_requests
expected = {"data": [{"polarity": 4}]}
with patch_requests(expected) as (request, response):
- super(Sentiment140Plugin, self).test(*args, **kwargs)
+ super(Sentiment140, self).test(*args, **kwargs)
assert request.called
assert response.json.called
diff --git a/senpy/static/css/main.css b/senpy/static/css/main.css
index 7ea452d..ea6cbf2 100644
--- a/senpy/static/css/main.css
+++ b/senpy/static/css/main.css
@@ -167,3 +167,36 @@ textarea{
color: 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); }
+}
diff --git a/senpy/static/js/main.js b/senpy/static/js/main.js
index 2499149..f7cab31 100644
--- a/senpy/static/js/main.js
+++ b/senpy/static/js/main.js
@@ -4,6 +4,7 @@ var plugins_params = default_params = {};
var plugins = [];
var defaultPlugin = {};
var gplugins = {};
+var pipeline = [];
function replaceURLWithHTMLLinks(text) {
console.log('Text: ' + text);
@@ -30,7 +31,10 @@ function hashchanged(){
function get_plugins(response){
- plugins = response.plugins;
+ for(ix in response.plugins){
+ plug = response.plugins[ix];
+ plugins[plug.name] = plug;
+ }
}
function get_datasets(response){
@@ -83,10 +87,32 @@ function draw_plugins_selection(){
html += ""
// Two elements with plugin class
// One from the evaluate tab and another one from the analyse tab
- document.getElementsByClassName('plugin')[0].innerHTML = html;
- document.getElementsByClassName('plugin')[1].innerHTML = html;
+ plugin_lists = document.getElementsByClassName('plugin')
+ 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 += ' ' + plug + ' ';
+ }
+ 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(){
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(){
html = "";
repeated_html = " "+url+""
- document.getElementById("results-div").style.display = 'block';
- try {
- response = JSON.parse(response);
- var options = {
- 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';
- }
+ $.ajax({type: "GET", url: url}).always(function(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();
+ }
+
+ rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
+ document.getElementById("input_request").innerHTML = ""+url+" "
+
+ $(".loading").removeClass("loader");
+ $("#preview").show();
+ });
}
function get_datasets_from_checkbox(){
@@ -347,40 +412,53 @@ function evaluate_JSON(){
url += "?algo="+plugin+"&dataset="+datasets
- var response = $.ajax({type: "GET", url: url , async: false, dataType: 'json'}).responseText;
- rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
-
- document.getElementById("input_request_eval").innerHTML = ""+url+" "
- 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]
- }
+ $('#doevaluate').attr("disabled", true);
+ $.ajax({type: "GET", url: url, dataType: 'json'}).done(function(resp) {
+ $('#doevaluate').attr("disabled", false);
+ response = resp.responseText;
- new_tbody = create_body_metrics(response.evaluations)
- table.replaceChild(new_tbody, table.lastElementChild)
+ rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
- 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';
-
-
- }
- catch(err){
- console.log("Error decoding JSON (got turtle?)");
- $('#evaluate-div a[href="#evaluate-raw"]').click();
- // location.hash = 'raw';
- }
+ document.getElementById("input_request_eval").innerHTML = ""+url+" "
+ 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]
+ }
-}
\ No newline at end of file
+ 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';
+
+
+ }
+ catch(err){
+ console.log("Error decoding JSON (got turtle?)");
+ $('#evaluate-div a[href="#evaluate-raw"]').click();
+ // location.hash = 'raw';
+ }
+ })
+}
+
+function draw_plugin_description(){
+ var plugin = plugins[get_selected_plugin()];
+ $("#plugdescription").text(plugin.description);
+ console.log(plugin);
+}
+
+function plugin_selected(){
+ draw_extra_parameters();
+ draw_plugin_description();
+}
diff --git a/senpy/templates/index.html b/senpy/templates/index.html
index 5d7e4f4..ee6a9a2 100755
--- a/senpy/templates/index.html
+++ b/senpy/templates/index.html
@@ -5,6 +5,9 @@
Playground {{version}}
+
@@ -32,7 +35,9 @@
@@ -61,6 +66,14 @@
+ Senpy is a research project. If you use it in your research, please cite:
+
+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.
+
+
@@ -70,8 +83,6 @@
-
-
@@ -96,17 +104,28 @@
whilst this text makes me happy and surprised at the same time.
I cannot believe it!
-
- Select the plugin:
-
-
-
+
@@ -155,6 +177,8 @@ I cannot believe it!
+ {% if evaluation %}
+
@@ -216,6 +240,7 @@ I cannot believe it!
+ {% endif %}
diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py
index 1698bc8..ec8b28f 100644
--- a/tests/test_blueprints.py
+++ b/tests/test_blueprints.py
@@ -36,7 +36,7 @@ class BlueprintsTest(TestCase):
def test_playground(self):
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):
"""