sequenceserver 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequenceserver +2 -2
  3. data/lib/sequenceserver/api_errors.rb +1 -1
  4. data/lib/sequenceserver/blast/job.rb +20 -3
  5. data/lib/sequenceserver/blast/report.rb +74 -86
  6. data/lib/sequenceserver/blast/tasks.rb +38 -0
  7. data/lib/sequenceserver/config.rb +54 -20
  8. data/lib/sequenceserver/makeblastdb.rb +16 -2
  9. data/lib/sequenceserver/report.rb +0 -6
  10. data/lib/sequenceserver/routes.rb +32 -21
  11. data/lib/sequenceserver/version.rb +1 -1
  12. data/lib/sequenceserver.rb +1 -1
  13. data/public/css/app.css +121 -0
  14. data/public/css/app.min.css +1 -0
  15. data/public/css/sequenceserver.css +0 -148
  16. data/public/css/sequenceserver.min.css +3 -3
  17. data/public/js/circos.js +2 -2
  18. data/public/js/collapse_preferences.js +37 -0
  19. data/public/js/databases.js +65 -37
  20. data/public/js/databases_tree.js +2 -1
  21. data/public/js/dnd.js +37 -50
  22. data/public/js/form.js +78 -50
  23. data/public/js/grapher.js +23 -37
  24. data/public/js/hits_overview.js +2 -2
  25. data/public/js/kablammo.js +2 -2
  26. data/public/js/length_distribution.js +3 -3
  27. data/public/js/null_plugins/grapher/histogram.js +25 -0
  28. data/public/js/null_plugins/options.js +3 -0
  29. data/public/js/null_plugins/query_stats.js +11 -0
  30. data/public/js/null_plugins/report_plugins.js +6 -1
  31. data/public/js/null_plugins/search_header_plugin.js +4 -0
  32. data/public/js/options.js +161 -56
  33. data/public/js/query.js +85 -59
  34. data/public/js/report.js +1 -1
  35. data/public/js/search.js +2 -0
  36. data/public/js/search_button.js +67 -56
  37. data/public/js/sidebar.js +1 -1
  38. data/public/js/tests/database.spec.js +5 -5
  39. data/public/js/tests/{advanced_parameters.spec.js → form.spec.js} +35 -1
  40. data/public/js/tests/mock_data/databases.json +5 -5
  41. data/public/js/tests/mocks/circos.js +6 -0
  42. data/public/js/tests/report.spec.js +4 -3
  43. data/public/js/tests/search_query.spec.js +5 -6
  44. data/public/sequenceserver-report.min.js +45 -23
  45. data/public/sequenceserver-search.min.js +57 -13
  46. data/public/sequenceserver_logo.webp +0 -0
  47. data/views/blastn_options.erb +66 -66
  48. data/views/blastp_options.erb +59 -59
  49. data/views/blastx_options.erb +68 -68
  50. data/views/layout.erb +60 -3
  51. data/views/search.erb +33 -38
  52. data/views/search_layout.erb +152 -0
  53. data/views/tblastn_options.erb +57 -57
  54. data/views/tblastx_options.erb +64 -64
  55. metadata +31 -22
  56. data/lib/sequenceserver/makeblastdb-modified-with-cache.rb +0 -345
  57. data/public/SequenceServer_logo.png +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05745f148bb0f76da55ed9093dd77152b218f5d20656294fc879802128c24bdf
4
- data.tar.gz: 76944e8d19192955b7c0f44c59466da4ff7c3db45a03190d62685abb0e5f3ccf
3
+ metadata.gz: 274ace0d8b4683a9bcbf6b54c83e7c28e5bc1f89b0211676f91c4d3241699148
4
+ data.tar.gz: d61d26277f381827f764e5d7bfd1cb61476e1e5da9dbeec9f4d9ccd4a383385c
5
5
  SHA512:
6
- metadata.gz: ea823aec0b9328ebeba756547b7d9f523105a6d87230ac18e27e106e86e2e44ec9a21c3a17f01b325bc12682c35b982f3606d73689d708a8611509695a973eba
7
- data.tar.gz: a0702e4ee63201fa75a366d5aa0cd7a6459d3e7e034744977d18139937008d60c68baffdb0ae0888ecc7e021b0072c1c94e2a429a0bdcb995e8738b2033c2604
6
+ metadata.gz: e83fe0932a797dcb8cb6f353b1256dd7e387031e078b3d3a3740662c4bd805a4f19554bf84c9bd79f4252df2decc2c6b58e911864e716944f7177014ceb4b1b4
7
+ data.tar.gz: fb38408e8e8da2f4f7574de8e2a0c0547072fa53b6124a68a89b2771fec533c98c01986c4928ecc2007c3f32308ba6c4bd909703d97c216cdd6d38a11fff6c97
data/bin/sequenceserver CHANGED
@@ -398,8 +398,8 @@ begin
398
398
  end
