summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorChristoph Helma <helma@in-silico.de>2009-09-08 16:04:23 +0200
committerChristoph Helma <helma@in-silico.de>2009-09-08 16:04:23 +0200
commit6a2c3f2100d030c30b7d8ac8c95dcece7edb040c (patch)
treec2e8069b3787b7ad2b90f062957d23eb12d955dd /lib
parent4d343f7c584ce09638005665b4ed2de718ff17a6 (diff)
api separated into individual components, adapted for new webservice versions
Diffstat (limited to 'lib')
-rw-r--r--lib/algorithm.rb35
-rw-r--r--lib/compound.rb67
-rw-r--r--lib/dataset.rb76
-rw-r--r--lib/environment.rb12
-rw-r--r--lib/feature.rb45
-rw-r--r--lib/model.rb51
-rw-r--r--lib/opentox-ruby-api-wrapper.rb174
-rw-r--r--lib/tasks/opentox.rb86
-rw-r--r--lib/templates/config.ru23
-rw-r--r--lib/templates/config.yaml12
-rw-r--r--lib/utils.rb11
11 files changed, 400 insertions, 192 deletions
diff --git a/lib/algorithm.rb b/lib/algorithm.rb
new file mode 100644
index 0000000..912e14d
--- /dev/null
+++ b/lib/algorithm.rb
@@ -0,0 +1,35 @@
+module OpenTox
+ module Algorithm
+
+ class Fminer < OpenTox
+ # Create a new dataset with BBRC features
+ def initialize(training_dataset)
+ @uri = RestClient.post @@config[:services]["opentox-fminer"], :dataset_uri => training_dataset.uri
+ end
+ end
+
+ class Similarity < OpenTox
+
+ def initialize
+ @uri = @@config[:services]["opentox-similarity"]
+ end
+
+ def self.tanimoto(dataset,compounds)
+ RestClient.post @uri + 'tanimoto', :dataset_uri => dataset.uri, :compound_uris => compounds.collect{ |c| c.uri }
+ end
+
+ def self.weighted_tanimoto(dataset,compounds)
+ RestClient.post @uri + 'weighted_tanimoto', :dataset_uri => dataset.uri, :compound_uris => compounds.collect{ |c| c.uri }
+ end
+
+ end
+
+ class Lazar < OpenTox
+ # Create a new prediction model from a dataset
+ def initialize(params)
+ @uri = RestClient.post @@config[:services]["opentox-lazar"] + 'models' , :dataset_uri => params[:dataset_uri]
+ end
+ end
+
+ end
+end
diff --git a/lib/compound.rb b/lib/compound.rb
new file mode 100644
index 0000000..4652770
--- /dev/null
+++ b/lib/compound.rb
@@ -0,0 +1,67 @@
+module OpenTox
+
+ # uri: /compound/:inchi
+ class Compound < OpenTox
+
+ attr_reader :inchi
+
+ # Initialize with <tt>:uri => uri</tt>, <tt>:smiles => smiles</tt> or <tt>:name => name</tt> (name can be also an InChI/InChiKey, CAS number, etc)
+ def initialize(params)
+ @@cactus_uri="http://cactus.nci.nih.gov/chemical/structure/"
+ if params[:smiles]
+ @inchi = smiles2inchi(params[:smiles])
+ @uri = File.join(@@config[:services]["opentox-dataset"],"compound",@inchi)
+ elsif params[:inchi]
+ @inchi = inchi
+ @uri = File.join(@@config[:services]["opentox-dataset"],"compound",@inchi)
+ elsif params[:name]
+ @inchi = RestClient.get "#{@@cactus_uri}#{params[:name]}/stdinchi"
+ @uri = File.join(@@config[:services]["opentox-dataset"],"compound",@inchi)
+ elsif params[:uri]
+ @inchi = params[:uri].sub(/^.*InChI/, 'InChI')
+ @uri = params[:uri]
+ end
+ end
+
+ # Get the (canonical) smiles
+ def smiles
+ obconversion(@inchi,'inchi','can')
+ end
+
+ def sdf
+ obconversion(@inchi,'inchi','sdf')
+ end
+
+ # Matchs a smarts string
+ def match?(smarts)
+ obconversion = OpenBabel::OBConversion.new
+ obmol = OpenBabel::OBMol.new
+ obconversion.set_in_format('inchi')
+ obconversion.read_string(obmol,@inchi)
+ smarts_pattern = OpenBabel::OBSmartsPattern.new
+ smarts_pattern.init(smarts)
+ smarts_pattern.match(obmol)
+ end
+
+ # Match an array of smarts features, returns matching features
+ def match(smarts_features)
+ smarts_features.all_features.collect{ |smarts| smarts if self.match?(smarts.name) }.compact
+ end
+
+ def smiles2inchi(smiles)
+ obconversion(smiles,'smi','inchi')
+ end
+
+ def smiles2cansmi(smiles)
+ obconversion(smiles,'smi','can')
+ end
+
+ def obconversion(identifier,input_format,output_format)
+ obconversion = OpenBabel::OBConversion.new
+ obmol = OpenBabel::OBMol.new
+ obconversion.set_in_and_out_formats input_format, output_format
+ obconversion.read_string obmol, identifier
+ obconversion.write_string(obmol).gsub(/\s/,'').chomp
+ end
+ end
+end
diff --git a/lib/dataset.rb b/lib/dataset.rb
new file mode 100644
index 0000000..ca82f11
--- /dev/null
+++ b/lib/dataset.rb
@@ -0,0 +1,76 @@
+module OpenTox
+
+ # key: /datasets
+ # set: dataset uris
+ # key: /dataset/:dataset/compounds
+ # set: compound uris
+ # key: /dataset/:dataset/compound/:inchi/:feature_type
+ # set: feature uris
+ class Dataset < OpenTox
+
+ # Initialize with <tt>:uri => uri</tt> or <tt>:name => name</tt> (creates a new dataset)
+ def initialize(uri)
+ super(uri)
+ end
+
+ def self.create(params)
+ uri = RestClient.post File.join(@@config[:services]["opentox-dataset"],"datasets"), :name => params[:name]
+ Dataset.new(uri.to_s)
+ end
+
+ def self.find(params)
+ if params[:name]
+ uri = RestClient.get File.join(@@config[:services]["opentox-dataset"], params[:name])
+ elsif params[:uri]
+ uri = params[:uri]
+ end
+ if RestClient.get uri
+ Dataset.new(uri)
+ else
+ nil
+ end
+ end
+
+ def import(params)
+ if params[:csv]
+ # RestClient seems not to work for file uploads
+ `curl -X POST -F "file=@#{params[:csv]};type=text/csv" -F compound_format=#{params[:compound_format]} -F feature_type=#{params[:feature_type]} #{@uri + '/import'}`
+ end
+ end
+
+ def add_features(features,feature_type)
+ #puts @uri
+ #puts feature_type
+ #puts features.to_yaml
+ HTTPClient.post @uri, {:feature_type => feature_type, :features => features.to_yaml}
+ #`curl -X POST -F feature_type="#{feature_type}" -F features="#{features.to_yaml}" #{@uri}`
+ end
+
+ # Get all compounds from a dataset
+ def compound_uris
+ RestClient.get(File.join(@uri, 'compounds')).split("\n")
+ end
+
+ def compounds
+ compound_uris.collect{|uri| Compound.new(:uri => uri)}
+ end
+
+ # Get all features for a compound
+ def feature_uris(compound,feature_type)
+ #puts File.join(@uri, 'compound', compound.inchi, feature_type)
+ RestClient.get(File.join(@uri, 'compound', compound.inchi, feature_type)).split("\n")
+ end
+
+ # Get all features for a compound
+ def features(compound,feature_type)
+ feature_uris(compound,feature_type).collect{|uri| Feature.new(:uri => uri)}
+ end
+
+ # Delete a dataset
+ def delete
+ RestClient.delete @uri
+ end
+
+ end
+
+end
diff --git a/lib/environment.rb b/lib/environment.rb
index 793fef9..c65e968 100644
--- a/lib/environment.rb
+++ b/lib/environment.rb
@@ -1,14 +1,18 @@
# load configuration
-ENV['RACK_ENV'] = 'development' unless ENV['RACK_ENV']
+ENV['RACK_ENV'] = 'test' unless ENV['RACK_ENV']
+
+basedir = File.join(ENV['HOME'], ".opentox")
+config_dir = File.join(basedir, "config")
+@@tmp_dir = File.join(basedir, "tmp")
+config_file = File.join(config_dir, "#{ENV['RACK_ENV']}.yaml")
-config_file = File.join(ENV['HOME'], ".opentox/config/#{ENV['RACK_ENV']}.yaml")
if File.exist?(config_file)
@@config = YAML.load_file(config_file)
else
- FileUtils.mkdir_p File.dirname(config_file)
+ FileUtils.mkdir_p config_dir
+ FileUtils.mkdir_p @@tmp_dir
FileUtils.cp(File.join(File.dirname(__FILE__), 'templates/config.yaml'), config_file)
puts "Please edit #{config_file} and restart your application."
exit
end
-
diff --git a/lib/feature.rb b/lib/feature.rb
new file mode 100644
index 0000000..0cad7c0
--- /dev/null
+++ b/lib/feature.rb
@@ -0,0 +1,45 @@
+module OpenTox
+
+ # uri: /feature/:name/:property_name/:property_value/...
+ class Feature < OpenTox
+
+ attr_accessor :name, :values
+
+ def initialize(params)
+ if params[:uri]
+ @uri = params[:uri]
+ items = URI.split(@uri)[5].split(/\//)
+ @name = items[1]
+ @values = {}
+ i = 3
+ while i < items.size
+ @values[items[i]] = items[i+1]
+ i += 2
+ end
+ else
+ @name = URI.encode(URI.decode(params[:name]))
+ @values = params[:values]
+ @uri = File.join(@@config[:services]["opentox-dataset"],"feature",path)
+ end
+ end
+
+ def values_path
+ path = ''
+ @values.each do |k,v|
+ path += '/' + URI.encode(k.to_s) + '/' + URI.encode(v.to_s)
+ end
+ path
+ end
+
+ def path
+ File.join(@name,values_path)
+ end
+
+ def value(property)
+ items = @uri.split(/\//)
+ i = items.index(property)
+ items[i+1]
+ end
+
+ end
+end
diff --git a/lib/model.rb b/lib/model.rb
new file mode 100644
index 0000000..7f43860
--- /dev/null
+++ b/lib/model.rb
@@ -0,0 +1,51 @@
+module OpenTox
+ module Model
+
+ class Lazar < OpenTox
+
+ # Create a new prediction model from a dataset
+ def initialize(params)
+ super(params[:uri])
+ end
+
+ # Predict a compound
+ def predict(compound)
+ LazarPrediction.new(:uri => RestClient.post(@uri, :compound_uri => compound.uri))
+ end
+
+ end
+
+ end
+
+ module Prediction
+
+ module Classification
+
+ class Lazar < OpenTox
+
+ def initialize(params)
+ super(params[:uri])
+ end
+
+ def classification
+ YAML.load(RestClient.get @uri)[:classification]
+ end
+
+ def confidence
+ YAML.load(RestClient.get @uri)[:confidence]
+ end
+
+ def neighbors
+ RestClient.get @uri + '/neighbors'
+ end
+
+ def features
+ RestClient.get @uri + '/features'
+ end
+
+ end
+
+ end
+
+ end
+end
diff --git a/lib/opentox-ruby-api-wrapper.rb b/lib/opentox-ruby-api-wrapper.rb
index d74b412..e504e65 100644
--- a/lib/opentox-ruby-api-wrapper.rb
+++ b/lib/opentox-ruby-api-wrapper.rb
@@ -1,4 +1,5 @@
-['rubygems', 'sinatra', 'sinatra/respond_to', 'sinatra/url_for', 'builder', 'rest_client', 'yaml', 'spork', 'environment'].each do |lib|
+#['rubygems', 'sinatra', 'sinatra/respond_to', 'sinatra/url_for', 'builder', 'rest_client', 'yaml', 'spork', 'environment', 'openbabel', 'httpclient'].each do |lib|
+['rubygems', 'sinatra', 'sinatra/url_for', 'builder', 'rest_client', 'yaml', 'spork', 'environment', 'openbabel', 'httpclient'].each do |lib|
require lib
end
@@ -7,10 +8,8 @@ module OpenTox
class OpenTox
attr_reader :uri
- # Escape all nonword characters
- def uri_escape(string)
- #URI.escape(string, /[^\w]/)
- URI.escape(string, /[^#{URI::PATTERN::UNRESERVED}]/)
+ def initialize(uri)
+ @uri = uri
end
# Get the object name
@@ -23,168 +22,15 @@ module OpenTox
RestClient.delete @uri
end
- end
-
- class Compound < OpenTox
-
- # Initialize with <tt>:uri => uri</tt>, <tt>:smiles => smiles</tt> or <tt>:name => name</tt> (name can be also an InChI/InChiKey, CAS number, etc)
- def initialize(params)
- if params[:uri]
- @uri = params[:uri].to_s
- elsif params[:smiles]
- @uri = RestClient.post @@config[:services]["opentox-compound"] ,:smiles => uri_escape(params[:smiles])
- elsif params[:name]
- @uri = RestClient.post @@config[:services]["opentox-compound"] ,:name => uri_escape(params[:name])
- end
- end
-
- # Get the (canonical) smiles
- def smiles
- RestClient.get @uri
- end
-
- # Get the unique id (URI encoded canonical smiles)
- def uid
- RestClient.get @uri + '.uid'
- end
-
- # Matchs a smarts string
- def match?(smarts)
- if RestClient.get(@uri + '/match/' + uri_escape(smarts)) == 'true'
- true
- else
- false
- end
- end
-
- # Match an array of smarts features, returns matching features
- def match(smarts_features)
- smarts_features.collect{ |smarts| smarts if self.match?(smarts.name) }.compact
- end
-
- end
-
- class Feature < OpenTox
-
- # Initialize with <tt>:uri => uri</tt>, or <tt>:name => name, :values => hash_of_property_names_and_values</tt>
- def initialize(params)
- if params[:uri]
- @uri = params[:uri].to_s
- else
- @uri = @@config[:services]["opentox-feature"] + uri_escape(params[:name])
- params[:values].each do |k,v|
- @uri += '/' + k.to_s + '/' + v.to_s
- end
- end
- end
-
- # Get the value of a property
- def value(property)
- RestClient.get @uri + '/' + property
+ # Object path without hostname
+ def path
+ URI.split(@uri)[5]
end
end
- class Dataset < OpenTox
-
- # Initialize with <tt>:uri => uri</tt> or <tt>:name => name</tt> (creates a new dataset)
- def initialize(params)
- if params[:uri]
- @uri = params[:uri].to_s
- elsif params[:name] and params[:filename]
- @uri = `curl -X POST -F file=@#{params[:filename]} -F name="#{params[:name]}" #{@@config[:services]["opentox-dataset"]}`
- elsif params[:name]
- @uri = RestClient.post @@config[:services]["opentox-dataset"], :name => params[:name]
- end
- end
-
- # Get all compounds from a dataset
- def compounds
- RestClient.get(@uri + '/compounds').split("\n").collect{ |c| Compound.new(:uri => c) }
- end
-
- # Get all compounds and features from a dataset, returns a hash with compound_uris as keys and arrays of feature_uris as values
- def all_compounds_and_features_uris
- YAML.load(RestClient.get(@uri + '/compounds/features.yaml'))
- end
-
- # Get all features from a dataset
- def all_features
- RestClient.get(@uri + '/features').split("\n").collect{|f| Feature.new(:uri => f)}
- end
-
- # Get all features for a compound
- def features(compound)
- RestClient.get(@uri + '/compound/' + uri_escape(compound.uri) + '/features').split("\n").collect{|f| Feature.new(:uri => f) }
- end
-
- # Add a compound and a feature to a dataset
- def add(compound,feature)
- RestClient.put @uri, :compound_uri => compound.uri, :feature_uri => feature.uri
- end
-
- # Tell the dataset that it is complete
- def close
- RestClient.put @uri, :finished => 'true'
- end
-
- end
-
- class Fminer < OpenTox
-
- # Create a new dataset with BBRC features
- def initialize(training_dataset)
- @dataset_uri = RestClient.post @@config[:services]["opentox-fminer"], :dataset_uri => training_dataset.uri
- end
-
- def dataset
- Dataset.new(:uri => @dataset_uri)
- end
-
- end
-
- class Lazar < OpenTox
-
- # Create a new prediction model from a dataset
- def initialize(params)
- if params[:uri]
- @uri = params[:uri]
- elsif params[:dataset_uri]
- @uri = RestClient.post @@config[:services]["opentox-lazar"] + 'models' , :dataset_uri => params[:dataset_uri]
- end
- end
-
- # Predict a compound
- def predict(compound)
- LazarPrediction.new(:uri => RestClient.post(@uri, :compound_uri => compound.uri))
- end
-
- end
-
- class LazarPrediction < OpenTox
-
- def initialize(params)
- if params[:uri]
- @uri = params[:uri]
- end
- end
-
- def classification
- YAML.load(RestClient.get @uri)[:classification]
- end
-
- def confidence
- YAML.load(RestClient.get @uri)[:confidence]
- end
-
- def neighbors
- RestClient.get @uri + '/neighbors'
- end
-
- def features
- RestClient.get @uri + '/features'
- end
-
- end
+end
+['compound','feature','dataset','algorithm','model','utils'].each do |lib|
+ require lib
end
diff --git a/lib/tasks/opentox.rb b/lib/tasks/opentox.rb
index 6f1284f..afb8cb6 100644
--- a/lib/tasks/opentox.rb
+++ b/lib/tasks/opentox.rb
@@ -1,4 +1,4 @@
-require "environment"
+load File.join(File.dirname(__FILE__), '..', 'environment.rb')
namespace :opentox do
@@ -18,18 +18,22 @@ namespace :opentox do
task :start do
@@config[:services].each do |service,uri|
dir = File.join(@@config[:base_dir], service)
- case @@config[:webserver]
- when 'thin'
+ server = @@config[:webserver]
+ `redis-server &`
+ case server
+ when /thin|mongrel|webrick/
port = uri.sub(/^.*:/,'').sub(/\/$/,'')
Dir.chdir dir
+ pid_file = File.join(@@tmp_dir,"#{service}.pid")
begin
- `thin --trace --rackup config.ru start -p #{port} -e #{ENV['RACK_ENV']} &`
- puts "#{service} started on port #{port}."
+ `#{server} --trace --rackup config.ru start -p #{port} -e #{ENV['RACK_ENV']} -P #{pid_file} -d &`
+ puts "#{service} started on localhost:#{port} in #{ENV['RACK_ENV']} environment with PID file #{pid_file}."
rescue
puts "Cannot start #{service} on port #{port}."
end
when 'passenger'
- puts "not yet implemented"
+ `touch #{File.join(dir, 'tmp/restart.txt')}`
+ puts "#{service} restarted."
else
puts "not yet implemented"
end
@@ -38,9 +42,18 @@ namespace :opentox do
desc "Stop opentox services"
task :stop do
- @@config[:services].each do |service,uri|
- port = uri.sub(/^.*:/,'').sub(/\/$/,'')
- `echo "SHUTDOWN" | nc localhost #{port}` if port
+ server = @@config[:webserver]
+ if server =~ /thin|mongrel|webrick/
+ @@config[:services].each do |service,uri|
+ port = uri.sub(/^.*:/,'').sub(/\/$/,'')
+ pid_file = File.join(@@tmp_dir,"#{service}.pid")
+ begin
+ puts `#{server} stop -P #{pid_file}`
+ puts "#{service} stopped on localhost:#{port}"
+ rescue
+ puts "Cannot stop #{service} on port #{port}."
+ end
+ end
end
end
@@ -49,20 +62,55 @@ namespace :opentox do
end
- namespace :test do
+ desc "Run all OpenTox tests"
+ task :test do
+ @@config[:services].each do |service,uri|
+ dir = File.join(@@config[:base_dir], service)
+ Dir.chdir dir
+ puts "Running tests in #{dir}"
+ `rake test -t 1>&2`
+ end
+ end
- ENV['RACK_ENV'] = 'test'
- test = "#{Dir.pwd}/test/test.rb"
+end
- desc "Run local tests"
- task :local => "opentox:services:restart" do
- load test
+desc "Start service in current directory"
+task :start do
+ service = File.basename(Dir.pwd).intern
+ server = @@config[:webserver]
+ case server
+ when /thin|mongrel|webrick/
+ port = @@config[:services][service].sub(/^.*:/,'').sub(/\/$/,'')
+ pid_file = File.join(@@tmp_dir,"#{service}.pid")
+ begin
+ `#{server} --trace --rackup config.ru start -p #{port} -e #{ENV['RACK_ENV']} -P #{pid_file} -d &`
+ puts "#{service} started on localhost:#{port} in #{ENV['RACK_ENV']} environment with PID file #{pid_file}."
+ rescue
+ puts "Cannot start #{service} on port #{port}."
+ end
+ when 'passenger'
+ `touch tmp/restart.txt`
+ puts "#{service} restarted."
+ else
+ puts "not yet implemented"
end
+end
- task :remote do
- #load 'test.rb'
+desc "Stop service in current directory"
+task :stop do
+ service = File.basename(Dir.pwd).intern
+ server = @@config[:webserver]
+ if server =~ /thin|mongrel|webrick/
+ port = @@config[:services][service].sub(/^.*:/,'').sub(/\/$/,'')
+ pid_file = File.join(@@tmp_dir,"#{service}.pid")
+ begin
+ puts `thin stop -P #{pid_file}`
+ puts "#{service} stopped on localhost:#{port}"
+ rescue
+ puts "Cannot stop #{service} on port #{port}."
end
-
end
-
end
+
+desc "Restart service in current directory"
+task :restart => [:stop, :start]
diff --git a/lib/templates/config.ru b/lib/templates/config.ru
new file mode 100644
index 0000000..63dd2ce
--- /dev/null
+++ b/lib/templates/config.ru
@@ -0,0 +1,23 @@
+require 'rubygems'
+require 'sinatra'
+require 'application.rb'
+require 'rack'
+require 'rack/contrib'
+
+FileUtils.mkdir_p 'log' unless File.exists?('log')
+log = File.new("log/#{ENV["RACK_ENV"]}.log", "a")
+$stdout.reopen(log)
+$stderr.reopen(log)
+
+if ENV['RACK_ENV'] == 'production'
+ use Rack::MailExceptions do |mail|
+ mail.to 'helma@in-silico.ch'
+ mail.subject '[ERROR] %s'
+ end
+elsif ENV['RACK_ENV'] == 'development'
+ use Rack::Reloader
+ use Rack::ShowExceptions
+end
+
+run Sinatra::Application
+
diff --git a/lib/templates/config.yaml b/lib/templates/config.yaml
index 768fa01..14326fe 100644
--- a/lib/templates/config.yaml
+++ b/lib/templates/config.yaml
@@ -1,8 +1,10 @@
:base_dir: /home/ch/webservices
:webserver: thin
:services:
- - opentox-feature
- - opentox-compound
- - opentox-dataset
- - opentox-fminer
- - opentox-lazar
+# make sure to provide a full uri (including training slash)
+ opentox-feature: "http://localhost:5000/"
+ opentox-compound: "http://localhost:5001/"
+ opentox-dataset: "http://localhost:5002/"
+ opentox-fminer: "http://localhost:5003/"
+ opentox-similarity: "http://localhost:5004/"
+ opentox-lazar: "http://localhost:5005/"
diff --git a/lib/utils.rb b/lib/utils.rb
new file mode 100644
index 0000000..2716f45
--- /dev/null
+++ b/lib/utils.rb
@@ -0,0 +1,11 @@
+module OpenTox
+ module Utils
+
+ # gauss kernel
+ def self.gauss(sim, sigma = 0.3)
+ x = 1.0 - sim
+ Math.exp(-(x*x)/(2*sigma*sigma))
+ end
+
+ end
+end