From 70c603e29e43ea08d871791c4110118ecd70ac8e Mon Sep 17 00:00:00 2001 From: Martijn Vermaat <martijn@vermaat.name> Date: Wed, 26 Jan 2011 09:03:36 +0000 Subject: [PATCH] templates/menu.html: - Link to bugtracker is now absolute. - Header for external links is no longer a link. src/Modules/File.py: - Bugfix for previous commit (transparent support for different types of line endings in batch input files). src/tests/test_wsgi.py: - Added batch tests with big input files. Added batch tests for different types of line endings in input files. git-svn-id: https://humgenprojects.lumc.nl/svn/mutalyzer/trunk@166 eb6bd6ab-9ccd-42b9-aceb-e2899b4a52f1 --- src/Modules/File.py | 42 ++++++++++-- src/tests/test_wsgi.py | 126 ++++++++++++++++++++++++++++++----- src/webservice.py | 10 +-- templates/base/css/style.css | 4 ++ templates/menu.html | 11 +-- 5 files changed, 158 insertions(+), 35 deletions(-) diff --git a/src/Modules/File.py b/src/Modules/File.py index ab9b37fe..79cb81af 100644 --- a/src/Modules/File.py +++ b/src/Modules/File.py @@ -24,6 +24,7 @@ import zipfile # ZipFile() import xml.dom.minidom # parseString() import os # remove() import types # UnicodeType +from cStringIO import StringIO from Modules import Misc @@ -137,11 +138,41 @@ class File() : # # The fix is to get the handle's file descriptor and create a new # handle for it, using 'U' mode. - handle = os.fdopen(handle.fileno(), 'rU') + # + # However, sometimes our handler has no .fileno(), for example when + # the input file is quite small (< 1kb). In that case, the cgi + # module seems to optimize things and use a StringIO for the file, + # which of course has no .fileno(). + # + # So our solution is: + # - We have .fileno(): Create a new handle, using 'U' mode. + # - We have no .fileno(): Replace all newlines by \n (in-memory) + # and wrap the result in a new StringIO. + + if hasattr(handle, 'fileno'): + # Todo: We get the following error in our logs (exact origin + # unknown): + # + # close failed in file object destructor: + # IOError: [Errno 9] Bad file descriptor + # + # I am unable to find the reason for this. Everything seems to + # be working though. + new_handle = os.fdopen(handle.fileno(), 'rU') + elif hasattr(handle, 'getvalue'): + data = handle.getvalue() + data = data.replace('\r\n', '\n').replace('\r', '\n') + new_handle = StringIO(data) + else: + self.__output.addMessage(__file__, 4, "EBPARSE", + "Fatal error parsing input file, please" + " report this as a bug including the" + " input file.") + return None # I don't think the .seek(0) is needed now we created a new handle - handle.seek(0) - buf = handle.read(self.__config.bufSize) + new_handle.seek(0) + buf = new_handle.read(self.__config.bufSize) # Default dialect dialect = 'excel' @@ -162,13 +193,14 @@ class File() : # if dialect.delimiter == ":": # dialect.delimiter = "\t" - handle.seek(0) - reader = csv.reader(handle, dialect) + new_handle.seek(0) + reader = csv.reader(new_handle, dialect) ret = [] for i in reader : ret.append(i) + new_handle.close() return ret #__parseCsvFile diff --git a/src/tests/test_wsgi.py b/src/tests/test_wsgi.py index 759a1378..559cd0c9 100755 --- a/src/tests/test_wsgi.py +++ b/src/tests/test_wsgi.py @@ -12,6 +12,7 @@ I just installed webtest by 'easy_install webtest'. @todo: Tests for /upload, /getGS, and /Variant_info. """ +#import logging; logging.basicConfig() import re import time import unittest @@ -54,8 +55,7 @@ class TestWSGI(unittest.TestCase): """ Test all links in the main menu. """ - ignore = ['external', # Todo: should not be a link - 'bugtracker'] + ignore = [] # This could contain relative links we want to skip r = self.app.get('/') for link in r.lxml.cssselect('#menu a'): href = link.get('href') @@ -202,7 +202,7 @@ class TestWSGI(unittest.TestCase): r = form.submit() r.mustcontain('NM_003002.2:c.204C>T') - def _batch(self, batch_type='NameChecker', arg1=None, variants=[], + def _batch(self, batch_type='NameChecker', arg1=None, file="", size=0, header=''): """ Submit a batch form. @@ -210,7 +210,8 @@ class TestWSGI(unittest.TestCase): @kwarg batch_type: Type of batch job to test. One of NameChecker, SyntaxChecker, PositionConverter. @kwarg arg1: Optional extra argument for the batch job. - @kwarg variants: Variants to use as input for the batch job. + @kwarg file: String with variants to use as input for the batch job. + @kwarg size: Number of variants in input. @kwarg header: Message that must be found in the batch job result. """ r = self.app.get('/batch') @@ -220,53 +221,146 @@ class TestWSGI(unittest.TestCase): form['batchType'] = batch_type form['batchEmail'] = 'm.vermaat.hg@lumc.nl' form.set('batchFile', ('test_%s.txt' % batch_type, - '\n'.join(variants))) + file)) r = form.submit() id = r.lxml.cssselect('#jobID')[0].get('value') max_tries = 60 for i in range(max_tries): - r = self.app.get('/progress?jobID=' + id + '&totalJobs=3&ajax=1') + r = self.app.get('/progress?jobID=' + id + '&totalJobs=' + str(size) + '&ajax=1') self.assertEqual(r.content_type, 'text/plain') #print '%s: %s' % (batch_type, r.body) if r.body == 'OK': break self.assertTrue(re.match('[0-9]+', r.body)) - time.sleep(5) + time.sleep(2) self.assertEqual(r.body, 'OK') r = self.app.get('/Results_' + id + '.txt') self.assertEqual(r.content_type, 'text/plain') r.mustcontain(header) - self.assertTrue(len(r.body.strip().split('\n')) == len(variants) + 1) + self.assertTrue(len(r.body.strip().split('\n')) == size + 1) def test_batch_namechecker(self): """ Submit the batch name checker form. """ + variants=['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] self._batch('NameChecker', - variants=['AB026906.1(SDHD):g.7872G>T', - 'NM_003002.1:c.3_4insG', - 'AL449423.14(CDKN2A_v002):c.5_400del'], + file='\n'.join(variants), + size=len(variants), header='Input\tErrors | Messages') def test_batch_syntaxchecker(self): """ Submit the batch syntax checker form. """ + variants = ['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] self._batch('SyntaxChecker', - variants = ['AB026906.1(SDHD):g.7872G>T', - 'NM_003002.1:c.3_4insG', - 'AL449423.14(CDKN2A_v002):c.5_400del'], + file='\n'.join(variants), + size=len(variants), header='Input\tStatus') def test_batch_positionconverter(self): """ Submit the batch position converter form. """ + variants = ['NM_003002.2:c.204C>T', + 'NC_000011.9:g.111959625C>T'] self._batch('PositionConverter', arg1='hg19', - variants = ['NM_003002.2:c.204C>T', - 'NC_000011.9:g.111959625C>T'], + file='\n'.join(variants), + size=len(variants), header='Input Variant') + def test_batch_syntaxchecker_newlines_unix(self): + """ + Submit the batch syntax checker form with unix line endings. + """ + variants = ['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] + self._batch('SyntaxChecker', + file='\n'.join(variants), + size=len(variants), + header='Input\tStatus') + + def test_batch_syntaxchecker_newlines_mac(self): + """ + Submit the batch syntax checker form with mac line endings. + """ + variants = ['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] + self._batch('SyntaxChecker', + file='\r'.join(variants), + size=len(variants), + header='Input\tStatus') + + def test_batch_syntaxchecker_newlines_windows(self): + """ + Submit the batch syntax checker form with windows line endings. + """ + variants = ['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] + self._batch('SyntaxChecker', + file='\r\n'.join(variants), + size=len(variants), + header='Input\tStatus') + + def test_batch_syntaxchecker_newlines_big_unix(self): + """ + Submit the batch syntax checker form with unix line ending + styles and a big input file. + """ + samples = ['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] + variants = [] + # Create 240 variants out of 3 samples + for i in range(80): + variants.extend(samples) + self._batch('SyntaxChecker', + file='\n'.join(variants), + size=len(variants), + header='Input\tStatus') + + def test_batch_syntaxchecker_newlines_big_mac(self): + """ + Submit the batch syntax checker form with mac line ending + styles and a big input file. + """ + samples = ['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] + variants = [] + # Create 240 variants out of 3 samples + for i in range(80): + variants.extend(samples) + self._batch('SyntaxChecker', + file='\r'.join(variants), + size=len(variants), + header='Input\tStatus') + + def test_batch_syntaxchecker_newlines_big_windows(self): + """ + Submit the batch syntax checker form with windows line ending + styles and a big input file. + """ + samples = ['AB026906.1(SDHD):g.7872G>T', + 'NM_003002.1:c.3_4insG', + 'AL449423.14(CDKN2A_v002):c.5_400del'] + variants = [] + # Create 240 variants out of 3 samples + for i in range(80): + variants.extend(samples) + self._batch('SyntaxChecker', + file='\r\n'.join(variants), + size=len(variants), + header='Input\tStatus') + def test_download_py(self): """ Download a Python example client for the webservice. diff --git a/src/webservice.py b/src/webservice.py index 445eaa49..bde27999 100644 --- a/src/webservice.py +++ b/src/webservice.py @@ -149,7 +149,7 @@ class MutalyzerService(DefinitionBase) : #if #__checkVariant - @soap(Mandatory.String, Mandatory.String, Mandatory.Integer, + @soap(Mandatory.String, Mandatory.String, Mandatory.Integer, _returns = List.String) def getTranscripts(self, build, chrom, pos) : """Get all the transcripts that overlap with a chromosomal position. @@ -360,7 +360,7 @@ class MutalyzerService(DefinitionBase) : return result #mappingInfo - @soap(Mandatory.String, Mandatory.String, Mandatory.String, + @soap(Mandatory.String, Mandatory.String, Mandatory.String, _returns = Transcript) def transcriptInfo(self, LOVD_ver, build, accNo) : """Search for an NM number in the MySQL database, if the version @@ -640,10 +640,10 @@ class MutalyzerService(DefinitionBase) : @soap(Mandatory.String, Mandatory.String, Mandatory.Integer, Mandatory.Integer, _returns = Mandatory.String) - def sliceChromosomeByGene(self, geneSymbol, organism, upStream, + def sliceChromosomeByGene(self, geneSymbol, organism, upStream, downStream) : """ - Todo: documentation, error handling, argument checking. + Todo: documentation, error handling, argument checking, tests. """ C = Config.Config() @@ -654,7 +654,7 @@ class MutalyzerService(DefinitionBase) : O.addMessage(__file__, -1, "INFO", "Received request sliceChromosomeByGene(%s, %s, %s, %s)" % ( geneSymbol, organism, upStream, downStream)) - + UD = retriever.retrievegene(geneSymbol, organism, upStream, downStream) O.addMessage(__file__, -1, "INFO", diff --git a/templates/base/css/style.css b/templates/base/css/style.css index e987b826..ddc3c662 100644 --- a/templates/base/css/style.css +++ b/templates/base/css/style.css @@ -263,6 +263,10 @@ ul { z-index : 10; } +#menu td { + color : #002E65; +} + .Interface { color : #FFCC00; background-color : #333333; diff --git a/templates/menu.html b/templates/menu.html index 4b48bc7b..3f1c02c3 100644 --- a/templates/menu.html +++ b/templates/menu.html @@ -391,7 +391,7 @@ <td colspan="3"> <a id="page_bugTrack" onclick="swapActive('bugTrack');" - href="bugtracker" + href="http://www.mutalyzer.nl/2.0/bugtracker" onmouseover="navAct('base/images/bullitlicht1.gif', 'bugTrack');" onmouseout="navDeAct('base/images/bullitdonker.gif', @@ -407,14 +407,7 @@ <img src="base/images/bullitdonker.gif" id="b_external"> </td> <td colspan="3"> - <a id="page_external" - onclick="swapActive('external');" - href="external" - onmouseover="navAct('base/images/bullitlicht1.gif', - 'external');" - onmouseout="navDeAct('base/images/bullitdonker.gif', - 'external');" - class="vertnavsub">External Links</a> + External Links </td> </tr> -- GitLab