opentox-ruby 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/model.rb CHANGED
@@ -1,143 +1,271 @@
1
1
  module OpenTox
2
- module Model
3
2
 
4
- class Generic
3
+ module Model
5
4
 
6
- MODEL_ATTRIBS = [:uri, :title, :creator, :date, :format, :predictedVariables, :independentVariables, :dependentVariables, :trainingDataset, :algorithm]
7
- MODEL_ATTRIBS.each{ |a| attr_accessor(a) }
5
+ include OpenTox
8
6
 
9
- def self.find(uri)
10
- owl = OpenTox::Owl.from_uri(uri, "Model")
11
- return self.new(owl)
7
+ # Run a model with parameters
8
+ # @param [Hash] params Parameters for OpenTox model
9
+ # @return [text/uri-list] Task or resource URI
10
+ def run(params)
11
+ if CONFIG[:yaml_hosts].include?(URI.parse(@uri).host)
12
+ accept = 'application/x-yaml'
13
+ else
14
+ accept = 'application/rdf+xml'
12
15
  end
13
-
14
- def self.to_rdf(model)
15
- owl = OpenTox::Owl.create 'Model', model.uri
16
- (MODEL_ATTRIBS - [:uri]).each do |a|
17
- owl.set(a.to_s,model.send(a.to_s))
18
- end
19
- owl.rdf
20
- end
21
-
22
- protected
23
- def initialize(owl)
24
- MODEL_ATTRIBS.each do |a|
25
- self.send("#{a.to_s}=".to_sym, owl.get(a.to_s)) unless a==:uri
26
- end
27
- @uri = owl.uri
28
- if ENV['RACK_ENV'] =~ /test|debug/
29
- begin
30
- raise "uri invalid" unless Utils.is_uri?(@uri)
31
- raise "no predicted variables" unless @predictedVariables and @predictedVariables.size>0
32
- rescue => ex
33
- RestClientWrapper.raise_uri_error "invalid model: '"+ex.message+"'\n"+self.to_yaml+"\n",@uri.to_s
34
- end
35
- LOGGER.warn "model has no dependent variable" unless @dependentVariables and @dependentVariables.size>0
36
- LOGGER.warn "model has no algorithm" unless @algorithm and @algorithm.size>0
37
- LOGGER.warn "model has no indenpendent variables" unless @independentVariables
38
- end
16
+ begin
17
+ RestClientWrapper.post(@uri,{:accept => accept},params).to_s
18
+ rescue => e
19
+ LOGGER.error "Failed to run #{@uri} with #{params.inspect} (#{e.inspect})"
20
+ raise "Failed to run #{@uri} with #{params.inspect}"
39
21
  end
40
22
  end
41
-
42
- class PredictionModel < Generic
43
-
44
- def self.build( algorithm_uri, algorithm_params )
45
-
46
- LOGGER.debug "Build model, algorithm_uri:"+algorithm_uri.to_s+", algorithm_parms: "+algorithm_params.inspect.to_s
47
- uri = OpenTox::RestClientWrapper.post(algorithm_uri,algorithm_params).to_s
48
- LOGGER.debug "Build model done: "+uri.to_s
49
- RestClientWrapper.raise_uri_error("Invalid build model result: '"+uri.to_s+"'", algorithm_uri, algorithm_params ) unless Utils.model_uri?(uri)
50
- return PredictionModel.find(uri)
51
- end
52
-
53
- def predict_dataset( dataset_uri )
54
-
55
- LOGGER.debug "Predict dataset: "+dataset_uri.to_s+" with model "+@uri.to_s
56
- uri = RestClientWrapper.post(@uri, {:accept => "text/uri-list", :dataset_uri=>dataset_uri})
57
- RestClientWrapper.raise_uri_error("Prediciton result no dataset uri: "+uri.to_s, @uri, {:dataset_uri=>dataset_uri} ) unless Utils.dataset_uri?(uri)
58
- uri
59
- end
60
-
61
- def classification?
62
- #HACK replace with request to ontology server
63
- if @title =~ /(?i)classification/
64
- return true
65
- elsif @title =~ /(?i)regression/
66
- return false
67
- elsif @uri =~/ntua/ and @title =~ /mlr/
68
- return false
69
- elsif @uri =~/tu-muenchen/ and @title =~ /regression|M5P|GaussP/
70
- return false
71
- elsif @uri =~/ambit2/ and @title =~ /pKa/ || @title =~ /Regression|Caco/
72
- return false
73
- elsif @uri =~/majority/
74
- return (@uri =~ /class/) != nil
75
- else
76
- raise "unknown model, uri:'"+@uri.to_s+"' title:'"+@title.to_s+"'"
77
- end
78
- end
23
+
24
+ # Generic OpenTox model class for all API compliant services
25
+ class Generic
26
+ include Model
79
27
  end
