solvebio 1.5.2 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.travis.yml +13 -8
  2. data/Gemfile +4 -2
  3. data/README.md +5 -3
  4. data/demo/cheatsheet.rb +31 -0
  5. data/lib/cli/auth.rb +6 -6
  6. data/lib/cli/irbrc.rb +2 -1
  7. data/lib/cli/options.rb +1 -1
  8. data/lib/client.rb +85 -83
  9. data/lib/credentials.rb +2 -2
  10. data/lib/main.rb +11 -2
  11. data/lib/query.rb +5 -6
  12. data/lib/resource/annotation.rb +23 -0
  13. data/lib/resource/apiresource.rb +241 -0
  14. data/lib/resource/dataset.rb +91 -0
  15. data/lib/resource/datasetfield.rb +37 -0
  16. data/lib/resource/depository.rb +50 -0
  17. data/lib/resource/depositoryversion.rb +69 -0
  18. data/lib/resource/main.rb +123 -0
  19. data/lib/resource/sample.rb +75 -0
  20. data/lib/{solveobject.rb → resource/solveobject.rb} +43 -22
  21. data/lib/resource/user.rb +5 -0
  22. data/lib/solvebio.rb +1 -1
  23. data/lib/util.rb +29 -0
  24. data/solvebio.gemspec +7 -4
  25. data/test/Makefile +9 -0
  26. data/test/data/sample.vcf.gz +0 -0
  27. data/test/helper.rb +9 -2
  28. data/test/test-annotation.rb +46 -0
  29. data/test/test-auth.rb +8 -4
  30. data/test/test-client.rb +6 -6
  31. data/test/test-conversion.rb +13 -0
  32. data/test/test-dataset.rb +42 -0
  33. data/test/test-depository.rb +35 -0
  34. data/test/test-netrc.rb +13 -3
  35. data/test/test-query-batch.rb +26 -46
  36. data/test/test-query-paging.rb +77 -98
  37. data/test/test-query.rb +47 -64
  38. data/test/test-resource.rb +8 -15
  39. data/test/test-sample-access.rb +59 -0
  40. data/test/test-sample-download.rb +20 -0
  41. data/test/test-tabulate.rb +27 -23
  42. data/test/{test-solveobject.rb → test-util.rb} +17 -2
  43. metadata +128 -56
  44. data/lib/apiresource.rb +0 -130
  45. data/lib/help.rb +0 -46
  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