opentox-client 0.0.2pre → 1.0.0pre1

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.
@@ -6,8 +6,9 @@ module OpenTox
6
6
  # @param [Hash] params Parameters for OpenTox model
7
7
  # @param [optional,OpenTox::Task] waiting_task (can be a OpenTox::Subtask as well), progress is updated accordingly
8
8
  # @return [text/uri-list] Task or resource URI
9
- def run params=nil
10
- post params, {:accept => 'text/uri-list'}
9
+ def run params=nil, wait=true
10
+ uri = RestClientWrapper.post @uri, params, { :content_type => "text/uri-list", :subjectid => @subjectid}
11
+ wait_for_task uri if wait
11
12
  end
12
13
 
13
14
  end
@@ -2,34 +2,39 @@ require 'rubygems'
2
2
  require "bundler/setup"
3
3
  require 'rdf'
4
4
  require 'rdf/raptor'
5
+ require 'rdf/n3'
5
6
  require "rest-client"
6
7
  require 'uri'
7
8
  require 'yaml'
8
9
  require 'json'
9
10
  require 'logger'
11
+ require "securerandom"
10
12
 
11
13
  # define constants and global variables
12
14
  #TODO: switch services to 1.2
13
15
  RDF::OT = RDF::Vocabulary.new 'http://www.opentox.org/api/1.2#'
14
16
  RDF::OT1 = RDF::Vocabulary.new 'http://www.opentox.org/api/1.1#'
15
17
  RDF::OTA = RDF::Vocabulary.new 'http://www.opentox.org/algorithmTypes.owl#'
18
+ RDF::OLO = RDF::Vocabulary.new 'http://purl.org/ontology/olo/core#'
16
19
 
17
- #CLASSES = ["Compound", "Feature", "Dataset", "Algorithm", "Model", "Validation", "Task", "ErrorReport", "Investigation"]
18
20
  CLASSES = ["Generic", "Compound", "Feature", "Dataset", "Algorithm", "Model", "Validation", "Task", "Investigation"]
19
21
  RDF_FORMATS = [:rdfxml,:ntriples,:turtle]
20
- $default_rdf = "application/rdf+xml"
21
22
 
22
23
  # Regular expressions for parsing classification data
23
24
  TRUE_REGEXP = /^(true|active|1|1.0|tox|activating|carcinogen|mutagenic)$/i
24
25
  FALSE_REGEXP = /^(false|inactive|0|0.0|low tox|deactivating|non-carcinogen|non-mutagenic)$/i
25
26
 
26
- require File.join(File.dirname(__FILE__),"overwrite.rb")
27
- require File.join(File.dirname(__FILE__),"error.rb")
28
- require File.join(File.dirname(__FILE__),"rest-client-wrapper.rb")
29
- require File.join(File.dirname(__FILE__),"authorization.rb")
30
- require File.join(File.dirname(__FILE__),"policy.rb")
31
- require File.join(File.dirname(__FILE__),"otlogger.rb") # avoid require conflicts with logger
32
- require File.join(File.dirname(__FILE__),"opentox.rb")
33
- require File.join(File.dirname(__FILE__),"task.rb")
34
- require File.join(File.dirname(__FILE__),"compound.rb")
35
- require File.join(File.dirname(__FILE__),"dataset.rb")
27
+ [
28
+ "overwrite.rb",
29
+ "rest-client-wrapper.rb",
30
+ "error.rb",
31
+ "authorization.rb",
32
+ "policy.rb",
33
+ "otlogger.rb",
34
+ "opentox.rb",
35
+ "task.rb",
36
+ "compound.rb",
37
+ "dataset.rb",
38
+ "model.rb",
39
+ ].each{ |f| require File.join(File.dirname(__FILE__),f) }
40
+
@@ -4,7 +4,7 @@ $logger.level = Logger::DEBUG
4
4
 
5
5
  module OpenTox
6
6
 
7
- attr_accessor :uri, :subjectid, :rdf, :response, :reload
7
+ attr_accessor :uri, :subjectid, :rdf
8
8
 
9
9
  # Ruby interface
10
10
 
@@ -13,134 +13,156 @@ module OpenTox
13
13
  # @param [optional,String] subjectid
