solvebio 1.5.2 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|