quorum 0.1.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 (165) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +15 -0
  4. data/Gemfile.lock +170 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +318 -0
  7. data/Rakefile +31 -0
  8. data/app/assets/images/quorum/.gitkeep +0 -0
  9. data/app/assets/images/quorum/bg.png +0 -0
  10. data/app/assets/images/quorum/knight_rider.gif +0 -0
  11. data/app/assets/images/quorum/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  12. data/app/assets/images/quorum/ui-bg_flat_75_ffffff_40x100.png +0 -0
  13. data/app/assets/images/quorum/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  14. data/app/assets/images/quorum/ui-bg_glass_65_ffffff_1x400.png +0 -0
  15. data/app/assets/images/quorum/ui-bg_glass_75_dadada_1x400.png +0 -0
  16. data/app/assets/images/quorum/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  17. data/app/assets/images/quorum/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  18. data/app/assets/images/quorum/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  19. data/app/assets/images/quorum/ui-icons_222222_256x240.png +0 -0
  20. data/app/assets/images/quorum/ui-icons_2e83ff_256x240.png +0 -0
  21. data/app/assets/images/quorum/ui-icons_454545_256x240.png +0 -0
  22. data/app/assets/images/quorum/ui-icons_888888_256x240.png +0 -0
  23. data/app/assets/images/quorum/ui-icons_cd0a0a_256x240.png +0 -0
  24. data/app/assets/javascripts/quorum/application.js +13 -0
  25. data/app/assets/javascripts/quorum/jobs.js +386 -0
  26. data/app/assets/stylesheets/quorum/application.css +224 -0
  27. data/app/assets/stylesheets/quorum/jobs.css +72 -0
  28. data/app/assets/stylesheets/quorum/jquery-ui-1.8.16.custom.css +568 -0
  29. data/app/assets/stylesheets/quorum/tipsy.css +25 -0
  30. data/app/controllers/quorum/application_controller.rb +5 -0
  31. data/app/controllers/quorum/jobs_controller.rb +102 -0
  32. data/app/helpers/quorum/application_helper.rb +4 -0
  33. data/app/models/quorum/blastn_job.rb +111 -0
  34. data/app/models/quorum/blastn_job_report.rb +7 -0
  35. data/app/models/quorum/blastp_job.rb +111 -0
  36. data/app/models/quorum/blastp_job_report.rb +7 -0
  37. data/app/models/quorum/blastx_job.rb +111 -0
  38. data/app/models/quorum/blastx_job_report.rb +7 -0
  39. data/app/models/quorum/job.rb +164 -0
  40. data/app/models/quorum/tblastn_job.rb +111 -0
  41. data/app/models/quorum/tblastn_job_report.rb +7 -0
  42. data/app/views/layouts/quorum/application.html.erb +15 -0
  43. data/app/views/quorum/jobs/_blastn_form.html.erb +71 -0
  44. data/app/views/quorum/jobs/_blastp_form.html.erb +71 -0
  45. data/app/views/quorum/jobs/_blastx_form.html.erb +71 -0
  46. data/app/views/quorum/jobs/_tblastn_form.html.erb +71 -0
  47. data/app/views/quorum/jobs/new.html.erb +45 -0
  48. data/app/views/quorum/jobs/show.html.erb +183 -0
  49. data/app/views/shared/_error_messages.html.erb +10 -0
  50. data/config/locales/en.yml +8 -0
  51. data/config/routes.rb +9 -0
  52. data/db/migrate/20111031204518_create_jobs.rb +12 -0
  53. data/db/migrate/20111031204701_create_blastn_jobs.rb +19 -0
  54. data/db/migrate/20111031204719_create_blastx_jobs.rb +19 -0
  55. data/db/migrate/20111031204733_create_blastp_jobs.rb +19 -0
  56. data/db/migrate/20111031204754_create_tblastn_jobs.rb +19 -0
  57. data/db/migrate/20111031204846_create_blastn_job_reports.rb +34 -0
  58. data/db/migrate/20111031204903_create_blastx_job_reports.rb +34 -0
  59. data/db/migrate/20111031204922_create_blastp_job_reports.rb +34 -0
  60. data/db/migrate/20111031204941_create_tblastn_job_reports.rb +34 -0
  61. data/lib/generators/quorum/install_generator.rb +68 -0
  62. data/lib/generators/quorum/styles_generator.rb +18 -0
  63. data/lib/generators/quorum/views_generator.rb +18 -0
  64. data/lib/generators/templates/README +25 -0
  65. data/lib/generators/templates/blast.rb +412 -0
  66. data/lib/generators/templates/logger.rb +43 -0
  67. data/lib/generators/templates/quorum_initializer.rb +36 -0
  68. data/lib/generators/templates/quorum_settings.yml +157 -0
  69. data/lib/generators/templates/search +141 -0
  70. data/lib/generators/templates/trollop.rb +781 -0
  71. data/lib/quorum/engine.rb +5 -0
  72. data/lib/quorum/helpers.rb +17 -0
  73. data/lib/quorum/sequence.rb +89 -0
  74. data/lib/quorum/version.rb +3 -0
  75. data/lib/quorum.rb +89 -0
  76. data/lib/tasks/blastdb/README +17 -0
  77. data/lib/tasks/blastdb/build_blast_db.rb +222 -0
  78. data/lib/tasks/quorum_resque.rake +3 -0
  79. data/lib/tasks/quorum_tasks.rake +50 -0
  80. data/lib/workers/quorum.rb +45 -0
  81. data/quorum.gemspec +29 -0
  82. data/script/rails +6 -0
  83. data/spec/data/nucl_prot_seqs.txt +36 -0
  84. data/spec/data/nucl_seqs.txt +32 -0
  85. data/spec/data/prot_seqs.txt +4 -0
  86. data/spec/data/seqs.docx +0 -0
  87. data/spec/data/seqs_not_fa.txt +16 -0
  88. data/spec/data/tmp/test.tgz +0 -0
  89. data/spec/dummy/Rakefile +7 -0
  90. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  91. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  92. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  93. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  94. data/spec/dummy/app/mailers/.gitkeep +0 -0
  95. data/spec/dummy/app/models/.gitkeep +0 -0
  96. data/spec/dummy/app/models/blast.rb +2 -0
  97. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  98. data/spec/dummy/config/application.rb +46 -0
  99. data/spec/dummy/config/boot.rb +10 -0
  100. data/spec/dummy/config/environment.rb +20 -0
  101. data/spec/dummy/config/environments/development.rb +30 -0
  102. data/spec/dummy/config/environments/production.rb +60 -0
  103. data/spec/dummy/config/environments/test.rb +42 -0
  104. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  105. data/spec/dummy/config/initializers/inflections.rb +10 -0
  106. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  107. data/spec/dummy/config/initializers/quorum_initializer.rb +36 -0
  108. data/spec/dummy/config/initializers/resque.rb +1 -0
  109. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  110. data/spec/dummy/config/initializers/session_store.rb +8 -0
  111. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  112. data/spec/dummy/config/locales/en.yml +5 -0
  113. data/spec/dummy/config/locales/quorum.en.yml +8 -0
  114. data/spec/dummy/config/quorum_settings.yml +145 -0
  115. data/spec/dummy/config/routes.rb +9 -0
  116. data/spec/dummy/config.ru +4 -0
  117. data/spec/dummy/db/schema.rb +214 -0
  118. data/spec/dummy/lib/assets/.gitkeep +0 -0
  119. data/spec/dummy/log/.gitkeep +0 -0
  120. data/spec/dummy/public/404.html +26 -0
  121. data/spec/dummy/public/422.html +26 -0
  122. data/spec/dummy/public/500.html +26 -0
  123. data/spec/dummy/public/favicon.ico +0 -0
  124. data/spec/dummy/quorum/bin/search +141 -0
  125. data/spec/dummy/quorum/blastdb/test/contigs.fa +2 -0
  126. data/spec/dummy/quorum/blastdb/test/peptides.fa +2 -0
  127. data/spec/dummy/quorum/blastdb/test.nhd +1 -0
  128. data/spec/dummy/quorum/blastdb/test.nhi +0 -0
  129. data/spec/dummy/quorum/blastdb/test.nhr +0 -0
  130. data/spec/dummy/quorum/blastdb/test.nin +0 -0
  131. data/spec/dummy/quorum/blastdb/test.nog +0 -0
  132. data/spec/dummy/quorum/blastdb/test.nsd +1 -0
  133. data/spec/dummy/quorum/blastdb/test.nsi +0 -0
  134. data/spec/dummy/quorum/blastdb/test.nsq +0 -0
  135. data/spec/dummy/quorum/blastdb/test.phd +1 -0
  136. data/spec/dummy/quorum/blastdb/test.phi +0 -0
  137. data/spec/dummy/quorum/blastdb/test.phr +0 -0
  138. data/spec/dummy/quorum/blastdb/test.pin +0 -0
  139. data/spec/dummy/quorum/blastdb/test.pog +0 -0
  140. data/spec/dummy/quorum/blastdb/test.psd +1 -0
  141. data/spec/dummy/quorum/blastdb/test.psi +0 -0
  142. data/spec/dummy/quorum/blastdb/test.psq +0 -0
  143. data/spec/dummy/quorum/lib/logger.rb +43 -0
  144. data/spec/dummy/quorum/lib/search_tools/blast.rb +412 -0
  145. data/spec/dummy/quorum/lib/trollop.rb +781 -0
  146. data/spec/dummy/script/rails +6 -0
  147. data/spec/models/blastn_job_report_spec.rb +13 -0
  148. data/spec/models/blastn_job_spec.rb +103 -0
  149. data/spec/models/blastp_job_report_spec.rb +13 -0
  150. data/spec/models/blastp_job_spec.rb +103 -0
  151. data/spec/models/blastx_job_report_spec.rb +13 -0
  152. data/spec/models/blastx_job_spec.rb +103 -0
  153. data/spec/models/job_spec.rb +40 -0
  154. data/spec/models/tblastn_job_report_spec.rb +13 -0
  155. data/spec/models/tblastn_job_spec.rb +103 -0
  156. data/spec/quorum/quorum_sequence_spec.rb +64 -0
  157. data/spec/quorum_installed_spec.rb +64 -0
  158. data/spec/requests/jobs_spec.rb +138 -0
  159. data/spec/spec_helper.rb +45 -0
  160. data/spec/tasks/blastdb_rake_spec.rb +119 -0
  161. data/spec/templates/blast_spec.rb +8 -0
  162. data/spec/templates/logger_spec.rb +35 -0
  163. data/vendor/assets/javascripts/jquery.tipsy.js +241 -0
  164. data/vendor/assets/javascripts/underscore-min.js +30 -0
  165. metadata +325 -0
