genevalidatorapp 1.4.13 → 1.5.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +5 -1
  4. data/bin/genevalidatorapp +227 -73
  5. data/config.ru +1 -1
  6. data/genevalidatorapp.gemspec +7 -6
  7. data/lib/genevalidatorapp.rb +247 -0
  8. data/lib/{GeneValidatorApp → genevalidatorapp}/config.rb +7 -6
  9. data/lib/{GeneValidatorApp → genevalidatorapp}/database.rb +8 -13
  10. data/lib/genevalidatorapp/exceptions.rb +162 -0
  11. data/lib/{GeneValidatorApp → genevalidatorapp}/genevalidator.rb +52 -58
  12. data/lib/{GeneValidatorApp → genevalidatorapp}/logger.rb +0 -0
  13. data/lib/genevalidatorapp/routes.rb +81 -0
  14. data/lib/genevalidatorapp/server.rb +63 -0
  15. data/lib/{GeneValidatorApp → genevalidatorapp}/version.rb +1 -1
  16. data/public/{web_files → src}/css/bootstrap1.min.css +0 -0
  17. data/public/{web_files → src}/css/custom.css +8 -13
  18. data/public/{web_files → src}/css/custom.min.css +0 -0
  19. data/public/{web_files → src}/css/font-awesome.min.css +0 -0
  20. data/public/{web_files → src}/js/bionode-seq.min.js +0 -0
  21. data/public/{web_files → src}/js/bootstrap.min.js +0 -0
  22. data/public/{web_files → src}/js/d3.v3.min.js +0 -0
  23. data/public/{web_files → src}/js/genevalidator.js +44 -49
  24. data/public/{web_files → src}/js/jquery.cookie.min.js +0 -0
  25. data/public/{web_files → src}/js/jquery.min.js +0 -0
  26. data/public/{web_files → src}/js/jquery.tablesorter.min.js +0 -0
  27. data/public/{web_files → src}/js/jquery.validate.min.js +0 -0
  28. data/public/src/js/plots.js +814 -0
  29. data/public/web_files/css/GV_compiled_css.min.css +15 -0
  30. data/public/web_files/js/GV_compiled_js.min.js +34 -0
  31. data/spec/app_spec.rb +1 -1
  32. data/spec/database_spec.rb +2 -2
  33. data/views/index.slim +1 -1
  34. data/views/layout.slim +15 -24
  35. data/views/results.slim +54 -0
  36. metadata +39 -35
  37. data/lib/GeneValidatorApp.rb +0 -321
  38. data/public/web_files/css/bootstrap.min.css +0 -7
  39. data/public/web_files/js/genevalidator.min.js +0 -1
  40. data/public/web_files/js/plots.js +0 -744
  41. data/public/web_files/js/plots.min.js +0 -1
@@ -36,6 +36,7 @@ module GeneValidatorApp
36
36
  # Write config data to config file.
37
37
  def write_config_file
38
38
  return unless config_file
39
+
39
40
  File.open(config_file, 'w') do |f|
40
41
  f.puts(data.delete_if { |_, v| v.nil? }.to_yaml)
41
42
  end
@@ -71,10 +72,11 @@ module GeneValidatorApp
71
72
  # Default configuration data.
72
73
  def defaults
73
74
  {
74
- :num_threads => 1,
75
- :port => 4567,
76
- :host => '0.0.0.0',
77
- :web_dir => Dir.pwd
75
+ :num_threads => 1,
76
+ :port => 4567,
77
+ :host => '0.0.0.0',
78
+ :gv_app_dir => '~/.genevalidatorapp/',
79
+ :max_characters => 'undefined'
78
80
  }
79
81
  end
80
82
 
@@ -82,5 +84,4 @@ module GeneValidatorApp
82
84
  '~/.genevalidatorapp.conf'
83
85
  end
84
86
  end
85
-
86
- end
87
+ end
@@ -3,15 +3,7 @@ require 'digest/md5'
3
3
  require 'forwardable'
4
4
 
5
5
  module GeneValidatorApp
6
- # Captures a directory containing FASTA files and BLAST databases.
7
- #
8
- # It is important that formatted BLAST database files have the same dirname and
9
- # basename as the source FASTA for GeneValidatorApp to be able to tell formatted
10
- # FASTA from unformatted. And that FASTA files be formatted with `parse_seqids`
11
- # option of `makeblastdb` for sequence retrieval to work.
12
- #
13
- # GeneValidatorApp will always place BLAST database files alongside input FASTA,
14
- # and use `parse_seqids` option of `makeblastdb` to format databases.
6
+ # class on the BLAST databases
15
7
  class Database < Struct.new(:name, :title, :type)
