From e22513f460eeb42af5164537a7ecea9d21035cea Mon Sep 17 00:00:00 2001 From: gebele Date: Mon, 25 Sep 2017 11:29:26 +0000 Subject: before new batch --- Gemfile | 2 + VERSION | 2 +- application.rb | 268 ++++++++++++++++++++++++++---------- helper.rb | 218 ------------------------------ lazar-gui.gemspec | 2 + views/batch.haml | 157 ++++++++++++---------- views/details.haml | 24 ---- views/error.haml | 3 +- views/faq.haml | 2 - views/faq_layout.haml | 67 --------- views/license.haml | 1 - views/model_details.haml | 284 ++++++++++++++++++++------------------- views/neighbors.haml | 42 ++---- views/predict.haml | 83 ++++++++---- views/prediction.haml | 190 +++++++++++++++++--------- views/significant_fragments.haml | 66 --------- views/style.scss | 43 ++++-- 17 files changed, 646 insertions(+), 808 deletions(-) delete mode 100644 helper.rb delete mode 100644 views/details.haml delete mode 100644 views/faq.haml delete mode 100644 views/faq_layout.haml delete mode 100644 views/license.haml delete mode 100644 views/significant_fragments.haml diff --git a/Gemfile b/Gemfile index c86c89a..7b443e3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,8 @@ source "https://rubygems.org" gemspec gem "lazar", :path => "../lazar" +gem "toxtree", :path => "../toxtree" +gem "kazius-alerts", :path => "../kazius-alerts" gem "gem-path" gem "sinatra" gem "sinatra-reloader" diff --git a/VERSION b/VERSION index ec63514..f599e28 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9 +10 diff --git a/application.rb b/application.rb index eefd833..70eecb5 100644 --- a/application.rb +++ b/application.rb @@ -1,38 +1,61 @@ -#require_relative 'helper.rb' -require 'rdiscount' include OpenTox -configure :development do + +configure :production do $logger = Logger.new(STDOUT) + enable :reloader end -helpers do - class Numeric - def percent_of(n) - self.to_f / n.to_f * 100.0 - end - end - +configure :development do + $logger = Logger.new(STDOUT) + enable :reloader end before do @version = File.read("VERSION").chomp end +not_found do + redirect to('/predict') +end + +error do + @error = request.env['sinatra.error'] + haml :error +end + +helpers do + def embedded_svg image, options={} + doc = Nokogiri::HTML::DocumentFragment.parse image + svg = doc.at_css 'svg' + title = doc.at_css 'title' + if options[:class].present? + svg['class'] = options[:class] + end + if options[:title].present? + title.children.remove + text_node = Nokogiri::XML::Text.new(options[:title], doc) + title.add_child(text_node) + end + doc.to_html.html_safe + end +end + get '/?' do redirect to('/predict') end get '/predict/?' do - @models = OpenTox::Model::Prediction.all + @models = Model::Validation.all @models = @models.delete_if{|m| m.model.name =~ /\b(Net cell association)\b/} @endpoints = @models.collect{|m| m.endpoint}.sort.uniq + @endpoints << "Oral toxicity (Cramer rules)" @models.count <= 0 ? (haml :info) : (haml :predict) end get '/predict/modeldetails/:model' do - model = OpenTox::Model::Prediction.find params[:model] - crossvalidations = OpenTox::Validation::RepeatedCrossValidation.find(model.repeated_crossvalidation_id).crossvalidations + model = Model::Validation.find params[:model] + crossvalidations = Validation::RepeatedCrossValidation.find(model.repeated_crossvalidation_id).crossvalidations return haml :model_details, :layout=> false, :locals => {:model => model, :crossvalidations => crossvalidations} end @@ -48,43 +71,10 @@ get '/predict/dataset/:name' do csv end -get '/predict/?:csv?' do +get '/predict/:tmppath/:filename/?' do response['Content-Type'] = "text/csv" - @csv = "\"Compound\",\"Endpoint\",\"Type\",\"Prediction\",\"95% Prediction interval\"\n" - @@batch.each do |key, values| - compound = key - smiles = compound.smiles - values.each do |array| - model = array[0] - type = model.model.class.to_s.match("Classification") ? "Classification" : "Regression" - prediction = array[1] - endpoint = "#{model.endpoint.gsub('_', ' ')} (#{model.species})" - if prediction[:confidence] == "measured" - if prediction[:value].is_a?(Array) - prediction[:value].each do |value| - pred = value.numeric? ? "#{value} (#{model.unit}), #{compound.mmol_to_mg(value.delog10)} #{(model.unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : value - int = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval]) - interval = (int.nil? ? "--" : "#{int[1].delog10} - #{int[0].delog10} (#{model.unit})") - @csv += "\"#{smiles}\",\"#{endpoint}\",\"#{type}\",\"#{pred}\",\"#{interval}\"\n" - end - else - pred = prediction[:value].numeric? ? "#{prediction[:value]} (#{model.unit}), #{compound.mmol_to_mg(prediction[:value].delog10)} #{(model.unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:value] - confidence = "measured activity" - end - elsif prediction[:neighbors].size > 0 - type = model.model.class.to_s.match("Classification") ? "Classification" : "Regression" - pred = prediction[:value].numeric? ? "#{prediction[:value].delog10} (#{model.unit}), #{compound.mmol_to_mg(prediction[:value].delog10)} #{(model.unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:value] - int = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval]) - interval = (int.nil? ? "--" : "#{int[1].delog10} - #{int[0].delog10} (#{model.unit})") - else - type = "" - pred = "Not enough similar compounds in training dataset." - interval = "" - end - @csv += "\"#{smiles}\",\"#{endpoint}\",\"#{type}\",\"#{pred}\",\"#{interval}\"\n" unless prediction[:value].is_a?(Array) - end - end - @csv + path = "/tmp/#{params[:tmppath]}" + send_file path, :filename => "lazar_batch_prediction_#{params[:filename]}", :type => "text/csv", :disposition => "attachment" end post '/predict/?' do @@ -92,66 +82,196 @@ post '/predict/?' do # process batch prediction if !params[:fileselect].blank? if params[:fileselect][:filename] !~ /\.csv$/ - @error_report = "Please submit a csv file." - return haml :error + bad_request_error "Please submit a csv file." end File.open('tmp/' + params[:fileselect][:filename], "w") do |f| f.write(params[:fileselect][:tempfile].read) end @filename = params[:fileselect][:filename] begin - input = OpenTox::Dataset.from_csv_file File.join("tmp", params[:fileselect][:filename]), true + input = Dataset.from_csv_file File.join("tmp", params[:fileselect][:filename]), true if input.class == OpenTox::Dataset - dataset = OpenTox::Dataset.find input + dataset = Dataset.find input else - @error_report = "Could not serialize file '#{@filename}' ." - return haml :error + bad_request_error "Could not serialize file '#{@filename}'." end rescue - @error_report = "Could not serialize file '#{@filename}' ." - return haml :error + bad_request_error "Could not serialize file '#{@filename}'." end @compounds = dataset.compounds if @compounds.size == 0 - @error_report = dataset[:warnings] + message = dataset[:warnings] dataset.delete - return haml :error + bad_request_error message end + + # for csv export @batch = {} - @compounds.each do |compound| - @batch[compound] = [] - params[:selection].keys.each do |model_id| - model = Model::Prediction.find model_id + # for haml table + @view = {} + + @compounds.each{|c| @view[c] = []} + params[:selection].keys.each do |model_id| + model = Model::Validation.find model_id + @batch[model] = [] + @compounds.each_with_index do |compound,idx| prediction = model.predict(compound) - @batch[compound] << [model, prediction] + @batch[model] << [compound, prediction] + @view[compound] << [model,prediction] end end - @@batch = @batch + + @csvhash = {} @warnings = dataset[:warnings] + dupEntries = {} + delEntries = "" + + # split duplicates and deleted entries + @warnings.each do |w| + substring = w.match(/line .* of/) + unless substring.nil? + delEntries += "\"#{w.sub(/\b(tmp\/)\b/,"")}\"\n" + end + substring = w.match(/rows .* Entries/) + unless substring.nil? + lines = [] + substring[0].split(",").each{|s| lines << s[/\d+/]} + lines.shift + lines.each{|l| dupEntries[l.to_i] = w.split(".").first} + end + end + + @batch.each_with_index do |hash, idx| + @csvhash[idx] = "" + model = hash[0] + # create header + if model.regression? + predAunit = "(#{model.unit})" + predBunit = "(#{model.unit =~ /mmol\/L/ ? "(mol/L)" : "(mg/kg_bw/day)"})" + @csvhash[idx] = "\"ID\",\"Endpoint\",\"Type\",\"Unique SMILES\",\"Prediction #{predAunit}\",\"Prediction #{predBunit}\",\"95% Prediction interval (low) #{predAunit}\",\"95% Prediction interval (high) #{predAunit}\",\"95% Prediction interval (low) #{predBunit}\",\"95% Prediction interval (high) #{predBunit}\",\"inApplicabilityDomain\",\"inTrainningSet\",\"Note\"\n" + else #classification + av = model.prediction_feature.accept_values + probFirst = av[0].capitalize + probLast = av[1].capitalize + @csvhash[idx] = "\"ID\",\"Endpoint\",\"Type\",\"Unique SMILES\",\"Prediction\",\"predProbability#{probFirst}\",\"predProbability#{probLast}\",\"inApplicabilityDomain\",\"inTrainningSet\",\"Note\"\n" + end + values = hash[1] + dupEntries.keys.each{|k| values.insert(k-1, dupEntries[k])}.compact! + + values.each_with_index do |array, id| + type = (model.regression? ? "Regression" : "Classification") + endpoint = "#{model.endpoint.gsub('_', ' ')} (#{model.species})" + + if id == 0 + @csvhash[idx] += delEntries unless delEntries.blank? + end + unless array.kind_of? String + compound = array[0] + prediction = array[1] + smiles = compound.smiles + + if prediction[:neighbors] + if prediction[:value] + pred = prediction[:value].numeric? ? "#{prediction[:value].delog10.signif(3)}" : prediction[:value] + predA = prediction[:value].numeric? ? "#{prediction[:value].delog10.signif(3)}" : prediction[:value] + predAunit = prediction[:value].numeric? ? "(#{model.unit})" : "" + predB = prediction[:value].numeric? ? "#{compound.mmol_to_mg(prediction[:value].delog10).signif(3)}" : prediction[:value] + predBunit = prediction[:value].numeric? ? "#{model.unit =~ /\b(mmol\/L)\b/ ? "(mg/L)" : "(mg/kg_bw/day)"}" : "" + int = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval]) + intervalLow = (int.nil? ? "" : "#{int[1].delog10.signif(3)}") + intervalHigh = (int.nil? ? "" : "#{int[0].delog10.signif(3)}") + intervalLowMg = (int.nil? ? "" : "#{compound.mmol_to_mg(int[1].delog10).signif(3)}") + intervalHighMg = (int.nil? ? "" : "#{compound.mmol_to_mg(int[0].delog10).signif(3)}") + inApp = "yes" + inT = prediction[:info] =~ /\b(identical)\b/i ? "yes" : "no" + note = prediction[:warnings].join("\n") + ( prediction[:info] ? prediction[:info].sub(/\'.*\'/,"") : "\n" ) + + unless prediction[:probabilities].nil? + av = model.prediction_feature.accept_values + propA = "#{prediction[:probabilities][av[0]].to_f.signif(3)}" + propB = "#{prediction[:probabilities][av[1]].to_f.signif(3)}" + end + else + # no prediction value only one neighbor + inApp = "no" + inT = prediction[:info] =~ /\b(identical)\b/i ? "yes" : "no" + note = prediction[:warnings].join("\n") + ( prediction[:info] ? prediction[:info].sub(/\'.*\'/,"") : "\n" ) + end + else + # no prediction value + inApp = "no" + inT = prediction[:info] =~ /\b(identical)\b/i ? "yes" : "no" + note = prediction[:warnings].join("\n") + ( prediction[:info] ? prediction[:info].sub(/\'.*\'/,"") : "\n" ) + end + if @warnings + @warnings.each do |w| + note += (w.split(".").first + ".") if /\b(#{Regexp.escape(smiles)})\b/ === w + end + end + else + # string note for duplicates + endpoint = type = smiles = pred = predA = predB = propA = propB = intervalLow = intervalHigh = intervalLowMg = intervalHighMg = inApp = inT = "" + note = array + end + if model.regression? + @csvhash[idx] += "\"#{id+1}\",\"#{endpoint}\",\"#{type}\",\"#{smiles}\",\"#{predA}\",\"#{predB}\",\"#{intervalLow}\",\"#{intervalHigh}\",\"#{intervalLowMg}\",\"#{intervalHighMg}\",\"#{inApp}\",\"#{inT}\",\"#{note.chomp}\"\n" + else + @csvhash[idx] += "\"#{id+1}\",\"#{endpoint}\",\"#{type}\",\"#{smiles}\",\"#{pred}\",\"#{propA}\",\"#{propB}\",\"#{inApp}\",\"#{inT}\",\"#{note.chomp}\"\n" + end + end + end + t = Tempfile.new + @csvhash.each do |model, csv| + t.write(csv) + t.write("\n") + end + t.rewind + @tmppath = t.path.split("/").last + dataset.delete File.delete File.join("tmp", params[:fileselect][:filename]) return haml :batch end # validate identifier input - # transfered input if !params[:identifier].blank? @identifier = params[:identifier] $logger.debug "input:#{@identifier}" # get compound from SMILES @compound = Compound.from_smiles @identifier - if @compound.blank? - @error_report = "'#{@identifier}' is not a valid SMILES string." - return haml :error - end - + bad_request_error "'#{@identifier}' is not a valid SMILES string." if @compound.blank? + @models = [] @predictions = [] + @toxtree = false params[:selection].keys.each do |model_id| - model = Model::Prediction.find model_id - @models << model - @predictions << model.predict(@compound) + if model_id == "Cramer" + @toxtree = true + @predictions << [Toxtree.predict(@compound.smiles, "Cramer rules"), Toxtree.predict(@compound.smiles, "Cramer rules with extensions")] + else + model = Model::Validation.find model_id + @models << model + if model.model.name =~ /kazius/ + sa_prediction = KaziusAlerts.predict(@compound.smiles) + lazar_mutagenicity = model.predict(@compound) + confidence = 0 + lazar_mutagenicity_val = (lazar_mutagenicity[:value] == "non-mutagenic" ? false : true) + if sa_prediction[:prediction] == false && lazar_mutagenicity_val == false + confidence = 0.85 + elsif sa_prediction[:prediction] == true && lazar_mutagenicity_val == true + confidence = 0.85 * ( 1 - sa_prediction[:error_product] ) + elsif sa_prediction[:prediction] == false && lazar_mutagenicity_val == true + confidence = 0.11 + elsif sa_prediction[:prediction] == true && lazar_mutagenicity_val == false + confidence = ( 1 - sa_prediction[:error_product] ) - 0.57 + end + @predictions << [lazar_mutagenicity, {:prediction => sa_prediction, :confidence => confidence}] + else + @predictions << model.predict(@compound) + end + end end + haml :prediction end end diff --git a/helper.rb b/helper.rb deleted file mode 100644 index caa1923..0000000 --- a/helper.rb +++ /dev/null @@ -1,218 +0,0 @@ -helpers do - - def is_authorized(uri, action) - if OpenTox::Authorization.server && session[:subjectid] != nil - return OpenTox::Authorization.authorized?(uri, action, session[:subjectid]) - else - return true - end - return false - end - - def is_aluist - OpenTox::Authorization.list_user_groups(session[:username], session[:subjectid]).include?("aluist") - end - - def hide_link(destination) - @link_id = 0 unless @link_id - @link_id += 1 - haml :js_link, :locals => {:name => "hide", :destination => destination, :method => "hide"}, :layout => false - end - - def toggle_link(destination,name) - @link_id = 0 unless @link_id - @link_id += 1 - haml :js_link, :locals => {:name => name, :destination => destination, :method => "toggle"}, :layout => false - end - - def sort(descriptors,value_map) - features = {:activating => [], :deactivating => [], :pc_features => []} - if descriptors.kind_of?(Array) - descriptors.each do |d| - if !value_map.empty? - features[:activating] << {:smarts => d[OT.smarts],:p_value => d[OT.pValue]} if d[OT.effect] == 2 - features[:deactivating] << {:smarts => d[OT.smarts],:p_value => d[OT.pValue]} if d[OT.effect] == 1 - else - if d[OT.effect] =~ TRUE_REGEXP - features[:activating] << {:smarts => d[OT.smarts],:p_value => d[OT.pValue]} - elsif d[OT.effect] =~ FALSE_REGEXP - features[:deactivating] << {:smarts => d[OT.smarts],:p_value => d[OT.pValue]} - end - end - end - else - descriptors.each do |d,v| - features[:pc_features] << {:feature => d, :value => v} - end - end - features - end - - def compound_image(compound,descriptors,value_map) - haml :compound_image, :locals => {:compound => compound, :features => sort(descriptors,value_map)}, :layout => false - end - - def activity_markup(activity,value_map) - if value_map and !value_map.empty? - if value_map.size == 2 - activity = value_map.index(activity) if value_map.has_value? activity - if activity.to_i == 2 - haml ".active #{value_map[activity]}", :layout => false - elsif activity.to_i == 1 - haml ".inactive #{value_map[activity]}", :layout => false - else - haml ".other #{activity.to_s}", :layout => false - end - else - haml ".other #{activity.to_s}", :layout => false - end - elsif OpenTox::Algorithm::numeric? activity - haml ".other #{sprintf('%.03g', activity.to_f)}", :layout => false - else - haml ".other #{activity.to_s}", :layout => false - end -=begin - case activity.class.to_s - when /Float/ - haml ".other #{sprintf('%.03g', activity)}", :layout => false - when /String/ - case activity - when "true" - haml ".active active", :layout => false - when "false" - haml ".inactive inactive", :layout => false - else - haml ".other #{activity.to_s}", :layout => false - end - else - if activity #true - haml ".active active", :layout => false - elsif !activity # false - haml ".inactive inactive", :layout => false - else - haml ".other #{activity.to_s}", :layout => false - end - end -=end - end - - def neighbors_navigation - @page = 0 unless @page - haml :neighbors_navigation, :layout => false - end - - def models_navigation - @page = 0 unless @page - haml :models_navigation, :layout => false - end - - def models_navigation_bottom - @page = 0 unless @page - haml :models_navigation_bottom, :layout => false - end - - def endpoint_option_list(max_time=3600) - out = "" - tmpfile = File.join(TMP_DIR, 'endpoint_option_list') - if File.exists? tmpfile - if Time.now-File.mtime(tmpfile) <= max_time - f = File.open(tmpfile, 'r+') - f.each{|line| out << line} - return out - else - File.unlink(tmpfile) - end - end - result = endpoint_selection() - if result.lines.count > 3 - f = File.new(tmpfile,'w') - f.print result - f.close - end - result - end - - def endpoint_level(endpoint="Endpoints", level=1) - results = OpenTox::Ontology::Echa.echa_endpoints(endpoint) rescue results = [] - out = "" - out += "\n" if results.size > 0 - return out - end - - def endpoint_selection() - out = " \n -
\n" - out += "Please select:\n" - out += endpoint_level - js = "" - out += "
\n" - return out - end - - def logmmol_to_mg(value ,mw) - mg = round_to((10**(-1.0*round_to(value.to_f, 2))*(mw.to_f*1000)),4) - return mg - end - - def logmg_to_mg(value) - mg = round_to(10**round_to(value.to_f, 2),4) - return mg - end - - def ptd50_to_td50(value ,mw) - td50 = round_to((10**(-1.0*round_to(value.to_f, 2))*(mw.to_f*1000)),4) - return td50 - end - - def round_to(value, deci) - rounded = (value.to_f*(10**deci)).round / (10**deci).to_f - return rounded - end - - def calc_mw(compound_uri) - ds = OpenTox::Dataset.new() - ds.save(@subjectid) - ds.add_compound(compound_uri) - ds.save(@subjectid) - mw_algorithm_uri = File.join(CONFIG[:services]["opentox-algorithm"],"pc/MW") - mw_uri = OpenTox::RestClientWrapper.post(mw_algorithm_uri, {:dataset_uri=>ds.uri}) - ds.delete(@subjectid) - mw_ds = OpenTox::Dataset.find(mw_uri, @subjectid) - mw = mw_ds.data_entries[compound_uri][mw_uri.to_s + "/feature/MW"].first.to_f - mw_ds.delete(@subjectid) - return mw - end - - def transform(value, compound_uri, name, haml) - prediction_trans = nil - model_name = name.to_s.downcase - if model_name.include? "ptd50" - mw = calc_mw(compound_uri) - td50 = ptd50_to_td50(value, mw) - prediction_trans = "TD50: #{td50}" - elsif model_name.include? "loael" - if model_name.include? "mol" - mw = calc_mw(compound_uri) - mg = logmmol_to_mg(value, mw) - prediction_trans = "mg/kg bw/day: #{mg}" - elsif model_name.include? "mg" - mg = logmg_to_mg(value) - prediction_trans = "mg/kg bw/day: #{mg}" - end - end - if haml == true - haml ".other #{prediction_trans.to_s}", :layout => false - else - return prediction_trans - end - end -end diff --git a/lazar-gui.gemspec b/lazar-gui.gemspec index 6f58687..592d929 100644 --- a/lazar-gui.gemspec +++ b/lazar-gui.gemspec @@ -14,6 +14,8 @@ Gem::Specification.new do |s| s.files = `git ls-files`.split("\n") s.add_runtime_dependency "lazar" + s.add_runtime_dependency "toxtree" + s.add_runtime_dependency "kazius-alerts" s.add_runtime_dependency "gem-path", "~> 0.6.1", '>= 0.6.1' s.add_runtime_dependency "sinatra", "~> 1.4.0", '>= 1.4.0' s.add_runtime_dependency "rdiscount", "~> 2.1.0", '>= 2.1.0' diff --git a/views/batch.haml b/views/batch.haml index 6c37a2b..0e7efc7 100644 --- a/views/batch.haml +++ b/views/batch.haml @@ -2,11 +2,11 @@ %a.btn.btn-warning{:href => to('/predict')} %span.glyphicon.glyphicon-menu-left{:aria=>{:hidden=>"true"}} New Prediction - %a.btn.btn-success{:href=>"#{to("/predict/#{@filename}")}", :title=>"download"} + %a.btn.btn-success{:id => "downbutton", :href=>"#{to("/predict/#{@tmppath}/#{@filename}")}", :title=>"download"} %span.glyphicon.glyphicon-download-alt - download CSV + Download CSV - / show processed file name + / show file name %topline %div.row %div.col-md-4 @@ -18,83 +18,92 @@ %div.table-responsive %table.table.table-bordered{:id=>"batch", :style=>"background-color:white;"} %tbody - - if @warnings - - @warnings.each do |warning| + - if @warnings + - @warnings.each do |warning| + %tr + %td + %b Warning + %td + = warning.sub(/\b(tmp\/)\b/,"") + - @view.each do |compound, array| %tr - %td - %b Warning - %td - = warning.sub(/\b(tmp\/)\b/,"") - / key = compound, values = [model,prediction] - - @batch.each do |key, values| - - compound = key - %tr - %td{:style=>"vertical-align:top;"} - %p= compound.svg - %p= compound.smiles - - / array[0] = model, array[1] = prediction - - values.each_with_index do |array,i| - %td{:style=>"vertical-align:top;white-space:nowrap;"} - - model = array[0] - / model type (classification|regression) - - model.model.class.to_s.match("Classification") ? type = "Classification" : type = "Regression" - - unit = model.unit - - prediction = array[1] - - %b{:class => "title"} - = "#{model.endpoint.gsub('_', ' ')} (#{model.species})" - - / check for prediction - - if prediction[:neighbors].size > 0 - %p - / show model type (classification|regression) - %b Type: - = type - %p - / check for database hit - - if prediction[:warning] =~ /\b(identical)\b/i - - / show message about dbhit and measurements + %td{:style=>"vertical-align:top;"} + %p= compound.svg + %p= compound.smiles + - array.each do |model,prediction| + %td{:style=>"vertical-align:top;white-space:nowrap;"} + - model.model.class.to_s.match("Classification") ? type = "Classification" : type = "Regression" + - unit = model.unit + + %b{:class => "title"} + = "#{model.endpoint.gsub('_', ' ')} (#{model.species})" + + / check for prediction + - if prediction[:value] %p - %b Compound is part of the training dataset + / show model type (classification|regression) + %b Type: + = type + %p + / check for database hit + - if prediction[:info] =~ /\b(identical)\b/i + + / show message about dbhit and measurements %p - %b Measured activity: + %b Compound is part of the training dataset + %p + %b Measured activity: + %br + - if prediction[:measurements].is_a?(Array) + = (type == "Regression") ? prediction[:measurements].collect{|value| "#{value.delog10.signif(3)} (#{unit})
#{compound.mmol_to_mg(value.delog10).signif(3)} #{unit =~ /mmol\/L/ ? "(mg/L)" : "(mg/kg_bw/day)"}"}.join("
") : prediction[:measurements].join(", ") + - else + = (type == "Regression") ? "#{prediction[:measurements].delog10.signif(3)} (#{unit})
#{compound.mmol_to_mg(prediction[:measurements].delog10).signif(3)} #{(unit =~ /\b(mmol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:measurements] + + + / show prediction + %p + %b Prediction: %br - - if prediction[:measurements].is_a?(Array) - = (type == "Regression") ? prediction[:measurements].collect{|value| "#{value.delog10} (#{unit})
#{compound.mmol_to_mg(value.delog10)} #{unit =~ /mmol\/L/ ? "(mg/L)" : "(mg/kg_bw/day)"}"}.join("
") : prediction[:measurements].join(", ") + = (type == "Regression") ? "#{prediction[:value].delog10.signif(3)} (#{unit})
#{compound.mmol_to_mg(prediction[:value].delog10).signif(3)} #{(unit =~ /\b(mmol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:value] + + / show prediction interval or probability + %p + - if type == "Regression" + %b 95% Prediction interval: + - interval = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval]) + %br + = interval.nil? ? "" : "#{interval[1].delog10.signif(3)} - #{interval[0].delog10.signif(3)} (#{unit})" + %br + = "#{compound.mmol_to_mg(interval[1].delog10).signif(3)} - #{compound.mmol_to_mg(interval[0].delog10).signif(3)} #{(unit =~ /\b(mmol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" if !prediction[:prediction_interval].nil? - else - = (type == "Regression") ? "#{prediction[:measurements].delog10} (#{unit})
#{compound.mmol_to_mg(prediction[:measurements].delog10)} #{(unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:measurements] - - - / show prediction - %p - %b Prediction: - %br - = (type == "Regression") ? "#{prediction[:value].delog10} (#{unit})
#{compound.mmol_to_mg(prediction[:value].delog10)} #{(unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:value] - - / show prediction interval or probability + %b Probability: + - unless prediction[:probabilities].nil? + - probabilities = "" + - prediction[:probabilities].each{|k,v| probabilities += "#{k}: #{v.signif(3)}
"} + %br + = probabilities + / show warnings %p - - if type == "Regression" - %b 95% Prediction interval: - - interval = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval]) + - if !prediction[:info].blank? + %b Info: %br - = interval.nil? ? "--" : "#{interval[1].delog10} - #{interval[0].delog10} (#{unit})" - %br - = "#{compound.mmol_to_mg(interval[1].delog10)} - #{compound.mmol_to_mg(interval[0].delog10)} #{(unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" if !prediction[:prediction_interval].nil? - - else - %b Probability: - - unless prediction[:probabilities].nil? + %p=prediction[:info].sub(/\'.*\'/,"").sub(/,/, ",
") + - if !prediction[:warnings].blank? + %b Warnings: + - prediction[:warnings].uniq.each do |warning| %br - = "#{prediction[:probabilities].keys[0]}: #{prediction[:probabilities].values[0]}" + %p=warning.sub(/substances/, "substances
").sub(/prediction\:/, "prediction\:
") + + / no prediction + - else + %br + - if !prediction[:info].blank? + %b Info: + %br + %p=prediction[:info].sub(/\'.*\'/,"").sub(/,/, ",
") + - if !prediction[:warnings].blank? + %b Warnings: + - prediction[:warnings].uniq.each do |warning| %br - / show warnings - %p - - if !prediction[:warning].nil? - %b Warnings: - %a.btn.glyphicon.glyphicon-info-sign{:href=>"javascript:void(0)", :title=>"Warnings", :tabindex=>"0", data: {trigger:"focus", toggle:"popover", placement:"left", html:"true", content:"#{prediction[:warning]}"}} - - / no prediction - - else - %p - = "Not enough similar compounds
in training dataset." + %p=warning.sub(/substances/, "substances
").sub(/prediction\:/, "prediction\:
") + %tr diff --git a/views/details.haml b/views/details.haml deleted file mode 100644 index bb8250d..0000000 --- a/views/details.haml +++ /dev/null @@ -1,24 +0,0 @@ -%div.modal-body{:style=>"padding:10px;"} - %button.close{ :type=>" button", data: { dismiss:"modal"}} × - %h3 - Names and synonyms: - %img.img-responsive{:src=>"#{@compound.uri}/image", :alt=>"Compound image not available", :width=>"300px", :heigth=>"300px", :style=>"float:left;"} - %p - %b="SMILES:" - %p= @smiles - %br - %b="InChI:" - %p= @inchi - %br - %b="Names:" - %p{:style=>"padding-left:0.5em;"} - - if @names !~ /^no names/i - = @names.join("; ") - - else - = @names - %hr - %p{:style=>"padding-left:0.5em;"} - / pubchem link - %a.btn.btn-primary{:href=>"http://aop.in-silico.ch/", :title=>"Link opens in new window.", :alt=>"pubchem read across", :rel=>"external"} PubChem read across - %i (experimental) - %br diff --git a/views/error.haml b/views/error.haml index 555982b..05cb0da 100644 --- a/views/error.haml +++ b/views/error.haml @@ -4,5 +4,4 @@ Back %hr %div.well{:style=>"width:100%;margin-bottom:2em;"} - = @error_report - + = @error diff --git a/views/faq.haml b/views/faq.haml deleted file mode 100644 index 818b96a..0000000 --- a/views/faq.haml +++ /dev/null @@ -1,2 +0,0 @@ -%div.well.faq - = @faq diff --git a/views/faq_layout.haml b/views/faq_layout.haml deleted file mode 100644 index a9b6664..0000000 --- a/views/faq_layout.haml +++ /dev/null @@ -1,67 +0,0 @@ -!!! -%html{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en"} - %head - %meta{'charset'=>"utf-8"} - %meta{'http-equiv'=>"X-UA-Compatible", :content=>"IE=edge"} - %meta{'name'=>"viewport", :content=>"width=device-width, initial-scale=1"} - %title Lazar GUI FAQ - %link{:rel=>'icon', :type=>'image/x-icon', :href=>'/images/favicon.ico'} - %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"} - %script{ :src=>"/javascripts/lazar-gui.js"} - %body - %noscript - %div{ :style=>"width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif"} - Your web browser must have JavaScript enabled in order for this application to display correctly. - %header.page-header - %div.row - %div.col-md-2 - %a{:href=> to("/predict")} - %img.media-object{:src=>"/images/ist_logo.png", :alt=>"logo", :style=>"margin:0 3em 0 2em;"} - %div.col-md-10 - %h1.media-heading{:style=>"margin: 0 0 0 1em;display:inline;"} Lazar GUI - A Graphical User Interface for the Lazar framework - - %div.container-fluid - :javascript - $(document).ready(function(){ - $("#back-top").hide(); - $(".blind").error(function(){ - $(this).attr('src', '/images/blind.png'); - }); - }); - - = yield - - %footer.footer - %div.container-fluid - %p.text-muted - © - %a{:href => 'http://www.in-silico.ch', :rel => "external"} in silico toxicology gmbh 2004 - #{Time.now.year.to_s} - - #back-top{:style => "z-index:100;position:fixed;bottom:1%;right:1%;"} - %a{:href => "", :style=>"text:decoration:none;color:#ccc;"} - %span.glyphicon.glyphicon-circle-arrow-up{:style => "font-size:3em;color:black;"} - :javascript - $("#back-top").hide(); - $(function () { - $(window).scroll(function () { - if ($(this).scrollTop() > 600) { - $('#back-top').fadeIn(); - } else { - $('#back-top').fadeOut(); - } - }); - // scroll body to 0px on click - $('#back-top a').click(function () { - $('body,html').animate({ - scrollTop: 0 - }, 500); - return false; - }); - }); diff --git a/views/license.haml b/views/license.haml deleted file mode 100644 index 2813220..0000000 --- a/views/license.haml +++ /dev/null @@ -1 +0,0 @@ -= @license diff --git a/views/model_details.haml b/views/model_details.haml index 3fa8c8b..f1d5f9d 100644 --- a/views/model_details.haml +++ b/views/model_details.haml @@ -1,139 +1,149 @@ -%b Model: -%br -Source: -%a{:href=>model.source, :rel=>"external"} - = model.source -%br -- model.classification? ? type = "Classification" : type = "Regression" -= "Type:\t" -= type -%br -- training_dataset = OpenTox::Dataset.find model.model.training_dataset_id -= "Training compounds:\t" -= training_dataset.compounds.size -%br -= "Training dataset:\t" -%a{:href=>"#{to("/predict/dataset/#{training_dataset.name}")}"} - = training_dataset.name -%br -%b Algorithms: -%br -Similarity: -%a{:href=> "http://www.rubydoc.info/gems/lazar/OpenTox%2F#{model.model.algorithms["similarity"]["method"].sub("::", "%2F")}", :rel=>"external"} - = model.model.algorithms["similarity"]["method"] -= ", min: #{model.model.algorithms["similarity"]["min"]}" -%br -Prediction: -%a{:href=>"http://www.rubydoc.info/gems/lazar/OpenTox%2F#{model.model.algorithms["prediction"]["method"].sub("::","%2f")}", :rel=>"external"} - = model.model.algorithms["prediction"]["method"] -%br -Descriptors: -= model.model.algorithms["descriptors"]["method"]+"," -= model.model.algorithms["descriptors"]["type"] -%p -- if type == "Classification" - %b Independent crossvalidations: -- else - %b Independent crossvalidations (-log10 transformed): -%div.row{:id=>"validations#{model.id}", :style=>"background-color:#f5f5f5;"} - - crossvalidations.each do |cv| - %span.col-xs-4.col-sm-4.col-md-4.col-lg-4 - = "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) if cv.accuracy - %br - = "Weighted accuracy:\t" - = cv.weighted_accuracy.round(3) if cv.weighted_accuracy - %br - = "True positive rate:\t" - = cv.true_rate["active"].round(3) if cv.true_rate - %br - = "True negative rate:\t" - = cv.true_rate["inactive"].round(3) if cv.true_rate - %br - = "Positive predictive value:\t" - = cv.predictivity["active"].round(3) if cv.predictivity - %br - = "Negative predictive value:\t" - = cv.predictivity["inactive"].round(3) if cv.predictivity - %p - - ["confusion_matrix", "weighted_confusion_matrix"].each_with_index do |matrix,idx| - %b= (idx == 0 ? "Confusion Matrix" : "Weighted 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 - =( idx == 1 ? cv.send(matrix)[0][0].round(3) : cv.send(matrix)[0][0]) - %td - =( idx == 1 ? cv.send(matrix)[0][1].round(3) : cv.send(matrix)[0][1]) - -#%td - =cv.confusion_matrix[0][0]+cv.confusion_matrix[0][1] - %tr - %td - %td inactive - %td - =( idx == 1 ? cv.send(matrix)[1][0].round(3) : cv.send(matrix)[1][0]) - %td - =( idx == 1 ? cv.send(matrix)[1][1].round(3) : cv.send(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 +%div.panel.panel-default + %div.panel-heading + %b Model: + %div.panel-body + Source: + %a{:href=>model.source, :rel=>"external"} + = model.source + %br + - model.classification? ? type = "Classification" : type = "Regression" + = "Type:\t" + = type + %br + - training_dataset = OpenTox::Dataset.find model.model.training_dataset_id + = "Training compounds:\t" + = training_dataset.data_entries.size + %br + = "Training dataset:\t" + %a{:href=>"#{to("/predict/dataset/#{training_dataset.name}")}"} + = training_dataset.name + +%div.panel.panel-default + %div.panel-heading + %b Algorithms: + %div.panel-body + Similarity: + %a{:href=> "http://www.rubydoc.info/gems/lazar/OpenTox%2F#{model.model.algorithms["similarity"]["method"].sub("::", "%2F")}", :rel=>"external"} + = model.model.algorithms["similarity"]["method"] + = ", min: #{model.model.algorithms["similarity"]["min"]}" + %br + Prediction: + %a{:href=>"http://www.rubydoc.info/gems/lazar/OpenTox%2F#{model.model.algorithms["prediction"]["method"].sub("::","%2f")}", :rel=>"external"} + = model.model.algorithms["prediction"]["method"] + %br + Descriptors: + = model.model.algorithms["descriptors"]["method"]+"," + = model.model.algorithms["descriptors"]["type"] + +%div.panel.panel-default + - if type == "Classification" + %div.panel-heading + %b Independent crossvalidations: + - else + %div.panel-heading + %b Independent crossvalidations (-log10 transformed): + %div.panel-body + /%div.row{:id=>"validations#{model.id}", :style=>"background-color:#f5f5f5;"} + %div.row{:id=>"validations#{model.id}"} + - crossvalidations.each do |cv| + %span.col-xs-4.col-sm-4.col-md-4.col-lg-4 + = "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) if cv.accuracy + %br + = "Weighted accuracy:\t" + = cv.weighted_accuracy.round(3) if cv.weighted_accuracy + - if cv.true_rate %br - %br - /= "Confidence plot:" - /%p.plot - / %img{:src=>"confp#{cv.id}.svg"} - - if model.regression? - %br - %a.ht5{:href=>"https://en.wikipedia.org/wiki/Root-mean-square_deviation", :rel=>"external"} RMSE: - = cv.rmse.round(3) if cv.rmse - %br - %a.ht5{:href=>"https://en.wikipedia.org/wiki/Mean_absolute_error", :rel=>"external"} MAE: - = cv.mae.round(3) if cv.mae - %br - %a.ht5{:href=>"https://en.wikipedia.org/wiki/Coefficient_of_determination", :rel=>"external"}= "R"+"2"+":" - = cv.r_squared.round(3) if cv.r_squared - %br - /= "Confidence plot:" - /%p.plot - / %img{:src=>"/confp#{cv.id}.svg"} - /%br - /= "Correlation plot" - /%p.plot - / %img{:src=>"/corrp#{cv.id}.svg"} + = "True positive rate:\t" + = cv.true_rate[cv.accept_values[0]].round(3) + %br + = "True negative rate:\t" + = cv.true_rate[cv.accept_values[1]].round(3) + - if cv.predictivity + %br + = "Positive predictive value:\t" + = cv.predictivity[cv.accept_values[0]].round(3) + %br + = "Negative predictive value:\t" + = cv.predictivity[cv.accept_values[1]].round(3) + %p + - ["confusion_matrix", "weighted_confusion_matrix"].each_with_index do |matrix,idx| + %b= (idx == 0 ? "Confusion Matrix" : "Weighted 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 + =( idx == 1 ? cv.send(matrix)[0][0].round(3) : cv.send(matrix)[0][0]) + %td + =( idx == 1 ? cv.send(matrix)[0][1].round(3) : cv.send(matrix)[0][1]) + -#%td + =cv.confusion_matrix[0][0]+cv.confusion_matrix[0][1] + %tr + %td + %td inactive + %td + =( idx == 1 ? cv.send(matrix)[1][0].round(3) : cv.send(matrix)[1][0]) + %td + =( idx == 1 ? cv.send(matrix)[1][1].round(3) : cv.send(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 + %br + %br + /= "Confidence plot:" + /%p.plot + / %img{:src=>"confp#{cv.id}.svg"} + - if model.regression? + %br + %a.ht5{:href=>"https://en.wikipedia.org/wiki/Root-mean-square_deviation", :rel=>"external"} RMSE: + = cv.rmse.round(3) if cv.rmse + %br + %a.ht5{:href=>"https://en.wikipedia.org/wiki/Mean_absolute_error", :rel=>"external"} MAE: + = cv.mae.round(3) if cv.mae + %br + %a.ht5{:href=>"https://en.wikipedia.org/wiki/Coefficient_of_determination", :rel=>"external"}= "R"+"2"+":" + = cv.r_squared.round(3) if cv.r_squared + %br + /= "Confidence plot:" + /%p.plot + / %img{:src=>"/confp#{cv.id}.svg"} + /%br + /= "Correlation plot" + /%p.plot + / %img{:src=>"/corrp#{cv.id}.svg"} -%br diff --git a/views/neighbors.haml b/views/neighbors.haml index 32b8389..9c12be9 100644 --- a/views/neighbors.haml +++ b/views/neighbors.haml @@ -1,38 +1,19 @@ -/ unpacks multi prediction array ; -/ prepare it for neighbors ; -/ align single prediction to endpoint ; -/ display preordered in table view ; - %div.results %h3 Neighbors: / tabs div #tabs %ul.nav.nav-tabs.nav-justified{:id=>"neighborTabs", :role=>"tablist"} - / each model a tab head ; - / hash for predictionFeature - - predictionFeature = {} - @models.each_with_index do |model,i| / get predictionFeature type - m = Model::Lazar.find model.model_id.to_s - - predFeature = Feature.find m.prediction_feature_id.to_s - / define feature type (numeric : nominal) - - predFeatureType = (predFeature.numeric? ? "numeric" : "nominal") - / use prediction feature id for neighbor compound features - - predFeatureId = m.prediction_feature_id.to_s - - predictionFeature[i] = {"id" => predFeatureId, "type" => predFeatureType} %li{:class => ("active" if i == 0)} %a{:href => "#results_#{i+1}", :id => "linkTab#{i+1}", data: {toggle:"tab"}} = "#{model.endpoint} (#{model.species})" %div.tab-content - / unpack to single arrays - @predictions.each_with_index do |prediction,j| - / pass model type for significant fragments view + - if prediction.dimension > 0 + - prediction = prediction[0] #results.tab-pane{:id=>"#{j+1}", :class => ("active" if j == 0)} - / prepare dataset for neighbors table ; - / delete first array which contains prediction ; - / following arrays are the neighbor predictions ; - / call the tablesorter plugin ; - / presort by similarity ; :javascript $(document).ready(function(){ $("table##{j+1}").tablesorter({ @@ -58,7 +39,7 @@ widthFixed: false }); }); - - if prediction[:neighbors].size > 0 + - if prediction[:neighbors] %div.table-responsive %table{:id=>"#{j+1}", :style=>"border-style: solid;"} %thead @@ -70,9 +51,7 @@ %a.btn.glyphicon.glyphicon-info-sign{:href=>"javascript:void(0)", :title=>"Measured Activity", :tabindex=>"0", data: {trigger:"focus", container:"body", toggle:"popover", placement:"auto", html:"true", content:"Experimental result(s) from the training dataset."}, :style=>"z-index:auto+10;"} %th.sorter-false{:style =>"vertical-align:middle;"} Similarity - %a.btn.glyphicon.glyphicon-info-sign{:href=>"javascript:void(0)", :title=>"Similarity", :tabindex=>"0", data: {trigger:"focus", container:"body", toggle:"popover", placement:"auto", html:"true", content:"Tanimoto/Jaccard similarity based on Molprint2D fingerprints."}, :style=>"z-index:auto+10;"} - / %th{:style =>"vertical-align:middle;"} - / Supporting Information + %a.btn.glyphicon.glyphicon-info-sign{:href=>"javascript:void(0)", :title=>"Similarity", :tabindex=>"0", data: {trigger:"focus", container:"body", toggle:"popover", placement:"auto", html:"true", content:"Tanimoto/Jaccard similarity based on Molprint2D fingerprints."}, :style=>"z-index:auto+10;"} %tbody - type = @model_types[j] - unit = @models[j].unit @@ -81,17 +60,16 @@ / Compound - c = Compound.find(neighbor) %td{:style =>"vertical-align:middle;padding-left:1em;width:50%;"} - /%a.btn.btn-link{:href => "#details#{j+1}", data: { toggle: "modal", remote: to("/prediction/#{CGI.escape(neighbor["_id"])}/details"), :id=>"link#{j+1}#{count}"}} - %p= c.svg + = c.svg %p= c.smiles / Measured Activity %td{:style =>"vertical-align:middle;padding-left:1em;width:20%;white-space:nowrap;"} - if neighbor[:measurement].is_a?(Array) - = (type == "Regression") ? neighbor[:measurement].collect{|value| "#{value.delog10} (#{unit})
#{c.mmol_to_mg(value.delog10)} #{unit =~ /mmol\/L/ ? "(mg/L)" : "(mg/kg_bw/day)"}"}.join("
") : neighbor[:measurement].join(", ") + = (type == "Regression") ? neighbor[:measurement].collect{|value| "#{value.delog10.signif(3)} (#{unit})
#{c.mmol_to_mg(value.delog10).signif(3)} #{unit =~ /mmol\/L/ ? "(mg/L)" : "(mg/kg_bw/day)"}"}.join("
") : neighbor[:measurement].join(", ") - else - = (type == "Regression") ? "#{neighbor[:measurement].delog10} (#{unit})
#{c.mmol_to_mg(neighbor[:measurement].delog10)} #{(unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : neighbor[:measurement] - + - if !neighbor[:measurement].nil? + = (type == "Regression") ? "#{neighbor[:measurement].delog10.signif(3)} (#{unit})
#{c.mmol_to_mg(neighbor[:measurement].delog10).signif(3)} #{(unit =~ /\b(mmol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : neighbor[:measurement] / Similarity = tanimoto %td{:style =>"vertical-align:middle;padding-left:1em;width:20%;"} = neighbor[:similarity].round(3) @@ -100,7 +78,3 @@ %span.btn.btn-default.disabled = "Not enough similar compounds in training dataset" - %div.modal.fade{:id=>"details#{j+1}", :role=>"dialog"} - %div.modal-dialog.modal-lg - %div.modal-content - diff --git a/views/predict.haml b/views/predict.haml index 9a7d0d4..eb9047b 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -111,7 +111,7 @@ }; // 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())" } +%form{:name => "form", :action => to('/predict'), :method => "post", :enctype => "multipart/form-data", :onsubmit => "return !!(showcircle())" } %fieldset#top.well %h2 1. Draw a chemical structure #insert @@ -135,38 +135,63 @@ %fieldset#middle.well %h2 2. Select one or more endpoints #models + %input{:type => "checkbox", :name => "checkAll", :id => "checkAll"} + %label{:for => "checkAll"} + All + :javascript + $("#checkAll").click(function () { + if ($(this).prop('checked')){ + $(".check").prop('checked', true); + }else{ + $(".check").prop('checked', false); + }; + }); - @endpoints.each do |endpoint| %div{:id=>endpoint.gsub(/\s+/, "_")} %h4.head-back=endpoint - - @models.select{|m| m.endpoint == endpoint}.each do |model| - %div.row{:id => model.id} - %span.col-sm-4 - %input{:type => "checkbox", :name => "selection[#{model.id}]", :id => "selection[#{model.species.gsub(/\s+/, "_")}]", :value => true, :disabled => false} - %label{:for => "selection[#{model.species.gsub(/\s+/, "_")}]"} - = model.species - %span.col-sm-8 - %a.btn.btn-default.btn-xs{:data=>{:toggle=>"collapse"}, :href=>"#details#{model.id}", :onclick=>"load#{model.id}Details('#{model}')", :id => "link#{model.id}", :style=>"font-size:small;"} - Details | Validation - %img.h2{:src=>"/images/wait30trans.gif", :id=>"circle#{model.id}", :class=>"circle#{model.id}", :alt=>"wait", :style=>"display:none;"} - %div.panel-collapse.collapse{:id=>"details#{model.id}", :style=>"margin-left:1em;"} - :javascript - function load#{model.id}Details(model) { - button = document.getElementById("link#{model.id}"); - image = document.getElementById("circle#{model.id}"); - if ($('modeldetails#{model.id}').length == 0) { - $(button).hide(); - $(image).show(); - aClient = new HttpClient(); - aClient.get("#{to("/predict/modeldetails/#{model.id}")}", function(response) { - var details = document.createElement("modeldetails#{model.id}"); - details.innerHTML = response; - document.getElementById("details#{model.id}").appendChild(details); - $(button).show(); - $(image).hide(); - addExternalLinks(); - }); + - unless endpoint =~ /^Oral/ + - @models.select{|m| m.endpoint == endpoint}.each do |model| + %div.row{:id => model.id,:style=>"margin-bottom:1em;"} + %span.col-lg-4.col-md-4.col-sm-4.col-xs-4 + %input.check{:type => "checkbox", :name => "selection[#{model.id}]", :id => "selection[#{model.species.gsub(/\s+/, "_")}]", :value => true, :disabled => false} + %label{:for => "selection[#{model.species.gsub(/\s+/, "_")}]"} + = (endpoint =~ /mutagenicity/i ? "Consensus mutagenicity" : model.species) + %span.col-lg-8.col-md-8.col-sm-8.col-xs-8 + %a.btn.btn-default.btn-xs{:data=>{:toggle=>"collapse"}, :href=>"#details#{model.id}", :onclick=>"load#{model.id}Details('#{model}')", :id => "link#{model.id}", :style=>"font-size:small;"} + %span.glyphicon.glyphicon-menu-right + Details | Validation + %img.h2{:src=>"/images/wait30trans.gif", :id=>"circle#{model.id}", :class=>"circle#{model.id}", :alt=>"wait", :style=>"display:none;"} + %div.panel-collapse.collapse{:id=>"details#{model.id}", :style=>"margin-left:1em;"} + :javascript + function load#{model.id}Details(model) { + button = document.getElementById("link#{model.id}"); + span = button.childNodes[1]; + if (span.className == "glyphicon glyphicon-menu-right"){ + span.className = "glyphicon glyphicon-menu-down"; + } else if (span.className = "glyphicon glyphicon-menu-down"){ + span.className = "glyphicon glyphicon-menu-right"; + }; + image = document.getElementById("circle#{model.id}"); + if ($('modeldetails#{model.id}').length == 0) { + $(button).hide(); + $(image).show(); + aClient = new HttpClient(); + aClient.get("#{to("/predict/modeldetails/#{model.id}")}", function(response) { + var details = document.createElement("modeldetails#{model.id}"); + details.innerHTML = response; + document.getElementById("details#{model.id}").appendChild(details); + $(button).show(); + $(image).hide(); + addExternalLinks(); + }); + } } - } + - else + %div.row{:id => "Cramer",:style=>"margin-bottom:1em;"} + %span.col-lg-4.col-md-4.col-sm-4.col-xs-4 + %input.check{:type => "checkbox", :name => "selection[Cramer]", :id => "selection[Cramer]", :value => true, :disabled => false} + %label{:for => "selection[Cramer]"} + Cramer rules %fieldset#bottom.well %div.row %div.col-md-2 diff --git a/views/prediction.haml b/views/prediction.haml index b090be5..62bed8b 100644 --- a/views/prediction.haml +++ b/views/prediction.haml @@ -4,7 +4,14 @@ $('.modal').on('hidden.bs.modal', function () { $(this).removeData('bs.modal'); }); + /*TableExport.prototype.bootstrap = ["btn", "btn-default", "btn-toolbar"]; + var BootstrapTable = document.getElementById('overview'); + new TableExport(BootstrapTable, { + ignoreCSS: ".ignore", + bootstrap: true, + });*/ }); + %div.well %a.btn.btn-warning{:href => to('/predict')} %i.glyphicon.glyphicon-menu-left @@ -15,83 +22,132 @@ %table.table.table-bordered{:id=>"overview"} %tbody %tr - %td{:id=>"compound", :style=>"vertical-align:top;"} - %p= @compound.svg + %td{:id=>"compound"} + %b.title Compound + %p= embedded_svg @compound.svg, class: '.ignore', title: "#{@compound.smiles}" %p= @compound.smiles - @model_types = {} - @dbhit = {} - - @predictions.each_with_index do |prediction,i| - - type = @models[i].model.class.to_s.match("Classification") ? "Classification" : "Regression" - - @model_types[i] = type - - unit = @models[i].unit - %td{:style=>"vertical-align:top;white-space:nowrap;"} - %b{:class => "title"} - = "#{@models[i].endpoint.gsub('_', ' ')} (#{@models[i].species})" - - / check for prediction - - if prediction[:neighbors].size > 0 - %p - / show model type (classification|regression) - %b Type: - = type - %p - / check for database hit - - if prediction[:warning] =~ /\b(identical)\b/i - - @dbhit[i] = true - - / show message about dbhit and measurements + - toxtree = @predictions.pop if @toxtree == true + - unless @predictions.blank? + - @predictions.each_with_index do |prediction,i| + - if prediction.dimension > 0 + - sa_prediction = prediction[1] + - prediction = prediction[0] + - type = @models[i].model.class.to_s.match("Classification") ? "Classification" : "Regression" + - @model_types[i] = type + - unit = @models[i].unit + %td{:style=>"vertical-align:top;white-space:nowrap;"} + %b.title + = (sa_prediction ? "Consensus mutagenicity" : "#{@models[i].endpoint.gsub('_', ' ')} (#{@models[i].species})") + / check kazius + - if sa_prediction + - hash = sa_prediction[:prediction] + - confidence = sa_prediction[:confidence] + %p + %b Structural alerts: %p - %b Compound is part of the training dataset - %p - %b Measured activity: - %br - - if prediction[:measurements].is_a?(Array) - = (type == "Regression") ? prediction[:measurements].collect{|value| "#{value.delog10} (#{unit})
#{@compound.mmol_to_mg(value.delog10)} #{unit =~ /mmol\/L/ ? "(mg/L)" : "(mg/kg_bw/day)"}"}.join("
") : prediction[:measurements].join(", ") - - else - = (type == "Regression") ? "#{prediction[:measurements].delog10} (#{unit})
#{@compound.mmol_to_mg(prediction[:measurements].delog10)} #{(unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:measurements] - - - else - - @dbhit[i] = false - - / show prediction - %p %b Prediction: - / prediction popover - -#%a.btn.glyphicon.glyphicon-info-sign{:href=>"#", :title=>"Prediction", :tabindex=>"0", data: {trigger:"focus", toggle:"popover", placement:"left", html:"true", content:"

