diff weblogolib/_cgi.py @ 7:8d676bbd1f2d

Uploaded
author davidmurphy
date Mon, 16 Jan 2012 07:03:36 -0500
parents c55bdc2fb9fa
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/weblogolib/_cgi.py	Mon Jan 16 07:03:36 2012 -0500
@@ -0,0 +1,446 @@
+#!/usr/bin/env python
+
+#  Copyright (c) 2003-2004 The Regents of the University of California.
+#  Copyright (c) 2005 Gavin E. Crooks
+#  Copyright (c) 2006, 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.
+#  <http://www.opensource.org/licenses/bsd-license.html>
+#
+#  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. 
+
+import sys
+import cgi as cgilib
+import cgitb; cgitb.enable()
+
+#print "Content-Type: text/html\n\n"
+#print "HELLO WORLD"
+#print __name__
+
+from StringIO import StringIO
+from color import *
+from colorscheme import ColorScheme, ColorGroup
+
+import weblogolib
+from corebio.utils import *
+from corebio._future import Template
+
+
+# TODO: Check units
+
+# TODO: In WebLogo2: why slash create.cgi? I think this was a workaround
+# for some browser quirk
+#<form method="post" action="/create.cgi" enctype="multipart/form-data">
+    
+def resource_string(resource, basefilename) :
+    import os
+    fn =  os.path.join(os.path.dirname(basefilename), resource)
+    return open( fn ).read()
+
+mime_type = {
+    'eps': 'application/postscript', 
+    'pdf': 'application/pdf',
+    'png': 'image/png',
+    'png_print': 'image/png',    
+    'txt' : 'text/plain',       
+    'jpeg'  : 'image/jpeg',
+}
+
+extension = {
+    'eps': 'eps',
+    'pdf': 'pdf',
+    'png': 'png',
+    'png_print': 'png',
+    'txt' : 'txt',        
+    'jpeg'  : 'png'
+}
+
+
+alphabets = {
+    'alphabet_auto': None, 
+    'alphabet_protein': weblogolib.unambiguous_protein_alphabet, 
+    'alphabet_rna': weblogolib.unambiguous_rna_alphabet,
+    'alphabet_dna': weblogolib.unambiguous_dna_alphabet}
+
+color_schemes = {}
+for k in weblogolib.std_color_schemes.keys():
+    color_schemes[ 'color_'+k.replace(' ', '_')] = weblogolib.std_color_schemes[k]
+    
+
+composition = {'comp_none' : 'none',
+    'comp_auto' : 'auto',
+    'comp_equiprobable':'equiprobable',
+    'comp_CG': 'percentCG',
+    'comp_Celegans' : 'C. elegans',
+    'comp_Dmelanogaster' : 'D. melanogaster',
+    'comp_Ecoli' : 'E. coli',
+    'comp_Hsapiens': 'H. sapiens',
+    'comp_Mmusculus' : 'M. musculus',
+    'comp_Scerevisiae': 'S. cerevisiae'
+}
+
+class Field(object) :
+    """ A representation of an HTML form field."""
+    def __init__(self, name, default=None, conversion= None, options=None, errmsg="Illegal value.") :
+        self.name = name
+        self.default = default
+        self.value = default
+        self.conversion = conversion        
+        self.options = options
+        self.errmsg = errmsg
+
+    def get_value(self) :
+        if self.options :
+            if not self.value in self.options :
+                raise ValueError, (self.name, self.errmsg)
+                
+        if self.conversion :
+            try :
+                return self.conversion(self.value)
+            except ValueError, e :
+                raise ValueError, (self.name, self.errmsg)
+        else:
+            return self.value
+    
+    
+def string_or_none(value) :
+    if value is None or value == 'auto':
+        return None
+    return str(value)
+    
+def truth(value) :
+    if value== "true" : return True
+    return bool(value)
+
+def int_or_none(value) :
+    if value =='' or value is None or value == 'auto':
+        return None
+    return int(value)
+    
+def float_or_none(value) :
+    if value =='' or value is None or value == 'auto':
+        return None
+    return float(value)
+        
+
+def main(htdocs_directory = None) :
+ 
+    logooptions = weblogolib.LogoOptions() 
+    
+    # A list of form fields.
+    # The default for checkbox values must be False (irrespective of
+    # the default in logooptions) since a checked checkbox returns 'true'
+    # but an unchecked checkbox returns nothing.
+    controls = [
+        Field( 'sequences', ''),
+        Field( 'format', 'png', weblogolib.formatters.get ,
+            options=['png_print', 'png', 'jpeg', 'eps', 'pdf', 'txt'] , 
+            errmsg="Unknown format option."),
+        Field( 'stacks_per_line', logooptions.stacks_per_line , int, 
+            errmsg='Invalid number of stacks per line.'),
+        Field( 'size','medium', weblogolib.std_sizes.get,
+            options=['small', 'medium', 'large'], errmsg='Invalid logo size.'),
+        Field( 'alphabet','alphabet_auto', alphabets.get,
+            options=['alphabet_auto', 'alphabet_protein', 'alphabet_dna', 
+                        'alphabet_rna'],
+            errmsg="Unknown sequence type."),
+        Field( 'unit_name', 'bits', 
+            options=[ 'probability', 'bits', 'nats', 'kT', 'kJ/mol', 
+                        'kcal/mol']),
+        Field( 'first_index', 1, int_or_none),
+        Field( 'logo_start', '', int_or_none),
+        Field( 'logo_end', '', int_or_none),
+        Field( 'composition', 'comp_auto', composition.get,
+            options=['comp_none','comp_auto','comp_equiprobable','comp_CG',
+            'comp_Celegans','comp_Dmelanogaster','comp_Ecoli',
+            'comp_Hsapiens','comp_Mmusculus','comp_Scerevisiae'], 
+            errmsg= "Illegal sequence composition."),
+        Field( 'percentCG', '', float_or_none, errmsg="Invalid CG percentage."),
+        Field( 'show_errorbars', False , truth),
+        Field( 'altype', False , truth),
+        Field( 'logo_title', logooptions.logo_title ),
+        Field( 'logo_label', logooptions.logo_label ),
+        Field( 'show_xaxis', False, truth),
+        Field( 'xaxis_label', logooptions.xaxis_label ),
+        Field( 'show_yaxis', False, truth),  
+        Field( 'yaxis_label', logooptions.yaxis_label, string_or_none ),
+        Field( 'yaxis_scale', logooptions.yaxis_scale , float_or_none,
+            errmsg="The yaxis scale must be a positive number." ),
+        Field( 'yaxis_tic_interval', logooptions.yaxis_tic_interval , 
+                float_or_none),
+        Field( 'show_ends', False, truth), 
+        Field( 'show_fineprint', False , truth), 
+        Field( 'color_scheme', 'color_auto', color_schemes.get,
+            options=color_schemes.keys() ,
+            errmsg = 'Unknown color scheme'),
+        Field( 'color0', ''),
+        Field( 'symbols0', ''),
+        Field( 'desc0', ''),
+        Field( 'color1', ''),
+        Field( 'symbols1', ''),
+        Field( 'desc1', ''),
+        Field( 'color2', ''),
+        Field( 'symbols2', ''),
+        Field( 'desc2', ''),
+        Field( 'color3', ''),
+        Field( 'symbols3', ''),
+        Field( 'desc3', ''),
+        Field( 'color4', ''),
+        Field( 'symbols4', ''),
+        Field( 'desc4', ''),
+        Field( 'ignore_lower_case', False, truth),
+        Field( 'altype', False, truth), 
+        Field( 'scale_width', False, truth), 
+        ]
+    
+    form = {}
+    for c in controls :
+        form[c.name] = c
+
+
+    form_values = cgilib.FieldStorage()
+    
+    # Send default form?
+    if len(form_values) ==0 or form_values.has_key("cmd_reset"):
+        # Load default truth values now.
+        form['show_errorbars'].value = logooptions.show_errorbars
+        form['show_xaxis'].value = logooptions.show_xaxis
+        form['show_yaxis'].value = logooptions.show_yaxis
+        form['show_ends'].value = logooptions.show_ends
+        form['show_fineprint'].value = logooptions.show_fineprint
+        form['scale_width'].value = logooptions.scale_width
+        form['altype'].value = logooptions.altype
+        
+        send_form(controls, htdocs_directory = htdocs_directory) 
+        return
+    
+    # Get form content
+    for c in controls :
+        c.value = form_values.getfirst( c.name, c.default) 
+       
+       
+    options_from_form = ['format', 'stacks_per_line', 'size', 
+        'alphabet', 'unit_name', 'first_index', 'logo_start','logo_end',
+         'composition', 
+        'show_errorbars', 'logo_title', 'logo_label', 'show_xaxis', 
+        'xaxis_label',
+        'show_yaxis', 'yaxis_label', 'yaxis_scale', 'yaxis_tic_interval',
+        'show_ends', 'show_fineprint', 'scale_width','altype']
+    
+    errors = []
+    for optname in options_from_form :
+        try :
+            value =  form[optname].get_value()
+            if value!=None : setattr(logooptions, optname, value)
+        except ValueError, err :
+            errors.append(err.args)            
+
+    #check if using codons or not.
+    if logooptions.altype!=True:
+      weblogolib.altype = ""      
+    print >> sys.stderr,logooptions.altype
+    print >> sys.stderr, "--nn-"
+
+    # Construct custom color scheme
+    custom = ColorScheme()
+    for i in range(0,5) :
+        color = form["color%d"%i].get_value()
+        symbols = form["symbols%d"%i].get_value()
+        desc = form["desc%d"%i].get_value() 
+
+        if color :
+            try :
+                custom.groups.append(weblogolib.ColorGroup(symbols, color, desc))
+            except ValueError, e : 
+                errors.append( ('color%d'%i, "Invalid color: %s" % color) )
+    
+    if form["color_scheme"].value == 'color_custom' :
+        logooptions.color_scheme =  custom
+    else :
+        try :
+            logooptions.color_scheme = form["color_scheme"].get_value()
+        except ValueError, err :
+            errors.append(err.args)            
+
+    sequences = None
+
+    # FIXME: Ugly fix: Must check that sequence_file key exists
+    # FIXME: Sending malformed or missing form keys should not cause a crash
+    # sequences_file = form["sequences_file"]
+    if form_values.has_key("sequences_file") :
+        sequences = form_values.getvalue("sequences_file") 
+        assert type(sequences) == str
+
+    if not sequences or len(sequences)  ==0:
+        sequences = form["sequences"].get_value()
+    
+    if not sequences or len(sequences)  ==0:
+        errors.append( ("sequences", "Please enter a multiple-sequence alignment in the box above, or select a file to upload."))
+  
+
+
+    # If we have uncovered errors or we want the chance to edit the logo 
+    # ("cmd_edit" command from examples page) then we return the form now.
+    # We do not proceed to the time consuming logo creation step unless
+    # required by a 'create' or 'validate' command, and no errors have been
+    # found yet.
+    if form_values.has_key("cmd_edit") or errors :
+        send_form(controls, errors, htdocs_directory)
+        return    
+                
+  
+      
+    
+    # We write the logo into a local buffer so that we can catch and
+    # handle any errors. Once the "Content-Type:" header has been sent
+    # we can't send any useful feedback
+    logo = StringIO()
+    try :
+        comp = form["composition"].get_value()
+        percentCG = form["percentCG"].get_value()
+        ignore_lower_case = form_values.has_key("ignore_lower_case") 
+        seqs = weblogolib.read_seq_data(StringIO( sequences), 
+                                        alphabet=logooptions.alphabet,
+                                        ignore_lower_case=ignore_lower_case
+                                        )
+        if comp=='percentCG': comp = str(percentCG/100)
+        prior = weblogolib.parse_prior(comp, seqs.alphabet)
+        data = weblogolib.LogoData.from_seqs(seqs, prior) 
+        logoformat =  weblogolib.LogoFormat(data, logooptions)
+        format = form["format"].value        
+        weblogolib.formatters[format](data, logoformat, logo)            
+    except ValueError, err :
+        errors.append( err.args )
+    except IOError, err :
+        errors.append( err.args)
+    except RuntimeError, err :
+        errors.append( err.args )
+              
+    if form_values.has_key("cmd_validate") or errors :
+        send_form(controls, errors, htdocs_directory) 
+        return    
+ 
+ 
+    #
+    #  RETURN LOGO OVER HTTP
+    #
+
+    print "Content-Type:", mime_type[format]
+    # Content-Disposition: inline       Open logo in browser window
+    # Content-Disposition: attachment   Download logo  
+    if form_values.has_key("download") :
+        print 'Content-Disposition: attachment; ' \
+            'filename="logo.%s"' % extension[format] 
+    else :       
+        print 'Content-Disposition: inline; ' \
+            'filename="logo.%s"' % extension[format]        
+    
+        
+    # Seperate header from data
+    print 
+        
+    # Finally, and at last, send the logo.
+    print logo.getvalue()        
+
+  
+def send_form(controls, errors=[], htdocs_directory=None) :
+    if htdocs_directory is None :
+        htdocs_directory = os.path.join(
+            os.path.dirname(__file__, "htdocs") )
+
+    subsitutions = {}
+    subsitutions["version"] = weblogolib.release_description 
+    
+    for c in controls :
+        if c.options :
+            for opt in c.options :
+                subsitutions[opt.replace('/','_')] = ''
+            subsitutions[c.value.replace('/','_')] = 'selected'
+        else :
+            value = c.value
+            if value == None : value = 'auto'
+            if value=='true':
+                subsitutions[c.name] = 'checked'
+            elif type(value)==bool :
+                if value :
+                    subsitutions[c.name] = 'checked'
+                else :
+                    subsitutions[c.name] = ''
+            else :
+                subsitutions[c.name] = str(value)
+        subsitutions[c.name+'_err']  = ''
+            
+    if errors :
+        print >>sys.stderr, errors
+        error_message = []
+        for e in errors :
+            if type(e) is str :
+                msg = e
+            elif len(e)==2:
+                subsitutions[e[0]+"_err"] = "class='error'"    
+                msg = e[1]
+            else :
+                msg = e[0]
+
+
+            error_message +=  "ERROR: " 
+            error_message +=  msg
+            error_message += ' <br />'
+            
+        error_message += \
+            "<input style='float:right; font-size:small' type='submit' name='cmd_validate' value='Clear Error' /> "
+        subsitutions["error_message"] = ''.join(error_message)
+    else :
+        subsitutions["error_message"] = ""
+    
+        
+    template = resource_string("create_html_template.html", htdocs_directory)
+    html = Template(template).safe_substitute(subsitutions) #FIXME
+
+    print "Content-Type: text/html\n\n"
+    print html
+
+    # DEBUG
+    # keys = subsitutions.keys()
+    # keys.sort()
+    # for k in keys :
+    #    print k,"=", subsitutions[k], " <br />" 
+
+    #for k in controls :
+    #    print k.name,"=", k.get_value(), " <br />" 
+
+
+
+if __name__=="__main__" :
+    main()
+
+
+
+