16
8
  class << self
17
9
  extend Forwardable
@@ -77,16 +69,19 @@ module GeneValidatorApp
77
69
  # Recurisvely scan `database_dir` for blast databases.
78
70
  def scan_databases_dir
79
71
  database_dir = config[:database_dir]
80
- list = `blastdbcmd -recursive -list #{database_dir} -list_outfmt "%p %f %t" 2>&1`
72
+ cmd = "blastdbcmd -recursive -list #{database_dir} -list_outfmt" \
73
+ ' "%p::%f::%t"'
74
+ list = `#{cmd} 2>&1`
81
75
  list.each_line do |line|
82
- type, name, title = line.split(' ')
76
+ type, name, title = line.split('::', 3)
83
77
  next if multipart_database_name?(name)
84
- next unless type.downcase == 'protein' # to ensure we only have protein dbs
78
+ next unless type.downcase == 'protein'
85
79
  self << Database.new(name, title, type)
86
80
  end
87
81
  end
88
82
 
89
- # Returns true if the database name appears to be a multi-part database name.
83
+ # Returns true if the database name appears to be a multi-part database
84
+ # name.
90
85
  #
91
86
  # e.g.
92
87
  # /home/ben/pd.ben/sequenceserver/db/nr.00 => yes
@@ -0,0 +1,162 @@
1
+ # This file defines all possible exceptions that can be thrown by
2
+ # GeneValidatorApp on startup.
3
+ #
4
+ # Exceptions only ever inform another entity (downstream code or users) of an
5
+ # issue. Exceptions may or may not be recoverable.
6
+ #
7
+ # Error classes should be seen as: the error code (class name), human readable
8
+ # message (to_s method), and necessary attributes to act on the error.
9
+ #
10
+ # We define as many error classes as needed to be precise about the issue, thus
11
+ # making it easy for downstream code (bin/genevalidatorapp or config.ru) to act
12
+ # on them.
13
+
14
+ module GeneValidatorApp
15
+ # Error in config file.
16
+ class CONFIG_FILE_ERROR < StandardError
17
+ def initialize(ent, err)
18
+ @ent = ent
19
+ @err = err
20
+ end
21
+
22
+ attr_reader :ent, :err
23
+
24
+ def to_s
25
+ <<MSG
26
+ Error reading config file: #{ent}.
27
+ #{err}
28
+ MSG
29
+ end
30
+ end
31
+
32
+ ## ENOENT ##
33
+
34
+ # Name borrowed from standard Errno::ENOENT, this class serves as a template
35
+ # for defining errors that mean "expected to find <entity> at <path>, but
36
+ # didn't".
37
+ #
38
+ # ENOENT is raised if and only if an entity was set, either using CLI or
39
+ # config file. For instance, it's compulsory to set database_dir. But ENOENT
40
+ # is not raised if database_dir is not set. ENOENT is raised if database_dir
41
+ # was set, but does not exist.
42
+ class ENOENT < StandardError
43
+ def initialize(des, ent)
44
+ @des = des
45
+ @ent = ent
46
+ end
47
+
48
+ attr_reader :des, :ent
49
+
50
+ def to_s
51
+ "Could not find #{des}: #{ent}"
52
+ end
53
+ end
54
+
55
+ # Raised if bin dir set, but does not exist.
56
+ class BIN_DIR_NOT_FOUND < ENOENT
57
+ def initialize(ent)
58
+ super 'bin dir', ent
59
+ end
60
+ end
61
+
62
+ # Raised if database dir set, but does not exist.
63
+ class DATABASE_DIR_NOT_FOUND < ENOENT
64
+ def initialize(ent)
65
+ super 'database dir', ent
66
+ end
67
+ end
68
+
69
+ # Raised if extension file set, but does not exist.
70
+ class EXTENSION_FILE_NOT_FOUND < ENOENT
71
+ def initialize(ent)
72
+ super 'extension file', ent
73
+ end
74
+ end
75
+
76
+ ## NUM THREADS ##
77
+
78
+ # Raised if num_threads set by the user is incorrect.
79
+ class NUM_THREADS_INCORRECT < StandardError
80
+ def to_s
81
+ 'Number of threads should be a number greater than or equal to 1.'
82
+ end
83
+ end
84
+
85
+ ## BLAST NOT INSTALLED OR NOT COMPATIBLE ##
86
+
87
+ # Raised if SequenceServer could not locate NCBI BLAST+ installation on
88
+ # user's system.
89
+ class BLAST_NOT_INSTALLED < StandardError
90
+ def to_s
91
+ 'Could not locate BLAST+ binaries.'
92
+ end
93
+ end
94
+
95
+ # Raised if SequenceServer could not successfully execute 'blastp -version'
96
+ # on user's system (see #141).
97
+ class BLAST_NOT_EXECUTABLE < StandardError
98
+ def to_s
99
+ 'Error executing BLAST+ binaries.'
100
+ end
101
+ end
102
+
103
+ # Raised if SequenceServer determined NCBI BLAST+ present on the user's
104
+ # system but not meeting SequenceServer's minimum version requirement.
105
+ class BLAST_NOT_COMPATIBLE < StandardError
106
+ def initialize(version)
107
+ @version = version
108
+ end
109
+
110
+ attr_reader :version
111
+
112
+ def to_s
113
+ <<MSG
114
+ Your BLAST+ version #{version} is outdated.
115
+ SequenceServer needs NCBI BLAST+ version #{MINIMUM_BLAST_VERSION} or higher.
116
+ MSG
117
+ end
118
+ end
119
+
120
+ ## BLAST+ DATABASE RELATED ##
121
+
122
+ # Raised if 'database_dir' not set.
123
+ class DATABASE_DIR_NOT_SET < StandardError
124
+ def to_s
125
+ 'Database dir not set.'
126
+ end
127
+ end
128
+
129
+ # Raised if not even one BLAST+ database was found in database_dir.
130
+ class NO_BLAST_DATABASE_FOUND < StandardError
131
+ def initialize(database_dir)
132
+ @database_dir = database_dir
133
+ end
134
+
135
+ attr_reader :database_dir
136
+
137
+ def to_s
138
+ "Could not find BLAST+ databases in: #{database_dir}."
139
+ end
140
+ end
141
+
142
+ # Raised if there was an error determining BLAST+ databases in database_dir.
143
+ class BLAST_DATABASE_ERROR < StandardError
144
+ def initialize(cmd, out)
145
+ @cmd = cmd
146
+ @out = out
147
+ end
148
+
149
+ attr_reader :cmd, :out
150
+
151
+ def to_s
152
+ <<MSG
153
+ Error obtaining BLAST databases.
154
+ Tried: #{cmd}
155
+ Error:
156
+ #{out.strip}
157
+
158
+ Please could you report this to 'https://groups.google.com/forum/#!forum/sequenceserver'?
159
+ MSG
160
+ end
161
+ end
162
+ end
@@ -2,6 +2,7 @@ require 'forwardable'
2
2
  require 'bio'
