From c20b9fb92b56c7818a4f24b22eead665b1dd1143 Mon Sep 17 00:00:00 2001 From: gebele Date: Thu, 17 Sep 2015 16:46:32 +0200 Subject: save stadium --- application.rb | 59 +++++++++---- views/batch.haml | 39 +++++++++ views/layout.haml | 1 + views/predict.haml | 225 +++++++++++++++++++++++++++++++++++++++++-------- views/style.scss | 240 +++-------------------------------------------------- 5 files changed, 286 insertions(+), 278 deletions(-) create mode 100644 views/batch.haml diff --git a/application.rb b/application.rb index acf3c73..0214db4 100644 --- a/application.rb +++ b/application.rb @@ -6,6 +6,10 @@ include OpenTox # Date: 18/11/2013 set :protection, :except => :path_traversal +configure :development do + $logger = Logger.new(STDOUT) +end + helpers do class Numeric def percent_of(n) @@ -195,24 +199,49 @@ get '/predict/:dataset/?' do end post '/predict/?' do + + # process batch prediction + if !params[:fileselect].blank? + File.open('tmp/' + params[:fileselect][:filename], "w") do |f| + f.write(params[:fileselect][:tempfile].read) + end + input = OpenTox::Dataset.from_csv_file File.join "tmp", params[:fileselect][:filename] + dataset = OpenTox::Dataset.find input.id + @compounds = dataset.compounds + @models = [] + @predictions = [] + @compounds.each do |compound| + params[:selection].keys.each do |model_id| + model = Model::Prediction.find model_id + @models << model + @predictions << model.predict(compound) + end + end + input.delete + return haml :batch + end + # validate identifier input # transfered input - @identifier = params[:identifier] - begin - # get compound from SMILES - @compound = Compound.from_smiles @identifier - rescue - @error_report = "Attention, '#{params[:identifier]}' is not a valid SMILES string." - return haml :error - end - @models = [] - @predictions = [] - params[:selection].keys.each do |model_id| - model = Model::Prediction.find model_id - @models << model - @predictions << model.predict(@compound) + if !params[:identifier].blank? + @identifier = params[:identifier] + begin + # get compound from SMILES + @compound = Compound.from_smiles @identifier + rescue + @error_report = "Attention, '#{params[:identifier]}' is not a valid SMILES string." + return haml :error + end + + @models = [] + @predictions = [] + params[:selection].keys.each do |model_id| + model = Model::Prediction.find model_id + @models << model + @predictions << model.predict(@compound) + end + haml :prediction end - haml :prediction end get '/style.css' do diff --git a/views/batch.haml b/views/batch.haml new file mode 100644 index 0000000..5bae411 --- /dev/null +++ b/views/batch.haml @@ -0,0 +1,39 @@ +/ displays all prediction result in first table +%div.table-responsive + %table.table.table-bordered{:id=>"batch", :style=>"background-color:white;"} + %thead + %tr + %h3 Batch Prediction Results: + + %tbody + - @compounds.each_with_index do |compound,i| + %tr + %td{:id=>"compound", :style=>"vertical-align:top;"} + %p= compound.svg + %p= compound.smiles + - @predictions.each_with_index do |prediction,i| + %td{:style=>"vertical-align:top;"} + %b{:class => "title"} + = "#{@models[i].endpoint.gsub('_', ' ')} (#{@models[i].species})" + %p + - if prediction[:confidence] == "measured" + %p + / TODO fix scientific notation from database + %b Measured activity: + = prediction[:value].numeric? ? "#{prediction[:value].round(3)} (#{@models[i].unit})" : prediction[:value] + %p Compound is part of the training dataset + - elsif prediction[:neighbors].size > 0 + %p + / model type (classification|regression) + %b Type: + = @models[i].model.class.to_s.match("Classification") ? "Classification" : "Regression" + %br + %b Prediction: + / TODO scientific notation + = prediction[:value].numeric? ? "#{'%.2e' % prediction[:value]} #{@models[i].unit}" : prediction[:value] + %br + / TODO probability + %b Confidence: + = prediction[:confidence].round(3) + %p + %p diff --git a/views/layout.haml b/views/layout.haml index 974a8ed..60f2bb3 100644 --- a/views/layout.haml +++ b/views/layout.haml @@ -9,6 +9,7 @@ %link{:rel=>'stylesheet', :href=>"#{'/css/bootstrap.min.css'}"} %link{:rel=>'stylesheet', :href=>"#{'/css/theme.default.min.css'}"} %link{:rel=>'stylesheet', :href=>"#{'/css/theme.bootstrap.min.css'}"} + %link{ :href=>"/style.css", :rel=>"stylesheet"} %link{ :href=>"/stylesheets/jquery-ui.css", :rel=>"stylesheet"} %script{:src=>"/javascripts/jquery-1.11.2.min.js"} %script{:src=>"/javascripts/bootstrap.min.js"} diff --git a/views/predict.haml b/views/predict.haml index 77f16bc..917a04a 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -1,21 +1,58 @@ %link{ :href=>"/jsme/jsa.css", :rel=>"stylesheet"} %script{:src=>"/jsme/jsme.nocache.js"} :javascript + function getTab(){ + t = $('.nav-tabs li.active').text(); + return t.trim(); + }; function showcircle() { - if (checksmiles() && checkboxes()){ + /*if (checksmiles() && checkboxes()){ button = document.getElementById("submit"); image = document.getElementById("circle"); button.parentNode.replaceChild(image, button); $("img.circle").show(); return true; }; + return false;*/ + switch (getTab()){ + case "single": + if (checksmiles() && checkboxes()){ + button = document.getElementById("submit"); + image = document.getElementById("circle"); + button.parentNode.replaceChild(image, button); + $("img.circle").show(); + return true; + }; + return false; + break; + case "batch": + if (checkfile() && checkboxes()){ + button = document.getElementById("submit"); + image = document.getElementById("circle"); + button.parentNode.replaceChild(image, button); + $("img.circle").show(); + return true; + }; + return false; + break; + default: false; + }; + return false; + }; + function checkfile() { + var fileinput = document.getElementById("fileselect"); + if(fileinput.value != "") { + //TODO check file type is csv + return true; + }; + alert("Please select a file (csv)."); return false; }; function checksmiles () { + getsmiles(); if (document.form.identifier.value == "") { alert("Please draw or insert a chemical structure."); document.form.identifier.focus(); - //$('.progress-bar').hide(); $("img.circle").hide(); return false; }; @@ -48,49 +85,171 @@ }; }; -// init task for progress - // whole site content needs to be in one form. Input and checkboxes are proofed by js functions. %form{:name => "form", :action => to('/predict'), :method => "post", :enctype => "multipart/form-data", :onsubmit => "return !!(showcircle())" } %fieldset#top.well - %h2 1. Draw a chemical structure - :javascript - $("a#linkInsert").click(function () { - $("#insert").toggle(); - document.location = document.location + "#" + "insert"; - }); - #insert - %p - %label   - #appletContainer - %br - %label{:for => 'identifier'} - or enter the - %a{:href => "http://en.wikipedia.org/wiki/Simplified_molecular_input_line_entry_specification", :rel => "external"} SMILES - string: - %br - %input{:type => 'text', :name => 'identifier', :id => 'identifier', :size => '60'} + // tabs to select single prediction or batch prediction + %ul.nav.nav-tabs{:role=>"tablist"} + - ["single", "batch"].each do |select| + %li{:class => ("active" if select == "single")} + %a{:href => "div##{select}", :id => "linkTab#{select}", :data=> {:toggle=>"tab"}} + = select + + %div.tab-content + #single.tab-pane.fade-in.active + %h2 1. Draw a chemical structure + #insert + %p + %label   + #appletContainer + %br + %label{:for => 'identifier'} + or enter the + %a{:href => "http://en.wikipedia.org/wiki/Simplified_molecular_input_line_entry_specification", :rel => "external"} SMILES + string: + %br + %input{:type => 'text', :name => 'identifier', :id => 'identifier', :size => '60'} + + #batch.tab-pane.fade-in + %h2 1. Select a file to upload + Browse (csv): + %span.btn.btn-default.btn-file + %input{:type=>"file", :name=> "fileselect", :id=>"fileselect"} %fieldset#middle.well %h2 2. Select one or more endpoints - - :javascript - $("a#linkModels").click(function () { - $("#models").toggle(); - document.location = document.location + "#" + "models"; - }); - #models - @endpoints.each do |endpoint| - %div - %b= endpoint + %div{:id=>endpoint} + %h4.head-back=endpoint - @models.select{|m| m.endpoint == endpoint}.each do |model| - %div{:id => model.id} - %input{:type => "checkbox", :name => "selection[#{model.id}]", :id => "selection[#{model.id}]", :value => true, :disabled => false} + %div.row{:id => model.id} + %span.col-sm-4 + %input{:type => "checkbox", :name => "selection[#{model.id}]", :id => "selection[#{model.id}]", :value => true, :disabled => false} %label{:for => "selection[#{model.id}]"} = model.species - %p + %span.col-sm-8 + %a.btn.btn-default.btn-xs{:data=>{:toggle=>"collapse"}, :href=>"#details#{model.id}", :id => "link#{model.id}", :style=>"font-size:small;"} + Details | Validation + %div.panel-collapse.collapse{:id=>"details#{model.id}", :style=>"margin-left:1em;"} + %b Model: + = model.id + %br + = "Algorithm:\tLAZAR" + %br + - model.classification? ? type = "classification" : type = "regression" + = "Type:\t" + = type + %br + - training_dataset = OpenTox::Dataset.find model.training_dataset.id + = "Training dataset:\t" + = training_dataset.source.split("/").last + %br + = "Training compounds:\t" + = training_dataset.compounds.size + + + %p + %b Validation: + %br + - cv = OpenTox::CrossValidation.find model.crossvalidation.id + = "Num folds:\t" + = cv.folds + %br + = "Num instances:\t" + = cv.nr_instances + %br + = "Num unpredicted" + = cv.nr_unpredicted + - if model.classification? + %br + = "Accuracy:\t" + = cv.accuracy.round(3) + %br + = "Weighted Accuracy:\t" + = cv.weighted_accuracy.round(3) + %br + = "True positive rate:\t" + = cv.true_rate["active"].round(3) + %br + = "True negative rate:\t" + = cv.true_rate["inactive"].round(3) + %br + = "Positive predictive value:\t" + = cv.predictivity["active"].round(3) + %br + = "Negative predictive value:\t" + = cv.predictivity["inactive"].round(3) + %p + %b Confusion Matrix: + %table.table.table-condensed.table-borderless{:style=>"width:20%;"} + %tbody + %tr + %td + %td + %td + %b actual + %td + %td + %tr + %td + %td + %td active + %td inactive + %td total + %tr + %td + %b predicted + %td active + %td + =cv.confusion_matrix[0][0] + %td + =cv.confusion_matrix[0][1] + %td + =cv.confusion_matrix[0][0]+cv.confusion_matrix[0][1] + %tr + %td + %td inactive + %td + =cv.confusion_matrix[1][0] + %td + =cv.confusion_matrix[1][1] + %td + =cv.confusion_matrix[1][0]+cv.confusion_matrix[1][1] + %tr + %td + %td total + %td + =cv.confusion_matrix[0][0]+cv.confusion_matrix[1][0] + %td + =cv.confusion_matrix[0][1]+cv.confusion_matrix[1][1] + %td + -#= "Confusion Matrix:\t" + -#= cv.confusion_matrix + - if model.regression? + %br + = "Root mean squared error:\t" + = cv.rmse.round(3) + %br + = "Weighted root mean squared error:\t" + = cv.weighted_rmse.round(3) + %br + = "Mean absolute error:\t" + = cv.mae.round(3) + %br + = "Weighted mean absolute error:\t" + = cv.weighted_mae.round(3) + %br + = "R square:\t" + = cv.r_squared.round(3) + %br + -#= "Correlation plot" + -#= cv.correlation_plot + %br + -#= "Confidence plot:" + -#= cv.confidence_plot + %hr %fieldset#bottom.well diff --git a/views/style.scss b/views/style.scss index 0802545..fd05c3e 100644 --- a/views/style.scss +++ b/views/style.scss @@ -1,230 +1,10 @@ -$ist-color: rgba(200, 194, 200, 0.4); -$black: #2b2b2b; -$orange: #f76700; - -body { - background-image: url("/images/gray_jean.png"); - overflow: scroll; - font-family: sans-serif; } - body a { - &:hover { - color: $orange; } - text-decoration: none; - font-weight: bold; - color: $black; } - body img { - border: 0; } - body h1 { - color: $black; } - body h2 { - padding-top: 12px; - font-size: x-large; - color: $orange; } - -.logo { - top: 0; - position: fixed; - background-color: #efefef; - width: 100%; - z-index: 40; } - .logo img { - float: left; - display: inline; - margin-right: 1em; - padding: 1em; } - .logo h1 { - width: 80%; } - .logo img.ote { - width: 100px; - height: 50px; - display: inline; - float: right; } - -.content { - background-image: url("/images/gray_jean.png"); - margin-top: 6em; } - .content h1 { - text-shadow: white 1px 1px 0; - font-size: x-large; - display: inline; } - .content .arrow { - margin: 8px 0px 5px 20px; } - .content .back { - display: inline; } - .content fieldset#top { - border: 0; - padding: 10px; - background-color: $ist-color; } - .content fieldset#top a#linkInsert { - display: block; - width: 100%; - height: 2em; } - .content fieldset#top #appletContainer { - padding: 10px; } - .content fieldset#middle { - border: 0; - padding: 10px; - background-color: $ist-color; } - .content fieldset#middle a#linkModels { - display: block; - width: 100%; - height: 2em; } - .content fieldset#middle #models { - padding-left: 1em; } - .content fieldset#middle #model a { - display: inline; - font-weight: normal; } - .content fieldset#bottom { - border: 0; - background-color: $ist-color; - padding: 10px; - margin-bottom: 5em; } - .content fieldset#bottom input#predict { - margin-left: 1em; } - .content .predictions { - background-color: $ist-color; - padding: 10px; - text-align: justify; } - .content .overview { - padding: 12px; - margin-top: 1em; - margin-bottom: 1em; - text-align: left; } - .content .overview a:hover { - color: $orange; } - .content .overview caption { - text-align: left; } - .content .overview #overview tr td { - background-color: white; - border: 1px solid #dad9c7; - padding-left: 1em; - padding-top: 0.5em; } - .content .overview #overview tr td#compound { - width: 250px; } - .content .overview #overview tr td b.c { - color: #d42200; } - .content .overview #overview tr td b.n { - color: #5c8533; } - .content .overview #overview tr td .confidence { - display: inline; } - .content .error { - background-color: $ist-color; - box-shadow: (1px 1px 1px rgba(white, 1) inset, -1px -1px 5px rgba($black, 0.3) inset); - padding: 15px; } - .content .error .message { - margin: 2em; - padding: 1em; - border: 2px solid $orange; - background-color: white; - color: $black; } - .content #closebutton { - color: $orange; - margin-left: 98%; } - -.details { - width: 98%; - box-shadow: (1px 1px 1px rgba(white, 1) inset, -1px -1px 5px rgba($black, 0.3) inset); - background-color: $ist-color; } - .details a:hover { - color: $orange; } - .details img { - display: inline; - float: left; - margin: 0.5em; } - -.significant_fragments { - width: 98%; - margin-bottom: 2em; - box-shadow: (1px 1px 1px rgba(white, 1) inset, -1px -1px 5px rgba($black, 0.3) inset); - background-color: $ist-color; } - .significant_fragments img { - display: inline; - float: left; - margin: 0.5em; } - .significant_fragments table#sf1 { - text-align: left; } - .significant_fragments table#sf1 td { - padding: 0.2em; } - - img.smarts{ - width: 100px; - //float: left; - } - img.smarts:hover{ - transform: scale(4); - border: solid 1px black; - } - -.descriptors { - width: 98%; - margin-bottom: 2em; - box-shadow: (1px 1px 1px rgba(white, 1) inset, -1px -1px 5px rgba($black, 0.3) inset); - background-color: $ist-color; } - .descriptors img { - display: inline; - float: left; - margin: 0.5em; } - .descriptors table#sf2 { - text-align: left; } - .descriptors table#sf2 td { - padding: 0.2em; } - -.results { - overflow-x: auto; - overflow-y: hidden; - margin-top: 2em; - margin-bottom: 2em; } - .results a:hover { - color: $orange; } - .results .tablesorter { - width: 100%; - text-align: left; } - .results .tablesorter thead { - background-color: $ist-color; } - .results .tablesorter thead tr th.header { - background-image: url("/images/bg.gif"); - background-repeat: no-repeat; - background-position: center left; - cursor: pointer; - padding-left: 20px; - border: 1px solid #dad9c7; - margin-left: -1px; } - .results .tablesorter thead tr th.headerSortDown { - background-image: url("/images/desc.gif"); - background-color: $ist-color; } - table.tablesorter .tablesorter-default thead tr .tablesorter-headerRow th .tablesorter-header .tablesorter-headerAsc { - background-image: url("/images/asc.gif"); - background-color: $ist-color; } - .results .tablesorter tbody tr td { - border-spacing: 1px; - padding-left: 1em; } - .results .tablesorter tbody .compound { - width: 250px; - padding: 0.2em; } - .results .tablesorter tbody .n { - color: #5c8533; } - .results .tablesorter tbody .c { - color: #d42200; } - -input#predict { - border: 0px; - background-color: $ist-color; - margin: 0; - padding: 0; } - -.tooltip { - background-color: white; - border: 2px solid $orange; - font-size: 0.5em; - padding: 1em; - display: none; - z-index: 50; } - -.footer { - margin: 20px 0 20px 0; } - .footer a { - text-decoration: none; - color: black; } - .footer a:hover { - color: $orange; } - +table.table-borderless tbody tr td{ + border-top: none; +} + +h4.head-back, h5.head-back{ + background-color: #E7E7E7; +} +//.btn:focus{ + //background: #f0ad4e; +//} -- cgit v1.2.3