#!/usr/bin/env ruby require 'readline' require 'English' require 'slop' ENV['RACK_ENV'] ||= 'production' # display name for tools like `ps` $PROGRAM_NAME = 'sequenceserver' # Given a url, downloads the archive into a temporary file # and and extracts it into ~/.sequenceserver. def download_from_url(url) archive = File.join('~/.sequenceserver', File.basename(url)) # -ip4 is required to avoid this curl bug http://sourceforge.net/p/curl/bugs/1424/?limit=25 # see also https://github.com/yannickwurm/sequenceserver/issues/139 cmd = 'mkdir -p ~/.sequenceserver' \ " && curl --ipv4 -C - #{url} -o #{archive}" \ " && tar xvf #{archive} -C ~/.sequenceserver" system(cmd) end def ask_to_join asked_to_join = File.join(SequenceServer::DOTDIR, 'asked_to_join') unless File.exist?(asked_to_join) puts puts <<~MSG Do you want to be notified of SequenceServer releases and any other important announcements (3-12 messages a year)? If yes, please provide your email address below or press enter to continue (you won't be prompted again). MSG print '>> ' response = STDIN.gets.to_s.strip puts if response =~ /@/ post_email_cmd = "curl -m 5 https://docs.google.com/forms/u/0/d/e/" \ "1FAIpQLSe7sOCiKzYbI7LwErid-g3wfIU5Zpi6VTm0ILJyR036RqC8zg/formResponse " \ "-d ifq -d emailAddress=#{response} -d submit=Submit > /dev/null 2> /dev/null" system post_email_cmd end system "mkdir -p #{SequenceServer::DOTDIR} && touch #{asked_to_join}" end end begin Slop.parse!(strict: true, help: true) do banner <<~BANNER SUMMARY custom, local, BLAST server USAGE sequenceserver [options] EXAMPLE # Launch SequenceServer. This will read configuration from # ~/.sequenceserver.conf, if present. $ sequenceserver # Use a different config file. $ sequenceserver -c ~/.sequenceserver.ants.conf # Set number of threads to use. This will save the number # of threads to use in config file. $ sequenceserver -s -n 16 # Search for FASTA files in database dir that haven't been # converted into BLAST database yet, and convert them. $ sequenceserver -m DESCRIPTION SequenceServer lets you rapidly set up a BLAST+ server with an intuitive user interface for use locally or over the web. If BLAST+ is not installed on your system, SequenceServer will offer to install BLAST+ for you. You should only ever have to point it to a directory of FASTA files / BLAST+ databases. In a given directory, SequenceServer is able to tell FASTA files that are yet to be formatted for use with BLAST+ and format them, and FASTA files that are already formatted for use with BLAST+, heuristically skipping all other files in the directory. Directories are scanned recursively. Type of sequences in a FASTA file is detected automagically. `parse_seqids` and `hash_index` options of `makeblastdb` are used to create BLAST+ databases. BANNER on 'c', 'config_file=', 'Use the given configuration file', argument: true on 'config=', 'Same as --config_file (deprecated)', argument: true on 'b', 'bin=', 'Load BLAST+ binaries from this directory', argument: true on 'd', 'database_dir=', 'Read FASTA and BLAST database from this directory', argument: true on 'n', 'num_threads=', 'Number of threads to use to run a BLAST search', argument: true on 'r', 'require=', 'Load extension from this file', argument: true on 'H', 'host=', 'Host to run SequenceServer on', argument: true on 'p', 'port=', 'Port to run SequenceServer on', argument: true on 'o', 'optimistic=', 'Optimistic mode does not perform database compatibility checks, which can be faster. ' \ 'Only use this if you format FASTAs with "sequenceserver -make-blast-databases"', argument: true on 's', 'set', 'Set configuration value in default or given config file' on 'm', 'make-blast-databases', 'Create BLAST databases' on 'download-taxdb', 'Download the taxdb files' on 'l', 'list_databases', 'List BLAST databases' on 'i', 'interactive', 'Run SequenceServer in interactive mode' on 'x', 'import=', 'Import the xml file', argument: true # on 'doctor', # 'Run SequenceServer doctor' on 'D', 'devel', 'Start SequenceServer in development mode' on '-v', '--version', 'Print version number of SequenceServer that will be loaded' on '-h', '--help', 'Display this help message' clean_opts = lambda do |hash| hash.delete_if { |k, v| k == :set || v.nil? } hash[:config_file] ||= hash.delete(:config) hash end run do if version? require 'sequenceserver/version' puts SequenceServer::VERSION exit end ENV['RACK_ENV'] = 'development' if devel? # Exit gracefully on SIGINT. stty = `stty -g`.chomp trap('INT') do puts '' puts 'Aborted.' system('stty', stty) exit end require 'sequenceserver' begin SequenceServer.init clean_opts[to_h] # The aim of following error recovery scenarios is to guide user to a # working SequenceServer installation. We expect to land following # error scenarios either when creating a new SequenceServer (first # time or later), or updating config values using -s CLI option. rescue SequenceServer::BLAST_NOT_INSTALLED_OR_NOT_EXECUTABLE, SequenceServer::BLAST_NOT_COMPATIBLE => e # Show original error message first. puts puts e # Set a flag so that if we recovered from error resulting config can be # saved. Config will be saved unless invoked with -b option. fetch_option(:set).value = !bin? # Ask user if she already has BLAST+ downloaded or offer to download # BLAST+ for her. puts puts <<~MSG If you have downloaded NCBI BLAST+ already, please enter the path below. Otherwise just press Enter and SequenceServer will download a copy of NCBI BLAST+ for itself. Press Ctrl+C to abort setup. MSG puts response = Readline.readline('>> ').to_s.strip if response.empty? puts puts 'Downloading NCBI BLAST+.' puts "RUBY_PLATFORM #{RUBY_PLATFORM}" puts 'Archive will be extracted in ~/.sequenceserver directory.' puts version = SequenceServer::BLAST_VERSION url = case RUBY_PLATFORM when /x86_64-linux/ # 64 bit Linux 'ftp://ftp.ncbi.nlm.nih.gov/blast/executables/blast+/' \ "#{version.chop}/ncbi-blast-#{version}-x64-linux.tar.gz" when /darwin/ # Mac 'ftp://ftp.ncbi.nlm.nih.gov/blast/executables/blast+/' \ "#{version.chop}/" \ "ncbi-blast-#{version}-x64-macosx.tar.gz" else puts <<~ERR ------------------------------------------------------------------------ FAILED!! to install NCBI BLAST+. We currently support Linux and Mac only (64 bit). If you believe you are running a supported platform, please open a support ticket titled "#{RUBY_PLATFORM}" at: https://github.com/yannickwurm/sequenceserver/issues ------------------------------------------------------------------------ ERR end download_from_url(url) unless $CHILD_STATUS.success? puts 'Failed to install BLAST+.' puts ' You may need to download BLAST+ from - ' puts ' http://www.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=Download' puts " Please ensure that you download BLAST+ version #{SequenceServer::BLAST_VERSION}." exit! end fetch_option(:bin).value = "~/.sequenceserver/ncbi-blast-#{version}/bin/" else unless File.basename(response) == 'bin' response = File.join(response, 'bin') end fetch_option(:bin).value = response puts end redo rescue SequenceServer::DATABASE_DIR_NOT_SET => e # Show original error message. puts puts e # Set a flag so that if we recovered from error resulting config can be # saved. Config will be saved unless invoked with -d option. fetch_option(:set).value = !database_dir? # Ask user for the directory containing sequences or BLAST+ # databases. puts puts <<~MSG SequenceServer needs to know where your FASTA files or BLAST+ databases are. Please enter the path to the relevant directory (default: current directory). Press Ctrl+C to quit. MSG puts response = Readline.readline('>> ').to_s.strip fetch_option(:database_dir).value = response redo rescue SequenceServer::NO_BLAST_DATABASE_FOUND => e unless list_databases? || make_blast_databases? # Print error raised. puts puts e # Offer user to format the FASTA files. database_dir = SequenceServer.config[:database_dir] puts puts <<~MSG Search for FASTA files (.fa, .fasta, .fna) in '#{database_dir}' and try creating BLAST+ databases? [y/n] (Default: y). MSG puts print '>> ' response = STDIN.gets.to_s.strip unless response =~ /^[n]$/i puts puts 'Searching ...' if SequenceServer.makeblastdb.any_to_format? formatted = SequenceServer.makeblastdb.format exit! if formatted.empty? && !set? redo unless set? else puts "Couldn't find any FASTA files." exit! end else exit! unless set? end end rescue SequenceServer::ENOENT, SequenceServer::CONFIG_FILE_ERROR, SequenceServer::BLAST_DATABASE_ERROR, SequenceServer::NUM_THREADS_INCORRECT => e # Known errors with clear error message are handled here. puts e exit! rescue => e # This will catch any unhandled error and some very special errors. # Ideally we will never hit this block. If we do, there's a bug in # SequenceServer or something really weird going on. If we hit this # error block we show the stacktrace to the user requesting them to # post the same to our Google Group. puts <<~MSG Something went wonky Looks like you have encountered a bug in SequenceServer. Please could you report this incident to our Google Group - https://groups.google.com/forum/?fromgroups#!forum/sequenceserver Error: #{e.backtrace.unshift(e.message).join("\n")} MSG exit! end if doctor? puts '*** Running SequenceServer Doctor.' puts ' Initializing an index of databases. This may take a while..' SequenceServer::Doctor.new.diagnose exit end if interactive? SequenceServer.irb exit end if list_databases? puts SequenceServer::Database.all exit end if make_blast_databases? if SequenceServer.makeblastdb.no_fastas? puts "Couldn't find any FASTA files in #{SequenceServer.config[:database_dir]}." elsif SequenceServer.makeblastdb.any_to_format_or_reformat? puts puts <<~MSG SequenceServer has scanned your databases directory and will now offer to convert FASTA files into BLAST databases. It will also offer to reformat any old-format BLAST databases and those created without the -parse_seqids option of makeblastdb (-parse_seqids option is required for sequence retrieval to correctly work). Note that reformatting process can be slow large BLAST databases and fail if sequence identifiers are longer than 50 characters. While we exepect the reformatting process to work in most other cases, things can inevitably go wrong. Thus, please back up your databases before reformatting and post any issues to our Google Group/GitHub so that we can all learn from it. Proceed? [y/n] (Default: y). MSG puts print '>> ' response = STDIN.gets.to_s.strip SequenceServer.makeblastdb.run unless response =~ /^[n]$/i else puts "All FASTA files in #{SequenceServer.config[:database_dir]} " \ 'are formatted.' end exit end if download_taxdb? download_from_url('ftp://ftp.ncbi.nlm.nih.gov/blast/db/'\ 'taxdb.tar.gz') puts puts 'taxdb downloaded!' exit end if import? xml_file_path = fetch_option(:import).value params = {:xml => xml_file_path} job = SequenceServer::BLAST::Job.new(params) puts job.id exit end if set? SequenceServer.config.write_config_file exit end SequenceServer.config.write_config_file if fetch_option(:set).value ask_to_join SequenceServer.run end end rescue Slop::Error => e puts e puts "Run '#{$PROGRAM_NAME} -h' for help with command line options." exit end