399
399
 
400
400
  if import?
401
- xml_file = fetch_option(:import).value
402
- params = {:xml => xml_file}
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,5 +1,5 @@
1
1
  module SequenceServer
2
- Error = Class.new(StandardError)
2
+ Error = Class.new(Sinatra::Error)
3
3
 
4
4
  # DatabaseUnreachableError is raised when the serialised Job object is
5
5
  # refering to a database that is not present in the current filesystem.
@@ -21,7 +21,8 @@ module SequenceServer
21
21
  else
22
22
  validate params
23
23
  super do
24
- @method = params[: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
- attr_reader :databases, :databases_ncharacters_total, :method, :num_threads, :options, :qfile, :query_length
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
- # Generate report.
62
- def generate
63
- return self if @_generated
64
-
65
- job.raise!
66
- xml_ir = nil
67
- tsv_ir = nil
68
- if job.imported_xml_file
69
- xml_ir = parse_xml File.read(job.imported_xml_file)
70
- tsv_ir = Hash.new do |h1, k1|
71
- h1[k1] = Hash.new do |h2, k2|
72
- h2[k2] = ['', '', []]
73
- end
74
- end
75
- else
76
- xml_ir = parse_xml(xml_formatter.read_file)
77
- tsv_ir = parse_tsv(tsv_formatter.read_file)
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
- extract_program_info xml_ir
80
- extract_db_info xml_ir
81
- extract_params xml_ir
82
- extract_stats xml_ir
83
- extract_queries xml_ir, tsv_ir
68
+ end
69
+
70
+ def dbtype
71
+ @dbtype ||= querydb&.first&.type || dbtype_from_program
72
+ end
84
73
 
85
- @_generated = true
74
+ def params
75
+ @params ||= extract_params
76
+ end
86
77
 
87
- self
78
+ def stats
79
+ @stats ||= extract_stats
88
80
  end
89
81
 
90
- def done?
91
- return true if job.imported_xml_file
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
- File.exist?(xml_formatter.filepath) && File.exist?(tsv_formatter.filepath)
87
+ query
88
+ end
94
89
  end
95
90
 
96
91
  private
97
92
 
98
- def xml_formatter
99
- @xml_formatter ||= Formatter.run(job, 'xml')
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 tsv_formatter
103
- @tsv_formatter ||= Formatter.run(job, 'custom_tsv')
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
- # Make program name and program name + version available via `program`
107
- # and `program_version` attributes.
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
- # Get database information (title and type) from job yaml or from XML.
114
- # Sets `querydb` and `dbtype` attributes.
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(ir)
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
- *ir[7].first.map { |k, v| [k.gsub('Parameters_', ''), v] }.flatten
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(ir)
154
- stats = ir[8].first[5][0]
155
- @stats = {
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 extract_hits(xml_ir, tsv_ir, query)
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.each do |n|
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
- extract_hsps(n[5], tsv_ir[n[1]][2], hit)
196
- query.hits << hit
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
- # Create HSP objects for the given hit from the given ir.
201
- def extract_hsps(xml_ir, tsv_ir, hit)
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
- hsp = HSP.new(hit, *n)
205
- hit.hsps << hsp
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.info 'You are using old configuration syntax. ' \
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
- # Old config files may have an options hash with array values. We turn the
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: ['-task blastn', '-evalue 1e-5']
102
+ default: {
103
+ description: nil,
104
+ attributes: ['-task blastn', '-evalue 1e-5']
105
+ }
116
106
  },
117
107
  blastp: {
118
- default: ['-evalue 1e-5']
108
+ default: {
109
+ description: nil,
110
+ attributes: ['-evalue 1e-5']
111
+ }
119
112
  },
120
113
  blastx: {
121
- default: ['-evalue 1e-5']
114
+ default: {
115
+ description: nil,
116
+ attributes: ['-evalue 1e-5']
117
+ }
122
118
  },
123
119
  tblastx: {
124
- default: ['-evalue 1e-5']
120
+ default: {
121
+ description: nil,
122
+ attributes: ['-evalue 1e-5']
123
+ }
125
124
  },
126
125
  tblastn: {
127
- default: ['-evalue 1e-5']
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, 1_048_576).split(/^>.+$/).delete_if(&:empty?)
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
@@ -5,6 +5,7 @@ require 'rest-client'
5
5
 
6
6
  require 'sequenceserver/job'
7
7
  require 'sequenceserver/blast'
8
+ require 'sequenceserver/blast/tasks'
8
9
  require 'sequenceserver/report'
9
10
  require 'sequenceserver/database'
10
11
  require 'sequenceserver/sequence'
@@ -32,6 +33,8 @@ module SequenceServer
32
33
  # Override in config.ru if the instance is served under a subpath
33
34
  # e.g. for example.org/our-sequenceserver set to '/our-sequenceserver'
34
35
  set :root_path_prefix, ''
36
+
37
+ set :search_layout, :'search_layout'
35
38
  end
36
39
 
37
40
  # See
@@ -74,7 +77,7 @@ module SequenceServer
74
77
 
75
78
  # Returns base HTML. Rest happens client-side: rendering the search form.
76
79
  get '/' do
77
- erb :search, layout: true
80
+ erb :search, layout: settings.search_layout
78
81
  end
79
82
 
80
83
  # Returns data that is used to render the search form client side. These
@@ -83,7 +86,8 @@ module SequenceServer
83
86
  searchdata = {
84
87
  query: Database.retrieve(params[:query]),
85
88
  database: Database.all,
86
- options: SequenceServer.config[:options]
89
+ options: SequenceServer.config[:options],
90
+ blastTaskMap: SequenceServer::BLAST::Tasks.to_h
87
91
  }
88
92
 
89
93
  searchdata.update(tree: Database.tree) if SequenceServer.config[:databases_widget] == 'tree'
@@ -100,7 +104,7 @@ module SequenceServer
100
104
  post '/' do
101
105
  if params[:input_sequence]
102
106
  @input_sequence = params[:input_sequence]
103
- erb :search, layout: true
107
+ erb :search, layout: settings.search_layout
104
108
  else
105
109
  job = Job.create(params)
106
110
  redirect to("/#{job.id}")
@@ -114,24 +118,11 @@ module SequenceServer
114
118
  halt 404, { error: 'Job not found' }.to_json if job.nil?
115
119
  halt 202 unless job.done?
116
120
 
117
- report = Report.generate(job)
121
+ report = BLAST::Report.new(job)
118
122
  halt 202 unless report.done?
119
123
 
120
- display_large_result_warning =
121
- SequenceServer.config[:large_result_warning_threshold].to_i.positive? &&
122
- params[:bypass_file_size_warning] != 'true' &&
123
- report.xml_file_size > SequenceServer.config[:large_result_warning_threshold]
124
-
125
- if display_large_result_warning
126
- halt 200,
127
- {
128
- user_warning: 'LARGE_RESULT',
129
- download_links: [
130
- { name: 'Standard Tabular Report', url: "download/#{jid}.std_tsv" },
131
- { name: 'Full Tabular Report', url: "/download/#{jid}.full_tsv" },
132
- { name: 'Results in XML', url: "/download/#{jid}.xml" }
133
- ]
134
- }.to_json
124
+ if display_large_result_warning?(report.xml_file_size)
125
+ halt 200, large_result_warning_payload(jid).to_json
135
126
  end
136
127
 
137
128
  report.to_json
@@ -307,12 +298,32 @@ module SequenceServer
307
298
  # the user hits the back button. Thus we do not test for empty string.
308
299
  method = job.method.to_sym
309
300
  if job.advanced && job.advanced !=
310
- searchdata[:options][method][:default].join(' ')
301
+ searchdata.dig(:options, method, :default, :attributes).to_a.join(' ')
311
302
  searchdata[:options] = searchdata[:options].deep_copy
312
- searchdata[:options][method]['last search'] = [job.advanced]
303
+ searchdata[:options][method]['last search'] = { attributes: [job.advanced] }
313
304
  end
314
305
  end
315
306
 
307
+ def display_large_result_warning?(xml_file_size)
308
+ threshold = SequenceServer.config[:large_result_warning_threshold].to_i
309
+ return false unless threshold.positive?
310
+
311
+ return false if params[:bypass_file_size_warning] == 'true'
312
+
313
+ xml_file_size > threshold
314
+ end
315
+
316
+ def large_result_warning_payload(jid)
317
+ {
318
+ user_warning: 'LARGE_RESULT',
319
+ download_links: [
320
+ { name: 'Standard Tabular Report', url: "download/#{jid}.std_tsv" },
321
+ { name: 'Full Tabular Report', url: "/download/#{jid}.full_tsv" },
322
+ { name: 'Results in XML', url: "/download/#{jid}.xml" }
323
+ ]
324
+ }
325
+ end
326
+
316
327
  helpers do
317
328
  def root_path_prefix
318
329
  settings.root_path_prefix.to_s
@@ -1,4 +1,4 @@
1
1
  # Define version number.
2
2
  module SequenceServer
3
- VERSION = '3.0.1'.freeze
3
+ VERSION = '3.1.0'.freeze
4
4
  end
@@ -8,7 +8,7 @@ Encoding.default_internal = 'UTF-8'
8
8
  # Top level module / namespace.
9
9
  module SequenceServer
10
10
  # The default version of BLAST that will be downloaded and configured for use.
11
- BLAST_VERSION = '2.12.0+'.freeze
11
+ BLAST_VERSION = '2.15.0+'.freeze
12
12
 
13
13
  # Default location of configuration file.
14
14
  DEFAULT_CONFIG_FILE = '~/.sequenceserver.conf'.freeze