14
14
  # @return [OpenTox] OpenTox object
15
15
  def initialize uri=nil, subjectid=nil
16
- @uri = uri.to_s.chomp
17
- @subjectid = subjectid
18
- @reload = true
19
16
  @rdf = RDF::Graph.new
17
+ if uri
18
+ @uri = uri.to_s.chomp
19
+ else
20
+ service = self.class.to_s.split('::').last.downcase
21
+ service_uri = eval("$#{service}[:uri]")
22
+ bad_request_error "$#{service}[:uri] variable not set. Please set $#{service}[:uri] or use an explicit uri as first constructor argument " unless service_uri
23
+ @uri = File.join service_uri, SecureRandom.uuid
24
+ end
25
+ append RDF.type, eval("RDF::OT."+self.class.to_s.split('::').last)
26
+ append RDF::DC.date, DateTime.now
27
+ @subjectid = subjectid
20
28
  end
21
29
 
22
- # Load metadata from service
23
- def pull
24
- # TODO generic method for all formats
25
- parse_rdfxml RestClientWrapper.get(@uri,{},{:accept => $default_rdf, :subjectid => @subjectid})
26
- end
27
-
28
- # Get object metadata
29
- # @return [Hash] Metadata
30
+ # Object metadata
31
+ # @return [Hash] Object metadata
30
32
  def metadata
31
- pull if @reload # force update
32
- @rdf.to_hash[RDF::URI.new(@uri)]
33
+ # return plain strings instead of RDF objects
34
+ @rdf.to_hash[RDF::URI.new(@uri)].inject({}) { |h, (predicate, values)| h[predicate.to_s] = values.collect{|v| v.to_s}; h }
33
35
  end
34
36
 
35
- # Get metadata values
36
- # @param [RDF] Key from RDF Vocabularies
37
- # @return [Array] Values for supplied key
38
- def [](key)
39
- pull if @reload # force update
40
- result = @rdf.query([RDF::URI.new(@uri),key,nil]).collect{|statement| statement.object}
41
- # TODO: convert to OpenTox objects??
42
- return nil if result and result.empty?
43
- return result.first.to_s if result.size == 1
44
- return result.collect{|r| r.to_s}
45
- result
37
+ # Metadata values
38
+ # @param [String] Predicate URI
39
+ # @return [Array, String] Predicate value(s)
40
+ def [](predicate)
41
+ return nil if metadata[predicate.to_s].nil?
42
+ metadata[predicate.to_s].size == 1 ? metadata[predicate.to_s].first : metadata[predicate.to_s]
46
43
  end
47
44
 
48
- # Save object at service
49
- def save
50
- #TODO: dynamic assignment
51
- post self.to_rdfxml, { :content_type => $default_rdf}
45
+ # Set object metadata
46
+ # @param [String] Predicate URI
47
+ # @param [Array, String] Predicate value(s)
48
+ def []=(predicate,values)
49
+ @rdf.delete [RDF::URI.new(@uri.to_s),RDF::URI.new(predicate.to_s),nil]
50
+ append predicate.to_s, values
52
51
  end
53
52
 
54
- RDF_FORMATS.each do |format|
55
-
56
- # rdf parse methods for all formats e.g. parse_rdfxml
57
- send :define_method, "parse_#{format}".to_sym do |rdf|
58
- @rdf = RDF::Graph.new
59
- RDF::Reader.for(format).new(rdf) do |reader|
60
- reader.each_statement{ |statement| @rdf << statement }
61
- end
53
+ def parameters
54
+ params = {}
55
+ query = RDF::Query.new({
56
+ :parameter => {
57
+ RDF.type => RDF::OT.Parameter,
58
+ :property => :value,
59
+ }
60
+ })
61
+ query.execute(@rdf).each do |solution|
62
+ params[solution.parameter] = {} unless params[solution.parameter]
63
+ params[solution.parameter][solution.property.to_s] = solution.value.to_s
62
64
  end
65
+ params.values
66
+ end
63
67
 
