solvebio 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +13 -0
  3. data/Gemfile +4 -0
  4. data/Gemspec +3 -0
  5. data/LICENSE +21 -0
  6. data/Makefile +17 -0
  7. data/README.md +64 -0
  8. data/Rakefile +59 -0
  9. data/bin/solvebio.rb +36 -0
  10. data/demo/README.md +14 -0
  11. data/demo/dataset/facets.rb +13 -0
  12. data/demo/dataset/field.rb +13 -0
  13. data/demo/depository/README.md +24 -0
  14. data/demo/depository/all.rb +13 -0
  15. data/demo/depository/retrieve.rb +13 -0
  16. data/demo/depository/versions-all.rb +13 -0
  17. data/demo/query/query-filter.rb +30 -0
  18. data/demo/query/query.rb +13 -0
  19. data/demo/query/range-filter.rb +18 -0
  20. data/demo/test-api.rb +98 -0
  21. data/lib/apiresource.rb +130 -0
  22. data/lib/cli/auth.rb +122 -0
  23. data/lib/cli/help.rb +13 -0
  24. data/lib/cli/irb.rb +58 -0
  25. data/lib/cli/irbrc.rb +53 -0
  26. data/lib/cli/options.rb +75 -0
  27. data/lib/client.rb +152 -0
  28. data/lib/credentials.rb +67 -0
  29. data/lib/errors.rb +81 -0
  30. data/lib/filter.rb +312 -0
  31. data/lib/help.rb +46 -0
  32. data/lib/locale.rb +47 -0
  33. data/lib/main.rb +37 -0
  34. data/lib/query.rb +415 -0
  35. data/lib/resource.rb +414 -0
  36. data/lib/solvebio.rb +14 -0
  37. data/lib/solveobject.rb +101 -0
  38. data/lib/tabulate.rb +706 -0
  39. data/solvebio.gemspec +75 -0
  40. data/test/data/netrc-save +6 -0
  41. data/test/helper.rb +3 -0
  42. data/test/test-auth.rb +54 -0
  43. data/test/test-client.rb +27 -0
  44. data/test/test-error.rb +36 -0
  45. data/test/test-filter.rb +70 -0
  46. data/test/test-netrc.rb +42 -0
  47. data/test/test-query-batch.rb +60 -0
  48. data/test/test-query-init.rb +29 -0
  49. data/test/test-query-paging.rb +123 -0
  50. data/test/test-query.rb +88 -0
  51. data/test/test-resource.rb +47 -0
  52. data/test/test-solveobject.rb +27 -0
  53. data/test/test-tabulate.rb +127 -0
  54. metadata +158 -0