3
3
  require 'fileutils'
4
4
  require 'genevalidator'
5
+ require 'json'
5
6
 
6
7
  module GeneValidatorApp
7
8
  # Module that runs GeneValidator
@@ -24,7 +25,7 @@ module GeneValidatorApp
24
25
  class << self
25
26
  extend Forwardable
26
27
 
27
- def_delegators GeneValidatorApp, :config, :logger
28
+ def_delegators GeneValidatorApp, :config, :logger, :public_dir
28
29
 
29
30
  attr_reader :gv_dir, :tmp_gv_dir, :input_file, :xml_file, :raw_seq,
30
31
  :unique_id, :params
@@ -33,7 +34,7 @@ module GeneValidatorApp
33
34
  def init(url, params)
34
35
  create_unique_id
35
36
  create_subdir_in_main_tmpdir
36
- create_soft_link_from_tmpdir_to_GV_dir
37
+ create_soft_link_from_tmpdir_to_gv_dir
37
38
  @params = params
38
39
  validate_params
39
40
  obtain_db_path
@@ -45,7 +46,9 @@ module GeneValidatorApp
45
46
  def run
46
47
  write_seq_to_file
47
48
  run_genevalidator
48
- (@params[:result_link]) ? @url : produce_table_html
49
+ copy_json_folder
50
+ (@params[:result_link]) ? @url : output_json_file_path
51
+ parse_output_json
49
52
  end
50
53
 
51
54
  private
@@ -53,7 +56,7 @@ module GeneValidatorApp
53
56
  # Creates a unique run ID (based on time),
54
57
  def create_unique_id
