Commit 83b23dbd authored by J. Fernando Sánchez's avatar J. Fernando Sánchez
Browse files

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.
parent 4675d9ac
...@@ -43,7 +43,6 @@ class Dictionary(plugins.SentimentPlugin): ...@@ -43,7 +43,6 @@ class Dictionary(plugins.SentimentPlugin):
class EmojiOnly(Dictionary): class EmojiOnly(Dictionary):
'''Sentiment annotation with a basic lexicon of emojis''' '''Sentiment annotation with a basic lexicon of emojis'''
description = 'A plugin'
dictionaries = [basic.emojis] dictionaries = [basic.emojis]
test_cases = [{ test_cases = [{
......
...@@ -47,7 +47,12 @@ def get_params(req): ...@@ -47,7 +47,12 @@ def get_params(req):
@demo_blueprint.route('/') @demo_blueprint.route('/')
def index(): 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/<entity>.jsonld') @api_blueprint.route('/contexts/<entity>.jsonld')
......
...@@ -509,7 +509,7 @@ def install_deps(*plugins): ...@@ -509,7 +509,7 @@ def install_deps(*plugins):
exitcode = process.wait() exitcode = process.wait()
installed = True installed = True
if exitcode != 0: 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', [])) nltk_resources |= set(info.get('nltk_resources', []))
installed |= nltk.download(list(nltk_resources)) installed |= nltk.download(list(nltk_resources))
......
---
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
...@@ -5,13 +5,27 @@ from nltk.tokenize.simple import LineTokenizer ...@@ -5,13 +5,27 @@ from nltk.tokenize.simple import LineTokenizer
import nltk import nltk
class SplitPlugin(AnalysisPlugin): class Split(AnalysisPlugin):
'''description: A sample plugin that chunks input text''' '''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): def activate(self):
nltk.download('punkt') nltk.download('punkt')
def analyse_entry(self, entry, params): def analyse_entry(self, entry, params):
yield entry
chunker_type = params["delimiter"] chunker_type = params["delimiter"]
original_text = entry['nif:isString'] original_text = entry['nif:isString']
if chunker_type == "sentence": if chunker_type == "sentence":
......
---
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
...@@ -5,8 +5,25 @@ from senpy.plugins import SentimentPlugin ...@@ -5,8 +5,25 @@ from senpy.plugins import SentimentPlugin
from senpy.models import Sentiment from senpy.models import Sentiment
class Sentiment140Plugin(SentimentPlugin): class Sentiment140(SentimentPlugin):
'''Connects to the sentiment140 free API: http://sentiment140.com''' '''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): def analyse_entry(self, entry, params):
lang = params["language"] lang = params["language"]
res = requests.post("http://www.sentiment140.com/api/bulkClassifyJson", res = requests.post("http://www.sentiment140.com/api/bulkClassifyJson",
...@@ -44,7 +61,7 @@ class Sentiment140Plugin(SentimentPlugin): ...@@ -44,7 +61,7 @@ class Sentiment140Plugin(SentimentPlugin):
from senpy.testing import patch_requests from senpy.testing import patch_requests
expected = {"data": [{"polarity": 4}]} expected = {"data": [{"polarity": 4}]}
with patch_requests(expected) as (request, response): with patch_requests(expected) as (request, response):
super(Sentiment140Plugin, self).test(*args, **kwargs) super(Sentiment140, self).test(*args, **kwargs)
assert request.called assert request.called
assert response.json.called assert response.json.called
......
...@@ -167,3 +167,36 @@ textarea{ ...@@ -167,3 +167,36 @@ textarea{
color: inherit; color: inherit;
text-decoration: 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); }
}
...@@ -4,6 +4,7 @@ var plugins_params = default_params = {}; ...@@ -4,6 +4,7 @@ var plugins_params = default_params = {};
var plugins = []; var plugins = [];
var defaultPlugin = {}; var defaultPlugin = {};
var gplugins = {}; var gplugins = {};
var pipeline = [];
function replaceURLWithHTMLLinks(text) { function replaceURLWithHTMLLinks(text) {
console.log('Text: ' + text); console.log('Text: ' + text);
...@@ -30,7 +31,10 @@ function hashchanged(){ ...@@ -30,7 +31,10 @@ function hashchanged(){
function get_plugins(response){ function get_plugins(response){
plugins = response.plugins; for(ix in response.plugins){
plug = response.plugins[ix];
plugins[plug.name] = plug;
}
} }
function get_datasets(response){ function get_datasets(response){
...@@ -83,10 +87,32 @@ function draw_plugins_selection(){ ...@@ -83,10 +87,32 @@ function draw_plugins_selection(){
html += "</optgroup>" html += "</optgroup>"
// Two elements with plugin class // Two elements with plugin class
// One from the evaluate tab and another one from the analyse tab // One from the evaluate tab and another one from the analyse tab
document.getElementsByClassName('plugin')[0].innerHTML = html; plugin_lists = document.getElementsByClassName('plugin')
document.getElementsByClassName('plugin')[1].innerHTML = html; 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 += '<span onclick="remove_plugin_pipeline(\'' + plug + '\')" class="btn btn-primary"><span ><i class="fa fa-minus"></i></span> ' + plug + '</span> <i class="fa fa-arrow-right"></i> ';
}
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(){ function draw_plugins_list(){
var availablePlugins = document.getElementById('availablePlugins'); var availablePlugins = document.getElementById('availablePlugins');
...@@ -105,6 +131,13 @@ function draw_plugins_list(){ ...@@ -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(){ function draw_datasets(){
html = ""; html = "";
repeated_html = "<input class=\"checks-datasets\" type=\"checkbox\" value=\""; repeated_html = "<input class=\"checks-datasets\" type=\"checkbox\" value=\"";
...@@ -118,16 +151,20 @@ function draw_datasets(){ ...@@ -118,16 +151,20 @@ function draw_datasets(){
$(document).ready(function() { $(document).ready(function() {
var response = JSON.parse($.ajax({type: "GET", url: "/api/plugins/" , async: false}).responseText); var response = JSON.parse($.ajax({type: "GET", url: "/api/plugins/" , async: false}).responseText);
defaultPlugin= JSON.parse($.ajax({type: "GET", url: "/api/plugins/default" , async: false}).responseText); defaultPlugin= JSON.parse($.ajax({type: "GET", url: "/api/plugins/default" , async: false}).responseText);
var response2 = JSON.parse($.ajax({type: "GET", url: "/api/datasets/" , async: false}).responseText);
get_plugins(response); get_plugins(response);
get_default_parameters(); get_default_parameters();
get_datasets(response2);
draw_plugins_list(); draw_plugins_list();
draw_plugins_selection(); draw_plugins_selection();
draw_parameters(); draw_parameters();
draw_datasets(); draw_plugin_description();
if (evaluation_enabled) {
var response2 = JSON.parse($.ajax({type: "GET", url: "/api/datasets/" , async: false}).responseText);
get_datasets(response2);
draw_datasets();
}
$(window).on('hashchange', hashchanged); $(window).on('hashchange', hashchanged);
hashchanged(); hashchanged();
...@@ -144,17 +181,34 @@ function get_default_parameters(){ ...@@ -144,17 +181,34 @@ function get_default_parameters(){
} }
function get_selected_plugin(){
return document.getElementsByClassName('plugin')[0].options[document.getElementsByClassName('plugin')[0].selectedIndex].value;
}
function draw_default_parameters(){ function draw_default_parameters(){
var basic_params = document.getElementById("basic_params"); var basic_params = document.getElementById("basic_params");
basic_params.innerHTML = params_div(default_params); basic_params.innerHTML = params_div(default_params);
} }
function update_params(params, plug){
ep = plugins_params[plug];
for(k in ep){
params[k] = ep[k];
}
return params
}
function draw_extra_parameters(){ function draw_extra_parameters(){
var plugin = document.getElementsByClassName('plugin')[0].options[document.getElementsByClassName('plugin')[0].selectedIndex].value; var plugin = get_selected_plugin();
get_parameters(); get_parameters();
var extra_params = document.getElementById("extra_params"); var extra_params = document.getElementById("extra_params");
extra_params.innerHTML = params_div(plugins_params[plugin]); var params = {};
for (sel in pipeline){
update_params(params, pipeline[sel]);
}
update_params(params, plugin);
extra_params.innerHTML = params_div(params);
} }
function draw_parameters(){ function draw_parameters(){
...@@ -261,6 +315,15 @@ function add_param(key, value){ ...@@ -261,6 +315,15 @@ function add_param(key, value){
return "&"+key+"="+value; return "&"+key+"="+value;
} }
function get_pipeline_arg(){
arg = "";
for (ix in pipeline){
arg = arg + pipeline[ix] + ",";
}
arg = arg + get_selected_plugin();
return arg;
}
function load_JSON(){ function load_JSON(){
url = "/api"; url = "/api";
...@@ -269,7 +332,9 @@ function load_JSON(){ ...@@ -269,7 +332,9 @@ function load_JSON(){
rawcontainer.innerHTML = ''; rawcontainer.innerHTML = '';
container.innerHTML = ''; container.innerHTML = '';
var plugin = document.getElementsByClassName("plugin")[0].options[document.getElementsByClassName("plugin")[0].selectedIndex].value; var plugin = get_pipeline_arg();
$(".loading").addClass("loader");
$("#preview").hide();
var input = encodeURIComponent(document.getElementById("input").value); var input = encodeURIComponent(document.getElementById("input").value);
url += "?algo="+plugin+"&i="+input url += "?algo="+plugin+"&i="+input
...@@ -280,27 +345,27 @@ function load_JSON(){ ...@@ -280,27 +345,27 @@ function load_JSON(){
url += add_param(key, params[key]); url += add_param(key, params[key]);
} }
var response = $.ajax({type: "GET", url: url , async: false}).responseText; $.ajax({type: "GET", url: url}).always(function(response){
rawcontainer.innerHTML = replaceURLWithHTMLLinks(response); document.getElementById("results-div").style.display = 'block';
if(typeof response=="object") {
document.getElementById("input_request").innerHTML = "<a href='"+url+"'>"+url+"</a>" var options = {
document.getElementById("results-div").style.display = 'block'; mode: 'view'
try { };
response = JSON.parse(response); var editor = new JSONEditor(container, options, response);
var options = { editor.expandAll();
mode: 'view' $('#results-div a[href="#viewer"]').click();
}; response = JSON.stringify(response, null, 4);
var editor = new JSONEditor(container, options, response); } else {
editor.expandAll(); console.log("Got turtle?");
// $('#results-div a[href="#viewer"]').tab('show'); $('#results-div a[href="#raw"]').click();
$('#results-div a[href="#viewer"]').click(); }
// location.hash = 'raw';
} rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
catch(err){ document.getElementById("input_request").innerHTML = "<a href='"+url+"'>"+url+"</a>"
console.log("Error decoding JSON (got turtle?)");
$('#results-div a[href="#raw"]').click(); $(".loading").removeClass("loader");
// location.hash = 'raw'; $("#preview").show();
} });
} }
function get_datasets_from_checkbox(){ function get_datasets_from_checkbox(){
...@@ -347,40 +412,53 @@ function evaluate_JSON(){ ...@@ -347,40 +412,53 @@ function evaluate_JSON(){
url += "?algo="+plugin+"&dataset="+datasets url += "?algo="+plugin+"&dataset="+datasets
var response = $.ajax({type: "GET", url: url , async: false, dataType: 'json'}).responseText; $('#doevaluate').attr("disabled", true);
rawcontainer.innerHTML = replaceURLWithHTMLLinks(response); $.ajax({type: "GET", url: url, dataType: 'json'}).done(function(resp) {
$('#doevaluate').attr("disabled", false);
document.getElementById("input_request_eval").innerHTML = "<a href='"+url+"'>"+url+"</a>" response = resp.responseText;
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]
}
new_tbody = create_body_metrics(response.evaluations) rawcontainer.innerHTML = replaceURLWithHTMLLinks(response);
table.replaceChild(new_tbody, table.lastElementChild)
var editor = new JSONEditor(container, options, response); document.getElementById("input_request_eval").innerHTML = "<a href='"+url+"'>"+url+"</a>"
editor.expandAll(); document.getElementById("evaluate-div").style.display = 'block';
// $('#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';
}
try {
response = JSON.parse(response);
var options = {
mode: 'view'
};
//Control the single response results
if (!(Array.isArray(response.evaluations))){
response.evaluations = [response.evaluations]
}
} new_tbody = create_body_metrics(response.evaluations)
\ No newline at end of file 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();
}
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
<title>Playground {{version}}</title> <title>Playground {{version}}</title>
</head> </head>
<script>
this.evaluation_enabled = {% if evaluation %}true{%else %}false{%endif%};
</script>
<script src="static/js/jquery-2.1.1.min.js" ></script> <script src="static/js/jquery-2.1.1.min.js" ></script>
<!--<script src="jquery.autosize.min.js"></script>--> <!--<script src="jquery.autosize.min.js"></script>-->
<link rel="stylesheet" href="static/css/bootstrap.min.css"> <link rel="stylesheet" href="static/css/bootstrap.min.css">
...@@ -32,7 +35,9 @@ ...@@ -32,7 +35,9 @@
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" ><a class="active" href="#about">About</a></li> <li role="presentation" ><a class="active" href="#about">About</a></li>
<li role="presentation"class="active"><a class="active" href="#test">Test it</a></li> <li role="presentation"class="active"><a class="active" href="#test">Test it</a></li>
{% if evaluation %}
<li role="presentation"><a class="active" href="#evaluate">Evaluate Plugins</a></li> <li role="presentation"><a class="active" href="#evaluate">Evaluate Plugins</a></li>
{% endif %}
</ul> </ul>
...@@ -61,6 +66,14 @@ ...@@ -61,6 +66,14 @@
</ul> </ul>
</p> </p>
<p>Senpy is a research project. If you use it in your research, please cite:
<pre>
Senpy: A Pragmatic Linked Sentiment Analysis Framework.
Sánchez-Rada, J. F., Iglesias, C. A., Corcuera, I., & Araque, Ó.