summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/compound.rb12
-rw-r--r--lib/dataset.rb134
-rw-r--r--lib/error.rb5
-rw-r--r--lib/opentox-client.rb2
-rw-r--r--lib/opentox.rb80
-rw-r--r--opentox-client.gemspec1
6 files changed, 161 insertions, 73 deletions
diff --git a/lib/compound.rb b/lib/compound.rb
index ce0fdbf..5992ee3 100644
--- a/lib/compound.rb
+++ b/lib/compound.rb
@@ -40,25 +40,25 @@ module OpenTox
# Get InChI
# @return [String] InChI string
def to_inchi
- get(:accept => 'chemical/x-inchi').to_s.chomp if @uri
+ RestClientWrapper.get(@uri,{},{:accept => 'chemical/x-inchi'}).chomp
end
# Get (canonical) smiles
# @return [String] Smiles string
def to_smiles
- get(:accept => 'chemical/x-daylight-smiles').chomp
+ RestClientWrapper.get(@uri,{},{:accept => 'chemical/x-daylight-smiles'}).chomp
end
# Get sdf
# @return [String] SDF string
def to_sdf
- get(:accept => 'chemical/x-mdl-sdfile').chomp
+ RestClientWrapper.get(@uri,{},{:accept => 'chemical/x-mdl-sdfile'}).chomp
end
# Get gif image
# @return [image/gif] Image data
def to_gif
- get("#{CACTUS_URI}#{to_inchi}/image")
+ RestClientWrapper.get("#{CACTUS_URI}#{to_inchi}/image")
end
# Get png image
@@ -66,7 +66,7 @@ module OpenTox
# image = compound.to_png
# @return [image/png] Image data
def to_png
- get(File.join @uri, "image")
+ RestClientWrapper.get(File.join @uri, "image")
end
# Get URI of compound image
@@ -81,7 +81,7 @@ module OpenTox
# @return [String] Compound names
def to_names
begin
- get("#{CACTUS_URI}#{to_inchi}/names").split("\n")
+ RestClientWrapper.get("#{CACTUS_URI}#{to_inchi}/names").split("\n")
rescue
"not available"
end
diff --git a/lib/dataset.rb b/lib/dataset.rb
index 33e1571..ed0ccdd 100644
--- a/lib/dataset.rb
+++ b/lib/dataset.rb
@@ -3,32 +3,128 @@ module OpenTox
# Ruby wrapper for OpenTox Dataset Webservices (http://opentox.org/dev/apis/api-1.2/dataset).
class Dataset
- def data_entries
- data_entries = []
- #pull
- #@reload = false
- begin
- self.[](RDF::OT.dataEntry).collect{|data_entry| data_entries << @rdf.to_hash[data_entry] }
- rescue
+ attr_accessor :features, :compounds, :data_entries
+
+ def initialize uri=nil, subjectid=nil
+ super uri, subjectid
+ @features = []
+ @compounds = []
+ @data_entries = []
+ append RDF.type, RDF::OT.OrderedDataset
+ end
+
+ def upload filename
+ file = File.new filename
+ RestClientWrapper.put(@uri, {:file => file}, {:subjectid => @subjectid})
+ end
+
+ def get
+ super
+ @features = []
+ @compounds = []
+ @data_entries = []
+ query = RDF::Query.new do
+ pattern [:uri, RDF.type, RDF::OT.OrderedDataset]
end
- begin
- # TODO: remove API 1.1
- self.[](RDF::OT1.dataEntry).collect{|data_entry| data_entries << @rdf.to_hash[data_entry] }
- rescue
+ if query.execute(@rdf).first # ordered dataset
+ query = RDF::Query.new do
+ pattern [:uri, RDF.type, RDF::OT.Compound]
+ pattern [:uri, RDF::OLO.index, :idx]
+ end
+ @compounds = query.execute(@rdf).sort_by{|s| s.idx}.collect{|s| OpenTox::Compound.new s.uri.to_s}
+ query = RDF::Query.new do
+ pattern [:uri, RDF.type, RDF::OT.Feature]
+ pattern [:uri, RDF::OLO.index, :idx]
+ end
+ @features = query.execute(@rdf).sort_by{|s| s.idx}.collect{|s| OpenTox::Feature.new(s.uri.to_s)}
+ numeric_features = @features.collect{|f| f.get; f[RDF.type].include? RDF::OT.NumericFeature}
+ @compounds.each_with_index do |compound,i|
+ query = RDF::Query.new do
+ pattern [:data_entry, RDF::OLO.index, i]
+ pattern [:data_entry, RDF::OT.values, :values]
+ pattern [:values, RDF::OT.feature, :feature]
+ pattern [:feature, RDF::OLO.index, :feature_idx]
+ pattern [:values, RDF::OT.value, :value]
+ end
+ values = query.execute(@rdf).sort_by{|s| s.feature_idx}.collect do |s|
+ numeric_features[s.feature_idx] ? s.value.to_s.to_f : s.value.to_s
+ end
+ @data_entries << values
+ end
+ else
+ query = RDF::Query.new do
+ pattern [:uri, RDF.type, RDF::OT.Feature]
+ end
+ @features = query.execute(@rdf).collect{|s| OpenTox::Feature.new(s.uri.to_s)}
+ query = RDF::Query.new do
+ pattern [:data_entry, RDF::OT.compound, :compound]
+ end
+ @compounds = query.execute(@rdf).sort_by{|s| s.data_entry}.collect{|s| OpenTox::Compound.new s.compound.to_s}
+ numeric_features = @features.collect{|f| f.get; f[RDF.type].include? RDF::OT.NumericFeature}
+ @compounds.each do |compound|
+ values = []
+ @features.each_with_index do |feature,i|
+ query = RDF::Query.new do
+ pattern [:data_entry, RDF::OT.compound, RDF::URI.new(compound.uri)]
+ pattern [:data_entry, RDF::OT.values, :values]
+ pattern [:values, RDF::OT.feature, RDF::URI.new(feature.uri)]
+ pattern [:values, RDF::OT.value, :value]
+ end
+ value = query.execute(@rdf).first.value.to_s
+ value = value.to_f if numeric_features[i]
+ values << value
+ end
+ @data_entries << values
+ end
end
- #@reload = true
- data_entries
end
- def compounds
- uri = File.join(@uri,"compounds")
- RestClientWrapper.get(uri,{},{:accept => "text/uri-list", :subjectid => @subjectid}).split("\n").collect{|uri| OpenTox::Compound.new uri}
+ def get_metadata
+ uri = File.join(@uri,"metadata")
+ begin
+ parse_ntriples RestClientWrapper.get(uri,{},{:accept => "text/plain", :subjectid => @subjectid})
+ rescue # fall back to rdfxml
+ parse_rdfxml RestClientWrapper.get(uri,{},{:accept => "application/rdf+xml", :subjectid => @subjectid})
+ end
+ metadata
end
- def features
- uri = File.join(@uri,"features")
- RestClientWrapper.get(uri,{},{:accept => "text/uri-list", :subjectid => @subjectid}).split("\n").collect{|uri| OpenTox::Feature.new uri}
+ def << data_entry
+ compound = data_entry.shift
+ bad_request_error "Dataset features are empty." unless features
+ bad_request_error "data_entry size does not match features size." unless data_entry.size == features.size
+ bad_request_error "First data_entry is not a OpenTox::Compound" unless compound.class == OpenTox::Compound
+ @compounds << compound
+ @data_entries << data_entry
end
+ RDF_FORMATS.each do |format|
+
+ # redefine rdf serialization methods
+ send :define_method, "to_#{format}".to_sym do
+ # TODO: check, might affect appending to unordered datasets
+ features.each_with_index do |feature,i|
+ @rdf << [RDF::URI.new(feature.uri), RDF::URI.new(RDF.type), RDF::URI.new(RDF::OT.Feature)]
+ @rdf << [RDF::URI.new(feature.uri), RDF::URI.new(RDF::OLO.index), RDF::Literal.new(i)]
+ end
+ compounds.each_with_index do |compound,i|
+ @rdf << [RDF::URI.new(compound.uri), RDF::URI.new(RDF.type), RDF::URI.new(RDF::OT.Compound)]
+ @rdf << [RDF::URI.new(compound.uri), RDF::URI.new(RDF::OLO.index), RDF::Literal.new(i)]
+ data_entry_node = RDF::Node.new
+ @rdf << [RDF::URI.new(@uri), RDF::URI.new(RDF::OT.dataEntry), data_entry_node]
+ @rdf << [data_entry_node, RDF::URI.new(RDF.type), RDF::URI.new(RDF::OT.DataEntry)]
+ @rdf << [data_entry_node, RDF::URI.new(RDF::OLO.index), RDF::Literal.new(i)]
+ @rdf << [data_entry_node, RDF::URI.new(RDF::OT.compound), RDF::URI.new(compound.uri)]
+ data_entries[i].each_with_index do |value,j|
+ value_node = RDF::Node.new
+ @rdf << [data_entry_node, RDF::URI.new(RDF::OT.values), value_node]
+ @rdf << [value_node, RDF::URI.new(RDF::OT.feature), RDF::URI.new(@features[j].uri)]
+ @rdf << [value_node, RDF::URI.new(RDF::OT.value), RDF::Literal.new(value)]
+ end
+ end
+ super()
+ end
+
+ end
end
end
diff --git a/lib/error.rb b/lib/error.rb
index 64b0fb1..51451e7 100644
--- a/lib/error.rb
+++ b/lib/error.rb
@@ -10,7 +10,7 @@ class RuntimeError
$logger.error "\n"+self.to_turtle
end
- # define to_ and self.from_ methods for various rdf formats
+ # define to_ methods for all RuntimeErrors and various rdf formats
RDF_FORMATS.each do |format|
send :define_method, "to_#{format}".to_sym do
@@ -22,7 +22,7 @@ class RuntimeError
subject = RDF::Node.new
writer << [subject, RDF.type, RDF::OT.ErrorReport]
writer << [subject, RDF::OT.actor, @uri.to_s]
- writer << [subject, RDF::OT.message, @message.to_s]
+ writer << [subject, RDF::OT.message, message.to_s]
writer << [subject, RDF::OT.statusCode, @http_code]
writer << [subject, RDF::OT.errorCode, self.class.to_s]
@@ -80,7 +80,6 @@ module OpenTox
class RestCallError < Error
attr_accessor :request#, :response
def initialize message, request, uri
- #def initialize request, response, message
@request = request
#@response = response
super 502, message, uri
diff --git a/lib/opentox-client.rb b/lib/opentox-client.rb
index 8c19225..ad7d3cc 100644
--- a/lib/opentox-client.rb
+++ b/lib/opentox-client.rb
@@ -2,6 +2,7 @@ require 'rubygems'
require "bundler/setup"
require 'rdf'
require 'rdf/raptor'
+require 'rdf/n3'
require "rest-client"
require 'uri'
require 'yaml'
@@ -14,6 +15,7 @@ require "securerandom"
RDF::OT = RDF::Vocabulary.new 'http://www.opentox.org/api/1.2#'
RDF::OT1 = RDF::Vocabulary.new 'http://www.opentox.org/api/1.1#'
RDF::OTA = RDF::Vocabulary.new 'http://www.opentox.org/algorithmTypes.owl#'
+RDF::OLO = RDF::Vocabulary.new 'http://purl.org/ontology/olo/core#'
CLASSES = ["Generic", "Compound", "Feature", "Dataset", "Algorithm", "Model", "Validation", "Task", "Investigation"]
RDF_FORMATS = [:rdfxml,:ntriples,:turtle]
diff --git a/lib/opentox.rb b/lib/opentox.rb
index b0c786a..9225bb0 100644
--- a/lib/opentox.rb
+++ b/lib/opentox.rb
@@ -14,7 +14,14 @@ module OpenTox
# @return [OpenTox] OpenTox object
def initialize uri=nil, subjectid=nil
@rdf = RDF::Graph.new
- uri ? @uri = uri.to_s.chomp : @uri = RDF::Node.uuid.to_s
+ if uri
+ @uri = uri.to_s.chomp
+ else
+ service = self.class.to_s.split('::').last.downcase
+ service_uri = eval("$#{service}[:uri]")
+ bad_request_error "$#{service}[:uri] variable not set. Please set $#{service}[:uri] or use an explicit uri as first constructor argument " unless service_uri
+ @uri = File.join service_uri, SecureRandom.uuid
+ end
append RDF.type, eval("RDF::OT."+self.class.to_s.split('::').last)
append RDF::DC.date, DateTime.now
@subjectid = subjectid
@@ -24,7 +31,6 @@ module OpenTox
# @return [Hash] Object metadata
def metadata
# return plain strings instead of RDF objects
- #puts @rdf.to_hash
@rdf.to_hash[RDF::URI.new(@uri)].inject({}) { |h, (predicate, values)| h[predicate.to_s] = values.collect{|v| v.to_s}; h }
end
@@ -32,6 +38,7 @@ module OpenTox
# @param [String] Predicate URI
# @return [Array, String] Predicate value(s)
def [](predicate)
+ return nil if metadata[predicate.to_s].nil?
metadata[predicate.to_s].size == 1 ? metadata[predicate.to_s].first : metadata[predicate.to_s]
end
@@ -55,30 +62,30 @@ module OpenTox
# Get object from webservice
def get
parse_ntriples RestClientWrapper.get(@uri,{},{:accept => "text/plain", :subjectid => @subjectid})
- rescue # fall back to rdfxml
- parse_rdfxml RestClientWrapper.get(@uri,{},{:accept => "application/rdf+xml", :subjectid => @subjectid})
+ #rescue # fall back to rdfxml
+ #parse_rdfxml RestClientWrapper.get(@uri,{},{:accept => "application/rdf+xml", :subjectid => @subjectid})
end
# Post object to webservice
def post service_uri
RestClientWrapper.post service_uri, to_ntriples, { :content_type => "text/plain", :subjectid => @subjectid}
- rescue # fall back to rdfxml
- RestClientWrapper.post service_uri, to_rdfxml, { :content_type => "application/rdf+xml", :subjectid => @subjectid}
+ #rescue # fall back to rdfxml
+ #RestClientWrapper.post service_uri, to_rdfxml, { :content_type => "application/rdf+xml", :subjectid => @subjectid}
end
# Save object at webservice
def put
append RDF::DC.modified, DateTime.now
- begin
+ #begin
RestClientWrapper.put @uri.to_s, self.to_ntriples, { :content_type => "text/plain", :subjectid => @subjectid}
- rescue # fall back to rdfxml
- RestClientWrapper.put @uri.to_s, self.to_rdfxml, { :content_type => "application/rdf+xml", :subjectid => @subjectid}
- end
+ #rescue # fall back to rdfxml
+ #RestClientWrapper.put @uri.to_s, self.to_rdfxml, { :content_type => "application/rdf+xml", :subjectid => @subjectid}
+ #end
end
# Delete object at webservice
def delete
- @response = RestClientWrapper.delete(@uri.to_s,nil,{:subjectid => @subjectid})
+ RestClientWrapper.delete(@uri.to_s,nil,{:subjectid => @subjectid})
end
RDF_FORMATS.each do |format|
@@ -100,42 +107,20 @@ module OpenTox
end
end
- # class methods
- module ClassMethods
-
- def all service_uri, subjectid=nil
- uris = RestClientWrapper.get(service_uri, {}, :accept => 'text/uri-list').split("\n").compact
- uris.collect{|uri| URI.task?(service_uri) ? from_uri(uri, subjectid, false) : from_uri(uri, subjectid)}
+ def to_turtle # redefine to use prefixes (not supported by RDF::Writer)
+ prefixes = {:rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#"}
+ ['OT', 'DC', 'XSD', 'OLO'].each{|p| prefixes[p.downcase.to_sym] = eval("RDF::#{p}.to_s") }
+ turtle = RDF::N3::Writer.for(:turtle).buffer(:prefixes => prefixes) do |writer|
+ @rdf.each{|statement| writer << statement}
end
+ end
- def from_file service_uri, filename, subjectid=nil
- file = File.new filename
- # sdf files are incorrectly detected
- file.mime_type = "chemical/x-mdl-sdfile" if File.extname(filename) == ".sdf"
- from_uri RestClientWrapper.post(service_uri, {:file => file}, {:subjectid => subjectid, :content_type => file.mime_type, :accept => "text/uri-list"}), subjectid
+ {:title => RDF::DC.title, :dexcription => RDF::DC.description}.each do |method,predicate|
+ send :define_method, method do
+ self.[](predicate)
end
-
- private
- def from_uri uri, subjectid=nil, wait=true
-
- uri.chomp!
- # TODO add waiting task
- if URI.task?(uri) and wait
- t = OpenTox::Task.new(uri)
- t.wait
- uri = t.resultURI
- end
-
- # guess class from uri, this is potentially unsafe, but polling metadata from large uri lists is way too slow (and not all service provide RDF.type in their metadata)
- result = CLASSES.collect{|s| s if uri =~ /#{s.downcase}/}.compact
- if result.size == 1
- klass = result.first
- else
- klass = OpenTox::Generic.new(uri)[RDF.type]
- internal_server_error "Cannot determine class from URI '#{uri} (Candidate classes are #{result.inspect}) or matadata." unless klass
- end
- # initialize with/without subjectid
- subjectid ? eval("#{self}.new(\"#{uri}\", \"#{subjectid}\")") : eval("#{self}.new(\"#{uri}\")")
+ send :define_method, "#{method}=" do |value|
+ self.[]=(predicate,value)
end
end
@@ -143,7 +128,12 @@ module OpenTox
CLASSES.each do |klass|
c = Class.new do
include OpenTox
- extend OpenTox::ClassMethods
+ #extend OpenTox::ClassMethods
+
+ def self.all service_uri, subjectid=nil
+ uris = RestClientWrapper.get(service_uri, {}, :accept => 'text/uri-list').split("\n").compact
+ uris.collect{|uri| self.new(uri, subjectid)}
+ end
end
OpenTox.const_set klass,c
end
diff --git a/opentox-client.gemspec b/opentox-client.gemspec
index 75d390b..f9d333a 100644
--- a/opentox-client.gemspec
+++ b/opentox-client.gemspec
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency "rest-client"
s.add_runtime_dependency "rdf"
s.add_runtime_dependency "rdf-raptor"
+ s.add_runtime_dependency 'rdf-n3'
s.add_runtime_dependency "open4"
# external requirements