bio-basespace-sdk 0.1.2

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.

Potentially problematic release.


This version of bio-basespace-sdk might be problematic. Click here for more details.

Files changed (65) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +16 -0
  5. data/License.txt +275 -0
  6. data/README.md +671 -0
  7. data/Rakefile +54 -0
  8. data/VERSION +1 -0
  9. data/examples/0_app_triggering.rb +135 -0
  10. data/examples/1_authentication.rb +156 -0
  11. data/examples/2_browsing.rb +84 -0
  12. data/examples/3_accessing_files.rb +129 -0
  13. data/examples/4_app_result_upload.rb +102 -0
  14. data/examples/5_purchasing.rb +119 -0
  15. data/lib/basespace.rb +126 -0
  16. data/lib/basespace/api/api_client.rb +313 -0
  17. data/lib/basespace/api/base_api.rb +242 -0
  18. data/lib/basespace/api/basespace_api.rb +789 -0
  19. data/lib/basespace/api/basespace_error.rb +80 -0
  20. data/lib/basespace/api/billing_api.rb +115 -0
  21. data/lib/basespace/model.rb +78 -0
  22. data/lib/basespace/model/app_result.rb +158 -0
  23. data/lib/basespace/model/app_result_response.rb +40 -0
  24. data/lib/basespace/model/app_session.rb +99 -0
  25. data/lib/basespace/model/app_session_compact.rb +43 -0
  26. data/lib/basespace/model/app_session_launch_object.rb +58 -0
  27. data/lib/basespace/model/app_session_response.rb +41 -0
  28. data/lib/basespace/model/application.rb +47 -0
  29. data/lib/basespace/model/application_compact.rb +44 -0
  30. data/lib/basespace/model/basespace_model.rb +60 -0
  31. data/lib/basespace/model/coverage.rb +48 -0
  32. data/lib/basespace/model/coverage_meta_response.rb +40 -0
  33. data/lib/basespace/model/coverage_metadata.rb +43 -0
  34. data/lib/basespace/model/coverage_response.rb +40 -0
  35. data/lib/basespace/model/file.rb +172 -0
  36. data/lib/basespace/model/file_response.rb +40 -0
  37. data/lib/basespace/model/genome_response.rb +40 -0
  38. data/lib/basespace/model/genome_v1.rb +56 -0
  39. data/lib/basespace/model/list_response.rb +53 -0
  40. data/lib/basespace/model/multipart_upload.rb +288 -0
  41. data/lib/basespace/model/product.rb +50 -0
  42. data/lib/basespace/model/project.rb +103 -0
  43. data/lib/basespace/model/project_response.rb +40 -0
  44. data/lib/basespace/model/purchase.rb +89 -0
  45. data/lib/basespace/model/purchase_response.rb +39 -0
  46. data/lib/basespace/model/purchased_product.rb +56 -0
  47. data/lib/basespace/model/query_parameters.rb +86 -0
  48. data/lib/basespace/model/query_parameters_purchased_product.rb +67 -0
  49. data/lib/basespace/model/refund_purchase_response.rb +40 -0
  50. data/lib/basespace/model/resource_list.rb +48 -0
  51. data/lib/basespace/model/response_status.rb +42 -0
  52. data/lib/basespace/model/run_compact.rb +55 -0
  53. data/lib/basespace/model/sample.rb +127 -0
  54. data/lib/basespace/model/sample_response.rb +40 -0
  55. data/lib/basespace/model/user.rb +80 -0
  56. data/lib/basespace/model/user_compact.rb +45 -0
  57. data/lib/basespace/model/user_response.rb +40 -0
  58. data/lib/basespace/model/variant.rb +57 -0
  59. data/lib/basespace/model/variant_header.rb +44 -0
  60. data/lib/basespace/model/variant_info.rb +48 -0
  61. data/lib/basespace/model/variants_header_response.rb +40 -0
  62. data/spec/basespaceapi_spec.rb +26 -0
  63. data/spec/basespaceerror_spec.rb +87 -0
  64. data/spec/basespacemodel_spec.rb +57 -0
  65. metadata +239 -0
