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,123 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require_relative 'solveobject'
4
+ require_relative 'annotation'
5
+ require_relative 'apiresource'
6
+ require_relative 'dataset'
7
+ require_relative 'datasetfield'
8
+ require_relative 'depository'
9
+ require_relative 'depositoryversion'
10
+ require_relative 'sample'
11
+ require_relative 'user'
12
+
13
+ class SolveBio::ListObject < SolveBio::SolveObject
14
+
15
+ include Enumerable
16
+
17
+ def all(params={})
18
+ return request('get', self['url'], {:params => params})
19
+ end
20
+
21
+ def create(params={})
22
+ return request('post', self['url'], {:params => params})
23
+ end
24
+
25
+ def next_page(params={})
26
+ if self['links']['next']
27
+ return request('get', self['links']['next'], {:params => params})
28
+ end
29
+ return nil
30
+ end
31
+
32
+ def prev_page(params={})
33
+ if self['links']['prev']
34
+ request('get', self['links']['prev'], {:params => params})
35
+ end
36
+ return nil
37
+ end
38
+
39
+ def at(i)
40
+ self.to_a[i]
41
+ end
42
+
43
+ def to_a
44
+ return to_solve_object(self['data'])
45
+ end
46
+
47
+ def each(*pass)
48
+ return self unless block_given?
49
+ i = 0
50
+ ary = self.dup
51
+ done = false
52
+ until done
53
+ if i >= ary['data'].size
54
+ ary = next_page
55
+ break unless ary
56
+ i = 0
57
+ end
58
+ yield(ary.at(i))
59
+ i += 1
60
+ end
61
+ return self
62
+ end
63
+
64
+ def first
65
+ self['data'][0]
66
+ end
67
+
68
+ # def max
69
+ # self['data'][self['total']]
70
+ # end
71
+
72
+ end
73
+
74
+
75
+ SolveBio::SolveObject::CONVERSION = {
76
+ 'Annotation' => SolveBio::Annotation,
77
+ 'Depository' => SolveBio::Depository,
78
+ 'DepositoryVersion' => SolveBio::DepositoryVersion,
79
+ 'Dataset' => SolveBio::Dataset,
80
+ 'DatasetField' => SolveBio::DatasetField,
81
+ 'Sample' => SolveBio::Sample,
82
+ 'User' => SolveBio::User,
83
+ 'list' => SolveBio::ListObject
84
+ }
85
+
86
+ if __FILE__ == $0
87
+ puts '-' * 50
88
+ resp = {
89
+ 'class_name' => 'Dataset',
90
+ 'data_url' => 'https://api.solvebio.com/v1/datasets/25/data',
91
+ 'depository' => 'ClinVar',
92
+ 'depository_id' => 223,
93
+ 'depository_version' => 'ClinVar/2.0.0-1',
94
+ 'depository_version_id' => 15,
95
+ 'description' => '',
96
+ 'fields_url' => 'https://api.solvebio.com/v1/datasets/25/fields',
97
+ 'full_name' => 'ClinVar/2.0.0-1/Variants',
98
+ 'id' => 25,
99
+ 'name' => 'Variants',
100
+ 'title' => 'Variants',
101
+ 'url' => 'https://api.solvebio.com/v1/datasets/25'
102
+ }
103
+ so = to_solve_object(resp)
104
+ so = resp.to_solvebio
105
+ puts so.inspect
106
+ puts so.to_s
107
+
108
+ if ARGV[0]
109
+ require_relative './cli/auth.rb'
110
+ include SolveBio::Auth
111
+ login
112
+ puts '-' * 30, ' HELP ', '-' * 30
113
+ puts SolveBio::Depository.retrieve('ClinVar').help
114
+ puts '-' * 30, ' Retrieve ClinVar ','-' * 30
115
+ puts SolveBio::Depository.retrieve('ClinVar').to_s
116
+ puts '-' * 30, ' Versions ClinVar ','-' * 30
117
+ puts SolveBio::Depository.retrieve('Clinvar').versions.to_s
118
+ puts '-' * 30, ' Dataset ','-' * 30
119
+ puts SolveBio::Dataset.retrieve('Clinvar/2.0.0-1/Variants').to_s
120
+ puts '-' * 30, ' All Depository ','-' * 30
121
+ puts SolveBio::Depository.all.to_s
122
+ end
123
+ end
@@ -0,0 +1,75 @@
1
+ # Solvebio API Resource for Samples
2
+ require_relative 'apiresource'
3
+ require_relative 'solveobject'
4
+ require_relative '../errors'
5
+
6
+ # Samples are VCF files uploaded to the SolveBio API. We currently
7
+ # support uncompressed, extension `.vcf`, and gzip-compressed, extension
8
+ # `.vcf.gz`, VCF files. Any other extension will be rejected.
9
+ class SolveBio::Sample < SolveBio::APIResource
10
+
11
+ include SolveBio::DeletableAPIResource
12
+ include SolveBio::DownloadableAPIResource
13
+ include SolveBio::ListableAPIResource
14
+ include SolveBio::HelpableAPIResource
15
+
16
+ def annotate
17
+ SolveBio::Annotation.create :sample_id => self.id
18
+ end
19
+
20
+ # FIXME: Rubyize APIResource.retrieve
21
+ def self.retrieve(id, params={})
22
+ SolveBio::APIResource.retrieve(self, id)
23
+ end
24
+
25
+ def self.create(genome_build, params={})
26
+ if params.member?(:vcf_url)
27
+ if params.member?(:vcf_file)
28
+ raise TypeError,
29
+ 'Specified both vcf_url and vcf_file; use only one'
30
+ end
31
+ self.create_from_url(genome_build, params[:vcf_url])
32
+ elsif params.member?(:vcf_file)
33
+ return create_from_file(genome_build, params[:vcf_file])
34
+ else
35
+ raise TypeError,
36
+ 'Must specify exactly one of vcf_url or vcf_file parameter'
37
+ end
38
+ end
39
+
40
+ # Creates from the specified file. The data of the should be in
41
+ # VCF format.
42
+ def self.create_from_file(genome_build, vcf_file)
43
+
44
+ fh = File.open(vcf_file, 'rb')
45
+ params = {:genome_build => genome_build,
46
+ :vcf_file => fh}
47
+ response = SolveBio::Client.client.post(class_url(self), params,
48
+ :no_json => true)
49
+ to_solve_object(response)
50
+ end
51
+
52
+ # Creates from the specified URL. The data of the should be in
53
+ # VCF format.
54
+ def self.create_from_url(genome_build, vcf_url)
55
+
56
+ params = {:genome_build => genome_build,
57
+ :vcf_url => vcf_url}
58
+ begin
59
+ response = SolveBio::Client.client.post class_url(self), params
60
+ rescue SolveBio::Error => response
61
+ end
62
+ to_solve_object(response)
63
+ end
64
+ end
65
+
66
+ if __FILE__ == $0
67
+ unless SolveBio::API_HOST == 'https://api.solvebio.com'
68
+ SolveBio::SolveObject::CONVERSION = {
69
+ 'Sample' => SolveBio::Sample,
70
+ } unless defined? SolveBio::SolveObject::CONVERSION
71
+ url = 'http://downloads.solvebio.com/vcf/small_sample.vcf.gz'
72
+ response = SolveBio::Sample.create_from_url 'hg19', url
73
+ puts response
74
+ end
75
+ end
@@ -2,15 +2,7 @@
2
2
 
