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