neurohmmerapp 0.0.1

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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +81 -0
  5. data/LICENSE.txt +661 -0
  6. data/README.md +96 -0
  7. data/Rakefile +21 -0
  8. data/bin/neurohmmerapp +166 -0
  9. data/config.ru +3 -0
  10. data/lib/neurohmmerapp/config.rb +87 -0
  11. data/lib/neurohmmerapp/exceptions.rb +77 -0
  12. data/lib/neurohmmerapp/logger.rb +24 -0
  13. data/lib/neurohmmerapp/neurohmmer.rb +158 -0
  14. data/lib/neurohmmerapp/routes.rb +79 -0
  15. data/lib/neurohmmerapp/server.rb +63 -0
  16. data/lib/neurohmmerapp/version.rb +3 -0
  17. data/lib/neurohmmerapp.rb +207 -0
  18. data/neurohmmerapp.gemspec +50 -0
  19. data/public/NeuroHmmer/loading.html +12 -0
  20. data/public/src/css/bootstrap1.min.css +7 -0
  21. data/public/src/css/custom.css +189 -0
  22. data/public/src/css/custom.min.css +1 -0
  23. data/public/src/css/font-awesome.min.css +4 -0
  24. data/public/src/js/bionode-seq.min.js +1 -0
  25. data/public/src/js/bootstrap.min.js +6 -0
  26. data/public/src/js/jquery.cookie.min.js +1 -0
  27. data/public/src/js/jquery.min.js +4 -0
  28. data/public/src/js/jquery.validate.min.js +4 -0
  29. data/public/src/js/neurohmmer.js +228 -0
  30. data/public/src/js/neurohmmer.min.js +1 -0
  31. data/public/web_files/css/nh_compiled_css.min.css +14 -0
  32. data/public/web_files/fonts/FontAwesome.otf +0 -0
  33. data/public/web_files/fonts/fontawesome-webfont.eot +0 -0
  34. data/public/web_files/fonts/fontawesome-webfont.svg +504 -0
  35. data/public/web_files/fonts/fontawesome-webfont.ttf +0 -0
  36. data/public/web_files/fonts/fontawesome-webfont.woff +0 -0
  37. data/public/web_files/js/nh_compiled_js.min.js +20 -0
  38. data/spec/empty_config.yml +0 -0
  39. data/spec/route_spec.rb +72 -0
  40. data/views/500.slim +5 -0
  41. data/views/index.slim +38 -0
  42. data/views/layout.slim +74 -0
  43. data/views/results.slim +9 -0
  44. metadata +281 -0
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # NeuroHmmerApp
2
+ [![Build Status](https://travis-ci.org/wurmlab/neurohmmerapp.svg?branch=master)](https://travis-ci.org/wurmlab/neurohmmerapp)
3
+ [![Gem Version](https://badge.fury.io/rb/neurohmmerapp.svg)](http://badge.fury.io/rb/neurohmmerapp)
4
+ [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/wurmlab/neurohmmerapp/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/wurmlab/neurohmmerapp/?branch=master)
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+ ## Introduction
13
+
14
+ This is a online web application for [Neurohmmer](https://github.com/wurmlab/neurohmmer). This app is currently hosted at: ...
15
+
16
+
17
+ If you use Neurohmmer in your work, please cite us as follows:
18
+ > "Moghul MI, Elphick M & Wurm Y (<em>in prep.</em>) NeuroHmmer: identify Neuropeptide Precursors"
19
+
20
+
21
+
22
+
23
+
24
+
25
+ -
26
+ ## Installation
27
+ ### Installation Requirements
28
+ * Ruby (>= 2.0.0)
29
+ * HMMer (>=3.0)
30
+
31
+
32
+ ### Installation
33
+ Simply run the following command in the terminal.
34
+
35
+ ```bash
36
+ gem install neurohmmerapp
37
+ ```
38
+
39
+ If that doesn't work, try `sudo gem install neurohmmerapp` instead.
40
+
41
+ ##### Running From Source (Not Recommended)
42
+ It is also possible to run from source. However, this is not recommended.
43
+
44
+ ```bash
45
+ # Clone the repository.
46
+ git clone https://github.com/wurmlab/neurohmmerapp.git
47
+
48
+ # Move into GeneValidatorApp source directory.
49
+ cd neurohmmerapp
50
+
51
+ # Install bundler
52
+ gem install bundler
53
+
54
+ # Use bundler to install dependencies
55
+ bundle install
56
+
57
+ # Optional: run tests and build the gem from source
58
+ bundle exec rake
59
+
60
+ # Run NeuroHmmer.
61
+ bundle exec neurohmmerapp -h
62
+ # note that `bundle exec` executes NeuroHmmerApp in the context of the bundle
63
+
64
+ # Alternativaly, install NeuroHmmerApp as a gem
65
+ bundle exec rake install
66
+ neurohmmerapp -h
67
+ ```
68
+
69
+
70
+
71
+
72
+ ## Launch NeuroHmmer
73
+
74
+ To configure and launch NeuroHmmerApp, run the following from a command line.
75
+
76
+ ```bash
77
+ neurohmmerapp
78
+ ```
79
+
80
+ NeuroHmmerApp will automatically guide you through an interactive setup process to help locate BLAST+ binaries and ask for the location of BLAST+ databases.
81
+
82
+ That's it! Open http://localhost:4567/ and start using NeuroHmmer!
83
+
84
+
85
+
86
+
87
+
88
+
89
+ ## Advanced Usage
90
+
91
+ See `$ neurohmmerapp -h` for more information on all the options available when running NeuroHmmerApp.
92
+
93
+
94
+ <hr>
95
+
96
+ This program was developed at [Wurm Lab](https://wurmlab.github.io), [QMUL](http://sbcs.qmul.ac.uk).
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core'
3
+ require 'rspec/core/rake_task'
4
+
5
+ task default: [:build]
6
+
7
+ desc 'Builds and installs'
8
+ task install: [:build] do
9
+ require_relative 'lib/genevalidatorapp/version'
10
+ sh "gem install ./genevalidatorapp-#{GeneValidatorApp::VERSION}.gem"
11
+ end
12
+
13
+ desc 'Runs tests and builds gem (default)'
14
+ task build: [:test] do
15
+ sh 'gem build genevalidatorapp.gemspec'
16
+ end
17
+
18
+ task test: :spec
19
+ RSpec::Core::RakeTask.new(:spec) do |spec|
20
+ spec.pattern = FileList['spec/**/*_spec.rb']
21
+ end
data/bin/neurohmmerapp ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env ruby
2
+ require 'readline'
3
+ require 'English'
4
+ require 'slop'
5
+
6
+ ENV['RACK_ENV'] ||= 'production'
7
+
8
+ # display name for tools like `ps`
9
+ $PROGRAM_NAME = 'neurohmmerapp'
10
+
11
+ begin
12
+ Slop.parse!(strict: true, help: true) do
13
+ banner <<BANNER
14
+ SUMMARY:
15
+ NeuroHmmer - Identify Neuropeptide Precursors
16
+
17
+ USAGE:
18
+ $ neurohmmerapp [options]
19
+
20
+ Examples:
21
+ # Launch NeuroHmmerApp with the given config file
22
+ $ neurohmmerapp --config ~/.neurohmmerapp.conf
23
+
24
+ # Launch NeuroHmmerApp with 8 threads at port 8888
25
+ $ neurohmmerapp --num_threads 8 --port 8888
26
+
27
+ # Create a config file with the other arguments
28
+ $ neurohmmerapp -s -d ~/database_dir
29
+
30
+ BANNER
31
+ on 'c', 'config_file=',
32
+ 'Use the given configuration file',
33
+ argument: true
34
+
35
+ on 'g', 'public_dir=',
36
+ 'dhe public directory that is served to the web application.',
37
+ argument: true
38
+
39
+ on 'b', 'bin=',
40
+ 'Load HMMER 3.0 binaries from this directory',
41
+ argument: true,
42
+ as: Array
43
+
44
+ on 'n', 'num_threads=',
45
+ 'Number of threads to use to run a BLAST search',
46
+ argument: true
47
+
48
+ on 'H', 'host=',
49
+ 'Host to run NeuroHmmerApp on',
50
+ argument: true
51
+
52
+ on 'p', 'port=',
53
+ 'Port to run NeuroHmmerApp on',
54
+ argument: true
55
+
56
+ on 's', 'set',
57
+ 'Set configuration value in default or given config file'
58
+
59
+ on 'D', 'devel',
60
+ 'Start NeuroHmmerApp in development mode'
61
+
62
+ on '-v', '--version',
63
+ 'Print version number of NeuroHmmerApp that will be loaded'
64
+
65
+ on '-h', '--help',
66
+ 'Display this help message'
67
+
68
+ clean_opts = lambda do |hash|
69
+ hash.delete_if { |k, v| k == :set || v.nil? }
70
+ hash
71
+ end
72
+
73
+ run do
74
+ if version?
75
+ require 'neurohmmerapp/version'
76
+ puts NeuroHmmerApp::VERSION
77
+ exit
78
+ end
79
+
80
+ ENV['RACK_ENV'] = 'development' if devel?
81
+
82
+ # Exit gracefully on SIGINT.
83
+ stty = `stty -g`.chomp
84
+ trap('INT') do
85
+ puts ''
86
+ puts 'Aborted.'
87
+ system('stty', stty)
88
+ exit
89
+ end
90
+
91
+ require 'neurohmmerapp'
92
+
93
+ begin
94
+ NeuroHmmerApp.init clean_opts[to_h]
95
+
96
+ # The aim of following error recovery scenarios is to guide user to a
97
+ # working NeuroHmmerApp installation. We expect to land following
98
+ # error scenarios either when creating a new NeuroHmmerApp (first
99
+ # time or later), or updating config values using -s CLI option.
100
+
101
+ rescue NeuroHmmerApp::CONFIG_FILE_ERROR => e
102
+
103
+ puts e
104
+ exit!
105
+
106
+ rescue NeuroHmmerApp::BIN_DIR_NOT_FOUND => e
107
+
108
+ puts e
109
+
110
+ unless bin?
111
+ puts 'You can set the correct value by running:'
112
+ puts
113
+ puts ' neurohmmerapp -s -b <value>'
114
+ puts
115
+ end
116
+
117
+ exit!
118
+
119
+
120
+ rescue NeuroHmmerApp::NUM_THREADS_INCORRECT => e
121
+
122
+ puts e
123
+
124
+ unless num_threads?
125
+ puts 'You can set the correct value by running:'
126
+ puts
127
+ puts ' neurohmmerapp -s -n <value>'
128
+ puts
129
+ end
130
+
131
+ exit!
132
+
133
+ rescue => e
134
+ # This will catch any unhandled error and some very special errors.
135
+ # Ideally we will never hit this block. If we do, there's a bug in
136
+ # NeuroHmmerApp or something really weird going on. If we hit this
137
+ # error block we show the stacktrace to the user requesting them to
138
+ # post the same to our Google Group.
139
+ puts <<MSG
140
+ Something went wonky
141
+
142
+ Looks like you have encountered a bug in NeuroHmmerApp. Please could you
143
+ report this incident here -
144
+ https://github.com/wurmlab/neurohmmerapp/issues
145
+
146
+ Error:
147
+ #{e.backtrace.unshift(e.message).join("\n")}
148
+ MSG
149
+ exit
150
+ end
151
+
152
+ if set?
153
+ NeuroHmmerApp.config.write_config_file
154
+ exit
155
+ end
156
+
157
+ NeuroHmmerApp.config.write_config_file if fetch_option(:set).value
158
+
159
+ NeuroHmmerApp.run
160
+ end
161
+ end
162
+ rescue Slop::Error => e
163
+ puts e
164
+ puts "Run '#{$PROGRAM_NAME} -h' for help with command line options."
165
+ exit
166
+ end
data/config.ru ADDED
@@ -0,0 +1,3 @@
1
+ require 'genevalidatorapp'
2
+ GeneValidatorApp.init
3
+ run GeneValidatorApp
@@ -0,0 +1,87 @@
1
+ require 'forwardable'
2
+
3
+ # Define Config class.
4
+ module NeuroHmmerApp
5
+ # Capture our configuration system.
6
+ class Config
7
+ extend Forwardable
8
+
9
+ def_delegators NeuroHmmerApp, :logger
10
+
11
+ def initialize(data = {})
12
+ @data = symbolise data
13
+ @config_file = @data.delete(:config_file) || default_config_file
14
+ @config_file = File.expand_path(@config_file)
15
+ @data = parse_config_file.update @data
16
+ @data = defaults.update @data
17
+ end
18
+
19
+ attr_reader :data, :config_file
20
+
21
+ # Get.
22
+ def [](key)
23
+ data[key]
24
+ end
25
+
26
+ # Set.
27
+ def []=(key, value)
28
+ data[key] = value
29
+ end
30
+
31
+ # Exists?
32
+ def include?(key)
33
+ data.include? key
34
+ end
35
+
36
+ # Write config data to config file.
37
+ def write_config_file
38
+ return unless config_file
39
+
40
+ File.open(config_file, 'w') do |f|
41
+ f.puts(data.delete_if { |_, v| v.nil? }.to_yaml)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ # Symbolizes keys. Changes `database` key to `database_dir`.
48
+ def symbolise(data)
49
+ return {} unless data
50
+ # Symbolize keys.
51
+ Hash[data.map { |k, v| [k.to_sym, v] }]
52
+ end
53
+
54
+ # Parses and returns data from config_file if it exists. Returns {}
55
+ # otherwise.
56
+ def parse_config_file
57
+ unless file? config_file
58
+ logger.debug "Configuration file not found: #{config_file}"
59
+ return {}
60
+ end
61
+
62
+ logger.debug "Reading configuration file: #{config_file}."
63
+ symbolise YAML.load_file(config_file)
64
+ rescue => error
65
+ raise CONFIG_FILE_ERROR.new(config_file, error)
66
+ end
67
+
68
+ def file?(file)
69
+ file && File.exist?(file) && File.file?(file)
70
+ end
71
+
72
+ # Default configuration data.
73
+ def defaults
74
+ {
75
+ :num_threads => 1,
76
+ :port => 4567,
77
+ :host => '0.0.0.0',
78
+ :public_dir => File.join(Dir.home, '.neurohmmerapp/'),
79
+ :max_characters => 'undefined',
80
+ }
81
+ end
82
+
83
+ def default_config_file
84
+ '~/.neurohmmerapp.conf'
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,77 @@
1
+ # This file defines all possible exceptions that can be thrown by
2
+ # NeuroHmmerApp 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 NeuroHmmerApp
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 extension file set, but does not exist.
63
+ class EXTENSION_FILE_NOT_FOUND < ENOENT
64
+ def initialize(ent)
65
+ super 'extension file', ent
66
+ end
67
+ end
68
+
69
+ ## NUM THREADS ##
70
+
71
+ # Raised if num_threads set by the user is incorrect.
72
+ class NUM_THREADS_INCORRECT < StandardError
73
+ def to_s
74
+ 'Number of threads should be a number greater than or equal to 1.'
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,24 @@
1
+ require 'logger'
2
+
3
+ module NeuroHmmerApp
4
+ class Logger < Logger
5
+ def initialize(dev, verbose = false)
6
+ super dev
7
+ self.level = verbose ? DEBUG : INFO
8
+ self.formatter = Formatter.new
9
+ end
10
+
11
+ # We change Logging format so that it is consistent with Sinatra's
12
+ class Formatter < Formatter
13
+ Format = "[%s] %s %s\n"
14
+
15
+ def initialize
16
+ self.datetime_format = '%Y-%m-%d %H:%M:%S'
17
+ end
18
+
19
+ def call(severity, time, _progname, msg)
20
+ Format % [format_datetime(time), severity, msg2str(msg)]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,158 @@
1
+ require 'forwardable'
2
+ require 'neurohmmer'
3
+
4
+ module NeuroHmmerApp
5
+ # Module that runs NeuroHmmer
6
+ module RunNeuroHmmer
7
+ # To signal error in query sequence or options.
8
+ #
9
+ # ArgumentError is raised when BLAST+'s exit status is 1; see [1].
10
+ class ArgumentError < ArgumentError
11
+ end
12
+
13
+ # To signal internal errors.
14
+ #
15
+ # RuntimeError is raised when there is a problem in writing the input file,
16
+ # in running BLAST, get_raw_sequence or genevalidator. These are rare,
17
+ # infrastructure errors, used internally, and of concern only to the
18
+ # admins/developers.
19
+ class RuntimeError < RuntimeError
20
+ end
21
+
22
+ class << self
23
+ extend Forwardable
24
+
25
+ def_delegators NeuroHmmerApp, :config, :logger, :public_dir
26
+
27
+ attr_reader :gv_dir, :input_file, :xml_file, :raw_seq, :unique_id, :params
28
+
29
+ # Setting the scene
30
+ def init(base_url, params)
31
+ create_unique_id
32
+ create_run_dir
33
+ @params = params
34
+ validate_params
35
+ # @url = produce_result_url_link(base_url)
36
+ end
37
+
38
+ # Runs genevalidator & Returns parsed JSON, or link to JSON/results file
39
+ def run
40
+ write_seqs_to_file
41
+ run_neurohmmer
42
+ end
43
+
44
+ private
45
+
46
+ # Creates a unique run ID (based on time),
47
+ def create_unique_id
48
+ @unique_id = Time.new.strftime('%Y-%m-%d_%H-%M-%S_%L-%N')
49
+ @run_dir = File.join(NeuroHmmerApp.public_dir, 'NeuroHmmer',
50
+ @unique_id)
51
+ ensure_unique_id
52
+ end
53
+
54
+ # Ensures that the Unique id is unique (if a sub dir is present in the
55
+ # temp dir with the unique id, it simply creates a new one)
56
+ def ensure_unique_id
57
+ while File.exist?(@run_dir)
58
+ @unique_id = create_unique_id
59
+ @run_dir = File.join(NeuroHmmerApp.public_dir, 'NeuroHmmer',
60
+ @unique_id)
61
+ end
62
+ logger.debug("Unique ID = #{@unique_id}")
63
+ end
64
+
65
+ # Create a sub_dir in the Tempdir (name is based on unique id)
66
+ def create_run_dir
67
+ logger.debug("NeuroHmmerApp Tempdir = #{@run_dir}")
68
+ FileUtils.mkdir_p(@run_dir)
69
+ end
70
+
71
+ # Validates the paramaters provided via the app.
72
+ # Only important if POST request is sent via API - Web APP also validates
73
+ # all params via Javascript.
74
+ def validate_params
75
+ logger.debug("Input Paramaters: #{@params}")
76
+ check_seq_param_present
77
+ check_seq_length
78
+ check_nps_param_present
79
+ end
80
+
81
+ # Simply asserts whether that the seq param is present
82
+ def check_seq_param_present
83
+ return if @params[:seq]
84
+ fail ArgumentError, 'No input sequence provided.'
85
+ end
86
+
87
+ def check_seq_length
88
+ return unless config[:max_characters] != 'undefined'
89
+ return if @params[:seq].length < config[:max_characters]
90
+ fail ArgumentError, 'The input sequence is too long.'
91
+ end
92
+
93
+ def check_nps_param_present
94
+ return if @params[:neuropeptides]
95
+ fail ArgumentError, 'No neuropeptides groups specified'
96
+ end
97
+
98
+ # Writes the input sequences to a file with the sub_dir in the temp_dir
99
+ def write_seqs_to_file
100
+ @input_file = File.join(@run_dir, 'input_file.fa')
101
+ logger.debug("Writing input seqs to: '#{@input_file}'")
102
+ ensure_unix_line_ending
103
+ ensure_fasta_valid
104
+ File.open(@input_file, 'w+') { |f| f.write(@params[:seq]) }
105
+ assert_input_file_present
106
+ end
107
+
108
+ def ensure_unix_line_ending
109
+ @params[:seq].gsub!(/\r\n?/, "\n")
110
+ end
111
+
112
+ # Adds a ID (based on the time when submitted) to sequences that are not
113
+ # in fasta format.
114
+ def ensure_fasta_valid
115
+ logger.debug('Adding an ID to sequences that are not in fasta format.')
116
+ unique_queries = {}
117
+ sequence = @params[:seq].lstrip
118
+ if sequence[0] != '>'
119
+ sequence.insert(0, '>Submitted:'\
120
+ "#{Time.now.strftime('%H:%M-%B_%d_%Y')}\n")
121
+ end
122
+ sequence.gsub!(/^\>(\S+)/) do |s|
123
+ if unique_queries.key?(s)
124
+ unique_queries[s] += 1
125
+ s + '_' + (unique_queries[s] - 1).to_s
126
+ else
127
+ unique_queries[s] = 1
128
+ s
129
+ end
130
+ end
131
+ @params[:seq] = sequence
132
+ end
133
+
134
+ # Asserts that the input file has been generated and is not empty
135
+ def assert_input_file_present
136
+ return if File.exist?(@input_file) && !File.zero?(@input_file)
137
+ fail 'NeuroHmmerApp was unable to create the input file.'
138
+ end
139
+
140
+ def run_neurohmmer
141
+ opt = {
142
+ temp_dir: File.join(@run_dir, 'tmp'),
143
+ input_file: @input_file,
144
+ num_threads: config[:num_threads]
145
+ }
146
+ Neurohmmer.init(opt)
147
+ Neurohmmer::Hmmer.search
148
+ hmm_results = Neurohmmer::Hmmer.analyse_output
149
+ Neurohmmer::Output.format_seqs_for_html(hmm_results)
150
+ end
151
+
152
+ def create_log_file
153
+ @log_file = File.join(@run_dir, 'log_file.txt')
154
+ logger.debug("Log file: #{@log_file}")
155
+ end
156
+ end
157
+ end
158
+ end