From 2e5b069fa486859a654d8bd9e6b948e8922d84f1 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Wed, 25 Aug 2010 14:49:33 +0200 Subject: opentox-api-wrapper bumped to 1.6.6 --- application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application.rb b/application.rb index 9616841..da7e4eb 100644 --- a/application.rb +++ b/application.rb @@ -1,7 +1,7 @@ ['rubygems', "haml", "sass", "rack-flash"].each do |lib| require lib end -gem "opentox-ruby-api-wrapper", "= 1.6.5" +gem "opentox-ruby-api-wrapper", "= 1.6.6" require 'opentox-ruby-api-wrapper' gem 'sinatra-static-assets' require 'sinatra/static_assets' -- cgit v1.2.3 From d214692e1828471084b00af4115a06196c6578f7 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Mon, 13 Sep 2010 18:23:45 +0200 Subject: RDF/XML download disabled, QMRF report generation enabled --- application.rb | 7 ---- model.rb | 117 ++++++++++++++++++++++--------------------------------- views/model.haml | 14 ++++--- views/style.sass | 2 +- 4 files changed, 55 insertions(+), 85 deletions(-) diff --git a/application.rb b/application.rb index 9616841..7a18fd5 100644 --- a/application.rb +++ b/application.rb @@ -58,13 +58,6 @@ get '/model/:id/:view/?' do haml :model, :locals=>{:model=>model}, :layout => false when /validation/ haml :validation, :locals=>{:model=>model}, :layout => false - #if model.type == "classification" - #haml :classification_validation, :locals=>{:model=>model}, :layout => false - #elsif model.type == "regression" - #haml :regression_validation, :locals=>{:model=>model}, :layout => false - #else - #return "Unknown model type '#{model.type}'" - #end else return "unable to render model: id #{params[:id]}, view #{params[:view]}" end diff --git a/model.rb b/model.rb index 99d6ab3..da12f0b 100644 --- a/model.rb +++ b/model.rb @@ -1,15 +1,25 @@ class ToxCreateModel include DataMapper::Resource + property :id, Serial property :name, String, :length => 255 - property :uri, String, :length => 255 + property :warnings, Text, :length => 2**32-1 + property :type, String + property :created_at, DateTime + property :task_uri, String, :length => 255 + property :uri, String, :length => 255 + property :validation_task_uri, String, :length => 255 property :validation_uri, String, :length => 255 + property :validation_report_task_uri, String, :length => 255 property :validation_report_uri, String, :length => 255 - property :warnings, Text, :length => 2**32-1 + + property :validation_qmrf_task_uri, String, :length => 255 + property :validation_qmrf_uri, String, :length => 255 + property :nr_compounds, Integer property :nr_predictions, Integer property :true_positives, Integer @@ -23,8 +33,6 @@ class ToxCreateModel property :r_square, Float property :root_mean_squared_error, Float property :mean_absolute_error, Float - property :type, String - property :created_at, DateTime def status #begin @@ -50,6 +58,14 @@ class ToxCreateModel end end + def validation_qmrf_status + begin + RestClient.get(File.join(@validation_qmrf_task_uri, 'hasStatus')).body + rescue + "Service offline" + end + end + def algorithm begin RestClient.get(File.join(@uri, 'algorithm')).body @@ -74,64 +90,6 @@ class ToxCreateModel end end -=begin - def classification_validation - begin - uri = File.join(@validation_uri, 'statistics') - yaml = RestClient.get(uri).body - v = YAML.load(yaml) - tp=0; tn=0; fp=0; fn=0; n=0 - v[:classification_statistics][:confusion_matrix][:confusion_matrix_cell].each do |cell| - if cell[:confusion_matrix_predicted] == "true" and cell[:confusion_matrix_actual] == "true" - tp = cell[:confusion_matrix_value] - n += tp - elsif cell[:confusion_matrix_predicted] == "false" and cell[:confusion_matrix_actual] == "false" - tn = cell[:confusion_matrix_value] - n += tn - elsif cell[:confusion_matrix_predicted] == "false" and cell[:confusion_matrix_actual] == "true" - fn = cell[:confusion_matrix_value] - n += fn - elsif cell[:confusion_matrix_predicted] == "true" and cell[:confusion_matrix_actual] == "false" - fp = cell[:confusion_matrix_value] - n += fp - end - end - @nr_predictions = n - @true_positives = tp - @false_positives = fp - @true_negatives = tn - @false_negatives = fn - @correct_predictions = 100*(tp+tn).to_f/n - @weighted_area_under_roc = v[:classification_statistics][:weighted_area_under_roc].to_f - @sensitivity = tp.to_f/(tp+fn) - @specificity = tn.to_f/(tn+fp) - save - #:correct_predictions => sprintf("%.2f", 100*(tp+tn).to_f/n), - #:weighted_area_under_roc => sprintf("%.3f", v[:classification_statistics][:weighted_area_under_roc].to_f), - #:sensitivity => sprintf("%.3f", tp.to_f/(tp+fn)), - #:specificity => sprintf("%.3f", tn.to_f/(tn+fp)) - #} - rescue - "Service offline" - end - end - - def regression_validation - begin - uri = File.join(@validation_uri, 'statistics') - yaml = RestClient.get(uri).body - v = YAML.load(yaml) - @nr_predictions = v[:num_instances] - v[:num_unpredicted] - @r_square = v[:regression_statistics][:r_square] - @root_mean_squared_error = v[:regression_statistics][:root_mean_squared_error] - @mean_absolute_error = v[:regression_statistics][:mean_absolute_error] - save - rescue - "Service offline" - end - end -=end - def process if @uri.nil? and status == "Completed" @@ -148,13 +106,20 @@ class ToxCreateModel elsif @validation_uri.nil? and validation_status == "Completed" begin + update :validation_uri => RestClient.get(File.join(@validation_task_uri, 'resultURI')).body LOGGER.debug "Validation URI: #{@validation_uri}" + update :validation_report_task_uri => RestClient.post(File.join(@@config[:services]["opentox-validation"],"/report/crossvalidation"), :validation_uris => @validation_uri).body LOGGER.debug "Validation Report Task URI: #{@validation_report_task_uri}" + + update :validation_qmrf_task_uri => RestClient.post(File.join(@@config[:services]["opentox-validation"],"/reach_report/qmrf"), :model_uri => @uri).body + LOGGER.debug "QMRF Report Task URI: #{@validation_qmrf_task_uri}" + uri = File.join(@validation_uri, 'statistics') yaml = RestClient.get(uri).body v = YAML.load(yaml) + case type when "classification" tp=0; tn=0; fp=0; fn=0; n=0 @@ -192,15 +157,25 @@ class ToxCreateModel LOGGER.warn "Cannot create Validation Report Task #{@validation_report_task_uri} for Validation URI #{@validation_uri} from Task #{@validation_task_uri}" end - elsif @validation_report_uri.nil? and validation_report_status == "Completed" - begin - LOGGER.debug File.join(@validation_report_task_uri, 'resultURI') - LOGGER.debug "Report URI: "+RestClient.get(File.join(@validation_report_task_uri, 'resultURI')).body - update :validation_report_uri => RestClient.get(File.join(@validation_report_task_uri, 'resultURI')).body - rescue - LOGGER.warn "Cannot create Validation Report for Task URI #{@validation_report_task_uri} " - end - end + else + + if @validation_report_uri.nil? and validation_report_status == "Completed" + begin + update :validation_report_uri => RestClient.get(File.join(@validation_report_task_uri, 'resultURI')).body + rescue + LOGGER.warn "Cannot create Validation Report for Task URI #{@validation_report_task_uri} " + end + end + + if @validation_qmrf_uri.nil? and validation_qmrf_status == "Completed" + begin + update :validation_qmrf_uri => RestClient.get(File.join(@validation_qmrf_task_uri, 'resultURI')).body + rescue + LOGGER.warn "Cannot create QMRF Report for Task URI #{@validation_qmrf_task_uri} " + end + end + + end end diff --git a/views/model.haml b/views/model.haml index 454423a..0c8ea8f 100644 --- a/views/model.haml +++ b/views/model.haml @@ -42,20 +42,22 @@ %dd %a{:href => "#{model.training_dataset}.xls"} Excel sheet , - %a{:href => "#{model.training_dataset}.rdf"} RDF/XML - %em (experts) , + -#%a{:href => "#{model.training_dataset}.rdf"} RDF/XML + -#%em (experts) , %a{:href => "#{model.training_dataset}.yaml"} YAML %em (experts) %dt Feature dataset: %dd - %a{:href => "#{model.feature_dataset}.rdf"} RDF/XML - , + -#%a{:href => "#{model.feature_dataset}.rdf"} RDF/XML + -#, %a{:href => "#{model.feature_dataset}.yaml"} YAML %em (experts, dataset too large for Excel) %dt Model: %dd - %a{:href => "#{model.uri}.rdf"} RDF/XML - , + -#%a{:href => "#{model.uri}.rdf"} RDF/XML + -#, + - unless model.validation_qmrf_uri.nil? + %a{:href => File.join(model.validation_qmrf_uri,"editor")} QMRF Editor, %a{:href => "#{model.uri}.yaml"} YAML %em (experts, models cannot be represented in Excel) = haml :validation, :locals=>{:model=>model}, :layout => false diff --git a/views/style.sass b/views/style.sass index ea9e65a..5f362a3 100644 --- a/views/style.sass +++ b/views/style.sass @@ -85,7 +85,7 @@ body font-weight: bold color: $fg_color label - width: 25em + width: 28em display: block float: left br -- cgit v1.2.3 From 1952b3ed877e4791429512def5bd7102e4ba7a99 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Wed, 24 Nov 2010 13:10:52 +0100 Subject: opentox-ruby-api-wrapper renamed to opentox-ruby --- application.rb | 452 ++++++++++++++++++++++++---------------- helper.rb | 12 +- model.rb | 42 ++-- parser.rb | 119 ----------- public/javascripts/toxcreate.js | 3 +- views/compound_image.haml | 2 +- views/create.haml | 10 +- views/lazar.haml | 17 +- views/model.haml | 40 ++-- views/model_status.haml | 2 +- views/models.haml | 3 +- views/neighbors.haml | 15 +- views/neighbors_navigation.haml | 4 +- views/prediction.haml | 7 +- views/validation.haml | 30 ++- 15 files changed, 376 insertions(+), 382 deletions(-) delete mode 100644 parser.rb 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 += "

Incorrect Smiles structures (ignored):

" + parser.smiles_errors.join("
") unless parser.smiles_errors.empty? - @model.warnings += "

Irregular activities (ignored):

" + parser.activity_errors.join("
") unless parser.activity_errors.empty? - duplicate_warnings = '' - parser.duplicates.each {|inchi,lines| duplicate_warnings += "

#{lines.join('
')}

" if lines.size > 1 } - @model.warnings += "

Duplicated structures (all structures/activities used for model building, please make sure, that the results were obtained from independent experiments):

" + 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 += "

Incorrect Smiles structures (ignored):

" + parser.smiles_errors.join("
") unless parser.smiles_errors.empty? + #@model.warnings += "

Irregular activities (ignored):

" + parser.activity_errors.join("
") unless parser.activity_errors.empty? + #duplicate_warnings = '' + #parser.duplicates.each {|inchi,lines| duplicate_warnings += "

#{lines.join('
')}

" if lines.size > 1 } + #@model.warnings += "

Duplicated structures (all structures/activities used for model building, please make sure, that the results were obtained from independent experiments):