@@ -0,0 +1,412 @@
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
+ has_one :quorum_blastn_job,
15
+ :foreign_key => "job_id"
16
+ has_many :quorum_blastn_job_reports,
17
+ :foreign_key => "blastn_job_id"
18
+
19
+ has_one :quorum_blastx_job,
20
+ :foreign_key => "job_id"
21
+ has_many :quorum_blastx_job_reports,
22
+ :foreign_key => "blastx_job_id"
23
+
24
+ has_one :quorum_tblastn_job,
25
+ :foreign_key => "job_id"
26
+ has_many :quorum_tblastn_job_reports,
27
+ :foreign_key => "tblastn_job_id"
28
+
29
+ has_one :quorum_blastp_job,
30
+ :foreign_key => "job_id"
31
+ has_many :quorum_blastp_job_reports,
32
+ :foreign_key => "blastp_job_id"
33
+ end
34
+
35
+ class QuorumBlastnJob < ActiveRecord::Base
36
+ belongs_to :quorum_job
37
+ has_many :quorum_blastn_job_reports
38
+ end
39
+
40
+ class QuorumBlastnJobReport < ActiveRecord::Base
41
+ belongs_to :quorum_blastn_job
42
+ end
43
+
44
+ class QuorumBlastxJob < ActiveRecord::Base
45
+ belongs_to :quorum_job
46
+ has_many :quorum_blastx_job_reports
47
+ end
48
+
49
+ class QuorumBlastxJobReport < ActiveRecord::Base
50
+ belongs_to :quorum_blastx_job
51
+ end
52
+
53
+ class QuorumTblastnJob < ActiveRecord::Base
54
+ belongs_to :quorum_job
55
+ has_many :quorum_tblastn_job_reports
56
+ end
57
+
58
+ class QuorumTblastnJobReport < ActiveRecord::Base
59
+ belongs_to :quorum_tblastn_job
60
+ end
61
+
62
+ class QuorumBlastpJob < ActiveRecord::Base
63
+ belongs_to :quorum_job
64
+ has_many :quorum_blastp_job_reports
65
+ end
66
+
67
+ class QuorumBlastpJobReport < ActiveRecord::Base
68
+ belongs_to :quorum_blastp_job
69
+ end
70
+
71
+ private
72
+
73
+ def initialize(args)
74
+ @algorithm = args[:search_tool]
75
+ @id = args[:id]
76
+ @log_directory = args[:log_directory]
77
+ @tmp = args[:tmp_directory]
78
+ @search_database = args[:search_database]
79
+ @threads = args[:threads]
80
+
81
+ @logger = Quorum::Logger.new(@log_directory)
82
+
83
+ begin
84
+ @job = QuorumJob.find(@id)
85
+ rescue Exception => e
86
+ @logger.log("ActiveRecord", e.message, 1)
87
+ end
88
+
89
+ @na_sequence = @job.na_sequence
90
+ @aa_sequence = @job.aa_sequence
91
+
92
+ ## Create for method invocation ##
93
+ @job_association = "quorum_#{@algorithm}_job".to_sym
94
+ @job_report_association = "quorum_#{@algorithm}_job_reports".to_sym
95
+
96
+ @filter = @job.method(@job_association).call.filter
97
+ @expectation = @job.method(@job_association).call.expectation
98
+ @max_score = @job.method(@job_association).call.max_score
99
+ @min_score = @job.method(@job_association).call.min_bit_score
100
+ @gapped_alignments = @job.method(@job_association).call.gapped_alignments
101
+ @gap_opening_penalty = @job.method(@job_association).call.gap_opening_penalty
102
+ @gap_extension_penalty = @job.method(@job_association).call.gap_extension_penalty
103
+
104
+ @db = @job.method(@job_association).call.blast_dbs.split(';')
105
+ @db.map! { |d| File.join(@search_database, d) }
106
+ @db = @db.join(' ')
107
+
108
+ @hash = create_hash(@job.sequence)
109
+ @tmp_files = File.join(@tmp, @hash) << "*"
110
+ end
111
+
112
+ #
113
+ # Create a unique hash plus timestamp.
114
+ #
115
+ def create_hash(sequence)
116
+ Digest::MD5.hexdigest(sequence).to_s + "-" + Time.now.to_f.to_s
117
+ end
118
+
119
+ def generate_blastn_cmd
120
+ blastn = "blastn " <<
121
+ "-db \"#{@db}\" " <<
122
+ "-query #{@na_fasta} " <<
123
+ "-outfmt 5 " <<
124
+ "-num_threads #{@threads} " <<
125
+ "-evalue #{@expectation} " <<
126
+ "-max_target_seqs #{@max_score} " <<
127
+ "-out #{@out} "
128
+ if @gapped_alignments
129
+ blastn << "-gapopen #{@gap_opening_penalty} "
130
+ blastn << "-gapextend #{@gap_extension_penalty} "
131
+ else
132
+ blastn << "-ungapped "
133
+ end
134
+ blastn << "-dust yes " if @filter
135
+ blastn
136
+ end
137
+
138
+ def generate_blastx_cmd
139
+ blastx = "blastx " <<
140
+ "-db \"#{@db}\" " <<
141
+ "-query #{@na_fasta} " <<
142
+ "-outfmt 5 " <<
143
+ "-num_threads #{@threads} " <<
144
+ "-evalue #{@expectation} " <<
145
+ "-max_target_seqs #{@max_score} " <<
146
+ "-out #{@out} "
147
+ if @gapped_alignments
148
+ blastx << "-gapopen #{@gap_opening_penalty} "
149
+ blastx << "-gapextend #{@gap_extension_penalty} "
150
+ else
151
+ blastx << "-ungapped "
152
+ end
153
+ blastx << "-seg yes " if @filter
154
+ blastx
155
+ end
156
+
157
+ def generate_tblastn_cmd
158
+ tblastn = "tblastn " <<
159
+ "-db \"#{@db}\" " <<
160
+ "-query #{@aa_fasta} " <<
161
+ "-outfmt 5 " <<
162
+ "-num_threads #{@threads} " <<
163
+ "-evalue #{@expectation} " <<
164
+ "-max_target_seqs #{@max_score} " <<
165
+ "-out #{@out} "
166
+ if @gapped_alignments
167
+ tblastn << "-gapopen #{@gap_opening_penalty} "
168
+ tblastn << "-gapextend #{@gap_extension_penalty} "
169
+ tblastn << "-comp_based_stats D "
170
+ else
171
+ tblastn << "-ungapped "
172
+ tblastn << "-comp_based_stats F "
173
+ end
174
+ tblastn << "-seg yes " if @filter
175
+ tblastn
176
+ end
177
+
178
+ def generate_blastp_cmd
179
+ blastp = "blastp " <<
180
+ "-db \"#{@db}\" " <<
181
+ "-query #{@aa_fasta} " <<
182
+ "-outfmt 5 " <<
183
+ "-num_threads #{@threads} " <<
184
+ "-evalue #{@expectation} " <<
185
+ "-max_target_seqs #{@max_score} " <<
186
+ "-out #{@out} "
187
+ if @gapped_alignments
188
+ blastp << "-gapopen #{@gap_opening_penalty} "
189
+ blastp << "-gapextend #{@gap_extension_penalty} "
190
+ blastp << "-comp_based_stats D "
191
+ else
192
+ blastp << "-ungapped "
193
+ blastp << "-comp_based_stats F "
194
+ end
195
+ blastp << "-seg yes " if @filter
196
+ blastp
197
+ end
198
+
199
+ #
200
+ # Generate Blast Command
201
+ #
202
+ def generate_blast_cmd
203
+ @cmd = ""
204
+
205
+ @na_fasta = File.join(@tmp, @hash + ".na.fa")
206
+ @aa_fasta = File.join(@tmp, @hash + ".aa.fa")
207
+ File.open(@na_fasta, "w") { |f| f << @na_sequence }
208
+ File.open(@aa_fasta, "w") { |f| f << @aa_sequence }
209
+
210
+ @out = File.join(@tmp, @hash + ".out.xml")
211
+ File.new(@out, "w")
212
+
213
+ case @algorithm
214
+ when "blastn"
215
+ @cmd << generate_blastn_cmd
216
+ when "blastx"
217
+ @cmd << generate_blastx_cmd
218
+ when "tblastn"
219
+ @cmd << generate_tblastn_cmd
220
+ when "blastp"
221
+ @cmd << generate_blastp_cmd
222
+ end
223
+ end
224
+
225
+ #
226
+ # Make the E-value look pretty.
227
+ #
228
+ def format_evalue(evalue)
229
+ evalue = evalue.to_s
230
+ e = evalue.slice!(/e.*/)
231
+ unless e.nil?
232
+ e = " x 10<sup>" << e.sub(/e/, '') << "</sup>"
233
+ end
234
+ evalue.to_f.round(1).to_s << e.to_s
235
+ end
236
+
237
+ #
238
+ # Format Blast report hit_id and hit_def.
239
+ #
240
+ # For added flexibility, Quorum doesn't parse seqids or deflines.
241
+ # Instead, format_hit splits the hit_def on whitespace and
242
+ # reports hit_id as the first element and hit_def as the second.
243
+ #
244
+ def format_hit(str)
245
+ hit_id = ""
246
+ hit_def = ""
247
+
248
+ hit = str.split(" ", 2)
249
+ if hit.length < 2
250
+ hit_id = hit.first
251
+ hit_def = "None"
252
+ else
253
+ hit_id = hit.first
254
+ hit_def = hit.last
255
+ end
256
+
257
+ return hit_id, hit_def
258
+ end
259
+
260
+ #
261
+ # Parse and save Blast results using bio-blastxmlparser.
262
+ # Only save Blast results if results.bit_score > @min_score.
263
+ #
264
+ def parse_and_save_results
265
+ # Helper to avoid having to perform a query.
266
+ saved = false
267
+
268
+ if File.size(@out) > 0
269
+ report = Bio::Blast::XmlIterator.new(@out)
270
+ report.to_enum.each do |iteration|
271
+
272
+ @data = {}
273
+
274
+ @data[:query] = iteration.query_def.split(" ").first
275
+ @data[:query_len] = iteration.query_len
276
+
277
+ iteration.each do |hit|
278
+ @data[:hit_id], @data[:hit_def] = format_hit(hit.hit_def)
279
+
280
+ @data[:hit_accession] = hit.accession
281
+ @data[:hit_len] = hit.len
282
+
283
+ hit.each do |hsp|
284
+ @data[:hsp_num] = hsp.hsp_num
285
+ @data[:bit_score] = hsp.bit_score
286
+ @data[:score] = hsp.score
287
+ @data[:evalue] = format_evalue(hsp.evalue)
288
+ @data[:query_from] = hsp.query_from
289
+ @data[:query_to] = hsp.query_to
290
+ @data[:hit_from] = hsp.hit_from
291
+ @data[:hit_to] = hsp.hit_to
292
+ @data[:query_frame] = hsp.query_frame
293
+ @data[:hit_frame] = hsp.hit_frame
294
+ @data[:identity] = hsp.identity
295
+ @data[:positive] = hsp.positive
296
+ @data[:align_len] = hsp.align_len
297
+ @data[:qseq] = hsp.qseq
298
+ @data[:hseq] = hsp.hseq
299
+ @data[:midline] = hsp.midline
300
+
301
+ # Hsps are only reported if a query hit against the Blast db.
302
+ # Only save the @data if bit_score exists.
303
+ if @data[:bit_score] &&
304
+ (@data[:bit_score].to_i > @min_score.to_i)
305
+ @data[:results] = true
306
+ @data["#{@algorithm}_job_id".to_sym] = @job.method(@job_association).call.job_id
307
+ saved = true
308
+ # Build a new report for each Hsp.
309
+ job_report = @job.method(@job_report_association).call.build(@data)
310
+ unless job_report.save!
311
+ @logger.log(
312
+ "ActiveRecord",
313
+ "Unable to save #{@algorithm} results to database.",
314
+ 1,
315
+ @tmp_files
316
+ )
317
+ end
318
+ end
319
+
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ # Save the record and set results to false.
326
+ unless saved
327
+ job_report = @job.method(@job_report_association).call.build(
328
+ "#{@algorithm}_job_id" => @job.method(@job_association).call.job_id,
329
+ "results" => false
330
+ )
331
+ unless job_report.save!
332
+ @logger.log(
333
+ "ActiveRecord",
334
+ "Unable to save #{@algorithm} results to database.",
335
+ 1,
336
+ @tmp_files
337
+ )
338
+ end
339
+ @logger.log(
340
+ "NCBI Blast",
341
+ "#{@algorithm} report empty.",
342
+ 0,
343
+ @tmp_files
344
+ )
345
+ end
346
+ end
347
+
348
+ #
349
+ # Group record ids of hsps belonging to the same query and hit_accession.
350
+ #
351
+ def add_hps_groups_to_reports
352
+ groups = {}
353
+
354
+ query_hit_acc = @job.method(
355
+ @job_report_association
356
+ ).call.select("DISTINCT query, hit_accession")
357
+
358
+ # Populate the hash of hashes with correct query and hit_accession.
359
+ query_hit_acc.each do |q|
360
+ groups[q.query] = {}
361
+ end
362
+ query_hit_acc.each do |q|
363
+ groups[q.query][q.hit_accession] = []
364
+ end
365
+
366
+ # Group record ids.
367
+ reports = @job.method(@job_report_association).call.order("id ASC")
368
+ reports.each do |r|
369
+ if groups[r.query][r.hit_accession]
370
+ groups[r.query][r.hit_accession] << r.id
371
+ end
372
+ end
373
+
374
+ # Save hsp_group as a comma delimited string.
375
+ reports.each do |r|
376
+ if groups[r.query][r.hit_accession]
377
+ if groups[r.query][r.hit_accession].length > 1
378
+ r.hsp_group = groups[r.query][r.hit_accession].join(",")
379
+ unless r.save!
380
+ @logger.log(
381
+ "ActiveRecord",
382
+ "Unable to save #{@algorithm} results to database.",
383
+ 1,
384
+ @tmp_files
385
+ )
386
+ end
387
+ end
388
+ end
389
+ end
390
+ end
391
+
392
+ def remove_tmp_files
393
+ `rm #{@tmp_files}` if @tmp_files
394
+ end
395
+
396
+ public
397
+
398
+ #
399
+ # Execute Blast on a given dataset.
400
+ #
401
+ def execute_blast
402
+ generate_blast_cmd
403
+ @logger.log("NCBI Blast", @cmd)
404
+ system(@cmd)
405
+ parse_and_save_results
406
+ add_hps_groups_to_reports
407
+ remove_tmp_files
408
+ end
409
+
410
+ end
411
+ end
412
+ end
@@ -0,0 +1,43 @@
1
+ module Quorum
2
+ class Logger
3
+
4
+ def initialize(dir)
5
+ @log_directory = dir
6
+ @log_file = "quorum.log"
7
+ end
8
+
9
+ #
10
+ # Write to log file and exit if exit_status is present.
11
+ #
12
+ def log(program, message, exit_status = nil, files = nil)
13
+ File.open(File.join(@log_directory, @log_file), "a") do |log|
14
+ log.puts ""
15
+ log.puts Time.now.to_s + " " + program
16
+ log.puts message
17
+ log.puts ""
18
+ end
19
+
20
+ if exit_status
21
+ remove_files(files) unless files.nil?
22
+ exit exit_status.to_i
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ #
29
+ # Removes instance files.
30
+ #
31
+ def remove_files(files)
32
+ unless Dir.glob(files).empty?
33
+ `rm #{files}`
34
+ else
35
+ log(
36
+ "remove_files",
37
+ "Unable to remove #{files}"
38
+ )
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ ## Quorum Settings ##
2
+ #
3
+ # DO NOT edit this file directly. To configure Quorum edit
4
+ # config/quorum_settings.yml.
5
+
6
+ ## Load Quorum Setttings ##
7
+ settings = YAML.load_file(
8
+ "#{::Rails.root.to_s}/config/quorum_settings.yml"
9
+ )[::Rails.env.to_s]
10
+
11
+ ## Sub %{RAILS_ROOT} ##
12
+ settings.keys.each do |k|
13
+ settings[k.to_s].each_value do |v|
14
+ if v.kind_of?(String)
15
+ v.to_s.gsub!('%{RAILS_ROOT}', ::Rails.root.to_s)
16
+ end
17
+ end
18
+ end
19
+
20
+ blast = settings['blast']
21
+
22
+ ## Quorum Blast Settings ##
23
+ Quorum.blast_remote = blast['remote']
24
+ Quorum.blast_ssh_host = blast['ssh_host']
25
+ Quorum.blast_ssh_user = blast['ssh_user']
26
+ Quorum.blast_ssh_options = blast['ssh_options']
27
+ Quorum.blast_script = blast['script']
28
+ Quorum.blast_log_dir = blast['log_dir']
29
+ Quorum.blast_tmp_dir = blast['tmp_dir']
30
+ Quorum.blast_db = blast['blast_db']
31
+ Quorum.tblastn = blast['tblastn']
32
+ Quorum.blastp = blast['blastp']
33
+ Quorum.blastn = blast['blastn']
34
+ Quorum.blastx = blast['blastx']
35
+ Quorum.blast_threads = blast['blast_threads']
36
+
@@ -0,0 +1,157 @@
1
+ # Quorum Settings
2
+ #
3
+ # ==> General
4
+ # Quorum initializer safely substitutes %{RAILS_ROOT} with ::Rails.root.to_s.
5
+ #
6
+ # ==> NCBI BLAST+ Settings
7
+ # blast:
8
+ # remote: true to execute script on remote machine via
9
+ # Net::SSH. false to execute locally via Kernel#system.
10
+ # See Net::SSH for more information.
11
+ # http://net-ssh.github.com/ssh/v2/api/index.html
12
+ #
13
+ # ssh_host: Net::SSH.start() host.
14
+ #
15
+ # ssh_user: Net::SSH.start() user.
16
+ #
17
+ # ssh_options: Net::SSH.start() optional params.
18
+ # See http://net-ssh.github.com/ssh/v2/api/classes/Net/SSH.html#M000002
19
+ # for a complete list of options.
20
+ # Example Usage:
21
+ # ssh_options:
22
+ # password: "secret password"
23
+ # port: 8888
24
+ #
25
+ # script: Absolute path to search script.
26
+ #
27
+ # log_dir: Absolute path to log file directory.
28
+ #
29
+ # tmp_dir: Absolute path to tmp file directory.
30
+ #
31
+ # blast_db: Absolute path to NCBI Blast database directory.
32
+ # Execute the rake task below to build your Blast DB.
33
+ # rake quorum:blastdb:build
34
+ #
35
+ # blastn: Collection of Blast DBs under the parent blast_db
36
+ # to use with blastn.
37
+ # Example Usage:
38
+ # blastn:
39
+ # "Display name": blastn_database_name
40
+ # "A really cool genome": really_cool_genome
41
+ #
42
+ # blastx: Collection of Blast DBs under the parent blast_db
43
+ # to use with blastx.
44
+ # Example Usage:
45
+ # blastx:
46
+ # "Display name": blastx_database_name
47
+ # "A really cool genome": really_cool_genome
48
+ #
49
+ # tblastn: Collection of Blast DBs under the parent blast_db
50
+ # to use with tblastn.
51
+ # Example Usage:
52
+ # tblastn:
53
+ # "Display name": tblastn_database_name
54
+ # "A really cool genome": really_cool_genome
55
+ #
56
+ # blastp: Collection of Blast DBs under the parent blast_db
57
+ # to use with blastp.
58
+ # Example Usage:
59
+ # blastp:
60
+ # "Display name": blastp_database_name
61
+ # "A really cool genome": really_cool_genome
62
+ #
63
+ # blast_threads: Number of Blast threads. >= 1
64
+ # Defaults to 1.
65
+
66
+ development:
67
+ blast:
68
+ remote: false
69
+ ssh_host:
70
+ ssh_user:
71
+ ssh_options:
72
+ # Example:
73
+ # password: "secret password"
74
+ # port: 8888
75
+ script: "%{RAILS_ROOT}/quorum/bin/search"
76
+ log_dir: "%{RAILS_ROOT}/quorum/log"
77
+ tmp_dir: "%{RAILS_ROOT}/quorum/tmp"
78
+ blast_db: "%{RAILS_ROOT}/quorum/blastdb"
79
+ blastn:
80
+ # Example:
81
+ # "Display name": blastn_database_name
82
+ # "A really cool genome": really_cool_genome
83
+ blastx:
84
+ # Example:
85
+ # "Display name": blastx_database_name
86
+ # "A really cool genome": really_cool_genome
87
+ tblastn:
88
+ # Example:
89
+ # "Display name": tblastn_database_name
90
+ # "A really cool genome": really_cool_genome
91
+ blastp:
92
+ # Example:
93
+ # "Display name": blastp_database_name
94
+ # "A really cool genome": really_cool_genome
95
+ blast_threads:
96
+
97
+ test:
98
+ blast:
99
+ remote: false
100
+ ssh_host:
101
+ ssh_user:
102
+ ssh_options:
103
+ # Example:
104
+ # password: "secret password"
105
+ # port: 8888
106
+ script: "%{RAILS_ROOT}/quorum/bin/search"
107
+ log_dir: "%{RAILS_ROOT}/quorum/log"
108
+ tmp_dir: "%{RAILS_ROOT}/quorum/tmp"
109
+ blast_db: "%{RAILS_ROOT}/quorum/blastdb"
110
+ blastn:
111
+ # Example:
112
+ # "Display name": blastn_database_name
113
+ # "A really cool genome": really_cool_genome
114
+ blastx:
115
+ # Example:
116
+ # "Display name": blastx_database_name
117
+ # "A really cool genome": really_cool_genome
118
+ tblastn:
119
+ # Example:
120
+ # "Display name": tblastn_database_name
121
+ # "A really cool genome": really_cool_genome
122
+ blastp:
123
+ # Example:
124
+ # "Display name": blastp_database_name
125
+ # "A really cool genome": really_cool_genome
126
+ blast_threads:
127
+
128
+ production:
129
+ blast:
130
+ remote: true
131
+ ssh_host:
132
+ ssh_user:
133
+ ssh_options:
134
+ # Example:
135
+ # password: "secret password"
136
+ # port: 8888
137
+ script:
138
+ log_dir:
139
+ tmp_dir:
140
+ blast_db:
141
+ blastn:
142
+ # Example:
143
+ # "Display name": blastn_database_name
144
+ # "A really cool genome": really_cool_genome
145
+ blastx:
146
+ # Example:
147
+ # "Display name": blastx_database_name
148
+ # "A really cool genome": really_cool_genome
149
+ tblastn:
150
+ # Example:
151
+ # "Display name": tblastn_database_name
152
+ # "A really cool genome": really_cool_genome
153
+ blastp:
154
+ # Example:
155
+ # "Display name": blastp_database_name
156
+ # "A really cool genome": really_cool_genome
157
+ blast_threads: