opentox-ruby-api-wrapper 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -10,10 +10,10 @@ begin
10
10
  gem.email = "helma@in-silico.ch"
11
11
  gem.homepage = "http://github.com/helma/opentox-ruby-api-wrapper"
12
12
  gem.authors = ["Christoph Helma"]
13
- ["sinatra", "rest-client", "rack", "rack-contrib", "rack-flash", "emk-sinatra-url-for", "cehoffman-sinatra-respond_to", "dm-more", "dm-core", "sinatra-static-assets"].each do |dep|
13
+ ["sinatra", "rest-client", "rack", "rack-contrib", "rack-flash", "emk-sinatra-url-for", "sinatra-respond_to", "dm-more", "dm-core", "sinatra-static-assets","tmail"].each do |dep|
14
14
  gem.add_dependency dep
15
15
  end
16
- ['cucumber','jeweler', "thin"].each do |dep|
16
+ ['cucumber','jeweler'].each do |dep|
17
17
  gem.add_development_dependency dep
18
18
  end
19
19
  gem.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.0
1
+ 1.5.0
@@ -90,9 +90,9 @@ sudo apt-get install postgresql-server-dev-8.4 | tee -a $INSTALLLOG
90
90
  #echo "Installing gems jeweler sinatra emk-sinatra-url-for dm-core cehoffman-sinatra-respond_to rest-client rack-contrib thin cucumber datamapper data_objects do_sqlite3 rinruby"
91
91
  sudo gem install jeweler | tee -a $INSTALLLOG
92
92
  sudo gem install sinatra | tee -a $INSTALLLOG
93
- sudo gem install emk-sinatra-url-for -s http://gems.github.com | tee -a $INSTALLLOG
93
+ sudo gem install sinatra-url-for | tee -a $INSTALLLOG
94
94
  sudo gem install dm-core | tee -a $INSTALLLOG
95
- sudo gem install cehoffman-sinatra-respond_to -s http://gems.github.com | tee -a $INSTALLLOG
95
+ sudo gem install sinatra-respond_to | tee -a $INSTALLLOG
96
96
  sudo gem install rest-client | tee -a $INSTALLLOG
97
97
  sudo gem install rack-contrib | tee -a $INSTALLLOG
98
98
  sudo gem install thin | tee -a $INSTALLLOG
data/bin/yaml2owl.rb CHANGED
@@ -5,7 +5,7 @@ require 'opentox-ruby-api-wrapper'
5
5
  input = YAML.load_file(ARGV[0])
6
6
  dataset = OpenTox::Dataset.new
7
7
  dataset.title = input[:title]
8
- dataset.source = input[:source]
8
+ dataset.creator = input[:source]
9
9
  input[:data].each do |c,f|
10
10
  f.each do |k,v|
11
11
  v.each do |value|
data/lib/algorithm.rb CHANGED
@@ -21,8 +21,11 @@ module OpenTox
21
21
  def self.create_model(params)
22
22
  LOGGER.debug params
23
23
  LOGGER.debug File.basename(__FILE__) + ": creating model"
24
+ LOGGER.debug File.join(@@config[:services]["opentox-algorithm"], "lazar")
25
+ #resource = RestClient::Resource.new(File.join(@@config[:services]["opentox-algorithm"], "lazar"), :user => @@users[:users].keys[0], :password => @@users[:users].values[0], :content_type => "application/x-yaml")
24
26
  resource = RestClient::Resource.new(File.join(@@config[:services]["opentox-algorithm"], "lazar"), :user => @@users[:users].keys[0], :password => @@users[:users].values[0], :content_type => "application/x-yaml")
25
- @uri = resource.post(:dataset_uri => params[:dataset_uri], :feature_uri => params[:feature_uri], :feature_generation_uri => File.join(@@config[:services]["opentox-algorithm"], "fminer")).chomp
27
+ #@uri = resource.post(:dataset_uri => params[:dataset_uri], :feature_uri => params[:feature_uri], :feature_generation_uri => File.join(@@config[:services]["opentox-algorithm"], "fminer")).chomp
28
+ @uri = resource.post(:dataset_uri => params[:dataset_uri], :prediction_feature => params[:prediction_feature], :feature_generation_uri => File.join(@@config[:services]["opentox-algorithm"], "fminer")).body.chomp
26
29
  end
