ols 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|