3
3
  require 'json'
4
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
5
+ require_relative '../client'
14
6
 
15
7
  # Base class for all SolveBio API resource objects
16
8
  class SolveBio::SolveObject < Hash
@@ -43,13 +35,13 @@ class SolveBio::SolveObject < Hash
43
35
  # Note: *key* is turned into a string before access, because the underlying key type
44
36
  # is a string.
45
37
  def [](key)
46
- return super(key.to_s)
38
+ super(key.to_s)
47
39
  end
48
40
 
49
41
  def self.construct_from(cls, values)
50
42
  instance = cls.new(values['id'])
51
43
  instance.refresh_from(values)
52
- return instance
44
+ instance
53
45
  end
54
46
 
55
47
  def refresh_from(values)
@@ -58,9 +50,10 @@ class SolveBio::SolveObject < Hash
58
50
  values.each { |k, v| self[k] = to_solve_object(v) }
59
51
  end
60
52
 
61
- def request(method, url, params=nil)
62
- response = SolveBio::Client.client.request(method, url, params)
63
- return to_solve_object(response)
53
+ def request(method, url, params={})
54
+ response = SolveBio::Client.client
55
+ .request method, url, {:params => params}
56
+ to_solve_object(response)
64
57
  end
65
58
 
66
59
  def inspect