27
30
 
28
31
  def self.uri
data/lib/compound.rb CHANGED
@@ -18,18 +18,18 @@ module OpenTox
18
18
  @uri = File.join(@@config[:services]["opentox-compound"],URI.escape(@inchi))
19
19
  elsif params[:name]
20
20
  # paranoid URI encoding to keep SMILES charges and brackets
21
- @inchi = RestClient.get("#{@@cactus_uri}#{URI.encode(params[:name], Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}/stdinchi").chomp
21
+ @inchi = RestClientWrapper.get("#{@@cactus_uri}#{URI.encode(params[:name], Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}/stdinchi").chomp
22
22
  @uri = File.join(@@config[:services]["opentox-compound"],URI.escape(@inchi))
23
23
  elsif params[:uri]
24
24
  @uri = params[:uri]
25
25
  case params[:uri]
26
26
  when /ambit/ # Ambit does not deliver InChIs reliably
27
- smiles = RestClient.get @uri, :accept => 'chemical/x-daylight-smiles'
27
+ smiles = RestClientWrapper.get @uri, :accept => 'chemical/x-daylight-smiles'
28
28
  @inchi = obconversion(smiles,'smi','inchi')
29
29
  when /InChI/ # shortcut for IST services
30
30
  @inchi = params[:uri].sub(/^.*InChI/, 'InChI')
31
31
  else
32
- @inchi = RestClient.get @uri, :accept => 'chemical/x-inchi'
32
+ @inchi = RestClientWrapper.get @uri, :accept => 'chemical/x-inchi'
33
33
  end
34
34
  end
35
35
  end
@@ -44,7 +44,7 @@ module OpenTox
44
44
  end
45
45
 
46
46
  def image
47
- RestClient.get("#{@@cactus_uri}#{@inchi}/image")
47
+ RestClientWrapper.get("#{@@cactus_uri}#{@inchi}/image")
48
48
  end
49
49
 
50
50
  def image_uri
@@ -18,3 +18,11 @@ set :raise_errors, true
18
18
  end
19
19
 
20
20
  use Rack::ShowExceptions
21
+ #if MAIL
22
+ # use Rack::MailExceptions do |mail|
23
+ # mail.to 'helma@in-silico.ch'
24
+ # mail.subject '[ERROR] %s'
25
+ # mail.from "toxcreate@in-silico.ch"
26
+ # mail.smtp MAIL
27
+ # end
28
+ #end
data/lib/dataset.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  LOGGER.progname = File.expand_path(__FILE__)
2
2
 
3
3
  module OpenTox
4
-
4
+
5
5
  class Dataset
6
6
 
7
- attr_accessor :uri, :title, :source, :identifier, :data, :features, :compounds
7
+ attr_accessor :uri, :title, :creator, :data, :features, :compounds
8
8
 
9
9
  def initialize
10
10
  @data = {}
@@ -12,171 +12,175 @@ module OpenTox
12
12
  @compounds = []
13
13
  end
14
14
 
15
- def self.find(uri)
16
- YAML.load RestClient.get(uri, :accept => 'application/x-yaml').to_s
15
+ def self.find(uri, accept_header=nil)
16
+
17
+ unless accept_header
18
+ #if uri.match(@@config[:services]["opentox-dataset"]) || uri=~ /188.40.32.88/ || uri =~ /informatik/
19
+ if !@@config[:accept_headers]["opentox-dataset"].grep(/yaml/).empty?
20
+ accept_header = 'application/x-yaml'
21
+ else
22
+ accept_header = "application/rdf+xml"
23
+ end
24
+ end
25
+
26
+ case accept_header
27
+ when "application/x-yaml"
28
+ d = YAML.load RestClientWrapper.get(uri.to_s.strip, :accept => 'application/x-yaml').to_s
29
+ d.uri = uri unless d.uri
30
+ when "application/rdf+xml"
31
+ owl = OpenTox::Owl.from_uri(uri.to_s.strip, "Dataset")
32
+
33
+ d = Dataset.new
34
+ d.title = owl.get("title")
35
+ d.creator = owl.get("creator")
36
+ d.uri = owl.uri
37
+
38
+ # when loading a dataset from owl, only compound- and feature-uris are loaded
39
+ owl.load_dataset(d.compounds, d.features)
40
+ # all features are marked as dirty, loaded dynamically later
41
+ d.init_dirty_features(owl)
42
+
43
+ d.compounds.uniq!
44
+ d.features.uniq!
45
+ else
46
+ raise "cannot get datset with accept header: "+accept_header.to_s
47
+ end
48
+ return d
17
49
  end
