quorum 0.5.2 → 0.6.0

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 (58) hide show
  1. data/.gitignore +3 -5
  2. data/.travis.yml +0 -2
  3. data/Gemfile.lock +2 -2
  4. data/HISTORY.md +12 -0
  5. data/Rakefile +13 -48
  6. data/app/models/quorum/blastn_job.rb +15 -22
  7. data/app/models/quorum/blastp_job.rb +15 -22
  8. data/app/models/quorum/blastx_job.rb +15 -22
  9. data/app/models/quorum/tblastn_job.rb +15 -22
  10. data/app/views/quorum/jobs/form/_blastn_form.html.erb +4 -4
  11. data/app/views/quorum/jobs/form/_blastp_form.html.erb +4 -4
  12. data/app/views/quorum/jobs/form/_blastx_form.html.erb +4 -4
  13. data/app/views/quorum/jobs/form/_tblastn_form.html.erb +4 -4
  14. data/app/views/quorum/jobs/templates/_blast_detailed_report_template.html.erb +6 -17
  15. data/db/migrate/20120918205556_rename_max_score_column.rb +15 -0
  16. data/lib/generators/templates/blast.rb +37 -31
  17. data/lib/quorum/version.rb +1 -1
  18. data/spec/config/quorum_settings.yml +55 -0
  19. data/spec/javascripts/fixtures/quorum_search_form.html +8 -8
  20. data/spec/javascripts/jobs_spec.js +8 -8
  21. data/spec/lib/tasks/travis.rake +120 -0
  22. data/spec/models/blastn_job_spec.rb +41 -22
  23. data/spec/models/blastp_job_spec.rb +41 -22
  24. data/spec/models/blastx_job_spec.rb +41 -22
  25. data/spec/models/job_spec.rb +1 -1
  26. data/spec/models/tblastn_job_spec.rb +41 -22
  27. data/spec/requests/jobs_spec.rb +8 -8
  28. data/spec/templates/blast_db_spec.rb +1 -1
  29. data/spec/templates/blast_spec.rb +1 -1
  30. metadata +7 -32
  31. data/spec/dummy/config/initializers/quorum_initializer.rb +0 -36
  32. data/spec/dummy/config/locales/quorum.en.yml +0 -8
  33. data/spec/dummy/config/quorum_settings.yml +0 -141
  34. data/spec/dummy/db/schema.rb +0 -213
  35. data/spec/dummy/quorum/bin/fetch +0 -115
  36. data/spec/dummy/quorum/bin/search +0 -141
  37. data/spec/dummy/quorum/blastdb/test/contigs.fa +0 -15
  38. data/spec/dummy/quorum/blastdb/test/peptides.fa +0 -4
  39. data/spec/dummy/quorum/blastdb/test.nhd +0 -2
  40. data/spec/dummy/quorum/blastdb/test.nhi +0 -0
  41. data/spec/dummy/quorum/blastdb/test.nhr +0 -0
  42. data/spec/dummy/quorum/blastdb/test.nin +0 -0
  43. data/spec/dummy/quorum/blastdb/test.nog +0 -0
  44. data/spec/dummy/quorum/blastdb/test.nsd +0 -2
  45. data/spec/dummy/quorum/blastdb/test.nsi +0 -0
  46. data/spec/dummy/quorum/blastdb/test.nsq +0 -0
  47. data/spec/dummy/quorum/blastdb/test.phd +0 -2
  48. data/spec/dummy/quorum/blastdb/test.phi +0 -0
  49. data/spec/dummy/quorum/blastdb/test.phr +0 -0
  50. data/spec/dummy/quorum/blastdb/test.pin +0 -0
  51. data/spec/dummy/quorum/blastdb/test.pog +0 -0
  52. data/spec/dummy/quorum/blastdb/test.psd +0 -2
  53. data/spec/dummy/quorum/blastdb/test.psi +0 -0
  54. data/spec/dummy/quorum/blastdb/test.psq +0 -0
  55. data/spec/dummy/quorum/lib/fetch_tools/blast_db.rb +0 -106
  56. data/spec/dummy/quorum/lib/logger.rb +0 -43
  57. data/spec/dummy/quorum/lib/search_tools/blast.rb +0 -452
  58. data/spec/dummy/quorum/lib/trollop.rb +0 -781
