Mercurial > repos > davidmurphy > codonlogo
comparison weblogolib/_cgi.py @ 0:c55bdc2fb9fa
Uploaded
author | davidmurphy |
---|---|
date | Thu, 27 Oct 2011 12:09:09 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c55bdc2fb9fa |
---|---|
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 |