50
+
51
+ # creates a new dataset, using only those compounsd specified in new_compounds
52
+ # returns uri of new dataset
53
+ def create_new_dataset( new_compounds, new_features, new_title, new_creator )
54
+
55
+ raise "no new compounds selected" unless new_compounds and new_compounds.size>0
56
+
57
+ # load require features
58
+ if ((defined? @dirty_features) && (@dirty_features - new_features).size > 0)
59
+ (@dirty_features - new_features).each{|f| load_feature_values(f)}
60
+ end
61
+
62
+ dataset = OpenTox::Dataset.new
63
+ dataset.title = new_title
64
+ dataset.creator = new_creator
65
+ dataset.features = new_features
66
+ dataset.compounds = new_compounds
67
+
68
+ # Copy dataset data for compounds and features
69
+ # PENDING: why storing feature values in an array?
70
+ new_compounds.each do |c|
71
+ data_c = []
72
+ @data[c].each do |d|
73
+ m = {}
74
+ new_features.each do |f|
75
+ m[f] = d[f]
76
+ end
77
+ data_c << m
78
+ end
79
+
80
+ dataset.data[c] = data_c
81
+ end
82
+ return dataset.save
83
+ end
84
+
85
+ # returns classification value
86
+ def get_predicted_class(compound, feature)
87
+ v = get_value(compound, feature)
88
+ if v.is_a?(Hash)
89
+ if v.has_key?(:classification)
90
+ return v[:classification]
91
+ else
92
+ return "no classification key"
93
+ end
94
+ else
95
+ raise "predicted class value is not a hash\n"+
96
+ "value "+v.to_s+"\n"+
97
+ "value-class "+v.class.to_s+"\n"+
98
+ "dataset "+@uri.to_s+"\n"+
99
+ "compound "+compound.to_s+"\n"+
100
+ "feature "+feature.to_s+"\n"
101
+ end
102
+
103
+ end
104
+
105
+ # returns prediction confidence if available
106
+ def get_prediction_confidence(compound, feature)
107
+ v = get_value(compound, feature)
108
+ if v.is_a?(Hash)
109
+ if v.has_key?(:confidence)
110
+ return v[:confidence].abs
111
+ else
112
+ # PENDING: return nil isntead of raising an exception
113
+ raise "no confidence key"
114
+ end
115
+ else
116
+ raise "prediction confidence value is not a hash value\n"+
117
+ "value "+v.to_s+"\n"+
118
+ "value-class "+v.class.to_s+"\n"+
119
+ "dataset "+@uri.to_s+"\n"+
120
+ "compound "+compound.to_s+"\n"+
121
+ "feature "+feature.to_s+"\n"
122
+ end
123
+ end
124
+
125
+ # return compound-feature value
126
+ def get_value(compound, feature)
127
+ if (defined? @dirty_features) && @dirty_features.include?(feature)
128
+ load_feature_values(feature)
129
+ end
130
+
131
+ v = @data[compound]
132
+ raise "no values for compound "+compound.to_s if v==nil
133
+ if v.is_a?(Array)
134
+ # PENDING: why using an array here?
135
+ v.each do |e|
136
+ if e.is_a?(Hash)
137
+ if e.has_key?(feature)
138
+ return e[feature]
139
+ end
140
+ else
141
+ raise "invalid internal value type"
142
+ end
143
+ end
144
+ raise "feature value no found: "+feature.to_s
145
+ else
146
+ raise "value is not an array\n"+
147
+ "value "+v.to_s+"\n"+
148
+ "value-class "+v.class.to_s+"\n"+
149
+ "dataset "+@uri.to_s+"\n"+
150
+ "compound "+compound.to_s+"\n"+
151
+ "feature "+feature.to_s+"\n"
152
+ end
153
+ end
18
154
 