" + 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 diff --git a/helper.rb b/helper.rb index d100c9e..d691103 100644 --- a/helper.rb +++ b/helper.rb @@ -12,8 +12,16 @@ helpers do haml :js_link, :locals => {:name => name, :destination => destination, :method => "toggle"}, :layout => false end - def compound_image(compound,features) - haml :compound_image, :locals => {:compound => compound, :features => features}, :layout => false + def sort(descriptors) + features = {:activating => [], :deactivating => []} + + descriptors.each { |d| LOGGER.debug d.inspect; features[d[OT.effect].to_sym] << {:smarts => d[OT.smarts],:p_value => d[OT.pValue]} } + LOGGER.debug features.to_yaml + features + end + + def compound_image(compound,descriptors) + haml :compound_image, :locals => {:compound => compound, :features => sort(descriptors)}, :layout => false end def activity_markup(activity) diff --git a/model.rb b/model.rb index da12f0b..9929ab0 100644 --- a/model.rb +++ b/model.rb @@ -5,19 +5,23 @@ class ToxCreateModel property :id, Serial property :name, String, :length => 255 property :warnings, Text, :length => 2**32-1 + property :error_messages, Text, :length => 2**32-1 # :errors interferes with datamapper validation property :type, String + property :status, String, :length => 255 property :created_at, DateTime property :task_uri, String, :length => 255 property :uri, String, :length => 255 - property :validation_task_uri, String, :length => 255 + property :training_dataset, String, :length => 255 + property :feature_dataset, String, :length => 255 + #property :validation_task_uri, String, :length => 255 property :validation_uri, String, :length => 255 - property :validation_report_task_uri, String, :length => 255 + #property :validation_report_task_uri, String, :length => 255 property :validation_report_uri, String, :length => 255 - property :validation_qmrf_task_uri, String, :length => 255 + #property :validation_qmrf_task_uri, String, :length => 255 property :validation_qmrf_uri, String, :length => 255 property :nr_compounds, Integer @@ -34,13 +38,16 @@ class ToxCreateModel property :root_mean_squared_error, Float property :mean_absolute_error, Float - def status +=begin +def status #begin RestClient.get(File.join(@task_uri, 'hasStatus')).body #rescue # "Service offline" #end end +=end +=begin def validation_status begin @@ -92,29 +99,23 @@ class ToxCreateModel def process + LOGGER.debug self.to_yaml + if @uri.nil? and status == "Completed" - update :uri => RestClient.get(File.join(@task_uri, 'resultURI')).body - lazar = YAML.load(RestClient.get(@uri, :accept => "application/x-yaml").body) - case lazar.dependentVariables - when /classification/ - update :type => "classification" - when /regression/ - update :type => "regression" - else - update :type => "unknown" - end + #update :uri => RestClient.get(File.join(@task_uri, 'resultURI')).body + #lazar = YAML.load(RestClient.get(@uri, :accept => "application/x-yaml").body) elsif @validation_uri.nil? and validation_status == "Completed" begin - update :validation_uri => RestClient.get(File.join(@validation_task_uri, 'resultURI')).body - LOGGER.debug "Validation URI: #{@validation_uri}" + #update :validation_uri => RestClient.get(File.join(@validation_task_uri, 'resultURI')).body + #LOGGER.debug "Validation URI: #{@validation_uri}" - update :validation_report_task_uri => RestClient.post(File.join(@@config[:services]["opentox-validation"],"/report/crossvalidation"), :validation_uris => @validation_uri).body - LOGGER.debug "Validation Report Task URI: #{@validation_report_task_uri}" + #update :validation_report_task_uri => RestClient.post(File.join(CONFIG[:services]["opentox-validation"],"/report/crossvalidation"), :validation_uris => @validation_uri).body + #LOGGER.debug "Validation Report Task URI: #{@validation_report_task_uri}" - update :validation_qmrf_task_uri => RestClient.post(File.join(@@config[:services]["opentox-validation"],"/reach_report/qmrf"), :model_uri => @uri).body - LOGGER.debug "QMRF Report Task URI: #{@validation_qmrf_task_uri}" + #update :validation_qmrf_task_uri => RestClient.post(File.join(CONFIG[:services]["opentox-validation"],"/reach_report/qmrf"), :model_uri => @uri).body + #LOGGER.debug "QMRF Report Task URI: #{@validation_qmrf_task_uri}" uri = File.join(@validation_uri, 'statistics') yaml = RestClient.get(uri).body @@ -178,6 +179,7 @@ class ToxCreateModel end end +=end end diff --git a/parser.rb b/parser.rb deleted file mode 100644 index 8468cea..0000000 --- a/parser.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'spreadsheet' -require 'roo' -class Parser - - attr_accessor :dataset, :format_errors, :smiles_errors, :activity_errors, :duplicates, :nr_compounds, :dataset_uri - - def initialize(file, endpoint_uri) - - @file = file - @dataset = OpenTox::Dataset.new - @feature_uri = endpoint_uri - @dataset.features << endpoint_uri - @dataset.title = URI.decode(endpoint_uri.split(/#/).last) - @format_errors = "" - @smiles_errors = [] - @activity_errors = [] - @duplicates = {} - @nr_compounds = 0 - @data = [] - @activities = [] - @type = "classification" - - # check format by extension - not all browsers provide correct content-type]) - case File.extname(@file[:filename]) - when ".csv" - self.csv - when ".xls", ".xlsx" - self.excel - else - @format_errors = "#{@file[:filename]} is a unsupported file type." - return false - end - - # create dataset - @data.each do |items| - @dataset.compounds << items[0] - @dataset.data[items[0]] = [] unless @dataset.data[items[0]] - case @type - when "classification" - case items[1].to_s - when TRUE_REGEXP - @dataset.data[items[0]] << {@feature_uri => true } - when FALSE_REGEXP - @dataset.data[items[0]] << {@feature_uri => false } - end - when "regression" - if items[1].to_f == 0 - @activity_errors << "Row #{items[2]}: Zero values not allowed for regression datasets - entry ignored." - else - @dataset.data[items[0]] << {@feature_uri => items[1].to_f} - end - end - end - @dataset_uri = @dataset.save - - end - - def csv - row = 0 - @file[:tempfile].each_line do |line| - row += 1 - unless line.chomp.match(/^.+[,;].*$/) # check CSV format - @format_errors = "#{@file[:filename]} is not a valid CSV file." - return false - end - items = line.chomp.gsub(/["']/,'').split(/\s*[,;]\s*/) # remove quotes - LOGGER.debug items.join(",") - input = validate(items[0], items[1], row) # smiles, activity - @data << input if input - end - end - - def excel - excel = 'tmp/' + @file[:filename] - File.mv(@file[:tempfile].path,excel) - begin - if File.extname(@file[:filename]) == ".xlsx" - book = Excelx.new(excel) - else - book = Excel.new(excel) - end - book.default_sheet = 0 - 1.upto(book.last_row) do |row| - input = validate( book.cell(row,1), book.cell(row,2), row ) # smiles, activity - @data << input if input - end - File.safe_unlink(@file[:tempfile]) - rescue - @format_errors = "#{@file[:filename]} is not a valid Excel input file." - return false - end - end - - def validate(smiles, act, row) - compound = OpenTox::Compound.new(:smiles => smiles) - if compound.nil? or compound.inchi.nil? or compound.inchi == "" - @smiles_errors << "Row #{row}: " + [smiles,act].join(", ") - return false - end - unless numeric?(act) or classification?(act) - @activity_errors << "Row #{row}: " + [smiles,act].join(", ") - return false - end - @duplicates[compound.inchi] = [] unless @duplicates[compound.inchi] - @duplicates[compound.inchi] << "Row #{row}: " + [smiles, act].join(", ") - @type = "regression" unless classification?(act) - @nr_compounds += 1 - [ compound.uri, act , row ] - end - - def numeric?(object) - true if Float(object) rescue false - end - - def classification?(object) - !object.to_s.strip.match(TRUE_REGEXP).nil? or !object.to_s.strip.match(FALSE_REGEXP).nil? - end - -end diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index d1ed490..500b685 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -85,7 +85,8 @@ $(function() { if(/^\d+$/.test(reload_id)) loadModel(reload_id, 'validation'); }; }); - var validationCheck = setTimeout('checkValidation()',15000); + //var validationCheck = setTimeout('checkValidation()',15000); + var validationCheck = setTimeout('checkValidation()',5000); } }); diff --git a/views/compound_image.haml b/views/compound_image.haml index c6f962a..18944dc 100644 --- a/views/compound_image.haml +++ b/views/compound_image.haml @@ -1,2 +1,2 @@ -%img{:src => compound.display_smarts_uri(features[:activating].collect{|f| f[:smarts]},@features[:deactivating].collect{|f| f[:smarts]}), :alt => compound.smiles} +%img{:src => compound.matching_smarts_image_uri(features[:activating].collect{|f| f[:smarts]},features[:deactivating].collect{|f| f[:smarts]}), :alt => compound.to_smiles} diff --git a/views/create.haml b/views/create.haml index 20de401..6563494 100644 --- a/views/create.haml +++ b/views/create.haml @@ -18,13 +18,13 @@ = link_to "instructions for creating training datasets", '/help' before submitting. - %form{ :action => url_for('/upload'), :method => "post", :enctype => "multipart/form-data" } + %form{ :action => url_for('/models'), :method => "post", :enctype => "multipart/form-data" } %fieldset - %label{:for => 'endpoint'} 1. Enter #{toggle_link("#endpoint_description","endpoint")} name and #{toggle_link("#unit","unit")} (for #{toggle_link("#regression","regression")}): - %input{:type => 'text', :name => 'endpoint', :id => 'endpoint', :size => '50'} - %br + -#%label{:for => 'endpoint'} 1. Enter #{toggle_link("#endpoint_description","endpoint")} name and #{toggle_link("#unit","unit")} (for #{toggle_link("#regression","regression")}): + -#%input{:type => 'text', :name => 'endpoint', :id => 'endpoint', :size => '50'} + -#%br %label{:for => 'file'} - 2. Upload training data in + Select training dataset in = link_to "Excel", '/help' or = link_to "CSV", '/help' diff --git a/views/lazar.haml b/views/lazar.haml index 59d5396..a46f82c 100644 --- a/views/lazar.haml +++ b/views/lazar.haml @@ -12,22 +12,25 @@ %table %thead %tr - %th= @title.gsub(/_lazar_.*$/,' ').capitalize + %th= @prediction.title %th= toggle_link("#lazar_algorithm","Prediction") %th= toggle_link("#confidence","Confidence") %th Supporting information %tr - %td.image= compound_image(@compound,@features) - %td= activity_markup(@activity) - %td= sprintf('%.03g', @confidence.to_f.abs) if @confidence + -# %td + %img{:src => @compound.to_image_uri, :alt => @compound.to_smiles} + %td.image= compound_image(@compound,@prediction.descriptors(@compound)) + %td= activity_markup(@prediction.value(@compound)) + %td= sprintf('%.03g', @prediction.confidence(@compound)) + -#%td= @prediction.confidence(@compound) %td %ul %li %a{:href => "#prediction", :id => "show_names"} Names and synonyms :javascript $("a#show_names").click(function () { - $("#compound_names").load("#{File.join("compound",@compound.inchi)}"); + $("#compound_names").load("#{File.join("/compound",@compound.inchi)}"); $("tr#names").toggle(); }); %li= toggle_link("#fragments","Significant fragments") @@ -49,7 +52,7 @@ %tr#fragments{ :style => "display: none;" } %td{:colspan => '4'} = hide_link('#fragments') - = haml :feature_table, :locals => {:features => @features}, :layout => false + = haml :feature_table, :locals => {:features => sort(@prediction.descriptors(@compound))}, :layout => false %tbody#neighbors - = haml :neighbors, :locals => {:neighbors => @neighbors, :page => @page}, :layout => :false + = haml :neighbors, :locals => {:neighbors => @prediction.neighbors(@compound), :page => @page}, :layout => :false diff --git a/views/model.haml b/views/model.haml index 0c8ea8f..d34079d 100644 --- a/views/model.haml +++ b/views/model.haml @@ -8,7 +8,6 @@ %div{:id => "model_#{model.id}"} %h2 = model.name - .model %dl %dt Status: @@ -20,24 +19,25 @@ ) %dt Started: %dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") - %dt Training compounds: - %dd= model.nr_compounds - %dt Warnings: - - if model.warnings == '' - %dd - - - else + - if model.nr_compounds + %dt Training compounds: + %dd= model.nr_compounds + - if model.error_messages + %dt Errors: + %dd= model.error_messages + - if model.warnings + %dt Warnings: %a{:href => "#", :id => "show_model_#{model.id}_warnings"} show %dd{:id => "model_#{model.id}_warnings", :style => "display: none;"}= model.warnings - - - if model.status == 'Completed' - %dt Algorithm: - %dd - = toggle_link("#lazar_description","lazar") + %dt Algorithm: + %dd= toggle_link("#lazar_description","lazar") + - if model.type %dt Type: %dd= toggle_link("##{model.type}","#{model.type}") - %dt Descriptors: - %dd - %a{:href => 'http://www.maunz.de/libfminer2-bbrc-doc/'} Fminer backbone refinement classes + %dt Descriptors: + %dd + %a{:href => 'http://www.maunz.de/libfminer2-bbrc-doc/'} Fminer backbone refinement classes + - if model.training_dataset %dt Training dataset: %dd %a{:href => "#{model.training_dataset}.xls"} Excel sheet @@ -46,18 +46,22 @@ -#%em (experts) , %a{:href => "#{model.training_dataset}.yaml"} YAML %em (experts) + - if model.feature_dataset %dt Feature dataset: %dd -#%a{:href => "#{model.feature_dataset}.rdf"} RDF/XML -#, + %a{:href => "#{model.feature_dataset}.xls"} Excel sheet + , %a{:href => "#{model.feature_dataset}.yaml"} YAML - %em (experts, dataset too large for Excel) + %em (experts) + - if model.uri %dt Model: %dd -#%a{:href => "#{model.uri}.rdf"} RDF/XML -#, - - unless model.validation_qmrf_uri.nil? + - if model.validation_qmrf_uri %a{:href => File.join(model.validation_qmrf_uri,"editor")} QMRF Editor, %a{:href => "#{model.uri}.yaml"} YAML %em (experts, models cannot be represented in Excel) - = haml :validation, :locals=>{:model=>model}, :layout => false + = haml :validation, :locals=>{:model=>model}, :layout => false diff --git a/views/model_status.haml b/views/model_status.haml index 1357d99..897e792 100644 --- a/views/model_status.haml +++ b/views/model_status.haml @@ -1,2 +1,2 @@ -= image_tag("/snake_transparent.gif") if model.status.match(/running|started|created/i) +-#= image_tag("/snake_transparent.gif") if model.status.match(/running|started|created/i) = model.status diff --git a/views/models.haml b/views/models.haml index 6712e3d..17d9c8a 100644 --- a/views/models.haml +++ b/views/models.haml @@ -9,7 +9,8 @@ if(reload_validation) setTimeout('checkValidation()',15000); }); -%p Get an overview about ToxCreate models. This page is refreshed every 15 seconds to update the model status. +-# %p Get an overview about ToxCreate models. This page is refreshed every 15 seconds to update the model status. +%p Get an overview about ToxCreate models. This page is refreshed every 5 seconds to update the model status. -# explanations = haml :lazar_description, :layout => false diff --git a/views/neighbors.haml b/views/neighbors.haml index 82fafd2..87fef13 100644 --- a/views/neighbors.haml +++ b/views/neighbors.haml @@ -9,15 +9,14 @@ - first = 5*page - last = first+4 - neighbor_id = 0 -- neighbors.sort{|a,b| b.last[:similarity] <=> a.last[:similarity]}[first..last].each do |uri,data| +-# LOGGER.debug neighbors.to_yaml +- neighbors.sort{|a,b| b[OT.similarity] <=> a[OT.similarity]}[first..last].each do |neighbor| - neighbor_id += 1 - - compound = OpenTox::Compound.new(:uri => uri) + - compound = OpenTox::Compound.new(neighbor[OT.compound]) %tr - %td.image= compound_image(compound,data[:features]) - %td - - data[:activities].each do |act| - = activity_markup(act) - %td= sprintf('%.03g', data[:similarity]) + %td.image= compound_image(compound,@prediction.descriptors(compound)) + %td= activity_markup(neighbor[OT.activity]) + %td= sprintf('%.03g', neighbor[OT.similarity]) %td %ul %li @@ -41,5 +40,5 @@ %tr{:id => "fragments#{neighbor_id}", :style => "display: none;" } %td{:colspan => '4'} = hide_link("#fragments#{neighbor_id}") - = haml :feature_table, :locals => {:features => data[:features]}, :layout => false + = haml :feature_table, :locals => {:features => sort(@prediction.descriptors(compound))}, :layout => false diff --git a/views/neighbors_navigation.haml b/views/neighbors_navigation.haml index 43ec3ed..864a99f 100644 --- a/views/neighbors_navigation.haml +++ b/views/neighbors_navigation.haml @@ -7,9 +7,9 @@ #prev= "prev" unless @page.to_i == 0 - = "(#{5*@page+1}-#{5*@page+5}/#{@neighbors.size})" + = "(#{5*@page+1}-#{5*@page+5}/#{@prediction.neighbors(@compound).size})" - #next= "next" unless 5*@page.to_i+5 >= @neighbors.size + #next= "next" unless 5*@page.to_i+5 >= @prediction.neighbors(@compound).size :javascript $("#prev").click(function() { diff --git a/views/prediction.haml b/views/prediction.haml index 0140b5e..8498abc 100644 --- a/views/prediction.haml +++ b/views/prediction.haml @@ -7,8 +7,9 @@ = @identifier %tr %td - %img{:src => @compound.image_uri, :alt => @compound.smiles} + %img{:src => @compound.to_image_uri, :alt => @compound.to_smiles} - @predictions.each do |p| + - LOGGER.debug p.to_yaml %td %b = p[:title] + ":" @@ -25,8 +26,8 @@ }); ) - - elsif p[:prediction].to_s.match(/not available/) - = p[:prediction] + - elsif p[:error] + %br= p[:error] - else = activity_markup(p[:prediction]) - if p[:confidence] diff --git a/views/validation.haml b/views/validation.haml index bb44058..5fc7371 100644 --- a/views/validation.haml +++ b/views/validation.haml @@ -1,18 +1,14 @@ -%dl{:id => "model_validation_#{model.id}"} - %dt - Validation: - %input{ :id => "model_validation_report_#{model.id}", :type => "hidden", :value => "#{model.validation_report_status}", :class => "model_validation_report" } - %dd - - if model.validation_uri +- if model.validation_uri + %dl{:id => "model_validation_#{model.id}"} + %dt + Validation: + -# %input{ :id => "model_validation_report_#{model.id}", :type => "hidden", :value => "#{model.validation_report_status}", :class => "model_validation_report" } + %dd %dl - %dt - Detailed report: - %dd - - if model.validation_report_uri + - if model.validation_report_uri + %dt Detailed report: + %dd %a{:href => model.validation_report_uri, :target => "_blank"} show - - else - = image_tag("/snake_transparent.gif") if model.validation_report_status == "Running" - %a{:href => model.validation_report_task_uri} #{model.validation_report_status} %dt Number of predictions %dd= model.nr_predictions - case model.type @@ -20,8 +16,8 @@ = haml :classification_validation, :locals=>{:model=>model}, :layout => false if model.correct_predictions - when "regression" = haml :regression_validation, :locals=>{:model=>model}, :layout => false - - else - = image_tag("/snake_transparent.gif") if model.validation_status == "Running" - %a{:href => model.validation_task_uri} #{model.validation_status} - +-# else + = image_tag("/snake_transparent.gif") if model.validation_status == "Running" + %a{:href => model.validation_task_uri} #{model.validation_status} + -- cgit v1.2.3 From 1bcac85620afbccae1164a9a880a2290e360aa62 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Wed, 24 Nov 2010 14:43:20 +0100 Subject: opentox-ruby gem in config.ru --- config.ru | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.ru b/config.ru index 78b1144..1616a96 100644 --- a/config.ru +++ b/config.ru @@ -1,5 +1,5 @@ require 'rubygems' -require 'opentox-ruby-api-wrapper' +require 'opentox-ruby' require 'config/config_ru' set :app_file, __FILE__ # to get the view path right run Sinatra::Application -- cgit v1.2.3 From 04c0b50d08292f2754e1d2a6ac00a644f251f593 Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 6 Dec 2010 12:17:53 +0100 Subject: clean before merge --- LICENSE | 674 ---------------------------------------------- README | 21 -- application.rb | 132 --------- config.ru | 5 - public/opentox-rgb-72.png | Bin 570 -> 0 bytes views/about.haml | 4 - views/create.haml | 17 -- views/csv_format.haml | 19 -- views/delete.haml | 25 -- views/features.haml | 13 - views/layout.haml | 36 --- views/predict.haml | 25 -- views/prediction.haml | 16 -- views/style.sass | 92 ------- views/tasks.haml | 21 -- views/validation.haml | 264 ------------------ 16 files changed, 1364 deletions(-) delete mode 100644 LICENSE delete mode 100644 README delete mode 100644 application.rb delete mode 100644 config.ru delete mode 100644 public/opentox-rgb-72.png delete mode 100644 views/about.haml delete mode 100644 views/create.haml delete mode 100644 views/csv_format.haml delete mode 100644 views/delete.haml delete mode 100644 views/features.haml delete mode 100644 views/layout.haml delete mode 100644 views/predict.haml delete mode 100644 views/prediction.haml delete mode 100644 views/style.sass delete mode 100644 views/tasks.haml delete mode 100644 views/validation.haml diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 94a9ed0..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README b/README deleted file mode 100644 index 50cc8c3..0000000 --- a/README +++ /dev/null @@ -1,21 +0,0 @@ -A graphical interface for the OpenTox ToxModel usecase - -INSTALLATION - -1. Follow the instructions on http://wiki.github.com/helma/opentox-documentation/installation-of-opentox-webservices - -2. Install opentox-toxmodel - - git clone git://github.com/helma/opentox-toxmodel.git - - or fork opentox-toxmodel on github (http://github.com/helma/opentox-toxmodel) and clone your new repository (see guides on http://github.com) - -3. Run opentox-toxmodel - - cd opentox-toxmodel - thin -R config.ru -e production start - - point your browser to http://localhost:3000/ - - or deploy with phusion passenger (http://www.modrails.com/) - diff --git a/application.rb b/application.rb deleted file mode 100644 index 11115ff..0000000 --- a/application.rb +++ /dev/null @@ -1,132 +0,0 @@ -['rubygems', "haml", "sass"].each do |lib| - require lib -end -require 'rack-flash' -#require 'benchmark' -gem 'opentox-ruby-api-wrapper', '= 1.2.6' -require 'opentox-ruby-api-wrapper' -gem 'sinatra-static-assets' -require 'sinatra/static_assets' - -use Rack::Flash -set :sessions, true - -get '/?' do - redirect url_for('/create') -end - -get '/predict/?' do - @models = OpenTox::Model::Lazar.find_all - haml :predict -end - -get '/create' do - haml :create -end - -get '/about' do - haml :about -end - -get '/csv_format' do - haml :csv_format -end - -get '/tasks' do - @tasks = OpenTox::Task.all - haml :tasks -end - -get '/task' do - @task = OpenTox::Task.find(session[:task_uri]) - haml :task -end - -post '/upload' do # create a new model - dataset = OpenTox::Dataset.new - title = params[:endpoint].sub(/\s+/,'_') - dataset.title = title - feature_uri = url_for("/feature#"+title, :full) - feature = dataset.find_or_create_feature(feature_uri) - params[:file][:tempfile].each_line do |line| - items = line.chomp.split(/\s*,\s*/) - smiles = items[0] - compound_uri = OpenTox::Compound.new(:smiles => smiles).uri - compound = dataset.find_or_create_compound(compound_uri) - case items[1].to_s - when '1' - dataset.add(compound,feature,true) - when '0' - dataset.add(compound,feature,false) - else - flash[:notice] = "Irregular activity '#{items[1]}' for SMILES #{smiles}. Please use 1 for active and 0 for inactive compounds" - end - end - dataset_uri = dataset.save - task_uri = OpenTox::Algorithm::Lazar.create_model(:dataset_uri => dataset_uri, :feature_uri => feature_uri) - flash[:notice] = "Model creation started - this may take some time. You can view and manage the status of current tasks at the #{link_to("Tasks page", "/tasks")}. #{link_to("Reload this page", "/predict")} to use the new model." - session[:task_uri] = task_uri - redirect url_for('/predict') -end - -post '/predict/?' do # post chemical name to model - @identifier = params[:identifier] - begin - @compound = OpenTox::Compound.new(:name => params[:identifier]) - rescue - flash[:notice] = "Could not find a structure for #{@identifier}. Please try again." - redirect '/predict' - end - @predictions = [] - params[:selection].keys.each do |uri| - prediction = nil - confidence = nil - title = nil - prediction = RestClient.post uri, :compound_uri => @compound.uri, :accept => "application/x-yaml" - model = Redland::Model.new Redland::MemoryStore.new - parser = Redland::Parser.new - parser.parse_string_into_model(model,prediction,'/') - f = model.subject(RDF['type'],OT['Feature']) # this can be dangerous if OWL is not properly sorted - title = model.object(f,DC['title']).to_s - model.subjects(RDF['type'], OT['FeatureValue']).each do |v| - feature = model.object(v,OT['feature']) - feature_name = model.object(feature,DC['title']).to_s - prediction = model.object(v,OT['value']).to_s if feature_name.match(/classification/) - confidence = model.object(v,OT['value']).to_s if feature_name.match(/confidence/) - end - case prediction.to_s - when "true" - prediction = "active" - when "false" - prediction = "inactive" - else - prediction = "not available" - end - @predictions << {:title => title, :prediction => prediction, :confidence => confidence} - end - - haml :prediction - #@predictions.to_yaml -end - -post '/task/cancel' do - puts params[:task_uri] - task = OpenTox::Task.find(params[:task_uri]) - task.cancel - redirect url_for('/tasks') -end - -delete '/:id' do - #begin - OpenTox::Model::LazarClassification.delete(params[:id]) - haml :index - #rescue - #"Deletion of model with ID #{params[:id]} failed. Please check your username and password." - #end -end - -# SASS stylesheet -get '/stylesheets/style.css' do - headers 'Content-Type' => 'text/css; charset=utf-8' - sass :style -end diff --git a/config.ru b/config.ru deleted file mode 100644 index efe0004..0000000 --- a/config.ru +++ /dev/null @@ -1,5 +0,0 @@ -require 'rubygems' -require 'opentox-ruby-api-wrapper' -require 'tasks/config' -set :app_file, __FILE__ # to get the view path right -run Sinatra::Application diff --git a/public/opentox-rgb-72.png b/public/opentox-rgb-72.png deleted file mode 100644 index ab53b01..0000000 Binary files a/public/opentox-rgb-72.png and /dev/null differ diff --git a/views/about.haml b/views/about.haml deleted file mode 100644 index da0f836..0000000 --- a/views/about.haml +++ /dev/null @@ -1,4 +0,0 @@ -%p - A demonstration application for the - %a{:href => "http://www.opentox.org"} OpenTox - framework. diff --git a/views/create.haml b/views/create.haml deleted file mode 100644 index 9477210..0000000 --- a/views/create.haml +++ /dev/null @@ -1,17 +0,0 @@ -.upload - %form{ :action => url_for('/upload'), :method => "post", :enctype => "multipart/form-data" } - %ol - %li - %label{:for => 'endpoint'}Enter endpoint name: - %input{:type => 'text', :name => 'endpoint', :id => 'endpoint'} - %li - %label{:for => 'file'} - Upload training data in CSV format: - %input{:type => 'file', :name => 'file', :id => 'file'} - ( - = link_to "formatting instructions ", 'csv_format' - ) - %li - %input{ :type => "submit", :value => "Create model"} - = link_to "Cancel", 'create' - diff --git a/views/csv_format.haml b/views/csv_format.haml deleted file mode 100644 index 0943c2f..0000000 --- a/views/csv_format.haml +++ /dev/null @@ -1,19 +0,0 @@ -= link_to "Back to model creation", 'create' -%p - The input file should contain two columns, separated by a comma. Enter in the first column the chemical structure in - = link_to "SMILES", "http://en.wikipedia.org/wiki/Simplified_molecular_input_line_entry_specification" - format, in the second column the activity classification (1: active, 0: inactive). -%p Example: -%p - %code - %br CC(=O)Nc1ccc(O)cc1, 1 - %br O=c1[nH]cnc2[nH]ncc12, 1 - %br CCCCNc1cc(cc(c1Oc2ccccc2)S(=O)(=O)N)C(=O)O, 1 - %br CC(C)(C)NCC(O)COc1cccc2NC(=O)CCc12, 1 - %br CN(C)CCCC1(OCc2cc(C#N)ccc21)c3ccc(F)cc3, 1 - %br CCC(CC)CCN1C(=O)CN=C(C2CCCCC2F)c3cc(Cl)ccc13, 0 - %br CCN(CC)CC(=O)Nc1c(C)cccc1C, 0 - %br CC(C)(C)NCC(O)COc1cccc2CC(O)C(O)Cc12, 0 - %br CN1CCCC1c2cccnc2, 0 - -%p You can create input files in Excel and export them as CSV files with the "Save As" option from the menu, selecting the CSV (comma delimited) format. diff --git a/views/delete.haml b/views/delete.haml deleted file mode 100644 index 88457e0..0000000 --- a/views/delete.haml +++ /dev/null @@ -1,25 +0,0 @@ -%hr -%p - You are going to remove the - = @model['name'] - model permanently! -%p Enter your username/password if you are sure or cancel the operation. - -%form{ :action => "/#{params[:id]}", :method => "post" } - %input{:type => 'hidden', :name => "_method", :value => "delete"} - - %table - %tr - %th Username: - %td - %input{:type => "text", :name => "username"} - %tr - %th Password: - %td - %input{:type => "text", :name => "password"} - - %tr - %td - %input{:type => "submit", :value => "Delete"} - %td - %a{:href => '/'} Cancel diff --git a/views/features.haml b/views/features.haml deleted file mode 100644 index 55f53f3..0000000 --- a/views/features.haml +++ /dev/null @@ -1,13 +0,0 @@ -%a{:href => "create"}Select/create dataset -→ Select endpoint -%p Dataset #{@dataset_uri} contains more than one endpoint. -%p Please select an endpoint to predict: -%form{ :action => 'create', :method => "post" } - %input{:type => :hidden, :name => :dataset_uri, :value => @dataset_uri} - %p - %select{:name => "feature_uri"} - - @features.each do |uri,title| - %option{:value => uri} #{title} - %input{ :type => "submit", :value => "Select"} - %a{:href => 'create'} Cancel - diff --git a/views/layout.haml b/views/layout.haml deleted file mode 100644 index 5426c34..0000000 --- a/views/layout.haml +++ /dev/null @@ -1,36 +0,0 @@ -!!! -%html{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en"} - - %head - %meta{'http-equiv' => 'Content-Type', :content => 'text/html'} - %title ToxModel - %link{:rel=>'stylesheet', :href=>'stylesheets/style.css', :type => "text/css"} - - %body - %h1 ToxModel - - .index - %ul - %li{:class => ("selected" if /create/ =~ request.path )} - = link_to "Create", "/create" - %li{:class => ("selected" if /predict/ =~ request.path )} - = link_to "Predict", "/predict" - %li{:class => ("selected" if /tasks/ =~ request.path )} - = link_to "Tasks", "/tasks" - %li{:class => ("selected" if /about/ =~ request.path )} - = link_to "About", "/about" - - .content - - - if flash[:notice] - .notice - = flash[:notice] - - = yield - - .footer - © - %a{:href => 'http://www.in-silico.ch'} in silico toxicology - 2009, powered by - %a{:href => 'http://www.opentox.org'} OpenTox - diff --git a/views/predict.haml b/views/predict.haml deleted file mode 100644 index 7c4b980..0000000 --- a/views/predict.haml +++ /dev/null @@ -1,25 +0,0 @@ -.upload - - unless @models.nil? - %form{ :action => url_for('/predict'), :method => "post", :enctype => "multipart/form-data" } - %ol - %li - %label{:for => 'identifier'}Enter a compound identifier: - %input{:type => 'text', :name => 'identifier', :id => 'identifier'} - (Name, InChI, Smiles, CAS, ...) - %li - %label{:for => 'model'}Choose one or more prediction models: - %ul - - @models.each do |model| - - yaml = RestClient.get model, :accept => 'application/x-yaml' - - yaml = YAML.load yaml - %br - -# %input{:type => 'checkbox', :name => 'model_uri', :value => model} - -# %input{:type => 'checkbox', :name => "selection[#{model}]", :value => true} - %input{:type => 'checkbox', :name => "selection[#{model}]", :value => true, :id => 'model'} - = yaml[:endpoint].split(/#/).last - - %li - %p - %input{ :type => "submit", :value => "Predict"} - = link_to 'Cancel', '/' - diff --git a/views/prediction.haml b/views/prediction.haml deleted file mode 100644 index aa84fee..0000000 --- a/views/prediction.haml +++ /dev/null @@ -1,16 +0,0 @@ -%p - = link_to "New prediction", "/predict" -.predictions - %table - %tr - %th - %img{:src => @compound.image_uri, :alt => @compound.smiles} - %br= @identifier - - @predictions.each do |p| - %td - %b - = p[:title] + ":" - = p[:prediction] - %br - Confidence: - = p[:confidence] diff --git a/views/style.sass b/views/style.sass deleted file mode 100644 index 4d29ddd..0000000 --- a/views/style.sass +++ /dev/null @@ -1,92 +0,0 @@ -!bg_color = #FFFFF3 -!fg_color = #36648B -!body_color = #ECF1EF -!text_color = black - -body - background-color = !body_color - color = !text_color - height: 100% - padding: 1em - - h1 - text-align: right - - .index - - ul - margin: 0 - padding: 0 - white-space: nowrap - list-style-type: none - - li - margin: 0 - padding: 0 - display: inline - border-right: 1px solid - color = !bg_color - border-top: 1px solid - color = !bg_color - background-color = !fg_color - padding-left: 0.5em - padding-right: 0.5em - - a - text-decoration: none - color = !bg_color - &:hover - color = !bg_color - - li.selected - font-weight: bolder - background-color= !bg_color - border: 1px solid - color = !fg_color - border-bottom: 1px solid - color = !bg_color - a - color = !fg_color - - - .content - background-color = !bg_color - height: 100% - padding: 0.5em - border: 1px solid - color = !fg_color - - .notice - background-color = !body_color - margin: 2% - padding: 1% - border: 1px solid red - - .upload - text-align: left - form - li - clear: left - label - width: 20em - float: left - margin-right: 0.5em - display: block - - .predictions - :text-align center - table - background-color: white - :border-spacing 0 - :border-collapse collapse - :margin 0 - :border 1px solid - - th - :border 0 - :margin 2% - td - :border 1px solid - :padding 2% - - .footer diff --git a/views/tasks.haml b/views/tasks.haml deleted file mode 100644 index 3de21b5..0000000 --- a/views/tasks.haml +++ /dev/null @@ -1,21 +0,0 @@ -%table - %tr - %th Task - %th Resource - %th Status - %th Started - %th Finished - - @tasks.each do |t| - -# This is inefficient - make only 1 query to the task service and save variables - %tr - %td= t.uri - %td= t.resource - %td= t.status - %td= t.created_at - %td= t.finished_at - -# %td - - case t.status - - when /created|started/ - %form{:name => "cancel", :action => url_for("/task/cancel"), :method => 'post'} - %input{:type => 'hidden', :name => 'uri', :value => t.uri} - %input{:type => "submit", :value => "Cancel"} diff --git a/views/validation.haml b/views/validation.haml deleted file mode 100644 index d1af379..0000000 --- a/views/validation.haml +++ /dev/null @@ -1,264 +0,0 @@ -= "-> " -%a{ :href => "/validation/#{params[:id]}" } Validation - -%h1 - = @model['name'].capitalize.gsub(/_/,' ') - validation - -Created on -= @model['created_at'] -by -= @model['user'] -( -%a{ :href => @model['validation']['details_uri'] } Prediction details -) - -%h3 Predictions weighted by confidence index -%p - %em Best indication of the overall performance - -%table - %tr - %th - True positive predictions - %td - tp - %td - = sprintf("%0.2f",@summary[:weighted][:tp].to_f) - %tr - %th - True negative predictions - %td - tn - %td - = sprintf("%0.2f",@summary[:weighted][:tn].to_f) - %tr - %th - False positive predictions - %td - fp - %td - = sprintf("%0.2f",@summary[:weighted][:fp].to_f) - %tr - %th - False negative predictions - %td - fn - %td - = sprintf("%0.2f",@summary[:weighted][:fn].to_f) - %tr - %th - Sensitivity (true positive rate) - %td - tp/(tp+fn) - %td - = (100*@summary[:weighted][:tp].to_f/(@summary[:weighted][:tp].to_f+@summary[:weighted][:fn].to_f)).round/100.00 - %tr - %th - Specificity (true negative rate) - %td - tn/(tn+fp) - %td - = (100*@summary[:weighted][:tn].to_f/(@summary[:weighted][:tn].to_f+@summary[:weighted][:fp].to_f).to_f).round/100.00 - %tr - %th - Positive predictivity - %td - tp/(tp+fp) - %td - = (100*@summary[:weighted][:tp].to_f/(@summary[:weighted][:tp].to_f+@summary[:weighted][:fp].to_f).to_f).round/100.00 - %tr - %th - Negative predictivity - %td - tn/(tn+fn) - %td - = (100*@summary[:weighted][:tn].to_f/(@summary[:weighted][:tn].to_f+@summary[:weighted][:fn].to_f).to_f).round/100.00 - %tr - %th - False positive rate - %td - fp/(tp+fn) - %td - = (100*@summary[:weighted][:fp].to_f/(@summary[:weighted][:tp].to_f+@summary[:weighted][:fn].to_f).to_f).round/100.00 - %tr - %th - False negative rate - %td - fn/(tn+fp) - %td - = (100*@summary[:weighted][:fn].to_f/(@summary[:weighted][:tn].to_f+@summary[:weighted][:fp].to_f).to_f).round/100.00 - %tr - %th - Accuracy (concordance) - %td - (tp+tn)/(tp+fp+tn+fn) - %th - = (100*(@summary[:weighted][:tp].to_f+@summary[:weighted][:tn].to_f)/(@summary[:weighted][:tp].to_f+@summary[:weighted][:tn].to_f+@summary[:weighted][:fn].to_f+@summary[:weighted][:fp].to_f).to_f).round/100.00 - - -%h3 Predictions within the applicability domain -%p - %em Hard cutoff at confidence > 0.025 - -%table - %tr - %th - True positive predictions - %td - tp - %td - = @summary[:within_ad][:tp].to_i - %tr - %th - True negative predictions - %td - tn - %td - = @summary[:within_ad][:tn].to_i - %tr - %th - False positive predictions - %td - fp - %td - = @summary[:within_ad][:fp].to_i - %tr - %th - False negative predictions - %td - fn - %td - = @summary[:within_ad][:fn].to_i - %tr - %th - Sensitivity (true positive rate) - %td - tp/(tp+fn) - %td - = (100*@summary[:within_ad][:tp].to_i/(@summary[:within_ad][:tp].to_i+@summary[:within_ad][:fn].to_i).to_f).round/100.00 - %tr - %th - Specificity (true negative rate) - %td - tn/(tn+fp) - %td - = (100*@summary[:within_ad][:tn].to_i/(@summary[:within_ad][:tn].to_i+@summary[:within_ad][:fp].to_i).to_f).round/100.00 - %tr - %th - Positive predictivity - %td - tp/(tp+fp) - %td - = (100*@summary[:within_ad][:tp].to_i/(@summary[:within_ad][:tp].to_i+@summary[:within_ad][:fp].to_i).to_f).round/100.00 - %tr - %th - Negative predictivity - %td - tn/(tn+fn) - %td - = (100*@summary[:within_ad][:tn].to_i/(@summary[:within_ad][:tn].to_i+@summary[:within_ad][:fn].to_i).to_f).round/100.00 - %tr - %th - False positive rate - %td - fp/(tp+fn) - %td - = (100*@summary[:within_ad][:fp].to_i/(@summary[:within_ad][:tp].to_i+@summary[:within_ad][:fn].to_i).to_f).round/100.00 - %tr - %th - False negative rate - %td - fn/(tn+fp) - %td - = (100*@summary[:within_ad][:fn].to_i/(@summary[:within_ad][:tn].to_i+@summary[:within_ad][:fp].to_i).to_f).round/100.00 - %tr - %th - Accuracy (concordance) - %td - (tp+tn)/(tp+fp+tn+fn) - %th - = (100*(@summary[:within_ad][:tp].to_i+@summary[:within_ad][:tn].to_i)/(@summary[:within_ad][:tp].to_i+@summary[:within_ad][:tn].to_i+@summary[:within_ad][:fn].to_i+@summary[:within_ad][:fp].to_i).to_f).round/100.00 - -%h3 All predictions -%p - %em Poor indication of the overall performance. Depends predominatly on the fraction of compounds within the applicability domain. - -%table - %tr - %th - True positive predictions - %td - tp - %td - = @summary[:all][:tp].to_i - %tr - %th - True negative predictions - %td - tn - %td - = @summary[:all][:tn].to_i - %tr - %th - False positive predictions - %td - fp - %td - = @summary[:all][:fp].to_i - %tr - %th - False negative predictions - %td - fn - %td - = @summary[:all][:fn].to_i - %tr - %th - Sensitivity (true positive rate) - %td - tp/(tp+fn) - %td - = (100*@summary[:all][:tp].to_i/(@summary[:all][:tp].to_i+@summary[:all][:fn].to_i).to_f).round/100.00 - %tr - %th - Specificity (true negative rate) - %td - tn/(tn+fp) - %td - = (100*@summary[:all][:tn].to_i/(@summary[:all][:tn].to_i+@summary[:all][:fp].to_i).to_f).round/100.00 - %tr - %th - Positive predictivity - %td - tp/(tp+fp) - %td - = (100*@summary[:all][:tp].to_i/(@summary[:all][:tp].to_i+@summary[:all][:fp].to_i).to_f).round/100.00 - %tr - %th - Negative predictivity - %td - tn/(tn+fn) - %td - = (100*@summary[:all][:tn].to_i/(@summary[:all][:tn].to_i+@summary[:all][:fn].to_i).to_f).round/100.00 - %tr - %th - False positive rate - %td - fp/(tp+fn) - %td - = (100*@summary[:all][:fp].to_i/(@summary[:all][:tp].to_i+@summary[:all][:fn].to_i).to_f).round/100.00 - %tr - %th - False negative rate - %td - fn/(tn+fp) - %td - = (100*@summary[:all][:fn].to_i/(@summary[:all][:tn].to_i+@summary[:all][:fp].to_i).to_f).round/100.00 - %tr - %th - Accuracy (concordance) - %td - (tp+tn)/(tp+fp+tn+fn) - %th - = (100*(@summary[:all][:tp].to_i+@summary[:all][:tn].to_i)/(@summary[:all][:tp].to_i+@summary[:all][:tn].to_i+@summary[:all][:fn].to_i+@summary[:all][:fp].to_i).to_f).round/100.00 -- cgit v1.2.3 From 29ac25b9bce4d23002eae991e901732b8b2c2a39 Mon Sep 17 00:00:00 2001 From: mr Date: Thu, 9 Dec 2010 10:32:26 +0100 Subject: A&A implementation --- application.rb | 69 +++++++++++++++++++++++++++++++++++++++++++++------ helper.rb | 36 +++++++++++++++++++++++++++ model.rb | 10 ++++++++ views/create.haml | 1 + views/fragment.haml | 1 + views/layout.haml | 6 ++++- views/models.haml | 2 +- views/predict.haml | 1 + views/prediction.haml | 1 + 9 files changed, 117 insertions(+), 10 deletions(-) diff --git a/application.rb b/application.rb index fe9b8b4..ecb4b7e 100644 --- a/application.rb +++ b/application.rb @@ -10,8 +10,9 @@ 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::Session::Cookie, :expire_after => 28800, + :secret => "ui6vaiNi-change_me" use Rack::Flash -set :sessions, true helpers do @@ -27,10 +28,23 @@ helpers do end +before do + unless env['REQUEST_METHOD'] == "GET" or ( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) or !AA_SERVER + if !logged_in() + flash[:notice] = "You have to login first to do this." + redirect url_for('/login') + end + end +end + get '/?' do redirect url_for('/create') end +get '/login' do + haml :login +end + get '/models/?' do @models = ToxCreateModel.all(:order => [ :created_at.desc ]) #@models.each { |model| model.process } @@ -93,18 +107,25 @@ get %r{/compound/(.*)} do |inchi| 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(/\..*$/,"")) + unless logged_in() + logout + flash[:notice] = "Please login to create a new model." + redirect url_for('/create') + end + + @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :token_id => session[:token_id]) + @model.update :web_uri => url_for("/model/#{@model.id}", :full) + @model.save 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 + @dataset = OpenTox::Dataset.create(nil, session[:token_id]) # check format by extension - not all browsers provide correct content-type]) case File.extname(params[:file][:filename]) when ".csv" @@ -119,6 +140,7 @@ post '/models' do # create a new model rescue => e error "Dataset creation failed with #{e.message}" end + @dataset.token_id = session[:token_id] if session[:token_id] @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." @@ -136,7 +158,7 @@ post '/models' do # create a new model @model.update :status => "Creating prediction model" begin - lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri) + lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :token_id => session[:token_id]) 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 @@ -159,6 +181,7 @@ post '/models' do # create a new model validation = OpenTox::Validation.create_crossvalidation( :algorithm_uri => OpenTox::Algorithm::Lazar.uri, :dataset_uri => lazar.parameter("dataset_uri"), + :token_id => session[:token_id], :prediction_feature => lazar.parameter("prediction_feature"), :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}" ) @@ -231,7 +254,7 @@ post '/predict/?' do # post chemical name to model title = nil db_activities = [] lazar = OpenTox::Model::Lazar.new model.uri - prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri) + prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri, :token_id => session[:token_id]) prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri) if prediction_dataset.metadata[OT.hasSource].match(/dataset/) @predictions << { @@ -298,7 +321,7 @@ post "/lazar/?" do # get detailed prediction @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_dataset_uri = lazar.run(:compound_uri => params[:compound_uri], :token_id => params[:token_id]) @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri) @compound = OpenTox::Compound.new(params[:compound_uri]) #@title = prediction.metadata[DC.title] @@ -328,12 +351,42 @@ post "/lazar/?" do # get detailed prediction haml :lazar end +post '/login' do + if session[:token_id] != nil + flash[:notice] = "You are already logged in as user: #{session[:username]}. Please log out first." + redirect url_for('/login') + end + if params[:username] == '' || params[:password] == '' + flash[:notice] = "Please enter username and password." + redirect url_for('/login') + end + if login(params[:username], params[:password]) + flash[:notice] = "Login successful." + else + flash[:notice] = "Login failed." + end + haml :login +end + +post '/logout' do + logout + redirect url_for('/login') +end + delete '/model/:id/?' do model = ToxCreateModel.get(params[:id]) begin - RestClient.delete model.uri if model.uri + RestClient.delete(model.uri, :token_id => session[:token_id]) if model.uri RestClient.delete model.task_uri if model.task_uri model.destroy + unless ToxCreateModel.get(params[:id]) + begin + aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, session[:token_id]) + LOGGER.debug "Policy deleted for Dataset URI: #{uri} with result: #{aa}" + rescue + LOGGER.warn "Policy delete error for Dataset URI: #{uri}" + end + end flash[:notice] = "#{model.name} model deleted." rescue flash[:notice] = "#{model.name} model delete error." diff --git a/helper.rb b/helper.rb index d691103..a660cc2 100644 --- a/helper.rb +++ b/helper.rb @@ -1,5 +1,41 @@ helpers do + def login(username, password) + session[:token_id] = OpenTox::Authorization.authenticate(username, password) + LOGGER.debug "ToxCreate login user #{username} with token_id: " + session[:token_id].to_s + if session[:token_id] != nil + session[:username] = username + return true + else + session[:username] = "" + return false + end + end + + def logout + if session[:token_id] != nil + session[:token_id] = nil + session[:username] = "" + return true + end + return false + end + + def logged_in() + return true if !AA_SERVER + if session[:token_id] != nil + return OpenTox::Authorization.is_token_valid(session[:token_id]) + end + return false + end + + def is_authorized(uri, action) + if session[:token_id] != nil + return OpenTox::Authorization.authorize(uri, action, session[:token_id]) + end + return false + end + def hide_link(destination) @link_id = 0 unless @link_id @link_id += 1 diff --git a/model.rb b/model.rb index 9929ab0..0a08eeb 100644 --- a/model.rb +++ b/model.rb @@ -38,6 +38,11 @@ class ToxCreateModel property :root_mean_squared_error, Float property :mean_absolute_error, Float + property :token_id, String, :length => 255 + property :web_uri, String, :length => 255 + + after :save, :check_policy + =begin def status #begin @@ -181,6 +186,11 @@ def status end =end + private + def check_policy + OpenTox::Authorization.check_policy(web_uri, token_id) + end + end DataMapper.auto_upgrade! diff --git a/views/create.haml b/views/create.haml index 6563494..d83d2b9 100644 --- a/views/create.haml +++ b/views/create.haml @@ -30,6 +30,7 @@ = link_to "CSV", '/help' format: %input{:type => 'file', :name => 'file', :id => 'file', :size => '41'} + %input{:type => 'hidden', :name => 'token_id', :id => 'token_id', :value => session[:token_id]} %input{ :type => "submit", :value => "Create model"} = link_to "Cancel", '/create' diff --git a/views/fragment.haml b/views/fragment.haml index fa7495d..eae786c 100644 --- a/views/fragment.haml +++ b/views/fragment.haml @@ -2,6 +2,7 @@ %input{:type => :hidden, :name => :compound_uri, :value => compound_uri} %input{:type => :hidden, :name => :model_uri, :value => model_uri} %input{:type => :hidden, :name => :highlight, :value => smarts} + %input{:type => :hidden, :name => :token_id, :value => session[:token_id]} - if smarts.nil? %input{ :type => "submit", :value => "Reset"} - else diff --git a/views/layout.haml b/views/layout.haml index a3db5e9..f1b63e9 100644 --- a/views/layout.haml +++ b/views/layout.haml @@ -11,7 +11,8 @@ %body .logo = image_tag "/ToxCreate_rgb_72.png", :alt => 'ToxCreate', :align => 'right' - %br Create and evaluate models to predict toxicity + %br + Create and evaluate models to predict toxicity .index %ul %li{:class => ("selected" if /\/create/ =~ request.path )} @@ -20,6 +21,9 @@ = link_to "Inspect", "/models" %li{:class => ("selected" if /predict|lazar/ =~ request.path )} = link_to "Predict", "/predict" + - if AA_SERVER + %li{:class => ("selected" if /login/ =~ request.path )} + = link_to "Login", "/login" %li{:class => ("selected" if /help/ =~ request.path )} = link_to "Help", "/help" diff --git a/views/models.haml b/views/models.haml index 17d9c8a..f018c08 100644 --- a/views/models.haml +++ b/views/models.haml @@ -3,7 +3,7 @@ :javascript $(function() { if(#{stati != 0}) { - setTimeout('checkStati("#{stati_to_check}")',1500); + setTimeout('checkStati("#{stati_to_check}")',5000); } var reload_validation = true; if(reload_validation) setTimeout('checkValidation()',15000); diff --git a/views/predict.haml b/views/predict.haml index 26c8094..6337a5b 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -29,6 +29,7 @@ %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id} %br + %input{:type => 'hidden', :name => 'token_id', :id => 'token_id', :value => session[:token_id]} %input{ :type => "submit", :value => "Predict", :onclick => "getsmiles();"} = link_to 'Cancel', '/predict' diff --git a/views/prediction.haml b/views/prediction.haml index 8498abc..f0b48e8 100644 --- a/views/prediction.haml +++ b/views/prediction.haml @@ -44,6 +44,7 @@ %form{:name => "form", :action => url_for('/lazar'), :method => "post", :enctype => "multipart/form-data" } %input{:type => :hidden, :name => :compound_uri, :value => @compound.uri} %input{:type => :hidden, :name => :model_uri, :value => p[:model_uri]} + %input{:type => :hidden, :name => :token_id, :value => session[:token_id]} %input{ :type => "submit", :value => "Details"} = haml :confidence, :layout => false -- cgit v1.2.3 From 4b1c065dc6422cb1918e59950e02b83bde6c4fd2 Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 13 Dec 2010 13:55:58 +0100 Subject: A&A for model delete --- views/model.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/views/model.haml b/views/model.haml index d34079d..cd1682c 100644 --- a/views/model.haml +++ b/views/model.haml @@ -13,10 +13,11 @@ %dt Status: %dd %span{:id => "model_#{model.id}_status", :class => model.status} - = haml :model_status, :locals=>{:model=>model}, :layout => false - ( - %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete'} delete - ) + = haml :model_status, :locals=>{:model=>model}, :layout => false + - if is_authorized(model.web_uri, "DELETE") + ( + %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete'} delete + ) %dt Started: %dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") - if model.nr_compounds -- cgit v1.2.3 From 42423071defcc0798c4fd97a9817f77867d2ffcb Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 13 Dec 2010 14:47:17 +0100 Subject: add login.haml --- views/login.haml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 views/login.haml diff --git a/views/login.haml b/views/login.haml new file mode 100644 index 0000000..4f45de3 --- /dev/null +++ b/views/login.haml @@ -0,0 +1,32 @@ +.input + + %p + Please login as guest or user. You can create a user account at + = link_to "www.opentox.org/join_form .", "http://www.opentox.org/join_form" + - if !logged_in() + %form{ :action => url_for('/login'), :method => "post" } + %fieldset + %label{:for => 'username'} Username: + %input{:type => 'text', :name => 'username', :id => 'username', :size => '50'} + %br + %label{:for => 'password'} Password: + %input{:type => 'password', :name => 'password', :id => 'password', :size => '50'} + %input{ :type => "submit", :value => "Login"} + = link_to "Cancel", '/login' + %br + or login with guest-account: + %form{ :action => url_for('/login'), :method => "post" } + %input{:type => 'hidden', :name => 'username', :id => 'username', :value => "guest"} + %input{:type => 'hidden', :name => 'password', :id => 'password', :value => "guest"} + %input{ :type => "submit", :value => "Login as guest"} + - else + %form{ :action => url_for('/logout'), :method => "post" } + %p + You are logged in as user: + %b= session[:username] + - if LOGGER.level == Logger::DEBUG + %br + = session[:token_id] + %input{ :type => "submit", :value => "Logout"} + = link_to "Cancel", '/login' + \ No newline at end of file -- cgit v1.2.3 From 5a01f50477e3fecd5921a8686c80045dbfcf799d Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 14 Dec 2010 12:31:57 +0100 Subject: remove token_id from tox_create_models --- application.rb | 4 ++-- model.rb | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/application.rb b/application.rb index ecb4b7e..30458bc 100644 --- a/application.rb +++ b/application.rb @@ -140,8 +140,8 @@ post '/models' do # create a new model rescue => e error "Dataset creation failed with #{e.message}" end - @dataset.token_id = session[:token_id] if session[:token_id] - @dataset.save + token_id = session[:token_id] if session[:token_id] + @dataset.save(token_id) 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 diff --git a/model.rb b/model.rb index 0a08eeb..82a0135 100644 --- a/model.rb +++ b/model.rb @@ -38,9 +38,11 @@ class ToxCreateModel property :root_mean_squared_error, Float property :mean_absolute_error, Float - property :token_id, String, :length => 255 property :web_uri, String, :length => 255 + attr_accessor :token_id + @token_id = nil + after :save, :check_policy =begin -- cgit v1.2.3 From c5d85910256c699ea9dedc96614f342b807b023e Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 14 Dec 2010 16:40:18 +0100 Subject: rename token_id to subjectid --- application.rb | 22 +++++++++++----------- helper.rb | 18 +++++++++--------- model.rb | 6 +++--- views/create.haml | 2 +- views/fragment.haml | 2 +- views/login.haml | 4 ++-- views/predict.haml | 2 +- views/prediction.haml | 2 +- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/application.rb b/application.rb index 30458bc..2790076 100644 --- a/application.rb +++ b/application.rb @@ -118,14 +118,14 @@ post '/models' do # create a new model redirect url_for('/create') end - @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :token_id => session[:token_id]) + @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => session[:subjectid]) @model.update :web_uri => url_for("/model/#{@model.id}", :full) @model.save 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(nil, session[:token_id]) + @dataset = OpenTox::Dataset.create(nil, session[:subjectid]) # check format by extension - not all browsers provide correct content-type]) case File.extname(params[:file][:filename]) when ".csv" @@ -140,8 +140,8 @@ post '/models' do # create a new model rescue => e error "Dataset creation failed with #{e.message}" end - token_id = session[:token_id] if session[:token_id] - @dataset.save(token_id) + subjectid = session[:subjectid] if session[:subjectid] + @dataset.save(subjectid) 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 @@ -158,7 +158,7 @@ post '/models' do # create a new model @model.update :status => "Creating prediction model" begin - lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :token_id => session[:token_id]) + lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => session[:subjectid]) 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 @@ -181,7 +181,7 @@ post '/models' do # create a new model validation = OpenTox::Validation.create_crossvalidation( :algorithm_uri => OpenTox::Algorithm::Lazar.uri, :dataset_uri => lazar.parameter("dataset_uri"), - :token_id => session[:token_id], + :subjectid => session[:subjectid], :prediction_feature => lazar.parameter("prediction_feature"), :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}" ) @@ -254,7 +254,7 @@ post '/predict/?' do # post chemical name to model title = nil db_activities = [] lazar = OpenTox::Model::Lazar.new model.uri - prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri, :token_id => session[:token_id]) + prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri, :subjectid => session[:subjectid]) prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri) if prediction_dataset.metadata[OT.hasSource].match(/dataset/) @predictions << { @@ -321,7 +321,7 @@ post "/lazar/?" do # get detailed prediction @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], :token_id => params[:token_id]) + prediction_dataset_uri = lazar.run(:compound_uri => params[:compound_uri], :subjectid => params[:subjectid]) @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri) @compound = OpenTox::Compound.new(params[:compound_uri]) #@title = prediction.metadata[DC.title] @@ -352,7 +352,7 @@ post "/lazar/?" do # get detailed prediction end post '/login' do - if session[:token_id] != nil + if session[:subjectid] != nil flash[:notice] = "You are already logged in as user: #{session[:username]}. Please log out first." redirect url_for('/login') end @@ -376,12 +376,12 @@ end delete '/model/:id/?' do model = ToxCreateModel.get(params[:id]) begin - RestClient.delete(model.uri, :token_id => session[:token_id]) if model.uri + RestClient.delete(model.uri, :subjectid => session[:subjectid]) if model.uri RestClient.delete model.task_uri if model.task_uri model.destroy unless ToxCreateModel.get(params[:id]) begin - aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, session[:token_id]) + aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, session[:subjectid]) LOGGER.debug "Policy deleted for Dataset URI: #{uri} with result: #{aa}" rescue LOGGER.warn "Policy delete error for Dataset URI: #{uri}" diff --git a/helper.rb b/helper.rb index a660cc2..e892669 100644 --- a/helper.rb +++ b/helper.rb @@ -1,9 +1,9 @@ helpers do def login(username, password) - session[:token_id] = OpenTox::Authorization.authenticate(username, password) - LOGGER.debug "ToxCreate login user #{username} with token_id: " + session[:token_id].to_s - if session[:token_id] != nil + session[:subjectid] = OpenTox::Authorization.authenticate(username, password) + LOGGER.debug "ToxCreate login user #{username} with subjectid: " + session[:subjectid].to_s + if session[:subjectid] != nil session[:username] = username return true else @@ -13,8 +13,8 @@ helpers do end def logout - if session[:token_id] != nil - session[:token_id] = nil + if session[:subjectid] != nil + session[:subjectid] = nil session[:username] = "" return true end @@ -23,15 +23,15 @@ helpers do def logged_in() return true if !AA_SERVER - if session[:token_id] != nil - return OpenTox::Authorization.is_token_valid(session[:token_id]) + if session[:subjectid] != nil + return OpenTox::Authorization.is_token_valid(session[:subjectid]) end return false end def is_authorized(uri, action) - if session[:token_id] != nil - return OpenTox::Authorization.authorize(uri, action, session[:token_id]) + if session[:subjectid] != nil + return OpenTox::Authorization.authorize(uri, action, session[:subjectid]) end return false end diff --git a/model.rb b/model.rb index 82a0135..eb459b3 100644 --- a/model.rb +++ b/model.rb @@ -40,8 +40,8 @@ class ToxCreateModel property :web_uri, String, :length => 255 - attr_accessor :token_id - @token_id = nil + attr_accessor :subjectid + @subjectid = nil after :save, :check_policy @@ -190,7 +190,7 @@ def status private def check_policy - OpenTox::Authorization.check_policy(web_uri, token_id) + OpenTox::Authorization.check_policy(web_uri, subjectid) end end diff --git a/views/create.haml b/views/create.haml index d83d2b9..5aeac67 100644 --- a/views/create.haml +++ b/views/create.haml @@ -30,7 +30,7 @@ = link_to "CSV", '/help' format: %input{:type => 'file', :name => 'file', :id => 'file', :size => '41'} - %input{:type => 'hidden', :name => 'token_id', :id => 'token_id', :value => session[:token_id]} + %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} %input{ :type => "submit", :value => "Create model"} = link_to "Cancel", '/create' diff --git a/views/fragment.haml b/views/fragment.haml index eae786c..3b9f68b 100644 --- a/views/fragment.haml +++ b/views/fragment.haml @@ -2,7 +2,7 @@ %input{:type => :hidden, :name => :compound_uri, :value => compound_uri} %input{:type => :hidden, :name => :model_uri, :value => model_uri} %input{:type => :hidden, :name => :highlight, :value => smarts} - %input{:type => :hidden, :name => :token_id, :value => session[:token_id]} + %input{:type => :hidden, :name => :subjectid, :value => session[:subjectid]} - if smarts.nil? %input{ :type => "submit", :value => "Reset"} - else diff --git a/views/login.haml b/views/login.haml index 4f45de3..747430e 100644 --- a/views/login.haml +++ b/views/login.haml @@ -26,7 +26,7 @@ %b= session[:username] - if LOGGER.level == Logger::DEBUG %br - = session[:token_id] + = session[:subjectid] %input{ :type => "submit", :value => "Logout"} = link_to "Cancel", '/login' - \ No newline at end of file + diff --git a/views/predict.haml b/views/predict.haml index 6337a5b..cd5c0d4 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -29,7 +29,7 @@ %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id} %br - %input{:type => 'hidden', :name => 'token_id', :id => 'token_id', :value => session[:token_id]} + %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} %input{ :type => "submit", :value => "Predict", :onclick => "getsmiles();"} = link_to 'Cancel', '/predict' diff --git a/views/prediction.haml b/views/prediction.haml index f0b48e8..9bb3f76 100644 --- a/views/prediction.haml +++ b/views/prediction.haml @@ -44,7 +44,7 @@ %form{:name => "form", :action => url_for('/lazar'), :method => "post", :enctype => "multipart/form-data" } %input{:type => :hidden, :name => :compound_uri, :value => @compound.uri} %input{:type => :hidden, :name => :model_uri, :value => p[:model_uri]} - %input{:type => :hidden, :name => :token_id, :value => session[:token_id]} + %input{:type => :hidden, :name => :subjectid, :value => session[:subjectid]} %input{ :type => "submit", :value => "Details"} = haml :confidence, :layout => false -- cgit v1.2.3 From 66ad7c83f2a1c175be626862e6922ea975110e70 Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 15 Dec 2010 09:58:42 +0100 Subject: gitignore add temp-file *.*~ --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index de4104c..f138bed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ tmp/* log/* TODO +documentation/ +*.*~ -- cgit v1.2.3 From e4f86ba5f38e03aa6ee257be75d63dc54e07ab10 Mon Sep 17 00:00:00 2001 From: mr Date: Thu, 16 Dec 2010 12:32:45 +0100 Subject: fix CSV link in help --- views/help.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/help.haml b/views/help.haml index eabf978..a8bbf4b 100644 --- a/views/help.haml +++ b/views/help.haml @@ -20,7 +20,7 @@ Input files are accepted in %a{:href => "http://en.wikipedia.org/wiki/Microsoft_Excel"} Excel and - %a{:href => "en.wikipedia.org/wiki/Comma-separated_values"} CSV + %a{:href => "http://en.wikipedia.org/wiki/Comma-separated_values"} CSV formats. %h3 Excel example -- cgit v1.2.3 From 5f97466a1309dcada878c6d5a5d28009220ce13b Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 4 Jan 2011 16:44:06 +0100 Subject: Authorizaton for GET requests --- application.rb | 28 ++++++++++++++-------------- views/model.haml | 10 +++++----- views/models.haml | 2 +- views/neighbors_navigation.haml | 1 + 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/application.rb b/application.rb index 2790076..fbd29a9 100644 --- a/application.rb +++ b/application.rb @@ -47,8 +47,9 @@ end get '/models/?' do @models = ToxCreateModel.all(:order => [ :created_at.desc ]) + subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" #@models.each { |model| model.process } - haml :models + haml :models, :locals=>{:models=>@models,:subjectstring => subjectstring} end get '/model/:id/status/?' do @@ -64,15 +65,15 @@ end get '/model/:id/:view/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) - + subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" begin #model.process #model.save case params[:view] when "model" - haml :model, :locals=>{:model=>model}, :layout => false + haml :model, :locals=>{:model=>model,:subjectstring => subjectstring}, :layout => false when /validation/ - haml :validation, :locals=>{:model=>model}, :layout => false + haml :validation, :locals=>{:model=>model,:subjectstring => subjectstring}, :layout => false else return "unable to render model: id #{params[:id]}, view #{params[:view]}" end @@ -117,30 +118,29 @@ post '/models' do # create a new model flash[:notice] = "Please login to create a new model." redirect url_for('/create') end - - @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => session[:subjectid]) + subjectid = session[:subjectid] ? session[:subjectid] : nil + @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => subjectid) @model.update :web_uri => url_for("/model/#{@model.id}", :full) @model.save 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(nil, session[:subjectid]) + @dataset = OpenTox::Dataset.create(nil, subjectid) # 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) + @dataset.load_csv(csv, subjectid) when ".xls", ".xlsx" - @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path) + @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path, subjectid) else error "#{params[:file][:filename]} has a unsupported file type." end rescue => e error "Dataset creation failed with #{e.message}" end - subjectid = session[:subjectid] if session[:subjectid] @dataset.save(subjectid) 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." @@ -158,7 +158,7 @@ post '/models' do # create a new model @model.update :status => "Creating prediction model" begin - lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => session[:subjectid]) + lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => subjectid) 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 @@ -181,7 +181,7 @@ post '/models' do # create a new model validation = OpenTox::Validation.create_crossvalidation( :algorithm_uri => OpenTox::Algorithm::Lazar.uri, :dataset_uri => lazar.parameter("dataset_uri"), - :subjectid => session[:subjectid], + :subjectid => subjectid, :prediction_feature => lazar.parameter("prediction_feature"), :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}" ) @@ -255,7 +255,7 @@ post '/predict/?' do # post chemical name to model db_activities = [] lazar = OpenTox::Model::Lazar.new model.uri prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri, :subjectid => session[:subjectid]) - prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri) + prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri, session[:subjectid]) if prediction_dataset.metadata[OT.hasSource].match(/dataset/) @predictions << { :title => model.name, @@ -322,7 +322,7 @@ post "/lazar/?" do # get detailed prediction @model_uri = params[:model_uri] lazar = OpenTox::Model::Lazar.new @model_uri prediction_dataset_uri = lazar.run(:compound_uri => params[:compound_uri], :subjectid => params[:subjectid]) - @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri) + @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri, session[:subjectid]) @compound = OpenTox::Compound.new(params[:compound_uri]) #@title = prediction.metadata[DC.title] # TODO dataset activity diff --git a/views/model.haml b/views/model.haml index cd1682c..1788250 100644 --- a/views/model.haml +++ b/views/model.haml @@ -41,20 +41,20 @@ - if model.training_dataset %dt Training dataset: %dd - %a{:href => "#{model.training_dataset}.xls"} Excel sheet + %a{:href => "#{model.training_dataset}.xls#{subjectstring}"} Excel sheet , -#%a{:href => "#{model.training_dataset}.rdf"} RDF/XML -#%em (experts) , - %a{:href => "#{model.training_dataset}.yaml"} YAML + %a{:href => "#{model.training_dataset}.yaml#{subjectstring}" } YAML %em (experts) - if model.feature_dataset %dt Feature dataset: %dd -#%a{:href => "#{model.feature_dataset}.rdf"} RDF/XML -#, - %a{:href => "#{model.feature_dataset}.xls"} Excel sheet + %a{:href => "#{model.feature_dataset}.xls#{subjectstring}"} Excel sheet , - %a{:href => "#{model.feature_dataset}.yaml"} YAML + %a{:href => "#{model.feature_dataset}.yaml#{subjectstring}"} YAML %em (experts) - if model.uri %dt Model: @@ -63,6 +63,6 @@ -#, - if model.validation_qmrf_uri %a{:href => File.join(model.validation_qmrf_uri,"editor")} QMRF Editor, - %a{:href => "#{model.uri}.yaml"} YAML + %a{:href => "#{model.uri}.yaml#{subjectstring}"} YAML %em (experts, models cannot be represented in Excel) = haml :validation, :locals=>{:model=>model}, :layout => false diff --git a/views/models.haml b/views/models.haml index f018c08..e4e9fbb 100644 --- a/views/models.haml +++ b/views/models.haml @@ -18,4 +18,4 @@ = haml :regression, :layout => false - @models.each do |model| - = haml :model, :locals=>{:model=>model}, :layout => false + = haml :model, :locals=>{:model=>model,:subjectstring=>subjectstring}, :layout => false diff --git a/views/neighbors_navigation.haml b/views/neighbors_navigation.haml index 864a99f..bf5ebb3 100644 --- a/views/neighbors_navigation.haml +++ b/views/neighbors_navigation.haml @@ -3,6 +3,7 @@ %form{:name => "nav", :action => url_for('/lazar#prediction'), :method => "post", :enctype => "multipart/form-data", :id => "nav"} %input{:type => :hidden, :name => :compound_uri, :value => @compound.uri} %input{:type => :hidden, :name => :model_uri, :value => @model_uri} + %input{:type => :hidden, :name => :subjectid, :value => session[:subjectid]} %input{:type => :hidden, :name => :page, :id => "page"} #prev= "prev" unless @page.to_i == 0 -- cgit v1.2.3 From f02811d0d553fabfa6a608ea642a7a85d18d31da Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 5 Jan 2011 10:27:40 +0100 Subject: diverse --- application.rb | 28 ++++++++++++++-------------- views/model.haml | 10 +++++----- views/models.haml | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/application.rb b/application.rb index 2790076..fbd29a9 100644 --- a/application.rb +++ b/application.rb @@ -47,8 +47,9 @@ end get '/models/?' do @models = ToxCreateModel.all(:order => [ :created_at.desc ]) + subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" #@models.each { |model| model.process } - haml :models + haml :models, :locals=>{:models=>@models,:subjectstring => subjectstring} end get '/model/:id/status/?' do @@ -64,15 +65,15 @@ end get '/model/:id/:view/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) - + subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" begin #model.process #model.save case params[:view] when "model" - haml :model, :locals=>{:model=>model}, :layout => false + haml :model, :locals=>{:model=>model,:subjectstring => subjectstring}, :layout => false when /validation/ - haml :validation, :locals=>{:model=>model}, :layout => false + haml :validation, :locals=>{:model=>model,:subjectstring => subjectstring}, :layout => false else return "unable to render model: id #{params[:id]}, view #{params[:view]}" end @@ -117,30 +118,29 @@ post '/models' do # create a new model flash[:notice] = "Please login to create a new model." redirect url_for('/create') end - - @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => session[:subjectid]) + subjectid = session[:subjectid] ? session[:subjectid] : nil + @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => subjectid) @model.update :web_uri => url_for("/model/#{@model.id}", :full) @model.save 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(nil, session[:subjectid]) + @dataset = OpenTox::Dataset.create(nil, subjectid) # 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) + @dataset.load_csv(csv, subjectid) when ".xls", ".xlsx" - @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path) + @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path, subjectid) else error "#{params[:file][:filename]} has a unsupported file type." end rescue => e error "Dataset creation failed with #{e.message}" end - subjectid = session[:subjectid] if session[:subjectid] @dataset.save(subjectid) 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." @@ -158,7 +158,7 @@ post '/models' do # create a new model @model.update :status => "Creating prediction model" begin - lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => session[:subjectid]) + lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => subjectid) 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 @@ -181,7 +181,7 @@ post '/models' do # create a new model validation = OpenTox::Validation.create_crossvalidation( :algorithm_uri => OpenTox::Algorithm::Lazar.uri, :dataset_uri => lazar.parameter("dataset_uri"), - :subjectid => session[:subjectid], + :subjectid => subjectid, :prediction_feature => lazar.parameter("prediction_feature"), :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}" ) @@ -255,7 +255,7 @@ post '/predict/?' do # post chemical name to model db_activities = [] lazar = OpenTox::Model::Lazar.new model.uri prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri, :subjectid => session[:subjectid]) - prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri) + prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri, session[:subjectid]) if prediction_dataset.metadata[OT.hasSource].match(/dataset/) @predictions << { :title => model.name, @@ -322,7 +322,7 @@ post "/lazar/?" do # get detailed prediction @model_uri = params[:model_uri] lazar = OpenTox::Model::Lazar.new @model_uri prediction_dataset_uri = lazar.run(:compound_uri => params[:compound_uri], :subjectid => params[:subjectid]) - @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri) + @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri, session[:subjectid]) @compound = OpenTox::Compound.new(params[:compound_uri]) #@title = prediction.metadata[DC.title] # TODO dataset activity diff --git a/views/model.haml b/views/model.haml index cd1682c..1788250 100644 --- a/views/model.haml +++ b/views/model.haml @@ -41,20 +41,20 @@ - if model.training_dataset %dt Training dataset: %dd - %a{:href => "#{model.training_dataset}.xls"} Excel sheet + %a{:href => "#{model.training_dataset}.xls#{subjectstring}"} Excel sheet , -#%a{:href => "#{model.training_dataset}.rdf"} RDF/XML -#%em (experts) , - %a{:href => "#{model.training_dataset}.yaml"} YAML + %a{:href => "#{model.training_dataset}.yaml#{subjectstring}" } YAML %em (experts) - if model.feature_dataset %dt Feature dataset: %dd -#%a{:href => "#{model.feature_dataset}.rdf"} RDF/XML -#, - %a{:href => "#{model.feature_dataset}.xls"} Excel sheet + %a{:href => "#{model.feature_dataset}.xls#{subjectstring}"} Excel sheet , - %a{:href => "#{model.feature_dataset}.yaml"} YAML + %a{:href => "#{model.feature_dataset}.yaml#{subjectstring}"} YAML %em (experts) - if model.uri %dt Model: @@ -63,6 +63,6 @@ -#, - if model.validation_qmrf_uri %a{:href => File.join(model.validation_qmrf_uri,"editor")} QMRF Editor, - %a{:href => "#{model.uri}.yaml"} YAML + %a{:href => "#{model.uri}.yaml#{subjectstring}"} YAML %em (experts, models cannot be represented in Excel) = haml :validation, :locals=>{:model=>model}, :layout => false diff --git a/views/models.haml b/views/models.haml index f018c08..e4e9fbb 100644 --- a/views/models.haml +++ b/views/models.haml @@ -18,4 +18,4 @@ = haml :regression, :layout => false - @models.each do |model| - = haml :model, :locals=>{:model=>model}, :layout => false + = haml :model, :locals=>{:model=>model,:subjectstring=>subjectstring}, :layout => false -- cgit v1.2.3 From a6585af10b70c11a7e2dca3b3bc8e90e6131e847 Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 11 Jan 2011 10:58:03 +0100 Subject: disable to predict models without authorization --- views/predict.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/predict.haml b/views/predict.haml index cd5c0d4..7d81402 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -26,7 +26,7 @@ - @models.each do |model| %label{:for => model.id} = model.name - %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id} + %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id, :disabled => !is_authorized(model.uri, "POST")} %br %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} -- cgit v1.2.3 From 8da8b742db45faeff56afc5b70d1ca263e4c0bb0 Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 18 Jan 2011 17:12:54 +0100 Subject: fix javascript --- application.rb | 24 ++++++++++++++++++++++-- public/javascripts/toxcreate.js | 14 +++++++------- views/model.haml | 8 ++++++-- views/models.haml | 4 ++-- views/predict.haml | 2 +- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/application.rb b/application.rb index fbd29a9..31bab62 100644 --- a/application.rb +++ b/application.rb @@ -26,6 +26,25 @@ helpers do redirect url_for('/create') end + private + def delete_model(model, subjectid=nil) + begin RestClient.put(File.join(model.task_uri, 'Cancelled'),subjectid) if model.task_uri rescue LOGGER.warn "Can not cancel task #{model.task_uri}" end + delete_dependent(model.uri, subjectid) if model.uri + delete_dependent(model.validation_uri, subjectid) if model.validation_uri + delete_dependent(model.validation_report_uri, subjectid) if model.validation_report_uri + delete_dependent(model.validation_qmrf_uri, subjectid) if model.validation_qmrf_uri + delete_dependent(model.training_dataset, subjectid) if model.training_dataset + delete_dependent(model.feature_dataset, subjectid) if model.feature_dataset + end + + def delete_dependent(uri, subjectid=nil) + begin + RestClient.delete(uri, :subjectid => subjectid) if subjectid + RestClient.delete(uri) if !subjectid + rescue + LOGGER.warn "Can not delete uri: #{uri}" + end + end end before do @@ -234,6 +253,7 @@ post '/models' do # create a new model end post '/predict/?' do # post chemical name to model + subjectid = session[:subjectid] ? session[:subjectid] : nil @identifier = params[:identifier] unless params[:selection] and params[:identifier] != '' flash[:notice] = "Please enter a compound identifier and select an endpoint from the list." @@ -376,8 +396,8 @@ end delete '/model/:id/?' do model = ToxCreateModel.get(params[:id]) begin - RestClient.delete(model.uri, :subjectid => session[:subjectid]) if model.uri - RestClient.delete model.task_uri if model.task_uri + + delete_model(model, session[:subjectid]) model.destroy unless ToxCreateModel.get(params[:id]) begin diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index 500b685..c74701a 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -14,19 +14,19 @@ $(function() { }); }; - checkStati = function(stati) { - stati = stati.split(", ") + checkStati = function(stati, subjectstr) { + stati = stati.split(", "); $("body") var newstati = new Array; $.each(stati, function(){ - if(checkStatus(this) > 0) newstati.push(this); + if(checkStatus(this, subjectstr) > 0) newstati.push(this); }); - if (newstati.length > 0) var statusCheck = setTimeout('checkStati("' + newstati.join(", ") + '")',10000); + if (newstati.length > 0) var statusCheck = setTimeout('checkStati("' + newstati.join(", ") + '", "' + subjectstr + '")',10000); }; - checkStatus = function(id) { + checkStatus = function(id, subjectstr) { if(id == "") return -1; - var opts = {method: 'get', action: 'model/' + id + '/status', id: id}; + var opts = {method: 'get', action: 'model/' + id + '/status' + subjectstr, id: id}; var status_changed = $.ajax({ type: opts.method, url: opts.action, @@ -36,7 +36,7 @@ $(function() { '_method': 'get' }, success: function(data) { - var erg = data.search(/Running/); + var erg = data.search(/Running|Creating|Upload|Validating/); status_changed = false; if(erg < 0) status_changed = true; $("span#model_" + id + "_status").animate({"opacity": "0.1"},1000); diff --git a/views/model.haml b/views/model.haml index 1788250..6c2aa56 100644 --- a/views/model.haml +++ b/views/model.haml @@ -14,9 +14,13 @@ %dd %span{:id => "model_#{model.id}_status", :class => model.status} = haml :model_status, :locals=>{:model=>model}, :layout => false - - if is_authorized(model.web_uri, "DELETE") + - if is_authorized(model.web_uri, "DELETE") ( - %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete'} delete + %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete'} + - if model.status == "Completed" + delete + - else + stop ) %dt Started: %dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") diff --git a/views/models.haml b/views/models.haml index e4e9fbb..ab113c5 100644 --- a/views/models.haml +++ b/views/models.haml @@ -3,10 +3,10 @@ :javascript $(function() { if(#{stati != 0}) { - setTimeout('checkStati("#{stati_to_check}")',5000); + setTimeout('checkStati("#{stati_to_check}", "#{subjectstring}")',5000); } var reload_validation = true; - if(reload_validation) setTimeout('checkValidation()',15000); + //if(reload_validation) setTimeout('checkValidation()',15000); }); -# %p Get an overview about ToxCreate models. This page is refreshed every 15 seconds to update the model status. diff --git a/views/predict.haml b/views/predict.haml index 7d81402..cd5c0d4 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -26,7 +26,7 @@ - @models.each do |model| %label{:for => model.id} = model.name - %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id, :disabled => !is_authorized(model.uri, "POST")} + %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id} %br %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} -- cgit v1.2.3 From f5107eebb9afc0a54c34b6cbbf6859022e8f19c8 Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 19 Jan 2011 13:35:53 +0100 Subject: javascripts: fix status changes - changes to delete model --- public/javascripts/toxcreate.js | 22 ++++++++++++++-------- views/model.haml | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index c74701a..5acabe9 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -14,6 +14,10 @@ $(function() { }); }; + trim = function() { + return this.replace(/^\s+|\s+$/g, ''); + } + checkStati = function(stati, subjectstr) { stati = stati.split(", "); $("body") @@ -36,15 +40,15 @@ $(function() { '_method': 'get' }, success: function(data) { - var erg = data.search(/Running|Creating|Upload|Validating/); - status_changed = false; - if(erg < 0) status_changed = true; - $("span#model_" + id + "_status").animate({"opacity": "0.1"},1000); + var status_before = ""; + if ($("span#model_" + id + "_status").html()) status_before = $("span#model_" + id + "_status").html().trim(); + var status_after = data.trim(); + $("span#model_" + id + "_status").animate({"opacity": "0.2"},1000); $("span#model_" + id + "_status").animate({"opacity": "1"},1000); - if( status_changed ) { + if( status_before != status_after) { $("span#model_" + id + "_status").html(data); loadModel(id, 'model'); - id = -1; + if (status_after == "Completed") id = -1; } }, error: function(data) { @@ -100,7 +104,8 @@ jQuery.fn.deleteModel = function(type, options) { var opts = $.extend(defaults, options); this.bind(opts.trigger_on, function() { if(confirm(opts.confirm_message)) { - $(opts.elem).fadeTo("slow",0.5); + $("div#model_" + opts.id).fadeTo("slow",0.5); + $("span#model_" + opts.id + "_status").html("Deleting"); $.ajax({ type: opts.method, url: opts.action, @@ -109,9 +114,10 @@ jQuery.fn.deleteModel = function(type, options) { '_method': 'delete' }, success: function(data) { - $(opts.elem).fadeTo("slow",0).slideUp("slow").remove(); + $("div#model_" + opts.id).fadeTo("slow",0).slideUp("slow").remove(); }, error: function(data) { + $("span#model_" + opts.id + "_status").html("Delete Error"); //alert("model delete error!"); } }); diff --git a/views/model.haml b/views/model.haml index 6c2aa56..6374bd8 100644 --- a/views/model.haml +++ b/views/model.haml @@ -1,5 +1,5 @@ - uri = url_for("/model/#{model.id}", :full) -- js = "$('#delete_#{model.id}').deleteModel('DELETE', {elem: '#model_#{model.id}'});\n " + "$('#show_model_#{model.id}_warnings').toggleWarnings('#{model.id}');" +- js = "$('#delete_#{model.id}').deleteModel('DELETE', {id: '#{model.id}'});\n " + "$('#show_model_#{model.id}_warnings').toggleWarnings('#{model.id}');" :javascript $(function() { #{js} @@ -12,7 +12,7 @@ %dl %dt Status: %dd - %span{:id => "model_#{model.id}_status", :class => model.status} + %span{:id => "model_#{model.id}_status", :class => "model_status"} = haml :model_status, :locals=>{:model=>model}, :layout => false - if is_authorized(model.web_uri, "DELETE") ( -- cgit v1.2.3 From 5f19f4776bb72d5d15471ecdea19d045fe157702 Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 19 Jan 2011 13:44:57 +0100 Subject: javascripts: fix status changes - changes to delete model pt2 --- public/javascripts/toxcreate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index 5acabe9..54ab19f 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -41,7 +41,7 @@ $(function() { }, success: function(data) { var status_before = ""; - if ($("span#model_" + id + "_status").html()) status_before = $("span#model_" + id + "_status").html().trim(); + if ($("span#model_" + id + "_status") !== null) status_before = $("span#model_" + id + "_status").html().trim(); var status_after = data.trim(); $("span#model_" + id + "_status").animate({"opacity": "0.2"},1000); $("span#model_" + id + "_status").animate({"opacity": "1"},1000); -- cgit v1.2.3 From 2ce114bae009a8d7ae7fa952b20f0c5fa8e7ef6d Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 24 Jan 2011 17:39:22 +0100 Subject: finetuning js and predition page --- public/javascripts/jquery.js | 291 +++++++++++++++++++++------------------- public/javascripts/toxcreate.js | 3 +- 2 files changed, 154 insertions(+), 140 deletions(-) mode change 100755 => 100644 public/javascripts/jquery.js diff --git a/public/javascripts/jquery.js b/public/javascripts/jquery.js old mode 100755 new mode 100644 index 7c24308..8f3ca2e --- a/public/javascripts/jquery.js +++ b/public/javascripts/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.4.2 + * jQuery JavaScript Library v1.4.4 * http://jquery.com/ * * Copyright 2010, John Resig @@ -11,144 +11,157 @@ * Copyright 2010, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * - * Date: Sat Feb 13 22:33:48 2010 -0500 + * Date: Thu Nov 11 19:04:53 2010 -0500 */ -(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, -Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& -(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, -a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== -"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, -function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; -var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, -parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= -false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= -s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, -applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; -else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, -a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== -w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, -cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= -c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); -a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, -function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); -k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), -C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= -e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& -f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; -if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", -e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, -"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, -d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, -e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); -t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| -g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, -CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, -g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, -text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, -setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= -h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== -"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, -h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& -q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; -if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); -(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: -function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= -{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== -"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", -d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? -a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== -1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h= +h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;kd)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La, +"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this, +e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a, +"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+ +a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/, +C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j, +s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this, +j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length}, +toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j=== +-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false; +if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload", +b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&& +!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&& +l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H
a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), +k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, +scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= +false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= +1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
t
";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= +"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= +c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); +else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; +if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, +attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& +b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; +c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, +arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= +d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ +c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== +8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== +"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ +d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= +B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== +"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== +0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); +(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; +break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, +q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= +l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, +m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== +true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== +g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- +0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== +i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; +if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, +g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); +n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& +function(){var g=k,i=t.createElement("div");i.innerHTML="

";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| +p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= +t.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? +function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= +h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): +c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, +2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, +b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& +e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1, +""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, -""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); -return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", -""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= -c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? -c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= -function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= -Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, -"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= -a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= -a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== -"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, -serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), -function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, -global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& -e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? -"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== -false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= -false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", -c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| -d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); -g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== -1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== -"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; -if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== -"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| -c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; -this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= -this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, -e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; -a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); -c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, -d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- -f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": -"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in -e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; +else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", +prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| +b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- +1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); +d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, +jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, +zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), +h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); +if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= +d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; +e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, +ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== +"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& +!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, +getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", +script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| +!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= +false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; +A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", +b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& +c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| +c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= +encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", +[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), +e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); +if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", +3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, +d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* +Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} +var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; +this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| +this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= +c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, +e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& +c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); +c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ +b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index 54ab19f..aef0455 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -41,7 +41,8 @@ $(function() { }, success: function(data) { var status_before = ""; - if ($("span#model_" + id + "_status") !== null) status_before = $("span#model_" + id + "_status").html().trim(); + if ($("span#model_" + id + "_status") != null) status_before = $("span#model_" + id + "_status").html().trim(); + if (status_before == "Deleting") return -1; var status_after = data.trim(); $("span#model_" + id + "_status").animate({"opacity": "0.2"},1000); $("span#model_" + id + "_status").animate({"opacity": "1"},1000); -- cgit v1.2.3 From 3203db9d2b47d0ac86c74829193f6e53e5eb76a8 Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 24 Jan 2011 17:47:54 +0100 Subject: finetuning js, predition page, jquery from 1.4.2 to 1.4.4 --- views/predict.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/predict.haml b/views/predict.haml index cd5c0d4..b6e1088 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -26,7 +26,7 @@ - @models.each do |model| %label{:for => model.id} = model.name - %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id} + %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id, :disabled => !is_authorized(model.uri, "GET")} %br %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} -- cgit v1.2.3 From 4edc55affca2a5312f5e9b03c874c02c0fc0dce6 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Fri, 28 Jan 2011 17:38:58 +0100 Subject: default login as guest --- application.rb | 21 +++++++++++++-------- helper.rb | 1 + views/create.haml | 18 +++++++++++++----- views/layout.haml | 4 ++++ views/login.haml | 53 ++++++++++++++++++++++++++++++++++------------------- views/style.sass | 52 ++++++++++++++++++++++++++++------------------------ 6 files changed, 93 insertions(+), 56 deletions(-) diff --git a/application.rb b/application.rb index 2790076..fe5f511 100644 --- a/application.rb +++ b/application.rb @@ -29,12 +29,13 @@ helpers do end before do - unless env['REQUEST_METHOD'] == "GET" or ( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) or !AA_SERVER - if !logged_in() - flash[:notice] = "You have to login first to do this." - redirect url_for('/login') + #unless env['REQUEST_METHOD'] == "GET" or ( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) or !AA_SERVER + if !logged_in and !( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) #or !AA_SERVER + login("guest","guest") + #flash[:notice] = "You have to login first to do this." + #redirect url_for('/login') end - end + #end end get '/?' do @@ -352,20 +353,24 @@ post "/lazar/?" do # get detailed prediction end post '/login' do +=begin if session[:subjectid] != nil flash[:notice] = "You are already logged in as user: #{session[:username]}. Please log out first." redirect url_for('/login') end +=end if params[:username] == '' || params[:password] == '' flash[:notice] = "Please enter username and password." redirect url_for('/login') end if login(params[:username], params[:password]) - flash[:notice] = "Login successful." + flash[:notice] = "Welcome #{session[:username]}!" + redirect url_for('/create') + #haml :create else - flash[:notice] = "Login failed." + flash[:notice] = "Login failed. Please try again." + haml :login end - haml :login end post '/logout' do diff --git a/helper.rb b/helper.rb index e892669..3fbd06f 100644 --- a/helper.rb +++ b/helper.rb @@ -1,6 +1,7 @@ helpers do def login(username, password) + logout session[:subjectid] = OpenTox::Authorization.authenticate(username, password) LOGGER.debug "ToxCreate login user #{username} with subjectid: " + session[:subjectid].to_s if session[:subjectid] != nil diff --git a/views/create.haml b/views/create.haml index 5aeac67..c04f841 100644 --- a/views/create.haml +++ b/views/create.haml @@ -19,18 +19,15 @@ before submitting. %form{ :action => url_for('/models'), :method => "post", :enctype => "multipart/form-data" } + %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} %fieldset - -#%label{:for => 'endpoint'} 1. Enter #{toggle_link("#endpoint_description","endpoint")} name and #{toggle_link("#unit","unit")} (for #{toggle_link("#regression","regression")}): - -#%input{:type => 'text', :name => 'endpoint', :id => 'endpoint', :size => '50'} - -#%br %label{:for => 'file'} - Select training dataset in + Select training data in = link_to "Excel", '/help' or = link_to "CSV", '/help' format: %input{:type => 'file', :name => 'file', :id => 'file', :size => '41'} - %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} %input{ :type => "submit", :value => "Create model"} = link_to "Cancel", '/create' @@ -40,3 +37,14 @@ = haml :regression, :layout => false = haml :endpoint, :layout => false = haml :unit, :layout => false + + .login_notice + + - if session[:username] == "guest" + You are currently logged in as + %b guest + and your models can be modified or deleted by other guests. Please + = link_to "log in", "/login" + with your + %a{:href => "www.opentox.org"} OpenTox + account to control your model permissions. diff --git a/views/layout.haml b/views/layout.haml index f1b63e9..78af858 100644 --- a/views/layout.haml +++ b/views/layout.haml @@ -28,6 +28,10 @@ = link_to "Help", "/help" .content + .login_info + User: + %b= session[:username] + - if `hostname`.match(/ot-test/) .notice This service is for testing purposes only - once a week all models will be deleted. Please send bug reports and feature requests to our diff --git a/views/login.haml b/views/login.haml index 747430e..6d15bb7 100644 --- a/views/login.haml +++ b/views/login.haml @@ -1,32 +1,47 @@ .input - %p - Please login as guest or user. You can create a user account at - = link_to "www.opentox.org/join_form .", "http://www.opentox.org/join_form" - - if !logged_in() - %form{ :action => url_for('/login'), :method => "post" } - %fieldset - %label{:for => 'username'} Username: - %input{:type => 'text', :name => 'username', :id => 'username', :size => '50'} - %br - %label{:for => 'password'} Password: - %input{:type => 'password', :name => 'password', :id => 'password', :size => '50'} - %input{ :type => "submit", :value => "Login"} - = link_to "Cancel", '/login' + - if session[:username] == "guest" + .login_notice + You are currently logged in as + %b guest + and your models can be modified or deleted by other guests. + To control models permissions you can + log in with your + %a{:href => "http://www.opentox.org"} OpenTox + username/password below. If have no + %a{:href => "http://www.opentox.org"} OpenTox + account yet you can + %a{:href => "http://www.opentox.org/join_form"} register here. + - else + %p + Change + %a{:href => "http://www.opentox.org"} OpenTox + account: + + -# if !logged_in() + %form{ :action => url_for('/login'), :method => "post" } + %fieldset + %label{:for => 'username'} Username: + %input{:type => 'text', :name => 'username', :id => 'username', :size => '50'} + %br + %label{:for => 'password'} Password: + %input{:type => 'password', :name => 'password', :id => 'password', :size => '50'} + %input{ :type => "submit", :value => "Login"} + = link_to "Cancel", '/login' + - if session[:username] != "guest" %br - or login with guest-account: %form{ :action => url_for('/login'), :method => "post" } %input{:type => 'hidden', :name => 'username', :id => 'username', :value => "guest"} %input{:type => 'hidden', :name => 'password', :id => 'password', :value => "guest"} %input{ :type => "submit", :value => "Login as guest"} - - else + -# else %form{ :action => url_for('/logout'), :method => "post" } - %p - You are logged in as user: + -#%p + You are logged in as: %b= session[:username] - - if LOGGER.level == Logger::DEBUG + -# if LOGGER.level == Logger::DEBUG %br = session[:subjectid] - %input{ :type => "submit", :value => "Logout"} + %input{ :type => "submit", :value => "Switch user"} = link_to "Cancel", '/login' diff --git a/views/style.sass b/views/style.sass index 5f362a3..b69e28c 100644 --- a/views/style.sass +++ b/views/style.sass @@ -4,7 +4,7 @@ $fg_color: #424345 $ot_purple: #5d308a $body_color: white $text_color: black -$notice_border: black +$notice_border: red body min-width: 30em @@ -34,13 +34,10 @@ body margin: 0 padding: 2px display: inline - border-right: 1px solid - color: $body_color - border-top: 1px solid - color: $body_color + border-right: 1px solid $body_color + border-bottom: 1px solid $body_color background-color: $fg_color - padding-left: 0.5em - padding-right: 0.5em + padding: 0.2em 0.5em font-weight: bold a text-decoration: none @@ -50,48 +47,55 @@ body li.selected font-weight: bold background-color: $bg_color - border: 1px solid - color: $fg_color - border-bottom: 1px solid - color: $bg_color + border-bottom: 1px solid $bg_color a color: $text_color .content + clear: both background-color: $bg_color height: 100% padding: 1em - border: 1px solid - color: $fg_color + border-top: 1px solid $body_color h2 margin: 20px 3px 2px 3px .notice - padding: 1em + //clear: both + //margin-top: 1em + padding: 0.5em 1em background-color: $body_color border: 1px solid border-color: $notice_border + .login_info + text-align: right + font-size: small + .login_notice + //clear: both + font-size: small + background-color: $bg_color + padding: 1em 0em + //font-style: italic .input + //clear: both text-align: left form padding: 1em background-color: $bg_color2 - border: 1px solid - color: $fg_color fieldset - margin: 0 + //margin: 0 padding: 0 + padding-right: 1em border: 0 legend font-weight: bold color: $fg_color label - width: 28em - display: block - float: left + //width: 28em + //display: block + //float: left br - clear: both + //clear: both .code - border: 1px solid black padding: 1% background-color: white table @@ -182,9 +186,9 @@ body //overflow-y: scroll; .model - padding: 0em 1em + padding: 0.5em 1em background-color: $bg_color2 - border: 1px solid + border: 0 margin-top: 0.6em dl dt -- cgit v1.2.3 From a64ce9f2e9c13b8f644bd75800ee6b1cd9942066 Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 31 Jan 2011 11:45:53 +0100 Subject: edit model name --- application.rb | 32 +++++++++++++++++++++++ public/javascripts/toxcreate.js | 57 +++++++++++++++++++++++++++++++++++++++++ views/model.haml | 8 +++--- views/model_name.haml | 12 +++++++++ views/model_name_edit.haml | 14 ++++++++++ views/style.sass | 9 +++++++ 6 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 views/model_name.haml create mode 100644 views/model_name_edit.haml diff --git a/application.rb b/application.rb index 31bab62..2126a48 100644 --- a/application.rb +++ b/application.rb @@ -81,6 +81,38 @@ get '/model/:id/status/?' do end end +get '/model/:id/name/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + begin + case params[:mode] + when 'edit' + haml :model_name_edit, :locals=>{:model=>model}, :layout => false + when 'show' + haml :model_name, :locals=>{:model=>model}, :layout => false + else + params.inspect + end + rescue + return "unavailable" + end +end + +put '/model/:id/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + begin + if params[:name] && model.name != params[:name] + model.name = params[:name] + model.save + end + redirect url_for("/model/#{model.id}/name?mode=show") + rescue + return "unavailable" + end +end + + get '/model/:id/:view/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index aef0455..493168f 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -95,6 +95,63 @@ $(function() { } }); +jQuery.fn.editModel = function(type, options) { + var defaults = { + method: 'get', + action: this.attr('href'), + trigger_on: 'click' + }; + var opts = $.extend(defaults, options); + this.bind(opts.trigger_on, function() { + $.ajax({ + type: opts.method, + url: opts.action, + dataType: 'html', + data: { + '_method': 'get' + }, + success: function(data) { + $("div#model_" + opts.id + "_name").html(data); + $("input#model_" + opts.id + "_name").focus(); + }, + error: function(data) { + alert("model edit error!"); + } + }); + return false; + }); +}; + +jQuery.fn.saveModel = function(type, options) { + var defaults = { + method: 'put', + action: 'model/' + options.id, + trigger_on: 'click' + }; + var opts = $.extend(defaults, options); + + this.bind(opts.trigger_on, function() { + var name = $("input#model_" + opts.id + "_name").val(); + $.ajax({ + type: opts.method, + url: opts.action, + dataType: 'html', + data: { + '_method': 'put', + 'name': name + }, + success: function(data) { + $("div#model_" + opts.id + "_name").html(data); + }, + error: function(data) { + alert("model save error!"); + } + }); + return false; + }); +}; + + jQuery.fn.deleteModel = function(type, options) { var defaults = { method: 'post', diff --git a/views/model.haml b/views/model.haml index 6374bd8..2125283 100644 --- a/views/model.haml +++ b/views/model.haml @@ -1,13 +1,13 @@ - uri = url_for("/model/#{model.id}", :full) -- js = "$('#delete_#{model.id}').deleteModel('DELETE', {id: '#{model.id}'});\n " + "$('#show_model_#{model.id}_warnings').toggleWarnings('#{model.id}');" +- js = "$('#delete_#{model.id}').deleteModel('DELETE', {id: '#{model.id}'});\n " + "$('#show_model_#{model.id}_warnings').toggleWarnings('#{model.id}');\n" :javascript $(function() { #{js} }); -%div{:id => "model_#{model.id}"} - %h2 - = model.name +%div{:id => "model_#{model.id}"} + %div{:id => "model_#{model.id}_name"} + = haml :model_name, :locals=>{:model=>model}, :layout => false .model %dl %dt Status: diff --git a/views/model_name.haml b/views/model_name.haml new file mode 100644 index 0000000..b1f6f10 --- /dev/null +++ b/views/model_name.haml @@ -0,0 +1,12 @@ +- js = "$('#edit_#{model.id}').editModel('PUT', {id: '#{model.id}', mode: 'edit'});\n " +:javascript + $(function() { + #{js} + }); +%h2 + = model.name + - if is_authorized(model.web_uri, "PUT") + %span{:class => "edit_button"} + ( + %a{:href => url_for("/model/#{model.id}/name?mode=edit"), :id => "edit_#{model.id}"} edit + ) \ No newline at end of file diff --git a/views/model_name_edit.haml b/views/model_name_edit.haml new file mode 100644 index 0000000..aec59fc --- /dev/null +++ b/views/model_name_edit.haml @@ -0,0 +1,14 @@ +- js = "$('#cancel_#{model.id}').editModel('GET', {id: '#{model.id}', mode: 'show'});\n " +- js = "$('#save_#{model.id}').saveModel('POST', {id: '#{model.id}', mode: 'show'});\n " +:javascript + $(function() { + #{js} + }); +%form{:name => "form", :action => url_for("/model/#{model.id}"), :method => "post", :enctype => "multipart/form-data" } + %input{:type => 'text', :name => 'name', :id => "model_#{model.id}_name", :class => 'input_model_name', :size => '40', :value => model.name} + %input{:type => 'hidden', :name => 'id', :id => 'id', :value => model.id} + %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} + %input{ :type => "submit", :value => "Save", :id => "save_#{model.id}", :class => "edit_button"} + %span{:class => "edit_button"} + %a{:href => url_for("/model/#{model.id}/name?mode=show"), :id => "cancel_#{model.id}"} Cancel + diff --git a/views/style.sass b/views/style.sass index 5f362a3..1fbd459 100644 --- a/views/style.sass +++ b/views/style.sass @@ -237,3 +237,12 @@ dl padding: 0.3em table border-collapse: collapse +.input_model_name + margin: 20px 3px 2px 3px + font-size: 1.2em + font-weight: bold + border: 0 + background-color: #FEFEFF + +.edit_button + font-size: 0.5em \ No newline at end of file -- cgit v1.2.3 From a9d8b84a2872ae354bba0ea50c1915e96d69d824 Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 1 Feb 2011 17:56:16 +0100 Subject: a&a for validation --- application.rb | 33 ++++++++++++++++++--------------- config.ru | 2 ++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/application.rb b/application.rb index abc180f..7b03948 100644 --- a/application.rb +++ b/application.rb @@ -28,13 +28,16 @@ helpers do private def delete_model(model, subjectid=nil) - begin RestClient.put(File.join(model.task_uri, 'Cancelled'),subjectid) if model.task_uri rescue LOGGER.warn "Can not cancel task #{model.task_uri}" end - delete_dependent(model.uri, subjectid) if model.uri - delete_dependent(model.validation_uri, subjectid) if model.validation_uri - delete_dependent(model.validation_report_uri, subjectid) if model.validation_report_uri - delete_dependent(model.validation_qmrf_uri, subjectid) if model.validation_qmrf_uri - delete_dependent(model.training_dataset, subjectid) if model.training_dataset - delete_dependent(model.feature_dataset, subjectid) if model.feature_dataset + task = OpenTox::Task.create("Deleting model: #{model.uri}",url_for("/delete",:full)) do + begin RestClient.put(File.join(model.task_uri, 'Cancelled'),subjectid) if model.task_uri rescue LOGGER.warn "Can not cancel task #{model.task_uri}" end + delete_dependent(model.uri, subjectid) if model.uri + delete_dependent(model.validation_uri, subjectid) if model.validation_uri + delete_dependent(model.validation_report_uri, subjectid) if model.validation_report_uri + delete_dependent(model.validation_qmrf_uri, subjectid) if model.validation_qmrf_uri + delete_dependent(model.training_dataset, subjectid) if model.training_dataset + delete_dependent(model.feature_dataset, subjectid) if model.feature_dataset + "" + end end def delete_dependent(uri, subjectid=nil) @@ -229,9 +232,10 @@ post '/models' do # create a new model unless url_for("",:full).match(/localhost/) @model.update :status => "Validating model" + LOGGER.debug "mr ::: #{lazar.metadata.inspect}" begin validation = OpenTox::Validation.create_crossvalidation( - :algorithm_uri => OpenTox::Algorithm::Lazar.uri, + :algorithm_uri => lazar.metadata[OT.algorithm], :dataset_uri => lazar.parameter("dataset_uri"), :subjectid => subjectid, :prediction_feature => lazar.parameter("prediction_feature"), @@ -243,22 +247,22 @@ post '/models' do # create a new model 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"} + validation.summary(@model.type, subjectid).each do |k,v| + LOGGER.debug "mr ::: k: #{k.inspect} - v: #{v.inspect}" + eval "@model.#{k.to_s} = v" + end @model.save - @model.update :status => "Creating validation report" begin - @model.update(:validation_report_uri => validation.create_report) + @model.update(:validation_report_uri => validation.create_report(subjectid)) 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) + @model.update(:validation_qmrf_report_uri => validation.create_qmrf_report(subjectid)) rescue => e LOGGER.debug "Validation QMRF report generation failed with #{e.message}." @model.warnings += "Validation QMRF report generation failed with #{e.message}." @@ -266,7 +270,6 @@ post '/models' do # create a new model end - #@model.warnings += "