64
- # rdf serialization methods for all formats e.g. to_rdfxml
65
- send :define_method, "to_#{format}".to_sym do
66
- rdf = RDF::Writer.for(format).buffer do |writer|
67
- @rdf.each{|statement| writer << statement}
68
- end
69
- rdf
68
+ def parameters=(parameters)
69
+ parameters.each do |param|
70
+ p_node = RDF::Node.new
71
+ @rdf << [RDF::URI.new(@uri), RDF::OT.parameters, p_node]
72
+ @rdf << [p_node, RDF.type, RDF::OT.Parameter]
73
+ param.each{ |p,o| @rdf << [p_node, p, o] }
70
74
  end
71
75
  end
72
76
 
73
- def to_yaml
74
- @rdf.to_hash.to_yaml
77
+ # Append object metadata
78
+ # @param [String] Predicate URI
79
+ # @param [Array, String] Predicate value(s)
80
+ def append(predicate,values)
81
+ uri = RDF::URI.new @uri
82
+ predicate = RDF::URI.new predicate
83
+ [values].flatten.each { |value| @rdf << [uri, predicate, value] }
75
84
  end
76
85
 
77
- def to_json
78
- to_hash.to_json
86
+ # Get object from webservice
87
+ def get mime_type="text/plain"
88
+ response = RestClientWrapper.get(@uri,{},{:accept => mime_type, :subjectid => @subjectid})
89
+ if URI.task?(response)
90
+ wait_for_task response
91
+ response = RestClientWrapper.get(t.resultURI,{},{:accept => mime_type, :subjectid => @subjectid})
92
+ end
93
+ parse_ntriples response if mime_type == "text/plain"
94
+ parse_rdfxml response if mime_type == "application/rdf+xml"
79
95
  end
80
96
 
81
- # REST API
82
- def get headers={}
83
- headers[:subjectid] ||= @subjectid
84
- headers[:accept] ||= 'application/rdf+xml'
85
- @response = RestClientWrapper.get @uri, {}, headers
97
+ # Post object to webservice
98
+ def post service_uri, wait=true
99
+ # TODO: RDFXML
100
+ uri = RestClientWrapper.post service_uri, to_ntriples, { :content_type => "text/plain", :subjectid => @subjectid}
101
+ wait_for_task uri if wait
86
102
  end
87
103
 
88
- def post payload={}, headers={}
89
- headers[:subjectid] ||= @subjectid
90
- headers[:accept] ||= 'application/rdf+xml'
91
- @response = RestClientWrapper.post(@uri.to_s, payload, headers)
104
+ # Save object at webservice
105
+ def put wait=true
106
+ # TODO: RDFXML
107
+ append RDF::DC.modified, DateTime.now
108
+ uri = RestClientWrapper.put @uri.to_s, self.to_ntriples, { :content_type => "text/plain", :subjectid => @subjectid}
109
+ wait_for_task uri if wait
92
110
  end
93
111
 
94
- def put payload={}, headers={}
95
- headers[:subjectid] ||= @subjectid
96
- headers[:accept] ||= 'application/rdf+xml'
97
- @response = RestClientWrapper.put(@uri.to_s, payload, headers)
112
+ # Delete object at webservice
113
+ def delete
114
+ RestClientWrapper.delete(@uri.to_s,nil,{:subjectid => @subjectid})
98
115
  end
99
116
 
100
- def delete headers={}
101
- headers[:subjectid] ||= @subjectid
102
- @response = RestClientWrapper.delete(@uri.to_s,nil,headers)
117
+ def wait_for_task uri
118
+ if URI.task?(uri)
119
+ t = OpenTox::Task.new uri
120
+ t.wait
121
+ unless t.completed?
122
+ #TODO raise correct error
123
+ #internal_server_error "Task #{uri} failed with #{$!.inspect}"
124
+ end
125
+ uri = t.resultURI
126
+ end
127
+ uri
103
128
  end
104
129
 
105
- # class methods
106
- module ClassMethods
130
+ RDF_FORMATS.each do |format|
107
131
 
108
- def all service_uri, subjectid=nil
109
- uris = RestClientWrapper.get(service_uri, {}, :accept => 'text/uri-list').split("\n").compact
110
- uris.collect{|uri| URI.task?(service_uri) ? from_uri(uri, subjectid, false) : from_uri(uri, subjectid)}
132
+ # rdf parse methods for all formats e.g. parse_rdfxml
133
+ send :define_method, "parse_#{format}".to_sym do |rdf|
134
+ @rdf = RDF::Graph.new
135
+ RDF::Reader.for(format).new(rdf) do |reader|
136
+ reader.each_statement{ |statement| @rdf << statement }
137
+ end
111
138
  end
