Class: OpenTox::Owl

Inherits:
Object
  • Object
show all
Defined in:
lib/owl.rb

Constant Summary collapse

OBJECT_PROPERTY_CLASS =

contains all owl:ObjectProperty as keys, and the respective classes as value some object properties link to objects from different classes (e.g. “values can be ”Tuple“, or ”FeatureValue“) in this case, use set_object_property() (instead of set()) and specify class manually

{}
LITERAL_DATATYPE_STRING =

literals point to primitive values (not to other resources) the literal datatype is encoded via uri:

XML["string"].uri
LITERAL_DATATYPE_URI =
XML["anyURI"].uri
LITERAL_DATATYPE_FLOAT =
XML["float"].uri
LITERAL_DATATYPE_DOUBLE =
XML["double"].uri
LITERAL_DATATYPE_DATE =
XML["date"].uri
LITERAL_DATATYPE_BOOLEAN =
XML["boolean"].uri
LITERAL_DATATYPE_DATETIME =
XML["dateTime"].uri
LITERAL_DATATYPE_INTEGER =
XML["integer"].uri
LITERAL_TYPES =

list all literals (to distinguish from objectProperties) as keys, datatype as values (do not add dc-identifier, deprecated, object are identified via name=uri)

{}
PARSE_LITERAL_TYPE =

some literals can have different types, parse from ruby type

"PARSE_LITERAL_TYPE"
OWL_TYPE_LITERAL =

constants for often used redland-resources

OWL_TYPE_CLASS =
OWL_TYPE_OBJECT_PROPERTY =
RDF_TYPE =
@@nodes =

store redland:resources (=nodes) to:

  • separate namespaces (OT from RDF and DC)

  • save time, as generating resources is timeconsuming in redland

