intermine 0.98.03 → 0.98.04
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/lib/intermine/query.rb +5 -0
- data/lib/intermine/results.rb +211 -45
- data/lib/intermine/service.rb +3 -0
- data/lib/intermine/version.rb +1 -1
- data/test/data/resultrow.json +5 -1
- data/test/test_result_row.rb +30 -6
- data/test/unit_tests.rb +1 -0
- metadata +4 -4
data/lib/intermine/query.rb
CHANGED
|
@@ -357,7 +357,12 @@ module InterMine::PathQuery
|
|
|
357
357
|
end
|
|
358
358
|
|
|
359
359
|
# Get your own result reader for handling the results at a low level.
|
|
360
|
+
# If no columns have been selected for output before requesting results,
|
|
361
|
+
# all attribute columns will be selected.
|
|
360
362
|
def results_reader(start=0, size=nil)
|
|
363
|
+
if @views.empty?
|
|
364
|
+
select("*")
|
|
365
|
+
end
|
|
361
366
|
return Results::ResultsReader.new(@url, self, start, size)
|
|
362
367
|
end
|
|
363
368
|
|
data/lib/intermine/results.rb
CHANGED
|
@@ -3,10 +3,52 @@ require "json"
|
|
|
3
3
|
require 'stringio'
|
|
4
4
|
require 'net/http'
|
|
5
5
|
|
|
6
|
+
# == Code for handling results
|
|
7
|
+
#
|
|
8
|
+
# The classes in this module are used to process
|
|
9
|
+
# the results of queries. They include mechanisms
|
|
10
|
+
# for reading from the connection in a record
|
|
11
|
+
# orientated format, and for interpreting the values
|
|
12
|
+
# received.
|
|
13
|
+
#
|
|
6
14
|
module InterMine::Results
|
|
7
15
|
|
|
16
|
+
# == Synopsis
|
|
17
|
+
#
|
|
18
|
+
# query.each_row do |row|
|
|
19
|
+
# puts row[0], row[1], row[2]
|
|
20
|
+
# puts row["symbol"], row["proteins.name"]
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# == Description
|
|
24
|
+
#
|
|
25
|
+
# A dual-faced object, these representations of rows
|
|
26
|
+
# of results are intended to provide a single object that
|
|
27
|
+
# allows Array-like and Hash-like access to an individual
|
|
28
|
+
# row of data. The primary means of access for this is
|
|
29
|
+
# the use of item access with ResultsRow#[], which accepts
|
|
30
|
+
# Array like index arguments, as well as Hash like key arguments.
|
|
31
|
+
#
|
|
32
|
+
# As well as value retrieval via indices and keys, iteration is also supported
|
|
33
|
+
# with ResultsRow#each and the aliases each_pair and each_value. If
|
|
34
|
+
# one parameter is requested the iteration will be by value, if
|
|
35
|
+
# two are requested it will be by pair.
|
|
36
|
+
#
|
|
37
|
+
# There is no attempt to fully emulate the complete Hash and Array
|
|
38
|
+
# interfaces - that would make little sense as it does not make any
|
|
39
|
+
# sense to insert values into a row, or to re-sort it in place. If you
|
|
40
|
+
# want to do such things, call ResultsRow#to_a or ResultsRow#to_h to
|
|
41
|
+
# get real Hash or Array values you are free to manipulate.
|
|
42
|
+
#
|
|
43
|
+
#:include:contact_header.rdoc
|
|
44
|
+
#
|
|
8
45
|
class ResultsRow
|
|
9
46
|
|
|
47
|
+
# Construct a new result row.
|
|
48
|
+
#
|
|
49
|
+
# You will not need to do this - ResultsRow objects are created for
|
|
50
|
+
# you when the results are parsed.
|
|
51
|
+
#
|
|
10
52
|
def initialize(results, columns)
|
|
11
53
|
@results = results.is_a?(Array) ? results : JSON.parse(results)
|
|
12
54
|
unless @results.is_a?(Array)
|
|
@@ -19,27 +61,104 @@ module InterMine::Results
|
|
|
19
61
|
@columns = columns
|
|
20
62
|
end
|
|
21
63
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
64
|
+
# == Retrieve values from the row
|
|
65
|
+
#
|
|
66
|
+
# row[0] # first element
|
|
67
|
+
# row[-1] # last element
|
|
68
|
+
# row[2, 3] # Slice of three cells, starting at cell no. 3 (index 2)
|
|
69
|
+
# row[1..3] # Slice of three cells, starting at cell no. 2 (index 1)
|
|
70
|
+
# row["length"] # Get a cell's value by column name
|
|
71
|
+
# row["Gene.length"] # Use of the root class is optional
|
|
72
|
+
# row[:length] # For bare attributes, a symbol may be used.
|
|
73
|
+
#
|
|
74
|
+
# This method emulated both the array and hash style of access, based
|
|
75
|
+
# on argument type. Passing in integer indexes or ranges retrieves
|
|
76
|
+
# values in a manner that treats the row as a list of values. Passing in
|
|
77
|
+
# string or symbols retrieves values in a manner that treates the
|
|
78
|
+
# row as a Hash. It is possible to access the values in the row by using
|
|
79
|
+
# either the short or long forms of the column name.
|
|
80
|
+
#
|
|
81
|
+
# Unlike the corresponding method in either Hash or Array, this method
|
|
82
|
+
# throws errors when trying to access single elements (not when requesting
|
|
83
|
+
# array slices) and the result cannot be found.
|
|
84
|
+
#
|
|
85
|
+
def [](arg, length=nil)
|
|
86
|
+
unless length.nil?
|
|
87
|
+
raise ArgumentError, "when providing a length, the first argument must be an Integer" unless arg.is_a? Integer
|
|
88
|
+
return @results[arg, length].map {|x| x["value"]}
|
|
27
89
|
end
|
|
28
|
-
|
|
29
|
-
|
|
90
|
+
|
|
91
|
+
case arg
|
|
92
|
+
when Range
|
|
93
|
+
return @results[arg].map {|x| x["value"]}
|
|
94
|
+
when Integer
|
|
95
|
+
idx = arg
|
|
96
|
+
else
|
|
97
|
+
idx = index_for(arg)
|
|
30
98
|
end
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
99
|
+
|
|
100
|
+
raise ArgumentError, "Bad argument: #{arg}" if idx.nil?
|
|
101
|
+
|
|
102
|
+
cell = @results[idx]
|
|
103
|
+
|
|
104
|
+
raise IndexError, "Argument out of range" if cell.nil?
|
|
105
|
+
|
|
106
|
+
return cell["value"]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns the first value in this row
|
|
110
|
+
def first
|
|
111
|
+
return @results[0]["value"]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Returns the last value in this row
|
|
115
|
+
def last
|
|
116
|
+
return @results.last["value"]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Iterate over the values in this row in the
|
|
120
|
+
# order specified by the query's view.
|
|
121
|
+
#
|
|
122
|
+
# row.each do |value|
|
|
123
|
+
# puts value
|
|
124
|
+
# end
|
|
125
|
+
#
|
|
126
|
+
# row.each do |column, value|
|
|
127
|
+
# puts "#{column} is #{value}"
|
|
128
|
+
# end
|
|
129
|
+
#
|
|
130
|
+
# If one parameter is specified, then the iteration
|
|
131
|
+
# simply includes values, if more than one is specified,
|
|
132
|
+
# then it will be by column/value pairs.
|
|
133
|
+
#
|
|
134
|
+
def each(&block)
|
|
135
|
+
if block
|
|
136
|
+
if block.arity == 1
|
|
137
|
+
@results.each {|x| block.call(x["value"])}
|
|
138
|
+
else block.arity == 2
|
|
139
|
+
(0 ... @results.size).to_a.each {|i| block.call(@columns[i], @results[i]["value"])}
|
|
140
|
+
end
|
|
35
141
|
end
|
|
36
|
-
return
|
|
142
|
+
return self
|
|
37
143
|
end
|
|
38
144
|
|
|
145
|
+
alias each_value each
|
|
146
|
+
alias each_pair each
|
|
147
|
+
|
|
148
|
+
# Return the number of cells in this row.
|
|
149
|
+
def size
|
|
150
|
+
return @results.size
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
alias length size
|
|
154
|
+
|
|
155
|
+
# Return an Array version of the row.
|
|
39
156
|
def to_a
|
|
40
157
|
return @results.map {|x| x["value"]}
|
|
41
158
|
end
|
|
42
159
|
|
|
160
|
+
# Return a Hash version of the row. All keys in this
|
|
161
|
+
# hash will be full length column headers.
|
|
43
162
|
def to_h
|
|
44
163
|
hash = {}
|
|
45
164
|
@results.each_index do |x|
|
|
@@ -49,6 +168,7 @@ module InterMine::Results
|
|
|
49
168
|
return hash
|
|
50
169
|
end
|
|
51
170
|
|
|
171
|
+
# Return a readable representation of the information in this row
|
|
52
172
|
def to_s
|
|
53
173
|
bufs = []
|
|
54
174
|
@results.each_index do |idx|
|
|
@@ -61,12 +181,16 @@ module InterMine::Results
|
|
|
61
181
|
return @columns.first.rootClass.name + ": " + bufs.join(",\t")
|
|
62
182
|
end
|
|
63
183
|
|
|
64
|
-
|
|
65
|
-
|
|
184
|
+
# Return the information in this row as a line suitable for prining in a
|
|
185
|
+
# CSV or TSV file. The optional separator argument will be used to delimit
|
|
186
|
+
# columns
|
|
187
|
+
def to_csv(sep="\t")
|
|
188
|
+
return @results.map {|x| x["value"].inspect}.join(sep)
|
|
66
189
|
end
|
|
67
190
|
|
|
68
191
|
private
|
|
69
192
|
|
|
193
|
+
# Return the index for a string or symbol key.
|
|
70
194
|
def index_for(key)
|
|
71
195
|
if @indexes.nil?
|
|
72
196
|
@indexes = {}
|
|
@@ -81,13 +205,30 @@ module InterMine::Results
|
|
|
81
205
|
end
|
|
82
206
|
end
|
|
83
207
|
end
|
|
84
|
-
return @indexes[key]
|
|
208
|
+
return @indexes[key.to_s]
|
|
85
209
|
end
|
|
86
210
|
end
|
|
87
211
|
|
|
88
212
|
|
|
213
|
+
# The class responsible for retrieving results and processing them
|
|
214
|
+
#
|
|
215
|
+
# query.each_row do |row|
|
|
216
|
+
# puts row[2..3]
|
|
217
|
+
# end
|
|
218
|
+
#
|
|
219
|
+
# Queries delegate their handling of results to these objects, which
|
|
220
|
+
# are responsible for creating the ResultsRow objects or model objects
|
|
221
|
+
# that the results represent.
|
|
222
|
+
#
|
|
223
|
+
#:include:contact_header.rdoc
|
|
224
|
+
#
|
|
89
225
|
class ResultsReader
|
|
90
226
|
|
|
227
|
+
# Construct a new ResultsReader.
|
|
228
|
+
#
|
|
229
|
+
# You will not need to do this yourself. It is handled by
|
|
230
|
+
# queries themselves.
|
|
231
|
+
#
|
|
91
232
|
def initialize(uri, query, start, size)
|
|
92
233
|
@uri = URI(uri)
|
|
93
234
|
@query = query
|
|
@@ -95,12 +236,7 @@ module InterMine::Results
|
|
|
95
236
|
@size = size
|
|
96
237
|
end
|
|
97
238
|
|
|
98
|
-
|
|
99
|
-
p = @query.params.merge("start" => @start, "format" => format)
|
|
100
|
-
p["size"] = @size unless @size.nil?
|
|
101
|
-
return p
|
|
102
|
-
end
|
|
103
|
-
|
|
239
|
+
# Run a request to get the size of the result set.
|
|
104
240
|
def get_size
|
|
105
241
|
query = params("jsoncount")
|
|
106
242
|
res = Net::HTTP.post_form(@uri, query)
|
|
@@ -113,31 +249,10 @@ module InterMine::Results
|
|
|
113
249
|
end
|
|
114
250
|
end
|
|
115
251
|
|
|
116
|
-
|
|
117
|
-
req = Net::HTTP::Post.new(@uri.path)
|
|
118
|
-
req.set_form_data(data)
|
|
119
|
-
Net::HTTP.new(@uri.host, @uri.port).start {|http|
|
|
120
|
-
http.request(req) {|resp|
|
|
121
|
-
holdover = ""
|
|
122
|
-
resp.read_body {|chunk|
|
|
123
|
-
sock = StringIO.new(holdover + chunk)
|
|
124
|
-
sock.each_line {|line|
|
|
125
|
-
if sock.eof?
|
|
126
|
-
holdover = line
|
|
127
|
-
else
|
|
128
|
-
yield line
|
|
129
|
-
end
|
|
130
|
-
}
|
|
131
|
-
sock.close
|
|
132
|
-
}
|
|
133
|
-
yield holdover
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
end
|
|
137
|
-
|
|
252
|
+
# Iterate over the result set one ResultsRow at a time
|
|
138
253
|
def each_row
|
|
139
254
|
container = ''
|
|
140
|
-
|
|
255
|
+
each_line(params("jsonrows")) do |line|
|
|
141
256
|
if line.start_with?("[")
|
|
142
257
|
begin
|
|
143
258
|
row = ResultsRow.new(line.chomp("," + $/), @query.views)
|
|
@@ -152,10 +267,25 @@ module InterMine::Results
|
|
|
152
267
|
check_result_set(container)
|
|
153
268
|
end
|
|
154
269
|
|
|
270
|
+
# Iterate over the resultset, one object at a time, where the
|
|
271
|
+
# object is the instantiation of the type of object specified as the
|
|
272
|
+
# query's root type. The actual type returned depends on the query
|
|
273
|
+
# itself, and any subclass information returned by the webservice.
|
|
274
|
+
#
|
|
275
|
+
# query = service.query("Gene").select("*")
|
|
276
|
+
# query.each_result do |gene|
|
|
277
|
+
# puts gene.symbol, gene.length
|
|
278
|
+
# end
|
|
279
|
+
#
|
|
280
|
+
# query = service.query("Organism").select("*")
|
|
281
|
+
# query.each_result do |organism|
|
|
282
|
+
# puts organism.shortName, organism.taxonId
|
|
283
|
+
# end
|
|
284
|
+
#
|
|
155
285
|
def each_result
|
|
156
286
|
model = @query.model
|
|
157
287
|
container = ''
|
|
158
|
-
|
|
288
|
+
each_line(params("jsonobjects")) do |line|
|
|
159
289
|
line.chomp!("," + $/)
|
|
160
290
|
if line.start_with?("{") and line.end_with?("}")
|
|
161
291
|
begin
|
|
@@ -176,6 +306,41 @@ module InterMine::Results
|
|
|
176
306
|
|
|
177
307
|
private
|
|
178
308
|
|
|
309
|
+
# Retrieve the results from the webservice, one line at a time.
|
|
310
|
+
# This method has responsibility for ensuring that the lines are
|
|
311
|
+
# complete, and not split over two or more chunks.
|
|
312
|
+
def each_line(data)
|
|
313
|
+
req = Net::HTTP::Post.new(@uri.path)
|
|
314
|
+
req.set_form_data(data)
|
|
315
|
+
Net::HTTP.new(@uri.host, @uri.port).start {|http|
|
|
316
|
+
http.request(req) {|resp|
|
|
317
|
+
holdover = ""
|
|
318
|
+
resp.read_body {|chunk|
|
|
319
|
+
sock = StringIO.new(holdover + chunk)
|
|
320
|
+
sock.each_line {|line|
|
|
321
|
+
if sock.eof?
|
|
322
|
+
holdover = line
|
|
323
|
+
else
|
|
324
|
+
yield line
|
|
325
|
+
end
|
|
326
|
+
}
|
|
327
|
+
sock.close
|
|
328
|
+
}
|
|
329
|
+
yield holdover
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# Get the parameters for this request, given the specified format.
|
|
336
|
+
def params(format)
|
|
337
|
+
p = @query.params.merge("start" => @start, "format" => format)
|
|
338
|
+
p["size"] = @size unless @size.nil?
|
|
339
|
+
return p
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Check that the request was successful according the metadata
|
|
343
|
+
# passed with the result.
|
|
179
344
|
def check_result_set(container)
|
|
180
345
|
begin
|
|
181
346
|
result_set = JSON.parse(container)
|
|
@@ -189,6 +354,7 @@ module InterMine::Results
|
|
|
189
354
|
end
|
|
190
355
|
end
|
|
191
356
|
|
|
357
|
+
# Errors if there are problems retrieving results from the webservice.
|
|
192
358
|
class ServiceError < RuntimeError
|
|
193
359
|
end
|
|
194
360
|
|
data/lib/intermine/service.rb
CHANGED
data/lib/intermine/version.rb
CHANGED
data/test/data/resultrow.json
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
[
|
|
1
|
+
[
|
|
2
|
+
{"id":320000014,"value":"DepartmentA1","class":"Department","url":"/report.do?id=320000014"},
|
|
3
|
+
{"id":320000015,"value":"EmployeeA1","class":"Manager","url":"/report.do?id=320000015"},
|
|
4
|
+
{"id":320000015,"value":33,"class":"Manager","url":"/report.do?id=320000015"}
|
|
5
|
+
]
|
data/test/test_result_row.rb
CHANGED
|
@@ -14,8 +14,8 @@ class TestResults < Test::Unit::TestCase
|
|
|
14
14
|
File.dirname(__FILE__) + "/data/resultrow.json", "r")
|
|
15
15
|
@data = file.read
|
|
16
16
|
@json = JSON.parse(@data)
|
|
17
|
-
@view = ["Company.departments.name","Company.departments.manager.name"]
|
|
18
|
-
@bad_view = ["Company.departments.name","Company.departments.manager.name"
|
|
17
|
+
@view = ["Company.departments.name","Company.departments.manager.name", "Company.departments.manager.age"]
|
|
18
|
+
@bad_view = ["Company.departments.name","Company.departments.manager.name"]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def test_initialize
|
|
@@ -54,7 +54,11 @@ class TestResults < Test::Unit::TestCase
|
|
|
54
54
|
assert_equal(rr[0], "DepartmentA1")
|
|
55
55
|
assert_equal(rr[1], "EmployeeA1")
|
|
56
56
|
|
|
57
|
-
rr
|
|
57
|
+
assert_equal(rr[1..2], ["EmployeeA1", 33])
|
|
58
|
+
assert_equal(rr[0, 2], ["DepartmentA1", "EmployeeA1"])
|
|
59
|
+
|
|
60
|
+
assert_equal(rr.first, "DepartmentA1")
|
|
61
|
+
assert_equal(rr.last, 33)
|
|
58
62
|
|
|
59
63
|
assert_raise IndexError do
|
|
60
64
|
rr[3]
|
|
@@ -62,6 +66,25 @@ class TestResults < Test::Unit::TestCase
|
|
|
62
66
|
|
|
63
67
|
end
|
|
64
68
|
|
|
69
|
+
def test_each
|
|
70
|
+
|
|
71
|
+
rr = ResultsRow.new(@data, @view)
|
|
72
|
+
expected = %w{DepartmentA1 EmployeeA1} << 33
|
|
73
|
+
|
|
74
|
+
c = 0
|
|
75
|
+
rr.each do |val|
|
|
76
|
+
assert_equal(expected[c], val)
|
|
77
|
+
c += 1
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
c = 0
|
|
81
|
+
rr.each do |key, val|
|
|
82
|
+
assert_equal(@view[c], key)
|
|
83
|
+
assert_equal(expected[c], val)
|
|
84
|
+
c += 1
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
65
88
|
def test_hash_access
|
|
66
89
|
|
|
67
90
|
rr = ResultsRow.new(@data, @view)
|
|
@@ -76,7 +99,7 @@ class TestResults < Test::Unit::TestCase
|
|
|
76
99
|
|
|
77
100
|
rr = ResultsRow.new(@json, @view)
|
|
78
101
|
|
|
79
|
-
assert_raise
|
|
102
|
+
assert_raise ArgumentError do
|
|
80
103
|
rr["foo"]
|
|
81
104
|
end
|
|
82
105
|
|
|
@@ -84,7 +107,7 @@ class TestResults < Test::Unit::TestCase
|
|
|
84
107
|
|
|
85
108
|
def test_to_a
|
|
86
109
|
|
|
87
|
-
expected = %w{DepartmentA1 EmployeeA1}
|
|
110
|
+
expected = %w{DepartmentA1 EmployeeA1} << 33
|
|
88
111
|
|
|
89
112
|
rr = ResultsRow.new(@data, @view)
|
|
90
113
|
|
|
@@ -101,7 +124,8 @@ class TestResults < Test::Unit::TestCase
|
|
|
101
124
|
|
|
102
125
|
expected = {
|
|
103
126
|
"Company.departments.name" => "DepartmentA1",
|
|
104
|
-
"Company.departments.manager.name" => "EmployeeA1"
|
|
127
|
+
"Company.departments.manager.name" => "EmployeeA1",
|
|
128
|
+
"Company.departments.manager.age" => 33
|
|
105
129
|
}
|
|
106
130
|
assert_equal(expected, rr.to_h)
|
|
107
131
|
|
data/test/unit_tests.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: intermine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 415
|
|
5
5
|
prerelease: false
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
8
|
- 98
|
|
9
|
-
-
|
|
10
|
-
version: 0.98.
|
|
9
|
+
- 4
|
|
10
|
+
version: 0.98.04
|
|
11
11
|
platform: ruby
|
|
12
12
|
authors:
|
|
13
13
|
- Alex Kalderimis
|
|
@@ -15,7 +15,7 @@ autorequire:
|
|
|
15
15
|
bindir: bin
|
|
16
16
|
cert_chain: []
|
|
17
17
|
|
|
18
|
-
date: 2011-08-
|
|
18
|
+
date: 2011-08-02 00:00:00 +01:00
|
|
19
19
|
default_executable:
|
|
20
20
|
dependencies:
|
|
21
21
|
- !ruby/object:Gem::Dependency
|