55
58
  @unique_id = Time.new.strftime('%Y-%m-%d_%H-%M-%S_%L-%N')
56
- @gv_tmpdir = GeneValidatorApp.tempdir + unique_id
59
+ @gv_tmpdir = File.join(GeneValidatorApp.temp_dir, unique_id)
57
60
  ensure_unique_id
58
61
  end
59
62
 
@@ -62,7 +65,7 @@ module GeneValidatorApp
62
65
  def ensure_unique_id
63
66
  while File.exist?(@gv_tmpdir)
64
67
  @unique_id = create_unique_id
65
- @gv_tmpdir = GeneValidatorApp.tempdir + @unique_id
68
+ @gv_tmpdir = File.join(GeneValidatorApp.temp_dir, unique_id)
66
69
  end
67
70
  logger.debug("Unique ID = #{@unique_id}")
68
71
  end
@@ -74,8 +77,9 @@ module GeneValidatorApp
74
77
  end
75
78
 
76
79
  # Create the Tmp Dir and the create a soft link to it.
77
- def create_soft_link_from_tmpdir_to_GV_dir
78
- @gv_dir = GeneValidatorApp.public_dir + 'GeneValidator' + @unique_id
80
+ def create_soft_link_from_tmpdir_to_gv_dir
81
+ @gv_dir = File.join(GeneValidatorApp.public_dir, 'GeneValidator',
82
+ @unique_id)
79
83
  logger.debug("Local GV dir = #{@gv_dir}")
80
84
  FileUtils.ln_s "#{@gv_tmpdir}", "#{@gv_dir}"
81
85
  end
@@ -84,6 +88,7 @@ module GeneValidatorApp
84
88
  # Only important if POST request is sent via API - Web APP also validates
85
89
  # all params via Javascript.
86
90
  def validate_params
91
+ logger.debug("Input Paramaters: #{@params}")
87
92
  check_seq_param_present
88
93
  check_seq_length
89
94
  check_validations_param_present
@@ -92,23 +97,20 @@ module GeneValidatorApp
92
97
 
93
98
  # Simply asserts whether that the seq param is present
94
99
  def check_seq_param_present
95
- unless @params[:seq]
96
- fail ArgumentError, 'No input sequence provided.'
97
- end
100
+ return if @params[:seq]
101
+ fail ArgumentError, 'No input sequence provided.'
98
102
  end
99
103
 
100
104
  def check_seq_length
101
- return unless config[:max_characters]
102
- unless @params[:seq].length < config[:max_characters]
103
- fail ArgumentError, 'The input sequence is too long.'
104
- end
105
+ return unless config[:max_characters] != 'undefined'
106
+ return if @params[:seq].length < config[:max_characters]
107
+ fail ArgumentError, 'The input sequence is too long.'
105
108
  end
106
109
 
107
110
  # Asserts whether the validations param are specified
108
111
  def check_validations_param_present
109
- unless @params[:validations]
110
- fail ArgumentError, 'No validations specified'
111
- end
112
+ return if @params[:validations]
113
+ fail ArgumentError, 'No validations specified'
112
114
  end
113
115
 
114
116
  # Asserts whether the database parameter is present
@@ -124,7 +126,7 @@ module GeneValidatorApp
124
126
 
125
127
  # Writes the input sequences to a file with the sub_dir in the temp_dir
126
128
  def write_seq_to_file
127
- @input_fasta_file = @gv_tmpdir + 'input_file.fa'
129
+ @input_fasta_file = File.join(@gv_tmpdir, 'input_file.fa')
128
130
  logger.debug("Writing input seqs to: '#{@input_fasta_file}'")
129
131
  ensure_unix_line_ending
130
132
  ensure_fasta_valid
@@ -145,7 +147,8 @@ module GeneValidatorApp
145
147
  unique_queries = {}
146
148
  sequence = @params[:seq].lstrip
147
149
  if sequence[0] != '>'
148
- sequence.insert(0, ">Submitted:#{Time.now.strftime('%H:%M-%B_%d_%Y')}\n")
150
+ sequence.insert(0, '>Submitted:'\
151
+ "#{Time.now.strftime('%H:%M-%B_%d_%Y')}\n")
149
152
  end
150
153
  sequence.gsub!(/^\>(\S+)/) do |s|
151
154
  if unique_queries.key?(s)
@@ -163,8 +166,7 @@ module GeneValidatorApp
163
166
  # empty
164
167
  def assert_input_file_present
