comparison weblogolib/_cgi.py @ 4:4d47ab2b7bcc

Uploaded
author davidmurphy
date Fri, 13 Jan 2012 07:18:19 -0500
parents c55bdc2fb9fa
children
comparison
equal deleted inserted replaced
3:09d2dac9ef73 4:4d47ab2b7bcc
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 #print "Content-Type: text/html\n\n"
44 #print "HELLO WORLD"
45 #print __name__
46
47 from StringIO import StringIO
48 from color import *
49 from colorscheme import ColorScheme, ColorGroup
50
51 import weblogolib
52 from corebio.utils import *
53 from corebio._future import Template
54
55
56 # TODO: Check units
57
58 # TODO: In WebLogo2: why slash create.cgi? I think this was a workaround
59 # for some browser quirk
60 #<form method="post" action="/create.cgi" enctype="multipart/form-data">
61
62 def resource_string(resource, basefilename) :
63 import os
64 fn = os.path.join(os.path.dirname(basefilename), resource)
65 return open( fn ).read()
66
67 mime_type = {
68 'eps': 'application/postscript',
69 'pdf': 'application/pdf',
70 'png': 'image/png',
71 'png_print': 'image/png',
72 'txt' : 'text/plain',
73 'jpeg' : 'image/jpeg',
74 }
75
76 extension = {
77 'eps': 'eps',
78 'pdf': 'pdf',
79 'png': 'png',
80 'png_print': 'png',
81 'txt' : 'txt',
82 'jpeg' : 'png'
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', 'txt'] ,
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( 'size','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( 'altype', False , truth),
188 Field( 'logo_title', logooptions.logo_title ),
189 Field( 'logo_label', logooptions.logo_label ),
190 Field( 'show_xaxis', False, truth),
191 Field( 'xaxis_label', logooptions.xaxis_label ),
192 Field( 'show_yaxis', False, truth),
193 Field( 'yaxis_label', logooptions.yaxis_label, string_or_none ),
194 Field( 'yaxis_scale', logooptions.yaxis_scale , float_or_none,
195 errmsg="The yaxis scale must be a positive number." ),
196 Field( 'yaxis_tic_interval', logooptions.yaxis_tic_interval ,
197 float_or_none),
198 Field( 'show_ends', False, truth),
199 Field( 'show_fineprint', False , truth),
200 Field( 'color_scheme', 'color_auto', color_schemes.get,
201 options=color_schemes.keys() ,
202 errmsg = 'Unknown color scheme'),
203 Field( 'color0', ''),
204 Field( 'symbols0', ''),
205 Field( 'desc0', ''),
206 Field( 'color1', ''),
207 Field( 'symbols1', ''),
208 Field( 'desc1', ''),
209 Field( 'color2', ''),
210 Field( 'symbols2', ''),
211 Field( 'desc2', ''),
212 Field( 'color3', ''),
213 Field( 'symbols3', ''),
214 Field( 'desc3', ''),
215 Field( 'color4', ''),
216 Field( 'symbols4', ''),
217 Field( 'desc4', ''),
218 Field( 'ignore_lower_case', False, truth),
219 Field( 'altype', False, truth),
220 Field( 'scale_width', False, truth),
221 ]
222
223 form = {}
224 for c in controls :
225 form[c.name] = c
226
227
228 form_values = cgilib.FieldStorage()
229
230 # Send default form?
231 if len(form_values) ==0 or form_values.has_key("cmd_reset"):
232 # Load default truth values now.
233 form['show_errorbars'].value = logooptions.show_errorbars
234 form['show_xaxis'].value = logooptions.show_xaxis
235 form['show_yaxis'].value = logooptions.show_yaxis
236 form['show_ends'].value = logooptions.show_ends
237 form['show_fineprint'].value = logooptions.show_fineprint
238 form['scale_width'].value = logooptions.scale_width
239 form['altype'].value = logooptions.altype
240
241 send_form(controls, htdocs_directory = htdocs_directory)
242 return
243
244 # Get form content
245 for c in controls :
246 c.value = form_values.getfirst( c.name, c.default)
247
248
249 options_from_form = ['format', 'stacks_per_line', 'size',
250 'alphabet', 'unit_name', 'first_index', 'logo_start','logo_end',
251 'composition',
252 'show_errorbars', 'logo_title', 'logo_label', 'show_xaxis',
253 'xaxis_label',
254 'show_yaxis', 'yaxis_label', 'yaxis_scale', 'yaxis_tic_interval',
255 'show_ends', 'show_fineprint', 'scale_width','altype']
256
257 errors = []
258 for optname in options_from_form :
259 try :
260 value = form[optname].get_value()
261 if value!=None : setattr(logooptions, optname, value)
262 except ValueError, err :
263 errors.append(err.args)
264
265 #check if using codons or not.
266 if logooptions.altype!=True:
267 weblogolib.altype = ""
268 print >> sys.stderr,logooptions.altype
269 print >> sys.stderr, "--nn-"
270
271 # Construct custom color scheme
272 custom = ColorScheme()
273 for i in range(0,5) :
274 color = form["color%d"%i].get_value()
275 symbols = form["symbols%d"%i].get_value()
276 desc = form["desc%d"%i].get_value()
277
278 if color :
279 try :
280 custom.groups.append(weblogolib.ColorGroup(symbols, color, desc))
281 except ValueError, e :
282 errors.append( ('color%d'%i, "Invalid color: %s" % color) )
283
284 if form["color_scheme"].value == 'color_custom' :
285 logooptions.color_scheme = custom
286 else :
287 try :
288 logooptions.color_scheme = form["color_scheme"].get_value()
289 except ValueError, err :
290 errors.append(err.args)
291
292 sequences = None
293
294 # FIXME: Ugly fix: Must check that sequence_file key exists
295 # FIXME: Sending malformed or missing form keys should not cause a crash
296 # sequences_file = form["sequences_file"]
297 if form_values.has_key("sequences_file") :
298 sequences = form_values.getvalue("sequences_file")
299 assert type(sequences) == str
300
301 if not sequences or len(sequences) ==0:
302 sequences = form["sequences"].get_value()
303
304 if not sequences or len(sequences) ==0:
305 errors.append( ("sequences", "Please enter a multiple-sequence alignment in the box above, or select a file to upload."))
306
307
308
309 # If we have uncovered errors or we want the chance to edit the logo
310 # ("cmd_edit" command from examples page) then we return the form now.
311 # We do not proceed to the time consuming logo creation step unless
312 # required by a 'create' or 'validate' command, and no errors have been
313 # found yet.
314 if form_values.has_key("cmd_edit") or errors :
315 send_form(controls, errors, htdocs_directory)
316 return
317
318
319
320
321 # We write the logo into a local buffer so that we can catch and
322 # handle any errors. Once the "Content-Type:" header has been sent
323 # we can't send any useful feedback
324 logo = StringIO()
325 try :
326 comp = form["composition"].get_value()
327 percentCG = form["percentCG"].get_value()
328 ignore_lower_case = form_values.has_key("ignore_lower_case")
329 seqs = weblogolib.read_seq_data(StringIO( sequences),
330 alphabet=logooptions.alphabet,
331 ignore_lower_case=ignore_lower_case
332 )
333 if comp=='percentCG': comp = str(percentCG/100)
334 prior = weblogolib.parse_prior(comp, seqs.alphabet)
335 data = weblogolib.LogoData.from_seqs(seqs, prior)
336 logoformat = weblogolib.LogoFormat(data, logooptions)
337 format = form["format"].value
338 weblogolib.formatters[format](data, logoformat, logo)
339 except ValueError, err :
340 errors.append( err.args )
341 except IOError, err :
342 errors.append( err.args)
343 except RuntimeError, err :
344 errors.append( err.args )
345
346 if form_values.has_key("cmd_validate") or errors :
347 send_form(controls, errors, htdocs_directory)
348 return
349
350
351 #
352 # RETURN LOGO OVER HTTP
353 #
354
355 print "Content-Type:", mime_type[format]
356 # Content-Disposition: inline Open logo in browser window
357 # Content-Disposition: attachment Download logo
358 if form_values.has_key("download") :
359 print 'Content-Disposition: attachment; ' \
360 'filename="logo.%s"' % extension[format]
361 else :
362 print 'Content-Disposition: inline; ' \
363 'filename="logo.%s"' % extension[format]
364
365
366 # Seperate header from data
367 print
368
369 # Finally, and at last, send the logo.
370 print logo.getvalue()
371
372
373 def send_form(controls, errors=[], htdocs_directory=None) :
374 if htdocs_directory is None :
375 htdocs_directory = os.path.join(
376 os.path.dirname(__file__, "htdocs") )
377
378 subsitutions = {}
379 subsitutions["version"] = weblogolib.release_description
380
381 for c in controls :
382 if c.options :
383 for opt in c.options :
384 subsitutions[opt.replace('/','_')] = ''
385 subsitutions[c.value.replace('/','_')] = 'selected'
386 else :
387 value = c.value
388 if value == None : value = 'auto'
389 if value=='true':
390 subsitutions[c.name] = 'checked'
391 elif type(value)==bool :
392 if value :
393 subsitutions[c.name] = 'checked'
394 else :
395 subsitutions[c.name] = ''
396 else :
397 subsitutions[c.name] = str(value)
398 subsitutions[c.name+'_err'] = ''
399
400 if errors :
401 print >>sys.stderr, errors
402 error_message = []
403 for e in errors :
404 if type(e) is str :
405 msg = e
406 elif len(e)==2:
407 subsitutions[e[0]+"_err"] = "class='error'"
408 msg = e[1]
409 else :
410 msg = e[0]
411
412
413 error_message += "ERROR: "
414 error_message += msg
415 error_message += ' <br />'
416
417 error_message += \
418 "<input style='float:right; font-size:small' type='submit' name='cmd_validate' value='Clear Error' /> "
419 subsitutions["error_message"] = ''.join(error_message)
420 else :
421 subsitutions["error_message"] = ""
422
423
424 template = resource_string("create_html_template.html", htdocs_directory)
425 html = Template(template).safe_substitute(subsitutions) #FIXME
426
427 print "Content-Type: text/html\n\n"
428 print html
429
430 # DEBUG
431 # keys = subsitutions.keys()
432 # keys.sort()
433 # for k in keys :
434 # print k,"=", subsitutions[k], " <br />"
435
436 #for k in controls :
437 # print k.name,"=", k.get_value(), " <br />"
438
439
440
441 if __name__=="__main__" :
442 main()
443
444
445
446