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.
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