sequenceserver 3.0.1 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sequenceserver might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/sequenceserver +2 -2
- data/lib/sequenceserver/api_errors.rb +32 -2
- data/lib/sequenceserver/blast/job.rb +20 -3
- data/lib/sequenceserver/blast/report.rb +74 -86
- data/lib/sequenceserver/blast/tasks.rb +38 -0
- data/lib/sequenceserver/config.rb +54 -20
- data/lib/sequenceserver/makeblastdb.rb +16 -2
- data/lib/sequenceserver/report.rb +0 -6
- data/lib/sequenceserver/routes.rb +66 -25
- data/lib/sequenceserver/sequence.rb +35 -7
- data/lib/sequenceserver/server.rb +1 -1
- data/lib/sequenceserver/version.rb +1 -1
- data/lib/sequenceserver.rb +1 -1
- data/public/404.html +1 -1
- data/public/css/app.css +121 -0
- data/public/css/app.min.css +1 -0
- data/public/css/sequenceserver.css +0 -148
- data/public/css/sequenceserver.min.css +3 -3
- data/public/js/circos.js +2 -2
- data/public/js/collapse_preferences.js +37 -0
- data/public/js/databases.js +65 -37
- data/public/js/databases_tree.js +2 -1
- data/public/js/dnd.js +37 -50
- data/public/js/download_fasta.js +1 -0
- data/public/js/form.js +79 -50
- data/public/js/grapher.js +23 -37
- data/public/js/hits_overview.js +2 -2
- data/public/js/kablammo.js +2 -2
- data/public/js/length_distribution.js +3 -3
- data/public/js/null_plugins/grapher/histogram.js +25 -0
- data/public/js/null_plugins/options.js +3 -0
- data/public/js/null_plugins/query_stats.js +11 -0
- data/public/js/null_plugins/report_plugins.js +6 -1
- data/public/js/null_plugins/search_header_plugin.js +4 -0
- data/public/js/options.js +161 -56
- data/public/js/query.js +85 -59
- data/public/js/report.js +1 -1
- data/public/js/search.js +2 -0
- data/public/js/search_button.js +67 -56
- data/public/js/sidebar.js +10 -1
- data/public/js/tests/database.spec.js +5 -5
- data/public/js/tests/form.spec.js +98 -0
- data/public/js/tests/mock_data/databases.json +5 -5
- data/public/js/tests/mocks/circos.js +6 -0
- data/public/js/tests/report.spec.js +4 -3
- data/public/js/tests/search_query.spec.js +16 -6
- data/public/sequenceserver-report.min.js +46 -24
- data/public/sequenceserver-search.min.js +57 -13
- data/public/sequenceserver_logo.webp +0 -0
- data/views/blastn_options.erb +66 -66
- data/views/blastp_options.erb +59 -59
- data/views/blastx_options.erb +68 -68
- data/views/layout.erb +61 -3
- data/views/search.erb +33 -38
- data/views/search_layout.erb +153 -0
- data/views/tblastn_options.erb +57 -57
- data/views/tblastx_options.erb +64 -64
- metadata +51 -22
- data/lib/sequenceserver/makeblastdb-modified-with-cache.rb +0 -345
- data/public/SequenceServer_logo.png +0 -0
- data/public/js/tests/advanced_parameters.spec.js +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b058e493103527b8d0120243180b748b256997416d9886c3d349539d0afe9420
|
4
|
+
data.tar.gz: 836645ba201b125b1f379b934b86186128b76717c5bd456c85caa71e5cdc9269
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16e202385e0704918489da25ade9f938e3afe92a3298e338a2462188da049d9c780e80ba3cfe6de1c156196901d50570c845e030de71980151b2580ffd15a65f
|
7
|
+
data.tar.gz: a1033d77cb3feb609d0cf0b2f6af7dbfd558cca05027c4a6308d176f39c39b281d9dbf16b5de4d4d58620d00e372078a14103b35b456dc79b153d2d76c3c24e0
|
data/bin/sequenceserver
CHANGED
@@ -398,8 +398,8 @@ begin
|
|
398
398
|
end
|
399
399
|
|
400
400
|
if import?
|
401
|
-
|
402
|
-
params = {:xml =>
|
401
|
+
xml_file_path = fetch_option(:import).value
|
402
|
+
params = {:xml => xml_file_path}
|
403
403
|
job = SequenceServer::BLAST::Job.new(params)
|
404
404
|
puts job.id
|
405
405
|
exit
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module SequenceServer
|
2
|
-
Error = Class.new(
|
2
|
+
Error = Class.new(Sinatra::Error)
|
3
3
|
|
4
|
+
ValidationError = Class.new(Error)
|
4
5
|
# DatabaseUnreachableError is raised when the serialised Job object is
|
5
6
|
# refering to a database that is not present in the current filesystem.
|
6
|
-
class DatabaseUnreachableError <
|
7
|
+
class DatabaseUnreachableError < ValidationError
|
7
8
|
attr_reader :more_info
|
8
9
|
|
9
10
|
def initialize(more_info)
|
@@ -11,6 +12,10 @@ module SequenceServer
|
|
11
12
|
@more_info = more_info
|
12
13
|
end
|
13
14
|
|
15
|
+
def http_status
|
16
|
+
422
|
17
|
+
end
|
18
|
+
|
14
19
|
def title
|
15
20
|
'Sequence database unreachable'
|
16
21
|
end
|
@@ -22,6 +27,31 @@ module SequenceServer
|
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
30
|
+
# InvalidSequenceIdError is raised when the FASTA sequence ID provided by the
|
31
|
+
# frontend appears to be invalid. It is important to validate the sequence ID
|
32
|
+
# format for security reasons.
|
33
|
+
class InvalidSequenceIdError < ValidationError
|
34
|
+
attr_reader :more_info
|
35
|
+
|
36
|
+
def initialize(more_info)
|
37
|
+
super
|
38
|
+
@more_info = more_info
|
39
|
+
end
|
40
|
+
|
41
|
+
def http_status
|
42
|
+
422
|
43
|
+
end
|
44
|
+
|
45
|
+
def title
|
46
|
+
'Sequence ID invalid'
|
47
|
+
end
|
48
|
+
|
49
|
+
def message
|
50
|
+
"The action you're trying to perform is not possible because \
|
51
|
+
one of the FASTA ids seems to be invalid."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
25
55
|
# API errors have an http status, title, message, and additional information
|
26
56
|
# like stacktrace or information from program output.
|
27
57
|
APIError = Class.new(Error)
|
@@ -21,7 +21,8 @@ module SequenceServer
|
|
21
21
|
else
|
22
22
|
validate params
|
23
23
|
super do
|
24
|
-
@method
|
24
|
+
@method = params[:method]
|
25
|
+
@query = params[:sequence]
|
25
26
|
@qfile = store('query.fa', params[:sequence])
|
26
27
|
@databases = Database[params[:databases]]
|
27
28
|
@advanced = params[:advanced].to_s.strip
|
@@ -29,6 +30,7 @@ module SequenceServer
|
|
29
30
|
# The following params are for analytics only
|
30
31
|
@num_threads = config[:num_threads]
|
31
32
|
@query_length = calculate_query_size
|
33
|
+
@number_of_query_sequences = calculate_number_of_sequences
|
32
34
|
@databases_ncharacters_total = calculate_databases_ncharacters_total
|
33
35
|
end
|
34
36
|
end
|
@@ -36,8 +38,15 @@ module SequenceServer
|
|
36
38
|
|
37
39
|
# :nodoc:
|
38
40
|
# Attributes used by us - should be considered private.
|
39
|
-
attr_reader :advanced
|
40
|
-
|
41
|
+
attr_reader :advanced,
|
42
|
+
:databases,
|
43
|
+
:databases_ncharacters_total,
|
44
|
+
:method,
|
45
|
+
:num_threads,
|
46
|
+
:options,
|
47
|
+
:qfile,
|
48
|
+
:query_length,
|
49
|
+
:number_of_query_sequences
|
41
50
|
|
42
51
|
# :nodoc:
|
43
52
|
# Deprecated; see Report#extract_params
|
@@ -92,6 +101,14 @@ module SequenceServer
|
|
92
101
|
size
|
93
102
|
end
|
94
103
|
|
104
|
+
def calculate_number_of_sequences
|
105
|
+
# splitting the query by ">" starting a new line lets us determine number of sequences
|
106
|
+
sequences = @query.split(/\n\s*>\s*+/)
|
107
|
+
# Remove any empty strings from the split result
|
108
|
+
sequences.reject!(&:empty?)
|
109
|
+
sequences.length
|
110
|
+
end
|
111
|
+
|
95
112
|
def validate(params)
|
96
113
|
validate_method params[:method]
|
97
114
|
validate_sequences params[:sequence]
|
@@ -24,20 +24,11 @@ module SequenceServer
|
|
24
24
|
class Report < Report
|
25
25
|
def initialize(job)
|
26
26
|
super do
|
27
|
-
@queries = []
|
28
27
|
@querydb = job.databases
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
|
-
# Attributes parsed out from BLAST output.
|
33
|
-
attr_reader :program, :program_version, :stats, :queries
|
34
|
-
|
35
|
-
# Attributes parsed from job metadata and BLAST output.
|
36
|
-
attr_reader :querydb, :dbtype, :params
|
37
|
-
|
38
31
|
def to_json(*_args)
|
39
|
-
generate
|
40
|
-
|
41
32
|
%i[querydb program program_version params stats
|
42
33
|
queries].inject({}) do |h, k|
|
43
34
|
h[k] = send(k)
|
@@ -53,83 +44,89 @@ module SequenceServer
|
|
53
44
|
def xml_file_size
|
54
45
|
return File.size(job.imported_xml_file) if job.imported_xml_file
|
55
46
|
|
56
|
-
generate
|
57
|
-
|
58
47
|
xml_formatter.size
|
59
48
|
end
|
60
49
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
50
|
+
def done?
|
51
|
+
return true if job.imported_xml_file
|
52
|
+
|
53
|
+
File.exist?(xml_formatter.filepath) && File.exist?(tsv_formatter.filepath)
|
54
|
+
end
|
55
|
+
|
56
|
+
def program
|
57
|
+
@program ||= xml_ir[0]
|
58
|
+
end
|
59
|
+
|
60
|
+
def program_version
|
61
|
+
@program_version ||= xml_ir[1]
|
62
|
+
end
|
63
|
+
|
64
|
+
def querydb
|
65
|
+
@querydb ||= xml_ir[3].split.map do |path|
|
66
|
+
{ title: File.basename(path) }
|
78
67
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
68
|
+
end
|
69
|
+
|
70
|
+
def dbtype
|
71
|
+
@dbtype ||= querydb&.first&.type || dbtype_from_program
|
72
|
+
end
|
84
73
|
|
85
|
-
|
74
|
+
def params
|
75
|
+
@params ||= extract_params
|
76
|
+
end
|
86
77
|
|
87
|
-
|
78
|
+
def stats
|
79
|
+
@stats ||= extract_stats
|
88
80
|
end
|
89
81
|
|
90
|
-
def
|
91
|
-
|
82
|
+
def queries
|
83
|
+
@queries ||= xml_ir[8].map do |n|
|
84
|
+
query = Query.new(self, n[0], n[2], n[3], [])
|
85
|
+
query.hits = query_hits(n[4], tsv_ir[query.id], query)
|
92
86
|
|
93
|
-
|
87
|
+
query
|
88
|
+
end
|
94
89
|
end
|
95
90
|
|
96
91
|
private
|
97
92
|
|
98
|
-
def
|
99
|
-
@
|
93
|
+
def xml_ir
|
94
|
+
@xml_ir ||=
|
95
|
+
if job.imported_xml_file
|
96
|
+
parse_xml File.read(job.imported_xml_file)
|
97
|
+
else
|
98
|
+
job.raise!
|
99
|
+
parse_xml(xml_formatter.read_file)
|
100
|
+
end
|
100
101
|
end
|
101
102
|
|
102
|
-
def
|
103
|
-
@
|
103
|
+
def tsv_ir
|
104
|
+
@tsv_ir ||=
|
105
|
+
if job.imported_xml_file
|
106
|
+
Hash.new do |h1, k1|
|
107
|
+
h1[k1] = Hash.new do |h2, k2|
|
108
|
+
h2[k2] = ['', '', []]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
else
|
112
|
+
job.raise!
|
113
|
+
parse_tsv(tsv_formatter.read_file)
|
114
|
+
end
|
104
115
|
end
|
105
116
|
|
106
|
-
|
107
|
-
|
108
|
-
def extract_program_info(ir)
|
109
|
-
@program = ir[0]
|
110
|
-
@program_version = ir[1]
|
117
|
+
def xml_formatter
|
118
|
+
@xml_formatter ||= Formatter.run(job, 'xml')
|
111
119
|
end
|
112
120
|
|
113
|
-
|
114
|
-
|
115
|
-
def extract_db_info(ir)
|
116
|
-
if @querydb.empty?
|
117
|
-
@querydb = ir[3].split.map do |path|
|
118
|
-
{ title: File.basename(path) }
|
119
|
-
end
|
120
|
-
@dbtype = dbtype_from_program
|
121
|
-
else
|
122
|
-
@dbtype = @querydb.first.type
|
123
|
-
end
|
121
|
+
def tsv_formatter
|
122
|
+
@tsv_formatter ||= Formatter.run(job, 'custom_tsv')
|
124
123
|
end
|
125
124
|
|
126
|
-
# Make search params available via `params` attribute.
|
127
|
-
#
|
128
125
|
# Search params tweak the results. Like evalue cutoff or penalty to open
|
129
126
|
# a gap. BLAST+ doesn't list all input params in the XML output. Only
|
130
127
|
# matrix, evalue, gapopen, gapextend, and filters are available from XML
|
131
128
|
# output.
|
132
|
-
def extract_params
|
129
|
+
def extract_params
|
133
130
|
# Parse/get params from the job first.
|
134
131
|
job_params = parse_advanced(job.advanced)
|
135
132
|
# Old jobs from beta releases may not have the advanced key but they
|
@@ -138,7 +135,7 @@ module SequenceServer
|
|
138
135
|
|
139
136
|
# Parse params from BLAST XML.
|
140
137
|
@params = Hash[
|
141
|
-
*
|
138
|
+
*xml_ir[7].first.map { |k, v| [k.gsub('Parameters_', ''), v] }.flatten
|
142
139
|
]
|
143
140
|
@params['evalue'] = @params.delete('expect')
|
144
141
|
|
@@ -146,13 +143,11 @@ module SequenceServer
|
|
146
143
|
@params = job_params.merge(@params)
|
147
144
|
end
|
148
145
|
|
149
|
-
# Make search stats available via `stats` attribute.
|
150
|
-
#
|
151
146
|
# Search stats are computed metrics. Like total number of sequences or
|
152
147
|
# effective search space.
|
153
|
-
def extract_stats
|
154
|
-
stats
|
155
|
-
|
148
|
+
def extract_stats
|
149
|
+
stats = xml_ir[8].first[5][0]
|
150
|
+
{
|
156
151
|
nsequences: stats[0],
|
157
152
|
ncharacters: stats[1],
|
158
153
|
hsp_length: stats[2],
|
@@ -163,20 +158,11 @@ module SequenceServer
|
|
163
158
|
}
|
164
159
|
end
|
165
160
|
|
166
|
-
# Create query objects for the given report from the given ir.
|
167
|
-
def extract_queries(xml_ir, tsv_ir)
|
168
|
-
xml_ir[8].each do |n|
|
169
|
-
query = Query.new(self, n[0], n[2], n[3], [])
|
170
|
-
extract_hits(n[4], tsv_ir[query.id], query)
|
171
|
-
queries << query
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
161
|
# Create Hit objects for the given query from the given ir.
|
176
|
-
def
|
177
|
-
return if xml_ir == ["\n"] # => No hits.
|
162
|
+
def query_hits(xml_ir, tsv_ir, query)
|
163
|
+
return [] if xml_ir == ["\n"] # => No hits.
|
178
164
|
|
179
|
-
xml_ir.
|
165
|
+
xml_ir.map do |n|
|
180
166
|
# If hit comes from a non -parse_seqids database, then id (n[1]) is a
|
181
167
|
# BLAST assigned internal id of the format 'gnl|BL_ORD_ID|serial'. We
|
182
168
|
# assign the id to accession (because we use accession for sequence
|
@@ -190,19 +176,21 @@ module SequenceServer
|
|
190
176
|
n[1] = defline.shift
|
191
177
|
n[2] = defline.join(' ')
|
192
178
|
end
|
179
|
+
|
193
180
|
hit = Hit.new(query, n[0], n[1], n[3], n[2], n[4],
|
194
181
|
tsv_ir[n[1]][0], tsv_ir[n[1]][1], [])
|
195
|
-
|
196
|
-
|
182
|
+
|
183
|
+
hit.hsps = hsps(n[5], tsv_ir[n[1]][2], hit)
|
184
|
+
|
185
|
+
hit
|
197
186
|
end
|
198
187
|
end
|
199
188
|
|
200
|
-
|
201
|
-
|
202
|
-
xml_ir.each_with_index do |n, i|
|
189
|
+
def hsps(xml_ir, tsv_ir, hit)
|
190
|
+
xml_ir.map.with_index do |n, i|
|
203
191
|
n.insert(14, tsv_ir[i])
|
204
|
-
|
205
|
-
hit
|
192
|
+
|
193
|
+
HSP.new(hit, *n)
|
206
194
|
end
|
207
195
|
end
|
208
196
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module SequenceServer
|
2
|
+
module BLAST
|
3
|
+
# Shells out to each blast algorithm to get the help text and then parses it to extract the tasks.
|
4
|
+
module Tasks
|
5
|
+
ALGORITHMS = %w[blastn blastp blastx tblastn tblastx].freeze
|
6
|
+
|
7
|
+
def self.to_h
|
8
|
+
@to_h ||= ALGORITHMS.map do |algorithm|
|
9
|
+
help_text = `#{algorithm} -help`
|
10
|
+
[algorithm, extract_tasks(help_text)]
|
11
|
+
end.to_h
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.extract_tasks(help_text)
|
15
|
+
lines = help_text.split("\n")
|
16
|
+
|
17
|
+
# Find task help paragraph start
|
18
|
+
task_line_index = lines.find_index { |line| line =~ /^\W-task/ }
|
19
|
+
return [] unless task_line_index.to_i.positive?
|
20
|
+
|
21
|
+
lines.slice!(0...task_line_index)
|
22
|
+
|
23
|
+
# Find the end of task help paragraph
|
24
|
+
next_option_line_index = lines.find_index { |line| line =~ /^\W-/ && !line.include?('-task') }
|
25
|
+
lines.slice!(next_option_line_index..-1)
|
26
|
+
|
27
|
+
extract_tasks_from_paragraph(lines)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.extract_tasks_from_paragraph(paragraph_lines)
|
31
|
+
as_one_liner = paragraph_lines.map(&:strip).join(' ')
|
32
|
+
as_one_liner.split('Permissible values:').last.split('>').first.split(' ').map do |task|
|
33
|
+
task.strip.gsub("'", '')
|
34
|
+
end.reject(&:empty?)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -22,7 +22,7 @@ module SequenceServer
|
|
22
22
|
|
23
23
|
return unless @upgraded
|
24
24
|
|
25
|
-
logger.
|
25
|
+
logger.warn 'You are using old configuration syntax. ' \
|
26
26
|
'Run `sequenceserver -s` to update your config file syntax.'
|
27
27
|
end
|
28
28
|
|
@@ -69,20 +69,7 @@ module SequenceServer
|
|
69
69
|
@upgrade = true
|
70
70
|
end
|
71
71
|
|
72
|
-
|
73
|
-
# array values into a hash. The logic is simple: If the array value is the
|
74
|
-
# same as default, we give it the key 'default', otherwise we give it the
|
75
|
-
# key 'custom'
|
76
|
-
data[:options]&.each do |key, val|
|
77
|
-
next if val.is_a? Hash
|
78
|
-
|
79
|
-
data[:options][key] = if val == defaults[:options][key][:default]
|
80
|
-
{ default: val }
|
81
|
-
else
|
82
|
-
{ custom: val }
|
83
|
-
end
|
84
|
-
@upgraded = true
|
85
|
-
end
|
72
|
+
data[:options] = convert_deprecated_options(data[:options]) if data[:options]
|
86
73
|
|
87
74
|
data
|
88
75
|
end
|
@@ -112,19 +99,34 @@ module SequenceServer
|
|
112
99
|
databases_widget: 'classic',
|
113
100
|
options: {
|
114
101
|
blastn: {
|
115
|
-
default:
|
102
|
+
default: {
|
103
|
+
description: nil,
|
104
|
+
attributes: ['-task blastn', '-evalue 1e-5']
|
105
|
+
}
|
116
106
|
},
|
117
107
|
blastp: {
|
118
|
-
default:
|
108
|
+
default: {
|
109
|
+
description: nil,
|
110
|
+
attributes: ['-evalue 1e-5']
|
111
|
+
}
|
119
112
|
},
|
120
113
|
blastx: {
|
121
|
-
default:
|
114
|
+
default: {
|
115
|
+
description: nil,
|
116
|
+
attributes: ['-evalue 1e-5']
|
117
|
+
}
|
122
118
|
},
|
123
119
|
tblastx: {
|
124
|
-
default:
|
120
|
+
default: {
|
121
|
+
description: nil,
|
122
|
+
attributes: ['-evalue 1e-5']
|
123
|
+
}
|
125
124
|
},
|
126
125
|
tblastn: {
|
127
|
-
default:
|
126
|
+
default: {
|
127
|
+
description: nil,
|
128
|
+
attributes: ['-evalue 1e-5']
|
129
|
+
}
|
128
130
|
}
|
129
131
|
},
|
130
132
|
num_threads: 1,
|
@@ -137,5 +139,37 @@ module SequenceServer
|
|
137
139
|
optimistic: false # Faster, but does not perform DB compatibility checks
|
138
140
|
}
|
139
141
|
end
|
142
|
+
|
143
|
+
def convert_deprecated_options(options)
|
144
|
+
options.each do |blast_algo, algo_config|
|
145
|
+
if algo_config.is_a?(Array)
|
146
|
+
# Very old config files may have a single array with CLI args.
|
147
|
+
# e.g. { blastn: ['-task blastn', '-evalue 1e-5'] }
|
148
|
+
# Convert the array values into a single hash naming it 'default' if
|
149
|
+
# the values match SequenceServer defaults.
|
150
|
+
options[blast_algo] = if algo_config == defaults.dig(:options, blast_algo, :default, :attributes)
|
151
|
+
{ default: { attributes: algo_config } }
|
152
|
+
else
|
153
|
+
{ custom: { attributes: algo_config } }
|
154
|
+
end
|
155
|
+
@upgraded = true
|
156
|
+
elsif algo_config.is_a?(Hash)
|
157
|
+
# v3.0.1 and older config files contain a flatter structure
|
158
|
+
# with an array instead of 'description' and 'attributes' keys.
|
159
|
+
# e.g. { blastn: { default: ['-task blastn', '-evalue 1e-5'] }
|
160
|
+
algo_config.each do |config_name, config|
|
161
|
+
next unless config.is_a?(Array)
|
162
|
+
|
163
|
+
options[blast_algo][config_name] = {
|
164
|
+
description: nil,
|
165
|
+
attributes: config
|
166
|
+
}
|
167
|
+
@upgraded = true
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
options
|
173
|
+
end
|
140
174
|
end
|
141
175
|
end
|
@@ -13,6 +13,7 @@ module SequenceServer
|
|
13
13
|
#
|
14
14
|
class MAKEBLASTDB
|
15
15
|
extend Forwardable
|
16
|
+
GUESS_SAMPLE_SIZE = 1_048_576
|
16
17
|
|
17
18
|
def_delegators SequenceServer, :config, :sys
|
18
19
|
|
@@ -333,8 +334,21 @@ module SequenceServer
|
|
333
334
|
# If the given file is FASTA, returns Array of as many different
|
334
335
|
# sequences in the portion of the file read. Returns the portion
|
335
336
|
# of the file read wrapped in an Array otherwise.
|
336
|
-
def sample_sequences(file)
|
337
|
-
File.read(file,
|
337
|
+
def sample_sequences(file, offset = 0)
|
338
|
+
sample = File.read(file, GUESS_SAMPLE_SIZE, offset)
|
339
|
+
|
340
|
+
return [] if sample.nil?
|
341
|
+
|
342
|
+
# remove all unknown bases (indicated by 'N') before sampling
|
343
|
+
sample.gsub!(/N/, '')
|
344
|
+
meaningful_samples = sample.split(/^>.+$/).map { |line| line.gsub(/^\n+$/, '') }.delete_if(&:empty?)
|
345
|
+
|
346
|
+
if meaningful_samples.empty?
|
347
|
+
offset += GUESS_SAMPLE_SIZE
|
348
|
+
sample_sequences(file, offset)
|
349
|
+
else
|
350
|
+
meaningful_samples
|
351
|
+
end
|
338
352
|
end
|
339
353
|
end
|
340
354
|
end
|
@@ -6,12 +6,6 @@ module SequenceServer
|
|
6
6
|
# Report is a generic superclass. Programs, like BLAST, must implement their
|
7
7
|
# own report subclass.
|
8
8
|
class Report
|
9
|
-
class << self
|
10
|
-
def generate(job)
|
11
|
-
BLAST::Report.new(job).generate
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
9
|
# Provide access to global `config` & `logger` services to the report
|
16
10
|
# objects.
|
17
11
|
extend Forwardable
|