155
+ # loads specified feature and removes dirty-flag, loads all features if feature is nil
156
+ def load_feature_values(feature=nil)
157
+ if feature
158
+ raise "feature already loaded" unless @dirty_features.include?(feature)
159
+ @owl.load_dataset_feature_values(@compounds, @data, feature)
160
+ @dirty_features.delete(feature)
161
+ else
162
+ @data = {}
163
+ @owl.load_dataset_feature_values(@compounds, @data)
164
+ @dirty_features.clear
165
+ end
166
+ end
19
167
 
20
168
  def save
169
+ # loads all features before loading
170
+ if ((defined? @dirty_features) && @dirty_features.size > 0)
171
+ load_feature_values()
172
+ end
173
+
21
174
  @features.uniq!
22
175
  @compounds.uniq!
23
- RestClient::Resource.new(@@config[:services]["opentox-dataset"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0]).post(self.to_yaml, :content_type => "application/x-yaml").chomp.to_s
24
- end
25
-
26
- =begin
27
- # create/add to entry from uris or Redland::Resources
28
- def add(compound,feature,value)
29
- compound = self.find_or_create_compound compound unless compound.class == Redland::Resource
30
- feature = self.find_or_create_feature feature unless feature.class == Redland::Resource
31
- data_entry = @model.subject OT['compound'], compound
32
- if data_entry.nil?
33
- data_entry = @model.create_resource
34
- dataset = @model.subject(RDF['type'],OT[self.owl_class])
35
- @model.add dataset, OT['dataEntry'], data_entry
36
- @model.add data_entry, RDF['type'], OT["DataEntry"]
37
- @model.add data_entry, OT['compound'], compound
38
- end
39
- values = @model.create_resource
40
- @model.add data_entry, OT['values'], values
41
- @model.add values, RDF['type'], OT['FeatureValue']
42
- @model.add values, OT['feature'], feature
43
- @model.add values, OT['value'], value.to_s
44
- end
45
-
46
- def add_tuple(compound,tuple)
47
- compound = self.find_or_create_compound compound unless compound.class == Redland::Resource
48
- data_entry = @model.subject OT['compound'], compound
49
- if data_entry.nil?
50
- data_entry = @model.create_resource
51
- dataset = @model.subject(RDF['type'],OT[self.owl_class])
52
- @model.add dataset, OT['dataEntry'], data_entry
53
- @model.add data_entry, RDF['type'], OT["DataEntry"]
54
- @model.add data_entry, OT['compound'], compound
55
- end
56
- @model.add data_entry, OT['values'], tuple
176
+ OpenTox::RestClientWrapper.post(@@config[:services]["opentox-dataset"],{:content_type => "application/x-yaml"},self.to_yaml).strip
57
177
  end
58
178
 