112
139
 
113
- def create service_uri, subjectid=nil
114
- uri = RestClientWrapper.post(service_uri, {}, {:accept => 'text/uri-list', :subjectid => subjectid})
115
- URI.task?(service_uri) ? from_uri(uri, subjectid, false) : from_uri(uri, subjectid)
140
+ # rdf serialization methods for all formats e.g. to_rdfxml
141
+ send :define_method, "to_#{format}".to_sym do
142
+ RDF::Writer.for(format).buffer do |writer|
143
+ @rdf.each{|statement| writer << statement}
144
+ end
116
145
  end
146
+ end
117
147
 
118
- def from_file service_uri, filename, subjectid=nil
119
- file = File.new filename
120
- from_uri RestClientWrapper.post(service_uri, {:file => file}, {:subjectid => subjectid, :content_type => file.mime_type, :accept => "text/uri-list"}), subjectid
148
+ def to_turtle # redefine to use prefixes (not supported by RDF::Writer)
149
+ prefixes = {:rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#"}
150
+ ['OT', 'DC', 'XSD', 'OLO'].each{|p| prefixes[p.downcase.to_sym] = eval("RDF::#{p}.to_s") }
151
+ RDF::N3::Writer.for(:turtle).buffer(:prefixes => prefixes) do |writer|
152
+ @rdf.each{|statement| writer << statement}
121
153
  end
154
+ end
122
155
 
123
- private
124
- def from_uri uri, subjectid=nil, wait=true
125
-
126
- uri.chomp!
127
- # TODO add waiting task
128
- if URI.task? uri and wait
129
- t = OpenTox::Task.new(uri)
130
- t.wait
131
- uri = t.resultURI
132
- end
133
-
134
- # 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)
135
- result = CLASSES.collect{|s| s if uri =~ /#{s.downcase}/}.compact
136
- if result.size == 1
137
- klass = result.first
138
- else
139
- klass = OpenTox::Generic.new(uri)[RDF.type]
140
- internal_server_error "Cannot determine class from URI '#{uri} (Candidate classes are #{result.inspect}) or matadata." unless klass
141
- end
142
- # initialize with/without subjectid
143
- subjectid ? eval("#{self}.new(\"#{uri}\", \"#{subjectid}\")") : eval("#{self}.new(\"#{uri}\")")
156
+ {
157
+ :title => RDF::DC.title,
158
+ :dexcription => RDF::DC.description,
159
+ :type => RDF.type
160
+ }.each do |method,predicate|
161
+ send :define_method, method do
162
+ self.[](predicate)
163
+ end
164
+ send :define_method, "#{method}=" do |value|
165
+ self.[]=(predicate,value)
144
166
  end
145
167
  end
146
168
 
@@ -148,7 +170,11 @@ module OpenTox
148
170
  CLASSES.each do |klass|
149
171
  c = Class.new do
150
172
  include OpenTox
151
- extend OpenTox::ClassMethods
173
+
174
+ def self.all service_uri, subjectid=nil
175
+ uris = RestClientWrapper.get(service_uri, {}, :accept => 'text/uri-list').split("\n").compact
176
+ uris.collect{|uri| self.new(uri, subjectid)}
177
+ end
152
178
  end
153
179
  OpenTox.const_set klass,c
154
180
  end
@@ -1,3 +1,21 @@
1
+ class Object
2
+ # An object is blank if it's false, empty, or a whitespace string.
3
+ # For example, "", " ", +nil+, [], and {} are all blank.
4
+ def blank?
5
+ respond_to?(:empty?) ? empty? : !self
6
+ end
7
+
8
+ def numeric?
9
+ true if Float(self) rescue false
10
+ end
11
+ end
12
+
13
+ module Enumerable
14
+ def duplicates
15
+ inject({}) {|h,v| h[v]=h[v].to_i+1; h}.reject{|k,v| v==1}.keys
16
+ end
17
+ end
18
+
1
19
  class String
2
20
  def underscore
3
21
  self.gsub(/::/, '/').
@@ -68,11 +86,10 @@ module Kernel
68
86
  stdout = stdout_stream.read
69
87
  stderr = stderr_stream.read
70
88
  end
71
- internal_server_error "`" + cmd + "` failed.\n" + stdout + stderr if !status.success?
89
+ internal_server_error "`" + cmd + "` failed.\n" + stdout + stderr unless status.success?
72
90
  return stdout
73
91
  rescue
74
- internal_server_error $!.message
92
+ internal_server_error $!.message
75
93
  end
76
94
 
77
95
  end
78
-
@@ -58,7 +58,7 @@ module OpenTox
58
58
  when "guest", "anonymous" then "default_guest_policy"
59
59
  else "default_policy"
60
60
  end
61
- xml = File.read(File.join(File.dirname(__FILE__), "templates/#{template}.xml"))
61
+ xml = get_xml_template(template)
62
62
  self.load_xml(xml)
63
63
  datestring = Time.now.strftime("%Y-%m-%d-%H-%M-%S-x") + rand(1000).to_s
64
64
 
@@ -78,6 +78,10 @@ module OpenTox
78
78
  return true
79
79
  end
80
80
 
81
+ def get_xml_template(template)
82
+ File.read(File.join(File.dirname(__FILE__), "templates/#{template}.xml"))
83
+ end
84
+
81
85
  #loads a xml template
82
86
  def load_xml(xml)
83
87
  rexml = REXML::Document.new(xml)
@@ -247,19 +251,22 @@ module OpenTox
247
251
  end
248
252
 
249
253
  # helper method sets value and type to opentox LDAP Distinguished Name (DN) of a user
254
+ # @param [String]Username set a username into LDAP DN
250
255
  def set_ot_user(username)
251
256
  self.value = "uid=#{username},ou=people,dc=opentox,dc=org"
252
257
  self.type = "LDAPUsers"
253
258
  true
254
259
  end
255
260
 
261
+ # @param [String]Username set a groupname into LDAP DN
256
262
  def set_ot_group(groupname)
257
263
  self.value = "cn=#{groupname},ou=groups,dc=opentox,dc=org"
258
264
  self.type = "LDAPGroups"
259
265
  true
260
266
  end
261
267
 
262
- #rule inside a policy
268
+ # policyrule
269
+ # sets the permission for REST actions (GET, POST, PUT, DELETE) of a specific URI to allow/deny/nil
263
270
  class Rule
264
271
 
265
272
  attr_accessor :name, :uri, :get, :post, :put, :delete, :read, :readwrite
@@ -293,14 +300,18 @@ module OpenTox
293
300
  @put = check_value(value, @put)
294
301
  end
295
302
 
303
+ # read getter method
296
304
  def read
297
305
  return true if @get == "allow" && (@put == "deny" || !@put) && (@post == "deny" || !@post)
298
306
  end
299
307
 
308
+ # readwrite getter method
300
309
  def readwrite
301
310
  return true if @get == "allow" && @put == "allow" && @post == "allow"
302
311
  end
303
312
 
313
+ # Set(true case) or remove read(GET=allow) permissions.
314
+ # @param [Boolean]value (true,false)
304
315
  def read=(value)
305
316
  if value
306
317
  @get = "allow"; @put = nil; @post = nil
@@ -309,6 +320,8 @@ module OpenTox
309
320
  end
310
321
  end
311
322
 
323
+ # Set(true case) or remove readwrite(GET=allow,POST=allow,PUT=allow) permissions.
324
+ # @param [Boolean]value (true,false)
312
325
  def readwrite=(value)
313
326
  if value
314
327
  @get = "allow"; @put = "allow"; @post = "allow"
@@ -324,6 +337,8 @@ module OpenTox
324
337
  end
325
338
  end
326
339
 
340
+ # Subject of a policy
341
+ # name(subjectname), type('LDAPUsers' or 'LDAPGroups'), value(LDAP DN e.G.:'uid=guest,ou=people,dc=opentox,dc=org')
327
342
  class Subject
328
343
 
329
344
  attr_accessor :name, :type, :value