80
28
 
81
- class Lazar < Generic
82
-
83
- attr_accessor :feature_dataset_uri, :effects, :activities, :p_values, :fingerprints, :features
84
-
85
- def initialize
86
- @source = "http://github.com/helma/opentox-model"
87
- @algorithm = File.join(@@config[:services]["opentox-algorithm"],"lazar")
88
- #@independent_variables = File.join(@@config[:services]["opentox-algorithm"],"fminer#BBRC_representative")
29
+ # Lazy Structure Activity Relationship class
30
+ class Lazar
31
+
32
+ include Model
33
+ include Algorithm
34
+
35
+ attr_accessor :compound, :prediction_dataset, :features, :effects, :activities, :p_values, :fingerprints, :feature_calculation_algorithm, :similarity_algorithm, :prediction_algorithm, :min_sim
36
+
37
+ def initialize(uri=nil)
38
+
39
+ if uri
40
+ super uri
41
+ else
42
+ super CONFIG[:services]["opentox-model"]
43
+ end
44
+
45
+ @metadata[OT.algorithm] = File.join(CONFIG[:services]["opentox-algorithm"],"lazar")
46
+
89
47
  @features = []
90
48
  @effects = {}
91
49
  @activities = {}
92
50
  @p_values = {}
93
51
  @fingerprints = {}
52
+
53
+ @feature_calculation_algorithm = "Substructure.match"
54
+ @similarity_algorithm = "Similarity.tanimoto"
55
+ @prediction_algorithm = "Neighbors.weighted_majority_vote"
56
+
57
+ @min_sim = 0.3
58
+
94
59
  end
95
60
 
