ols 0.0.1
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/.gitignore +5 -0
- data/Gemfile +4 -0
- data/History.txt +6 -0
- data/README.rdoc +117 -0
- data/Rakefile +12 -0
- data/lib/ols.rb +422 -0
- data/lib/ols/version.rb +3 -0
- data/ols.gemspec +39 -0
- data/test/test_helper.rb +34 -0
- data/test/test_ols.rb +124 -0
- metadata +160 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/History.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
= OLS
|
2
|
+
|
3
|
+
http://github.com/dazoakley/ols
|
4
|
+
|
5
|
+
OLS provides a simple interface to the EBI's Ontology Lookup Service (http://www.ebi.ac.uk/ontology-lookup/).
|
6
|
+
It provides an easy lookup of ontology terms and automagically builds up ontology trees using RubyTree
|
7
|
+
(http://rubytree.rubyforge.org/) as a base library.
|
8
|
+
|
9
|
+
<b>PLEASE NOTE</b>: The current version of this gem requires a local install of the OLS database running on MySQL.
|
10
|
+
Please see http://www.ebi.ac.uk/ontology-lookup/databaseExport.do I will update the code in the future
|
11
|
+
to run off the soap service.
|
12
|
+
|
13
|
+
== Install
|
14
|
+
|
15
|
+
gem install ols
|
16
|
+
|
17
|
+
== Usage
|
18
|
+
|
19
|
+
Include the module in your code:
|
20
|
+
|
21
|
+
require 'rubygems'
|
22
|
+
require 'ols'
|
23
|
+
|
24
|
+
Then, to lookup an ontology term:
|
25
|
+
|
26
|
+
ont = OLS::OntologyTerm.new('EMAP:3018')
|
27
|
+
|
28
|
+
This will create a simple tree object for the EMAP term EMAP:3018
|
29
|
+
|
30
|
+
ont.name # => "EMAP:3018"
|
31
|
+
ont.term # => "EMAP:3018"
|
32
|
+
|
33
|
+
ont.content # => "TS18,nose"
|
34
|
+
ont.term_name # => "TS18,nose"
|
35
|
+
|
36
|
+
Find out about your ontology term
|
37
|
+
|
38
|
+
ont.is_root? # => false
|
39
|
+
ont.is_leaf? # => false
|
40
|
+
|
41
|
+
<b>NOTE</b>: OntologyTerm's are lazily loaded, so upon initial build the OntologyTerm is just a single node in a
|
42
|
+
tree - no parents, no children. See here (note the "Total Nodes Loaded" count):
|
43
|
+
|
44
|
+
ont.to_s # => "Term: EMAP:3018 Term Name: TS18,nose Root Term?: false Leaf Node?: false Total Nodes Loaded: 1"
|
45
|
+
|
46
|
+
The tree for the term is built up (parents) and down (children) as it is requested:
|
47
|
+
|
48
|
+
ont.parentage # => Array of all parent terms (these will be loaded into the tree now)
|
49
|
+
ont.children # => Array of all direct child terms (these will also be loaded into the tree now)
|
50
|
+
ont.to_s # => "Term: EMAP:3018 Term Name: TS18,nose Root Term?: false Leaf Node?: false Total Nodes Loaded: 4"
|
51
|
+
|
52
|
+
Alternatively, if you want to force load the tree:
|
53
|
+
|
54
|
+
ont.buld_tree # => Will load both parents and children into the tree
|
55
|
+
|
56
|
+
Find out more about your tree:
|
57
|
+
|
58
|
+
ont.root # => Gives you the root node - in this case: "EMAP:0"
|
59
|
+
ont.all_child_terms # => An array of all child ontology terms
|
60
|
+
ont.all_child_name # => An array of all child ontology term names
|
61
|
+
|
62
|
+
For more information on general usage, take a look in the test suite. Also, OLS::OntologyTerm is a child object of
|
63
|
+
Tree::TreeNode (from the rubytree gem - http://rubytree.rubyforge.org/) and as such inherits all of its methods.
|
64
|
+
|
65
|
+
== Database Connection Details
|
66
|
+
|
67
|
+
The default database connection details are:
|
68
|
+
|
69
|
+
{
|
70
|
+
:database => 'ols',
|
71
|
+
:host => '127.0.0.1',
|
72
|
+
:port => 3306,
|
73
|
+
:user => 'ols',
|
74
|
+
:password => 'ols'
|
75
|
+
}
|
76
|
+
|
77
|
+
If your local copy of the OLS database requires different details, simply do the following once in your code
|
78
|
+
(substituting in your connection details) before any calls to OLS::OntologyTerm are made:
|
79
|
+
|
80
|
+
OLS.db_connection_details = {
|
81
|
+
:database => 'my_ols',
|
82
|
+
:host => 'ols.example.com',
|
83
|
+
:port => 3307,
|
84
|
+
:user => 'user1',
|
85
|
+
:password => 'pass1'
|
86
|
+
}
|
87
|
+
|
88
|
+
== Meta
|
89
|
+
|
90
|
+
Written by Darren Oakley (daz dot oakley at gmail dot com)
|
91
|
+
|
92
|
+
http://github.com/dazoakley/ols
|
93
|
+
|
94
|
+
== License
|
95
|
+
|
96
|
+
(The MIT License)
|
97
|
+
|
98
|
+
Copyright (c) 2011 Darren Oakley
|
99
|
+
|
100
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
101
|
+
a copy of this software and associated documentation files (the
|
102
|
+
'Software'), to deal in the Software without restriction, including
|
103
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
104
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
105
|
+
permit persons to whom the Software is furnished to do so, subject to
|
106
|
+
the following conditions:
|
107
|
+
|
108
|
+
The above copyright notice and this permission notice shall be
|
109
|
+
included in all copies or substantial portions of the Software.
|
110
|
+
|
111
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
112
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
113
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
114
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
115
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
116
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
117
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'shoulda'
|
5
|
+
require 'rake/testtask'
|
6
|
+
|
7
|
+
desc 'Run the test suite under /test'
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << 'test'
|
10
|
+
t.test_files = FileList['test/test*.rb']
|
11
|
+
t.verbose = true
|
12
|
+
end
|
data/lib/ols.rb
ADDED
@@ -0,0 +1,422 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'tree'
|
4
|
+
require 'sequel'
|
5
|
+
require 'mysql2'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
# Simple wrapper for interacting with the OLS (Ontology Lookup Service - http://www.ebi.ac.uk/ontology-lookup/)
|
9
|
+
# database (created and managed by the EBI). Handles interaction with the service and automagically turns
|
10
|
+
# the retieved ontology terms into usable tree stuctures.
|
11
|
+
#
|
12
|
+
# @author Darren Oakley
|
13
|
+
module OLS
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :db_connection_details
|
17
|
+
|
18
|
+
def ols_db
|
19
|
+
@ols_db = connect_to_db() if @ols_db.nil?
|
20
|
+
@ols_db
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def get_db_connection_details
|
26
|
+
defaults = {
|
27
|
+
:database => 'ols',
|
28
|
+
:host => '127.0.0.1',
|
29
|
+
:port => 3306,
|
30
|
+
:user => 'ols',
|
31
|
+
:password => 'ols'
|
32
|
+
}
|
33
|
+
|
34
|
+
if !OLS.db_connection_details.nil? and OLS.db_connection_details.is_a? Hash
|
35
|
+
defaults.merge!(OLS.db_connection_details)
|
36
|
+
end
|
37
|
+
|
38
|
+
defaults
|
39
|
+
end
|
40
|
+
|
41
|
+
def connect_to_db
|
42
|
+
params = { :adapter => 'mysql2', :encoding => 'utf8' }.merge(get_db_connection_details)
|
43
|
+
Sequel.connect(params)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Error class for when we can't find a given ontology term.
|
48
|
+
class OntologyTermNotFoundError < StandardError; end
|
49
|
+
|
50
|
+
# Class representing an ontology term as part of a tree structure. Uses the Tree::TreeNode (rubytree)
|
51
|
+
# gem as a base class.
|
52
|
+
class OntologyTerm < Tree::TreeNode
|
53
|
+
attr_accessor :already_fetched_parents, :already_fetched_children
|
54
|
+
|
55
|
+
attr_accessor :root_term, :leaf_node
|
56
|
+
attr_writer :all_child_terms, :all_child_names
|
57
|
+
protected :root_term, :leaf_node
|
58
|
+
|
59
|
+
# @param [String] name The ontology term (id) i.e. GO:00032
|
60
|
+
# @param [String] content The ontology term name/description - optional this will be looked up in the OLS database
|
61
|
+
def initialize( name, content=nil )
|
62
|
+
super
|
63
|
+
|
64
|
+
@already_fetched_parents = false
|
65
|
+
@already_fetched_children = false
|
66
|
+
@root_term = false
|
67
|
+
@leaf_node = false
|
68
|
+
|
69
|
+
@all_child_terms = nil
|
70
|
+
@all_child_names = nil
|
71
|
+
|
72
|
+
get_term_details if @content.nil? or @content.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Override to ensure compatibility with Tree::TreeNode.
|
76
|
+
#
|
77
|
+
# @return [String] The ontology term (id) i.e. GO00032
|
78
|
+
def term
|
79
|
+
self.name
|
80
|
+
end
|
81
|
+
|
82
|
+
# Override to ensure compatibility with Tree::TreeNode.
|
83
|
+
#
|
84
|
+
# @return [String] The ontology term name/description
|
85
|
+
def term_name
|
86
|
+
self.content
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns +true+ if the receiver is a root node. Note that
|
90
|
+
# orphaned children will also be reported as root nodes.
|
91
|
+
#
|
92
|
+
# @return [Boolean] +true+ if this is a root node.
|
93
|
+
def is_root?
|
94
|
+
@root_term
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns +true+ if the receiver node is a 'leaf' - i.e., one without
|
98
|
+
# any children.
|
99
|
+
#
|
100
|
+
# @return [Boolean] +true+ if this is a leaf node.
|
101
|
+
def is_leaf?
|
102
|
+
@leaf_node
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns string representation of the receiver node.
|
106
|
+
# This method is primarily meant for debugging purposes.
|
107
|
+
#
|
108
|
+
# @return [String] A string representation of the node.
|
109
|
+
def to_s
|
110
|
+
"Term: #{@name}" +
|
111
|
+
" Term Name: " + (@content || "<Empty>") +
|
112
|
+
" Root Term?: #{is_root?}" +
|
113
|
+
" Leaf Node?: #{is_leaf?} " +
|
114
|
+
" Total Nodes Loaded: #{size()}"
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns an array of parent OntologyTerm objects.
|
118
|
+
#
|
119
|
+
# @return [Array] An array of parent OntologyTerm objects
|
120
|
+
def parentage
|
121
|
+
get_parents
|
122
|
+
super
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the children of this term as a tree. Will include the current term
|
126
|
+
# as the 'root' of the tree.
|
127
|
+
#
|
128
|
+
# @return [OntologyTerm] The children of this term as a tree. Will include the current term as the 'root' of the tree.
|
129
|
+
def child_tree
|
130
|
+
build_tree
|
131
|
+
child_tree = self.clone
|
132
|
+
child_tree.remove_from_parent!
|
133
|
+
child_tree
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns an array of the direct children (OntologyTerm objects) of this term.
|
137
|
+
#
|
138
|
+
# @return [Array] An array of the direct children (OntologyTerm objects) of this term.
|
139
|
+
def children
|
140
|
+
get_children
|
141
|
+
super
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a flat array containing all the possible child terms
|
145
|
+
# for this given ontology term.
|
146
|
+
#
|
147
|
+
# @return [Array] An array of all possible child terms (Strings) for this given ontology term
|
148
|
+
def all_child_terms
|
149
|
+
get_all_child_lists
|
150
|
+
return @all_child_terms
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns a flat array containing all the possible child term
|
154
|
+
# names for this given ontology term.
|
155
|
+
#
|
156
|
+
# @return [Array] A flat array containing all the possible child term names (Strings) for this given ontology term
|
157
|
+
def all_child_names
|
158
|
+
get_all_child_lists
|
159
|
+
return @all_child_names
|
160
|
+
end
|
161
|
+
|
162
|
+
# Function to force the OntologyTerm object to flesh out it's structure
|
163
|
+
# from the OLS database. By default OntologyTerm objects are lazy and will
|
164
|
+
# only retieve child data one level below themselves, so this is used to
|
165
|
+
# recursivley flesh out a full tree.
|
166
|
+
def build_tree
|
167
|
+
get_parents
|
168
|
+
get_children( self, true )
|
169
|
+
end
|
170
|
+
|
171
|
+
# Creates a JSON representation of this node including all it's children. This requires the JSON gem to be
|
172
|
+
# available, or else the operation fails with a warning message.
|
173
|
+
#
|
174
|
+
# @return The JSON representation of this subtree.
|
175
|
+
#
|
176
|
+
# @see {OntologyTerm#json_create}
|
177
|
+
def to_json(*a)
|
178
|
+
json_hash = {
|
179
|
+
"name" => name,
|
180
|
+
"content" => content,
|
181
|
+
"root_term" => @root_term,
|
182
|
+
"leaf_node" => @leaf_node,
|
183
|
+
"all_child_terms" => @all_child_terms,
|
184
|
+
"all_child_names" => @all_child_names,
|
185
|
+
JSON.create_id => self.class.name
|
186
|
+
}
|
187
|
+
|
188
|
+
if has_children?
|
189
|
+
json_hash["children"] = children
|
190
|
+
end
|
191
|
+
|
192
|
+
return JSON.generate( json_hash, :max_nesting => false )
|
193
|
+
end
|
194
|
+
|
195
|
+
# Class level function to build an OntologyTerm object from a serialized JSON hash
|
196
|
+
#
|
197
|
+
# @example
|
198
|
+
# emap = JSON.parse( File.read("emap.json"), :max_nesting => false )
|
199
|
+
#
|
200
|
+
# @param [Hash] json_hash The parsed JSON hash to de-serialize
|
201
|
+
# @return [OntologyTerm] The de-serialized object
|
202
|
+
def self.json_create(json_hash)
|
203
|
+
node = new(json_hash["name"], json_hash["content"])
|
204
|
+
node.already_fetched_children = true if json_hash["children"]
|
205
|
+
|
206
|
+
node.root_term = true if json_hash["root_term"]
|
207
|
+
node.leaf_node = true if json_hash["leaf_node"]
|
208
|
+
node.all_child_terms = json_hash["all_child_terms"]
|
209
|
+
node.all_child_names = json_hash["all_child_names"]
|
210
|
+
|
211
|
+
json_hash["children"].each do |child|
|
212
|
+
child.already_fetched_parents = true
|
213
|
+
child.already_fetched_children = true if child.has_children?
|
214
|
+
node << child
|
215
|
+
end if json_hash["children"]
|
216
|
+
|
217
|
+
return node
|
218
|
+
end
|
219
|
+
|
220
|
+
# Method to set the parent node for the receiver node.
|
221
|
+
# This method should *NOT* be invoked by client code.
|
222
|
+
#
|
223
|
+
# @param [OntologyTerm] parent The parent node.
|
224
|
+
# @return [OntologyTerm] The parent node.
|
225
|
+
def parent=(parent) # :nodoc:
|
226
|
+
@parent = parent
|
227
|
+
end
|
228
|
+
|
229
|
+
# Recursive function to query the OLS database and collect all of
|
230
|
+
# the parent objects and insert them into @parents in the correct
|
231
|
+
# order.
|
232
|
+
def get_parents( node=self )
|
233
|
+
unless @already_fetched_parents
|
234
|
+
sql = <<-SQL
|
235
|
+
select
|
236
|
+
subject_term.identifier as child_identifier,
|
237
|
+
subject_term.term_name as child_term,
|
238
|
+
predicate_term.term_name as relation,
|
239
|
+
object_term.identifier as parent_identifier,
|
240
|
+
object_term.term_name as parent_term,
|
241
|
+
object_term.is_root_term as parent_is_root
|
242
|
+
from
|
243
|
+
term_relationship tr
|
244
|
+
join term as subject_term on tr.subject_term_pk = subject_term.term_pk
|
245
|
+
join term as predicate_term on tr.predicate_term_pk = predicate_term.term_pk
|
246
|
+
join term as object_term on tr.object_term_pk = object_term.term_pk
|
247
|
+
where
|
248
|
+
predicate_term.term_name in ('part_of','is_a','develops_from')
|
249
|
+
and subject_term.identifier = ?
|
250
|
+
SQL
|
251
|
+
|
252
|
+
OLS.ols_db[ sql, node.term ].each do |row|
|
253
|
+
parent = OntologyTerm.new( row[:parent_identifier], row[:parent_term] )
|
254
|
+
parent.root_term = true if row[:parent_is_root].to_i == 1
|
255
|
+
parent << node
|
256
|
+
get_parents( parent )
|
257
|
+
end
|
258
|
+
|
259
|
+
@already_fetched_parents = true
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Recursive function to query the OLS database and collect all of
|
264
|
+
# the child objects and build up a tree of OntologyTerm's.
|
265
|
+
def get_children( node=self, recursively=false )
|
266
|
+
unless @already_fetched_children or node.has_children?
|
267
|
+
sql = <<-SQL
|
268
|
+
select
|
269
|
+
subject_term.identifier as child_identifier,
|
270
|
+
subject_term.term_name as child_term,
|
271
|
+
subject_term.is_leaf as child_is_leaf,
|
272
|
+
predicate_term.term_name as relation,
|
273
|
+
object_term.identifier as parent_identifier,
|
274
|
+
object_term.term_name as parent_term
|
275
|
+
from
|
276
|
+
term_relationship tr
|
277
|
+
join term as subject_term on tr.subject_term_pk = subject_term.term_pk
|
278
|
+
join term as predicate_term on tr.predicate_term_pk = predicate_term.term_pk
|
279
|
+
join term as object_term on tr.object_term_pk = object_term.term_pk
|
280
|
+
where
|
281
|
+
predicate_term.term_name in ('part_of','is_a','develops_from')
|
282
|
+
and object_term.identifier = ?
|
283
|
+
SQL
|
284
|
+
|
285
|
+
OLS.ols_db[sql,node.term].each do |row|
|
286
|
+
child = OntologyTerm.new( row[:child_identifier], row[:child_term] )
|
287
|
+
child.leaf_node = true if row[:child_is_leaf].to_i == 1
|
288
|
+
child.get_children( child, true ) if recursively and !child.is_leaf?
|
289
|
+
node << child
|
290
|
+
end
|
291
|
+
|
292
|
+
@already_fetched_children = true
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Returns a copy of the receiver node, with its parent and children links removed.
|
297
|
+
# The original node remains attached to its tree.
|
298
|
+
#
|
299
|
+
# @return [OntologyTerm] A copy of the receiver node.
|
300
|
+
def detached_copy
|
301
|
+
copy = OLS::OntologyTerm.new(@name, @content ? @content.clone : nil)
|
302
|
+
copy.root_term = @root_term
|
303
|
+
copy.leaf_node = @leaf_node
|
304
|
+
return copy
|
305
|
+
end
|
306
|
+
|
307
|
+
# Function that merges one OntologyTerm tree into another.
|
308
|
+
#
|
309
|
+
# @param [OntologyTerm] tree The tree that is to be merged into self
|
310
|
+
# @return [OntologyTerm] The merged tree
|
311
|
+
def merge( tree )
|
312
|
+
unless tree.is_a?(OLS::OntologyTerm)
|
313
|
+
raise TypeError, "You can only merge in another OntologyTerm tree!"
|
314
|
+
end
|
315
|
+
|
316
|
+
unless self.root.name == tree.root.name
|
317
|
+
raise ArgumentError, "Unable to merge trees as they do not share the same root!"
|
318
|
+
end
|
319
|
+
|
320
|
+
new_tree = merge_subtrees( self.root, tree.root )
|
321
|
+
end
|
322
|
+
|
323
|
+
private
|
324
|
+
|
325
|
+
# Utility function to recursivley merge two subtrees
|
326
|
+
#
|
327
|
+
# @param [OntologyTerm] tree1 The target tree to merge into
|
328
|
+
# @param [OntologyTerm] tree2 The donor tree (that will be merged into target)
|
329
|
+
def merge_subtrees( tree1, tree2 )
|
330
|
+
names1 = tree1.has_children? ? tree1.children.map { |child| child.name } : []
|
331
|
+
names2 = tree2.has_children? ? tree2.children.map { |child| child.name } : []
|
332
|
+
|
333
|
+
names_to_merge = names2 - names1
|
334
|
+
names_to_merge.each do |name|
|
335
|
+
tree1 << tree2[name].detached_subtree_copy
|
336
|
+
end
|
337
|
+
|
338
|
+
tree1.children.each do |child|
|
339
|
+
unless tree2[child.name].nil?
|
340
|
+
merge_subtrees( child, tree2[child.name] )
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
return tree1
|
345
|
+
end
|
346
|
+
|
347
|
+
# Helper function to query the OLS database and grab the full
|
348
|
+
# details of the ontology term.
|
349
|
+
def get_term_details
|
350
|
+
# This query ensures we look at the most recent fully loaded ontologies
|
351
|
+
sql = <<-SQL
|
352
|
+
select term.*
|
353
|
+
from term
|
354
|
+
join ontology on ontology.ontology_id = term.ontology_id
|
355
|
+
where term.identifier = ?
|
356
|
+
order by ontology.fully_loaded desc, ontology.load_date asc
|
357
|
+
SQL
|
358
|
+
|
359
|
+
term_set = OLS.ols_db[ sql, @name ].all()
|
360
|
+
|
361
|
+
if term_set.size == 0
|
362
|
+
get_term_from_synonym
|
363
|
+
else
|
364
|
+
subject = term_set.first
|
365
|
+
@content = subject[:term_name]
|
366
|
+
@term_pk = subject[:term_pk]
|
367
|
+
@ontology_id = subject[:ontology_id]
|
368
|
+
@root_term = true if subject[:is_root_term].to_i == 1
|
369
|
+
@leaf_node = true if subject[:is_leaf].to_i == 1
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Helper function to try to find an ontology term via a synonym.
|
374
|
+
def get_term_from_synonym
|
375
|
+
sql = <<-SQL
|
376
|
+
select term.*
|
377
|
+
from term
|
378
|
+
join ontology on ontology.ontology_id = term.ontology_id
|
379
|
+
join term_synonym on term.term_pk = term_synonym.term_pk
|
380
|
+
where term_synonym.synonym_value = ?
|
381
|
+
order by ontology.fully_loaded desc, ontology.load_date asc
|
382
|
+
SQL
|
383
|
+
|
384
|
+
term_set = OLS.ols_db[ sql, @name ].all()
|
385
|
+
|
386
|
+
if term_set.size == 0
|
387
|
+
raise OLS::OntologyTermNotFoundError, "Unable to find the term '#{@name}' in the OLS database."
|
388
|
+
end
|
389
|
+
|
390
|
+
subject = term_set.first
|
391
|
+
@name = subject[:identifier]
|
392
|
+
@content = subject[:term_name]
|
393
|
+
@term_pk = subject[:term_pk]
|
394
|
+
@ontology_id = subject[:ontology_id]
|
395
|
+
@root_term = true if subject[:is_root_term].to_i == 1
|
396
|
+
@leaf_node = true if subject[:is_leaf].to_i == 1
|
397
|
+
end
|
398
|
+
|
399
|
+
# Helper function to produce the flat lists of all the child
|
400
|
+
# terms and names.
|
401
|
+
def get_all_child_lists
|
402
|
+
get_children
|
403
|
+
|
404
|
+
if @all_child_terms.nil? and @all_child_names.nil?
|
405
|
+
@all_child_terms = []
|
406
|
+
@all_child_names = []
|
407
|
+
|
408
|
+
self.children.each do |child|
|
409
|
+
@all_child_terms.push( child.term )
|
410
|
+
@all_child_terms.push( child.all_child_terms )
|
411
|
+
@all_child_names.push( child.term_name )
|
412
|
+
@all_child_names.push( child.all_child_names )
|
413
|
+
end
|
414
|
+
|
415
|
+
@all_child_terms = @all_child_terms.flatten.uniq
|
416
|
+
@all_child_names = @all_child_names.flatten.uniq
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
end
|
421
|
+
|
422
|
+
end
|
data/lib/ols/version.rb
ADDED
data/ols.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ols/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ols"
|
7
|
+
s.version = OLS::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Darren Oakley"]
|
10
|
+
s.email = ["daz.oakley@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/dazoakley/ols"
|
12
|
+
s.summary = %q{A simple wrapper around a local copy of the OLS database}
|
13
|
+
s.description = %q{
|
14
|
+
OLS provides a simple interface to the EBI's Ontology Lookup Service (http://www.ebi.ac.uk/ontology-lookup/).
|
15
|
+
It provides an easy lookup of ontology terms and automagically builds up ontology trees using RubyTree
|
16
|
+
(http://rubytree.rubyforge.org/) as a base library.
|
17
|
+
|
18
|
+
PLEASE NOTE: The current version of this gem requires a local install of the OLS database running on MySQL.
|
19
|
+
Please see http://www.ebi.ac.uk/ontology-lookup/databaseExport.do I will update the code in the future
|
20
|
+
to run off the soap service.
|
21
|
+
}
|
22
|
+
|
23
|
+
s.rubyforge_project = "ols"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
|
30
|
+
s.add_dependency "rubytree"
|
31
|
+
s.add_dependency "sequel"
|
32
|
+
s.add_dependency "mysql2"
|
33
|
+
s.add_dependency "json"
|
34
|
+
|
35
|
+
s.add_development_dependency "rake"
|
36
|
+
s.add_development_dependency "shoulda"
|
37
|
+
s.add_development_dependency "simplecov"
|
38
|
+
s.add_development_dependency "simplecov-rcov"
|
39
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Add the lib directory to the search path
|
2
|
+
$:.unshift( "#{File.expand_path(File.dirname(__FILE__))}/../lib" )
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
# Set-up SimpleCov (code coverage tool for Ruby 1.9)
|
7
|
+
if /^1.9/ === RUBY_VERSION
|
8
|
+
begin
|
9
|
+
require 'simplecov'
|
10
|
+
require 'simplecov-rcov'
|
11
|
+
|
12
|
+
class SimpleCov::Formatter::MergedFormatter
|
13
|
+
def format(result)
|
14
|
+
SimpleCov::Formatter::HTMLFormatter.new.format(result)
|
15
|
+
SimpleCov::Formatter::RcovFormatter.new.format(result)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
|
20
|
+
SimpleCov.start
|
21
|
+
rescue LoadError
|
22
|
+
puts "[ERROR] Unable to load 'simplecov' - please run 'bundle install'"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'shoulda'
|
27
|
+
require 'ols'
|
28
|
+
|
29
|
+
OLS.db_connection_details = {
|
30
|
+
:port => 13306,
|
31
|
+
:database => 'htgt_ols',
|
32
|
+
:user => 'htgt',
|
33
|
+
:password => 'htgt'
|
34
|
+
}
|
data/test/test_ols.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class OLSTest < Test::Unit::TestCase
|
4
|
+
include OLS
|
5
|
+
|
6
|
+
context "An OntologyTerm object" do
|
7
|
+
setup do
|
8
|
+
@emap_id = "EMAP:3018"
|
9
|
+
@emap_name = "TS18,nose"
|
10
|
+
@ont = OntologyTerm.new(@emap_id)
|
11
|
+
end
|
12
|
+
|
13
|
+
should "have basic attributes" do
|
14
|
+
assert_equal( @emap_id, @ont.name, "OntologyTerm.name does not equal '#{@emap_id}'." )
|
15
|
+
assert_equal( @emap_id, @ont.term, "OntologyTerm.term does not equal '#{@emap_id}'." )
|
16
|
+
|
17
|
+
assert_equal( @emap_name, @ont.content, "OntologyTerm.content does not equal '#{@emap_name}'." )
|
18
|
+
assert_equal( @emap_name, @ont.term_name, "OntologyTerm.term_name does not equal '#{@emap_name}'." )
|
19
|
+
|
20
|
+
assert_equal( false, @ont.term_name.nil?, "The OntologyTerm.term_name is nil." )
|
21
|
+
assert_equal( false, @ont.content.nil?, "The OntologyTerm.term_name is nil." )
|
22
|
+
end
|
23
|
+
|
24
|
+
should "raise appropriate errors" do
|
25
|
+
assert_raise(OntologyTermNotFoundError) { OntologyTerm.new("FLIBBLE:5") }
|
26
|
+
end
|
27
|
+
|
28
|
+
should "be able to represent itself as a String" do
|
29
|
+
string = @ont.to_s
|
30
|
+
|
31
|
+
assert( string.include?('Term Name'), "OntologyTerm.to_s does not include 'Term Name'." )
|
32
|
+
assert( string.include?(@ont.content), "OntologyTerm.to_s does not include '@content'." )
|
33
|
+
assert( string.include?('Root Term?'), "OntologyTerm.to_s does not include 'Root Term?'." )
|
34
|
+
assert( string.include?('Leaf Node?'), "OntologyTerm.to_s does not include 'Leaf Node?'." )
|
35
|
+
end
|
36
|
+
|
37
|
+
should "respond correctly to the .parentage method" do
|
38
|
+
assert( @ont.parentage.is_a?(Array), "OntologyTerm.parentage is not an Array when we have parents." )
|
39
|
+
assert( @ont.parentage[0].is_a?(OntologyTerm), "OntologyTerm.parentage[0] does not return an OntologyTerm tree." )
|
40
|
+
assert_equal( 4, @ont.parentage.size, "OntologyTerm.parentage is not returning the correct number of entries (we expect 4 for #{@emap_id})." )
|
41
|
+
end
|
42
|
+
|
43
|
+
should "be able to generate its child tree" do
|
44
|
+
assert( @ont.child_tree.is_a?(OntologyTerm), "OntologyTerm.child_tree does not return an OntologyTerm tree." )
|
45
|
+
assert_equal( @ont.term, @ont.child_tree.term, "OntologyTerm.child_tree.root is equal to self." )
|
46
|
+
end
|
47
|
+
|
48
|
+
should "respond correctly to the .children method" do
|
49
|
+
assert( @ont.children.is_a?(Array), "OntologyTerm.children is not an Array when we have children." )
|
50
|
+
assert( @ont.children[0].is_a?(OntologyTerm), "OntologyTerm.children[0] does not return an OntologyTerm tree." )
|
51
|
+
assert_equal( 3, @ont.children.size, "OntologyTerm.children is not returning the correct number of entries (we expect 3 direct children for #{@emap_id})." )
|
52
|
+
end
|
53
|
+
|
54
|
+
should "be able to generate a flat list of all child terms/names" do
|
55
|
+
assert( @ont.all_child_terms.is_a?(Array), "OntologyTerm.all_child_terms is not an Array." )
|
56
|
+
assert( @ont.all_child_terms[0].is_a?(String), "OntologyTerm.all_child_terms[0] is not an String." )
|
57
|
+
assert_equal( 14, @ont.all_child_terms.size, "OntologyTerm.all_child_terms is not returning the correct number of entries (we expect 14 children for #{@emap_id})." )
|
58
|
+
|
59
|
+
assert( @ont.all_child_names.is_a?(Array), "OntologyTerm.all_child_names is not an Array." )
|
60
|
+
assert( @ont.all_child_names[0].is_a?(String), "OntologyTerm.all_child_names[0] is not an String." )
|
61
|
+
assert_equal( 14, @ont.all_child_names.size, "OntologyTerm.all_child_names is not returning the correct number of entries (we expect 14 children for #{@emap_id})." )
|
62
|
+
|
63
|
+
assert_equal( @ont.all_child_terms.size, @ont.all_child_names.size, "OntologyTerm.all_child_terms and OntologyTerm.all_child_names do not produce an array of the same size." )
|
64
|
+
end
|
65
|
+
|
66
|
+
should "be able to locate ontology terms via synonyms" do
|
67
|
+
go_id = 'GO:0007242'
|
68
|
+
ont = OntologyTerm.new(go_id)
|
69
|
+
|
70
|
+
assert_equal( 'GO:0023034', ont.term, "A synonym search for GO:0007242 has not found GO:0023034." )
|
71
|
+
assert_equal( 'intracellular signaling pathway', ont.term_name, "A synonym search for GO:0007242 has not found 'intracellular signaling pathway'." )
|
72
|
+
end
|
73
|
+
|
74
|
+
should "be able to serialize/deserialize itself as a JSON string" do
|
75
|
+
@ont.build_tree()
|
76
|
+
|
77
|
+
json_string = nil
|
78
|
+
assert_nothing_raised(Exception) { json_string = @ont.to_json }
|
79
|
+
assert( json_string.is_a?(String) )
|
80
|
+
|
81
|
+
duplicate = nil
|
82
|
+
assert_nothing_raised(Exception) { duplicate = JSON.parse(json_string) }
|
83
|
+
assert( duplicate.is_a?(OntologyTerm) )
|
84
|
+
assert_equal( @ont.term, duplicate.term )
|
85
|
+
assert_equal( @ont.term_name, duplicate.term_name )
|
86
|
+
assert_equal( @ont.is_root?, duplicate.is_root? )
|
87
|
+
assert_equal( @ont.is_leaf?, duplicate.is_leaf? )
|
88
|
+
end
|
89
|
+
|
90
|
+
should "be able to produce a detached copy of itself" do
|
91
|
+
@ont.build_tree()
|
92
|
+
duplicate = @ont.detached_copy
|
93
|
+
|
94
|
+
assert_equal( @ont.term, duplicate.term )
|
95
|
+
assert_equal( @ont.term_name, duplicate.term_name )
|
96
|
+
assert_equal( @ont.is_root?, duplicate.is_root? )
|
97
|
+
assert_equal( @ont.is_leaf?, duplicate.is_leaf? )
|
98
|
+
|
99
|
+
assert_equal( false, duplicate.send(:already_fetched_parents) )
|
100
|
+
assert_equal( false, duplicate.send(:already_fetched_children) )
|
101
|
+
end
|
102
|
+
|
103
|
+
should "be able to merge two ontology trees" do
|
104
|
+
@ont2 = OntologyTerm.new('EMAP:3003')
|
105
|
+
|
106
|
+
@ont.build_tree
|
107
|
+
@ont2.build_tree
|
108
|
+
|
109
|
+
merged_tree = @ont.merge(@ont2)
|
110
|
+
|
111
|
+
assert( merged_tree['EMAP:2636']['EMAP:2822']['EMAP:2987'].is_a?(OntologyTerm) )
|
112
|
+
assert_equal( 2, merged_tree['EMAP:2636']['EMAP:2822']['EMAP:2987'].children.size )
|
113
|
+
assert_equal( 34, merged_tree.size )
|
114
|
+
|
115
|
+
another_ont = OntologyTerm.new('GO:0023034')
|
116
|
+
yet_another_ont = OntologyTerm.new('EMAP:3003')
|
117
|
+
another_ont.build_tree
|
118
|
+
yet_another_ont.build_tree
|
119
|
+
|
120
|
+
assert_raise(ArgumentError) { foo = another_ont.merge(yet_another_ont) }
|
121
|
+
assert_raise(TypeError) { bar = another_ont.merge('EMAP:3003') }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ols
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Darren Oakley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-15 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rubytree
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sequel
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: mysql2
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: json
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: rake
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: shoulda
|
73
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: simplecov
|
84
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: *id007
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: simplecov-rcov
|
95
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
type: :development
|
102
|
+
prerelease: false
|
103
|
+
version_requirements: *id008
|
104
|
+
description: "\n OLS provides a simple interface to the EBI's Ontology Lookup Service (http://www.ebi.ac.uk/ontology-lookup/). \n It provides an easy lookup of ontology terms and automagically builds up ontology trees using RubyTree \n (http://rubytree.rubyforge.org/) as a base library.\n\n PLEASE NOTE: The current version of this gem requires a local install of the OLS database running on MySQL. \n Please see http://www.ebi.ac.uk/ontology-lookup/databaseExport.do I will update the code in the future \n to run off the soap service.\n "
|
105
|
+
email:
|
106
|
+
- daz.oakley@gmail.com
|
107
|
+
executables: []
|
108
|
+
|
109
|
+
extensions: []
|
110
|
+
|
111
|
+
extra_rdoc_files: []
|
112
|
+
|
113
|
+
files:
|
114
|
+
- .gitignore
|
115
|
+
- Gemfile
|
116
|
+
- History.txt
|
117
|
+
- README.rdoc
|
118
|
+
- Rakefile
|
119
|
+
- lib/ols.rb
|
120
|
+
- lib/ols/version.rb
|
121
|
+
- ols.gemspec
|
122
|
+
- test/test_helper.rb
|
123
|
+
- test/test_ols.rb
|
124
|
+
has_rdoc: true
|
125
|
+
homepage: https://github.com/dazoakley/ols
|
126
|
+
licenses: []
|
127
|
+
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
none: false
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
hash: -2283321119041692548
|
139
|
+
segments:
|
140
|
+
- 0
|
141
|
+
version: "0"
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
hash: -2283321119041692548
|
148
|
+
segments:
|
149
|
+
- 0
|
150
|
+
version: "0"
|
151
|
+
requirements: []
|
152
|
+
|
153
|
+
rubyforge_project: ols
|
154
|
+
rubygems_version: 1.6.2
|
155
|
+
signing_key:
|
156
|
+
specification_version: 3
|
157
|
+
summary: A simple wrapper around a local copy of the OLS database
|
158
|
+
test_files:
|
159
|
+
- test/test_helper.rb
|
160
|
+
- test/test_ols.rb
|