diff options
author | Christoph Helma <helma@in-silico.ch> | 2010-11-24 13:10:52 +0100 |
---|---|---|
committer | Christoph Helma <helma@in-silico.ch> | 2010-11-24 13:10:52 +0100 |
commit | 1952b3ed877e4791429512def5bd7102e4ba7a99 (patch) | |
tree | 624d3fde40b7abd2ae902e9635b1a1dcfa5df2b0 /application.rb | |
parent | 99e3566a11a1d51df91a6d3d016e8ec260920020 (diff) |
opentox-ruby-api-wrapper renamed to opentox-ruby
Diffstat (limited to 'application.rb')
-rw-r--r-- | application.rb | 452 |
1 files changed, 275 insertions, 177 deletions
diff --git a/application.rb b/application.rb index 6b8de41..fe9b8b4 100644 --- a/application.rb +++ b/application.rb @@ -1,252 +1,350 @@ ['rubygems', "haml", "sass", "rack-flash"].each do |lib| - require lib + require lib end -gem "opentox-ruby-api-wrapper", "= 1.6.6" -require 'opentox-ruby-api-wrapper' +gem "opentox-ruby", "~> 0" +require 'opentox-ruby' gem 'sinatra-static-assets' require 'sinatra/static_assets' require 'ftools' require File.join(File.dirname(__FILE__),'model.rb') require File.join(File.dirname(__FILE__),'helper.rb') -require File.join(File.dirname(__FILE__),'parser.rb') +#require File.join(File.dirname(__FILE__),'parser.rb') use Rack::Flash set :sessions, true -get '/?' do - redirect url_for('/create') +helpers do + + def error(message) + @model.error_messages = message + LOGGER.error message + @model.status = "Error" + @model.save + #@dataset.delete + flash[:notice] = message + redirect url_for('/create') + end + end -get '/models/?' do - @models = ToxCreateModel.all(:order => [ :created_at.desc ]) - @models.each { |model| model.process } - haml :models +get '/?' do + redirect url_for('/create') end -delete '/model/:id/?' do - model = ToxCreateModel.get(params[:id]) - begin - RestClient.delete model.uri if model.uri - RestClient.delete model.task_uri if model.task_uri - model.destroy! - flash[:notice] = "#{model.name} model deleted." - rescue - flash[:notice] = "#{model.name} model delete error." - end - redirect url_for('/models') +get '/models/?' do + @models = ToxCreateModel.all(:order => [ :created_at.desc ]) + #@models.each { |model| model.process } + haml :models end get '/model/:id/status/?' do response['Content-Type'] = 'text/plain' - model = ToxCreateModel.get(params[:id]) - begin - haml :model_status, :locals=>{:model=>model}, :layout => false - rescue + model = ToxCreateModel.get(params[:id]) + begin + haml :model_status, :locals=>{:model=>model}, :layout => false + rescue return "unavailable" - end + end end get '/model/:id/:view/?' do response['Content-Type'] = 'text/plain' - model = ToxCreateModel.get(params[:id]) + model = ToxCreateModel.get(params[:id]) begin - model.process - model.save + #model.process + #model.save case params[:view] when "model" - haml :model, :locals=>{:model=>model}, :layout => false - when /validation/ + haml :model, :locals=>{:model=>model}, :layout => false + when /validation/ haml :validation, :locals=>{:model=>model}, :layout => false - else - return "unable to render model: id #{params[:id]}, view #{params[:view]}" - end - rescue + else + return "unable to render model: id #{params[:id]}, view #{params[:view]}" + end + rescue return "unable to render model: id #{params[:id]}, view #{params[:view]}" - end + end end get '/predict/?' do - @models = ToxCreateModel.all(:order => [ :created_at.desc ]) - @models = @models.collect{|m| m if m.status == 'Completed'}.compact - haml :predict + @models = ToxCreateModel.all(:order => [ :created_at.desc ]) + @models = @models.collect{|m| m if m.status == 'Completed'}.compact + haml :predict end get '/create' do - haml :create + haml :create end get '/help' do - haml :help + haml :help end get "/confidence" do - haml :confidence + haml :confidence +end + +# proxy to get data from compound service +# (jQuery load does not work with external URIs) +get %r{/compound/(.*)} do |inchi| + inchi = URI.unescape request.env['REQUEST_URI'].sub(/^\//,'').sub(/.*compound\//,'') + OpenTox::Compound.from_inchi(inchi).to_names.join(', ') end -post '/upload' do # create a new model - - if params[:endpoint] == '' - flash[:notice] = "Please enter an endpoint name." - redirect url_for('/create') - end - unless params[:endpoint] and params[:file] and params[:file][:tempfile] - flash[:notice] = "Please enter an endpoint name and upload a Excel or CSV file." - redirect url_for('/create') - end - - @model = ToxCreateModel.new - @model.name = params[:endpoint] - feature_uri = url_for("/feature#"+URI.encode(params[:endpoint]), :full) - parser = Parser.new params[:file], feature_uri - - unless parser.format_errors.empty? - flash[:notice] = "Incorrect file format. Please follow the instructions for #{link_to "Excel", "/excel_format"} or #{link_to "CSV", "/csv_format"} formats." - end - - if parser.dataset.compounds.empty? - flash[:notice] = "Dataset #{params[:file][:filename]} is empty." - redirect url_for('/create') - end - - begin - @model.task_uri = OpenTox::Algorithm::Lazar.create_model(:dataset_uri => parser.dataset_uri, :prediction_feature => feature_uri) - rescue - flash[:notice] = "Model creation failed. Please check if the input file is in a valid #{link_to "Excel", "/excel_format"} or #{link_to "CSV", "/csv_format"} format." - redirect url_for('/create') - end +post '/models' do # create a new model - begin - validation_task_uri = OpenTox::Validation.crossvalidation( - :algorithm_uri => OpenTox::Algorithm::Lazar.uri, - :dataset_uri => parser.dataset_uri, - :prediction_feature => feature_uri, - :algorithm_params => "feature_generation_uri=#{OpenTox::Algorithm::Fminer.uri}" - ).uri - LOGGER.debug "Validation task: " + validation_task_uri - @model.validation_task_uri = validation_task_uri - rescue - flash[:notice] = "Model validation failed." + unless params[:file] and params[:file][:tempfile] #params[:endpoint] and + flash[:notice] = "Please upload a Excel or CSV file." + redirect url_for('/create') end -=begin - if parser.nr_compounds < 10 - flash[:notice] = "Too few compounds to create a prediction model. Did you provide compounds in SMILES format and classification activities as described in the #{link_to "instructions", "/excel_format"}? As a rule of thumb you will need at least 100 training compounds for nongeneric datasets. A lower number could be sufficient for congeneric datasets." - redirect url_for('/create') - end -=end + @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,"")) + task = OpenTox::Task.create("Uploading dataset and creating lazar model",url_for("/models",:full)) do + + @model.update :status => "Uploading and saving dataset" + begin + @dataset = OpenTox::Dataset.create + # check format by extension - not all browsers provide correct content-type]) + case File.extname(params[:file][:filename]) + when ".csv" + csv = params[:file][:tempfile].read + LOGGER.debug csv + @dataset.load_csv(csv) + when ".xls", ".xlsx" + @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path) + else + error "#{params[:file][:filename]} has a unsupported file type." + end + rescue => e + error "Dataset creation failed with #{e.message}" + end + @dataset.save + if @dataset.compounds.size < 10 + error "Too few compounds to create a prediction model. Did you provide compounds in SMILES format and classification activities as described in the #{link_to "instructions", "/excel_format"}? As a rule of thumb you will need at least 100 training compounds for nongeneric datasets. A lower number could be sufficient for congeneric datasets." + end + if @dataset.features.keys.size != 1 + error "More than one feature in dataset #{params[:file][:filename]}. Please delete irrelvant columns and try again." + end + if @dataset.metadata[OT.Errors] + error "Incorrect file format. Please follow the instructions for #{link_to "Excel", "/excel_format"} or #{link_to "CSV", "/csv_format"} formats." + end + @model.training_dataset = @dataset.uri + @model.nr_compounds = @dataset.compounds.size + @model.warnings = @dataset.metadata[OT.Warnings] unless @dataset.metadata[OT.Warnings].empty? + @model.save - @model.nr_compounds = parser.nr_compounds - @model.warnings = '' + @model.update :status => "Creating prediction model" + begin + lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri) + rescue => e + error "Model creation failed with '#{e.message}'. Please check if the input file is in a valid #{link_to "Excel", "/excel_format"} or #{link_to "CSV", "/csv_format"} format." + end + LOGGER.debug lazar.metadata.to_yaml + @model.feature_dataset = lazar.metadata[OT.featureDataset] + @model.uri = lazar.uri + case lazar.metadata[OT.isA] + when /Classification/ + @model.type = "classification" + when /Regression/ + @model.type = "regression" + else + @model.type = "unknown" + end + @model.save - @model.warnings += "<p>Incorrect Smiles structures (ignored):</p>" + parser.smiles_errors.join("<br/>") unless parser.smiles_errors.empty? - @model.warnings += "<p>Irregular activities (ignored):</p>" + parser.activity_errors.join("<br/>") unless parser.activity_errors.empty? - duplicate_warnings = '' - parser.duplicates.each {|inchi,lines| duplicate_warnings += "<p>#{lines.join('<br/>')}</p>" if lines.size > 1 } - @model.warnings += "<p>Duplicated structures (all structures/activities used for model building, please make sure, that the results were obtained from <em>independent</em> experiments):</p>" + duplicate_warnings unless duplicate_warnings.empty? - @model.save + unless url_for("",:full).match(/localhost/) + @model.update :status => "Validating model" + begin + validation = OpenTox::Validation.create_crossvalidation( + :algorithm_uri => OpenTox::Algorithm::Lazar.uri, + :dataset_uri => lazar.parameter("dataset_uri"), + :prediction_feature => lazar.parameter("prediction_feature"), + :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}" + ) + @model.update(:validation_uri => validation.uri) + LOGGER.debug "Validation URI: #{@model.validation_uri}" + rescue => e + LOGGER.debug "Model validation failed with #{e.message}." + @model.warnings += "Model validation failed with #{e.message}." + end - flash[:notice] = "Model creation and validation started - this may last up to several hours depending on the number and size of the training compounds." - redirect url_for('/models') + # create summary + validation.summary(@model.type).each{|k,v| eval "@model.#{k.to_s} = v"} + @model.save + + @model.update :status => "Creating validation report" + begin + @model.update(:validation_report_uri => validation.create_report) + rescue => e + LOGGER.debug "Validation report generation failed with #{e.message}." + @model.warnings += "Validation report generation failed with #{e.message}." + end - # TODO: check for empty model + @model.update :status => "Creating QMRF report" + begin + @model.update(:validation_qmrf_report_uri => validation.create_qmrf_report) + rescue => e + LOGGER.debug "Validation QMRF report generation failed with #{e.message}." + @model.warnings += "Validation QMRF report generation failed with #{e.message}." + end + end + + + + #@model.warnings += "<p>Incorrect Smiles structures (ignored):</p>" + parser.smiles_errors.join("<br/>") unless parser.smiles_errors.empty? + #@model.warnings += "<p>Irregular activities (ignored):</p>" + parser.activity_errors.join("<br/>") unless parser.activity_errors.empty? + #duplicate_warnings = '' + #parser.duplicates.each {|inchi,lines| duplicate_warnings += "<p>#{lines.join('<br/>')}</p>" if lines.size > 1 } + #@model.warnings += "<p>Duplicated structures (all structures/activities used for model building, please make sure, that the results were obtained from <em>independent</em> experiments):</p>" + duplicate_warnings unless duplicate_warnings.empty? + @model.update :status => "Completed" + lazar.uri + end + @model.update(:task_uri => task.uri) + @model.save + + flash[:notice] = "Model creation and validation started - this may last up to several hours depending on the number and size of the training compounds." + redirect url_for('/models') + +=begin +=end end post '/predict/?' do # post chemical name to model - @identifier = params[:identifier] - unless params[:selection] and params[:identifier] != '' - flash[:notice] = "Please enter a compound identifier and select an endpoint from the list." - redirect url_for('/predict') - end - begin - @compound = OpenTox::Compound.new(:name => params[:identifier]) - rescue - flash[:notice] = "Could not find a structure for '#{@identifier}'. Please try again." - redirect url_for('/predict') - end - @predictions = [] - params[:selection].keys.each do |id| - model = ToxCreateModel.get(id.to_i) - model.process unless model.uri - prediction = nil - confidence = nil - title = nil - db_activities = [] - #LOGGER.debug "curl -X POST -d 'compound_uri=#{@compound.uri}' -H 'Accept:application/x-yaml' #{model.uri}" - prediction = YAML.load(`curl -X POST -d 'compound_uri=#{@compound.uri}' -H 'Accept:application/x-yaml' #{model.uri}`) - #prediction = YAML.load(OpenTox::Model::Lazar.predict(params[:compound_uri],params[:model_uri])) - source = prediction.creator - if prediction.data[@compound.uri] - if source.to_s.match(/model/) # real prediction - prediction = prediction.data[@compound.uri].first.values.first - #LOGGER.debug prediction[File.join(@@config[:services]["opentox-model"],"lazar#classification")] - #LOGGER.debug prediction[File.join(@@config[:services]["opentox-model"],"lazar#confidence")] - if !prediction[File.join(@@config[:services]["opentox-model"],"lazar#classification")].nil? - @predictions << { + @identifier = params[:identifier] + unless params[:selection] and params[:identifier] != '' + flash[:notice] = "Please enter a compound identifier and select an endpoint from the list." + redirect url_for('/predict') + end + begin + @compound = OpenTox::Compound.from_name(params[:identifier]) + rescue + flash[:notice] = "Could not find a structure for '#{@identifier}'. Please try again." + redirect url_for('/predict') + end + @predictions = [] + params[:selection].keys.each do |id| + model = ToxCreateModel.get(id.to_i) + #model.process unless model.uri + prediction = nil + confidence = nil + title = nil + db_activities = [] + lazar = OpenTox::Model::Lazar.new model.uri + prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri) + prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri) + if prediction_dataset.metadata[OT.hasSource].match(/dataset/) + @predictions << { + :title => model.name, + :measured_activities => prediction_dataset.measured_activities(@compound) + } + else + predicted_feature = prediction_dataset.metadata[OT.dependentVariables] + prediction = OpenTox::Feature.find(predicted_feature) + LOGGER.debug prediction.to_yaml + if prediction.metadata[OT.error] + @predictions << { + :title => model.name, + :error => prediction.metadata[OT.error] + } + else + @predictions << { + :title => model.name, + :model_uri => model.uri, + :prediction => prediction.metadata[OT.prediction], + :confidence => prediction.metadata[OT.confidence] + } + end + end + # TODO failed/unavailable predictions +=begin + source = prediction.creator + if prediction.data[@compound.uri] + if source.to_s.match(/model/) # real prediction + prediction = prediction.data[@compound.uri].first.values.first + #LOGGER.debug prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#classification")] + #LOGGER.debug prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#confidence")] + if !prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#classification")].nil? + @predictions << { :title => model.name, :model_uri => model.uri, - :prediction => prediction[File.join(@@config[:services]["opentox-model"],"lazar#classification")], - :confidence => prediction[File.join(@@config[:services]["opentox-model"],"lazar#confidence")] + :prediction => prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#classification")], + :confidence => prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#confidence")] } - elsif !prediction[File.join(@@config[:services]["opentox-model"],"lazar#regression")].nil? - @predictions << { + elsif !prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#regression")].nil? + @predictions << { :title => model.name, :model_uri => model.uri, - :prediction => prediction[File.join(@@config[:services]["opentox-model"],"lazar#regression")], - :confidence => prediction[File.join(@@config[:services]["opentox-model"],"lazar#confidence")] + :prediction => prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#regression")], + :confidence => prediction[File.join(CONFIG[:services]["opentox-model"],"lazar#confidence")] } - end - else # database value - prediction = prediction.data[@compound.uri].first.values - @predictions << {:title => model.name, :measured_activities => prediction} - end - else - @predictions << {:title => model.name, :prediction => "not available (not enough similar compounds in the training dataset)"} - end - end - LOGGER.debug @predictions.inspect - - haml :prediction + end + else # database value + prediction = prediction.data[@compound.uri].first.values + @predictions << {:title => model.name, :measured_activities => prediction} + end + else + @predictions << {:title => model.name, :prediction => "not available (not enough similar compounds in the training dataset)"} + end +=end + end + LOGGER.debug @predictions.inspect + + haml :prediction end post "/lazar/?" do # get detailed prediction @page = 0 @page = params[:page].to_i if params[:page] @model_uri = params[:model_uri] - @prediction = YAML.load(OpenTox::Model::Lazar.predict(params[:compound_uri],params[:model_uri])) - @compound = OpenTox::Compound.new(:uri => params[:compound_uri]) - @title = @prediction.title - if @prediction.data[@compound.uri] - if @prediction.creator.to_s.match(/model/) # real prediction - p = @prediction.data[@compound.uri].first.values.first - if !p[File.join(@@config[:services]["opentox-model"],"lazar#classification")].nil? - feature = File.join(@@config[:services]["opentox-model"],"lazar#classification") - elsif !p[File.join(@@config[:services]["opentox-model"],"lazar#regression")].nil? - feature = File.join(@@config[:services]["opentox-model"],"lazar#regression") - end - @activity = p[feature] - @confidence = p[File.join(@@config[:services]["opentox-model"],"lazar#confidence")] - @neighbors = p[File.join(@@config[:services]["opentox-model"],"lazar#neighbors")] - @features = p[File.join(@@config[:services]["opentox-model"],"lazar#features")] - else # database value - @measured_activities = @prediction.data[@compound.uri].first.values - end - else - @activity = "not available (no similar compounds in the training dataset)" - end + lazar = OpenTox::Model::Lazar.new @model_uri + prediction_dataset_uri = lazar.run(:compound_uri => params[:compound_uri]) + @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri) + @compound = OpenTox::Compound.new(params[:compound_uri]) + #@title = prediction.metadata[DC.title] + # TODO dataset activity + #@activity = prediction.metadata[OT.prediction] + #@confidence = prediction.metadata[OT.confidence] + #@neighbors = [] + #@features = [] +# if @prediction.data[@compound.uri] +# if @prediction.creator.to_s.match(/model/) # real prediction +# p = @prediction.data[@compound.uri].first.values.first +# if !p[File.join(CONFIG[:services]["opentox-model"],"lazar#classification")].nil? +# feature = File.join(CONFIG[:services]["opentox-model"],"lazar#classification") +# elsif !p[File.join(CONFIG[:services]["opentox-model"],"lazar#regression")].nil? +# feature = File.join(CONFIG[:services]["opentox-model"],"lazar#regression") +# end +# @activity = p[feature] +# @confidence = p[File.join(CONFIG[:services]["opentox-model"],"lazar#confidence")] +# @neighbors = p[File.join(CONFIG[:services]["opentox-model"],"lazar#neighbors")] +# @features = p[File.join(CONFIG[:services]["opentox-model"],"lazar#features")] +# else # database value +# @measured_activities = @prediction.data[@compound.uri].first.values +# end +# else +# @activity = "not available (no similar compounds in the training dataset)" +# end haml :lazar end -# proxy to get data from compound service -# (jQuery load does not work with external URIs) -get %r{/compound/(.*)} do |inchi| - OpenTox::Compound.new(:inchi => inchi).names.gsub(/\n/,', ') +delete '/model/:id/?' do + model = ToxCreateModel.get(params[:id]) + begin + RestClient.delete model.uri if model.uri + RestClient.delete model.task_uri if model.task_uri + model.destroy + flash[:notice] = "#{model.name} model deleted." + rescue + flash[:notice] = "#{model.name} model delete error." + end + redirect url_for('/models') end delete '/?' do - ToxCreateModel.auto_migrate! - response['Content-Type'] = 'text/plain' - "All Models deleted." + DataMapper.auto_migrate! + response['Content-Type'] = 'text/plain' + "All Models deleted." end # SASS stylesheet |