59
- def create_tuple(feature,t)
60
- feature = self.find_or_create_feature feature unless feature.class == Redland::Resource
61
- tuple = @model.create_resource
62
- @model.add tuple, RDF['type'], OT["Tuple"]
63
- @model.add tuple, OT['feature'], feature
64
- t.each do |name,value|
65
- f = self.find_or_create_feature name unless name.class == Redland::Resource
66
- complex_value = @model.create_resource
67
- feature = self.find_or_create_feature(name)
68
- @model.add tuple, OT['complexValue'], complex_value
69
- @model.add complex_value, RDF['type'], OT["FeatureValue"]
70
- @model.add complex_value, OT['feature'], f
71
- @model.add complex_value, OT['value'], value.to_s
72
- end
73
-
74
- tuple
75
- end
76
-
77
- # find or create a new compound and return the resource
78
- def find_or_create_compound(uri)
79
- compound = @model.subject(DC["identifier"], uri)
80
- if compound.nil?
81
- compound = @model.create_resource(uri)
82
- @model.add compound, RDF['type'], OT["Compound"]
83
- @model.add compound, DC["identifier"], uri
84
- end
85
- compound
86
- end
87
-
88
- # find or create a new feature and return the resource
89
- def find_or_create_feature(uri)
90
- feature = @model.subject(DC["identifier"], uri)
91
- if feature.nil?
92
- feature = @model.create_resource(uri)
93
- @model.add feature, RDF['type'], OT["Feature"]
94
- @model.add feature, DC["identifier"], uri
95
- @model.add feature, DC["title"], File.basename(uri).split(/#/)[1]
96
- @model.add feature, DC['source'], uri
97
- end
98
- feature
99
- end
100
-
101
- def self.create(data, content_type = 'application/rdf+xml')
102
- resource = RestClient::Resource.new(@@config[:services]["opentox-dataset"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
103
- uri = resource.post data, :content_type => content_type
104
- dataset = Dataset.new
105
- dataset.read uri.chomp.to_s
106
- dataset
107
- end
108
-
109
- def features
110
- features = []
111
- @model.subjects(RDF['type'], OT["Feature"]).each do |feature_node|
112
- features << @model.object(feature_node, DC["identifier"])#
113
- end
114
- features
115
- end
116
-
117
- def data
118
- data = {}
119
- @model.subjects(RDF['type'], OT['DataEntry']).each do |data_entry|
120
- compound_node = @model.object(data_entry, OT['compound'])
121
- compound_uri = @model.object(compound_node, DC['identifier']).to_s
122
- @model.find(data_entry, OT['values'], nil) do |s,p,values|
123
- feature_node = @model.object values, OT['feature']
124
- feature_uri = @model.object(feature_node, DC['identifier']).to_s.sub(/\^\^.*$/,'') # remove XML datatype
125
- type = @model.object(values, RDF['type'])
126
- if type == OT['FeatureValue']
127
- value = @model.object(values, OT['value']).to_s
128
- case value.to_s
129
- when TRUE_REGEXP # defined in environment.rb
130
- value = true
131
- when FALSE_REGEXP # defined in environment.rb
132
- value = false
133
- else
134
- LOGGER.warn compound_uri + " has value '" + value.to_s + "' for feature " + feature_uri
135
- value = nil
136
- end
137
- data[compound_uri] = {} unless data[compound_uri]
138
- data[compound_uri][feature_uri] = [] unless data[compound_uri][feature_uri]
139
- data[compound_uri][feature_uri] << value unless value.nil?
140
- elsif type == OT['Tuple']
141
- entry = {}
142
- data[compound_uri] = {} unless data[compound_uri]
143
- data[compound_uri][feature_uri] = [] unless data[compound_uri][feature_uri]
144
- @model.find(values, OT['complexValue'],nil) do |s,p,complex_value|
145
- name_node = @model.object complex_value, OT['feature']
146
- name = @model.object(name_node, DC['title']).to_s
147
- value = @model.object(complex_value, OT['value']).to_s
148
- v = value.sub(/\^\^.*$/,'') # remove XML datatype
149
- v = v.to_f if v.match(/^[\.|\d]+$/) # guess numeric datatype
150
- entry[name] = v
151
- end
152
- data[compound_uri][feature_uri] << entry
153
- end
154
- end
155
- end
156
- data
157
- end
158
-
159
- def compounds
160
- compounds = []
161
- @model.subjects(RDF['type'], OT["Compound"]).each do |compound_node|
162
- compounds << @model.object(compound_node, DC["identifier"]).to_s
163
- end
164
- compounds
165
- end
166
-
167
- # Delete a dataset
168
- def delete
169
- resource = RestClient::Resource.new(@uri, :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
170
- resource.delete
179
+ def init_dirty_features(owl)
180
+ @dirty_features = @features
181
+ @owl = owl
171
182
  end
183
+ end
172
184
 
173
- def to_owl
174
- end
175
-
176
- def from_owl
177
- end
178
-
179
- =end
180
- end
181
185
 
182
186
  end
data/lib/environment.rb CHANGED
@@ -40,10 +40,77 @@ if @@config[:database]
40
40
  end
41
41
  end
42
42
 
43
+ # mail for error messages
44
+ load File.join config_dir,"mail.rb" if File.exists?(File.join config_dir,"mail.rb")
45
+
46
+ # hack: store sinatra in global var to make url_for and halt methods accessible
47
+ before {$sinatra = self unless $sinatra}
48
+
49
+ class Sinatra::Base
50
+ # overwriting halt to log halts (!= 202)
51
+ def halt(*response)
52
+ LOGGER.error "halt "+response.first.to_s+" "+(response.size>1 ? response[1].to_s : "") if response and response.first and response.first >= 300
53
+ # orig sinatra code:
54
+ response = response.first if response.length == 1
55
+ throw :halt, response
56
+ end
57
+ end
58
+
43
59
  # logging
60
+ class MyLogger < Logger
61
+
62
+ def pwd
63
+ path = Dir.pwd.to_s
64
+ index = path.rindex(/\//)
65
+ return path if index==nil
66
+ path[(index+1)..-1]
67
+ end
68
+
69
+ def trace()
70
+ lines = caller(0)
71
+ n = 2
72
+ line = lines[n]
73
+
74
+ while (line =~ /spork.rb/ or line =~ /as_task/ or line =~ /environment.rb/)
75
+ n += 1
76
+ line = lines[n]
77
+ end
78
+
79
+ index = line.rindex(/\/.*\.rb/)
80
+ return line if index==nil
81
+ line[index..-1]
82
+ end
83
+
84
+ def format(msg)
85
+ pwd.ljust(18)+" :: "+msg.to_s+" :: "+trace+" :: "+ENV['REMOTE_ADDR'].to_s
86
+ end
87
+
88
+ def debug(msg)
89
+ super format(msg)
90
+ end
91
+
92
+ def info(msg)
93
+ super format(msg)
94
+ end
95
+
96
+ def warn(msg)
97
+ super format(msg)
98
+ end
99
+
100
+ def error(msg)
101
+ super format(msg)
102
+ end
103
+
104
+ end
105
+
106
+
44
107
  logfile = "#{LOG_DIR}/#{ENV["RACK_ENV"]}.log"
45
- LOGGER = Logger.new(logfile,'daily') # daily rotation
46
- LOGGER.level = Logger::DEBUG
108
+ LOGGER = MyLogger.new(logfile,'daily') # daily rotation
109
+
110
+ #LOGGER = MyLogger.new(STDOUT)
111
+ #LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S "
112
+
113
+ #LOGGER.level = Logger::DEBUG
47
114
 
48
115
  if File.exist?(user_file)
49
116
  @@users = YAML.load_file(user_file)
@@ -56,8 +123,7 @@ end
56
123
  begin
57
124
  0 < @@users[:users].keys.length
58
125
  rescue
59
- puts "Please edit #{user_file} and restart your application. Create at least one user with password."
60
- exit
126
+ raise "Please edit #{user_file} and restart your application. Create at least one user with password."
61
127
  end
62
128
 
63
129
  # RDF namespaces
@@ -65,7 +131,12 @@ RDF = Redland::Namespace.new 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
65
131
  OWL = Redland::Namespace.new 'http://www.w3.org/2002/07/owl#'
66
132
  DC = Redland::Namespace.new 'http://purl.org/dc/elements/1.1/'
67
133
  OT = Redland::Namespace.new 'http://www.opentox.org/api/1.1#'
134
+ XML = Redland::Namespace.new 'http://www.w3.org/2001/XMLSchema#'
68
135
 
69
136
  # Regular expressions for parsing classification data
70
- TRUE_REGEXP = /^(true|active|1)/
71
- FALSE_REGEXP = /^(false|inactive|0)/
137
+ TRUE_REGEXP = /^(true|active|$1^)/
138
+ FALSE_REGEXP = /^(false|inactive|$0^)/
139
+
140
+ # Task durations
141
+ DEFAULT_TASK_MAX_DURATION = @@config[:default_task_max_duration]
142
+ EXTERNAL_TASK_MAX_DURATION = @@config[:external_task_max_duration]
data/lib/features.rb ADDED
@@ -0,0 +1,15 @@
1
+ module OpenTox
2
+
3
+ module Feature
4
+
5
+ def self.domain( feature_uri )
6
+ #TODO
7
+ if feature_uri =~ /ambit/
8
+ return nil
9
+ else
10
+ return ["true", "false"]
11
+ end
12
+ end
13
+
14
+ end
15
+ end