data/lib/resource.rb ADDED
@@ -0,0 +1,414 @@
1
+ # -*- coding: utf-8 -*-
2
+ # from utils.tabulate import tabulate
3
+
4
+ require_relative 'solveobject'
5
+ require_relative 'apiresource'
6
+ require_relative 'client'
7
+ require_relative 'query'
8
+ require_relative 'help'
9
+
10
+ class SolveBio::ListObject < SolveBio::SolveObject
11
+
12
+ include Enumerable
13
+
14
+ def all(params={})
15
+ return request('get', self['url'], params)
16
+ end
17
+
18
+ def create(params={})
19
+ return request('post', self['url'], params)
20
+ end
21
+
22
+ def next_page(params={})
23
+ if self['links']['next']
24
+ return request('get', self['links']['next'], params)
25
+ end
26
+ return nil
27
+ end
28
+
29
+ def prev_page(params={})
30
+ if self['links']['prev']
31
+ request('get', self['links']['prev'], params)
32
+ end
33
+ return nil
34
+ end
35
+
36
+ def at(i)
37
+ self.to_a[i]
38
+ end
39
+
40
+ def to_a
41
+ return to_solve_object(self['data'])
42
+ end
43
+
44
+ def each(*pass)
45
+ return self unless block_given?
46
+ i = 0
47
+ ary = self.dup
48
+ done = false
49
+ until done
50
+ if i >= ary['data'].size
51
+ ary = next_page
52
+ break unless ary
53
+ i = 0
54
+ end
55
+ yield(ary.at(i))
56
+ i += 1
57
+ end
58
+ return self
59
+ end
60
+
61
+ def first
62
+ self['data'][0]
63
+ end
64
+
65
+ # def max
66
+ # self['data'][self['total']]
67
+ # end
68
+
69
+ end
70
+
71
+
72
+ class SingletonAPIResource < SolveBio::APIResource
73
+
74
+ def self.retrieve(cls)
75
+ return super(SingletonAPIResource, cls).retrieve(nil)
76
+ end
77
+
78
+ def self.class_url(cls)
79
+ # cls_name = cls.class_name()
80
+ cls_name = cls.to_s.sub('SolveBio::', '')
81
+ cls_name = camelcase_to_underscore(cls_name)
82
+ return "/v1/%s #{cls_name}"
83
+ end
84
+
85
+ def instance_url
86
+ class_url()
87
+ end
88
+ end
89
+
90
+
91
+ # API resources
92
+
93
+ class SolveBio::User < SingletonAPIResource
94
+ end
95
+
96
+
97
+ class SolveBio::Depository < SolveBio::APIResource
98
+
99
+ include SolveBio::CreateableAPIResource
100
+ include SolveBio::ListableAPIResource
101
+ include SolveBio::SearchableAPIResource
102
+ include SolveBio::UpdateableAPIResource
103
+ include SolveBio::HelpableAPIResource
104
+
105
+ ALLOW_FULL_NAME_ID = true
106
+ FULL_NAME_REGEX = %r{^[\w\-\.]+$}
107
+
108
+ # lookup by ID or full name
109
+ def self.retrieve(id, params={})
110
+ if id.kind_of?(String)
111
+ _id = id.strip
112
+ id = nil
113
+ if _id =~ FULL_NAME_REGEX
114
+ params['full_name'] = _id
115
+ else
116
+ raise Exception, 'Unrecognized full name: "%s"' % _id
117
+ end
118
+ end
119
+
120
+ return SolveBio::APIResource.
121
+ retrieve(SolveBio::Depository, id, params)
122
+ end
123
+
124
+ def versions_url
125
+ return SolveBio::APIResource.
126
+ retrieve(SolveBio::Depository, self['id'])['versions_url']
127
+ end
128
+
129
+ def versions(name=nil, params={})
130
+ # construct the depo version full name
131
+ return SolveBio::DepositoryVersion.
132
+ retrieve("#{self['full_name']}/#{name}") if name
133
+
134
+ response = SolveBio::Client.
135
+ client.request('get', versions_url, params)
136
+ return response.to_solvebio
137
+ end
138
+
139
+ end
140
+
141
+ class SolveBio::DepositoryVersion < SolveBio::APIResource
142
+
143
+
144
+ include SolveBio::CreateableAPIResource
145
+ include SolveBio::ListableAPIResource
146
+ include SolveBio::UpdateableAPIResource
147
+ include SolveBio::HelpableAPIResource
148
+
149
+ ALLOW_FULL_NAME_ID = true
150
+
151
+ # FIXME: base off of Depository::FULL_NAME_REGEX
152
+ # Sample matches:
153
+ # 'Clinvar/2.0.0-1'
154
+ FULL_NAME_REGEX = %r{^[\w\.]+/[\w\-\.]+$}
155
+
156
+ # Supports lookup by full name
157
+ def self.retrieve(id, params={})
158
+ if id.kind_of?(String)
159
+ _id = id.strip
160
+ id = nil
161
+ if _id =~ FULL_NAME_REGEX
162
+ params['full_name'] = _id
163
+ else
164
+ raise Exception, 'Unrecognized full name.'
165
+ end
166
+ end
167
+
168
+ return SolveBio::APIResource.
169
+ retrieve(SolveBio::DepositoryVersion, id, params)
170
+ end
171
+
172
+ def datasets_url(name=nil)
173
+ name ||= self['name']
174
+ "#{self['full_name']}/#{name}"
175
+ end
176
+
177
+ def datasets(name=nil, params={})
178
+ if name
179
+ # construct the dataset full name
180
+ return SolveBio::Dataset.retrieve(datasets_url(name))
181
+ end
182
+
183
+ response = SolveBio::Client.
184
+ client.request('get', datasets_url, params)
185
+ return response.to_solvebio
186
+ end
187
+
188
+ # Set the released flag and optional release date and save
189
+ def release(released_at=nil)
190
+ if released_at
191
+ @released_at = released_at
192
+ end
193
+ @released = true
194
+ save()
195
+ end
196
+
197
+ # Unset the released flag and save
198
+ def unrelease
199
+ @released = false
200
+ save()
201
+ end
202
+
203
+ # FIXME: is there a better field to sort on?
204
+ def <=>(other)
205
+ self.id <=> other.id
206
+ end
207
+
208
+ end
209
+
210
+ class SolveBio::Dataset < SolveBio::APIResource
211
+
212
+ include SolveBio::CreateableAPIResource
213
+ include SolveBio::ListableAPIResource
214
+ include SolveBio::UpdateableAPIResource
215
+ include SolveBio::HelpableAPIResource
216
+
217
+ ALLOW_FULL_NAME_ID = true
218
+
219
+ # FIXME: base off of DepositoryVersion::FULL_NAME_REGEX
220
+ # Sample matches:
221
+ # 'Clinvar/2.0.0-1/Variants'
222
+ # 'omim/0.0.1-1/omim'
223
+ FULL_NAME_REGEX = %r{^([\w\-\.]+/){2}[\w\-\.]+$}
224
+
225
+ # Dataset lookup by full string name
226
+ def self.retrieve(id, params={})
227
+ if id.kind_of?(String)
228
+ _id = id.strip
229
+ id = nil
230
+ if _id =~ FULL_NAME_REGEX
231
+ params['full_name'] = _id
232
+ else
233
+ raise Exception, 'Unrecognized full name.'
234
+ end
235
+ end
236
+
237
+ return SolveBio::APIResource.
238
+ retrieve(SolveBio::Dataset, id, params)
239
+ end
240
+
241
+ def depository_version
242
+ return SolveBio::DepositoryVersion.
243
+ retrieve(self['depository_version'])
244
+ end
245
+
246
+ def depository
247
+ return SolveBio::Depository.retrieve(self['depository'])
248
+ end
249
+
250
+ def fields(name=nil, params={})
251
+ unless self['fields_url']
252
+ raise Exception,
253
+ 'Please use Dataset.retrieve({ID}) before doing looking ' +
254
+ 'up fields'
255
+ end
256
+
257
+ if name
258
+ # construct the field's full_name if a field name is provided
259
+ return DatasetField.retrieve("#{self['full_name']}/#{name}")
260
+ end
261
+
262
+ SolveBio::Client.
263
+ client.request('get', self['fields_url']).to_solvebio
264
+ end
265
+
266
+ def query(params={})
267
+ paging = false
268
+ if params.member?(:paging)
269
+ paging = params[:paging]
270
+ params.delete(:paging)
271
+ end
272
+ q = paging ? SolveBio::PagingQuery.new(self['id'], params) :
273
+ SolveBio::Query.new(self['id'], params)
274
+
275
+ if params[:filters]
276
+ return q.filter(params[:filters])
277
+ end
278
+ return q
279
+ end
280
+
281
+ private
282
+ def data_url
283
+ unless self['data_url']
284
+ unless self['id']
285
+ raise Exception,
286
+ 'No Dataset ID was provided. ' +
287
+ 'Please instantiate the Dataset ' +
288
+ 'object with an ID or full_name.'
289
+ end
290
+ # automatically construct the data_url from the ID
291
+ return instance_url() + '/data'
292
+ end
293
+ return self['data_url']
294
+ end
295
+
296
+ end
297
+
298
+ class SolveBio::DatasetField < SolveBio::APIResource
299
+
300
+ include SolveBio::CreateableAPIResource
301
+ include SolveBio::ListableAPIResource
302
+ include SolveBio::UpdateableAPIResource
303
+
304
+ ALLOW_FULL_NAME_ID = true
305
+ FULL_NAME_REGEX = %r{^([\w\-\.]+/){3}[\w\-\.]+$}
306
+
307
+ # Supports lookup by ID or full name
308
+ def self.retrieve(id, params={})
309
+ if id.kind_of?(String)
310
+ _id = id.strip
311
+ id = nil
312
+ if FULL_NAME_REGEX =~ _id
313
+ params['full_name'] = _id
314
+ else
315
+ raise Exception, 'Unrecognized full name.'
316
+ end
317
+ end
318
+
319
+ return SolveBio::APIResource.
320
+ retrieve(SolveBio::DatasetField, id, params)
321
+ end
322
+
323
+ def facets_url
324
+ return "/v1/dataset_fields/#{self.id}/facets"
325
+ end
326
+
327
+ def facets(params={})
328
+ response = SolveBio::Client.
329
+ client.request('get', facets_url, params)
330
+ return response.to_solvebio
331
+ end
332
+
333
+ def help
334
+ facets
335
+ end
336
+ end
337
+
338
+ SolveBio::SolveObject::CONVERSION = {
339
+ 'Depository' => SolveBio::Depository,
340
+ 'DepositoryVersion' => SolveBio::DepositoryVersion,
341
+ 'Dataset' => SolveBio::Dataset,
342
+ 'DatasetField' => SolveBio::DatasetField,
343
+ 'User' => SolveBio::User,
344
+ 'list' => SolveBio::ListObject
345
+ }
346
+
347
+ class Hash
348
+ def to_solvebio
349
+ resp = self.dup()
350
+ klass_name = resp['class_name']
351
+ if klass_name.kind_of?(String)
352
+ klass = SolveBio::SolveObject::CONVERSION[klass_name] ||
353
+ SolveBio::SolveObject
354
+ else
355
+ klass = SolveBio::SolveObject
356
+ end
357
+ SolveBio::SolveObject::construct_from(klass, resp)
358
+ end
359
+ end
360
+
361
+ class Array
362
+ def to_solvebio
363
+ return self.map{|i| to_solve_object(i)}
364
+ end
365
+ end
366
+
367
+
368
+ def to_solve_object(resp)
369
+ if resp.kind_of?(Array) or
370
+ (not resp.kind_of? SolveBio::SolveObject and resp.kind_of?(Hash))
371
+ resp.to_solvebio
372
+ else
373
+ return resp
374
+ end
375
+ end
376
+
377
+ if __FILE__ == $0
378
+ puts '-' * 50
379
+ resp = {
380
+ 'class_name' => 'Dataset',
381
+ 'data_url' => 'https://api.solvebio.com/v1/datasets/25/data',
382
+ 'depository' => 'ClinVar',
383
+ 'depository_id' => 223,
384
+ 'depository_version' => 'ClinVar/2.0.0-1',
385
+ 'depository_version_id' => 15,
386
+ 'description' => '',
387
+ 'fields_url' => 'https://api.solvebio.com/v1/datasets/25/fields',
388
+ 'full_name' => 'ClinVar/2.0.0-1/Variants',
389
+ 'id' => 25,
390
+ 'name' => 'Variants',
391
+ 'title' => 'Variants',
392
+ 'url' => 'https://api.solvebio.com/v1/datasets/25'
393
+ }
394
+ so = to_solve_object(resp)
395
+ so = resp.to_solvebio
396
+ puts so.inspect
397
+ puts so.to_s
398
+
399
+ if ARGV[0]
400
+ require_relative './cli/auth.rb'
401
+ include SolveBio::Auth
402
+ login
403
+ puts '-' * 30, ' HELP ', '-' * 30
404
+ puts SolveBio::Depository.retrieve('ClinVar').help
405
+ puts '-' * 30, ' Retrieve ClinVar ','-' * 30
406
+ puts SolveBio::Depository.retrieve('ClinVar').to_s
407
+ puts '-' * 30, ' Versions ClinVar ','-' * 30
408
+ puts SolveBio::Depository.retrieve('Clinvar').versions.to_s
409
+ puts '-' * 30, ' Dataset ','-' * 30
410
+ puts SolveBio::Dataset.retrieve('Clinvar/2.0.0-1/Variants').to_s
411
+ puts '-' * 30, ' All Depository ','-' * 30
412
+ puts SolveBio::Depository.all.to_s
413
+ end
414
+ end
data/lib/solvebio.rb ADDED
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Something to pull in the entire SolveBio API.
3
+
4
+ require_relative 'resource'
5
+ require_relative 'query'
6
+
7
+ # cli/auth is a little nicer than credentials
8
+ # FIXME: consider moving cli/auth moving out of cli?
9
+ require_relative 'cli/auth'
10
+
11
+ # Set authentication if possible
12
+ include SolveBio::Credentials
13
+ creds = get_credentials()
14
+ SolveBio.api_key = SolveBio::Client.client.api_key = creds[1] if creds
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'json'
4
+ require 'set'
5
+ require_relative 'client'
6
+
7
+ # Add underscore before internal uppercase letters. Also, lowercase
8
+ # all letters.
9
+ def camelcase_to_underscore(name)
10
+ # Using [[:upper:]] and [[:lower]] should help with Unicode.
11
+ s1 = name.gsub(/(.)([[:upper:]])([[:lower:]]+)/){"#{$1}_#{$2}#{$3}"}
12
+ return (s1.gsub(/([a-z0-9])([[:upper:]])/){"#{$1}_#{$2}"}).downcase
13
+ end
14
+
15
+ # Base class for all SolveBio API resource objects
16
+ class SolveBio::SolveObject < Hash
17
+
18
+ ALLOW_FULL_NAME_ID = false # Treat full_name parameter as an ID?
19
+
20
+ attr_reader :unsaved_values
21
+
22
+ def allow_full_name_id
23
+ self.class.const_get(:ALLOW_FULL_NAME_ID)
24
+ end
25
+
26
+ def initialize(id=nil, params={})
27
+
28
+ super()
29
+ # store manually updated values for partial updates
30
+ @unsaved_values = Set.new
31
+
32
+ if id
33
+ self['id'] = id
34
+ elsif allow_full_name_id and params['full_name']
35
+ self['full_name'] = params['full_name']
36
+ # no ID was provided so temporarily set the id as full_name
37
+ # this will get updated when the resource is refreshed
38
+ self['id'] = params['full_name']
39
+ end
40
+ end
41
+
42
+ # Element Reference — Retrieves the value object corresponding to the key object.
43
+ # Note: *key* is turned into a string before access, because the underlying key type
44
+ # is a string.
45
+ def [](key)
46
+ return super(key.to_s)
47
+ end
48
+
49
+ def self.construct_from(cls, values)
50
+ instance = cls.new(values['id'])
51
+ instance.refresh_from(values)
52
+ return instance
53
+ end
54
+
55
+ def refresh_from(values)
56
+ self.clear()
57
+ @unsaved_values = Set.new
58
+ values.each { |k, v| self[k] = to_solve_object(v) }
59
+ end
60
+
61
+ def request(method, url, params=nil)
62
+ response = SolveBio::Client.client.request(method, url, params)
63
+ return to_solve_object(response)
64
+ end
65
+
66
+ def inspect
67
+ ident_parts = [self.class]
68
+
69
+ if self['id'].kind_of?(Integer)
70
+ ident_parts << "id=#{self['id']}"
71
+ end
72
+
73
+ if allow_full_name_id and self['full_name']
74
+ ident_parts << "full_name=#{self['full_name']}"
75
+ end
76
+
77
+ return '<%s:%x> JSON: %s' % [ident_parts.join(' '),
78
+ self.object_id, self.to_json]
79
+
80
+ end
81
+
82
+ def to_s
83
+ # No equivalent of Python's json sort_keys?
84
+ return JSON.pretty_generate(self, :indent => ' ')
85
+ # return self.to_json json.dumps(self, sort_keys=true, indent=2)
86
+ end
87
+
88
+ # @property
89
+ def id
90
+ return self['id']
91
+ end
92
+ end
93
+
94
+ if __FILE__ == $0
95
+ %w(abc abcDef abc01Def aBcDef a1B2C3 ?Foo Dataset).each do |word|
96
+ puts word + " -> " + camelcase_to_underscore(word)
97
+ end
98
+ puts SolveBio::SolveObject.new.inspect
99
+ puts SolveBio::SolveObject.new(64).inspect
100
+
101
+ end