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,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
|
-
|
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
|
-
|
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=
|
62
|
-
response = SolveBio::Client.client
|
63
|
-
|
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
|
-
|
78
|
-
|
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
|
-
|
85
|
-
#
|
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
|
-
|
83
|
+
self['id']
|
91
84
|
end
|
92
85
|
end
|
93
86
|
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
|
data/lib/solvebio.rb
CHANGED
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.
|
18
|
-
s.date = '2014-
|
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
Binary file
|
data/test/helper.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
3
|
-
|
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
|