summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Rakefile16
-rw-r--r--lib/algorithm.rb168
-rw-r--r--lib/authorization.rb29
-rw-r--r--lib/dataset.rb33
-rw-r--r--lib/environment.rb2
-rw-r--r--lib/error.rb4
-rw-r--r--lib/feature.rb26
-rw-r--r--lib/helper.rb67
-rw-r--r--lib/model.rb146
-rw-r--r--lib/ontology.rb (renamed from lib/ontology_service.rb)28
-rw-r--r--lib/opentox-ruby.rb2
-rw-r--r--lib/opentox.rb9
-rw-r--r--lib/overwrite.rb15
-rw-r--r--lib/parser.rb53
-rw-r--r--lib/rest_client_wrapper.rb2
-rw-r--r--lib/serializer.rb20
-rw-r--r--lib/task.rb4
-rw-r--r--lib/templates/config.yaml86
-rw-r--r--lib/to-html.rb2
-rw-r--r--lib/validation.rb127
20 files changed, 553 insertions, 286 deletions
diff --git a/Rakefile b/Rakefile
index b496cc2..08959b0 100644
--- a/Rakefile
+++ b/Rakefile
@@ -28,8 +28,17 @@ begin
"tmail",
"rinruby",
"ohm",
+ "ohm-contrib",
"SystemTimer",
- "rjb"
+ "rjb",
+ #valiation-gems
+ "dm-core",
+ "dm-serializer",
+ "dm-timestamps",
+ "dm-types",
+ "dm-migrations",
+ "dm-validations",
+ "dm-sqlite-adapter"
].each { |dep| gem.add_dependency dep }
=begin
[ "dm-core",
@@ -41,11 +50,12 @@ begin
"dm-validations",
].each {|dep| gem.add_dependency dep, ">= 1" }
=end
+ #valiation-gem
gem.add_dependency "haml", ">=3"
+ # validation-gems
+ gem.add_dependency "ruby-plot", "~>0.4.0"
['jeweler'].each { |dep| gem.add_development_dependency dep }
gem.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
- #gem.files.include %w(lib/environment.rb, lib/algorithm.rb, lib/compound.rb, lib/dataset.rb, lib/model.rb, lib/validation.rb, lib/templates/*)
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
rescue LoadError
diff --git a/lib/algorithm.rb b/lib/algorithm.rb
index 21a5729..5b41cbf 100644
--- a/lib/algorithm.rb
+++ b/lib/algorithm.rb
@@ -52,9 +52,9 @@ module OpenTox
class BBRC
include Fminer
# Initialize bbrc algorithm
- def initialize
+ def initialize(subjectid=nil)
super File.join(CONFIG[:services]["opentox-algorithm"], "fminer/bbrc")
- load_metadata
+ load_metadata(subjectid)
end
end
@@ -62,9 +62,9 @@ module OpenTox
class LAST
include Fminer
# Initialize last algorithm
- def initialize
+ def initialize(subjectid=nil)
super File.join(CONFIG[:services]["opentox-algorithm"], "fminer/last")
- load_metadata
+ load_metadata(subjectid)
end
end
@@ -74,9 +74,9 @@ module OpenTox
class Lazar
include Algorithm
# Initialize lazar algorithm
- def initialize
+ def initialize(subjectid=nil)
super File.join(CONFIG[:services]["opentox-algorithm"], "lazar")
- load_metadata
+ load_metadata(subjectid)
end
end
@@ -164,65 +164,127 @@ module OpenTox
# @param [Array] neighbors, each neighbor is a hash with keys `:similarity, :activity, :features`
# @param [Hash] params Keys `:similarity_algorithm,:p_values` are required
# @return [Hash] Hash with keys `:prediction, :confidence`
- def self.local_svm_regression(neighbors,params )
- sims = neighbors.collect{ |n| Algorithm.gauss(n[:similarity]) } # similarity values between query and neighbors
- conf = sims.inject{|sum,x| sum + x }
+ def self.local_svm_regression(neighbors, params)
+ take_logs=true
+ neighbors.each do |n|
+ if (! n[:activity].nil?) && (n[:activity].to_f < 0.0)
+ take_logs = false
+ end
+ end
acts = neighbors.collect do |n|
act = n[:activity]
- Math.log10(act.to_f)
+ take_logs ? Math.log10(act.to_f) : act.to_f
end # activities of neighbors for supervised learning
- neighbor_matches = neighbors.collect{ |n| n[:features] } # as in classification: URIs of matches
- gram_matrix = [] # square matrix of similarities between neighbors; implements weighted tanimoto kernel
- if neighbor_matches.size == 0
- raise "No neighbors found"
- else
- # gram matrix
- (0..(neighbor_matches.length-1)).each do |i|
- gram_matrix[i] = [] unless gram_matrix[i]
- # upper triangle
- ((i+1)..(neighbor_matches.length-1)).each do |j|
- sim = eval("#{params[:similarity_algorithm]}(neighbor_matches[i], neighbor_matches[j], params[:p_values])")
- gram_matrix[i][j] = Algorithm.gauss(sim)
- gram_matrix[j] = [] unless gram_matrix[j]
- gram_matrix[j][i] = gram_matrix[i][j] # lower triangle
- end
- gram_matrix[i][i] = 1.0
- end
+ sims = neighbors.collect{ |n| Algorithm.gauss(n[:similarity]) } # similarity values btwn q and nbors
+ begin
+ prediction = local_svm(neighbors, acts, sims, "nu-svr", params)
+ prediction = (take_logs ? 10**(prediction.to_f) : prediction.to_f)
+ LOGGER.debug "Prediction is: '" + prediction.to_s + "'."
+ rescue Exception => e
+ LOGGER.debug "#{e.class}: #{e.message} #{e.backtrace}"
+ end
- #LOGGER.debug gram_matrix.to_yaml
- @r = RinRuby.new(false,false) # global R instance leads to Socket errors after a large number of requests
- @r.eval "library('kernlab')" # this requires R package "kernlab" to be installed
- LOGGER.debug "Setting R data ..."
- # set data
- @r.gram_matrix = gram_matrix.flatten
- @r.n = neighbor_matches.size
- @r.y = acts
- @r.sims = sims
+ conf = sims.inject{|sum,x| sum + x }
+ confidence = conf/neighbors.size if neighbors.size > 0
+ {:prediction => prediction, :confidence => confidence}
+
+ end
- LOGGER.debug "Preparing R data ..."
- # prepare data
- @r.eval "y<-as.vector(y)"
- @r.eval "gram_matrix<-as.kernelMatrix(matrix(gram_matrix,n,n))"
- @r.eval "sims<-as.vector(sims)"
-
- # model + support vectors
- LOGGER.debug "Creating SVM model ..."
- @r.eval "model<-ksvm(gram_matrix, y, kernel=matrix, type=\"nu-svr\", nu=0.8)"
- @r.eval "sv<-as.vector(SVindex(model))"
- @r.eval "sims<-sims[sv]"
- @r.eval "sims<-as.kernelMatrix(matrix(sims,1))"
- LOGGER.debug "Predicting ..."
- @r.eval "p<-predict(model,sims)[1,1]"
- prediction = 10**(@r.p.to_f)
- LOGGER.debug "Prediction is: '" + @prediction.to_s + "'."
- @r.quit # free R
+ # Local support vector classification from neighbors
+ # @param [Array] neighbors, each neighbor is a hash with keys `:similarity, :activity, :features`
+ # @param [Hash] params Keys `:similarity_algorithm,:p_values` are required
+ # @return [Hash] Hash with keys `:prediction, :confidence`
+ def self.local_svm_classification(neighbors, params)
+ acts = neighbors.collect do |n|
+ act = n[:activity]
+ end # activities of neighbors for supervised learning
+ acts_f = acts.collect {|v| v == true ? 1.0 : 0.0}
+ sims = neighbors.collect{ |n| Algorithm.gauss(n[:similarity]) } # similarity values btwn q and nbors
+ begin
+ prediction = local_svm (neighbors, acts_f, sims, "C-bsvc", params)
+ LOGGER.debug "Prediction is: '" + prediction.to_s + "'."
+ rescue Exception => e
+ LOGGER.debug "#{e.class}: #{e.message} #{e.backtrace}"
end
+
+ conf = sims.inject{|sum,x| sum + x }
confidence = conf/neighbors.size if neighbors.size > 0
{:prediction => prediction, :confidence => confidence}
end
+
+ # Local support vector prediction from neighbors.
+ # Not to be called directly (use local_svm_regression or local_svm_classification.
+ # @param [Array] neighbors, each neighbor is a hash with keys `:similarity, :activity, :features`
+ # @param [Array] acts, activities for neighbors.
+ # @param [Array] sims, similarities for neighbors.
+ # @param [String] type, one of "nu-svr" (regression) or "C-bsvc" (classification).
+ # @param [Hash] params Keys `:similarity_algorithm,:p_values` are required
+ # @return [Numeric] A prediction value.
+ def self.local_svm(neighbors, acts, sims, type, params)
+ neighbor_matches = neighbors.collect{ |n| n[:features] } # URIs of matches
+ gram_matrix = [] # square matrix of similarities between neighbors; implements weighted tanimoto kernel
+ if neighbor_matches.size == 0
+ raise "No neighbors found."
+ else
+ # gram matrix
+ (0..(neighbor_matches.length-1)).each do |i|
+ gram_matrix[i] = [] unless gram_matrix[i]
+ # upper triangle
+ ((i+1)..(neighbor_matches.length-1)).each do |j|
+ sim = eval("#{params[:similarity_algorithm]}(neighbor_matches[i], neighbor_matches[j], params[:p_values])")
+ gram_matrix[i][j] = Algorithm.gauss(sim)
+ gram_matrix[j] = [] unless gram_matrix[j]
+ gram_matrix[j][i] = gram_matrix[i][j] # lower triangle
+ end
+ gram_matrix[i][i] = 1.0
+ end
+
+ #LOGGER.debug gram_matrix.to_yaml
+ @r = RinRuby.new(false,false) # global R instance leads to Socket errors after a large number of requests
+ @r.eval "library('kernlab')" # this requires R package "kernlab" to be installed
+ LOGGER.debug "Setting R data ..."
+ # set data
+ @r.gram_matrix = gram_matrix.flatten
+ @r.n = neighbor_matches.size
+ @r.y = acts
+ @r.sims = sims
+
+ begin
+ LOGGER.debug "Preparing R data ..."
+ # prepare data
+ @r.eval "y<-as.vector(y)"
+ @r.eval "gram_matrix<-as.kernelMatrix(matrix(gram_matrix,n,n))"
+ @r.eval "sims<-as.vector(sims)"
+
+ # model + support vectors
+ LOGGER.debug "Creating SVM model ..."
+ @r.eval "model<-ksvm(gram_matrix, y, kernel=matrix, type=\"#{type}\", nu=0.5)"
+ @r.eval "sv<-as.vector(SVindex(model))"
+ @r.eval "sims<-sims[sv]"
+ @r.eval "sims<-as.kernelMatrix(matrix(sims,1))"
+ LOGGER.debug "Predicting ..."
+ if type == "nu-svr"
+ @r.eval "p<-predict(model,sims)[1,1]"
+ elsif type == "C-bsvc"
+ @r.eval "p<-predict(model,sims)"
+ end
+ if type == "nu-svr"
+ prediction = @r.p
+ elsif type == "C-bsvc"
+ prediction = (@r.p.to_f == 1.0 ? true : false)
+ end
+ @r.quit # free R
+ rescue Exception => e
+ LOGGER.debug "#{e.class}: #{e.message} #{e.backtrace}"
+ end
+
+ end
+ prediction
+ end
+
end
module Substructure
diff --git a/lib/authorization.rb b/lib/authorization.rb
index eab20df..288733a 100644
--- a/lib/authorization.rb
+++ b/lib/authorization.rb
@@ -137,16 +137,23 @@ module OpenTox
# Lists policies alongside with affected uris
# @param [String] subjectid
# @return [Hash] keys: all policies of the subjectid owner, values: uris affected by those policies
- def self.list_policy_uris( subjectid )
+ def self.list_policies_uris( subjectid )
names = list_policies(subjectid)
policies = {}
names.each do |n|
- p = OpenTox::Policies.new
- p.load_xml( list_policy(n, subjectid) )
- policies[n] = p.uris
+ policies[n] = list_policy_uris( n, subjectid )
end
policies
end
+
+ # Lists policies alongside with affected uris
+ # @param [String] subjectid
+ # @return [Hash] keys: all policies of the subjectid owner, values: uris affected by those policies
+ def self.list_policy_uris( policy, subjectid )
+ p = OpenTox::Policies.new
+ p.load_xml( list_policy(policy, subjectid) )
+ p.uris
+ end
#Returns the owner (who created the first policy) of an URI
# @param [String, String]uri,subjectid
@@ -220,7 +227,9 @@ module OpenTox
begin
resource = RestClient::Resource.new("#{AA_SERVER}/opensso/identity/search")
grps = resource.post(:admin => subjectid, :attributes_names => "objecttype", :attributes_values_objecttype => "group")
- grps.split("\n").collect{|x| x.sub("string=","")}
+ grps = grps.split("\n").collect{|x| x.sub("string=","")}
+ grps.delete_if{|g|g=="MemberManagement"||g=="Webmasters"}
+ grps
rescue
[]
end
@@ -279,10 +288,12 @@ module OpenTox
# @return [Boolean]
def self.delete_policies_from_uri(uri, subjectid)
policies = list_uri_policies(uri, subjectid)
- policies.each do |policy|
- ret = delete_policy(policy, subjectid)
- LOGGER.debug "OpenTox::Authorization delete policy: #{policy} - with result: #{ret}"
- end
+ if policies
+ policies.each do |policy|
+ ret = delete_policy(policy, subjectid)
+ LOGGER.debug "OpenTox::Authorization delete policy: #{policy} - with result: #{ret}"
+ end
+ end
return true
end
diff --git a/lib/dataset.rb b/lib/dataset.rb
index 546eb2e..4005c1c 100644
--- a/lib/dataset.rb
+++ b/lib/dataset.rb
@@ -167,24 +167,33 @@ module OpenTox
@features
end
+ def feature_classes(feature, subjectid=nil)
+ if Feature.find(feature, subjectid).feature_type == "classification"
+ classes = []
+ @data_entries.each do |c,e|
+ e[feature].each { |v| classes << v.to_s }
+ end
+ classes.uniq.sort
+ else
+ nil
+ end
+ end
+
+=begin
# Detect feature type(s) in the dataset
# @return [String] `classification", "regression", "mixed" or unknown`
def feature_type(subjectid=nil)
load_features(subjectid)
- feature_types = @features.collect{|f,metadata| metadata[OT.isA]}.uniq
- if feature_types.size > 1
- "mixed"
+ feature_types = @features.collect{|f,metadata| metadata[RDF.type]}.flatten.uniq
+ if feature_types.include?(OT.NominalFeature)
+ "classification"
+ elsif feature_types.include?(OT.NumericFeature)
+ "regression"
else
- case feature_types.first
- when /NominalFeature/
- "classification"
- when /NumericFeature/
- "regression"
- else
- "unknown"
- end
+ "unknown"
end
end
+=end
# Get Spreadsheet representation
# @return [Spreadsheet::Workbook] Workbook which can be written with the spreadsheet gem (data_entries only, metadata will will be discarded))
@@ -288,7 +297,7 @@ module OpenTox
else
compounds.each do |c|
features.each do |f|
- unless @data_entries[c][f]
+ if @data_entries[c]==nil or @data_entries[c][f]==nil
dataset.add(c,f,nil)
else
@data_entries[c][f].each do |v|
diff --git a/lib/environment.rb b/lib/environment.rb
index 59578c1..ffc4f60 100644
--- a/lib/environment.rb
+++ b/lib/environment.rb
@@ -23,7 +23,7 @@ else
end
# database
-`redis-server /opt/redis/redis.conf` unless File.exists? "/var/run/redis.pid"
+#`redis-server /opt/redis/redis.conf` unless File.exists? "/var/run/redis.pid" # removed by AM
Ohm.connect :thread_safe => true
# load mail settings for error messages
diff --git a/lib/error.rb b/lib/error.rb
index 7ca9767..b92f2a4 100644
--- a/lib/error.rb
+++ b/lib/error.rb
@@ -69,7 +69,7 @@ module OpenTox
def rdf_content()
c = {
- RDF.type => OT.ErrorReport,
+ RDF.type => [OT.ErrorReport],
OT.statusCode => @http_code,
OT.message => @message,
OT.actor => @actor,
@@ -96,4 +96,4 @@ class Array
end
short.join("\n")
end
-end \ No newline at end of file
+end
diff --git a/lib/feature.rb b/lib/feature.rb
index e768f7b..2f1ab6c 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -16,23 +16,20 @@ module OpenTox
feature
end
- # provides domain (possible target values) of classification feature
- # @return [Array] list with possible target values
- def domain
- if metadata[OT.acceptValue]
- raise "accept value found, remove hack and implement correctly"
- else
- if @uri=~/feature\/26221/ || @uri=~/feature\/221726/
- return ["mutagen" , "nonmutagen"]
- end
- return [true, false]
- end
- end
-
+
# provides feature type, possible types are "regression" or "classification"
# @return [String] feature type, unknown if OT.isA property is unknown/ not set
def feature_type
- case metadata[OT.isA]
+ if metadata[RDF.type].flatten.include?(OT.NominalFeature)
+ "classification"
+ elsif metadata[RDF.type].flatten.include?(OT.NumericFeature)
+ "regression"
+ else
+ #"unknown"
+ metadata[RDF.type].inspect
+ end
+=begin
+ case metadata[RDF.type]
when /NominalFeature/
"classification"
when /NumericFeature/
@@ -40,6 +37,7 @@ module OpenTox
else
"unknown"
end
+=end
end
end
diff --git a/lib/helper.rb b/lib/helper.rb
index 7009082..3a6126a 100644
--- a/lib/helper.rb
+++ b/lib/helper.rb
@@ -44,21 +44,12 @@ helpers do
def uri_available?(urlStr)
url = URI.parse(urlStr)
- unless @subjectid
- Net::HTTP.start(url.host, url.port) do |http|
- return http.head(url.request_uri).code == "200"
- end
- else
- Net::HTTP.start(url.host, url.port) do |http|
- return http.post(url.request_uri, "subjectid=#{@subjectid}").code == "202"
- end
+ Net::HTTP.start(url.host, url.port) do |http|
+ return http.head("#{url.request_uri}?subjectid=#{CGI.escape @subjectid}").code == "200"
end
end
-end
-
-before do
- unless !AA_SERVER or login_requests or CONFIG[:authorization][:free_request].include?(env['REQUEST_METHOD'])
+ def get_subjectid
begin
subjectid = nil
subjectid = session[:subjectid] if session[:subjectid]
@@ -69,31 +60,37 @@ before do
subjectid = CGI.unescape(subjectid) if subjectid.include?("%23")
@subjectid = subjectid
rescue
- #LOGGER.debug "OpenTox ruby api wrapper: helper before filter: NO subjectid for URI: #{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}"
- subjectid = ""
+ subjectid = nil
end
- @subjectid = subjectid
- protected!(subjectid)
-
- extension = File.extname(request.path_info) # params[:id] is not yet available
+ end
+ def get_extension
+ extension = File.extname(request.path_info)
unless extension.empty?
- #request.path_info.sub!(/\.#{extension}$/,'')
- case extension
- when "html"
- @accept = 'text/html'
- when "yaml"
- @accept = 'application/x-yaml'
- when "csv"
- @accept = 'text/csv'
- when "rdfxml"
- @accept = 'application/rdf+xml'
- when "xls"
- @accept = 'application/ms-excel'
- else
- #halt 404, "File format #{extension} not supported."
- end
- end
-
+ case extension.gsub(".","")
+ when "html"
+ @accept = 'text/html'
+ when "yaml"
+ @accept = 'application/x-yaml'
+ when "csv"
+ @accept = 'text/csv'
+ when "rdfxml"
+ @accept = 'application/rdf+xml'
+ when "xls"
+ @accept = 'application/ms-excel'
+ when "css"
+ @accept = 'text/css'
+ else
+ # halt 404, "File format #{extension} not supported."
+ end
+ end
+ end
+end
+
+before do
+ @subjectid = get_subjectid()
+ @accept = get_extension()
+ unless !AA_SERVER or login_requests or CONFIG[:authorization][:free_request].include?(env['REQUEST_METHOD'])
+ protected!(@subjectid)
end
end
diff --git a/lib/model.rb b/lib/model.rb
index 74408d8..998d2dc 100644
--- a/lib/model.rb
+++ b/lib/model.rb
@@ -23,7 +23,7 @@ module OpenTox
# Generic OpenTox model class for all API compliant services
class Generic
include Model
-
+
# Find Generic Opentox Model via URI, and loads metadata, could raise NotFound/NotAuthorized error
# @param [String] uri Model URI
# @return [OpenTox::Model::Generic] Model instance
@@ -34,21 +34,20 @@ module OpenTox
raise "could not load model metadata '"+uri.to_s+"'" if model.metadata==nil or model.metadata.size==0
model
end
-
- # provides feature type, possible types are "regression" or "classification"
- # @return [String] feature type, "unknown" if type could not be estimated
+
+ # provides feature type, possible types are "regression" or "classification"
+ # @return [String] feature type, "unknown" if type could not be estimated
def feature_type(subjectid=nil)
return @feature_type if @feature_type
-
+
# dynamically perform restcalls if necessary
load_metadata(subjectid) if @metadata==nil or @metadata.size==0 or (@metadata.size==1 && @metadata.values[0]==@uri)
algorithm = OpenTox::Algorithm::Generic.find(@metadata[OT.algorithm], subjectid)
algorithm_title = algorithm ? algorithm.metadata[DC.title] : nil
- algorithm_type = algorithm ? algorithm.metadata[OT.isA] : nil
+ algorithm_type = algorithm ? algorithm.metadata[RDF.type] : nil
dependent_variable = OpenTox::Feature.find( @metadata[OT.dependentVariables],subjectid )
dependent_variable_type = dependent_variable ? dependent_variable.feature_type : nil
- type_indicators = [dependent_variable_type, @metadata[OT.isA], @metadata[DC.title],
- @uri, algorithm_type, algorithm_title]
+ type_indicators = [dependent_variable_type, @metadata[RDF.type], @metadata[DC.title], @uri, algorithm_type, algorithm_title].flatten
type_indicators.each do |type|
case type
when /(?i)classification/
@@ -61,9 +60,9 @@ module OpenTox
raise "unknown model "+type_indicators.inspect unless @feature_type
@feature_type
end
-
+
end
-
+
# Lazy Structure Activity Relationship class
class Lazar
@@ -79,7 +78,7 @@ module OpenTox
else
super CONFIG[:services]["opentox-model"]
end
-
+
@metadata[OT.algorithm] = File.join(CONFIG[:services]["opentox-algorithm"],"lazar")
@features = []
@@ -113,9 +112,10 @@ module OpenTox
# @param [optional,Hash] params Parameters for the lazar algorithm (OpenTox::Algorithm::Lazar)
# @return [OpenTox::Model::Lazar] lazar model
def self.create(params)
+ subjectid = params[:subjectid]
lazar_algorithm = OpenTox::Algorithm::Generic.new File.join( CONFIG[:services]["opentox-algorithm"],"lazar")
model_uri = lazar_algorithm.run(params)
- OpenTox::Model::Lazar.find(model_uri, params[:subjectid])
+ OpenTox::Model::Lazar.find(model_uri, subjectid)
end
# Get a parameter value
@@ -178,16 +178,72 @@ module OpenTox
return @prediction_dataset if database_activity(subjectid)
- neighbors
- prediction = eval("#{@prediction_algorithm}(@neighbors,{:similarity_algorithm => @similarity_algorithm, :p_values => @p_values})")
+ if metadata[RDF.type] == [OTA.ClassificationLazySingleTarget]
+ # AM: Balancing, see http://www.maunz.de/wordpress/opentox/2011/balanced-lazar
+ l = Array.new # larger
+ s = Array.new # smaller fraction
+ @fingerprints.each do |training_compound,training_features|
+ @activities[training_compound].each do |act|
+ case act.to_s
+ when "false"
+ l << training_compound
+ when "true"
+ s << training_compound
+ else
+ LOGGER.warn "BLAZAR: Activity #{act.to_s} should not be reached."
+ end
+ end
+ end
+ if s.size > l.size then
+ l,s = s,l # happy swapping
+ LOGGER.info "BLAZAR: |s|=#{s.size}, |l|=#{l.size}."
+ end
+ # determine ratio
+ modulo = l.size.divmod(s.size)# modulo[0]=ratio, modulo[1]=rest
+ LOGGER.info "BLAZAR: Balance: #{modulo[0]}, rest #{modulo[1]}."
+
+ # AM: Balanced predictions
+ addon = (modulo[1].to_f/modulo[0]).ceil # what will be added in each round
+ slack = modulo[1].divmod(addon)[1] # what remains for the last round
+ position = 0
+ predictions = Array.new
+
+ prediction_best=nil
+ neighbors_best=nil
+
+ begin
+ for i in 1..modulo[0] do
+ (i == modulo[0]) && (slack>0) ? lr_size = s.size + slack : lr_size = s.size + addon # determine fraction
+ LOGGER.info "BLAZAR: Neighbors round #{i}: #{position} + #{lr_size}."
+ neighbors_balanced(s, l, position, lr_size) # get ratio fraction of larger part
+ prediction = eval("#{@prediction_algorithm}(@neighbors,{:similarity_algorithm => @similarity_algorithm, :p_values => @p_values})")
+ if prediction_best.nil? || prediction[:confidence].abs > prediction_best[:confidence].abs
+ prediction_best=prediction
+ neighbors_best=@neighbors
+ end
+ position = position + lr_size
+ end
+ rescue Exception => e
+ LOGGER.error "BLAZAR failed in prediction: "+e.class.to_s+": "+e.message
+ end
+
+ prediction=prediction_best
+ @neighbors=neighbors_best
+ ### END AM balanced predictions
+
+ else # regression case: no balancing
+ neighbors
+ prediction = eval("#{@prediction_algorithm}(@neighbors,{:similarity_algorithm => @similarity_algorithm, :p_values => @p_values})")
+ end
+
prediction_feature_uri = File.join( @prediction_dataset.uri, "feature", "prediction", File.basename(@metadata[OT.dependentVariables]),@prediction_dataset.compounds.size.to_s)
# TODO: fix dependentVariable
@prediction_dataset.metadata[OT.dependentVariables] = prediction_feature_uri
if @neighbors.size == 0
@prediction_dataset.add_feature(prediction_feature_uri, {
- OT.isA => OT.MeasuredFeature,
+ RDF.type => [OT.MeasuredFeature],
OT.hasSource => @uri,
DC.creator => @uri,
DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
@@ -198,7 +254,7 @@ module OpenTox
else
@prediction_dataset.add_feature(prediction_feature_uri, {
- OT.isA => OT.ModelPrediction,
+ RDF.type => [OT.ModelPrediction],
OT.hasSource => @uri,
DC.creator => @uri,
DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
@@ -215,7 +271,7 @@ module OpenTox
feature_uri = File.join( @prediction_dataset.uri, "feature", "descriptor", f.to_s)
features[feature] = feature_uri
@prediction_dataset.add_feature(feature_uri, {
- OT.isA => OT.Substructure,
+ RDF.type => [OT.Substructure],
OT.smarts => feature,
OT.pValue => @p_values[feature],
OT.effect => @effects[feature]
@@ -236,7 +292,7 @@ module OpenTox
OT.compound => neighbor[:compound],
OT.similarity => neighbor[:similarity],
OT.measuredActivity => neighbor[:activity],
- OT.isA => OT.Neighbor
+ RDF.type => [OT.Neighbor]
})
@prediction_dataset.add @compound.uri, neighbor_uri, true
f = 0 unless f
@@ -250,7 +306,7 @@ module OpenTox
unless features.has_key? feature
features[feature] = feature_uri
@prediction_dataset.add_feature(feature_uri, {
- OT.isA => OT.Substructure,
+ RDF.type => [OT.Substructure],
OT.smarts => feature,
OT.pValue => @p_values[feature],
OT.effect => @effects[feature]
@@ -269,27 +325,55 @@ module OpenTox
end
# Find neighbors and store them as object variable
- def neighbors
-
+ def neighbors_balanced(s, l, start, offset)
@compound_features = eval("#{@feature_calculation_algorithm}(@compound,@features)") if @feature_calculation_algorithm
@neighbors = []
- @fingerprints.each do |training_compound,training_features|
- sim = eval("#{@similarity_algorithm}(@compound_features,training_features,@p_values)")
- if sim > @min_sim
- @activities[training_compound].each do |act|
- @neighbors << {
- :compound => training_compound,
- :similarity => sim,
- :features => training_features,
- :activity => act
- }
+ begin
+ #@fingerprints.each do |training_compound,training_features| # AM: this is original by CH
+ [ l[start, offset ] , s ].flatten.each do |training_compound| # AM: access only a balanced subset
+ training_features = @fingerprints[training_compound]
+ sim = eval("#{@similarity_algorithm}(@compound_features,training_features,@p_values)")
+ if sim > @min_sim
+ @activities[training_compound].each do |act|
+ this_neighbor = {
+ :compound => training_compound,
+ :similarity => sim,
+ :features => training_features,
+ :activity => act
+ }
+ @neighbors << this_neighbor
+ end
end
end
+ rescue Exception => e
+ LOGGER.error "BLAZAR failed in neighbors: "+e.class.to_s+": "+e.message
end
end
+
+ # Find neighbors and store them as object variable
+ def neighbors
+
+ @compound_features = eval("#{@feature_calculation_algorithm}(@compound,@features)") if @feature_calculation_algorithm
+
+ @neighbors = []
+ @fingerprints.each do |training_compound,training_features|
+ sim = eval("#{@similarity_algorithm}(@compound_features,training_features,@p_values)")
+ if sim > @min_sim
+ @activities[training_compound].each do |act|
+ @neighbors << {
+ :compound => training_compound,
+ :similarity => sim,
+ :features => training_features,
+ :activity => act
+ }
+ end
+ end
+ end
+ end
+
# Find database activities and store them in @prediction_dataset
# @return [Boolean] true if compound has databasse activities, false if not
def database_activity(subjectid)
diff --git a/lib/ontology_service.rb b/lib/ontology.rb
index 4ff688f..fa4ea6f 100644
--- a/lib/ontology_service.rb
+++ b/lib/ontology.rb
@@ -1,6 +1,7 @@
module OpenTox
- module OntologyService
- module Endpoints
+ module Ontology
+ module Echa
+=begin
require 'sparql/client'
@sparql = SPARQL::Client.new("http://apps.ideaconsult.net:8080/ontology")
def self.qs(classname="Endpoints")
@@ -12,11 +13,11 @@ module OpenTox
PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX otee:<http://www.opentox.org/echaEndpoints.owl#>
PREFIX toxcast:<http://www.opentox.org/toxcast.owl#>
- select ?Endpoints ?title ?id
- where {?Endpoints rdfs:subClassOf otee:#{classname}.
- OPTIONAL {?Endpoints dc:title ?title}.
- OPTIONAL {?Endpoints dc:identifier ?id}.}
- ORDER BY ?title"
+ select *
+ where {
+ ?endpoint rdfs:subClassOf otee:#{classname}.
+ ?endpoint dc:title ?title.
+ }"
end
def self.make_option_list(endpoint="Endpoints", level=1)
@@ -38,6 +39,17 @@ module OpenTox
out += "</select>\n"
return out
end
+=end
+
+ def self.endpoints
+ RestClientWrapper.get("http://apps.ideaconsult.net:8080/ambit2/query/ndatasets_endpoint",:accept => "text/csv").collect { |line| line.split(',').first if line.match(/^http/) }.compact
+ end
+
+ def self.datasets(endpoint)
+ RestClientWrapper.get("http://apps.ideaconsult.net:8080/ambit2/dataset?feature_sameas=#{URI.encode endpoint}", :accept => "text/uri-list").split("\n")
+ end
+
end
+
end
-end \ No newline at end of file
+end
diff --git a/lib/opentox-ruby.rb b/lib/opentox-ruby.rb
index ab8d824..ae05cb2 100644
--- a/lib/opentox-ruby.rb
+++ b/lib/opentox-ruby.rb
@@ -9,6 +9,6 @@ rescue LoadError
end
['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature',
- 'rest_client_wrapper', 'authorization', 'policy', 'helper', 'to-html' ].each do |lib|
+ 'rest_client_wrapper', 'authorization', 'policy', 'helper', 'to-html', 'ontology' ].each do |lib|
require lib
end
diff --git a/lib/opentox.rb b/lib/opentox.rb
index 1992896..c76e21a 100644
--- a/lib/opentox.rb
+++ b/lib/opentox.rb
@@ -31,7 +31,14 @@ module OpenTox
end
def add_metadata(metadata)
- metadata.each { |k,v| @metadata[k] = v }
+ metadata.each do |k,v|
+ if v.is_a? Array
+ @metadata[k] = [] unless @metadata[k]
+ @metadata[k] << v
+ else
+ @metadata[k] = v
+ end
+ end
end
# Get OWL-DL representation in RDF/XML format
diff --git a/lib/overwrite.rb b/lib/overwrite.rb
index fbe775d..df4e1b7 100644
--- a/lib/overwrite.rb
+++ b/lib/overwrite.rb
@@ -8,6 +8,7 @@ before {
$url_provider = self
# stupid internet explorer does not ask for text/html, add this manually
request.env['HTTP_ACCEPT'] += ";text/html" if request.env["HTTP_USER_AGENT"]=~/MSIE/
+ request.env['HTTP_ACCEPT']=request.params["media"] if request.params["media"]
}
# Error handling
@@ -39,20 +40,24 @@ end
class Sinatra::Base
def return_task( task )
- code = task.running? ? 202 : 200
+ raise "http_code == nil" unless task.http_code!=nil
case request.env['HTTP_ACCEPT']
when /rdf/
response['Content-Type'] = "application/rdf+xml"
- halt code,task.to_rdfxml
+ halt task.http_code,task.to_rdfxml
when /yaml/
response['Content-Type'] = "application/x-yaml"
- halt code,task.to_yaml # PENDING differs from task-webservice
+ halt task.http_code,task.to_yaml # PENDING differs from task-webservice
when /html/
response['Content-Type'] = "text/html"
- halt code,OpenTox.text_to_html(task.to_yaml, @subjectid)
+ halt task.http_code,OpenTox.text_to_html(task.to_yaml, @subjectid)
else # default /uri-list/
response['Content-Type'] = "text/uri-list"
- halt code,task.uri+"\n"
+ if task.completed?
+ halt task.http_code,task.resultURI+"\n"
+ else
+ halt task.http_code,task.uri+"\n"
+ end
end
end
end
diff --git a/lib/parser.rb b/lib/parser.rb
index f33017d..7bdee95 100644
--- a/lib/parser.rb
+++ b/lib/parser.rb
@@ -55,7 +55,14 @@ module OpenTox
parameter_ids = []
`rapper -i rdfxml -o ntriples #{file.path} 2>/dev/null`.each_line do |line|
triple = line.to_triple
- @metadata[triple[1]] = triple[2].split('^^').first if triple[0] == @uri and triple[1] != RDF['type']
+ if triple[0] == @uri
+ if triple[1] == RDF.type # allow multiple types
+ @metadata[triple[1]] = [] unless @metadata[triple[1]]
+ @metadata[triple[1]] << triple[2].split('^^').first
+ else
+ @metadata[triple[1]] = triple[2].split('^^').first
+ end
+ end
statements << triple
parameter_ids << triple[2] if triple[1] == OT.parameters
end
@@ -156,6 +163,7 @@ module OpenTox
data = {}
feature_values = {}
feature = {}
+ feature_accept_values = {}
other_statements = {}
`rapper -i rdfxml -o ntriples #{file.path} 2>/dev/null`.each_line do |line|
triple = line.chomp.split(' ',3)
@@ -175,6 +183,9 @@ module OpenTox
if triple[2]=~/#{OT.Compound}/i and !data[triple[0]]
data[triple[0]] = {:compound => triple[0], :values => []}
end
+ when /#{OT.acceptValue}/i # acceptValue in ambit datasets is only provided in dataset/<id> no in dataset/<id>/features
+ feature_accept_values[triple[0]] = [] unless feature_accept_values[triple[0]]
+ feature_accept_values[triple[0]] << triple[2]
else
end
end
@@ -185,20 +196,25 @@ module OpenTox
@dataset.add_compound(entry[:compound])
else
entry[:values].each do |value_id|
- split = feature_values[value_id].split(/\^\^/)
- case split[-1]
- when XSD.double, XSD.float
- value = split.first.to_f
- when XSD.boolean
- value = split.first=~/(?i)true/ ? true : false
- else
- value = split.first
+ if feature_values[value_id]
+ split = feature_values[value_id].split(/\^\^/)
+ case split[-1]
+ when XSD.double, XSD.float
+ value = split.first.to_f
+ when XSD.boolean
+ value = split.first=~/(?i)true/ ? true : false
+ else
+ value = split.first
+ end
end
@dataset.add entry[:compound],feature[value_id],value
end
end
end
load_features subjectid
+ feature_accept_values.each do |feature, values|
+ @dataset.features[feature][OT.acceptValue] = values
+ end
@dataset.metadata = load_metadata(subjectid)
@dataset
end
@@ -223,7 +239,7 @@ module OpenTox
`rapper -i rdfxml -o ntriples #{file.path} 2>/dev/null`.each_line do |line|
triple = line.chomp.split('> ').collect{|i| i.sub(/\s+.$/,'').gsub(/[<>"]/,'')}[0..2]
statements << triple
- features << triple[0] if triple[1] == RDF['type'] and (triple[2] == OT.Feature || triple[2] == OT.NumericFeature)
+ features << triple[0] if triple[1] == RDF.type and (triple[2] =~ /Feature|Substructure/)
end
File.delete(to_delete) if to_delete
statements.each do |triple|
@@ -289,7 +305,7 @@ module OpenTox
else
type = types.first
end
- @dataset.add_feature_metadata(feature,{OT.isA => type})
+ @dataset.add_feature_metadata(feature,{RDF.type => [type]})
info += "\"#{@dataset.feature_name(feature)}\" detected as #{type.split('#').last}."
# TODO: rewrite feature values
@@ -341,16 +357,23 @@ module OpenTox
when OT.NominalFeature
case value.to_s
when TRUE_REGEXP
- @dataset.add(compound.uri, feature, true )
+ val = true
when FALSE_REGEXP
- @dataset.add(compound.uri, feature, false )
+ val = false
end
when OT.NumericFeature
- @dataset.add compound.uri, feature, value.to_f
+ val = value.to_f
when OT.StringFeature
- @dataset.add compound.uri, feature, value.to_s
+ val = value.to_s
@activity_errors << smiles+", "+row.join(", ")
end
+ if val!=nil
+ @dataset.add(compound.uri, feature, val)
+ if type!=OT.NumericFeature
+ @dataset.features[feature][OT.acceptValue] = [] unless @dataset.features[feature][OT.acceptValue]
+ @dataset.features[feature][OT.acceptValue] << val.to_s unless @dataset.features[feature][OT.acceptValue].include?(val.to_s)
+ end
+ end
end
end
diff --git a/lib/rest_client_wrapper.rb b/lib/rest_client_wrapper.rb
index dac24dc..747a353 100644
--- a/lib/rest_client_wrapper.rb
+++ b/lib/rest_client_wrapper.rb
@@ -98,6 +98,8 @@ module OpenTox
rescue RestClient::RequestTimeout => ex
received_error ex.message, 408, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
+ rescue Errno::ETIMEDOUT => ex
+ received_error ex.message, 408, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
rescue Errno::ECONNREFUSED => ex
received_error ex.message, 500, nil, {:rest_uri => uri, :headers => headers, :payload => payload}
rescue RestClient::ExceptionWithResponse => ex
diff --git a/lib/serializer.rb b/lib/serializer.rb
index 44b4414..e4cb541 100644
--- a/lib/serializer.rb
+++ b/lib/serializer.rb
@@ -26,6 +26,8 @@ module OpenTox
OT.Algorithm => { RDF["type"] => [{ "type" => "uri", "value" => OWL['Class'] }] } ,
OT.Parameter => { RDF["type"] => [{ "type" => "uri", "value" => OWL['Class'] }] } ,
OT.Task => { RDF["type"] => [{ "type" => "uri", "value" => OWL['Class'] }] } ,
+ OTA.PatternMiningSupervised => { RDF["type"] => [{ "type" => "uri", "value" => OWL['Class'] }] } ,
+
#classes for validation
OT.Validation => { RDF["type"] => [{ "type" => "uri", "value" => OWL['Class'] }] } ,
OT.ClassificationStatistics => { RDF["type"] => [{ "type" => "uri", "value" => OWL['Class'] }] } ,
@@ -40,10 +42,10 @@ module OpenTox
OT.compound => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
OT.feature => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
OT.dataEntry => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
- OT.acceptValue => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
OT.values => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
OT.algorithm => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
OT.parameters => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
+
#object props for validation#
OT.model => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
OT.trainingDataset => { RDF["type"] => [{ "type" => "uri", "value" => OWL.ObjectProperty }] } ,
@@ -67,12 +69,14 @@ module OpenTox
DC.creator => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
DC.description => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
DC.date => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
- OT.isA => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
+ #OT.isA => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
OT.Warnings => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
XSD.anyURI => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
OT.hasStatus => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
OT.resultURI => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
OT.percentageCompleted => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
+ OT.acceptValue => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
+
# annotation props for validation
OT.numUnpredicted => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
OT.crossvalidationFold => { RDF["type"] => [{ "type" => "uri", "value" => OWL.AnnotationProperty }] } ,
@@ -256,7 +260,8 @@ module OpenTox
def add_metadata(uri,metadata)
id = 0
metadata.each do |u,v|
- if v.is_a? Array and u == OT.parameters
+ #if v.is_a? Array and (u == OT.parameters or u == RDF.type)
+ if v.is_a? Array and u == OT.parameters#or u == RDF.type)
@object[uri][u] = [] unless @object[uri][u]
v.each do |value|
id+=1
@@ -267,7 +272,13 @@ module OpenTox
@object[genid][name] = [{"type" => type(entry), "value" => entry }]
end
end
- else # v.is_a? String
+ elsif v.is_a? Array and u == RDF.type
+ @object[uri] = {} unless @object[uri]
+ v.each do |value|
+ @object[uri][u] = [] unless @object[uri][u]
+ @object[uri][u] << {"type" => type(value), "value" => value }
+ end
+ elsif v.is_a? String
@object[uri] = {} unless @object[uri]
@object[uri][u] = [{"type" => type(v), "value" => v }]
end
@@ -309,6 +320,7 @@ module OpenTox
OT.value => v
}
@object[feature][RDF["type"]] << { "type" => "uri", "value" => featuretype(value) }
+ #@object[feature][RDF["type"]] = { "type" => "uri", "value" => featuretype(value) }
end
# Serializers
diff --git a/lib/task.rb b/lib/task.rb
index 0ee3a11..42d3d17 100644
--- a/lib/task.rb
+++ b/lib/task.rb
@@ -8,6 +8,7 @@ module OpenTox
def initialize(uri=nil)
super uri
+ @http_code = 202
@metadata = {
DC.title => "",
DC.date => "",
@@ -37,6 +38,7 @@ module OpenTox
task = Task.new(task_uri.chomp)
# measure current memory consumption
+=begin
memory = `free -m|sed -n '2p'`.split
free_memory = memory[3].to_i + memory[6].to_i # include cache
if free_memory < 20 # require at least 200 M free memory
@@ -55,6 +57,7 @@ module OpenTox
# return task
# #raise "Server too busy to start a new task"
#end
+=end
task_pid = Spork.spork(:logger => LOGGER) do
LOGGER.debug "Task #{task.uri} started #{Time.now}"
@@ -154,6 +157,7 @@ module OpenTox
# not stored just for to_rdf
def add_error_report( error_report )
+ raise "not an error report: "+error_report.class.to_s unless error_report.is_a?(ErrorReport)
@error_report = error_report
end
diff --git a/lib/templates/config.yaml b/lib/templates/config.yaml
deleted file mode 100644
index 8a5e460..0000000
--- a/lib/templates/config.yaml
+++ /dev/null
@@ -1,86 +0,0 @@
-# Example configuration for OpenTox, please adjust to your settings
-#
-# Database setup:
-#
-# Example MySql:
-#
-:database:
- :adapter: mysql
- :database: production
- :username: root
- :password: opentox
- :host: localhost
-#
-# Example 1: Using external test services
-#
-# :services:
-# opentox-compound: "http://webservices.in-silico.ch/compound/"
-# opentox-dataset: "http://webservices.in-silico.ch/dataset/"
-# opentox-algorithm: "http://webservices.in-silico.ch/algorithm/"
-# opentox-model: "http://webservices.in-silico.ch/model/"
-# opentox-task: "http://webservices.in-silico.ch/task/"
-# opentox-validation: "http://opentox.informatik.uni-freiburg.de/validation/"
-#
-# Example 2: Using local services
-:base_dir: /home/ist/webservices
-:webserver: passenger
-:services:
- opentox-compound: "http://localhost/compound/"
- opentox-dataset: "http://localhost/dataset/"
- opentox-algorithm: "http://localhost/algorithm/"
- opentox-model: "http://localhost/model/"
- opentox-task: "http://localhost/task/"
- opentox-validation: "http://localhost/validation/"
-#
-# Yaml capable hosts (faster than OWL-DL)
-#
-:yaml_hosts:
- - "localhost"
-
-# Uncomment for verbose logging
-# :logger: debug
-# :backtrace: 1
-
-
-# OpenSSO Authorization
-# set ":server: " to disable A&A
-:authorization:
- :server: "https://opensso.in-silico.ch"
- :free_request: #request-method not controlled by A&A
- - "GET"
- :authenticate_request: #only for authenticated user
- - "POST"
- :authorize_request: #only for authenticated and authorizeduser
- - "DELETE"
- - "PUT"
- # Exceptions:
- :free_uris: #request-method for uri not controlled by A&A
- ? - :GET
- : - !ruby/regexp /localhost\/algorithm/
- - "http://localhost/dataset"
- - "http://localhost/model"
- - "http://localhost/validation"
- - "http://localhost/validation/crossvalidation"
- - "http://localhost/validation/reach_report"
- - "http://localhost/validation/reach_report/crossvalidation"
- - "http://localhost/validation/report"
- - "http://localhost/validation/report/crossvalidation"
- - "http://localhost/validation/reach_report/qmrf"
- ? - :GET
- - :POST
- : - !ruby/regexp /localhost\/toxcreate/
- - !ruby/regexp /localhost\/task/
- - !ruby/regexp /localhost\/compound/
- ? - :PUT
- : - !ruby/regexp /localhost\/task/
-
- :authorize_exceptions: #request-method for uri only authenticated, no authorization
- ? - :POST
- : - !ruby/regexp /localhost\/algorithm/
- - "http://localhost/dataset"
- - "http://localhost/model"
- - "http://localhost/validation"
- - !ruby/regexp /localhost\/validation\/[a-z,A-Z,\/,_\-]*$/
-
-
- \ No newline at end of file
diff --git a/lib/to-html.rb b/lib/to-html.rb
index 6785974..66a3e74 100644
--- a/lib/to-html.rb
+++ b/lib/to-html.rb
@@ -1,5 +1,5 @@
-OT_LOGO = "http://opentox.informatik.uni-freiburg.de/ot-logo.png"
+OT_LOGO = File.join(CONFIG[:services]["opentox-validation"],"resources/ot-logo.png")
class String
diff --git a/lib/validation.rb b/lib/validation.rb
index a47a554..d7a337c 100644
--- a/lib/validation.rb
+++ b/lib/validation.rb
@@ -12,6 +12,40 @@ module OpenTox
val
end
+ # returns a filtered list of validation uris
+ # @param [Hash,optional] params, validation-params to filter the uris (could be model, training_dataset, ..)
+ # @return [Array]
+ def self.list( params={} )
+ filter_string = ""
+ params.each do |k,v|
+ filter_string = "?" if filter_string.length==0
+ filter_string += k.to_s+"="+v
+ end
+ (OpenTox::RestClientWrapper.get(CONFIG[:services]["opentox-validation"]+filter_string).split("\n"))
+ end
+
+ # creates a training test split validation, waits until it finishes, may take some time
+ # @param [Hash] params (required:algorithm_uri,dataset_uri,prediction_feature, optional:algorithm_params,split_ratio(0.67),random_seed(1))
+ # @param [String,optional] subjectid
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
+ # @return [OpenTox::Validation]
+ def self.create_training_test_split( params, subjectid=nil, waiting_task=nil )
+ params[:subjectid] = subjectid if subjectid
+ uri = OpenTox::RestClientWrapper.post( File.join(CONFIG[:services]["opentox-validation"],"training_test_split"),
+ params,{:content_type => "text/uri-list"},waiting_task )
+ Validation.new(uri)
+ end
+
+ # looks for report for this validation, creates a report if no report is found
+ # @param [String,optional] subjectid
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
+ # @return [String] report uri
+ def find_or_create_report( subjectid=nil, waiting_task=nil )
+ @report = ValidationReport.find_for_validation(@uri, subjectid) unless @report
+ @report = ValidationReport.create(@uri, subjectid, waiting_task) unless @report
+ @report.uri
+ end
+
# creates a validation object from crossvaldiation statistics, raise error if not found
# (as crossvaldiation statistics are returned as an average valdidation over all folds)
# @param [String] crossvalidation uri
@@ -31,7 +65,7 @@ module OpenTox
def summary
if @metadata[OT.classificationStatistics]
res = {
- :nr_predictions => @metadata[OT.numInstances] - @metadata[OT.numUnpredicted],
+ :nr_predictions => @metadata[OT.numInstances].to_i - @metadata[OT.numUnpredicted].to_i,
:correct_predictions => @metadata[OT.classificationStatistics][OT.percentCorrect],
:weighted_area_under_roc => @metadata[OT.classificationStatistics][OT.weightedAreaUnderRoc],
}
@@ -42,14 +76,14 @@ module OpenTox
res[:true_negatives] = s[OT.numTrueNegatives]
res[:false_negatives] = s[OT.numFalseNegatives]
res[:sensitivity] = s[OT.truePositiveRate]
- res[:specificity] = s[OT.falsePositiveRate]
+ res[:specificity] = s[OT.trueNegativeRate]
break
end
end
res
elsif @metadata[OT.regressionStatistics]
{
- :nr_predictions => @metadata[OT.numInstances] - @metadata[OT.numUnpredicted],
+ :nr_predictions => @metadata[OT.numInstances].to_i - @metadata[OT.numUnpredicted].to_i,
:r_square => @metadata[OT.regressionStatistics][OT.rSquare],
:root_mean_squared_error => @metadata[OT.regressionStatistics][OT.rootMeanSquaredError],
:mean_absolute_error => @metadata[OT.regressionStatistics][OT.meanAbsoluteError],
@@ -72,6 +106,18 @@ module OpenTox
cv.load_metadata( subjectid )
cv
end
+
+ # returns a filtered list of crossvalidation uris
+ # @param [Hash,optional] params, crossvalidation-params to filter the uris (could be algorithm, dataset, ..)
+ # @return [Array]
+ def self.list( params={} )
+ filter_string = ""
+ params.each do |k,v|
+ filter_string = "?" if filter_string.length==0
+ filter_string += k.to_s+"="+v
+ end
+ (OpenTox::RestClientWrapper.get(File.join(CONFIG[:services]["opentox-validation"],"crossvalidation")+filter_string).split("\n"))
+ end
# creates a crossvalidations, waits until it finishes, may take some time
# @param [Hash] params (required:algorithm_uri,dataset_uri,prediction_feature, optional:algorithm_params,num_folds(10),random_seed(1),stratified(false))
@@ -110,6 +156,17 @@ module OpenTox
class ValidationReport
include OpenTox
+ # finds ValidationReport via uri, raises error if not found
+ # @param [String] uri
+ # @param [String,optional] subjectid
+ # @return [OpenTox::ValidationReport]
+ def self.find( uri, subjectid=nil )
+ OpenTox::RestClientWrapper.get(uri,{:subjectid => subjectid})
+ rep = ValidationReport.new(uri)
+ rep.load_metadata( subjectid )
+ rep
+ end
+
# finds ValidationReport for a particular validation
# @param [String] crossvalidation uri
# @param [String,optional] subjectid
@@ -120,6 +177,17 @@ module OpenTox
uris.size==0 ? nil : ValidationReport.new(uris[-1])
end
+ # creates a validation report via validation
+ # @param [String] validation uri
+ # @param [String,optional] subjectid
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
+ # @return [OpenTox::ValidationReport]
+ def self.create( validation_uri, subjectid=nil, waiting_task=nil )
+ uri = RestClientWrapper.post(File.join(CONFIG[:services]["opentox-validation"],"/report/validation"),
+ { :validation_uris => validation_uri, :subjectid => subjectid }, {}, waiting_task )
+ ValidationReport.new(uri)
+ end
+
end
class CrossvalidationReport
@@ -130,9 +198,10 @@ module OpenTox
# @param [String,optional] subjectid
# @return [OpenTox::CrossvalidationReport]
def self.find( uri, subjectid=nil )
- # PENDING load report data?
OpenTox::RestClientWrapper.get(uri,{:subjectid => subjectid})
- CrossvalidationReport.new(uri)
+ rep = CrossvalidationReport.new(uri)
+ rep.load_metadata( subjectid )
+ rep
end
# finds CrossvalidationReport for a particular crossvalidation
@@ -157,6 +226,54 @@ module OpenTox
end
end
+
+ class AlgorithmComparisonReport
+ include OpenTox
+
+ # finds AlgorithmComparisonReport via uri, raises error if not found
+ # @param [String] uri
+ # @param [String,optional] subjectid
+ # @return [OpenTox::CrossvalidationReport]
+ def self.find( uri, subjectid=nil )
+ OpenTox::RestClientWrapper.get(uri,{:subjectid => subjectid})
+ rep = AlgorithmComparisonReport.new(uri)
+ rep.load_metadata( subjectid )
+ rep
+ end
+
+ # finds AlgorithmComparisonReport for a particular crossvalidation
+ # @param [String] crossvalidation uri
+ # @param [String,optional] subjectid
+ # @return [OpenTox::AlgorithmComparisonReport] nil if no report found
+ def self.find_for_crossvalidation( crossvalidation_uri, subjectid=nil )
+ uris = RestClientWrapper.get(File.join(CONFIG[:services]["opentox-validation"],
+ "/report/algorithm_comparison?crossvalidation="+crossvalidation_uri), {:subjectid => subjectid}).chomp.split("\n")
+ uris.size==0 ? nil : AlgorithmComparisonReport.new(uris[-1])
+ end
+
+ # creates a crossvalidation report via crossvalidation
+ # @param [Hash] crossvalidation uri_hash, see example
+ # @param [String,optional] subjectid
+ # @param [OpenTox::Task,optional] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
+ # @return [OpenTox::AlgorithmComparisonReport]
+ # example for hash:
+ # { :lazar-bbrc => [ http://host/validation/crossvalidation/x1, http://host/validation/crossvalidation/x2 ],
+ # :lazar-last => [ http://host/validation/crossvalidation/xy, http://host/validation/crossvalidation/xy ] }
+ def self.create( crossvalidation_uri_hash, subjectid=nil, waiting_task=nil )
+ identifier = []
+ validation_uris = []
+ crossvalidation_uri_hash.each do |id, uris|
+ uris.each do |uri|
+ identifier << id
+ validation_uris << uri
+ end
+ end
+ uri = RestClientWrapper.post(File.join(CONFIG[:services]["opentox-validation"],"/report/algorithm_comparison"),
+ { :validation_uris => validation_uris.join(","), :identifier => identifier.join(","), :subjectid => subjectid }, {}, waiting_task )
+ AlgorithmComparisonReport.new(uri)
+ end
+ end
+
class QMRFReport
include OpenTox