summaryrefslogtreecommitdiff
path: root/application.rb
diff options
context:
space:
mode:
Diffstat (limited to 'application.rb')
-rw-r--r--application.rb354
1 files changed, 354 insertions, 0 deletions
diff --git a/application.rb b/application.rb
new file mode 100644
index 0000000..fe9b8b4
--- /dev/null
+++ b/application.rb
@@ -0,0 +1,354 @@
+['rubygems', "haml", "sass", "rack-flash"].each do |lib|
+ require lib
+end
+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')
+
+use Rack::Flash
+set :sessions, true
+
+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 '/?' do
+ redirect url_for('/create')
+end
+
+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
+ return "unavailable"
+ end
+end
+
+get '/model/:id/:view/?' do
+ response['Content-Type'] = 'text/plain'
+ model = ToxCreateModel.get(params[:id])
+
+ begin
+ #model.process
+ #model.save
+ case params[:view]
+ when "model"
+ 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
+ return "unable to render model: id #{params[:id]}, view #{params[:view]}"
+ end
+end
+
+get '/predict/?' do
+ @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
+end
+
+get '/help' do
+ haml :help
+end
+
+get "/confidence" do
+ 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 '/models' do # create a new model
+
+ unless params[:file] and params[:file][:tempfile] #params[:endpoint] and
+ flash[:notice] = "Please upload a Excel or CSV file."
+ redirect url_for('/create')
+ 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.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
+
+ 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
+
+ # 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
+
+ @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.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")]
+ }
+ 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")]
+ }
+ 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]
+ 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
+
+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
+ DataMapper.auto_migrate!
+ response['Content-Type'] = 'text/plain'
+ "All Models deleted."
+end
+
+# SASS stylesheet
+get '/stylesheets/style.css' do
+ headers 'Content-Type' => 'text/css; charset=utf-8'
+ sass :style
+end