@@ -74,27 +67,55 @@ class SolveBio::SolveObject < Hash
74
67
  ident_parts << "full_name=#{self['full_name']}"
75
68
  end
76
69
 
77
- return '<%s:%x> JSON: %s' % [ident_parts.join(' '),
78
- self.object_id, self.to_json]
70
+ '<%s:%x> JSON: %s' % [ident_parts.join(' '),
71
+ self.object_id, self.to_json]
79
72
 
80
73
  end
81
74
 
82
75
  def to_s
83
76
  # 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)
77
+ JSON.pretty_generate(self, :indent => ' ')
78
+ # self.to_json json.dumps(self, sort_keys=true, indent=2)
86
79
  end
87
80
 
88
81
  # @property
89
82
  def id
90
- return self['id']
83
+ self['id']
91
84
  end
92
85
  end
93
86
 
94
- if __FILE__ == $0
95
- %w(abc abcDef abc01Def aBcDef a1B2C3 ?Foo Dataset).each do |word|
96
- puts word + " -> " + camelcase_to_underscore(word)
87
+ class Hash
88
+ def to_solvebio(klass=nil)
89
+ resp = self.dup()
90
+ if ! klass
91
+ klass_name ||= resp['class_name']
92
+ if klass_name.kind_of?(String)
93
+ klass = SolveBio::SolveObject::CONVERSION[klass_name] ||
94
+ SolveBio::SolveObject
95
+ else
96
+ klass = SolveBio::SolveObject
97
+ end
98
+ end
99
+ SolveBio::SolveObject::construct_from(klass, resp)
100
+ end
101
+ end
102
+
103
+ class Array
104
+ def to_solvebio
105
+ return self.map{|i| to_solve_object(i)}
97
106
  end
107
+ end
108
+
109
+ def to_solve_object(resp)
110
+ if resp.kind_of?(Array) or
111
+ (not resp.kind_of? SolveBio::SolveObject and resp.kind_of?(Hash))
112
+ resp.to_solvebio
113
+ else
114
+ return resp
115
+ end
116
+ end
117
+
118
+ if __FILE__ == $0
98
119
  puts SolveBio::SolveObject.new.inspect
99
120
  puts SolveBio::SolveObject.new(64).inspect
100
121
 
@@ -0,0 +1,5 @@
1
+ require_relative 'apiresource'
2
+
3
+ class SolveBio::User < SolveBio::APIResource
4
+ include SolveBio::SingletonAPIResource
5
+ end
data/lib/solvebio.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # Something to pull in the entire SolveBio API.
3
3
 
4
- require_relative 'resource'
4
+ require_relative 'resource/main'
5
5
  require_relative 'query'
6
6
 
7
7
  # cli/auth is a little nicer than credentials
data/lib/util.rb ADDED
@@ -0,0 +1,29 @@
1
+ module SolveBio
2
+
3
+ module_function
4
+ def pluralize(name)
5
+ if name.end_with?('y')
6
+ name = name[0..-2] + 'ie'
7
+ end
8
+ return name + "s"
9
+ end
10
+
11
+ # Add underscore before internal uppercase letters. Also, lowercase
12
+ # all letters.
13
+ def camelcase_to_underscore(name)
14
+ # Using [[:upper:]] and [[:lower]] should help with Unicode.
15
+ s1 = name.gsub(/(.)([[:upper:]])([[:lower:]]+)/){"#{$1}_#{$2}#{$3}"}
16
+ return (s1.gsub(/([a-z0-9])([[:upper:]])/){"#{$1}_#{$2}"}).downcase
17
+ end
18
+ end
19
+
20
+ # Demo code
21
+ if __FILE__ == $0
22
+ include SolveBio
23
+ %w(abc abcDef abc01Def aBcDef a1B2C3 ?Foo Dataset).each do |word|
24
+ puts word + " -> " + camelcase_to_underscore(word)
25
+ end
26
+ ['depository', 'dataset'].each do |word|
27
+ puts word + " -> " + pluralize(word)
28
+ end
29
+ end
data/solvebio.gemspec CHANGED
@@ -14,8 +14,8 @@ Gem::Specification.new do |s|
14
14
  ## If your rubyforge_project name is different, then edit it and comment out
