Package weblogolib :: Module _cgi

Source Code for Module weblogolib._cgi

  1  #!/usr/bin/env python 
  2   
  3  #  Copyright (c) 2003-2004 The Regents of the University of California. 
  4  #  Copyright (c) 2005 Gavin E. Crooks 
  5  #  Copyright (c) 2006, The Regents of the University of California, through  
  6  #  Lawrence Berkeley National Laboratory (subject to receipt of any required 
  7  #  approvals from the U.S. Dept. of Energy).  All rights reserved. 
  8   
  9  #  This software is distributed under the new BSD Open Source License. 
 10  #  <http://www.opensource.org/licenses/bsd-license.html> 
 11  # 
 12  #  Redistribution and use in source and binary forms, with or without  
 13  #  modification, are permitted provided that the following conditions are met:  
 14  # 
 15  #  (1) Redistributions of source code must retain the above copyright notice,  
 16  #  this list of conditions and the following disclaimer.  
 17  # 
 18  #  (2) Redistributions in binary form must reproduce the above copyright  
 19  #  notice, this list of conditions and the following disclaimer in the  
 20  #  documentation and or other materials provided with the distribution.  
 21  # 
 22  #  (3) Neither the name of the University of California, Lawrence Berkeley  
 23  #  National Laboratory, U.S. Dept. of Energy nor the names of its contributors  
 24  #  may be used to endorse or promote products derived from this software  
 25  #  without specific prior written permission.  
 26  # 
 27  #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  
 28  #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  
 29  #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  
 30  #  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE  
 31  #  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  
 32  #  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  
 33  #  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  
 34  #  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  
 35  #  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  
 36  #  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  
 37  #  POSSIBILITY OF SUCH DAMAGE.  
 38   
 39  import sys 
 40  import cgi as cgilib 
 41  import cgitb; cgitb.enable() 
 42   
 43  from StringIO import StringIO 
 44  from color import * 
 45  from colorscheme import ColorScheme, ColorGroup 
 46   
 47  import weblogolib 
 48  from corebio.utils import * 
 49  from string import Template 
 50   
 51   
 52  # TODO: Check units 
 53   
 54  # TODO: In WebLogo2: why slash create.cgi? I think this was a workaround 
 55  # for some browser quirk 
 56  #<form method="post" action="/create.cgi" enctype="multipart/form-data"> 
 57   
 58   
 59  # Should replace with corebio.utils?     