Incorrect Smiles structures (ignored):

" + parser.smiles_errors.join("
") unless parser.smiles_errors.empty? #@model.warnings += "

Irregular activities (ignored):

" + parser.activity_errors.join("
") unless parser.activity_errors.empty? #duplicate_warnings = '' diff --git a/config.ru b/config.ru index 1616a96..ec39d1b 100644 --- a/config.ru +++ b/config.ru @@ -3,3 +3,5 @@ require 'opentox-ruby' require 'config/config_ru' set :app_file, __FILE__ # to get the view path right run Sinatra::Application +set :raise_errors, false +set :show_exceptions, false \ No newline at end of file -- cgit v1.2.3 From 86e652e6ebbf13e2385c2ce9114d92d3eaf9a87d Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Tue, 8 Feb 2011 10:05:25 +0100 Subject: initial crossvalidation --- application.rb | 25 +++++++++++++++---------- views/lazar.haml | 4 +++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/application.rb b/application.rb index fe5f511..7de342c 100644 --- a/application.rb +++ b/application.rb @@ -154,6 +154,7 @@ post '/models' do # create a new model end @model.training_dataset = @dataset.uri @model.nr_compounds = @dataset.compounds.size + @model.warnings = "" @model.warnings = @dataset.metadata[OT.Warnings] unless @dataset.metadata[OT.Warnings].empty? @model.save @@ -176,11 +177,13 @@ post '/models' do # create a new model end @model.save +=begin +=end unless url_for("",:full).match(/localhost/) @model.update :status => "Validating model" - begin - validation = OpenTox::Validation.create_crossvalidation( - :algorithm_uri => OpenTox::Algorithm::Lazar.uri, + #begin + validation = OpenTox::Crossvalidation.create( + :algorithm_uri => File.join(CONFIG[:services]["opentox-algorithm"],"lazar"), :dataset_uri => lazar.parameter("dataset_uri"), :subjectid => session[:subjectid], :prediction_feature => lazar.parameter("prediction_feature"), @@ -188,18 +191,19 @@ post '/models' do # create a new model ) @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 + #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"} +=begin + validation.summary(@model.type,session[:subjectid]).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) + @model.update(:validation_report_uri => validation.create_report(session[:subjectid])) rescue => e LOGGER.debug "Validation report generation failed with #{e.message}." @model.warnings += "Validation report generation failed with #{e.message}." @@ -207,11 +211,12 @@ post '/models' do # create a new model @model.update :status => "Creating QMRF report" begin - @model.update(:validation_qmrf_report_uri => validation.create_qmrf_report) + @model.update(:validation_qmrf_report_uri => validation.create_qmrf_report(session[:subjectid])) rescue => e LOGGER.debug "Validation QMRF report generation failed with #{e.message}." @model.warnings += "Validation QMRF report generation failed with #{e.message}." end +=end end diff --git a/views/lazar.haml b/views/lazar.haml index a46f82c..64b82bf 100644 --- a/views/lazar.haml +++ b/views/lazar.haml @@ -55,4 +55,6 @@ = haml :feature_table, :locals => {:features => sort(@prediction.descriptors(@compound))}, :layout => false %tbody#neighbors - = haml :neighbors, :locals => {:neighbors => @prediction.neighbors(@compound), :page => @page}, :layout => :false + -#= @prediction.neighbors(@compound).to_yaml + = haml :neighbors, :locals => {:neighbors => @prediction.neighbors(@compound), :page => @page}#, :layout => :false + -#= haml :neighbors -- cgit v1.2.3 From 076de3e6bdde29936926814409bdce4b0c7b7477 Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 8 Feb 2011 12:54:13 +0100 Subject: authorization A&A, task progress for model creation --- application.rb | 49 +++++++++++++++++++++++++++++++---------------- views/model.haml | 6 ++++++ views/model_progress.haml | 1 + views/model_status.haml | 3 +-- 4 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 views/model_progress.haml diff --git a/application.rb b/application.rb index 7b03948..f561b54 100644 --- a/application.rb +++ b/application.rb @@ -28,14 +28,21 @@ helpers do private def delete_model(model, subjectid=nil) - task = OpenTox::Task.create("Deleting model: #{model.uri}",url_for("/delete",:full)) do + task = OpenTox::Task.create("Deleting model: #{model.uri}",url_for("/delete",:full)) do |task| begin RestClient.put(File.join(model.task_uri, 'Cancelled'),subjectid) if model.task_uri rescue LOGGER.warn "Can not cancel task #{model.task_uri}" end + task.progress(15) delete_dependent(model.uri, subjectid) if model.uri + task.progress(30) delete_dependent(model.validation_uri, subjectid) if model.validation_uri + task.progress(45) delete_dependent(model.validation_report_uri, subjectid) if model.validation_report_uri + task.progress(60) delete_dependent(model.validation_qmrf_uri, subjectid) if model.validation_qmrf_uri + task.progress(75) delete_dependent(model.training_dataset, subjectid) if model.training_dataset + task.progress(90) delete_dependent(model.feature_dataset, subjectid) if model.feature_dataset + task.progress(100) "" end end @@ -72,12 +79,13 @@ get '/models/?' do @models = ToxCreateModel.all(:order => [ :created_at.desc ]) subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" #@models.each { |model| model.process } - haml :models, :locals=>{:models=>@models,:subjectstring => subjectstring} + haml :models, :locals=>{:models=>@models, :subjectstring => subjectstring} end get '/model/:id/status/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) + #percentage_completed = task.metadata[OT.percentageCompleted] if (task = OpenTox::Task.exist?(model.task_uri)) begin haml :model_status, :locals=>{:model=>model}, :layout => false rescue @@ -175,9 +183,9 @@ post '/models' do # create a new model end subjectid = session[:subjectid] ? session[:subjectid] : nil @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => subjectid) - @model.update :web_uri => url_for("/model/#{@model.id}", :full) + @model.update :web_uri => url_for("/model/#{@model.id}", :full), :warnings => "" @model.save - task = OpenTox::Task.create("Uploading dataset and creating lazar model",url_for("/models",:full)) do + task = OpenTox::Task.create("Uploading dataset and creating lazar model",url_for("/models",:full)) do |task| @model.update :status => "Uploading and saving dataset" begin @@ -197,6 +205,7 @@ post '/models' do # create a new model error "Dataset creation failed with #{e.message}" end @dataset.save(subjectid) + task.progress(10) 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 @@ -212,12 +221,13 @@ post '/models' do # create a new model @model.save @model.update :status => "Creating prediction model" + task.progress(15) begin lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => subjectid) 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 + task.progress(25) @model.feature_dataset = lazar.metadata[OT.featureDataset] @model.uri = lazar.uri case lazar.metadata[OT.isA] @@ -234,13 +244,13 @@ post '/models' do # create a new model @model.update :status => "Validating model" LOGGER.debug "mr ::: #{lazar.metadata.inspect}" begin - validation = OpenTox::Validation.create_crossvalidation( - :algorithm_uri => lazar.metadata[OT.algorithm], + validation = OpenTox::Crossvalidation.create( + {:algorithm_uri => lazar.metadata[OT.algorithm], :dataset_uri => lazar.parameter("dataset_uri"), :subjectid => subjectid, :prediction_feature => lazar.parameter("prediction_feature"), - :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}" - ) + :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}"}, + nil, OpenTox::SubTask.new(task,25,80)) @model.update(:validation_uri => validation.uri) LOGGER.debug "Validation URI: #{@model.validation_uri}" rescue => e @@ -248,21 +258,28 @@ post '/models' do # create a new model @model.warnings += "Model validation failed with #{e.message}." end # create summary - validation.summary(@model.type, subjectid).each do |k,v| + validation.summary(subjectid).each do |k,v| LOGGER.debug "mr ::: k: #{k.inspect} - v: #{v.inspect}" - eval "@model.#{k.to_s} = v" + begin + eval "@model.#{k.to_s} = v" if v + rescue + eval "@model.#{k.to_s} = 0" + end end @model.save + @model.update :status => "Creating validation report" begin - @model.update(:validation_report_uri => validation.create_report(subjectid)) + @model.update(:validation_report_uri => validation.find_or_create_report(subjectid, OpenTox::SubTask.new(task,80,90))) unless @model.dirty? 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(subjectid)) + #@model.update(:validation_qmrf_report_uri => validation.create_qmrf_report(subjectid)) unless @model.dirty? + qmrf_report = OpenTox::Crossvalidation::QMRFReport.create(@model.uri, subjectid, OpenTox::SubTask.new(task,90,99)) + @model.update(:validation_qmrf_uri => qmrf_report.uri) rescue => e LOGGER.debug "Validation QMRF report generation failed with #{e.message}." @model.warnings += "Validation QMRF report generation failed with #{e.message}." @@ -435,13 +452,13 @@ end delete '/model/:id/?' do model = ToxCreateModel.get(params[:id]) + raise OpenTox::NotFoundError.new("Model with id: #{params[:id]} not found!") unless model begin - - delete_model(model, session[:subjectid]) + delete_model(model, @subjectid) model.destroy unless ToxCreateModel.get(params[:id]) begin - aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, session[:subjectid]) + aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, @subjectid) LOGGER.debug "Policy deleted for Dataset URI: #{uri} with result: #{aa}" rescue LOGGER.warn "Policy delete error for Dataset URI: #{uri}" diff --git a/views/model.haml b/views/model.haml index 2125283..32439a9 100644 --- a/views/model.haml +++ b/views/model.haml @@ -14,6 +14,11 @@ %dd %span{:id => "model_#{model.id}_status", :class => "model_status"} = haml :model_status, :locals=>{:model=>model}, :layout => false + - if model.task_uri && model.status != "Completedss" + - if (task = OpenTox::Task.exist?(model.task_uri)) + - percentage_completed = task.metadata[OT.percentageCompleted] + %span{:id => "model_#{model.id}_progress", :class => "model_progress"} + = haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false - if is_authorized(model.web_uri, "DELETE") ( %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete'} @@ -22,6 +27,7 @@ - else stop ) + %dt Started: %dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") - if model.nr_compounds diff --git a/views/model_progress.haml b/views/model_progress.haml new file mode 100644 index 0000000..0064ae1 --- /dev/null +++ b/views/model_progress.haml @@ -0,0 +1 @@ += percentage_completed diff --git a/views/model_status.haml b/views/model_status.haml index 897e792..60e1eaf 100644 --- a/views/model_status.haml +++ b/views/model_status.haml @@ -1,2 +1 @@ --#= image_tag("/snake_transparent.gif") if model.status.match(/running|started|created/i) -= model.status += model.status.to_s -- cgit v1.2.3 From ce68f6a763ce8de8957f84af4f52dbe1d74cd232 Mon Sep 17 00:00:00 2001 From: mr Date: Tue, 8 Feb 2011 18:31:02 +0100 Subject: minor fixes and graphical progressbar from jquery-ui --- application.rb | 14 + public/javascripts/jquery-ui-progressbar.js | 49 ++++ public/javascripts/toxcreate.js | 26 ++ .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 225 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../images/ui-bg_gloss-wave_16_121212_500x100.png | Bin 0 -> 478 bytes .../ui-bg_highlight-hard_15_888888_1x100.png | Bin 0 -> 150 bytes .../ui-bg_highlight-hard_55_555555_1x100.png | Bin 0 -> 115 bytes .../ui-bg_highlight-soft_35_adadad_1x100.png | Bin 0 -> 145 bytes .../ui-bg_highlight-soft_60_dddddd_1x100.png | Bin 0 -> 106 bytes .../images/ui-bg_inset-soft_15_121212_1x100.png | Bin 0 -> 103 bytes .../progressbar/images/ui-icons_666666_256x240.png | Bin 0 -> 4369 bytes .../progressbar/images/ui-icons_aaaaaa_256x240.png | Bin 0 -> 4369 bytes .../progressbar/images/ui-icons_bbbbbb_256x240.png | Bin 0 -> 5355 bytes .../progressbar/images/ui-icons_c98000_256x240.png | Bin 0 -> 4369 bytes .../progressbar/images/ui-icons_cccccc_256x240.png | Bin 0 -> 4369 bytes .../progressbar/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../progressbar/images/ui-icons_f29a00_256x240.png | Bin 0 -> 4369 bytes public/progressbar/progressbar.css | 305 +++++++++++++++++++++ views/layout.haml | 4 +- views/model.haml | 14 +- views/style.sass | 7 +- views/validation.haml | 2 +- 23 files changed, 414 insertions(+), 7 deletions(-) create mode 100644 public/javascripts/jquery-ui-progressbar.js create mode 100644 public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 public/progressbar/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 public/progressbar/images/ui-bg_gloss-wave_16_121212_500x100.png create mode 100644 public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png create mode 100644 public/progressbar/images/ui-bg_highlight-hard_55_555555_1x100.png create mode 100644 public/progressbar/images/ui-bg_highlight-soft_35_adadad_1x100.png create mode 100644 public/progressbar/images/ui-bg_highlight-soft_60_dddddd_1x100.png create mode 100644 public/progressbar/images/ui-bg_inset-soft_15_121212_1x100.png create mode 100644 public/progressbar/images/ui-icons_666666_256x240.png create mode 100644 public/progressbar/images/ui-icons_aaaaaa_256x240.png create mode 100644 public/progressbar/images/ui-icons_bbbbbb_256x240.png create mode 100644 public/progressbar/images/ui-icons_c98000_256x240.png create mode 100644 public/progressbar/images/ui-icons_cccccc_256x240.png create mode 100644 public/progressbar/images/ui-icons_cd0a0a_256x240.png create mode 100644 public/progressbar/images/ui-icons_f29a00_256x240.png create mode 100644 public/progressbar/progressbar.css diff --git a/application.rb b/application.rb index f561b54..ab6eb46 100644 --- a/application.rb +++ b/application.rb @@ -93,6 +93,20 @@ get '/model/:id/status/?' do end end +get '/model/:id/progress/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + if (task = OpenTox::Task.exist?(model.task_uri)) + task = OpenTox::Task.exist?(model.task_uri) + percentage_completed = task.metadata[OT.percentageCompleted] + end + begin + haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false + rescue + return "unavailable" + end +end + get '/model/:id/name/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) diff --git a/public/javascripts/jquery-ui-progressbar.js b/public/javascripts/jquery-ui-progressbar.js new file mode 100644 index 0000000..64c2a2c --- /dev/null +++ b/public/javascripts/jquery-ui-progressbar.js @@ -0,0 +1,49 @@ +/*! + * jQuery UI 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.9",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, +NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this, +"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position"); +if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f, +"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h, +d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}}); +c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a
").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); +this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* +this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.9"})})(jQuery); +; \ No newline at end of file diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index 493168f..949b9ea 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -23,6 +23,7 @@ $(function() { $("body") var newstati = new Array; $.each(stati, function(){ + checkProgress(this, subjectstr); if(checkStatus(this, subjectstr) > 0) newstati.push(this); }); if (newstati.length > 0) var statusCheck = setTimeout('checkStati("' + newstati.join(", ") + '", "' + subjectstr + '")',10000); @@ -59,6 +60,31 @@ $(function() { }); return id; }; + + + checkProgress = function(id, subjectstr) { + var opts = {action: 'model/' + id + '/progress' + subjectstr, id: id}; + var progress_changed = $.ajax({ + url: opts.action, + async: false, + dataType: 'html', + data: { + '_method': 'get' + }, + success: function(data) { + var progress = data.trim(); + if (progress == "100") return -1; + + $("div#model_" + id + "_progress").progressbar("value", parseInt(progress)); + }, + error: function(data) { + id = -1; + } + }); + return id; + }; + + loadModel = function(id, view) { if(id == "") return -1; diff --git a/public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png b/public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000..e3dcd29 Binary files /dev/null and b/public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/public/progressbar/images/ui-bg_glass_95_fef1ec_1x400.png b/public/progressbar/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 0000000..4443fdc Binary files /dev/null and b/public/progressbar/images/ui-bg_glass_95_fef1ec_1x400.png differ diff --git a/public/progressbar/images/ui-bg_gloss-wave_16_121212_500x100.png b/public/progressbar/images/ui-bg_gloss-wave_16_121212_500x100.png new file mode 100644 index 0000000..a2371bf Binary files /dev/null and b/public/progressbar/images/ui-bg_gloss-wave_16_121212_500x100.png differ diff --git a/public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png b/public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png new file mode 100644 index 0000000..da27188 Binary files /dev/null and b/public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png differ diff --git a/public/progressbar/images/ui-bg_highlight-hard_55_555555_1x100.png b/public/progressbar/images/ui-bg_highlight-hard_55_555555_1x100.png new file mode 100644 index 0000000..1453bc1 Binary files /dev/null and b/public/progressbar/images/ui-bg_highlight-hard_55_555555_1x100.png differ diff --git a/public/progressbar/images/ui-bg_highlight-soft_35_adadad_1x100.png b/public/progressbar/images/ui-bg_highlight-soft_35_adadad_1x100.png new file mode 100644 index 0000000..5a5a41d Binary files /dev/null and b/public/progressbar/images/ui-bg_highlight-soft_35_adadad_1x100.png differ diff --git a/public/progressbar/images/ui-bg_highlight-soft_60_dddddd_1x100.png b/public/progressbar/images/ui-bg_highlight-soft_60_dddddd_1x100.png new file mode 100644 index 0000000..747e1fa Binary files /dev/null and b/public/progressbar/images/ui-bg_highlight-soft_60_dddddd_1x100.png differ diff --git a/public/progressbar/images/ui-bg_inset-soft_15_121212_1x100.png b/public/progressbar/images/ui-bg_inset-soft_15_121212_1x100.png new file mode 100644 index 0000000..9df26a0 Binary files /dev/null and b/public/progressbar/images/ui-bg_inset-soft_15_121212_1x100.png differ diff --git a/public/progressbar/images/ui-icons_666666_256x240.png b/public/progressbar/images/ui-icons_666666_256x240.png new file mode 100644 index 0000000..f87de1c Binary files /dev/null and b/public/progressbar/images/ui-icons_666666_256x240.png differ diff --git a/public/progressbar/images/ui-icons_aaaaaa_256x240.png b/public/progressbar/images/ui-icons_aaaaaa_256x240.png new file mode 100644 index 0000000..64af7f1 Binary files /dev/null and b/public/progressbar/images/ui-icons_aaaaaa_256x240.png differ diff --git a/public/progressbar/images/ui-icons_bbbbbb_256x240.png b/public/progressbar/images/ui-icons_bbbbbb_256x240.png new file mode 100644 index 0000000..9d71edb Binary files /dev/null and b/public/progressbar/images/ui-icons_bbbbbb_256x240.png differ diff --git a/public/progressbar/images/ui-icons_c98000_256x240.png b/public/progressbar/images/ui-icons_c98000_256x240.png new file mode 100644 index 0000000..ba8dafc Binary files /dev/null and b/public/progressbar/images/ui-icons_c98000_256x240.png differ diff --git a/public/progressbar/images/ui-icons_cccccc_256x240.png b/public/progressbar/images/ui-icons_cccccc_256x240.png new file mode 100644 index 0000000..9254e05 Binary files /dev/null and b/public/progressbar/images/ui-icons_cccccc_256x240.png differ diff --git a/public/progressbar/images/ui-icons_cd0a0a_256x240.png b/public/progressbar/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000..2ab019b Binary files /dev/null and b/public/progressbar/images/ui-icons_cd0a0a_256x240.png differ diff --git a/public/progressbar/images/ui-icons_f29a00_256x240.png b/public/progressbar/images/ui-icons_f29a00_256x240.png new file mode 100644 index 0000000..540e43b Binary files /dev/null and b/public/progressbar/images/ui-icons_f29a00_256x240.png differ diff --git a/public/progressbar/progressbar.css b/public/progressbar/progressbar.css new file mode 100644 index 0000000..92e3f82 --- /dev/null +++ b/public/progressbar/progressbar.css @@ -0,0 +1,305 @@ +/* + * jQuery UI CSS Framework 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?tr&ffDefault=Helvetica,%20Arial,%20sans-serif&fwDefault=normal&fsDefault=1.1&fsDefaultUnit=em&cornerRadius=5&cornerRadiusUnit=px&bgColorHeader=888888&bgTextureHeader=04_highlight_hard.png&bgImgOpacityHeader=15&borderColorHeader=404040&fcHeader=ffffff&iconColorHeader=cccccc&bgColorContent=121212&bgTextureContent=12_gloss_wave.png&bgImgOpacityContent=16&borderColorContent=404040&fcContent=eeeeee&iconColorContent=bbbbbb&bgColorDefault=adadad&bgTextureDefault=03_highlight_soft.png&bgImgOpacityDefault=35&borderColorDefault=cccccc&fcDefault=333333&iconColorDefault=666666&bgColorHover=dddddd&bgTextureHover=03_highlight_soft.png&bgImgOpacityHover=60&borderColorHover=dddddd&fcHover=000000&iconColorHover=c98000&bgColorActive=121212&bgTextureActive=05_inset_soft.png&bgImgOpacityActive=15&borderColorActive=000000&fcActive=ffffff&iconColorActive=f29a00&bgColorHighlight=555555&bgTextureHighlight=04_highlight_hard.png&bgImgOpacityHighlight=55&borderColorHighlight=404040&fcHighlight=cccccc&iconColorHighlight=aaaaaa&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Helvetica, Arial, sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Helvetica, Arial, sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #404040; background: #121212 url(images/ui-bg_gloss-wave_16_121212_500x100.png) 50% top repeat-x; color: #eeeeee; } +.ui-widget-content a { color: #eeeeee; } +.ui-widget-header { border: 1px solid #404040; background: #888888 url(images/ui-bg_highlight-hard_15_888888_1x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } +.ui-widget-header a { color: #ffffff; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #adadad url(images/ui-bg_highlight-soft_35_adadad_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #333333; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #333333; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #dddddd; background: #dddddd url(images/ui-bg_highlight-soft_60_dddddd_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #000000; } +.ui-state-hover a, .ui-state-hover a:hover { color: #000000; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #000000; background: #121212 url(images/ui-bg_inset-soft_15_121212_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #ffffff; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #404040; background: #555555 url(images/ui-bg_highlight-hard_55_555555_1x100.png) 50% top repeat-x; color: #cccccc; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #cccccc; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_bbbbbb_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_bbbbbb_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_cccccc_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_666666_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_c98000_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_f29a00_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_aaaaaa_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; } +.ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; } +.ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; } +.ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; } +.ui-corner-right { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; } +.ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; } +.ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* + * jQuery UI Progressbar 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { height:20px; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:20px; } \ No newline at end of file diff --git a/views/layout.haml b/views/layout.haml index 78af858..d54b413 100644 --- a/views/layout.haml +++ b/views/layout.haml @@ -6,7 +6,9 @@ %title ToxCreate %script{:type => "text/javascript", :src => "javascripts/jquery.js"} %script{:type => "text/javascript", :src => "javascripts/toxcreate.js"} - %link{:rel=>'stylesheet', :href=>'stylesheets/style.css', :type => "text/css"} + %script{:type => "text/javascript", :src => "javascripts/jquery-ui-progressbar.js"} + %link{:rel=>'stylesheet', :href=>'stylesheets/style.css', :type => "text/css"} + %link{:rel=>'stylesheet', :href=>'progressbar/progressbar.css', :type => "text/css"} %body .logo diff --git a/views/model.haml b/views/model.haml index 32439a9..52d143c 100644 --- a/views/model.haml +++ b/views/model.haml @@ -14,11 +14,17 @@ %dd %span{:id => "model_#{model.id}_status", :class => "model_status"} = haml :model_status, :locals=>{:model=>model}, :layout => false - - if model.task_uri && model.status != "Completedss" + - if model.task_uri && model.status != "Completed" - if (task = OpenTox::Task.exist?(model.task_uri)) - percentage_completed = task.metadata[OT.percentageCompleted] - %span{:id => "model_#{model.id}_progress", :class => "model_progress"} - = haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false + - js = "$('#model_#{model.id}_progress').progressbar({ value: #{percentage_completed} })"; + :javascript + $(function() { + #{js} + }); + + %div{:id => "model_#{model.id}_progress", :class => "model_progress"} + //= haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false - if is_authorized(model.web_uri, "DELETE") ( %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete'} @@ -75,4 +81,4 @@ %a{:href => File.join(model.validation_qmrf_uri,"editor")} QMRF Editor, %a{:href => "#{model.uri}.yaml#{subjectstring}"} YAML %em (experts, models cannot be represented in Excel) - = haml :validation, :locals=>{:model=>model}, :layout => false + = haml :validation, :locals=>{:model=>model,:subjectstring => subjectstring}, :layout => false diff --git a/views/style.sass b/views/style.sass index 27bf6fe..a2b8191 100644 --- a/views/style.sass +++ b/views/style.sass @@ -249,4 +249,9 @@ dl background-color: #FEFEFF .edit_button - font-size: 0.5em \ No newline at end of file + font-size: 0.5em + +.model_progress + width: 240px + height: 20px + float: right \ No newline at end of file diff --git a/views/validation.haml b/views/validation.haml index 5fc7371..ce35536 100644 --- a/views/validation.haml +++ b/views/validation.haml @@ -8,7 +8,7 @@ - if model.validation_report_uri %dt Detailed report: %dd - %a{:href => model.validation_report_uri, :target => "_blank"} show + %a{:href => model.validation_report_uri + subjectstring, :target => "_blank"} show %dt Number of predictions %dd= model.nr_predictions - case model.type -- cgit v1.2.3 From b40e9a00a1a779b11da4d5ab5078c76207a3456a Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 9 Feb 2011 09:50:18 +0100 Subject: refining thhe progressbar --- public/javascripts/toxcreate.js | 4 +++- .../progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 225 -> 2062 bytes .../images/ui-bg_highlight-hard_15_888888_1x100.png | Bin 150 -> 214 bytes views/model.haml | 12 +++++------- views/style.sass | 13 +++++++++---- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index 949b9ea..de5e73d 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -75,7 +75,9 @@ $(function() { var progress = data.trim(); if (progress == "100") return -1; - $("div#model_" + id + "_progress").progressbar("value", parseInt(progress)); + $("div#model_" + id + "_progress").progressbar("value", parseInt(progress)); + $("div#model_" + id + "_progress").attr({title: parseInt(progress) + "%", alt: parseInt(progress) + "%"}); + //$("div#model_" + id + "_progress").attr("alt", parseInt(progress) + "%"); }, error: function(data) { id = -1; diff --git a/public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png b/public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png index e3dcd29..f4b49ad 100644 Binary files a/public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png and b/public/progressbar/images/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png b/public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png index da27188..7a61a4d 100644 Binary files a/public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png and b/public/progressbar/images/ui-bg_highlight-hard_15_888888_1x100.png differ diff --git a/views/model.haml b/views/model.haml index 52d143c..d218fea 100644 --- a/views/model.haml +++ b/views/model.haml @@ -16,23 +16,21 @@ = haml :model_status, :locals=>{:model=>model}, :layout => false - if model.task_uri && model.status != "Completed" - if (task = OpenTox::Task.exist?(model.task_uri)) - - percentage_completed = task.metadata[OT.percentageCompleted] + - percentage_completed = task.metadata[OT.percentageCompleted].to_i - js = "$('#model_#{model.id}_progress').progressbar({ value: #{percentage_completed} })"; :javascript $(function() { #{js} }); - %div{:id => "model_#{model.id}_progress", :class => "model_progress"} + %div{:id => "model_#{model.id}_progress", :class => "model_progress", :title => "#{percentage_completed}%", :alt => "#{percentage_completed}%"} //= haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false - if is_authorized(model.web_uri, "DELETE") - ( - %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete'} + %a{:href => url_for("/model/#{model.id}"), :id => "delete_#{model.id}", :class => 'delete_link'} - if model.status == "Completed" - delete + (delete) - else - stop - ) + (stop) %dt Started: %dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") diff --git a/views/style.sass b/views/style.sass index a2b8191..db1f1bf 100644 --- a/views/style.sass +++ b/views/style.sass @@ -250,8 +250,13 @@ dl .edit_button font-size: 0.5em - + +.model_status + float: left + .model_progress - width: 240px - height: 20px - float: right \ No newline at end of file + width: 200px + height: 16px + margin-left: 16px + margin-right: 16px + float: left -- cgit v1.2.3 From fca252329ebb821d75a0bfc66fdc7332da92646c Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 9 Feb 2011 11:20:13 +0100 Subject: return in page --- views/model.haml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/model.haml b/views/model.haml index d218fea..9730a3f 100644 --- a/views/model.haml +++ b/views/model.haml @@ -31,6 +31,8 @@ (delete) - else (stop) + %span + %br %dt Started: %dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") -- cgit v1.2.3 From 1f0b19023c2a308c21f9ddcf456053bf5b7ecb32 Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 9 Feb 2011 17:14:04 +0100 Subject: refine haml --- views/model.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/model.haml b/views/model.haml index 9730a3f..340d29e 100644 --- a/views/model.haml +++ b/views/model.haml @@ -14,7 +14,7 @@ %dd %span{:id => "model_#{model.id}_status", :class => "model_status"} = haml :model_status, :locals=>{:model=>model}, :layout => false - - if model.task_uri && model.status != "Completed" + - if model.task_uri && model.status !~ /Completed|Cancelled|Error/ - if (task = OpenTox::Task.exist?(model.task_uri)) - percentage_completed = task.metadata[OT.percentageCompleted].to_i - js = "$('#model_#{model.id}_progress').progressbar({ value: #{percentage_completed} })"; -- cgit v1.2.3 From 50a06e7077681317bcf0da4fc4aa6ff36c19e4a3 Mon Sep 17 00:00:00 2001 From: mr Date: Fri, 11 Feb 2011 16:49:13 +0100 Subject: refining task_uri query --- application.rb | 25 ++++++++++++++----------- views/lazar.haml | 2 +- views/model.haml | 3 ++- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/application.rb b/application.rb index ab6eb46..a36252b 100644 --- a/application.rb +++ b/application.rb @@ -96,14 +96,18 @@ end get '/model/:id/progress/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) - if (task = OpenTox::Task.exist?(model.task_uri)) - task = OpenTox::Task.exist?(model.task_uri) - percentage_completed = task.metadata[OT.percentageCompleted] - end - begin - haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false - rescue - return "unavailable" + if model.task_uri + if (task = OpenTox::Task.exist?(model.task_uri)) + task = OpenTox::Task.exist?(model.task_uri) + percentage_completed = task.percentageCompleted + end + begin + haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false + rescue + return "unavailable" + end + else + return "" end end @@ -256,7 +260,6 @@ post '/models' do # create a new model unless url_for("",:full).match(/localhost/) @model.update :status => "Validating model" - LOGGER.debug "mr ::: #{lazar.metadata.inspect}" begin validation = OpenTox::Crossvalidation.create( {:algorithm_uri => lazar.metadata[OT.algorithm], @@ -341,8 +344,8 @@ post '/predict/?' do # post chemical name to model title = nil db_activities = [] lazar = OpenTox::Model::Lazar.new model.uri - prediction_dataset_uri = lazar.run(:compound_uri => @compound.uri, :subjectid => session[:subjectid]) - prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri, session[:subjectid]) + prediction_dataset_uri = lazar.run({:compound_uri => @compound.uri, :subjectid => subjectid}) + prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri, subjectid) if prediction_dataset.metadata[OT.hasSource].match(/dataset/) @predictions << { :title => model.name, diff --git a/views/lazar.haml b/views/lazar.haml index a46f82c..9d9145e 100644 --- a/views/lazar.haml +++ b/views/lazar.haml @@ -55,4 +55,4 @@ = haml :feature_table, :locals => {:features => sort(@prediction.descriptors(@compound))}, :layout => false %tbody#neighbors - = haml :neighbors, :locals => {:neighbors => @prediction.neighbors(@compound), :page => @page}, :layout => :false + = haml :neighbors, :locals => {:neighbors => @prediction.neighbors(@compound), :page => @page}, :layout => false diff --git a/views/model.haml b/views/model.haml index 340d29e..8112dd6 100644 --- a/views/model.haml +++ b/views/model.haml @@ -14,8 +14,9 @@ %dd %span{:id => "model_#{model.id}_status", :class => "model_status"} = haml :model_status, :locals=>{:model=>model}, :layout => false - - if model.task_uri && model.status !~ /Completed|Cancelled|Error/ + - if model.task_uri && model.status !~ /Completed|Cancelled|Error|Deleting/ - if (task = OpenTox::Task.exist?(model.task_uri)) + %input{:type => 'hidden', :id => "model_#{model.id}_task", :value => "#{model.task_uri}"} - percentage_completed = task.metadata[OT.percentageCompleted].to_i - js = "$('#model_#{model.id}_progress').progressbar({ value: #{percentage_completed} })"; :javascript -- cgit v1.2.3 From cf3995c5b82dcd29763bfa22bfbfb4030bddef7d Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 14 Feb 2011 09:58:54 +0100 Subject: get percentageCompleted direct from task --- application.rb | 2 +- public/javascripts/toxcreate.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/application.rb b/application.rb index a36252b..a892c03 100644 --- a/application.rb +++ b/application.rb @@ -97,7 +97,7 @@ get '/model/:id/progress/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) if model.task_uri - if (task = OpenTox::Task.exist?(model.task_uri)) + if (OpenTox::Task.exist?(model.task_uri)) task = OpenTox::Task.exist?(model.task_uri) percentage_completed = task.percentageCompleted end diff --git a/public/javascripts/toxcreate.js b/public/javascripts/toxcreate.js index de5e73d..410778f 100755 --- a/public/javascripts/toxcreate.js +++ b/public/javascripts/toxcreate.js @@ -63,7 +63,8 @@ $(function() { checkProgress = function(id, subjectstr) { - var opts = {action: 'model/' + id + '/progress' + subjectstr, id: id}; + var task = $("input#model_" + id + "_task").attr('value'); + var opts = {action: task + "/percentageCompleted" , id: id}; var progress_changed = $.ajax({ url: opts.action, async: false, -- cgit v1.2.3 From c7fdbd935660ec29fed16646b2ac0c9fb585644a Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 21 Feb 2011 15:45:49 +0100 Subject: models view --- views/models.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/models.haml b/views/models.haml index ab113c5..a99144a 100644 --- a/views/models.haml +++ b/views/models.haml @@ -1,4 +1,4 @@ -- stati = @models.map{|m| "#{m.id}" if m.status != "Completed"}.compact +- stati = @models.map{|m| "#{m.id}" if m.status !~ /Completed|Error/}.compact - stati_to_check = stati.length > 0 ? stati.join(", ") : stati = 0 :javascript $(function() { -- cgit v1.2.3 From d65363b4a0e152a978c8cc0bc974e6aab562135d Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Mon, 21 Feb 2011 16:03:44 +0100 Subject: application.rb added again --- application.rb | 501 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 application.rb diff --git a/application.rb b/application.rb new file mode 100644 index 0000000..a892c03 --- /dev/null +++ b/application.rb @@ -0,0 +1,501 @@ +['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::Session::Cookie, :expire_after => 28800, + :secret => "ui6vaiNi-change_me" +use Rack::Flash + +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 + + private + def delete_model(model, subjectid=nil) + task = OpenTox::Task.create("Deleting model: #{model.uri}",url_for("/delete",:full)) do |task| + begin RestClient.put(File.join(model.task_uri, 'Cancelled'),subjectid) if model.task_uri rescue LOGGER.warn "Can not cancel task #{model.task_uri}" end + task.progress(15) + delete_dependent(model.uri, subjectid) if model.uri + task.progress(30) + delete_dependent(model.validation_uri, subjectid) if model.validation_uri + task.progress(45) + delete_dependent(model.validation_report_uri, subjectid) if model.validation_report_uri + task.progress(60) + delete_dependent(model.validation_qmrf_uri, subjectid) if model.validation_qmrf_uri + task.progress(75) + delete_dependent(model.training_dataset, subjectid) if model.training_dataset + task.progress(90) + delete_dependent(model.feature_dataset, subjectid) if model.feature_dataset + task.progress(100) + "" + end + end + + def delete_dependent(uri, subjectid=nil) + begin + RestClient.delete(uri, :subjectid => subjectid) if subjectid + RestClient.delete(uri) if !subjectid + rescue + LOGGER.warn "Can not delete uri: #{uri}" + end + end +end + +before do + #unless env['REQUEST_METHOD'] == "GET" or ( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) or !AA_SERVER + if !logged_in and !( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) #or !AA_SERVER + login("guest","guest") + #flash[:notice] = "You have to login first to do this." + #redirect url_for('/login') + end + #end +end + +get '/?' do + redirect url_for('/create') +end + +get '/login' do + haml :login +end + +get '/models/?' do + @models = ToxCreateModel.all(:order => [ :created_at.desc ]) + subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" + #@models.each { |model| model.process } + haml :models, :locals=>{:models=>@models, :subjectstring => subjectstring} +end + +get '/model/:id/status/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + #percentage_completed = task.metadata[OT.percentageCompleted] if (task = OpenTox::Task.exist?(model.task_uri)) + begin + haml :model_status, :locals=>{:model=>model}, :layout => false + rescue + return "unavailable" + end +end + +get '/model/:id/progress/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + if model.task_uri + if (OpenTox::Task.exist?(model.task_uri)) + task = OpenTox::Task.exist?(model.task_uri) + percentage_completed = task.percentageCompleted + end + begin + haml :model_progress, :locals=>{:percentage_completed=>percentage_completed}, :layout => false + rescue + return "unavailable" + end + else + return "" + end +end + +get '/model/:id/name/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + begin + case params[:mode] + when 'edit' + haml :model_name_edit, :locals=>{:model=>model}, :layout => false + when 'show' + haml :model_name, :locals=>{:model=>model}, :layout => false + else + params.inspect + end + rescue + return "unavailable" + end +end + +put '/model/:id/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + begin + if params[:name] && model.name != params[:name] + model.name = params[:name] + model.save + end + redirect url_for("/model/#{model.id}/name?mode=show") + rescue + return "unavailable" + end +end + + +get '/model/:id/:view/?' do + response['Content-Type'] = 'text/plain' + model = ToxCreateModel.get(params[:id]) + subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" + begin + #model.process + #model.save + case params[:view] + when "model" + haml :model, :locals=>{:model=>model,:subjectstring => subjectstring}, :layout => false + when /validation/ + haml :validation, :locals=>{:model=>model,:subjectstring => subjectstring}, :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 + + unless logged_in() + logout + flash[:notice] = "Please login to create a new model." + redirect url_for('/create') + end + subjectid = session[:subjectid] ? session[:subjectid] : nil + @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => subjectid) + @model.update :web_uri => url_for("/model/#{@model.id}", :full), :warnings => "" + @model.save + task = OpenTox::Task.create("Uploading dataset and creating lazar model",url_for("/models",:full)) do |task| + + @model.update :status => "Uploading and saving dataset" + begin + @dataset = OpenTox::Dataset.create(nil, subjectid) + # 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, subjectid) + when ".xls", ".xlsx" + @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path, subjectid) + else + error "#{params[:file][:filename]} has a unsupported file type." + end + rescue => e + error "Dataset creation failed with #{e.message}" + end + @dataset.save(subjectid) + task.progress(10) + 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" + task.progress(15) + begin + lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => subjectid) + 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 + task.progress(25) + @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::Crossvalidation.create( + {:algorithm_uri => lazar.metadata[OT.algorithm], + :dataset_uri => lazar.parameter("dataset_uri"), + :subjectid => subjectid, + :prediction_feature => lazar.parameter("prediction_feature"), + :algorithm_params => "feature_generation_uri=#{lazar.parameter("feature_generation_uri")}"}, + nil, OpenTox::SubTask.new(task,25,80)) + @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(subjectid).each do |k,v| + LOGGER.debug "mr ::: k: #{k.inspect} - v: #{v.inspect}" + begin + eval "@model.#{k.to_s} = v" if v + rescue + eval "@model.#{k.to_s} = 0" + end + end + @model.save + + @model.update :status => "Creating validation report" + begin + @model.update(:validation_report_uri => validation.find_or_create_report(subjectid, OpenTox::SubTask.new(task,80,90))) unless @model.dirty? + 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(subjectid)) unless @model.dirty? + qmrf_report = OpenTox::Crossvalidation::QMRFReport.create(@model.uri, subjectid, OpenTox::SubTask.new(task,90,99)) + @model.update(:validation_qmrf_uri => qmrf_report.uri) + 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 += "

Incorrect Smiles structures (ignored):

" + parser.smiles_errors.join("
") unless parser.smiles_errors.empty? + #@model.warnings += "

Irregular activities (ignored):

" + parser.activity_errors.join("
") unless parser.activity_errors.empty? + #duplicate_warnings = '' + #parser.duplicates.each {|inchi,lines| duplicate_warnings += "

#{lines.join('
')}

" if lines.size > 1 } + #@model.warnings += "

Duplicated structures (all structures/activities used for model building, please make sure, that the results were obtained from independent experiments):

" + 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 + subjectid = session[:subjectid] ? session[:subjectid] : nil + @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, :subjectid => subjectid}) + prediction_dataset = OpenTox::LazarPrediction.find(prediction_dataset_uri, subjectid) + 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], :subjectid => params[:subjectid]) + @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri, session[:subjectid]) + @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 + +post '/login' do +=begin + if session[:subjectid] != nil + flash[:notice] = "You are already logged in as user: #{session[:username]}. Please log out first." + redirect url_for('/login') + end +=end + if params[:username] == '' || params[:password] == '' + flash[:notice] = "Please enter username and password." + redirect url_for('/login') + end + if login(params[:username], params[:password]) + flash[:notice] = "Welcome #{session[:username]}!" + redirect url_for('/create') + #haml :create + else + flash[:notice] = "Login failed. Please try again." + haml :login + end +end + +post '/logout' do + logout + redirect url_for('/login') +end + +delete '/model/:id/?' do + model = ToxCreateModel.get(params[:id]) + raise OpenTox::NotFoundError.new("Model with id: #{params[:id]} not found!") unless model + begin + delete_model(model, @subjectid) + model.destroy + unless ToxCreateModel.get(params[:id]) + begin + aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, @subjectid) + LOGGER.debug "Policy deleted for Dataset URI: #{uri} with result: #{aa}" + rescue + LOGGER.warn "Policy delete error for Dataset URI: #{uri}" + end + end + 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 -- cgit v1.2.3 From f020b6324e567459cb5ce309371142052782ee86 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Wed, 23 Feb 2011 17:12:08 +0100 Subject: lazar.haml added again --- views/lazar.haml | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 views/lazar.haml diff --git a/views/lazar.haml b/views/lazar.haml new file mode 100644 index 0000000..3de7400 --- /dev/null +++ b/views/lazar.haml @@ -0,0 +1,59 @@ +%p= link_to "New prediction", "/predict" +.lazar-predictions + + -# explanations + = haml :lazar_algorithm, :layout => false + = haml :confidence, :layout => false + = haml :similarity, :layout => false + = haml :significant_fragments, :layout => false + = haml :training_data, :layout => false + + %a{:name => "prediction"} + %table + %thead + %tr + %th= @prediction.title + %th= toggle_link("#lazar_algorithm","Prediction") + %th= toggle_link("#confidence","Confidence") + %th Supporting information + + %tr + -# %td + %img{:src => @compound.to_image_uri, :alt => @compound.to_smiles} + %td.image= compound_image(@compound,@prediction.descriptors(@compound)) + %td= activity_markup(@prediction.value(@compound)) + %td= sprintf('%.03g', @prediction.confidence(@compound)) + -#%td= @prediction.confidence(@compound) + %td + %ul + %li + %a{:href => "#prediction", :id => "show_names"} Names and synonyms + :javascript + $("a#show_names").click(function () { + $("#compound_names").load("#{File.join("/compound",@compound.inchi)}"); + $("tr#names").toggle(); + }); + %li= toggle_link("#fragments","Significant fragments") + -# This does not work, ask nina/vedrin + -# %li + %a{:href => "http://ambit.uni-plovdiv.bg:8080/ambit2/query/structure/?search=#{@compound.smiles}"} Ambit data + -# %li + %a{:href => "http://www.ncbi.nlm.nih.gov/sites/entrez?cmd=PureSearch&db=pccompound&term=#{URI.encode('"'+@compound.inchi+'"[InChI]')}"} PubChem data + (external) + -# %li + %a{:href => "http://chem.sis.nlm.nih.gov/chemidplus/direct.jsp?result=advanced&inchi=#{URI.encode @compound.inchi}"} ToxNet data + -#http://chem.sis.nlm.nih.gov/chemidplus/direct.jsp?result=advanced®no=000143157 + + %tr#names{ :style => "display: none;" } + %td{:colspan => '4'} + %a{:name => 'names'} + = hide_link('#names') + #compound_names + %tr#fragments{ :style => "display: none;" } + %td{:colspan => '4'} + = hide_link('#fragments') + = haml :feature_table, :locals => {:features => sort(@prediction.descriptors(@compound))}, :layout => false + + %tbody#neighbors + = haml :neighbors, :locals => {:neighbors => @prediction.neighbors(@compound), :page => @page}, :layout => false + -- cgit v1.2.3 From aad966c8dfd7ccdcf93c5698a8155b00a91c653f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 24 Feb 2011 11:31:25 +0000 Subject: predictions always enabled --- application.rb | 2 ++ views/predict.haml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/application.rb b/application.rb index a892c03..88763d9 100644 --- a/application.rb +++ b/application.rb @@ -14,6 +14,8 @@ use Rack::Session::Cookie, :expire_after => 28800, :secret => "ui6vaiNi-change_me" use Rack::Flash +set :lock, true + helpers do def error(message) diff --git a/views/predict.haml b/views/predict.haml index b6e1088..347592c 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -26,7 +26,8 @@ - @models.each do |model| %label{:for => model.id} = model.name - %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id, :disabled => !is_authorized(model.uri, "GET")} + -#%input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id, :disabled => !is_authorized(model.uri, "GET")} + %input{:type => 'checkbox', :name => "selection[#{model.id}]", :value => true, :id => model.id, :disabled => false} %br %input{:type => 'hidden', :name => 'subjectid', :id => 'subjectid', :value => session[:subjectid]} -- cgit v1.2.3 From 18801dca5ff1458384491b5bdf5c15271f0681d5 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 25 Feb 2011 17:51:37 +0000 Subject: ohm/redis backend --- application.rb | 135 ++++++++++++++++++++--------------- helper.rb | 2 +- model.rb | 119 +++++++++++++++++++----------- views/classification_validation.haml | 8 +-- views/model.haml | 4 +- 5 files changed, 159 insertions(+), 109 deletions(-) diff --git a/application.rb b/application.rb index 88763d9..13248e6 100644 --- a/application.rb +++ b/application.rb @@ -19,11 +19,8 @@ set :lock, true helpers do def error(message) - @model.error_messages = message LOGGER.error message - @model.status = "Error" - @model.save - #@dataset.delete + @model.update :status => "Error", :error_messages => message flash[:notice] = message redirect url_for('/create') end @@ -60,13 +57,9 @@ helpers do end before do - #unless env['REQUEST_METHOD'] == "GET" or ( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) or !AA_SERVER if !logged_in and !( env['REQUEST_URI'] =~ /\/login$/ and env['REQUEST_METHOD'] == "POST" ) #or !AA_SERVER login("guest","guest") - #flash[:notice] = "You have to login first to do this." - #redirect url_for('/login') end - #end end get '/?' do @@ -78,7 +71,7 @@ get '/login' do end get '/models/?' do - @models = ToxCreateModel.all(:order => [ :created_at.desc ]) + @models = ToxCreateModel.all#(:order => [ :created_at.desc ]) subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" #@models.each { |model| model.process } haml :models, :locals=>{:models=>@models, :subjectstring => subjectstring} @@ -135,8 +128,8 @@ put '/model/:id/?' do model = ToxCreateModel.get(params[:id]) begin if params[:name] && model.name != params[:name] - model.name = params[:name] - model.save + model.update :name => params[:name] + #model.save end redirect url_for("/model/#{model.id}/name?mode=show") rescue @@ -166,7 +159,7 @@ get '/model/:id/:view/?' do end get '/predict/?' do - @models = ToxCreateModel.all(:order => [ :created_at.desc ]) + @models = ToxCreateModel.all#(:order => [ :created_at.desc ]) @models = @models.collect{|m| m if m.status == 'Completed'}.compact haml :predict end @@ -204,9 +197,10 @@ post '/models' do # create a new model subjectid = session[:subjectid] ? session[:subjectid] : nil @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => subjectid) @model.update :web_uri => url_for("/model/#{@model.id}", :full), :warnings => "" - @model.save + #@model.save task = OpenTox::Task.create("Uploading dataset and creating lazar model",url_for("/models",:full)) do |task| + task.progress(5) @model.update :status => "Uploading and saving dataset" begin @dataset = OpenTox::Dataset.create(nil, subjectid) @@ -214,7 +208,7 @@ post '/models' do # create a new model case File.extname(params[:file][:filename]) when ".csv" csv = params[:file][:tempfile].read - LOGGER.debug csv + #LOGGER.debug csv @dataset.load_csv(csv, subjectid) when ".xls", ".xlsx" @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path, subjectid) @@ -235,12 +229,14 @@ post '/models' do # create a new model 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" + @model.update :training_dataset => @dataset.uri, :nr_compounds => @dataset.compounds.size, :status => "Creating prediction model" + @model.update :warnings => @dataset.metadata[OT.Warnings] unless @dataset.metadata[OT.Warnings].empty? + #@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" task.progress(15) begin lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => subjectid) @@ -248,17 +244,16 @@ post '/models' do # create a new model 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 task.progress(25) - @model.feature_dataset = lazar.metadata[OT.featureDataset] - @model.uri = lazar.uri + #@model.feature_dataset = lazar.metadata[OT.featureDataset] + #@model.uri = lazar.uri + type = "unknown" case lazar.metadata[OT.isA] when /Classification/ - @model.type = "classification" + type = "classification" when /Regression/ - @model.type = "regression" - else - @model.type = "unknown" + type = "regression" end - @model.save + @model.update :type => type, :feature_dataset => lazar.metadata[OT.featureDataset], :uri => lazar.uri unless url_for("",:full).match(/localhost/) @model.update :status => "Validating model" @@ -272,37 +267,43 @@ post '/models' do # create a new model nil, OpenTox::SubTask.new(task,25,80)) @model.update(:validation_uri => validation.uri) LOGGER.debug "Validation URI: #{@model.validation_uri}" + + # create summary + validation.summary(subjectid).each do |k,v| + #LOGGER.debug "mr ::: k: #{k.inspect} - v: #{v.inspect}" + begin + eval "@model.update :#{k.to_s} => v" if v + rescue + eval "@model.update :#{k.to_s} => 0" + end + end + + @model.update :status => "Creating validation report" + #begin + validation_report_uri = validation.find_or_create_report(subjectid, OpenTox::SubTask.new(task,80,90)) #unless @model.dirty? + @model.update :validation_report_uri => validation_report_uri, :status => "Creating QMRF report" + #rescue => e + #LOGGER.debug "Validation report generation failed with #{e.message}." + #@model.update :warnings => @model.warnings + "\nValidation report generation failed with #{e.message}." + #end + #@model.update :status => "Creating QMRF report" + #begin + #@model.update(:validation_qmrf_report_uri => validation.create_qmrf_report(subjectid)) unless @model.dirty? + qmrf_report = OpenTox::Crossvalidation::QMRFReport.create(@model.uri, subjectid, OpenTox::SubTask.new(task,90,99)) + @model.update(:validation_qmrf_uri => qmrf_report.uri, :status => "Completed") + #rescue => e + #LOGGER.debug "Validation QMRF report generation failed with #{e.message}." + #@model.update :warnings => @model.warnings + "\nValidation QMRF report generation failed with #{e.message}." + #end + #@model.update :status => "Completed" + rescue => e LOGGER.debug "Model validation failed with #{e.message}." - @model.warnings += "Model validation failed with #{e.message}." - end - # create summary - validation.summary(subjectid).each do |k,v| - LOGGER.debug "mr ::: k: #{k.inspect} - v: #{v.inspect}" - begin - eval "@model.#{k.to_s} = v" if v - rescue - eval "@model.#{k.to_s} = 0" - end + @model.save # to avoid dirty models + @model.update :warnings => @model.warnings + "\nModel validation failed with #{e.message}.", :status => "Error", :error_messages => e.message end - @model.save + #@model.save - @model.update :status => "Creating validation report" - begin - @model.update(:validation_report_uri => validation.find_or_create_report(subjectid, OpenTox::SubTask.new(task,80,90))) unless @model.dirty? - 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(subjectid)) unless @model.dirty? - qmrf_report = OpenTox::Crossvalidation::QMRFReport.create(@model.uri, subjectid, OpenTox::SubTask.new(task,90,99)) - @model.update(:validation_qmrf_uri => qmrf_report.uri) - rescue => e - LOGGER.debug "Validation QMRF report generation failed with #{e.message}." - @model.warnings += "Validation QMRF report generation failed with #{e.message}." - end end @@ -311,11 +312,10 @@ post '/models' do # create a new model #duplicate_warnings = '' #parser.duplicates.each {|inchi,lines| duplicate_warnings += "

#{lines.join('
')}

" if lines.size > 1 } #@model.warnings += "

Duplicated structures (all structures/activities used for model building, please make sure, that the results were obtained from independent experiments):

" + duplicate_warnings unless duplicate_warnings.empty? - @model.update :status => "Completed" lazar.uri end @model.update(:task_uri => task.uri) - @model.save + #@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') @@ -356,7 +356,7 @@ post '/predict/?' do # post chemical name to model else predicted_feature = prediction_dataset.metadata[OT.dependentVariables] prediction = OpenTox::Feature.find(predicted_feature) - LOGGER.debug prediction.to_yaml + #LOGGER.debug prediction.to_yaml if prediction.metadata[OT.error] @predictions << { :title => model.name, @@ -403,7 +403,7 @@ post '/predict/?' do # post chemical name to model end =end end - LOGGER.debug @predictions.inspect + #LOGGER.debug @predictions.inspect haml :prediction end @@ -491,7 +491,24 @@ delete '/model/:id/?' do end delete '/?' do - DataMapper.auto_migrate! + #DataMapper.auto_migrate! + ToxCreateModel.all.each do |model| +# begin +# delete_model(model, @subjectid) + model.delete +# unless ToxCreateModel.get(params[:id]) +# begin +# aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, @subjectid) +# LOGGER.debug "Policy deleted for Dataset URI: #{uri} with result: #{aa}" +# rescue +# LOGGER.warn "Policy delete error for Dataset URI: #{uri}" +# end +# end +# LOGGER.debug "#{model.name} model deleted." +# rescue +# LOGGER.error "#{model.name} model delete error." +# end + end response['Content-Type'] = 'text/plain' "All Models deleted." end diff --git a/helper.rb b/helper.rb index 3fbd06f..d2c5593 100644 --- a/helper.rb +++ b/helper.rb @@ -53,7 +53,7 @@ helpers do features = {:activating => [], :deactivating => []} descriptors.each { |d| LOGGER.debug d.inspect; features[d[OT.effect].to_sym] << {:smarts => d[OT.smarts],:p_value => d[OT.pValue]} } - LOGGER.debug features.to_yaml + #LOGGER.debug features.to_yaml features end diff --git a/model.rb b/model.rb index eb459b3..7d22a4d 100644 --- a/model.rb +++ b/model.rb @@ -1,51 +1,84 @@ -class ToxCreateModel - - include DataMapper::Resource - - property :id, Serial - property :name, String, :length => 255 - property :warnings, Text, :length => 2**32-1 - property :error_messages, Text, :length => 2**32-1 # :errors interferes with datamapper validation - property :type, String - property :status, String, :length => 255 - property :created_at, DateTime - - property :task_uri, String, :length => 255 - property :uri, String, :length => 255 - - property :training_dataset, String, :length => 255 - property :feature_dataset, String, :length => 255 - #property :validation_task_uri, String, :length => 255 - property :validation_uri, String, :length => 255 - - #property :validation_report_task_uri, String, :length => 255 - property :validation_report_uri, String, :length => 255 - - #property :validation_qmrf_task_uri, String, :length => 255 - property :validation_qmrf_uri, String, :length => 255 - - property :nr_compounds, Integer - property :nr_predictions, Integer - property :true_positives, Integer - property :false_positives, Integer - property :true_negatives, Integer - property :false_negatives, Integer - property :correct_predictions, Integer - property :weighted_area_under_roc, Float - property :sensitivity, Float - property :specificity, Float - property :r_square, Float - property :root_mean_squared_error, Float - property :mean_absolute_error, Float - - property :web_uri, String, :length => 255 +require 'ohm' +#require 'redis/objects' +#@@redis = Redis.new(:thread_safe=>true)#(:host => '127.0.0.1', :port => 6379) + +class ToxCreateModel < Ohm::Model + + #include Redis::Objects + #include DataMapper::Resource + #attribute :id + attribute :name + attribute :warnings + attribute :error_messages + attribute :type + attribute :status + attribute :created_at + + attribute :task_uri + attribute :uri + + attribute :training_dataset + attribute :feature_dataset + #attributey :validation_task_uri + attribute :validation_uri + + #attributey :validation_report_task_uri + attribute :validation_report_uri + + #attributey :validation_qmrf_task_uri + attribute :validation_qmrf_uri + + attribute :nr_compounds + attribute :nr_predictions + attribute :true_positives + attribute :false_positives + attribute :true_negatives + attribute :false_negatives + attribute :correct_predictions + attribute :weighted_area_under_roc + attribute :sensitivity + attribute :specificity + attribute :r_square + attribute :root_mean_squared_error + attribute :mean_absolute_error + + attribute :web_uri attr_accessor :subjectid @subjectid = nil - after :save, :check_policy + #after :save, :check_policy =begin + attr_accessor :id, :name, :warnings, :error_messages, :type, :status, :created_at, :task_uri, :uri, :training_dataset, :feature_dataset, :validation_task_uri, :validation_uri, :validation_report_task_uri, :validation_report_uri, :validation_qmrf_task_uri, :validation_qmrf_uri, :nr_compounds, :nr_predictions, :true_positives, :false_positives, :true_negatives, :false_negatives, :correct_predictions, :weighted_area_under_roc, :sensitivity, :specificity, :r_square, :root_mean_squared_error, :mean_absolute_error, :web_uri + + def self.all + end + + def self.get(id) + end + + def self.create(params) + @id = @@redis.incr "toxcreate" + params.each { |k,v| @@redis.hset "toxcreate:#{@id}", k, v } + self.get(@id) + end + + def update(params) + end + + def method_missing + begin + rescue + raise "Unknown method" + end + end + + attr_accessor :subjectid + @subjectid = nil + + after :save, :check_policy + def status #begin RestClient.get(File.join(@task_uri, 'hasStatus')).body @@ -195,4 +228,4 @@ def status end -DataMapper.auto_upgrade! +#DataMapper.auto_upgrade! diff --git a/views/classification_validation.haml b/views/classification_validation.haml index 94ba8f9..41362ba 100644 --- a/views/classification_validation.haml +++ b/views/classification_validation.haml @@ -1,17 +1,17 @@ %dt Correct predictions: %dd - = sprintf("%.2f", model.correct_predictions) if model.correct_predictions + = sprintf("%.2f", model.correct_predictions.to_f) if model.correct_predictions = '%' %dt %a{:href => "http://en.wikipedia.org/wiki/Receiver_operating_characteristic", :target => "_blank"} Weighted area under ROC: %dd - = sprintf("%.3f", model.weighted_area_under_roc) if model.weighted_area_under_roc + = sprintf("%.3f", model.weighted_area_under_roc.to_f) if model.weighted_area_under_roc %dt %a{:href => "http://en.wikipedia.org/wiki/Sensitivity_and_specificity", :target => "_blank"} Specificity: -%dd= sprintf("%.3f", model.specificity) if model.specificity +%dd= sprintf("%.3f", model.specificity.to_f) if model.specificity %dt %a{:href => "http://en.wikipedia.org/wiki/Sensitivity_and_specificity", :target => "_blank"} Sensitivity: -%dd= sprintf("%.3f", model.sensitivity) if model.sensitivity +%dd= sprintf("%.3f", model.sensitivity.to_f) if model.sensitivity %dt %a{:href => "http://en.wikipedia.org/wiki/Confusion_matrix", :target => "_blank"} Confusion Matrix: %dd diff --git a/views/model.haml b/views/model.haml index 8112dd6..e1bdd99 100644 --- a/views/model.haml +++ b/views/model.haml @@ -35,8 +35,8 @@ %span %br - %dt Started: - %dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") + -#%dt Started: + -#%dd= model.created_at.strftime("%m/%d/%Y - %I:%M:%S%p") - if model.nr_compounds %dt Training compounds: %dd= model.nr_compounds -- cgit v1.2.3 From 0f7a8d6692170bb3e369a78a92e848a60baa7ad3 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Sun, 27 Feb 2011 09:32:57 +0100 Subject: code cleanup --- application.rb | 148 ++++++++++----------------------------------------------- helper.rb | 1 - 2 files changed, 24 insertions(+), 125 deletions(-) diff --git a/application.rb b/application.rb index 13248e6..fd762aa 100644 --- a/application.rb +++ b/application.rb @@ -8,7 +8,6 @@ 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::Session::Cookie, :expire_after => 28800, :secret => "ui6vaiNi-change_me" @@ -28,7 +27,7 @@ helpers do private def delete_model(model, subjectid=nil) task = OpenTox::Task.create("Deleting model: #{model.uri}",url_for("/delete",:full)) do |task| - begin RestClient.put(File.join(model.task_uri, 'Cancelled'),subjectid) if model.task_uri rescue LOGGER.warn "Can not cancel task #{model.task_uri}" end + begin RestClient.put(File.join(model.task_uri, 'Cancelled'),subjectid) if model.task_uri rescue LOGGER.warn "Cannot cancel task #{model.task_uri}" end task.progress(15) delete_dependent(model.uri, subjectid) if model.uri task.progress(30) @@ -71,20 +70,18 @@ get '/login' do end get '/models/?' do - @models = ToxCreateModel.all#(:order => [ :created_at.desc ]) + @models = ToxCreateModel.all.reverse subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" - #@models.each { |model| model.process } haml :models, :locals=>{:models=>@models, :subjectstring => subjectstring} end get '/model/:id/status/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) - #percentage_completed = task.metadata[OT.percentageCompleted] if (task = OpenTox::Task.exist?(model.task_uri)) begin haml :model_status, :locals=>{:model=>model}, :layout => false rescue - return "unavailable" + return "Model #{params[:id]} not available" end end @@ -127,10 +124,7 @@ put '/model/:id/?' do response['Content-Type'] = 'text/plain' model = ToxCreateModel.get(params[:id]) begin - if params[:name] && model.name != params[:name] - model.update :name => params[:name] - #model.save - end + model.update :name => params[:name] if params[:name] && model.name != params[:name] redirect url_for("/model/#{model.id}/name?mode=show") rescue return "unavailable" @@ -143,8 +137,6 @@ get '/model/:id/:view/?' do model = ToxCreateModel.get(params[:id]) subjectstring = session[:subjectid] ? "?subjectid=#{CGI.escape(session[:subjectid])}" : "" begin - #model.process - #model.save case params[:view] when "model" haml :model, :locals=>{:model=>model,:subjectstring => subjectstring}, :layout => false @@ -159,7 +151,7 @@ get '/model/:id/:view/?' do end get '/predict/?' do - @models = ToxCreateModel.all#(:order => [ :created_at.desc ]) + @models = ToxCreateModel.all.reverse @models = @models.collect{|m| m if m.status == 'Completed'}.compact haml :predict end @@ -197,7 +189,6 @@ post '/models' do # create a new model subjectid = session[:subjectid] ? session[:subjectid] : nil @model = ToxCreateModel.create(:name => params[:file][:filename].sub(/\..*$/,""), :subjectid => subjectid) @model.update :web_uri => url_for("/model/#{@model.id}", :full), :warnings => "" - #@model.save task = OpenTox::Task.create("Uploading dataset and creating lazar model",url_for("/models",:full)) do |task| task.progress(5) @@ -208,7 +199,6 @@ post '/models' do # create a new model case File.extname(params[:file][:filename]) when ".csv" csv = params[:file][:tempfile].read - #LOGGER.debug csv @dataset.load_csv(csv, subjectid) when ".xls", ".xlsx" @dataset.load_spreadsheet(Excel.new params[:file][:tempfile].path, subjectid) @@ -231,12 +221,6 @@ post '/models' do # create a new model end @model.update :training_dataset => @dataset.uri, :nr_compounds => @dataset.compounds.size, :status => "Creating prediction model" @model.update :warnings => @dataset.metadata[OT.Warnings] unless @dataset.metadata[OT.Warnings].empty? - #@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" task.progress(15) begin lazar = OpenTox::Model::Lazar.create(:dataset_uri => @dataset.uri, :subjectid => subjectid) @@ -244,8 +228,6 @@ post '/models' do # create a new model 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 task.progress(25) - #@model.feature_dataset = lazar.metadata[OT.featureDataset] - #@model.uri = lazar.uri type = "unknown" case lazar.metadata[OT.isA] when /Classification/ @@ -279,30 +261,16 @@ post '/models' do # create a new model end @model.update :status => "Creating validation report" - #begin - validation_report_uri = validation.find_or_create_report(subjectid, OpenTox::SubTask.new(task,80,90)) #unless @model.dirty? - @model.update :validation_report_uri => validation_report_uri, :status => "Creating QMRF report" - #rescue => e - #LOGGER.debug "Validation report generation failed with #{e.message}." - #@model.update :warnings => @model.warnings + "\nValidation report generation failed with #{e.message}." - #end - #@model.update :status => "Creating QMRF report" - #begin - #@model.update(:validation_qmrf_report_uri => validation.create_qmrf_report(subjectid)) unless @model.dirty? - qmrf_report = OpenTox::Crossvalidation::QMRFReport.create(@model.uri, subjectid, OpenTox::SubTask.new(task,90,99)) - @model.update(:validation_qmrf_uri => qmrf_report.uri, :status => "Completed") - #rescue => e - #LOGGER.debug "Validation QMRF report generation failed with #{e.message}." - #@model.update :warnings => @model.warnings + "\nValidation QMRF report generation failed with #{e.message}." - #end - #@model.update :status => "Completed" + validation_report_uri = validation.find_or_create_report(subjectid, OpenTox::SubTask.new(task,80,90)) #unless @model.dirty? + @model.update :validation_report_uri => validation_report_uri, :status => "Creating QMRF report" + qmrf_report = OpenTox::Crossvalidation::QMRFReport.create(@model.uri, subjectid, OpenTox::SubTask.new(task,90,99)) + @model.update(:validation_qmrf_uri => qmrf_report.uri, :status => "Completed") rescue => e LOGGER.debug "Model validation failed with #{e.message}." @model.save # to avoid dirty models @model.update :warnings => @model.warnings + "\nModel validation failed with #{e.message}.", :status => "Error", :error_messages => e.message end - #@model.save end @@ -315,13 +283,10 @@ post '/models' do # create a new model 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 @@ -340,7 +305,6 @@ post '/predict/?' do # post chemical name to model @predictions = [] params[:selection].keys.each do |id| model = ToxCreateModel.get(id.to_i) - #model.process unless model.uri prediction = nil confidence = nil title = nil @@ -356,7 +320,6 @@ post '/predict/?' do # post chemical name to model 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, @@ -372,38 +335,7 @@ post '/predict/?' do # post chemical name to model 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 @@ -416,40 +348,10 @@ post "/lazar/?" do # get detailed prediction prediction_dataset_uri = lazar.run(:compound_uri => params[:compound_uri], :subjectid => params[:subjectid]) @prediction = OpenTox::LazarPrediction.find(prediction_dataset_uri, session[:subjectid]) @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 post '/login' do -=begin - if session[:subjectid] != nil - flash[:notice] = "You are already logged in as user: #{session[:username]}. Please log out first." - redirect url_for('/login') - end -=end if params[:username] == '' || params[:password] == '' flash[:notice] = "Please enter username and password." redirect url_for('/login') @@ -457,7 +359,6 @@ post '/login' do if login(params[:username], params[:password]) flash[:notice] = "Welcome #{session[:username]}!" redirect url_for('/create') - #haml :create else flash[:notice] = "Login failed. Please try again." haml :login @@ -474,7 +375,7 @@ delete '/model/:id/?' do raise OpenTox::NotFoundError.new("Model with id: #{params[:id]} not found!") unless model begin delete_model(model, @subjectid) - model.destroy + model.delete unless ToxCreateModel.get(params[:id]) begin aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, @subjectid) @@ -491,23 +392,22 @@ delete '/model/:id/?' do end delete '/?' do - #DataMapper.auto_migrate! ToxCreateModel.all.each do |model| -# begin -# delete_model(model, @subjectid) + begin + delete_model(model, @subjectid) model.delete -# unless ToxCreateModel.get(params[:id]) -# begin -# aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, @subjectid) -# LOGGER.debug "Policy deleted for Dataset URI: #{uri} with result: #{aa}" -# rescue -# LOGGER.warn "Policy delete error for Dataset URI: #{uri}" -# end -# end -# LOGGER.debug "#{model.name} model deleted." -# rescue -# LOGGER.error "#{model.name} model delete error." -# end + unless ToxCreateModel.get(params[:id]) + begin + aa = OpenTox::Authorization.delete_policies_from_uri(model.web_uri, @subjectid) + LOGGER.debug "Policy deleted for Dataset URI: #{uri} with result: #{aa}" + rescue + LOGGER.warn "Policy delete error for Dataset URI: #{uri}" + end + end + LOGGER.debug "#{model.name} model deleted." + rescue + LOGGER.error "#{model.name} model delete error." + end end response['Content-Type'] = 'text/plain' "All Models deleted." diff --git a/helper.rb b/helper.rb index d2c5593..0813fff 100644 --- a/helper.rb +++ b/helper.rb @@ -53,7 +53,6 @@ helpers do features = {:activating => [], :deactivating => []} descriptors.each { |d| LOGGER.debug d.inspect; features[d[OT.effect].to_sym] << {:smarts => d[OT.smarts],:p_value => d[OT.pValue]} } - #LOGGER.debug features.to_yaml features end -- cgit v1.2.3 From c1964f8418296f14d0f3728b5b55354fb9d982f0 Mon Sep 17 00:00:00 2001 From: Christoph Helma Date: Wed, 9 Mar 2011 12:00:09 +0100 Subject: version bumped to 1.0.0 --- application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application.rb b/application.rb index fd762aa..1771661 100644 --- a/application.rb +++ b/application.rb @@ -1,7 +1,7 @@ ['rubygems', "haml", "sass", "rack-flash"].each do |lib| require lib end -gem "opentox-ruby", "~> 0" +gem "opentox-ruby", "~> 1" require 'opentox-ruby' gem 'sinatra-static-assets' require 'sinatra/static_assets' -- cgit v1.2.3