15
15
  ## the sub! line in the Rakefile
16
16
  s.name = 'solvebio'
17
- s.version = '1.5.2'
18
- s.date = '2014-10-01'
17
+ s.version = '1.6.1'
18
+ s.date = '2014-11-05'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
21
21
  ## as you like.
@@ -54,9 +54,12 @@ EOD
54
54
 
55
55
  ## List your runtime dependencies here. Runtime dependencies are those
56
56
  ## that are needed for an end user to actually USE your code.
57
- s.add_dependency('netrc', '>=0.7.7')
58
- # s.add_dependency('openssl', '>=1.1.0')
59
57
 
58
+ s.add_dependency('netrc', '>=0.7.7') # handling .netrc
59
+ s.add_dependency('rest_client', '>=1.8.1') # better URI handler
60
+ s.add_dependency('addressable', '>=2.3.6') # better URI parsing
61
+
62
+ # s.add_dependency('openssl', '>=1.1.0')
60
63
 
61
64
  # There is no way to specify optional dependencies.
62
65
  # s.add_optional_dependency 'launchy' # opens URL in web browser for help
data/test/Makefile ADDED
@@ -0,0 +1,9 @@
1
+ # Whatever it is you want to do other run demos, it should be forwarded to the
2
+ # to top-level directory
3
+ .PHONY: all
4
+
5
+ #: the default target - same as running "check"
6
+ all: check
7
+
8
+ %:
9
+ $(MAKE) -C .. $@
Binary file
data/test/helper.rb CHANGED
@@ -1,3 +1,10 @@
1
1
  require 'test/unit'
2
- require_relative '../lib/resource'
3
- SolveBio.api_key = 'ce68f783a65275d3e81463621d825bad20eb20b0'
2
+ ENV['SOLVEBIO_API_HOST'] ||= 'https://api.solvebio.com'
3
+ require_relative '../lib/main'
4
+
5
+ TEST_DATASET_NAME = 'HGNC/1.0.0-1/HGNC'
6
+
7
+
8
+ def local_api?
9
+ ENV['SOLVEBIO_LOCAL_API']
10
+ end
@@ -0,0 +1,46 @@
1
+ require_relative './helper'
2
+
3
+ class TestAnnotation < Test::Unit::TestCase
4
+
5
+ def check_response(response, expect, msg)
6
+ expect.each do |key, val|
7
+ assert_equal(val, response[key], msg)
8
+ end
9
+ end
10
+
11
+ def test_annotation
12
+ if SolveBio::API_HOST == 'https://api.solvebio.com'
13
+ skip "Annotation testing only on local/dev environments"
14
+ end
15
+
16
+ vcf_file = File.join(File.dirname(__FILE__), "data/sample.vcf.gz")
17
+ my_sample = SolveBio::Sample
18
+ .create('GRCh37', :vcf_file => vcf_file)
19
+ assert(my_sample)
20
+
21
+ sample_id = my_sample['id']
22
+ expect = {
23
+ 'class_name' => 'Annotation',
24
+ 'error_message' => '',
25
+ 'sample_id' => sample_id
26
+ }
27
+
28
+ response = SolveBio::Annotation.create(:sample_id => sample_id)
29
+ check_response(response, expect,
30
+ "'Annotation.create(:sample_id=>{#sample_id}")
31
+
32
+ ['status', 'user_id', 'created_at', 'updated_at'].each do |field|
33
+ assert(response.member?(field) ,
34
+ "response has field #{field}")
35
+ end
36
+
37
+ all = SolveBio::Annotation.all()
38
+ assert(all.total > 1,
39
+ "Annotation.all() returns more than one value")
40
+
41
+ response = my_sample.annotate
42
+ # FIXME: test annotate() more.
43
+
44
+ my_sample.delete
45
+ end
46
+ end