96
- def save
97
- @features.uniq!
98
- resource = RestClient::Resource.new(@@config[:services]["opentox-model"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
99
- resource.post(self.to_yaml, :content_type => "application/x-yaml").chomp.to_s
61
+ # Get URIs of all lazar models
62
+ # @return [Array] List of lazar model URIs
63
+ def self.all
64
+ RestClientWrapper.get(CONFIG[:services]["opentox-model"]).to_s.split("\n")
100
65
  end
101
66
 
102
- def self.find_all
103
- RestClientWrapper.get(@@config[:services]["opentox-model"]).chomp.split("\n")
67
+ # Find a lazar model
68
+ # @param [String] uri Model URI
69
+ # @return [OpenTox::Model::Lazar] lazar model
70
+ def self.find(uri)
71
+ YAML.load RestClientWrapper.get(uri,:accept => 'application/x-yaml')
104
72
  end
105
73
 
106
- def self.predict(compound_uri,model_uri)
107
- #RestClientWrapper.post(model_uri,{:compound_uri => compound_uri, :accept => 'application/x-yaml'})
108
- `curl -X POST -d 'compound_uri=#{compound_uri}' -H 'Accept:application/x-yaml' #{model_uri}`
74
+ # Create a new lazar model
75
+ # @param [optional,Hash] params Parameters for the lazar algorithm (OpenTox::Algorithm::Lazar)
76
+ # @return [OpenTox::Model::Lazar] lazar model
77
+ def self.create(params)
78
+ lazar_algorithm = OpenTox::Algorithm::Generic.new File.join( CONFIG[:services]["opentox-algorithm"],"lazar")
79
+ model_uri = lazar_algorithm.run(params)
80
+ OpenTox::Model::Lazar.find(model_uri)
109
81
  end
110
- end
111
-
112
- class PropertyLazar < Generic
113
-
114
- attr_accessor :feature_dataset_uri, :properties, :features, :activities#, :effects, :p_values
115
-
116
- def initialize
117
- @source = "http://github.com/helma/opentox-model"
118
- @algorithm = File.join(@@config[:services]["opentox-algorithm"],"property_lazar")
119
- #@independent_variables = File.join(@@config[:services]["opentox-algorithm"],"fminer#BBRC_representative")
120
- @features = []
121
- #@effects = {}
122
- @activities = {}
123
- #@p_values = {}
124
- @properties = {}
82
+
83
+ # Get a parameter value
84
+ # @param [String] param Parameter name
85
+ # @return [String] Parameter value
86
+ def parameter(param)
87
+ @metadata[OT.parameters].collect{|p| p[OT.paramValue] if p[DC.title] == param}.compact.first
125
88
  end
126
89
 
127
- def save
128
- @features.uniq!
129
- resource = RestClient::Resource.new(@@config[:services]["opentox-model"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
130
- resource.post(self.to_yaml, :content_type => "application/x-yaml").chomp.to_s
90
+ # Predict a dataset
91
+ # @param [String] dataset_uri Dataset URI
92
+ # @return [OpenTox::Dataset] Dataset with predictions
93
+ def predict_dataset(dataset_uri)
94
+ @prediction_dataset = Dataset.create
95
+ @prediction_dataset.add_metadata({
96
+ OT.hasSource => @uri,
97
+ DC.creator => @uri,
98
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
99
+ OT.parameters => [{DC.title => "dataset_uri", OT.paramValue => dataset_uri}]
100
+ })
101
+ d = Dataset.new(dataset_uri)
102
+ d.load_compounds
103
+ d.compounds.each do |compound_uri|
104
+ predict(compound_uri,false)
105
+ end
106
+ @prediction_dataset.save
107
+ @prediction_dataset
131
108
  end
132
109
 
133
- def self.find_all
134
- RestClientWrapper.get(@@config[:services]["opentox-model"]).chomp.split("\n")
110
+ # Predict a compound
111
+ # @param [String] compound_uri Compound URI
112
+ # @param [optinal,Boolean] verbose Verbose prediction (output includes neighbors and features)
113
+ # @return [OpenTox::Dataset] Dataset with prediction
114
+ def predict(compound_uri,verbose=false)
115
+
116
+ @compound = Compound.new compound_uri
117
+ features = {}
118
+
119
+ unless @prediction_dataset
120
+ #@prediction_dataset = cached_prediction
121
+ #return @prediction_dataset if cached_prediction
122
+ @prediction_dataset = Dataset.create
123
+ @prediction_dataset.add_metadata( {
124
+ OT.hasSource => @uri,
125
+ DC.creator => @uri,
126
+ # TODO: fix dependentVariable
127
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
128
+ OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
129
+ } )
130
+ end
131
+
132
+ return @prediction_dataset if database_activity
133
+
134
+ neighbors
135
+ prediction = eval("#{@prediction_algorithm}(@neighbors,{:similarity_algorithm => @similarity_algorithm, :p_values => @p_values})")
136
+
137
+ prediction_feature_uri = File.join( @prediction_dataset.uri, "feature", "prediction", File.basename(@metadata[OT.dependentVariables]),@prediction_dataset.compounds.size.to_s)
138
+ # TODO: fix dependentVariable
139
+ @prediction_dataset.metadata[OT.dependentVariables] = prediction_feature_uri
140
+
141
+ if @neighbors.size == 0
142
+ @prediction_dataset.add_feature(prediction_feature_uri, {
143
+ OT.isA => OT.MeasuredFeature,
144
+ OT.hasSource => @uri,
145
+ DC.creator => @uri,
146
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
147
+ OT.error => "No similar compounds in training dataset.",
148
+ OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
149
+ })
150
+ @prediction_dataset.add @compound.uri, prediction_feature_uri, prediction[:prediction]
151
+
152
+ else
153
+ @prediction_dataset.add_feature(prediction_feature_uri, {
154
+ OT.isA => OT.ModelPrediction,
155
+ OT.hasSource => @uri,
156
+ DC.creator => @uri,
157
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
158
+ OT.prediction => prediction[:prediction],
159
+ OT.confidence => prediction[:confidence],
160
+ OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
161
+ })
162
+ @prediction_dataset.add @compound.uri, prediction_feature_uri, prediction[:prediction]
163
+
164
+ if verbose
165
+ if @feature_calculation_algorithm == "Substructure.match"
166
+ f = 0
167
+ @compound_features.each do |feature|
168
+ feature_uri = File.join( @prediction_dataset.uri, "feature", "descriptor", f.to_s)
169
+ features[feature] = feature_uri
170
+ @prediction_dataset.add_feature(feature_uri, {
171
+ OT.isA => OT.Substructure,
172
+ OT.smarts => feature,
173
+ OT.pValue => @p_values[feature],
174
+ OT.effect => @effects[feature]
175
+ })
176
+ @prediction_dataset.add @compound.uri, feature_uri, true
177
+ f+=1
178
+ end
179
+ else
180
+ @compound_features.each do |feature|
181
+ features[feature] = feature
182
+ @prediction_dataset.add @compound.uri, feature, true
183
+ end
184
+ end
185
+ n = 0
186
+ @neighbors.each do |neighbor|
187
+ neighbor_uri = File.join( @prediction_dataset.uri, "feature", "neighbor", n.to_s )
188
+ @prediction_dataset.add_feature(neighbor_uri, {
189
+ OT.compound => neighbor[:compound],
190
+ OT.similarity => neighbor[:similarity],
191
+ OT.measuredActivity => neighbor[:activity],
192
+ OT.isA => OT.Neighbor
193
+ })
194
+ @prediction_dataset.add @compound.uri, neighbor_uri, true
195
+ f = 0 unless f
196
+ neighbor[:features].each do |feature|
197
+ if @feature_calculation_algorithm == "Substructure.match"
198
+ feature_uri = File.join( @prediction_dataset.uri, "feature", "descriptor", f.to_s) unless feature_uri = features[feature]
199
+ else
200
+ feature_uri = feature
201
+ end
202
+ @prediction_dataset.add neighbor[:compound], feature_uri, true
203
+ unless features.has_key? feature
204
+ features[feature] = feature_uri
205
+ @prediction_dataset.add_feature(feature_uri, {
206
+ OT.isA => OT.Substructure,
207
+ OT.smarts => feature,
208
+ OT.pValue => @p_values[feature],
209
+ OT.effect => @effects[feature]
210
+ })
211
+ f+=1
212
+ end
213
+ end
214
+ n+=1
215
+ end
216
+ # what happens with dataset predictions?
217
+ end
218
+ end
219
+
220
+ @prediction_dataset.save
221
+ @prediction_dataset
135
222
  end
136
223
 
137
- def self.predict(compound_uri,model_uri)
138
- #RestClientWrapper.post(model_uri,{:compound_uri => compound_uri, :accept => 'application/x-yaml'})
139
- `curl -X POST -d 'compound_uri=#{compound_uri}' -H 'Accept:application/x-yaml' #{model_uri}`
224
+ # Find neighbors and store them as object variable
225
+ def neighbors
226
+
227
+ @compound_features = eval("#{@feature_calculation_algorithm}(@compound,@features)") if @feature_calculation_algorithm
228
+
229
+ @neighbors = []
230
+ @fingerprints.each do |training_compound,training_features|
231
+ sim = eval("#{@similarity_algorithm}(@compound_features,training_features,@p_values)")
232
+ if sim > @min_sim
233
+ @activities[training_compound].each do |act|
234
+ @neighbors << {
235
+ :compound => training_compound,
236
+ :similarity => sim,
237
+ :features => training_features,
238
+ :activity => act
239
+ }
240
+ end
241
+ end
242
+ end
243
+
140
244
  end
245
+
246
+ # Find database activities and store them in @prediction_dataset
247
+ # @return [Boolean] true if compound has databasse activities, false if not
248
+ def database_activity
249
+ if @activities[@compound.uri]
250
+ @activities[@compound.uri].each { |act| @prediction_dataset.add @compound.uri, @metadata[OT.dependentVariables], act }
251
+ @prediction_dataset.add_metadata(OT.hasSource => @metadata[OT.trainingDataset])
252
+ @prediction_dataset.save
253
+ true
254
+ else
255
+ false
256
+ end
257
+ end
258
+
259
+ # Save model at model service
260
+ def save
261
+ self.uri = RestClientWrapper.post(@uri,{:content_type => "application/x-yaml"},self.to_yaml)
262
+ end
263
+
264
+ # Delete model at model service
265
+ def delete
266
+ RestClientWrapper.delete @uri unless @uri == CONFIG[:services]["opentox-model"]
267
+ end
268
+
141
269
  end
142
270
  end
143
271
  end
@@ -0,0 +1,13 @@
1
+ ['rubygems', 'sinatra', 'sinatra/url_for', 'rest_client', 'yaml', 'cgi', 'spork', 'overwrite', 'environment'].each do |lib|
2
+ require lib
3
+ end
4
+
5
+ begin
6
+ require 'openbabel'
7
+ rescue LoadError
8
+ puts "Please install Openbabel with 'rake openbabel:install' in the compound component"
9
+ end
10
+
11
+ ['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature', 'rest_client_wrapper'].each do |lib|
12
+ require lib
13
+ end
data/lib/opentox.rb ADDED
@@ -0,0 +1,47 @@
1
+ module OpenTox
2
+
3
+ attr_reader :uri
4
+ attr_accessor :metadata
5
+
6
+ # Initialize OpenTox object with optional uri
7
+ # @param [optional, String] URI
8
+ def initialize(uri=nil)
9
+ @metadata = {}
10
+ self.uri = uri if uri
11
+ end
12
+
13
+ # Set URI
14
+ # @param [String] URI
15
+ def uri=(uri)
16
+ @uri = uri
17
+ @metadata[XSD.anyURI] = uri
18
+ end
19
+
20
+ # Get all objects from a service
21
+ # @return [Array] List of available URIs
22
+ def self.all(uri)
23
+ RestClientWrapper.get(uri,:accept => "text/uri-list").to_s.split(/\n/)
24
+ end
25
+
26
+ # Load (and return) metadata from object URI
27
+ # @return [Hash] Metadata
28
+ def load_metadata
29
+ @metadata = Parser::Owl::Generic.new(@uri).load_metadata
30
+ @metadata
31
+ end
32
+
33
+ def add_metadata(metadata)
34
+ metadata.each { |k,v| @metadata[k] = v }
35
+ end
36
+
37
+ # Get OWL-DL representation in RDF/XML format
38
+ # @return [application/rdf+xml] RDF/XML representation
39
+ def to_rdfxml
40
+ s = Serializer::Owl.new
41
+ s.add_metadata(@uri,@metadata)
42
+ #s.add_parameters(@uri,@parameters) if @parameters
43
+ s.to_rdfxml
44
+ end
45
+
46
+ end
47
+
data/lib/overwrite.rb CHANGED
@@ -12,3 +12,75 @@ class Sinatra::Base
12
12
  end
13
13
  end
14
14
 
15
+ class String
16
+ def task_uri?
17
+ self.uri? && !self.match(/task/).nil?
18
+ end
19
+
20
+ def dataset_uri?
21
+ self.uri? && !self.match(/dataset/).nil?
22
+ end
23
+
24
+ def self.model_uri?
25
+ self.uri? && !self.match(/model/).nil?
26
+ end
27
+
28
+ def uri?
29
+ begin
30
+ u = URI::parse(self)
31
+ return (u.scheme!=nil and u.host!=nil)
32
+ rescue URI::InvalidURIError
33
+ return false
34
+ end
35
+ end
36
+ end
37
+
38
+ require 'logger'
39
+ # logging
40
+ #class Logger
41
+ class OTLogger < Logger
42
+
43
+ def pwd
44
+ path = Dir.pwd.to_s
45
+ index = path.rindex(/\//)
46
+ return path if index==nil
47
+ path[(index+1)..-1]
48
+ end
49
+
50
+ def trace()
51
+ lines = caller(0)
52
+ n = 2
53
+ line = lines[n]
54
+
55
+ while (line =~ /spork.rb/ or line =~ /create/ or line =~ /ot-logger.rb/)
56
+ n += 1
57
+ line = lines[n]
58
+ end
59
+
60
+ index = line.rindex(/\/.*\.rb/)
61
+ return line if index==nil
62
+ line[index..-1]
63
+ end
64
+
65
+ def format(msg)
66
+ pwd.ljust(18)+" :: "+msg.to_s+" :: "+trace+" :: "+($sinatra ? $sinatra.request.env['REMOTE_ADDR'] : nil).to_s
67
+ end
68
+
69
+ def debug(msg)
70
+ super format(msg)
71
+ end
72
+
73
+ def info(msg)
74
+ super format(msg)
75
+ end
76
+
77
+ def warn(msg)
78
+ super format(msg)
79
+ end
80
+
81
+ def error(msg)
82
+ super format(msg)
83
+ end
84
+
85
+ end
86
+