bio-basespace-sdk 0.1.2

Sign up to get free protection for your applications and to get access to all the features.

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
+