165
168
  unless File.exist?(@input_fasta_file) || File.zero?(@input_fasta_file)
166
- fail RuntimeError, 'GeneValidatorApp was unable to create the input' \
167
- ' file.'
169
+ fail 'GeneValidatorApp was unable to create the input file.'
168
170
  end
169
171
  end
170
172
 
@@ -180,62 +182,54 @@ module GeneValidatorApp
180
182
 
181
183
  # Runs GeneValidator
182
184
  def run_genevalidator
183
- opts = set_up_gv_opts
184
- logger.debug("Running GeneValidator with options: #{opts}")
185
185
  create_gv_log_file
186
186
  run_gv
187
- assert_table_output_file_produced
187
+ assert_json_output_file_produced
188
188
  rescue SystemExit
189
- raise RuntimeError, 'GeneValidator failed to run properly'
189
+ raise 'GeneValidator failed to run properly'
190
190
  end
191
191
 
192
192
  def run_gv
193
- original_stdout = $stdout.clone unless logger.debug?
194
- $stdout.reopen(@gv_log_file, 'w') unless logger.debug?
195
- (GeneValidator::Validation.new(opts, 1, true, true)).run
196
- $stdout = original_stdout unless logger.debug?
197
- end
198
-
199
- def set_up_gv_opts
200
- {
201
- validations: @params[:validations],
202
- db: @db,
203
- num_threads: config[:num_threads],
204
- fast: true,
205
- input_fasta_file: @input_fasta_file.to_s
206
- }
193
+ cmd = "genevalidator -v '#{@params[:validations].join(', ')}'" \
194
+ " -d #{@db} -n #{config[:num_threads]} #{@input_fasta_file}"
195
+ logger.debug("GV command: $ #{cmd}")
196
+ log_file = (logger.debug?) ? '' : "> #{@gv_log_file} 2>&1"
197
+ `#{cmd} #{log_file}`
207
198
  end
208
199
 
209
200
  def create_gv_log_file
210
- @gv_log_file = (@gv_tmpdir + 'log_file.txt').to_s
201
+ @gv_log_file = File.join(@gv_tmpdir, 'log_file.txt')
211
202
  logger.debug("Log file: #{@gv_log_file}")
212
203
  end
213
204
 
214
205
  # Assets whether the results file is produced by GeneValidator.
215
- def assert_table_output_file_produced
216
- @table_file = @gv_dir + 'input_file.fa.html/files/table.html'
217
- unless File.exist?(@table_file)
218
- fail RuntimeError, 'GeneValidator did not produce the required' \
219
- ' output file.'
220
- end
221
- end
222
-
223
- # Reads the GV output table file.
224
- # Updates links to the plots with relative links to plot jsons.
225
- def produce_table_html
226
- orig_plots_dir = 'files/json/input_file.fa_'
227
- local_plots_dir = Pathname.new('GeneValidator') + @unique_id +
228
- 'input_file.fa.html/files/json/input_file.fa_'
229
- full_html = IO.binread(@table_file)
230
- full_html.gsub(/#{orig_plots_dir}/, local_plots_dir.to_s).gsub(
231
- '#Place_external_results_link_here', @url)
206
+ def assert_json_output_file_produced
207
+ @json_file = File.join(@gv_dir, 'input_file.fa.json')
208
+ return if File.exist?(@json_file)
209
+ fail 'GeneValidator did not produce the required output file.'
232
210
  end
233
211
 
234
212
  # Reuturns the URL of the results page.
235
213
  def produce_result_url_link(url)
236
- url.gsub(/input/, '').gsub(/\/*$/, '') +
214
+ url.gsub(/input/, '').gsub(%r{/*$}, '') +
237
215
  "/GeneValidator/#{@unique_id}/input_file.fa.html/results.html"
238
216
  end
217
+
218
+ def parse_output_json
219
+ json_contents = File.read(output_json_file_path)
220
+ JSON.parse(json_contents)
221
+ end
222
+
223
+ def output_json_file_path
224
+ "#{@input_fasta_file}.json"
225
+ end
226
+
227
+ def copy_json_folder
228
+ json_dir = File.join("#{@input_fasta_file}.html", 'files/json')
229
+ web_dir_json = File.join(public_dir, 'web_files/json')
230
+ logger.debug("Moving JSON files from #{json_dir} to #{web_dir_json}")
231
+ FileUtils.cp_r(json_dir, web_dir_json)
232
+ end
239
233
  end
240
234
  end
241
235
  end