{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#modelObject

ot_class is the class of the object as string, e.g. “Model”,“Dataset”, … root_node is the root-object node in the rdf uri the uri of the object



191
192
193
# File 'lib/owl.rb', line 191

def model
  @model
end

#ot_classObject

ot_class is the class of the object as string, e.g. “Model”,“Dataset”, … root_node is the root-object node in the rdf uri the uri of the object



191
192
193
# File 'lib/owl.rb', line 191

def ot_class
  @ot_class
end

#root_nodeObject

ot_class is the class of the object as string, e.g. “Model”,“Dataset”, … root_node is the root-object node in the rdf uri the uri of the object



191
192
193
# File 'lib/owl.rb', line 191

def root_node
  @root_node
end

#uriObject

ot_class is the class of the object as string, e.g. “Model”,“Dataset”, … root_node is the root-object node in the rdf uri the uri of the object



191
192
193
# File 'lib/owl.rb', line 191

def uri
  @uri
end

Class Method Details

.create(ot_class, uri) ⇒ Object



202
203
204
205
206
207
208
209
210
# File 'lib/owl.rb', line 202

def self.create( ot_class, uri )
 
   owl = OpenTox::Owl.new
   owl.ot_class = ot_class
   owl.root_node = Redland::Resource.new(uri.to_s.strip)
   owl.set("type",owl.ot_class)
   owl.uri = uri
owl
end

.from_data(data, base_uri, ot_class) ⇒ Object

loads owl from data



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/owl.rb', line 213

def self.from_data(data, base_uri, ot_class)
  
  owl = OpenTox::Owl.new
  parser = Redland::Parser.new
  
  begin
    parser.parse_string_into_model(owl.model, data, base_uri)
    
    # now loading root_node and uri
    owl.model.find(nil, RDF_TYPE, owl.node(ot_class)) do |s,p,o|
      #LOGGER.debug "about statements "+s.to_s+" . "+p.to_s+" -> "+o.to_s
      is_root = true  
      owl.model.find(nil, nil, s) do |ss,pp,oo|
        is_root = false
        break
      end
      if is_root
        # handle error if root is already set
        raise "cannot derieve root object from rdf, more than one object specified" if owl.uri
        raise "illegal root node type, no uri specified\n"+data.to_s if s.blank?
        #store root note and uri
        owl.uri = s.uri.to_s
        owl.root_node = s
      end
    end
    
    # handle error if no root node was found
    unless owl.root_node
      types = []
      owl.model.find(nil, RDF_TYPE, nil){ |s,p,o| types << o.to_s }
      raise "root node for class '"+owl.node(ot_class).to_s+"' not found (available type nodes: "+types.inspect+")"
    end
    raise "no uri in rdf: '"+owl.uri+"'" unless owl.uri and Utils.is_uri?(owl.uri) 
    owl.ot_class = ot_class
    owl
  rescue => e
    RestClientWrapper.raise_uri_error(e.message, base_uri)
  end
end

.from_uri(uri, ot_class) ⇒ Object



253
254
255
# File 'lib/owl.rb', line 253

def self.from_uri(uri, ot_class)
    return from_data(RestClientWrapper.get(uri,:accept => "application/rdf+xml").to_s, uri, ot_class) 
end

Instance Method Details

#add_data_entries(compound_uri, features) ⇒ Object

PENDING move to dataset.rb this is for dataset.to_owl adds feautre value for a single compound



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/owl.rb', line 445

def add_data_entries(compound_uri,features)
  
  data_entry = { :compound => compound_uri }
  if features
    feature_values = []
    features.each do |f|
      f.each do |feature_uri,value|
        if value.is_a?(Hash)
          complex_values = []
          value.each do |uri,v|
            complex_values << { :feature => uri, :value => v }
          end
          feature_values << { :class => "Tuple", :feature => feature_uri, :complexValue => complex_values }
        else
          feature_values << { :class => "FeatureValue", :feature => feature_uri, :value => value }
        end
      end
    end
    data_entry[:values] = feature_values
  end
  set_data( :dataEntry => data_entry )
end

#get(property) ⇒ Object

returns the first object for subject:root_node and property (sufficient for accessing simple, root-node properties)



263
264
265
266
# File 'lib/owl.rb', line 263

def get( property )
  raise "uri is no prop, use owl.uri instead" if property=="uri"
  return get_value( @model.object( @root_node, node(property.to_s)) )
end

#get_nested(property_array) ⇒ Object

returns an array of objects (not only the first one) that fit for the property accepts array of properties to access not-root-node vaules i.e. validation_owl.get_nested( [ “confusionMatrix”, “confusionMatrixCell”, “confusionMatrixValue” ] returns an array of all confusionMatrixValues



272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/owl.rb', line 272

def get_nested( property_array )
  n = [ @root_node ] 
  property_array.each do |p|
    new_nodes = []
    n.each do |nn|
      @model.find( nn, node(p), nil ) do |sub,pred,obj|
        new_nodes << obj
      end
    end
    n = new_nodes
  end
  return n.collect{|nn| get_value( nn )}
end

#load_dataset(compounds, features) ⇒ Object

PENDING move to dataset.rb feature values are not loaded for performance reasons loading compounds and features into arrays that are given as params



471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/owl.rb', line 471

def load_dataset( compounds, features )
  
  @model.subjects(RDF_TYPE, node('Compound')).each do |compound|
    compounds << get_value(compound)
  end
  
  @model.subjects(RDF_TYPE, node('Feature')).each do |feature|
    feature_value_found=false
    @model.find(nil, node("feature"), feature) do |potential_feature_value,p,o|
      @model.find(nil, node("values"), potential_feature_value) do |s,p,o|
        feature_value_found=true
        break
      end
      break if feature_value_found
    end
    features << get_value(feature) if feature_value_found
  end
  LOGGER.debug "loaded "+compounds.size.to_s+" compounds and "+features.size.to_s+" features from dataset "+uri.to_s
end

#load_dataset_feature_values(compounds, data, feature_uris) ⇒ Object

PENDING move to dataset.rb loading feature values for the specified feature if feature is nil, all feature values are loaded

general remark on the rdf loading (found out with some testing): the search methods (subjects/find) are fast, the time consuming parts is creating resources, which cannot be avoided in general



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# File 'lib/owl.rb', line 498

def load_dataset_feature_values( compounds, data, feature_uris )
  
  raise "no feature-uri array" unless feature_uris.is_a?(Array)
  
   # values are stored in the data-hash, hash has a key for each compound
  compounds.each{|c| data[c] = [] unless data[c]}
  
  count = 0

  feature_uris.each do |feature_uri|
    LOGGER.debug("load feature values for feature: "+feature_uri )
    feature_node = Redland::Resource.new(feature_uri)
    
     # search for all feature_value_node with property 'ot_feature' and the feature we are looking for
     @model.find(nil, node('feature'), feature_node) do |feature_value_node,p,o|
  
      # get compound_uri by "backtracking" to values node (property is 'values'), then get compound_node via 'compound'
      value_nodes = @model.subjects(node('values'),feature_value_node)
      if value_nodes.size>0
        raise "more than one value node "+value_nodes.size.to_s if value_nodes.size>1
        value_node = value_nodes[0]
        
        compound_uri = get_value( @model.object(value_node, node('compound')) )
        unless compound_uri
           LOGGER.warn "'compound' missing for data-entry of feature "+feature_uri.to_s+
             ", value: "+@model.object(feature_value_node,node("value")).to_s
           next
        end
        
        value_node_type = @model.object(feature_value_node, RDF_TYPE)
        if (value_node_type == node('FeatureValue'))
          value_literal = @model.object( feature_value_node, node('value'))
          raise "plain feature value no literal: "+value_literal.to_s unless value_literal.is_a?(Redland::Literal)
          data[compound_uri] << {feature_uri => value_literal.get_value }
        elsif (value_node_type == node('Tuple'))
          complex_values = {}
          @model.find(feature_value_node,node('complexValue'),nil) do |p,s,complex_value|
            complex_value_type = @model.object(complex_value, RDF_TYPE)
            raise "complex feature value no feature value: "+complex_value.to_s unless complex_value_type==node('FeatureValue')
            complex_feature_uri = get_value(@model.object( complex_value, node('feature')))
            complex_value = @model.object( complex_value, node('value'))
            raise "complex value no literal: "+complex_value.to_s unless complex_value.is_a?(Redland::Literal)
            complex_values[ complex_feature_uri ] = complex_value.get_value
          end
          data[compound_uri] << { feature_uri => complex_values } if complex_values.size>0
        end
        count += 1
        LOGGER.debug "loading feature values ("+count.to_s+")" if (count%1000 == 0)
      end
    end
    LOGGER.debug "loaded "+count.to_s+" feature values for feature "+feature_node.to_s
  end
end

#new_class_node(name, current_node = @root_node) ⇒ Object

create a new (internal class) node with unique, uri-like name



409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/owl.rb', line 409

def new_class_node(name, current_node=@root_node)
  # to avoid anonymous nodes, make up uris for sub-objects
  # use counter to make sure each uri is unique
  # for example we will get ../confusion_matrix_cell/1, ../confusion_matrix_cell/2, ...
  count = 1
  while (true)
    res = Redland::Resource.new( File.join(current_node.uri.to_s,name.to_s,count.to_s) )
    match = false
    @model.find(nil, nil, res) do |s,p,o|
      match = true
      break
    end
    if match
      count += 1
    else
      break
    end
  end
  return res
end

#node(property) ⇒ Object



180
181
182
183
184
185
186
# File 'lib/owl.rb', line 180

def node(property)
  raise "can only create node for non-empty-string, but given "+property.class.to_s+" (value: "+
    property.to_s+")" unless property.is_a?(String) and property.size>0
  raise "dc[identifier] deprecated, use owl.uri" if property=="identifier"
  @@nodes[property] = OT[property] unless @@nodes.has_key?(property)
  return @@nodes[property]
end

#parameters=(params) ⇒ Object

for “backwards-compatiblity” better use directly: set_data( { “parameters” => [ { “title” => <t>, “paramScope” => <s>, “paramValue” => <v> } ] )



433
434
435
436
437
438
439
440
# File 'lib/owl.rb', line 433

def parameters=(params)
    
    converted_params = []
    params.each do |name, settings|
      converted_params << { :title => name, :paramScope => settings[:scope], :paramValue => settings[:value] }
    end
    set_data( :parameters => converted_params )
end

#rdfObject



257
258
259
# File 'lib/owl.rb', line 257

def rdf
@model.to_string
end

#set(predicate, object, current_node = @root_node) ⇒ Object

sets values of current_node (by default root_node)

note: this does not delete existing triples

  • there can be several triples for the same subject and predicate ( e.g. after set(“description”,“bla1”) and set(“description”,“bla2”)

    both descriptions are in the model, 
    but the get("description") will give you only one object (by chance)
    
  • this does not matter in pratice (only dataset uses this -> load_dataset-methods)

  • identical values appear only once in rdf



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/owl.rb', line 309

def set(predicate, object, current_node=@root_node )
  
  pred = predicate.to_s
  raise "uri is no prop, cannot set uri" if pred=="uri"
  raise "dc[identifier] deprecated, use owl.uri" if pred=="identifier"
  if (object.is_a?(Redland::Node) and object.blank?) or nil==object or object.to_s.size==0
    # set only not-nil values
    LOGGER.warn "skipping (not setting) empty value in rdf for property: '"+pred+"'"
    return 
  end
  
  if pred=="type"
    # predicate is type, set class of current node
    set_type(object, current_node)
  elsif LITERAL_TYPES.has_key?(pred)
    # predicate is literal
    set_literal(pred,object,LITERAL_TYPES[pred],current_node)
  elsif OBJECT_PROPERTY_CLASS.has_key?(pred)
    # predicte is objectProperty, object is another resource
    set_object_property(pred,object,OBJECT_PROPERTY_CLASS[pred],current_node)
  else
    raise "unkonwn rdf-property, please add: '"+pred+"' to OpenTox::OWL.OBJECT_PROPERTY_CLASS or OpenTox::OWL.LITERAL_TYPES"
  end
end

#set_data(hash, current_node = @root_node) ⇒ Object

this is (a recursiv method) to set nested-data via hashes (not only simple properties) example (for a dataset) { :description => “bla”,

:dataEntry => { :compound => "compound_uri", 
                :values => [ { :class => "FeatureValue"
                               :feature => "feat1", 
                               :value => 42 },
                             { :class => "FeatureValue"
                               :feature => "feat2", 
                               :value => 123 } ] } }


373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/owl.rb', line 373

def set_data(hash, current_node=@root_node)
  
  hash.each do |k,v|
    if v.is_a?(Hash)
      # value is again a hash
      prop = k.to_s
      
      # :class is a special key to specify the class value, if not defined in OBJECT_PROPERTY_CLASS
      object_class = v.has_key?(:class) ? v.delete(:class) : OBJECT_PROPERTY_CLASS[prop]
      raise "hash key must be a object-property, please add '"+prop.to_s+
        "' to OpenTox::OWL.OBJECT_PROPERTY_CLASS or specify :class value" unless object_class
        
      # the new node is a class node, to specify the uri of the resource use key :uri
      if v[:uri] 
        # identifier is either a specified uri
        class_node = Redland::Resource.new(v.delete(:uri))
      else
        # or a new uri, make up internal uri with increment
        class_node = new_class_node(object_class,current_node)
      end
      set_object_property(prop,class_node,object_class,current_node)
      # recursivly call set_data method with new node
      set_data(v,class_node)
    elsif v.is_a?(Array)
      # value is an array, each array element is added with current key as predicate
      v.each do |value|
        set_data( { k => value }, current_node )
      end
    else
      # neither hash nor array, call simple set-method
      set( k, v, current_node )
    end
  end
end

#set_literal(literal_name, literal_value, literal_datatype, current_node = @root_node) ⇒ Object

example-triples for setting description of a model: model_xy,ot:description,bla..bla^^xml:string ot:description,rdf:type,owl:Literal



345
346
347
348
# File 'lib/owl.rb', line 345

def set_literal(literal_name, literal_value, literal_datatype, current_node=@root_node)
  @model.add current_node, node(literal_name), Redland::Literal.create(literal_value, literal_datatype)
  @model.add node(literal_name), RDF_TYPE, OWL_TYPE_LITERAL
end

#set_object_property(property, object, object_class, current_node = @root_node) ⇒ Object

example-triples for setting algorithm property of a model: model_xy,ot:algorithm,algorihtm_xy ot:algorithm,rdf:type,owl:ObjectProperty algorihtm_xy,rdf:type,ot:Algorithm ot:Algorithm,rdf:type,owl:Class



355
356
357
358
359
360
361
# File 'lib/owl.rb', line 355

def set_object_property(property, object, object_class, current_node=@root_node)
  object_node = Redland::Resource.new(object)
  @model.add current_node, node(property), object_node
  @model.add node(property), RDF_TYPE, OWL_TYPE_OBJECT_PROPERTY
  @model.add object_node, RDF_TYPE, node(object_class)
  @model.add node(object_class), RDF_TYPE, OWL_TYPE_CLASS
end

#set_type(ot_class, current_node = @root_node) ⇒ Object

example-triples for setting rdf-type to model: model_xy,rdf:type,ot:Model ot:Model,rdf:type,owl:Class



337
338
339
340
# File 'lib/owl.rb', line 337

def set_type(ot_class, current_node=@root_node)
  @model.add current_node, RDF_TYPE, node(ot_class)
  @model.add node(ot_class), RDF_TYPE, OWL_TYPE_CLASS
end