60 -def resource_string(resource, basefilename) :
61 import os 62 fn = os.path.join(os.path.dirname(basefilename), resource) 63 return open( fn ).read()
64 65 mime_type = { 66 'eps': 'application/postscript', 67 'pdf': 'application/pdf', 68 'svg': 'image/svg+xml', 69 'png': 'image/png', 70 'png_print': 'image/png', 71 'logodata' : 'text/plain', 72 'jpeg' : 'image/jpeg', 73 } 74 75 extension = { 76 'eps': 'eps', 77 'pdf': 'pdf', 78 'png': 'png', 79 'svg': 'svg', 80 'png_print': 'png', 81 'logodata' : 'txt', 82 'jpeg' : 'jpeg' 83 } 84 85 86 alphabets = { 87 'alphabet_auto': None, 88 'alphabet_protein': weblogolib.unambiguous_protein_alphabet, 89 'alphabet_rna': weblogolib.unambiguous_rna_alphabet, 90 'alphabet_dna': weblogolib.unambiguous_dna_alphabet} 91 92 color_schemes = {} 93 for k in weblogolib.std_color_schemes.keys(): 94 color_schemes[ 'color_'+k.replace(' ', '_')] = weblogolib.std_color_schemes[k] 95 96 97 composition = {'comp_none' : 'none', 98 'comp_auto' : 'auto', 99 'comp_equiprobable':'equiprobable', 100 'comp_CG': 'percentCG', 101 'comp_Celegans' : 'C. elegans', 102 'comp_Dmelanogaster' : 'D. melanogaster', 103 'comp_Ecoli' : 'E. coli', 104 'comp_Hsapiens': 'H. sapiens', 105 'comp_Mmusculus' : 'M. musculus', 106 'comp_Scerevisiae': 'S. cerevisiae' 107 } 108
109 -class Field(object) :
110 """ A representation of an HTML form field."""
111 - def __init__(self, name, default=None, conversion= None, options=None, errmsg="Illegal value.") :
112 self.name = name 113 self.default = default 114 self.value = default 115 self.conversion = conversion 116 self.options = options 117 self.errmsg = errmsg
118
119 - def get_value(self) :
120 if self.options : 121 if not self.value in self.options : 122 raise ValueError, (self.name, self.errmsg) 123 124 if self.conversion : 125 try : 126 return self.conversion(self.value) 127 except ValueError, e : 128 raise ValueError, (self.name, self.errmsg) 129 else: 130 return self.value
131 132
133 -def string_or_none(value) :
134 if value is None or value == 'auto': 135 return None 136 return str(value)
137
138 -def truth(value) :
139 if value== "true" : return True 140 return bool(value)
141
142 -def int_or_none(value) :
143 if value =='' or value is None or value == 'auto': 144 return None 145 return int(value)
146
147 -def float_or_none(value) :
148 if value =='' or value is None or value == 'auto': 149 return None 150 return float(value)
151 152
153 -def main(htdocs_directory = None) :
154 155 logooptions = weblogolib.LogoOptions() 156 157 # A list of form fields. 158 # The default for checkbox values must be False (irrespective of 159 # the default in logooptions) since a checked checkbox returns 'true' 160 # but an unchecked checkbox returns nothing. 161 controls = [ 162 Field( 'sequences', ''), 163 Field( 'format', 'png', weblogolib.formatters.get , 164 options=['png_print', 'png', 'jpeg', 'eps', 'pdf', 'svg', 'logodata'] , #TODO: Should copy list from __init__.formatters 165 errmsg="Unknown format option."), 166 Field( 'stacks_per_line', logooptions.stacks_per_line , int, 167 errmsg='Invalid number of stacks per line.'), 168 Field( 'stack_width','medium', weblogolib.std_sizes.get, 169 options=['small', 'medium', 'large'], errmsg='Invalid logo size.'), 170 Field( 'alphabet','alphabet_auto', alphabets.get, 171 options=['alphabet_auto', 'alphabet_protein', 'alphabet_dna', 172 'alphabet_rna'], 173 errmsg="Unknown sequence type."), 174 Field( 'unit_name', 'bits', 175 options=[ 'probability', 'bits', 'nats', 'kT', 'kJ/mol', 176 'kcal/mol']), 177 Field( 'first_index', 1, int_or_none), 178 Field( 'logo_start', '', int_or_none), 179 Field( 'logo_end', '', int_or_none), 180 Field( 'composition', 'comp_auto', composition.get, 181 options=['comp_none','comp_auto','comp_equiprobable','comp_CG', 182 'comp_Celegans','comp_Dmelanogaster','comp_Ecoli', 183 'comp_Hsapiens','comp_Mmusculus','comp_Scerevisiae'], 184 errmsg= "Illegal sequence composition."), 185 Field( 'percentCG', '', float_or_none, errmsg="Invalid CG percentage."), 186 Field( 'show_errorbars', False , truth), 187 Field( 'logo_title', logooptions.logo_title ), 188 Field( 'logo_label', logooptions.logo_label ), 189 Field( 'show_xaxis', False, truth), 190 Field( 'xaxis_label', logooptions.xaxis_label ), 191 Field( 'show_yaxis', False, truth), 192 Field( 'yaxis_label', logooptions.yaxis_label, string_or_none ), 193 Field( 'yaxis_scale', logooptions.yaxis_scale , float_or_none, 194 errmsg="The yaxis scale must be a positive number." ), 195 Field( 'yaxis_tic_interval', logooptions.yaxis_tic_interval , 196 float_or_none), 197 Field( 'show_ends', False, truth), 198 Field( 'show_fineprint', False , truth), 199 Field( 'color_scheme', 'color_auto', color_schemes.get, 200 options=color_schemes.keys() , 201 errmsg = 'Unknown color scheme'), 202 Field( 'color0', ''), 203 Field( 'symbols0', ''), 204 Field( 'desc0', ''), 205 Field( 'color1', ''), 206 Field( 'symbols1', ''), 207 Field( 'desc1', ''), 208 Field( 'color2', ''), 209 Field( 'symbols2', ''), 210 Field( 'desc2', ''), 211 Field( 'color3', ''), 212 Field( 'symbols3', ''), 213 Field( 'desc3', ''), 214 Field( 'color4', ''), 215 Field( 'symbols4', ''), 216 Field( 'desc4', ''), 217 Field( 'ignore_lower_case', False, truth), 218 Field( 'scale_width', False, truth), 219 ] 220 221 form = {} 222 for c in controls : 223 form[c.name] = c 224 225 226 form_values = cgilib.FieldStorage() 227 228 # Send default form? 229 if len(form_values) ==0 or form_values.has_key("cmd_reset"): 230 # Load default truth values now. 231 form['show_errorbars'].value = logooptions.show_errorbars 232 form['show_xaxis'].value = logooptions.show_xaxis 233 form['show_yaxis'].value = logooptions.show_yaxis 234 form['show_ends'].value = logooptions.show_ends 235 form['show_fineprint'].value = logooptions.show_fineprint 236 form['scale_width'].value = logooptions.scale_width 237 238 send_form(controls, htdocs_directory = htdocs_directory) 239 return 240 241 # Get form content 242 for c in controls : 243 c.value = form_values.getfirst( c.name, c.default) 244 245 246 options_from_form = ['format', 'stacks_per_line', 'stack_width', 247 'alphabet', 'unit_name', 'first_index', 'logo_start','logo_end', 248 'composition', 249 'show_errorbars', 'logo_title', 'logo_label', 'show_xaxis', 250 'xaxis_label', 251 'show_yaxis', 'yaxis_label', 'yaxis_scale', 'yaxis_tic_interval', 252 'show_ends', 'show_fineprint', 'scale_width'] 253 254 255 errors = [] 256 for optname in options_from_form : 257 try : 258 value = form[optname].get_value() 259 if value!=None : setattr(logooptions, optname, value) 260 except ValueError, err : 261 errors.append(err.args) 262 263 264 # Construct custom color scheme 265 custom = ColorScheme() 266 for i in range(0,5) : 267 color = form["color%d"%i].get_value() 268 symbols = form["symbols%d"%i].get_value() 269 desc = form["desc%d"%i].get_value() 270 271 if color : 272 try : 273 custom.groups.append(weblogolib.ColorGroup(symbols, color, desc)) 274 except ValueError, e : 275 errors.append( ('color%d'%i, "Invalid color: %s" % color) ) 276 277 if form["color_scheme"].value == 'color_custom' : 278 logooptions.color_scheme = custom 279 else : 280 try : 281 logooptions.color_scheme = form["color_scheme"].get_value() 282 except ValueError, err : 283 errors.append(err.args) 284 285 sequences = None 286 287 # FIXME: Ugly fix: Must check that sequence_file key exists 288 # FIXME: Sending malformed or missing form keys should not cause a crash 289 # sequences_file = form["sequences_file"] 290 if form_values.has_key("sequences_file") : 291 sequences = form_values.getvalue("sequences_file") 292 assert type(sequences) == str 293 294 if not sequences or len(sequences) ==0: 295 sequences = form["sequences"].get_value() 296 297 if not sequences or len(sequences) ==0: 298 errors.append( ("sequences", "Please enter a multiple-sequence alignment in the box above, or select a file to upload.")) 299 300 301 302 # If we have uncovered errors or we want the chance to edit the logo 303 # ("cmd_edit" command from examples page) then we return the form now. 304 # We do not proceed to the time consuming logo creation step unless 305 # required by a 'create' or 'validate' command, and no errors have been 306 # found yet. 307 if form_values.has_key("cmd_edit") or errors : 308 send_form(controls, errors, htdocs_directory) 309 return 310 311 # We write the logo into a local buffer so that we can catch and 312 # handle any errors. Once the "Content-Type:" header has been sent 313 # we can't send any useful feedback 314 logo = StringIO() 315 try : 316 comp = form["composition"].get_value() 317 percentCG = form["percentCG"].get_value() 318 ignore_lower_case = form_values.has_key("ignore_lower_case") 319 if comp=='percentCG': comp = str(percentCG/100) 320 321 from corebio.matrix import Motif 322 323 try: 324 # Try reading data in transfac format first. 325 # TODO Refactor this code 326 motif = Motif.read_transfac(StringIO( sequences), alphabet=logooptions.alphabet) 327 prior = weblogolib.parse_prior( comp,motif.alphabet) 328 data = weblogolib.LogoData.from_counts(motif.alphabet, motif, prior) 329 except ValueError, motif_err : 330 seqs = weblogolib.read_seq_data(StringIO( sequences), 331 alphabet=logooptions.alphabet, 332 ignore_lower_case=ignore_lower_case 333 ) 334 prior = weblogolib.parse_prior(comp, seqs.alphabet) 335 data = weblogolib.LogoData.from_seqs(seqs, prior) 336 337 logoformat = weblogolib.LogoFormat(data, logooptions) 338 format = form["format"].value 339 weblogolib.formatters[format](data, logoformat, logo) 340 except ValueError, err : 341 errors.append( err.args ) 342 except IOError, err : 343 errors.append( err.args) 344 except RuntimeError, err : 345 errors.append( err.args ) 346 347 if form_values.has_key("cmd_validate") or errors : 348 send_form(controls, errors, htdocs_directory) 349 return 350 351 352 # 353 # RETURN LOGO OVER HTTP 354 # 355 356 print "Content-Type:", mime_type[format] 357 # Content-Disposition: inline Open logo in browser window 358 # Content-Disposition: attachment Download logo 359 if form_values.has_key("download") : 360 print 'Content-Disposition: attachment; ' \ 361 'filename="logo.%s"' % extension[format] 362 else : 363 print 'Content-Disposition: inline; ' \ 364 'filename="logo.%s"' % extension[format] 365 366 367 # Separate header from data 368 print 369 370 # Finally, and at last, send the logo. 371 print logo.getvalue()
372 373
374 -def send_form(controls, errors=[], htdocs_directory=None) :
375 if htdocs_directory is None : 376 htdocs_directory = os.path.join( 377 os.path.dirname(__file__, "htdocs") ) 378 379 substitutions = {} 380 substitutions["version"] = weblogolib.release_description 381 # Bug fix. Not sure why this default substitution isn't added automatically like everything else 382 substitutions['color_custom'] = '' 383 for c in controls : 384 if c.options : 385 for opt in c.options : 386 substitutions[opt.replace('/','_')] = '' 387 substitutions[c.value.replace('/','_')] = 'selected' 388 else : 389 value = c.value 390 if value == None : value = 'auto' 391 if value=='true': 392 substitutions[c.name] = 'checked' 393 elif type(value)==bool : 394 if value : 395 substitutions[c.name] = 'checked' 396 else : 397 substitutions[c.name] = '' 398 else : 399 substitutions[c.name] = str(value) 400 substitutions[c.name+'_err'] = '' 401 substitutions['logo_range_err'] = '' 402 403 # Disable graphics options if necessary auxiliary programs are not installed. 404 try: 405 command = find_command('gs') 406 except EnvironmentError: 407 try: 408 command = find_command('gswin32c.exe') 409 except EnvironmentError: 410 substitutions['png_print'] = 'disabled="disabled"' 411 substitutions['png'] = 'disabled="disabled"' 412 substitutions['jpeg'] = 'disabled="disabled"' 413 substitutions['pdf'] = 'disabled="disabled"' 414 substitutions['svg'] = 'disabled="disabled"' 415 substitutions['eps'] = 'selected="selected"' 416 try: 417 command = find_command('pdf2svg') 418 except EnvironmentError: 419 substitutions['svg'] = 'disabled="disabled"' 420 421 422 if errors : 423 print >>sys.stderr, errors 424 error_message = [] 425 for e in errors : 426 if type(e) is str : 427 msg = e 428 elif len(e)==2: 429 substitutions[e[0]+"_err"] = "class='error'" 430 msg = e[1] 431 else : 432 msg = e[0] 433 434 435 error_message += "ERROR: " 436 error_message += msg 437 error_message += ' <br />' 438 439 error_message += \ 440 "<input style='float:right; font-size:small' type='submit' name='cmd_validate' value='Clear Error' /> " 441 substitutions["error_message"] = ''.join(error_message) 442 else : 443 substitutions["error_message"] = "" 444 445 446 template = resource_string( "create_html_template.html", htdocs_directory) 447 html = Template(template).safe_substitute(substitutions) #FIXME 448 449 print "Content-Type: text/html\n\n" 450 print html
451 452 # DEBUG 453 #keys = substitutions.keys() 454 #keys.sort() 455 #for k in keys : 456 # print k,"=", substitutions[k], " <br />" 457 458 #print " <br />" 459 #print " <br />" 460 #for k in controls : 461 # print k.name,"=", k.get_value(), " <br />" 462 463 464 465 if __name__=="__main__" : 466 import _cli 467 _cli.main() 468