0
|
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
|