@@ -1,452 +0,0 @@
1
- $LOAD_PATH.unshift(File.expand_path("../../", __FILE__))
2
-
3
- require 'bio-blastxmlparser'
4
- require 'logger'
5
-
6
- module Quorum
7
- module SearchTools
8
- #
9
- # Blast Search Tool
10
- #
11
- class Blast
12
-
13
- class QuorumJob < ActiveRecord::Base
14
- self.table_name = "quorum_jobs"
15
-
16
- has_one :quorum_blastn_job,
17
- :foreign_key => "job_id"
18
- has_many :quorum_blastn_job_reports,
19
- :foreign_key => "blastn_job_id"
20
-
21
- has_one :quorum_blastx_job,
22
- :foreign_key => "job_id"
23
- has_many :quorum_blastx_job_reports,
24
- :foreign_key => "blastx_job_id"
25
-
26
- has_one :quorum_tblastn_job,
27
- :foreign_key => "job_id"
28
- has_many :quorum_tblastn_job_reports,
29
- :foreign_key => "tblastn_job_id"
30
-
31
- has_one :quorum_blastp_job,
32
- :foreign_key => "job_id"
33
- has_many :quorum_blastp_job_reports,
34
- :foreign_key => "blastp_job_id"
35
- end
36
-
37
- class QuorumBlastnJob < ActiveRecord::Base
38
- self.table_name = "quorum_blastn_jobs"
39
- belongs_to :quorum_job
40
- has_many :quorum_blastn_job_reports
41
- end
42
-
43
- class QuorumBlastnJobReport < ActiveRecord::Base
44
- self.table_name = "quorum_blastn_job_reports"
45
- belongs_to :quorum_blastn_job
46
- end
47
-
48
- class QuorumBlastxJob < ActiveRecord::Base
49
- self.table_name = "quorum_blastx_jobs"
50
- belongs_to :quorum_job
51
- has_many :quorum_blastx_job_reports
52
- end
53
-
54
- class QuorumBlastxJobReport < ActiveRecord::Base
55
- self.table_name = "quorum_blastx_job_reports"
56
- belongs_to :quorum_blastx_job
57
- end
58
-
59
- class QuorumTblastnJob < ActiveRecord::Base
60
- self.table_name = "quorum_tblastn_jobs"
61
- belongs_to :quorum_job
62
- has_many :quorum_tblastn_job_reports
63
- end
64
-
65
- class QuorumTblastnJobReport < ActiveRecord::Base
66
- self.table_name = "quorum_tblastn_job_reports"
67
- belongs_to :quorum_tblastn_job
68
- end
69
-
70
- class QuorumBlastpJob < ActiveRecord::Base
71
- self.table_name = "quorum_blastp_jobs"
72
- belongs_to :quorum_job
73
- has_many :quorum_blastp_job_reports
74
- end
75
-
76
- class QuorumBlastpJobReport < ActiveRecord::Base
77
- self.table_name = "quorum_blastp_job_reports"
78
- belongs_to :quorum_blastp_job
79
- end
80
-
81
- private
82
-
83
- def initialize(args)
84
- @algorithm = args[:search_tool]
85
- @id = args[:id]
86
- @log_directory = args[:log_directory]
87
- @tmp = args[:tmp_directory]
88
- @search_database = args[:search_database]
89
- @threads = args[:threads]
90
-
91
- @logger = Quorum::Logger.new(@log_directory)
92
-
93
- begin
94
- @job = QuorumJob.find(@id)
95
- rescue Exception => e
96
- @logger.log("ActiveRecord", e.message, 1)
97
- end
98
-
99
- @na_sequence = @job.na_sequence
100
- @aa_sequence = @job.aa_sequence
101
-
102
- ## Create for method invocation ##
103
- @job_association = "quorum_#{@algorithm}_job".to_sym
104
- @job_report_association = "quorum_#{@algorithm}_job_reports".to_sym
105
-
106
- @filter = @job.method(@job_association).call.filter
107
- @expectation = @job.method(@job_association).call.expectation
108
- @max_score = @job.method(@job_association).call.max_score
109
- @min_score = @job.method(@job_association).call.min_bit_score
110
- @gapped_alignments = @job.method(@job_association).call.gapped_alignments
111
- @gap_opening_penalty = @job.method(@job_association).call.gap_opening_penalty
112
- @gap_extension_penalty = @job.method(@job_association).call.gap_extension_penalty
113
-
114
- @db = @job.method(@job_association).call.blast_dbs.split(';')
115
- @db.map! { |d| File.join(@search_database, d) }
116
- @db = @db.join(' ')
117
-
118
- @hash = create_hash(@job.sequence)
119
- @tmp_files = File.join(@tmp, @hash) << "*"
120
- end
121
-
122
- #
123
- # Create a unique hash plus timestamp.
124
- #
125
- def create_hash(sequence)
126
- Digest::MD5.hexdigest(sequence).to_s + "-" + Time.now.to_f.to_s
127
- end
128
-
129
- #
130
- # Blastn command
131
- #
132
- def generate_blastn_cmd
133
- blastn = "blastn " <<
134
- "-db \"#{@db}\" " <<
135
- "-query #{@na_fasta} " <<
136
- "-outfmt 5 " <<
137
- "-num_threads #{@threads} " <<
138
- "-evalue #{@expectation} " <<
139
- "-max_target_seqs #{@max_score} " <<
140
- "-out #{@out} "
141
- if @gapped_alignments
142
- blastn << "-gapopen #{@gap_opening_penalty} "
143
- blastn << "-gapextend #{@gap_extension_penalty} "
144
- else
145
- blastn << "-ungapped "
146
- end
147
- blastn << "-dust yes " if @filter
148
- blastn
149
- end
150
-
151
- #
152
- # Blastx command
153
- #
154
- def generate_blastx_cmd
155
- blastx = "blastx " <<
156
- "-db \"#{@db}\" " <<
157
- "-query #{@na_fasta} " <<
158
- "-outfmt 5 " <<
159
- "-num_threads #{@threads} " <<
160
- "-evalue #{@expectation} " <<
161
- "-max_target_seqs #{@max_score} " <<
162
- "-out #{@out} "
163
- if @gapped_alignments
164
- blastx << "-gapopen #{@gap_opening_penalty} "
165
- blastx << "-gapextend #{@gap_extension_penalty} "
166
- else
167
- blastx << "-ungapped "
168
- end
169
- blastx << "-seg yes " if @filter
170
- blastx
171
- end
172
-
173
- #
174
- # Tblastn command
175
- #
176
- def generate_tblastn_cmd
177
- tblastn = "tblastn " <<
178
- "-db \"#{@db}\" " <<
179
- "-query #{@aa_fasta} " <<
180
- "-outfmt 5 " <<
181
- "-num_threads #{@threads} " <<
182
- "-evalue #{@expectation} " <<
183
- "-max_target_seqs #{@max_score} " <<
184
- "-out #{@out} "
185
- if @gapped_alignments
186
- tblastn << "-gapopen #{@gap_opening_penalty} "
187
- tblastn << "-gapextend #{@gap_extension_penalty} "
188
- tblastn << "-comp_based_stats D "
189
- else
190
- tblastn << "-ungapped "
191
- tblastn << "-comp_based_stats F "
192
- end
193
- tblastn << "-seg yes " if @filter
194
- tblastn
195
- end
196
-
197
- #
198
- # Blastp command
199
- #
200
- def generate_blastp_cmd
201
- blastp = "blastp " <<
202
- "-db \"#{@db}\" " <<
203
- "-query #{@aa_fasta} " <<
204
- "-outfmt 5 " <<
205
- "-num_threads #{@threads} " <<
206
- "-evalue #{@expectation} " <<
207
- "-max_target_seqs #{@max_score} " <<
208
- "-out #{@out} "
209
- if @gapped_alignments
210
- blastp << "-gapopen #{@gap_opening_penalty} "
211
- blastp << "-gapextend #{@gap_extension_penalty} "
212
- blastp << "-comp_based_stats D "
213
- else
214
- blastp << "-ungapped "
215
- blastp << "-comp_based_stats F "
216
- end
217
- blastp << "-seg yes " if @filter
218
- blastp
219
- end
220
-
221
- #
222
- # Generate Blast Command
223
- #
224
- def generate_blast_cmd
225
- @cmd = ""
226
-
227
- @na_fasta = File.join(@tmp, @hash + ".na.fa")
228
- @aa_fasta = File.join(@tmp, @hash + ".aa.fa")
229
- File.open(@na_fasta, "w") { |f| f << @na_sequence }
230
- File.open(@aa_fasta, "w") { |f| f << @aa_sequence }
231
-
232
- @out = File.join(@tmp, @hash + ".out.xml")
233
- File.new(@out, "w")
234
-
235
- case @algorithm
236
- when "blastn"
237
- @cmd << generate_blastn_cmd
238
- when "blastx"
239
- @cmd << generate_blastx_cmd
240
- when "tblastn"
241
- @cmd << generate_tblastn_cmd
242
- when "blastp"
243
- @cmd << generate_blastp_cmd
244
- end
245
- end
246
-
247
- #
248
- # Format Blast report hit_display_id and hit_def.
249
- #
250
- # For added flexibility, Quorum doesn't parse seqids or deflines.
251
- # Instead, format_hit splits the hit_def on whitespace and
252
- # reports hit_display_id as the first element and hit_def as the second.
253
- #
254
- def format_hit(str)
255
- hit_display_id = ""
256
- hit_def = ""
257
-
258
- hit = str.split(" ", 2)
259
- if hit.length < 2
260
- hit_display_id = hit.first
261
- hit_def = "None"
262
- else
263
- hit_display_id = hit.first
264
- hit_def = hit.last
265
- end
266
-
267
- return hit_display_id, hit_def
268
- end
269
-
270
- #
271
- # Save Blast Job Report
272
- #
273
- # Hsps are only reported if a query hit against the Blast db.
274
- # Only save the @data if bit_score exists and it's > the user
275
- # defined minimum score.
276
- #
277
- # Set the attribute results to true for downstream processes.
278
- #
279
- def save_hsp_results
280
- if @data[:bit_score] && (@data[:bit_score].to_i > @min_score.to_i)
281
- @data[:results] = true
282
- @data["#{@algorithm}_job_id".to_sym] = @job.method(@job_association).call.job_id
283
-
284
- job_report = @job.method(@job_report_association).call.build(@data)
285
-
286
- unless job_report.save!
287
- @logger.log(
288
- "ActiveRecord",
289
- "Unable to save #{@algorithm} results to database.",
290
- 1,
291
- @tmp_files
292
- )
293
- end
294
- end
295
- end
296
-
297
- #
298
- # Save empty Blast Job Report
299
- #
300
- # Set the attribute results to false for downstream processes.
301
- #
302
- def save_empty_results
303
- job_report = @job.method(@job_report_association).call.build(
304
- "#{@algorithm}_job_id" => @job.method(@job_association).call.job_id,
305
- "results" => false
306
- )
307
- unless job_report.save!
308
- @logger.log(
309
- "ActiveRecord",
310
- "Unable to save #{@algorithm} results to database.",
311
- 1,
312
- @tmp_files
313
- )
314
- end
315
- @logger.log(
316
- "NCBI Blast",
317
- "#{@algorithm} report empty.",
318
- 0,
319
- @tmp_files
320
- )
321
- end
322
-
323
- #
324
- # Parse and save Blast results.
325
- #
326
- # Parse the Blast XML output and save results.
327
- #
328
- def parse_and_save_results
329
- @results = false # Did the xml contain results?
330
-
331
- if File.size(@out) > 0
332
- report = Bio::Blast::XmlIterator.new(@out)
333
- report.to_enum.each do |iteration|
334
-
335
- @data = {}
336
-
337
- @data[:query] = iteration.query_def.split(" ").first
338
- @data[:query_len] = iteration.query_len
339
-
340
- iteration.each do |hit|
341
- @data[:hit_display_id], @data[:hit_def] = format_hit(hit.hit_def)
342
-
343
- @data[:hit_id] = hit.hit_id
344
- @data[:hit_accession] = hit.accession
345
- @data[:hit_len] = hit.len
346
-
347
- hit.each do |hsp|
348
- @data[:hsp_num] = hsp.hsp_num
349
- @data[:bit_score] = hsp.bit_score
350
- @data[:score] = hsp.score
351
- @data[:evalue] = hsp.evalue
352
- @data[:query_from] = hsp.query_from
353
- @data[:query_to] = hsp.query_to
354
- @data[:hit_from] = hsp.hit_from
355
- @data[:hit_to] = hsp.hit_to
356
- @data[:query_frame] = hsp.query_frame
357
- @data[:hit_frame] = hsp.hit_frame
358
- @data[:identity] = hsp.identity
359
- @data[:positive] = hsp.positive
360
- @data[:gaps] = hsp.gaps
361
- @data[:align_len] = hsp.align_len
362
- @data[:qseq] = hsp.qseq
363
- @data[:hseq] = hsp.hseq
364
- @data[:midline] = hsp.midline
365
-
366
- if @data[:bit_score] &&
367
- (@data[:bit_score].to_i > @min_score.to_i)
368
- @results = true
369
- save_hsp_results
370
- end
371
-
372
- end
373
- end
374
- end
375
- end
376
-
377
- save_empty_results unless @results
378
- end
379
-
380
- #
381
- # Group record ids of hsps belonging to the same query and hit_accession.
382
- #
383
- def add_hps_groups_to_reports
384
- groups = {}
385
-
386
- query_hit_acc = @job.method(
387
- @job_report_association
388
- ).call.select("DISTINCT query, hit_accession")
389
-
390
- # Populate the hash of hashes with correct query and hit_accession.
391
- query_hit_acc.each do |q|
392
- groups[q.query] = {}
393
- end
394
- query_hit_acc.each do |q|
395
- groups[q.query][q.hit_accession] = []
396
- end
397
-
398
- # Group record ids.
399
- reports = @job.method(@job_report_association).call.order("id ASC")
400
- reports.each do |r|
401
- if groups[r.query][r.hit_accession]
402
- groups[r.query][r.hit_accession] << r.id
403
- end
404
- end
405
-
406
- # Save hsp_group as a comma delimited string.
407
- reports.each do |r|
408
- if groups[r.query][r.hit_accession]
409
- if groups[r.query][r.hit_accession].length > 1
410
- r.hsp_group = groups[r.query][r.hit_accession].join(",")
411
- unless r.save!
412
- @logger.log(
413
- "ActiveRecord",
414
- "Unable to save #{@algorithm} results to database.",
415
- 1,
416
- @tmp_files
417
- )
418
- end
419
- end
420
- end
421
- end
422
- end
423
-
424
- #
425
- # Remove tmp files.
426
- #
427
- def remove_tmp_files
428
- `rm #{@tmp_files}` if @tmp_files
429
- end
430
-
431
- public
432
-
433
- #
434
- # Execute Blast on a given dataset.
435
- #
436
- def execute_blast
437
- generate_blast_cmd
438
- @logger.log("NCBI Blast", @cmd)
439
- system(@cmd)
440
-
441
- # Wrap these methods in a transaction to prevent premature return.
442
- @job.method(@job_report_association).call.transaction do
443
- parse_and_save_results
444
- add_hps_groups_to_reports
445
- end
446
-
447
- remove_tmp_files
448
- end
449
-
450
- end
451
- end
452
- end