@@ -0,0 +1,288 @@
1
+ # Copyright 2013 Toshiaki Katayama, Joachim Baran
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ require 'digest/md5'
15
+ require 'base64'
16
+
17
+ module Bio
18
+ module BaseSpace
19
+
20
+ # Multipart file upload helper class.
21
+ #
22
+ # TODO This file is not yet ported as the multipartFileUpload class is
23
+ # just mentioned in the comment section of the BaseSpaceAPI file.
24
+ class UploadTask
25
+
26
+ # Create a new upload task object.
27
+ #
28
+ # +api+:: BaseSpaceAPI instance.
29
+ # +bs_file_id+:: BaseSpace file ID.
30
+ # +part+:: Part number of the multi-part upload.
31
+ # +total+:: Total number of parts in the multi-part upload.
32
+ # +myfile+:: Local file to be uploaded.
33
+ # +attempt+:: Number of attempts that the file was previously uploaded (upload tries).
34
+ def initialize(self, api, bs_file_id, part, total, myfile, attempt)
35
+ @api = api
36
+ @part = part # part number
37
+ @total = total # out of total part count
38
+ @file = myfile # the local file to be uploaded
39
+ @bs_file_id = bs_file_id # the baseSpace fileId
40
+ @attempt = attempt # the # of attempts we've made to upload this guy
41
+ @state = 0 # 0=pending, 1=ran, 2=error
42
+ end
43
+
44
+ # Returns the filename (without path) if the file to be uploaded.
45
+ def upload_file_name
46
+ return @file.split('/').last + '_' + @part.to_s
47
+ end
48
+
49
+ # Upload a part of the file.
50
+ def call
51
+ # read the byte string in
52
+ @attempt += 1
53
+ trans_file = @file + @part.to_s
54
+ cmd = "split -d -n #{@part}/#{@total} #{@file}"
55
+ out = `#{cmd}`
56
+ File.open(trans_file, "w") do |f|
57
+ f.write(out)
58
+ end
59
+ # [TODO] confirm whether md5(out).digest is equivalent to MD5.digest (or MD5.hexdigest?)
60
+ @md5 = Base64.encode64(Digest::MD5.digest(out))
61
+ res = self.api.upload_multipart_unit(@bs_file_id, @part, @md5, trans_file)
62
+ # puts "my result #{res}"
63
+ File.delete(trans_file)
64
+ if res['Response'].has_key?('ETag')
65
+ @state = 1 # case things went well
66
+ else
67
+ @state = 2
68
+ end
69
+ return self
70
+ end
71
+
72
+ # Returns information about which part of which file is uploaded, including the total number of parts.
73
+ def to_s
74
+ return "#{@part} / #{@total} - #{@file}"
75
+ end
76
+
77
+ end # class UploadTask
78
+
79
+ class Consumer # [TODO] inherit multiprocessing.Process
80
+ def initialize(task_queue, result_queue, pause_event, halt_event)
81
+ # [TODO] http://stackoverflow.com/questions/710785/working-with-multiple-processes-in-ruby
82
+ # http://stackoverflow.com/questions/855805/please-introduce-a-multi-processing-library-in-perl-or-ruby
83
+ # http://docs.python.jp/2.6/library/multiprocessing.html
84
+ #multiprocessing.Process.__init__(self)
85
+ @task_queue = task_queue
86
+ @result_queue = result_queue
87
+ @pause = pauseEvent
88
+ @halt = haltEvent
89
+ end
90
+
91
+ # [TODO]
92
+ def run
93
+ proc_name = self.name
94
+ while True:
95
+ unless self.pause.is_set()
96
+ next_task = self.task_queue.get()
97
+ end
98
+
99
+ if next_task is None or self.halt.is_set(): # check if we are out of jobs or have been halted
100
+ # Poison pill means shutdown
101
+ print '%s: Exiting' % proc_name
102
+ self.task_queue.task_done()
103
+ break
104
+ elsif self.pause.is_set(): # if we have been paused, sleep for a bit then check back
105
+ print '%s: Paused' % proc_name
106
+ time.sleep(3)
107
+ else: # do some work
108
+ print '%s: %s' % (proc_name, next_task)
109
+ answer = next_task()
110
+ self.task_queue.task_done()
111
+ if answer.state == 1: # case everything went well
112
+ self.result_queue.put(answer)
113
+ else: # case something sent wrong
114
+ if next_task.attempt < 3:
115
+ self.task_queue.put(next_task) # queue the guy for a retry
116
+ else: # problems, shutting down this party
117
+ self.halt.set() # halt all other process
118
+ end
119
+ end
120
+ end
121
+ end
122
+ return
123
+ end
124
+ end # class Consumer
125
+
126
+
127
+ class MultipartUpload:
128
+ def initialize(api, a_id, local_file, file_object, cpu_count, part_size, temp_dir, verbose)
129
+ @api = api
130
+ @analysis_id = a_id
131
+ @local_file = local_file
132
+ @remote_file = file_object
133
+ @part_size = part_size
134
+ @cpu_count = cpu_count
135
+ @verbose = verbose
136
+ @temp_dir = temp_dir
137
+ @status = 'Initialized'
138
+ @start_time = -1
139
+ #@repeat_count = 0 # number of chunks we uploaded multiple times
140
+ setup
141
+ end
142
+
143
+ def to_s
144
+ # [TODO] fix this.
145
+ return "MPU - Stat: " + @status + ", LiveThread: " + str(self.getRunningThreadCount()) + \
146
+ ", RunTime: " + str(self.getRunningTime())[:5] + 's' + \
147
+ ", Q-size " + str(self.tasks.qsize()) + \
148
+ ", Completed " + str(self.getProgressRatio()) + \
149
+ ", AVG TransferRate " + self.getTransRate() + \
150
+ ", Data transfered " + str(self.getTotalTransfered())[:5] + 'Gb'
151
+ end
152
+
153
+ def to_str
154
+ return self.inspect
155
+ end
156
+
157
+ def run
158
+ while @status == 'Paused' or __check_queue__
159
+ time.sleep(self.wait)
160
+ end
161
+ end
162
+
163
+ def setup
164
+ # determine the
165
+ totalSize = os.path.getsize(self.localFile)
166
+ fileCount = int(math.ceil(totalSize/(self.partSize*1024.0*1000)))
167
+
168
+ if self.verbose:
169
+ print "TotalSize " + str(totalSize)
170
+ print "Using split size " + str(self.partSize) +"Mb"
171
+ print "Filecount " + str(fileCount)
172
+ print "CPUs " + str(self.cpuCount)
173
+ end
174
+
175
+ # Establish communication queues
176
+ self.tasks = multiprocessing.JoinableQueue()
177
+ self.completedPool = multiprocessing.Queue()
178
+ for i in xrange(1,fileCount+1): # set up the task queue
179
+ t = uploadTask(self.api,self.remoteFile.Id,i, fileCount, self.localFile, 0)
180
+ self.tasks.put(t)
181
+ end
182
+ self.totalTask = self.tasks.qsize()
183
+
184
+ # create consumers
185
+ self.pauseEvent = multiprocessing.Event()
186
+ self.haltEvent = multiprocessing.Event()
187
+ if self.verbose:
188
+ print 'Creating %d consumers' % self.cpuCount
189
+ print "queue size " + str(self.tasks.qsize())
190
+ end
191
+ self.consumers = [ Consumer(self.tasks, self.completedPool,self.pauseEvent,self.haltEvent) for i in xrange(self.cpuCount) ]
192
+ for c in self.consumers: self.tasks.put(None) # add poisson pill
193
+ end
194
+ end
195
+
196
+ def __cleanUp__(self):
197
+ self.stats[0] += 1
198
+ end
199
+
200
+ def startUpload(self,returnOnFinish=0,testInterval=5):
201
+ if self.Status=='Terminated' or self.Status=='Completed':
202
+ raise Exception('Cannot resume a ' + self.Status + ' multi-part upload session.')
203
+
204
+ if self.Status == 'Initialized':
205
+ self.StartTime = time.time()
206
+ for w in self.consumers:
207
+ w.start()
208
+ if self.Status == 'Paused':
209
+ self.pauseEvent.clear()
210
+ self.Status = 'Running'
211
+
212
+ # If returnOnFinish is set
213
+ if returnOnFinish:
214
+ i=0
215
+ while not self.hasFinished():
216
+ if self.verbose and i: print str(i) + ': ' + str(self)
217
+ time.sleep(testInterval)
218
+ i+=1
219
+ self.finalize()
220
+ return 1
221
+ else:
222
+ return 1
223
+ end
224
+ end
225
+
226
+ def finalize(self):
227
+ if self.getRunningThreadCount():
228
+ raise Exception('Cannot finalize a transfer with running threads.')
229
+ if self.Status=='Running':
230
+ # code here for
231
+ self.Status=='Completed'
232
+ else:
233
+ raise Exception('To finalize the status of the transfer must be "Running."')
234
+ end
235
+ end
236
+
237
+ def hasFinished(self):
238
+ if self.Status == 'Initialized': return 0
239
+ return not self.getRunningThreadCount()>0
240
+ end
241
+
242
+ def pauseUpload(self):
243
+ self.pauseEvent.set()
244
+ self.Status = 'Paused'
245
+ end
246
+
247
+ def haltUpload(self):
248
+ for c in self.consumers: c.terminate()
249
+ self.Status = 'Terminated'
250
+ end
251
+
252
+ def getStatus(self):
253
+ return self.Status
254
+ end
255
+
256
+ def getFileResponse(self):
257
+ return self.remoteFile
258
+ end
259
+
260
+ def getRunningThreadCount(self):
261
+ return sum([c.is_alive() for c in self.consumers])
262
+ end
263
+
264
+ def getTransRate(self):
265
+ # tasks completed size of file-parts
266
+ return str((self.totalTask - self.tasks.qsize())*self.partSize/self.getRunningTime())[:6] + ' mb/s'
267
+ end
268
+
269
+ def getRunningTime(self):
270
+ if self.StartTime==-1: return 0
271
+ else: return time.time() - self.StartTime
272
+ end
273
+
274
+ # Returns the total data amoun transfered in Gb
275
+ def getTotalTransfered(self):
276
+ return float((self.totalTask - self.tasks.qsize())*self.partSize)/1000.0
277
+ end
278
+
279
+ def getProgressRatio(self):
280
+ currentQ = float(self.tasks.qsize() - len(self.consumers))
281
+ return str(float(self.totalTask - currentQ)/self.totalTask)[:6]
282
+ end
283
+
284
+ end # class MultipartUpload
285
+
286
+ end # module BaseSpace
287
+ end # module Bio
288
+
@@ -0,0 +1,50 @@
1
+ # Copyright 2013 Toshiaki Katayama, Joachim Baran
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ require 'basespace/model'
15
+
16
+ module Bio
17
+ module BaseSpace
18
+
19
+ # Product information model.
20
+ class Product < Model
21
+
22
+ # Create a new Product instance.
23
+ def initialize
24
+ @swagger_types = {
25
+ 'Id' => 'str',
26
+ 'Name' => 'str',
27
+ 'Price' => 'str',
28
+ 'Quantity' => 'str',
29
+ 'PersistenceStatus' => 'str', # NOPERSISTENCE, ACTIVE, EXPIRED
30
+ 'Tags' => 'list<str>',
31
+ }
32
+ @attributes = {
33
+ 'Id' => nil,
34
+ 'Name' => nil,
35
+ 'Price' => nil,
36
+ 'Quantity' => nil,
37
+ 'PersistenceStatus' => nil,
38
+ 'Tags' => nil,
39
+ }
40
+ end
41
+
42
+ # Return the name of the product.
43
+ def to_s
44
+ return get_attr('Name').to_s
45
+ end
46
+
47
+ end
48
+
49
+ end # module BaseSpace
50
+ end # module Bio
@@ -0,0 +1,103 @@
1
+ # Copyright 2013 Toshiaki Katayama, Joachim Baran
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ require 'basespace/api/basespace_error'
15
+ require 'basespace/model'
16
+ require 'basespace/model/query_parameters'
17
+
18
+ module Bio
19
+ module BaseSpace
20
+
21
+ # Represents a BaseSpace Project object.
22
+ class Project < Model
23
+
24
+ # Create a new Project instance.
25
+ def initialize
26
+ @swagger_types = {
27
+ 'Name' => 'str',
28
+ 'HrefSamples' => 'str',
29
+ 'HrefAppResults' => 'str',
30
+ 'HrefBaseSpaceUI' => 'str',
31
+ 'DateCreated' => 'datetime',
32
+ 'Id' => 'str',
33
+ 'Href' => 'str',
34
+ 'UserOwnedBy' => 'UserCompact',
35
+ }
36
+ @attributes = {
37
+ 'Name' => nil, # str
38
+ 'HrefSamples' => nil, # str
39
+ 'HrefAppResults' => nil, # str
40
+ 'HrefBaseSpaceUI' => nil, # str
41
+ 'DateCreated' => nil, # datetime
42
+ 'Id' => nil, # str
43
+ 'Href' => nil, # str
44
+ 'UserOwnedBy' => nil, # UserCompact
45
+ }
46
+ end
47
+
48
+ # Returns the name and ID of the project.
49
+ def to_s
50
+ return "#{get_attr('Name')} - id=#{get_attr('Id')}"
51
+ end
52
+
53
+ # Test if the Project instance has been initialized.
54
+ #
55
+ # Throws ModelNotInitializedError, if the object has not been populated yet.
56
+ def is_init
57
+ raise ModelNotInitializedError.new('The project model has not been initialized yet') unless get_attr('Id')
58
+ end
59
+
60
+ # Returns the scope-string to used for requesting BaseSpace access to the object.
61
+ #
62
+ # +scope+:: The scope-type that is requested (write|read).
63
+ def get_access_str(scope = 'write')
64
+ is_init
65
+ return scope + ' project ' + get_attr('Id').to_s
66
+ end
67
+
68
+ # Returns a list of AppResult objects.
69
+ #
70
+ # +api+:: BaseSpaceAPI instance.
71
+ # +my_qp+:: Query parameters for filtering the returned list.
72
+ # +statuses+:: An optional list of statuses.
73
+ def get_app_results(api, my_qp = {}, statuses = [])
74
+ is_init
75
+ query_pars = QueryParameters.new(my_qp)
76
+ return api.get_app_results_by_project(get_attr('Id'), query_pars, statuses)
77
+ end
78
+
79
+ # Returns a list of Sample objects.
80
+ #
81
+ # +api+:: BaseSpaceAPI instance.
82
+ def get_samples(api)
83
+ is_init
84
+ return api.get_samples_by_project(get_attr('Id'))
85
+ end
86
+
87
+ # Return a newly created AppResult object.
88
+ #
89
+ # +api+:: BaseSpaceAPI instance.
90
+ # +name+:: The name of the AppResult.
91
+ # +desc+:: A description of the AppResult.
92
+ # +app_session_id+:: An App session ID.
93
+ # +samples+:: A list of samples.
94
+ def create_app_result(api, name, desc, app_session_id = nil, samples = [])
95
+ is_init
96
+ return api.create_app_result(get_attr('Id'), name, desc, samples, app_session_id)
97
+ end
98
+
99
+ end
100
+
101
+ end # module BaseSpace
102
+ end # module Bio
103
+
@@ -0,0 +1,40 @@
1
+ # Copyright 2013 Toshiaki Katayama, Joachim Baran
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ require 'basespace/model'
15
+
16
+ module Bio
17
+ module BaseSpace
18
+
19
+ # Project response.
20
+ class ProjectResponse < Model
21
+
22
+ # Create a new ProjectResponse instance.
23
+ def initialize
24
+ @swagger_types = {
25
+ 'ResponseStatus' => 'ResponseStatus',
26
+ 'Response' => 'Project',
27
+ 'Notifications' => 'list<Str>',
28
+ }
29
+ @attributes = {
30
+ 'ResponseStatus' => nil, # ResponseStatus
31
+ 'Response' => nil, # Project
32
+ 'Notifications' => nil, # list<Str>
33
+ }
34
+ end
35
+
36
+ end
37
+
38
+ end # module BaseSpace
39
+ end # module Bio
40
+