solvebio 1.5.2 → 1.6.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/.travis.yml +13 -8
- data/Gemfile +4 -2
- data/README.md +5 -3
- data/demo/cheatsheet.rb +31 -0
- data/lib/cli/auth.rb +6 -6
- data/lib/cli/irbrc.rb +2 -1
- data/lib/cli/options.rb +1 -1
- data/lib/client.rb +85 -83
- data/lib/credentials.rb +2 -2
- data/lib/main.rb +11 -2
- data/lib/query.rb +5 -6
- data/lib/resource/annotation.rb +23 -0
- data/lib/resource/apiresource.rb +241 -0
- data/lib/resource/dataset.rb +91 -0
- data/lib/resource/datasetfield.rb +37 -0
- data/lib/resource/depository.rb +50 -0
- data/lib/resource/depositoryversion.rb +69 -0
- data/lib/resource/main.rb +123 -0
- data/lib/resource/sample.rb +75 -0
- data/lib/{solveobject.rb → resource/solveobject.rb} +43 -22
- data/lib/resource/user.rb +5 -0
- data/lib/solvebio.rb +1 -1
- data/lib/util.rb +29 -0
- data/solvebio.gemspec +7 -4
- data/test/Makefile +9 -0
- data/test/data/sample.vcf.gz +0 -0
- data/test/helper.rb +9 -2
- data/test/test-annotation.rb +46 -0
- data/test/test-auth.rb +8 -4
- data/test/test-client.rb +6 -6
- data/test/test-conversion.rb +13 -0
- data/test/test-dataset.rb +42 -0
- data/test/test-depository.rb +35 -0
- data/test/test-netrc.rb +13 -3
- data/test/test-query-batch.rb +26 -46
- data/test/test-query-paging.rb +77 -98
- data/test/test-query.rb +47 -64
- data/test/test-resource.rb +8 -15
- data/test/test-sample-access.rb +59 -0
- data/test/test-sample-download.rb +20 -0
- data/test/test-tabulate.rb +27 -23
- data/test/{test-solveobject.rb → test-util.rb} +17 -2
- metadata +128 -56
- data/lib/apiresource.rb +0 -130
- data/lib/help.rb +0 -46
- data/lib/resource.rb +0 -414
@@ -0,0 +1,23 @@
|
|
1
|
+
# Solvebio API Resource for Samples
|
2
|
+
require_relative 'apiresource'
|
3
|
+
require_relative 'solveobject'
|
4
|
+
require_relative '../errors'
|
5
|
+
|
6
|
+
# Annotations are genomic samples that have been annotated.
|
7
|
+
# See https://www.solvebio.com/docs/api/?ruby#annotations
|
8
|
+
class SolveBio::Annotation < SolveBio::APIResource
|
9
|
+
include SolveBio::CreateableAPIResource
|
10
|
+
include SolveBio::DeletableAPIResource
|
11
|
+
include SolveBio::DownloadableAPIResource
|
12
|
+
include SolveBio::ListableAPIResource
|
13
|
+
end
|
14
|
+
|
15
|
+
if __FILE__ == $0
|
16
|
+
unless SolveBio::API_HOST == 'https://api.solvebio.com'
|
17
|
+
SolveBio::SolveObject::CONVERSION = {
|
18
|
+
'Annotation' => SolveBio::Annotation,
|
19
|
+
} unless defined? SolveBio::SolveObject::CONVERSION
|
20
|
+
response = SolveBio::Annotation.all()
|
21
|
+
puts response
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
require 'rest_client'
|
5
|
+
require_relative 'solveobject'
|
6
|
+
require_relative '../main'
|
7
|
+
require_relative '../client'
|
8
|
+
require_relative '../util'
|
9
|
+
require_relative '../errors'
|
10
|
+
|
11
|
+
class SolveBio::APIResource < SolveBio::SolveObject
|
12
|
+
|
13
|
+
def self.retrieve(cls, id, params={})
|
14
|
+
instance = cls.new(id, params)
|
15
|
+
instance.refresh()
|
16
|
+
instance
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.class_to_api_name(cls)
|
20
|
+
cls_name = cls.to_s.sub('SolveBio::', '')
|
21
|
+
SolveBio::camelcase_to_underscore(SolveBio::pluralize(cls_name))
|
22
|
+
end
|
23
|
+
|
24
|
+
def refresh
|
25
|
+
refresh_from request('get', instance_url)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.class_url(cls)
|
30
|
+
cls_name = cls.to_s.sub('SolveBio::', '')
|
31
|
+
"/v1/#{class_to_api_name(cls_name)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# Get instance URL by ID or full name (if available)
|
36
|
+
def instance_url
|
37
|
+
id = self['id']
|
38
|
+
base = SolveBio::APIResource.class_url(self.class)
|
39
|
+
|
40
|
+
if id
|
41
|
+
return "#{base}/#{id}"
|
42
|
+
else
|
43
|
+
msg = 'Could not determine which URL to request: %s instance ' +
|
44
|
+
'has invalid ID: %s' % [self.class, id]
|
45
|
+
raise Exception, msg
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module SolveBio::CreateableAPIResource
|
51
|
+
|
52
|
+
def self.included base
|
53
|
+
base.extend ClassMethods
|
54
|
+
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
def create(params={})
|
58
|
+
url = SolveBio::APIResource.class_url(self)
|
59
|
+
response = SolveBio::Client.client
|
60
|
+
.request('post', url, {:payload => params} )
|
61
|
+
to_solve_object(response)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
module SolveBio::DeletableAPIResource
|
68
|
+
|
69
|
+
def delete(params={})
|
70
|
+
begin
|
71
|
+
self.refresh_from(SolveBio::Client.client
|
72
|
+
.request('delete', instance_url,
|
73
|
+
{:payload => params}))
|
74
|
+
rescue SolveBio::Error => response
|
75
|
+
response.to_solve_object(cls)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module SolveBio::DownloadableAPIResource
|
81
|
+
|
82
|
+
#
|
83
|
+
# Download the file to the specified path (or a temp. dir).
|
84
|
+
#
|
85
|
+
def download(path=nil)
|
86
|
+
download_url = instance_url + '/download'
|
87
|
+
response = SolveBio::Client.client.get(download_url, :raw => true)
|
88
|
+
|
89
|
+
if response.code != 302
|
90
|
+
# Some kind of error. We expect a redirect
|
91
|
+
raise SolveError('Could not download file: response code' %
|
92
|
+
response.status_code)
|
93
|
+
end
|
94
|
+
|
95
|
+
download_url = response.headers[:location]
|
96
|
+
filename = download_url.split('%3B%20filename%3D')[1]
|
97
|
+
|
98
|
+
path = Dir.tmpdir unless path
|
99
|
+
filename = File.join(path, filename)
|
100
|
+
response = nil
|
101
|
+
|
102
|
+
response = SolveBio::Client.client.get(download_url, :raw => true,
|
103
|
+
:default_headers => false)
|
104
|
+
|
105
|
+
File.open(filename, 'wb') do |fh|
|
106
|
+
fh.write(response.body)
|
107
|
+
end
|
108
|
+
|
109
|
+
self['filename'] = filename
|
110
|
+
self['code'] = response.code
|
111
|
+
self
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
module SolveBio::HelpableAPIResource
|
116
|
+
|
117
|
+
attr_reader :have_launchy
|
118
|
+
|
119
|
+
@@have_launchy = false
|
120
|
+
begin
|
121
|
+
@@have_launchy = require 'launchy'
|
122
|
+
rescue LoadError
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.included base
|
126
|
+
base.send :include, InstanceMethods
|
127
|
+
end
|
128
|
+
|
129
|
+
module InstanceMethods
|
130
|
+
def help
|
131
|
+
open_help(self['full_name'])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def open_help(path)
|
136
|
+
url = URI::join('https://www.solvebio.com/', path)
|
137
|
+
if @@have_launchy
|
138
|
+
Launchy.open(url)
|
139
|
+
else
|
140
|
+
puts('The SolveBio Ruby client needs the "launchy" gem to ' +
|
141
|
+
"open help url: #{url.to_s}")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
module SolveBio::ListableAPIResource
|
147
|
+
|
148
|
+
def self.included base
|
149
|
+
base.extend ClassMethods
|
150
|
+
end
|
151
|
+
|
152
|
+
module ClassMethods
|
153
|
+
def all(params={})
|
154
|
+
url = SolveBio::APIResource.class_url(self)
|
155
|
+
response = SolveBio::Client.client
|
156
|
+
.request('get', url, {:params => params})
|
157
|
+
return response.to_solvebio(self)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# How many items are in this list?
|
162
|
+
def size
|
163
|
+
self[:total]
|
164
|
+
end
|
165
|
+
alias :total :size
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
module SolveBio::SearchableAPIResource
|
170
|
+
|
171
|
+
def self.included base
|
172
|
+
base.extend ClassMethods
|
173
|
+
end
|
174
|
+
|
175
|
+
module ClassMethods
|
176
|
+
def search(query='', params={})
|
177
|
+
params['q'] = query
|
178
|
+
url = SolveBio::APIResource.class_url(self)
|
179
|
+
response = SolveBio::Client.client
|
180
|
+
.request('get', url, {:params => params})
|
181
|
+
response.to_solvebio
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
module SolveBio::SingletonAPIResource
|
187
|
+
|
188
|
+
def self.class_to_api_name(cls)
|
189
|
+
cls_name = cls.to_s.sub('SolveBio::', '')
|
190
|
+
SolveBio::camelcase_to_underscore(cls_name)
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.retrieve(cls)
|
194
|
+
super(SingletonAPIResource, cls).retrieve(nil)
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.class_url(cls)
|
198
|
+
"/v1/#{class_to_api_name(cls)}"
|
199
|
+
end
|
200
|
+
|
201
|
+
def instance_url
|
202
|
+
class_url
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
module SolveBio::UpdateableAPIResource
|
207
|
+
|
208
|
+
def self.included base
|
209
|
+
base.extend ClassMethods
|
210
|
+
end
|
211
|
+
|
212
|
+
module ClassMethods
|
213
|
+
def save
|
214
|
+
refresh_from(request('patch', instance_url(),
|
215
|
+
{:params => serialize(self)}))
|
216
|
+
return self
|
217
|
+
end
|
218
|
+
|
219
|
+
def serialize(obj)
|
220
|
+
params = {}
|
221
|
+
if obj.unsaved_values
|
222
|
+
obj.unsaved_values.each do |k|
|
223
|
+
next if k == 'id'
|
224
|
+
params[k] = getattr(obj, k) or ''
|
225
|
+
end
|
226
|
+
end
|
227
|
+
params
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Demo code
|
233
|
+
if __FILE__ == $0
|
234
|
+
include SolveBio::HelpableAPIResource
|
235
|
+
if @@have_launchy
|
236
|
+
open_help('docs')
|
237
|
+
sleep 1
|
238
|
+
else
|
239
|
+
puts "Don't have launchy"
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative 'apiresource'
|
2
|
+
require_relative '../query'
|
3
|
+
|
4
|
+
class SolveBio::Dataset < SolveBio::APIResource
|
5
|
+
|
6
|
+
include SolveBio::CreateableAPIResource
|
7
|
+
include SolveBio::ListableAPIResource
|
8
|
+
include SolveBio::UpdateableAPIResource
|
9
|
+
include SolveBio::HelpableAPIResource
|
10
|
+
|
11
|
+
ALLOW_FULL_NAME_ID = true
|
12
|
+
|
13
|
+
# FIXME: base off of DepositoryVersion::FULL_NAME_REGEX
|
14
|
+
# Sample matches:
|
15
|
+
# 'Clinvar/2.0.0-1/Variants'
|
16
|
+
# 'omim/0.0.1-1/omim'
|
17
|
+
FULL_NAME_REGEX = %r{^([\w\-\.]+/){2}[\w\-\.]+$}
|
18
|
+
|
19
|
+
# Dataset lookup by full string name
|
20
|
+
def self.retrieve(id, params={})
|
21
|
+
if id.kind_of?(String)
|
22
|
+
_id = id.strip
|
23
|
+
id = nil
|
24
|
+
if _id =~ FULL_NAME_REGEX
|
25
|
+
params['full_name'] = _id
|
26
|
+
else
|
27
|
+
raise Exception, 'Unrecognized full name.'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
return SolveBio::APIResource.
|
32
|
+
retrieve(SolveBio::Dataset, id, params)
|
33
|
+
end
|
34
|
+
|
35
|
+
def depository_version
|
36
|
+
return SolveBio::DepositoryVersion.
|
37
|
+
retrieve(self['depository_version'])
|
38
|
+
end
|
39
|
+
|
40
|
+
def depository
|
41
|
+
return SolveBio::Depository.retrieve(self['depository'])
|
42
|
+
end
|
43
|
+
|
44
|
+
def fields(name=nil, params={})
|
45
|
+
unless self['fields_url']
|
46
|
+
raise Exception,
|
47
|
+
'Please use Dataset.retrieve({ID}) before doing looking ' +
|
48
|
+
'up fields'
|
49
|
+
end
|
50
|
+
|
51
|
+
if name
|
52
|
+
# construct the field's full_name if a field name is provided
|
53
|
+
return DatasetField.retrieve("#{self['full_name']}/#{name}")
|
54
|
+
end
|
55
|
+
|
56
|
+
result = SolveBio::Client.
|
57
|
+
client.request('get', self['fields_url'])
|
58
|
+
result.to_solvebio(self.class)
|
59
|
+
end
|
60
|
+
|
61
|
+
def query(params={})
|
62
|
+
paging = false
|
63
|
+
if params.member?(:paging)
|
64
|
+
paging = params[:paging]
|
65
|
+
params.delete(:paging)
|
66
|
+
end
|
67
|
+
q = paging ? SolveBio::PagingQuery.new(self['id'], params) :
|
68
|
+
SolveBio::Query.new(self['id'], params)
|
69
|
+
|
70
|
+
if params[:filters]
|
71
|
+
return q.filter(params[:filters])
|
72
|
+
end
|
73
|
+
return q
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def data_url
|
78
|
+
unless self['data_url']
|
79
|
+
unless self['id']
|
80
|
+
raise Exception,
|
81
|
+
'No Dataset ID was provided. ' +
|
82
|
+
'Please instantiate the Dataset ' +
|
83
|
+
'object with an ID or full_name.'
|
84
|
+
end
|
85
|
+
# automatically construct the data_url from the ID
|
86
|
+
return instance_url() + '/data'
|
87
|
+
end
|
88
|
+
return self['data_url']
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'apiresource'
|
2
|
+
|
3
|
+
class SolveBio::DatasetField < SolveBio::APIResource
|
4
|
+
|
5
|
+
include SolveBio::CreateableAPIResource
|
6
|
+
include SolveBio::ListableAPIResource
|
7
|
+
include SolveBio::UpdateableAPIResource
|
8
|
+
|
9
|
+
ALLOW_FULL_NAME_ID = true
|
10
|
+
FULL_NAME_REGEX = %r{^([\w\-\.]+/){3}[\w\-\.]+$}
|
11
|
+
|
12
|
+
# Supports lookup by ID or full name
|
13
|
+
def self.retrieve(id, params={})
|
14
|
+
if id.kind_of?(String)
|
15
|
+
_id = id.strip
|
16
|
+
id = nil
|
17
|
+
if FULL_NAME_REGEX =~ _id
|
18
|
+
params['full_name'] = _id
|
19
|
+
else
|
20
|
+
raise Exception, 'Unrecognized full name.'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
SolveBio::APIResource.
|
25
|
+
retrieve(SolveBio::DatasetField, id, params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def facets(params={})
|
29
|
+
response = SolveBio::Client.client
|
30
|
+
.request 'get', self[:facets_url], {:params => params}
|
31
|
+
response.to_solvebio(SolveBio::SolveObject)
|
32
|
+
end
|
33
|
+
|
34
|
+
def help
|
35
|
+
facets
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'apiresource'
|
2
|
+
|
3
|
+
# A depository (or data repository) is like a source code
|
4
|
+
# repository, but for datasets. Depositories have one or more
|
5
|
+
# versions, which in turn contain one or more datasets. Typically,
|
6
|
+
# depositories contain a series of datasets that are compatible with
|
7
|
+
# each other (i.e. they come from the same data source or project).
|
8
|
+
class SolveBio::Depository < SolveBio::APIResource
|
9
|
+
|
10
|
+
include SolveBio::CreateableAPIResource
|
11
|
+
include SolveBio::ListableAPIResource
|
12
|
+
include SolveBio::SearchableAPIResource
|
13
|
+
include SolveBio::UpdateableAPIResource
|
14
|
+
include SolveBio::HelpableAPIResource
|
15
|
+
|
16
|
+
ALLOW_FULL_NAME_ID = true
|
17
|
+
FULL_NAME_REGEX = %r{^[\w\-\.]+$}
|
18
|
+
|
19
|
+
# lookup by ID or full name
|
20
|
+
def self.retrieve(id, params={})
|
21
|
+
if id.kind_of?(String)
|
22
|
+
_id = id.strip
|
23
|
+
id = nil
|
24
|
+
if _id =~ FULL_NAME_REGEX
|
25
|
+
params['full_name'] = _id
|
26
|
+
else
|
27
|
+
raise Exception, 'Unrecognized full name: "%s"' % _id
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
return SolveBio::APIResource.
|
32
|
+
retrieve(SolveBio::Depository, id, params)
|
33
|
+
end
|
34
|
+
|
35
|
+
def versions_url
|
36
|
+
return SolveBio::APIResource.
|
37
|
+
retrieve(SolveBio::Depository, self['id'])['versions_url']
|
38
|
+
end
|
39
|
+
|
40
|
+
def versions(name=nil, params={})
|
41
|
+
# construct the depo version full name
|
42
|
+
return SolveBio::DepositoryVersion.
|
43
|
+
retrieve("#{self['full_name']}/#{name}") if name
|
44
|
+
|
45
|
+
response = SolveBio::Client.client
|
46
|
+
.request('get', versions_url, {:params => params})
|
47
|
+
return response.to_solvebio
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative 'apiresource'
|
2
|
+
|
3
|
+
class SolveBio::DepositoryVersion < SolveBio::APIResource
|
4
|
+
|
5
|
+
|
6
|
+
include SolveBio::CreateableAPIResource
|
7
|
+
include SolveBio::ListableAPIResource
|
8
|
+
include SolveBio::UpdateableAPIResource
|
9
|
+
include SolveBio::HelpableAPIResource
|
10
|
+
|
11
|
+
ALLOW_FULL_NAME_ID = true
|
12
|
+
|
13
|
+
# FIXME: base off of Depository::FULL_NAME_REGEX
|
14
|
+
# Sample matches:
|
15
|
+
# 'Clinvar/2.0.0-1'
|
16
|
+
FULL_NAME_REGEX = %r{^[\w\.]+/[\w\-\.]+$}
|
17
|
+
|
18
|
+
# Supports lookup by full name
|
19
|
+
def self.retrieve(id, params={})
|
20
|
+
if id.kind_of?(String)
|
21
|
+
_id = id.strip
|
22
|
+
id = nil
|
23
|
+
if _id =~ FULL_NAME_REGEX
|
24
|
+
params['full_name'] = _id
|
25
|
+
else
|
26
|
+
raise Exception, 'Unrecognized full name.'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
return SolveBio::APIResource.
|
31
|
+
retrieve(SolveBio::DepositoryVersion, id, params)
|
32
|
+
end
|
33
|
+
|
34
|
+
def datasets_url(name=nil)
|
35
|
+
name ||= self['name']
|
36
|
+
"#{self['full_name']}/#{name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def datasets(name=nil, params={})
|
40
|
+
if name
|
41
|
+
# construct the dataset full name
|
42
|
+
return SolveBio::Dataset.retrieve(datasets_url(name))
|
43
|
+
end
|
44
|
+
|
45
|
+
response = SolveBio::Client.client
|
46
|
+
request('get', datasets_url, {:params => params})
|
47
|
+
return response.to_solvebio
|
48
|
+
end
|
49
|
+
|
50
|
+
# Set the released flag and optional release date and save
|
51
|
+
def release(released_at=nil)
|
52
|
+
if released_at
|
53
|
+
@released_at = released_at
|
54
|
+
end
|
55
|
+
@released = true
|
56
|
+
save()
|
57
|
+
end
|
58
|
+
|
59
|
+
# Unset the released flag and save
|
60
|
+
def unrelease
|
61
|
+
@released = false
|
62
|
+
save()
|
63
|
+
end
|
64
|
+
|
65
|
+
def <=>(other)
|
66
|
+
self[:full_name] <=> other[:full_name]
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|