#!/usr/bin/env python # -------------------------------- WebLogo -------------------------------- # Copyright (c) 2003-2004 The Regents of the University of California. # Copyright (c) 2005 Gavin E. Crooks # Copyright (c) 2006-2011, The Regents of the University of California, through # Lawrence Berkeley National Laboratory (subject to receipt of any required # approvals from the U.S. Dept. of Energy). All rights reserved. # This software is distributed under the new BSD Open Source License. # # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # (1) Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # (2) Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and or other materials provided with the distribution. # # (3) Neither the name of the University of California, Lawrence Berkeley # National Laboratory, U.S. Dept. of Energy nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # WebLogo Command Line Interfaceg import os import sys from optparse import OptionGroup from io import StringIO from . import seq_io from .seq import Seq, SeqList, nucleic_alphabet from .utils import resource_filename from .utils.deoptparse import DeOptionParser from . import (LogoOptions, LogoData, LogoFormat, parse_prior, description, release_description, formatters, default_formatter, std_alphabets, std_units, std_sizes, std_color_schemes, read_seq_data) from .logo import (_seq_names, _seq_formats) from .colorscheme import ColorScheme, SymbolColor # ====================== Main: Parse Command line ============================= def main(): """WebLogo command line interface """ # ------ Parse Command line ------ parser = _build_option_parser() (opts, args) = parser.parse_args(sys.argv[1:]) if args: parser.error("Unparsable arguments: %s " % args) if opts.serve: httpd_serve_forever(opts.port) # Never returns? # pragma: no cover sys.exit(0) # pragma: no cover # ------ Create Logo ------ try: data = _build_logodata(opts) format = _build_logoformat(data, opts) formatter = opts.formatter logo = formatter(data, format) # logo = logo.encode() opts.fout.buffer.write(logo) except ValueError as err: print('Error:', err, file=sys.stderr) sys.exit(2) except KeyboardInterrupt: sys.exit(0) # End main() def httpd_serve_forever(port=8080): """ Start a webserver on a local port.""" import http.server as server import http.server as cgiserver class __HTTPRequestHandler(cgiserver.CGIHTTPRequestHandler): # Modify CGIHTTPRequestHandler so that it will run the cgi script directly, # instead of exec'ing # This bypasses the need for the cgi script to have execute permissions set, # since distutils install does not preserve permissions. def is_cgi(self): self.have_fork = False # Prevent CGIHTTPRequestHandler from using fork if self.path == "/create.cgi": self.cgi_info = '', 'create.cgi' return True return False def is_python(self, path): # Let CGIHTTPRequestHandler know that cgi script is python return True # Add current directory to PYTHONPATH. This is # so that we can run the standalone server # without having to run the install script. pythonpath = os.getenv("PYTHONPATH", '') pythonpath += os.pathsep + os.path.abspath(sys.path[0]) # .split()[0] os.environ["PYTHONPATH"] = pythonpath htdocs = resource_filename(__name__, 'htdocs', __file__) os.chdir(htdocs) HandlerClass = __HTTPRequestHandler ServerClass = server.HTTPServer httpd = ServerClass(('', port), HandlerClass) print("WebLogo server running at http://localhost:%d/" % port) try: httpd.serve_forever() except KeyboardInterrupt: sys.exit(0) # end httpd_serve_forever() def _build_logodata(options): motif_flag = False fin = options.fin if options.upload is None: if fin is None: fin = StringIO(sys.stdin.read()) else: if fin is None: from . import _from_URL_fileopen fin = _from_URL_fileopen(options.upload) else: raise ValueError("error: options --fin and --upload are incompatible") try: # Try reading data in transfac format first. from .matrix import Motif motif = Motif.read_transfac(fin, alphabet=options.alphabet) motif_flag = True except ValueError as motif_err: # Failed reading Motif, try reading as multiple sequence data. if options.input_parser == "transfac": raise motif_err # Adding transfac as str insted of parser is a bit of a ugly kludge seqs = read_seq_data(fin, options.input_parser.read, alphabet=options.alphabet, ignore_lower_case=options.ignore_lower_case) if motif_flag: if options.ignore_lower_case: raise ValueError("error: option --ignore-lower-case incompatible with matrix input") if options.reverse or options.revcomp: motif.reverse() if options.complement or options.revcomp: motif.complement() prior = parse_prior(options.composition, motif.alphabet, options.weight) data = LogoData.from_counts(motif.alphabet, motif, prior) else: if options.reverse or options.revcomp: seqs = SeqList([s.reverse() for s in seqs], seqs.alphabet) if options.complement or options.revcomp: if not nucleic_alphabet.alphabetic(seqs.alphabet): raise ValueError('non-nucleic sequence cannot be complemented') # pragam: no cover aaa = seqs.alphabet seqs.alphabet = nucleic_alphabet seqs = SeqList([Seq(s, seqs.alphabet).complement() for s in seqs], seqs.alphabet) seqs.alphabet = aaa prior = parse_prior(options.composition, seqs.alphabet, options.weight) data = LogoData.from_seqs(seqs, prior) return data def _build_logoformat(logodata, opts): """ Extract and process relevant option values and return a LogoFormat object.""" args = {} direct_from_opts = [ # Logo Data Options. "alphabet", "unit_name", "first_index", "logo_start", "logo_end", # Logo Format Options. "stack_width", "stacks_per_line", "logo_title", "logo_label", "show_xaxis", "xaxis_label", "annotate", "rotate_numbers", "number_interval", "yaxis_scale", "show_yaxis", "yaxis_label", "show_ends", "fineprint", "yaxis_tic_interval", "show_errorbars", "reverse_stacks", # Color Options. "color_scheme", "default_color", # Font Format Options. "fontsize", "title_fontsize", "small_fontsize", "number_fontsize", "text_font", "logo_font", "title_font", # Advanced Format Options. "stack_aspect_ratio", "show_boxes", "resolution", "scale_width", "debug", "errorbar_fraction", "errorbar_width_fraction", "errorbar_gray", ] for k in direct_from_opts: args[k] = opts.__dict__[k] # logo_size = copy.copy(opts.__dict__['logo_size']) # size_from_opts = ["stack_width", "stack_height"] # for k in size_from_opts : # length = getattr(opts, k) # if length : # setattr( logo_size, k, length ) # args["size"] = logo_size if opts.colors: color_scheme = ColorScheme() for color, symbols, desc in opts.colors: try: # c = Color.from_string(color) color_scheme.rules.append(SymbolColor(symbols, color, desc)) except ValueError: raise ValueError( "error: option --color: invalid value: '%s'" % color) args["color_scheme"] = color_scheme if opts.annotate: args["annotate"] = opts.annotate.split(',') logooptions = LogoOptions() for a, v in args.items(): setattr(logooptions, a, v) theformat = LogoFormat(logodata, logooptions) return theformat # ========================== OPTIONS ========================== def _build_option_parser(): defaults = LogoOptions() parser = DeOptionParser(usage="%prog [options] < sequence_data.fa > sequence_logo.eps", description=description, version=release_description, add_verbose_options=False ) io_grp = OptionGroup(parser, "Input/Output Options", ) data_grp = OptionGroup(parser, "Logo Data Options", ) trans_grp = OptionGroup(parser, "Transformations", "Optional transformations of the sequence data.") format_grp = OptionGroup(parser, "Logo Format Options", "These options control the format and display of the logo.") color_grp = OptionGroup(parser, "Color Options", "Colors can be specified using CSS2 syntax. e.g. 'red', '#FF0000', etc") font_grp = OptionGroup(parser, "Font Format Options", "These options provide control over the font sizes and types.") advanced_grp = OptionGroup(parser, "Advanced Format Options", "These options provide fine control over the display of the logo.") server_grp = OptionGroup(parser, "WebLogo Server", "Run a standalone webserver on a local port.") parser.add_option_group(io_grp) parser.add_option_group(data_grp) parser.add_option_group(trans_grp) parser.add_option_group(format_grp) parser.add_option_group(color_grp) parser.add_option_group(font_grp) parser.add_option_group(advanced_grp) parser.add_option_group(server_grp) # ========================== IO OPTIONS ========================== io_grp.add_option("-f", "--fin", dest="fin", action="store", type="file_in", default=None, help="Sequence input file (default: stdin)", metavar="FILENAME") io_grp.add_option("", "--upload", dest="upload", action="store", default=None, help="Upload input file from URL", metavar="URL") io_grp.add_option("-D", "--datatype", dest="input_parser", action="store", type="dict", default=seq_io, choices=_seq_formats(), help="Type of multiple sequence alignment or position" " weight matrix file: (%s)" % ', '.join(_seq_names()), metavar="FORMAT") io_grp.add_option("-o", "--fout", dest="fout", type="file_out", default=sys.stdout, help="Output file (default: stdout)", metavar="FILENAME") io_grp.add_option("-F", "--format", dest="formatter", action="store", type="dict", choices=formatters, metavar="FORMAT", help="Format of output: eps (default), png, png_print, pdf, jpeg, svg, " "logodata", default=default_formatter) # ========================== Data OPTIONS ========================== data_grp.add_option("-A", "--sequence-type", dest="alphabet", action="store", type="dict", choices=std_alphabets, help="The type of sequence data: 'protein', 'rna' or 'dna'.", metavar="TYPE") data_grp.add_option("-a", "--alphabet", dest="alphabet", action="store", help="The set of symbols to count, e.g. 'AGTC'. " "All characters not in the alphabet are ignored. " "If neither the alphabet nor sequence-type are specified then weblogo " "will examine the input data and make an educated guess. " "See also --sequence-type, --ignore-lower-case") data_grp.add_option("-U", "--units", dest="unit_name", action="store", choices=list(std_units.keys()), type="choice", default=defaults.unit_name, help="A unit of entropy ('bits' (default), 'nats', 'digits'), or a unit of" "free energy ('kT', 'kJ/mol', 'kcal/mol'), or 'probability' for" " probabilities", metavar="UNIT") data_grp.add_option("", "--composition", dest="composition", action="store", type="string", default="auto", help="The expected composition of the sequences: 'auto' (default), " "'equiprobable', 'none' (do not perform any compositional " "adjustment), a CG percentage, a species name (e.g. 'E. coli', " "'H. sapiens'), or an explicit distribution (e.g. \"{'A':10, 'C':40," " 'G':40, 'T':10}\"). The automatic option uses a typical " "distribution for proteins and equiprobable distribution for " "everything else. ", metavar="COMP.") data_grp.add_option("", "--weight", dest="weight", action="store", type="float", default=None, help="The weight of prior data. Default depends on alphabet length", metavar="NUMBER") data_grp.add_option("-i", "--first-index", dest="first_index", action="store", type="int", default=1, help="Index of first position in sequence data (default: 1)", metavar="INDEX") data_grp.add_option("-l", "--lower", dest="logo_start", action="store", type="int", help="Lower bound of sequence to display", metavar="INDEX") data_grp.add_option("-u", "--upper", dest="logo_end", action="store", type="int", help="Upper bound of sequence to display", metavar="INDEX") # ========================== Transformation OPTIONS ========================== # FIXME Add test? trans_grp.add_option("", "--ignore-lower-case", dest="ignore_lower_case", action="store_true", default=False, help="Disregard lower case letters and only count upper case letters" " in sequences." ) trans_grp.add_option("", "--reverse", dest="reverse", action="store_true", default=False, help="reverse sequences", ) trans_grp.add_option("", "--complement", dest="complement", action="store_true", default=False, help="complement nucleic sequences", ) trans_grp.add_option("", "--revcomp", dest="revcomp", action="store_true", default=False, help="reverse complement nucleic sequences", ) # ========================== FORMAT OPTIONS ========================== format_grp.add_option("-s", "--size", dest="stack_width", action="store", type="dict", choices=std_sizes, metavar="LOGOSIZE", default=defaults.stack_width, help="Specify a standard logo size (small, medium (default), large)") format_grp.add_option("-n", "--stacks-per-line", dest="stacks_per_line", action="store", type="int", help="Maximum number of logo stacks per logo line. (default: %default)", default=defaults.stacks_per_line, metavar="COUNT") format_grp.add_option("-t", "--title", dest="logo_title", action="store", type="string", help="Logo title text.", default=defaults.logo_title, metavar="TEXT") format_grp.add_option("", "--label", dest="logo_label", action="store", type="string", help="A figure label, e.g. '2a'", default=defaults.logo_label, metavar="TEXT") format_grp.add_option("-X", "--show-xaxis", action="store", type="boolean", default=defaults.show_xaxis, metavar="YES/NO", help="Display sequence numbers along x-axis? (default: %default)") format_grp.add_option("-x", "--xlabel", dest="xaxis_label", action="store", type="string", default=defaults.xaxis_label, help="X-axis label", metavar="TEXT") format_grp.add_option("", "--annotate", dest="annotate", action="store", type="string", default=None, help="A comma separated list of custom stack annotations, " "e.g. '1,3,4,5,6,7'. Annotation list must be same length as " "sequences.", metavar="TEXT") format_grp.add_option("", "--rotate-numbers", dest="rotate_numbers", action="store", type="boolean", default=defaults.rotate_numbers, help="Draw X-axis numbers with vertical orientation (default: %default).", metavar="YES/NO") format_grp.add_option("", "--number-interval", dest="number_interval", action="store", type="float", default=defaults.number_interval, help="Distance between numbers on X-axis (default: %s)" % defaults.number_interval, metavar="NUMBER") format_grp.add_option("-S", "--yaxis", dest="yaxis_scale", action="store", type="float", help="Height of yaxis in units. (Default: Maximum value with " "uninformative prior.)", metavar="NUMBER") format_grp.add_option("-Y", "--show-yaxis", action="store", type="boolean", dest="show_yaxis", default=defaults.show_yaxis, metavar="YES/NO", help="Display entropy scale along y-axis? (default: %default)") format_grp.add_option("-y", "--ylabel", dest="yaxis_label", action="store", type="string", help="Y-axis label (default depends on plot type and units)", metavar="TEXT") format_grp.add_option("-E", "--show-ends", action="store", type="boolean", default=defaults.show_ends, metavar="YES/NO", help="Label the ends of the sequence? (default: %default)") format_grp.add_option("-P", "--fineprint", dest="fineprint", action="store", type="string", default=defaults.fineprint, help="The fine print (default: weblogo version)", metavar="TEXT") format_grp.add_option("", "--ticmarks", dest="yaxis_tic_interval", action="store", type="float", default=defaults.yaxis_tic_interval, help="Distance between ticmarks (default: %default)", metavar="NUMBER") format_grp.add_option("", "--errorbars", dest="show_errorbars", action="store", type="boolean", default=defaults.show_errorbars, metavar="YES/NO", help="Display error bars? (default: %default)") format_grp.add_option("", "--reverse-stacks", dest="reverse_stacks", action="store", type="boolean", default=defaults.show_errorbars, metavar="YES/NO", help="Draw stacks with largest letters on top? (default: %default)") # ========================== Color OPTIONS ========================== # TODO: Future Feature # color_grp.add_option( "-K", "--color-key", # dest= "show_color_key", # action="store", # type = "boolean", # default= defaults.show_color_key, # metavar = "YES/NO", # help="Display a color key (default: %default)") color_scheme_choices = list(std_color_schemes.keys()) color_scheme_choices.sort() color_grp.add_option("-c", "--color-scheme", dest="color_scheme", action="store", type="dict", choices=std_color_schemes, metavar="SCHEME", default=None, # Auto help="Specify a standard color scheme (%s)" % \ ", ".join(color_scheme_choices)) color_grp.add_option("-C", "--color", dest="colors", action="append", metavar="COLOR SYMBOLS DESCRIPTION ", nargs=3, default=[], help="Specify symbol colors, e.g. --color black AG 'Purine' " "--color red TC 'Pyrimidine' ") color_grp.add_option("", "--default-color", dest="default_color", action="store", metavar="COLOR", default=defaults.default_color, help="Symbol color if not otherwise specified.") # ========================== Font options ========================= font_grp.add_option("", "--fontsize", dest="fontsize", action="store", type="float", default=defaults.fontsize, help="Regular text font size in points (default: %s)" % defaults.fontsize, metavar="POINTS") font_grp.add_option("", "--title-fontsize", dest="title_fontsize", action="store", type="float", default=defaults.title_fontsize, help="Title text font size in points (default: %s)" % defaults.title_fontsize, metavar="POINTS") font_grp.add_option("", "--small-fontsize", dest="small_fontsize", action="store", type="float", default=defaults.small_fontsize, help="Small text font size in points (default: %s)" % defaults.small_fontsize, metavar="POINTS") font_grp.add_option("", "--number-fontsize", dest="number_fontsize", action="store", type="float", default=defaults.number_fontsize, help="Axis numbers font size in points (default: %s)" % defaults.number_fontsize, metavar="POINTS") font_grp.add_option("", "--text-font", dest="text_font", action="store", type="string", default=defaults.text_font, help="Specify font for labels (default: %s)" % defaults.text_font, metavar="FONT") font_grp.add_option("", "--logo-font", dest="logo_font", action="store", type="string", default=defaults.text_font, help="Specify font for logo (default: %s)" % defaults.logo_font, metavar="FONT") font_grp.add_option("", "--title-font", dest="title_font", action="store", type="string", default=defaults.title_font, help="Specify font for title (default: %s)" % defaults.title_font, metavar="FONT") # ========================== Advanced options ========================= advanced_grp.add_option("-W", "--stack-width", dest="stack_width", action="store", type="float", default=defaults.stack_width, help="Width of a logo stack (default: %s)" % defaults.stack_width, metavar="POINTS") advanced_grp.add_option("", "--aspect-ratio", dest="stack_aspect_ratio", action="store", type="float", default=defaults.stack_aspect_ratio, help="Ratio of stack height to width (default: %s)" % defaults.stack_aspect_ratio, metavar="POINTS") advanced_grp.add_option("", "--box", dest="show_boxes", action="store", type="boolean", default=False, metavar="YES/NO", help="Draw boxes around symbols? (default: no)") advanced_grp.add_option("", "--resolution", dest="resolution", action="store", type="float", default=96, help="Bitmap resolution in dots per inch (DPI). (Default: 96 DPI," " except png_print, 600 DPI) Low resolution bitmaps (DPI<300)" " are antialiased.", metavar="DPI") advanced_grp.add_option("", "--scale-width", dest="scale_width", action="store", type="boolean", default=True, metavar="YES/NO", help="Scale the visible stack width by the fraction of symbols in the" " column? (I.e. columns with many gaps of unknowns are narrow.) " "(Default: yes)") advanced_grp.add_option("", "--debug", action="store", type="boolean", default=defaults.debug, metavar="YES/NO", help="Output additional diagnostic information. (Default: %default)") advanced_grp.add_option("", "--errorbar-fraction", dest="errorbar_fraction", action="store", type="float", default=defaults.errorbar_fraction, help="Sets error bars display proportion (default: %s)" % defaults.errorbar_fraction, metavar="NUMBER") advanced_grp.add_option("", "--errorbar-width-fraction", dest="errorbar_width_fraction", action="store", type="float", default=defaults.errorbar_width_fraction, help="Sets error bars width display proportion (default: %s)" % defaults.errorbar_width_fraction, metavar="NUMBER") advanced_grp.add_option("", "--errorbar-gray", dest="errorbar_gray", action="store", type="float", default=defaults.errorbar_gray, help="Sets error bars' gray scale percentage (default: %s)" % defaults.errorbar_gray, metavar="NUMBER") # ========================== Server options ========================= server_grp.add_option("", "--serve", dest="serve", action="store_true", default=False, help="Start a standalone WebLogo server for creating sequence logos.") server_grp.add_option("", "--port", dest="port", action="store", type="int", default=8080, help="Listen to this local port. (Default: %default)", metavar="PORT") return parser # END _build_option_parser ##############################################################