lazar searches the training dataset for similar compounds (neighbors) and calculates the prediction from their experimental activities.

Classification:
Majority vote of neighbor activities weighted by similarity.

Regression:
Prediction from a local partial least squares regression model with neighbor activities weighted by similarity.

Original publication."}} %br - = (type == "Regression") ? "#{prediction[:value].delog10} (#{unit})
#{@compound.mmol_to_mg(prediction[:value].delog10)} #{(unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:value] + =(hash[:prediction] == true ? "mutagenic" : "non-mutagenic") + %p + %b Confidence: + %br + =confidence.to_f.signif(3) + %p + %b Structural alerts for mutagenicity: + %br + =hash[:matches].flatten.first + %p + / check for prediction + - if prediction[:neighbors] and !prediction[:value].nil? + %b.title= ("Lazar #{@models[i].endpoint.gsub('_', ' ').downcase} (#{@models[i].species}):" if sa_prediction) + %p + / show model type (classification|regression) + %b Type: + = type + %p + / check for database hit + - if prediction[:info] =~ /\b(identical)\b/i + - @dbhit[i] = true + / show message about dbhit and measurements + %p + :plain + This compound was part of the training dataset. All information
+ from this compound was removed from the training data before the
+ prediction, to obtain unbiased results. + %p + %b Measured activity: + %br + - if prediction[:measurements].is_a?(Array) + = (type == "Regression") ? prediction[:measurements].collect{|value| "#{value.delog10.signif(3)} (#{unit})
#{@compound.mmol_to_mg(value.delog10).signif(3)} #{unit =~ /mmol\/L/ ? "(mg/L)" : "(mg/kg_bw/day)"}"}.join("
") : prediction[:measurements].join(", ") + - else + = (type == "Regression") ? "#{prediction[:measurements].delog10.signif(3)} (#{unit})
#{@compound.mmol_to_mg(prediction[:measurements].delog10).signif(3)} #{(unit =~ /\b(mmol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:measurements] + + - else + - @dbhit[i] = false - / show prediction interval or probability + / show prediction %p - - if type == "Regression" - %b 95% Prediction interval: - - interval = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval]) - / prediction interval popover - -#%a.btn.glyphicon.glyphicon-info-sign{:href=>"#", :title=>"Prediction intervall", :tabindex=>"0", data: {trigger:"focus", toggle:"popover", placement:"left", html:"true", content:"An estimate of prediction uncertainty. The \"real\" value should be with 95% probability within the prediction interval."}} - %br - = interval.nil? ? "--" : "#{interval[1].delog10} - #{interval[0].delog10} (#{unit})" + %b Prediction: %br - = "#{@compound.mmol_to_mg(interval[1].delog10)} - #{@compound.mmol_to_mg(interval[0].delog10)} #{(unit =~ /\b(mol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" if !prediction[:prediction_interval].nil? - - else - %b Probability: - - unless prediction[:probabilities].nil? + = (type == "Regression") ? "#{prediction[:value].delog10.signif(3)} (#{unit})
#{@compound.mmol_to_mg(prediction[:value].delog10).signif(3)} #{(unit =~ /\b(mmol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" : prediction[:value] + + / show prediction interval or probability + %p + - if type == "Regression" + %b 95% Prediction interval: + - interval = (prediction[:prediction_interval].nil? ? nil : prediction[:prediction_interval]) + %br + = interval.nil? ? "--" : "#{interval[1].delog10.signif(3)} - #{interval[0].delog10.signif(3)} (#{unit})" %br - = "#{prediction[:probabilities].keys[0]}: #{prediction[:probabilities].values[0]}" + = "#{@compound.mmol_to_mg(interval[1].delog10).signif(3)} - #{@compound.mmol_to_mg(interval[0].delog10).signif(3)} #{(unit =~ /\b(mmol\/L)\b/) ? "(mg/L)" : "(mg/kg_bw/day)"}" if !interval.nil? + - else + %b Probability: + - unless prediction[:probabilities].nil? + %br + = "#{prediction[:probabilities].keys[0]}: #{prediction[:probabilities].values[0].signif(3)}" + - if prediction[:probabilities].size == 2 + %br + = "#{prediction[:probabilities].keys[1]}: #{prediction[:probabilities].values[1].signif(3)}" + + / show warnings and info + -#%p + - if !prediction[:info].blank? + %b Info: %br - = "#{prediction[:probabilities].keys[1]}: #{prediction[:probabilities].values[1]}" - / probability popover - -#%a.btn.glyphicon.glyphicon-info-sign{:href=>"#", :title=>"Confidence", :tabindex=>"0", data: {trigger:"focus", toggle:"popover", placement:"left", html:"true", content:"Indicates the applicability domain of a model. Predictions with a high confidence can be expected to be more reliable than predictions with low confidence. Confidence values may take any value between 0 and 1. For most models confidence > 0.025 is a sensible (hard) cutoff to distinguish between reliable and unreliable predictions."}} - - / show warnings - %p - - if !prediction[:warning].nil? - %b Warnings: - %a.btn.glyphicon.glyphicon-info-sign{:href=>"javascript:void(0)", :title=>"Warnings", :tabindex=>"0", data: {trigger:"focus", toggle:"popover", placement:"auto", html:"true", content:"#{prediction[:warning]}"}} - - else - - @dbhit[i] = false - %p - = "Not enough similar compounds
in training dataset." + %p=prediction[:info].sub(/\'.*\'/,"").sub(/,/, ",
") + - if !prediction[:warnings].blank? + %b Warnings: + - prediction[:warnings].uniq.each do |warning| + %br + %p=warning.sub(/,/, ",
") + - else + %p + - if !prediction[:info].blank? + %b Info: + %br + %p=prediction[:info].sub(/\'.*\'/,"").sub(/,/, ",
") + - if !prediction[:warnings].blank? + %b Warnings: + - prediction[:warnings].uniq.each do |warning| + %p=warning.sub(/,/, ",
") + + / show Cramer rules if checked + - if @toxtree == true + %td{:style=>"vertical-align:top;white-space:nowrap;"} + %b.title Oral toxicity (Cramer rules) + %p + %b Cramer rules: + %br + =toxtree.first["Cramer rules"] + %p + %b Cramer rules, with extensions: + %br + =toxtree.last["Cramer rules, with extensions"] - / always show the neighbors table, message is given there - = haml :neighbors, :layout => false, :model_type => @model_types, :dbhit => @dbhit + / always show the neighbors table, message is given there. Except only Cramer is selected. + - unless @predictions.blank? + = haml :neighbors, :layout => false, :model_type => @model_types, :dbhit => @dbhit + diff --git a/views/significant_fragments.haml b/views/significant_fragments.haml deleted file mode 100644 index bd3598d..0000000 --- a/views/significant_fragments.haml +++ /dev/null @@ -1,66 +0,0 @@ -%div.modal-body{:style=>"padding:10px;"} - %button.close{ :type=>" button", data: { dismiss:"modal"}} × - - if @type =~ /classification/i - %div.row - %div.col-md-4 - %h3{:style=>"margin-left:8px;"} - Significant fragments: - %img{:src=>"#{@compound.uri}/image", :alt=>"#{@compound.smiles}", :title=>"#{@compound.smiles}", :width=>"300px", :heigth=>"300px"} - - if !@significant_fragments.empty? - %div.col-md-8 - %table{:id => "sf"} - %tr - / @value_map => hash; interpret the effect - %th{:style=>"border:2px solid #E6E6E6;"} - Predominantly in compounds with activity "inactive" - %th{:style=>"border:2px solid #E6E6E6;"} - p value - - @significant_fragments.each do |fragments| - / fragments == [effect, smarts, pValue] - / fragments[0] = effect - /- $logger.debug "effect:\t#{fragments[0]}\n" - - if fragments[0] == @value_map.index("inactive") - %tr - %td - / fragments[1] = smarts - = fragments[1] - -#%img{:src => "http://smartsview.de/smartsview/auto/png/1/both/#{CGI.escape(fragments[1])}", :class => "smarts"} - %td - /fragments[2] = pValue - = fragments[2] - %tr - %th{:style=>"border:2px solid #E6E6E6;"} - Predominantly in compounds with activity "active" - %th{:style=>"border:2px solid #E6E6E6;"} - p value - - @significant_fragments.each do |fragments| - / fragments == [effect, smarts, pValue] - - if fragments[0] == @value_map.index("active") - %tr - %td - -#%img{:src => "http://smartsview.de/smartsview/auto/png/1/both/#{CGI.escape(fragments[1])}", :class => "smarts"} - = fragments[1] - %td - = fragments[2] - %br - - if @type =~ /regression/i - %div.row - %div.col-md-4 - %h3 - Descriptors: - %img{:src=>"#{@compound.uri}/image", :alt=>"#{@compound.smiles}", :title=>"#{@compound.smiles}", :width=>"300px", :heigth=>"300px"} - - if !@significant_fragments.empty? - %div.col-md-8 - %table{:id =>"de"} - %tr - %th{:style=>"border:2px solid #E6E6E6;"} - Descriptor - %th{:style=>"border:2px solid #E6E6E6;"} - value - - @significant_fragments.each do |fragments| - %tr - %td - = fragments[0] - %td - = fragments[1][0].round(3) - %br diff --git a/views/style.scss b/views/style.scss index 2c84781..ac070a1 100644 --- a/views/style.scss +++ b/views/style.scss @@ -1,3 +1,29 @@ +@media (min-width: 320px){ + .page-header{ + img.media-object{ + margin-left:0; + } + background-color: #fff; + padding:10px 0 10px 0; + margin: 0; + text-align:left; + //display:inline-block; + width:100%; + } +} +@media (min-width: 480px){ + .page-header{ + img.media-object{ + margin-left:1em; + } + background-color: #fff; + padding:20px 0 20px 0; + margin: 0; + text-align:center; + //display:inline-block; + width:100%; + } +} body { background-color:#E7E7E7; } @@ -9,14 +35,13 @@ h4.head-back, h5.head-back{ } .nav-tabs { background-color: #E7E7E7; - margin-bottom: 0; li.active a:hover { background-color: #f5f5f5; } li a { - height: 5em; + height: 7em; } } img { @@ -40,6 +65,9 @@ img { .tablesorter-bootstrap thead .sorter-false { cursor: default; } +.tablesorter-bootstrap thead { + background-color: #E7E7E7; +} ul.share-buttons{ list-style: none; } @@ -50,24 +78,15 @@ ul.share-buttons{ .dropdown-menu a { padding: 0 2px 1px 2px !important; } -.page-header{ - background-color: #fff; - padding:20px 0 20px 0; - margin: 0; - text-align:center; - display:inline-block; - width:100%; -} .share{ width: 30px; padding-right: 5px; } supporters{ - background-color: white; text-align:center; img{ width: 200px; - margin-right: 1em; + margin: 1em; } } -- cgit v1.2.3