quorum 0.1.0 → 0.2.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.
- data/Gemfile.lock +82 -74
- data/HISTORY.md +9 -0
- data/README.rdoc +101 -23
- data/app/assets/javascripts/quorum/jobs.js +45 -3
- data/app/controllers/quorum/jobs_controller.rb +47 -2
- data/app/models/quorum/job.rb +52 -9
- data/app/views/quorum/jobs/new.html.erb +7 -6
- data/app/views/quorum/jobs/show.html.erb +7 -118
- data/app/views/quorum/jobs/templates/_blast_detailed_report_template.html.erb +53 -0
- data/app/views/quorum/jobs/templates/_blast_template.html.erb +68 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20120109232446_add_hit_display_id_to_blast_reports.rb +8 -0
- data/lib/generators/quorum/images_generator.rb +18 -0
- data/lib/generators/quorum/install_generator.rb +41 -13
- data/lib/generators/templates/README +1 -1
- data/lib/generators/templates/blast.rb +18 -7
- data/lib/generators/templates/blast_db.rb +106 -0
- data/lib/generators/templates/fetch +115 -0
- data/lib/generators/templates/quorum_initializer.rb +1 -1
- data/lib/generators/templates/quorum_settings.yml +4 -4
- data/lib/quorum/helpers.rb +71 -1
- data/lib/quorum/version.rb +1 -1
- data/lib/quorum.rb +15 -5
- data/lib/workers/quorum.rb +12 -2
- data/quorum.gemspec +4 -2
- data/spec/data/seqs_not_fa.txt +16 -16
- data/spec/dummy/config/initializers/quorum_initializer.rb +1 -1
- data/spec/dummy/config/initializers/resque.rb +4 -1
- data/spec/dummy/config/quorum_settings.yml +4 -4
- data/spec/dummy/db/schema.rb +8 -1
- data/spec/dummy/quorum/bin/fetch +115 -0
- data/spec/dummy/quorum/blastdb/test.nin +0 -0
- data/spec/dummy/quorum/blastdb/test.pin +0 -0
- data/spec/dummy/quorum/lib/fetch_tools/blast_db.rb +106 -0
- data/spec/dummy/quorum/lib/search_tools/blast.rb +18 -7
- data/spec/models/job_spec.rb +3 -4
- data/spec/quorum_installed_spec.rb +5 -0
- data/spec/requests/jobs_spec.rb +14 -1
- data/spec/spec_helper.rb +32 -1
- data/spec/support/streams.rb +16 -0
- data/spec/templates/blast_db_spec.rb +63 -0
- data/spec/templates/blast_spec.rb +108 -2
- metadata +64 -37
- /data/app/assets/images/quorum/{knight_rider.gif → loading.gif} +0 -0
- /data/app/views/quorum/jobs/{_blastn_form.html.erb → form/_blastn_form.html.erb} +0 -0
- /data/app/views/quorum/jobs/{_blastp_form.html.erb → form/_blastp_form.html.erb} +0 -0
- /data/app/views/quorum/jobs/{_blastx_form.html.erb → form/_blastx_form.html.erb} +0 -0
- /data/app/views/quorum/jobs/{_tblastn_form.html.erb → form/_tblastn_form.html.erb} +0 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
|
5
|
+
require 'trollop'
|
6
|
+
require 'fetch_tools/blast_db'
|
7
|
+
|
8
|
+
FETCH_TOOLS = {
|
9
|
+
"blastdbcmd" => ["blastdbcmd"]
|
10
|
+
}
|
11
|
+
|
12
|
+
module Quorum
|
13
|
+
#
|
14
|
+
# Parse Options for Quorum Fetch
|
15
|
+
#
|
16
|
+
class Fetch
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
opts = Trollop::options do
|
20
|
+
text <<-HEAD
|
21
|
+
|
22
|
+
Quorum
|
23
|
+
|
24
|
+
A flexible bioinformatics search tool.
|
25
|
+
|
26
|
+
Options:
|
27
|
+
|
28
|
+
HEAD
|
29
|
+
|
30
|
+
# Fetch tool
|
31
|
+
opt :fetch_tool, "Fetch tool to execute. Available tools: " <<
|
32
|
+
"#{FETCH_TOOLS.keys.join(', ')}", :type => :string,
|
33
|
+
:required => true, :short => "-f"
|
34
|
+
|
35
|
+
# General settings
|
36
|
+
opt :log_directory, "Path to log directory", :type => :string,
|
37
|
+
:required => true, :short => "-l"
|
38
|
+
opt :tmp_directory, "Path to tmp directory", :type => :string,
|
39
|
+
:required => true, :short => "-m"
|
40
|
+
|
41
|
+
# Global settings
|
42
|
+
opt :fetch_database, "Path to fetch tool database", :type => :string,
|
43
|
+
:short => "-d"
|
44
|
+
opt :fetch_database_names, "Semicolon delimited database names",
|
45
|
+
:type => :string, :short => "-n"
|
46
|
+
|
47
|
+
# Algorithm specific opts
|
48
|
+
opt :blast_hit_id, "Blast hit id", :type => :string, :short => "-b"
|
49
|
+
opt :blast_hit_display_id, "Blast hit display id", :type => :string,
|
50
|
+
:short => "-s"
|
51
|
+
opt :blast_algo, "Blast algorithm", :type => :string, :short => "-a"
|
52
|
+
end
|
53
|
+
|
54
|
+
@args = {} # Contains valid opts.
|
55
|
+
|
56
|
+
## Additional opt validation. ##
|
57
|
+
|
58
|
+
unless FETCH_TOOLS.keys.include?(opts[:fetch_tool].downcase)
|
59
|
+
Trollop::die :fetch_tool, "fetch_tool not found\n" <<
|
60
|
+
"Available tools: " + FETCH_TOOLS.keys.join(", ")
|
61
|
+
end
|
62
|
+
|
63
|
+
@args[:fetch_tool] = opts[:fetch_tool].downcase
|
64
|
+
|
65
|
+
unless opts[:fetch_database] && Dir.exists?(opts[:fetch_database])
|
66
|
+
Trollop::die :fetch_database, "fetch tool database not found\n" <<
|
67
|
+
"Supplied directory: " + opts[:fetch_database]
|
68
|
+
end
|
69
|
+
|
70
|
+
@args[:fetch_database] = opts[:fetch_database]
|
71
|
+
@args[:fetch_database_names] = opts[:fetch_database_names]
|
72
|
+
|
73
|
+
unless Dir.exists?(opts[:log_directory])
|
74
|
+
Trollop::die :log_directory, "log directory not found\n" <<
|
75
|
+
"Supplied directory: " + opts[:log_directory]
|
76
|
+
end
|
77
|
+
|
78
|
+
@args[:log_directory] = opts[:log_directory]
|
79
|
+
@args[:tmp_directory] = opts[:tmp_directory]
|
80
|
+
@args[:blast_hit_id] = opts[:blast_hit_id]
|
81
|
+
@args[:blast_hit_display_id] = opts[:blast_hit_display_id]
|
82
|
+
@args[:blast_algo] = opts[:blast_algo]
|
83
|
+
|
84
|
+
## Check System Dependancies ##
|
85
|
+
|
86
|
+
check_dependencies
|
87
|
+
|
88
|
+
## Execute fetch tool ##
|
89
|
+
|
90
|
+
if @args[:fetch_tool].include? "blastdbcmd"
|
91
|
+
blast = Quorum::FetchTools::BlastDb.new(@args)
|
92
|
+
blast.execute_blast_db_cmd
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Check system dependencies.
|
98
|
+
#
|
99
|
+
def check_dependencies
|
100
|
+
FETCH_TOOLS[@args[:fetch_tool]].each do |f|
|
101
|
+
system("which #{f} > /dev/null 2>&1")
|
102
|
+
if $?.exitstatus > 0
|
103
|
+
Trollop::die "Quorum dependency not found for tool " <<
|
104
|
+
"#{@args[:fetch_tool].to_s}. Please add `#{f}` to your PATH."
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if __FILE__ == $0
|
113
|
+
Quorum::Fetch.new
|
114
|
+
end
|
115
|
+
|
Binary file
|
Binary file
|
@@ -0,0 +1,106 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path("../../", __FILE__))
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Quorum
|
6
|
+
module FetchTools
|
7
|
+
#
|
8
|
+
# Blast DB Fetch Tool
|
9
|
+
#
|
10
|
+
class BlastDb
|
11
|
+
|
12
|
+
# blastdbcmd -dbtype
|
13
|
+
DB_TYPE = {
|
14
|
+
"blastn" => "nucl",
|
15
|
+
"blastx" => "prot",
|
16
|
+
"tblastn" => "nucl",
|
17
|
+
"blastp" => "prot"
|
18
|
+
}
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def initialize(args)
|
23
|
+
@tool = args[:fetch_tool]
|
24
|
+
@entry = args[:blast_hit_id]
|
25
|
+
@hit_display_id = args[:blast_hit_display_id]
|
26
|
+
@blast_algo = args[:blast_algo]
|
27
|
+
@log_directory = args[:log_directory]
|
28
|
+
@tmp_directory = args[:tmp_directory]
|
29
|
+
@fetch_database = args[:fetch_database]
|
30
|
+
@databases = args[:fetch_database_names]
|
31
|
+
|
32
|
+
@logger = Quorum::Logger.new(@log_directory)
|
33
|
+
|
34
|
+
@db = @databases.split(';')
|
35
|
+
@db.map! { |d| File.join(@fetch_database, d) }
|
36
|
+
|
37
|
+
@db_type = DB_TYPE[@blast_algo]
|
38
|
+
@db_type ||= 'guess'
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Generate the blastdbcmd(s).
|
43
|
+
#
|
44
|
+
def generate_blast_db_cmds
|
45
|
+
@cmds = []
|
46
|
+
@db.each do |d|
|
47
|
+
blastdbcmd = "blastdbcmd " <<
|
48
|
+
"-db '#{d}' " <<
|
49
|
+
"-dbtype '#{@db_type}' " <<
|
50
|
+
"-entry '#{@entry}' "
|
51
|
+
@cmds << blastdbcmd
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Parse the blastdbcmd returned sequence(s) and delete if @hit_display_id
|
57
|
+
# is not present.
|
58
|
+
#
|
59
|
+
def parse_and_send_results
|
60
|
+
seqs = @seqs.split('>')
|
61
|
+
seqs.delete_if { |s| s.empty? || !s.include?(@hit_display_id) }
|
62
|
+
if seqs.length != 1
|
63
|
+
@logger.log(
|
64
|
+
"Quorum::FetchTools::BlastDb#parse_and_send_results",
|
65
|
+
"blastdbcmd returned #{seqs.length} sequence(s). Please ensure " <<
|
66
|
+
"your Blast database source FASTA headers are unique across ALL " <<
|
67
|
+
"databases. See the entry above for details."
|
68
|
+
)
|
69
|
+
"An error occurred while processing your request."
|
70
|
+
else
|
71
|
+
">" << seqs.first
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
public
|
76
|
+
|
77
|
+
#
|
78
|
+
# Execute the blastdbcmd(s) and return the matching sequence.
|
79
|
+
#
|
80
|
+
# To make Blast execute as quickly as possible, each selected
|
81
|
+
# algorithm blasts against all supplied databases at once.
|
82
|
+
#
|
83
|
+
# See quorum/lib/search_tools/blast.rb for more information.
|
84
|
+
#
|
85
|
+
# One consequence of this is not knowing which Blast database to use
|
86
|
+
# when retrieving a hit sequence via blastdbcmd.
|
87
|
+
#
|
88
|
+
# See blastdbcmd -help for more information.
|
89
|
+
#
|
90
|
+
# To work around this issue, simply execute blastdbcmd against all
|
91
|
+
# Blast databases and filter on hit_display_id.
|
92
|
+
#
|
93
|
+
def execute_blast_db_cmd
|
94
|
+
generate_blast_db_cmds
|
95
|
+
@logger.log("NCBI Blast", @cmds.join('; '))
|
96
|
+
|
97
|
+
@seqs = ""
|
98
|
+
@cmds.each { |c| @seqs << `#{c} 2> /dev/null` }
|
99
|
+
|
100
|
+
seq = parse_and_send_results
|
101
|
+
$stdout.print seq
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -11,6 +11,8 @@ module Quorum
|
|
11
11
|
class Blast
|
12
12
|
|
13
13
|
class QuorumJob < ActiveRecord::Base
|
14
|
+
set_table_name "quorum_jobs"
|
15
|
+
|
14
16
|
has_one :quorum_blastn_job,
|
15
17
|
:foreign_key => "job_id"
|
16
18
|
has_many :quorum_blastn_job_reports,
|
@@ -33,38 +35,46 @@ module Quorum
|
|
33
35
|
end
|
34
36
|
|
35
37
|
class QuorumBlastnJob < ActiveRecord::Base
|
38
|
+
set_table_name "quorum_blastn_jobs"
|
36
39
|
belongs_to :quorum_job
|
37
40
|
has_many :quorum_blastn_job_reports
|
38
41
|
end
|
39
42
|
|
40
43
|
class QuorumBlastnJobReport < ActiveRecord::Base
|
44
|
+
set_table_name "quorum_blastn_job_reports"
|
41
45
|
belongs_to :quorum_blastn_job
|
42
46
|
end
|
43
47
|
|
44
48
|
class QuorumBlastxJob < ActiveRecord::Base
|
49
|
+
set_table_name "quorum_blastx_jobs"
|
45
50
|
belongs_to :quorum_job
|
46
51
|
has_many :quorum_blastx_job_reports
|
47
52
|
end
|
48
53
|
|
49
54
|
class QuorumBlastxJobReport < ActiveRecord::Base
|
55
|
+
set_table_name "quorum_blastx_job_reports"
|
50
56
|
belongs_to :quorum_blastx_job
|
51
57
|
end
|
52
58
|
|
53
59
|
class QuorumTblastnJob < ActiveRecord::Base
|
60
|
+
set_table_name "quorum_tblastn_jobs"
|
54
61
|
belongs_to :quorum_job
|
55
62
|
has_many :quorum_tblastn_job_reports
|
56
63
|
end
|
57
64
|
|
58
65
|
class QuorumTblastnJobReport < ActiveRecord::Base
|
66
|
+
set_table_name "quorum_tblastn_job_reports"
|
59
67
|
belongs_to :quorum_tblastn_job
|
60
68
|
end
|
61
69
|
|
62
70
|
class QuorumBlastpJob < ActiveRecord::Base
|
71
|
+
set_table_name "quorum_blastp_jobs"
|
63
72
|
belongs_to :quorum_job
|
64
73
|
has_many :quorum_blastp_job_reports
|
65
74
|
end
|
66
75
|
|
67
76
|
class QuorumBlastpJobReport < ActiveRecord::Base
|
77
|
+
set_table_name "quorum_blastp_job_reports"
|
68
78
|
belongs_to :quorum_blastp_job
|
69
79
|
end
|
70
80
|
|
@@ -235,26 +245,26 @@ module Quorum
|
|
235
245
|
end
|
236
246
|
|
237
247
|
#
|
238
|
-
# Format Blast report
|
248
|
+
# Format Blast report hit_display_id and hit_def.
|
239
249
|
#
|
240
250
|
# For added flexibility, Quorum doesn't parse seqids or deflines.
|
241
251
|
# Instead, format_hit splits the hit_def on whitespace and
|
242
|
-
# reports
|
252
|
+
# reports hit_display_id as the first element and hit_def as the second.
|
243
253
|
#
|
244
254
|
def format_hit(str)
|
245
|
-
|
255
|
+
hit_display_id = ""
|
246
256
|
hit_def = ""
|
247
257
|
|
248
258
|
hit = str.split(" ", 2)
|
249
259
|
if hit.length < 2
|
250
|
-
|
260
|
+
hit_display_id = hit.first
|
251
261
|
hit_def = "None"
|
252
262
|
else
|
253
|
-
|
263
|
+
hit_display_id = hit.first
|
254
264
|
hit_def = hit.last
|
255
265
|
end
|
256
266
|
|
257
|
-
return
|
267
|
+
return hit_display_id, hit_def
|
258
268
|
end
|
259
269
|
|
260
270
|
#
|
@@ -275,8 +285,9 @@ module Quorum
|
|
275
285
|
@data[:query_len] = iteration.query_len
|
276
286
|
|
277
287
|
iteration.each do |hit|
|
278
|
-
@data[:
|
288
|
+
@data[:hit_display_id], @data[:hit_def] = format_hit(hit.hit_def)
|
279
289
|
|
290
|
+
@data[:hit_id] = hit.hit_id
|
280
291
|
@data[:hit_accession] = hit.accession
|
281
292
|
@data[:hit_len] = hit.len
|
282
293
|
|
data/spec/models/job_spec.rb
CHANGED
@@ -4,6 +4,7 @@ describe Quorum::Job do
|
|
4
4
|
|
5
5
|
before(:each) do
|
6
6
|
@job = Quorum::Job.new()
|
7
|
+
ResqueSpec.reset!
|
7
8
|
end
|
8
9
|
|
9
10
|
it "fails validation without params (using error_on)" do
|
@@ -22,9 +23,6 @@ describe Quorum::Job do
|
|
22
23
|
end
|
23
24
|
|
24
25
|
it "queues workers after save" do
|
25
|
-
resque = double("Resque")
|
26
|
-
resque.stub(:enqueue)
|
27
|
-
|
28
26
|
@job.sequence = File.open(
|
29
27
|
File.expand_path("../../data/nucl_prot_seqs.txt", __FILE__)
|
30
28
|
).read
|
@@ -33,8 +31,9 @@ describe Quorum::Job do
|
|
33
31
|
@job.blastn_job.queue = true
|
34
32
|
@job.blastn_job.blast_dbs = ["tmp"]
|
35
33
|
|
36
|
-
@job.should_receive(:queue_workers)
|
37
34
|
@job.save!
|
35
|
+
|
36
|
+
Workers::System.should have_queue_size_of(1)
|
38
37
|
end
|
39
38
|
|
40
39
|
end
|
@@ -61,4 +61,9 @@ describe "Quorum" do
|
|
61
61
|
f = File.open(File.join(@dummy_path, "config", "routes.rb"), "r")
|
62
62
|
f.read.include?("mount Quorum::Engine => \"/quorum\"").should be_true
|
63
63
|
end
|
64
|
+
|
65
|
+
it "ensures Resque::Server is mounted in dummy/config/routes.rb" do
|
66
|
+
f = File.open(File.join(@dummy_path, "config", "routes.rb"), "r")
|
67
|
+
f.read.include?("mount Resque::Server.new, :at => \"/quorum/resque\"").should be_true
|
68
|
+
end
|
64
69
|
end
|
data/spec/requests/jobs_spec.rb
CHANGED
@@ -51,8 +51,12 @@ describe "Jobs" do
|
|
51
51
|
before(:all) do
|
52
52
|
Capybara.current_driver = :selenium
|
53
53
|
end
|
54
|
+
before(:each) do
|
55
|
+
ResqueSpec.reset!
|
56
|
+
ResqueSpec.inline = true
|
57
|
+
end
|
54
58
|
describe "submit sequences in attached file" do
|
55
|
-
it "check
|
59
|
+
it "check algorithms, fill in values, view results and download hit sequence" do
|
56
60
|
visit new_job_path
|
57
61
|
current_path.should eq(new_job_path)
|
58
62
|
|
@@ -109,13 +113,22 @@ describe "Jobs" do
|
|
109
113
|
click_link "Tblastn"
|
110
114
|
page.should have_content("Your search returned 0 hits.")
|
111
115
|
|
116
|
+
## Interact with the Blast results. ##
|
112
117
|
click_link "Blastn"
|
118
|
+
|
119
|
+
# Render modal box.
|
113
120
|
find("#blastn-results").find("td a").click
|
114
121
|
page.should have_content("Quorum Report Details")
|
115
122
|
page.should have_content("qseq")
|
116
123
|
page.should have_content("hseq")
|
124
|
+
# Download sequence
|
125
|
+
find("p.small a#download_sequence_1").click
|
126
|
+
page.should have_content("Fetching sequence...")
|
127
|
+
page.should have_content("Sequence Downloaded Successfully")
|
117
128
|
|
118
129
|
click_link "Blastp"
|
130
|
+
|
131
|
+
# Render modal box.
|
119
132
|
find("#blastp-results").find("td a").click
|
120
133
|
page.should have_content("Quorum Report Details")
|
121
134
|
page.should have_content("qseq")
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,7 @@ require 'rspec/rails'
|
|
5
5
|
require 'capybara/rspec'
|
6
6
|
require 'factory_girl'
|
7
7
|
require 'database_cleaner'
|
8
|
+
require 'resque_spec'
|
8
9
|
|
9
10
|
FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
|
10
11
|
FactoryGirl.find_definitions
|
@@ -13,7 +14,7 @@ ENGINE_RAILS_ROOT = File.expand_path("../../", __FILE__)
|
|
13
14
|
|
14
15
|
# Requires supporting ruby files with custom matchers and macros, etc,
|
15
16
|
# in spec/support/ and its subdirectories.
|
16
|
-
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
17
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
|
17
18
|
|
18
19
|
RSpec.configure do |config|
|
19
20
|
config.mock_with :rspec
|
@@ -42,4 +43,34 @@ RSpec.configure do |config|
|
|
42
43
|
config.after(:each) do
|
43
44
|
DatabaseCleaner.clean
|
44
45
|
end
|
46
|
+
|
47
|
+
## Redis ##
|
48
|
+
REDIS_PID = File.expand_path("../dummy/tmp/pids/redis-test.pid", __FILE__)
|
49
|
+
REDIS_CACHE_PATH = File.expand_path("../dummy/tmp/cache/", __FILE__)
|
50
|
+
|
51
|
+
config.before(:suite) do
|
52
|
+
redis_options = {
|
53
|
+
"daemonize" => "yes",
|
54
|
+
"pidfile" => REDIS_PID,
|
55
|
+
"port" => 9736,
|
56
|
+
"timeout" => 300,
|
57
|
+
"save 900" => 1,
|
58
|
+
"save 300" => 1,
|
59
|
+
"save 60" => 10000,
|
60
|
+
"dbfilename" => "dump.rdb",
|
61
|
+
"dir" => REDIS_CACHE_PATH,
|
62
|
+
"loglevel" => "debug",
|
63
|
+
"logfile" => "stdout",
|
64
|
+
"databases" => 16
|
65
|
+
}.map { |k, v| "#{k} #{v}" }.join('
|
66
|
+
')
|
67
|
+
`echo '#{redis_options}' | redis-server -`
|
68
|
+
end
|
69
|
+
|
70
|
+
config.after(:suite) do
|
71
|
+
%x{
|
72
|
+
cat #{REDIS_PID} | xargs kill -QUIT
|
73
|
+
rm -f #{REDIS_CACHE_PATH}dump.rdb
|
74
|
+
}
|
75
|
+
end
|
45
76
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Capture STDOUT and STDERR
|
5
|
+
#
|
6
|
+
def capture(*streams)
|
7
|
+
streams.map! { |stream| stream.to_s }
|
8
|
+
begin
|
9
|
+
result = StringIO.new
|
10
|
+
streams.each { |stream| eval "$#{stream} = result" }
|
11
|
+
yield
|
12
|
+
ensure
|
13
|
+
streams.each { |stream| eval("$#{stream} = #{stream.upcase}") }
|
14
|
+
end
|
15
|
+
result.string
|
16
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.expand_path("../../dummy/quorum/lib/fetch_tools/blast_db", __FILE__)
|
3
|
+
|
4
|
+
describe "Quorum::FetchTools::BlastDb" do
|
5
|
+
describe "#execute_blast_db_cmd" do
|
6
|
+
before(:each) do
|
7
|
+
# Set the args as though we executed bin/fetch
|
8
|
+
@args = {
|
9
|
+
:fetch_tool => "blastdbcmd",
|
10
|
+
:blast_hit_id => nil,
|
11
|
+
:blast_hit_display_id => nil,
|
12
|
+
:blast_algo => nil,
|
13
|
+
:fetch_database_names => "test",
|
14
|
+
|
15
|
+
:log_directory => File.expand_path(
|
16
|
+
"../../dummy/quorum/log", __FILE__
|
17
|
+
),
|
18
|
+
:tmp_directory => File.expand_path(
|
19
|
+
"../../dummy/quorum/tmp", __FILE__
|
20
|
+
),
|
21
|
+
:fetch_database => File.expand_path(
|
22
|
+
"../../dummy/quorum/blastdb", __FILE__
|
23
|
+
)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "executes blastdbcmd for blastn and returns correct sequence" do
|
28
|
+
@args[:blast_hit_id] = "gnl|BL_ORD_ID|0"
|
29
|
+
@args[:blast_hit_display_id] = "TOG900080"
|
30
|
+
@args[:blast_algo] = "blastn"
|
31
|
+
|
32
|
+
seqs = File.readlines(
|
33
|
+
File.expand_path("../../data/nucl_seqs.txt", __FILE__)
|
34
|
+
)
|
35
|
+
seqs[0].gsub!(">", "")
|
36
|
+
seqs.insert(0, ">" + @args[:blast_hit_id] + " ")
|
37
|
+
|
38
|
+
# Sequence #execute_blast_db_cmd should return.
|
39
|
+
seq = seqs.slice(0,3).join("").gsub("\n", "")
|
40
|
+
|
41
|
+
fetch = Quorum::FetchTools::BlastDb.new(@args)
|
42
|
+
lambda {
|
43
|
+
output = capture(:stdout) {
|
44
|
+
fetch.execute_blast_db_cmd
|
45
|
+
}
|
46
|
+
output.gsub("\n", "").should eq(seq)
|
47
|
+
}.should_not raise_error
|
48
|
+
end
|
49
|
+
|
50
|
+
it "returns error message if filtered sequence.length != 1" do
|
51
|
+
@args[:blast_hit_id] = "null"
|
52
|
+
@args[:blast_hit_display_id] = "null"
|
53
|
+
@args[:blast_algo] = "blastp"
|
54
|
+
fetch = Quorum::FetchTools::BlastDb.new(@args)
|
55
|
+
lambda {
|
56
|
+
output = capture(:stdout) {
|
57
|
+
fetch.execute_blast_db_cmd
|
58
|
+
}
|
59
|
+
output.should eq("An error occurred while processing your request.")
|
60
|
+
}.should_not raise_error
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,8 +1,114 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require
|
2
|
+
require File.expand_path("../../dummy/quorum/lib/search_tools/blast", __FILE__)
|
3
3
|
|
4
|
-
# Let the request specs handle Quorum::SearchTools
|
5
4
|
describe "Quorum::SearchTools::Blast" do
|
6
5
|
describe "#execute_blast" do
|
6
|
+
before(:each) do
|
7
|
+
# Set args as though we executed bin/search.
|
8
|
+
@args = {
|
9
|
+
:search_tool => "blastn",
|
10
|
+
:id => nil,
|
11
|
+
:log_directory => File.expand_path(
|
12
|
+
"../../dummy/quorum/log", __FILE__
|
13
|
+
),
|
14
|
+
:tmp_directory => File.expand_path(
|
15
|
+
"../../dummy/quorum/tmp", __FILE__
|
16
|
+
),
|
17
|
+
:search_database => File.expand_path(
|
18
|
+
"../../dummy/quorum/blastdb", __FILE__
|
19
|
+
),
|
20
|
+
:threads => 1
|
21
|
+
}
|
22
|
+
|
23
|
+
@job = Quorum::Job.new()
|
24
|
+
|
25
|
+
@job.sequence = File.open(
|
26
|
+
File.expand_path("../../data/nucl_prot_seqs.txt", __FILE__)
|
27
|
+
).read
|
28
|
+
|
29
|
+
@job.build_blastn_job
|
30
|
+
@job.blastn_job.queue = true
|
31
|
+
@job.blastn_job.blast_dbs = ["test"]
|
32
|
+
|
33
|
+
@job.build_blastx_job
|
34
|
+
@job.blastx_job.queue = true
|
35
|
+
@job.blastx_job.blast_dbs = ["test"]
|
36
|
+
|
37
|
+
@job.build_tblastn_job
|
38
|
+
@job.tblastn_job.queue = true
|
39
|
+
@job.tblastn_job.blast_dbs = ["test"]
|
40
|
+
|
41
|
+
@job.build_blastp_job
|
42
|
+
@job.blastp_job.queue = true
|
43
|
+
@job.blastp_job.blast_dbs = ["test"]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "executes blastn on a given dataset" do
|
47
|
+
@job.stub(:queue_workers)
|
48
|
+
@job.save!
|
49
|
+
|
50
|
+
@args[:id] = @job.id
|
51
|
+
|
52
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
53
|
+
expect {
|
54
|
+
blast.execute_blast
|
55
|
+
}.to_not raise_error
|
56
|
+
|
57
|
+
Dir.glob(
|
58
|
+
File.join(@args[:tmp_directory], "*")
|
59
|
+
).length.should be == 0
|
60
|
+
end
|
61
|
+
|
62
|
+
it "executes blastx on a given dataset" do
|
63
|
+
@job.stub(:queue_workers)
|
64
|
+
@job.save!
|
65
|
+
|
66
|
+
@args[:search_tool] = "blastx"
|
67
|
+
@args[:id] = @job.id
|
68
|
+
|
69
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
70
|
+
expect {
|
71
|
+
blast.execute_blast
|
72
|
+
}.to raise_error(SystemExit)
|
73
|
+
|
74
|
+
Dir.glob(
|
75
|
+
File.join(@args[:tmp_directory], "*")
|
76
|
+
).length.should be == 0
|
77
|
+
end
|
78
|
+
|
79
|
+
it "executes tblastn on a given dataset" do
|
80
|
+
@job.stub(:queue_workers)
|
81
|
+
@job.save!
|
82
|
+
|
83
|
+
@args[:search_tool] = "tblastn"
|
84
|
+
@args[:id] = @job.id
|
85
|
+
|
86
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
87
|
+
expect {
|
88
|
+
blast.execute_blast
|
89
|
+
}.to raise_error(SystemExit)
|
90
|
+
|
91
|
+
Dir.glob(
|
92
|
+
File.join(@args[:tmp_directory], "*")
|
93
|
+
).length.should be == 0
|
94
|
+
end
|
95
|
+
|
96
|
+
it "executes blastp on a given dataset" do
|
97
|
+
@job.stub(:queue_workers)
|
98
|
+
@job.save!
|
99
|
+
|
100
|
+
@args[:search_tool] = "blastp"
|
101
|
+
@args[:id] = @job.id
|
102
|
+
|
103
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
104
|
+
expect {
|
105
|
+
blast.execute_blast
|
106
|
+
}.to_not raise_error
|
107
|
+
|
108
|
+
Dir.glob(
|
109
|
+
File.join(@args[:tmp_directory], "*")
|
110
|
+
).length.should be == 0
|
111
|
+
end
|
112
|
+
|
7
113
|
end
|
8
114
|
end
|