Add support for scanning for results in the IPAR directory.
authorDiane Trout <diane@caltech.edu>
Thu, 30 Oct 2008 22:03:12 +0000 (22:03 +0000)
committerDiane Trout <diane@caltech.edu>
Thu, 30 Oct 2008 22:03:12 +0000 (22:03 +0000)
The field that was the firecrest class in PipelineRun is now the
"image_analysis" field and can be either firecrest or ipar.

I also extracted some of the common functions out of the runfolder test
modules and added them to a seperate "simulate_runfolder" module.

htsworkflow/pipelines/gerald.py
htsworkflow/pipelines/ipar.py [new file with mode: 0644]
htsworkflow/pipelines/runfolder.py
htsworkflow/pipelines/test/__init__.py [new file with mode: 0644]
htsworkflow/pipelines/test/simulate_runfolder.py [new file with mode: 0644]
htsworkflow/pipelines/test/test_runfolder026.py
htsworkflow/pipelines/test/test_runfolder030.py
htsworkflow/pipelines/test/test_runfolder_ipar100.py [new file with mode: 0644]

index 109c6a811b86dd4b97fbea00e5aa1517a7eccb75..59608da12ed402e51e7b1caa8d7443e62c73e880 100644 (file)
@@ -33,7 +33,7 @@ class Gerald(object):
         def __init__(self, gerald, key):
             self._gerald = gerald
             self._key = key
-        
+
         def __get_attribute(self, xml_tag):
             subtree = self._gerald.tree.find('LaneSpecificRunParameters')
             container = subtree.find(xml_tag)
@@ -81,9 +81,9 @@ class Gerald(object):
             if self._keys is None:
                 tree = self._gerald.tree
                 analysis = tree.find('LaneSpecificRunParameters/ANALYSIS')
-                # according to the pipeline specs I think their fields 
+                # according to the pipeline specs I think their fields
                 # are sampleName_laneID, with sampleName defaulting to s
-                # since laneIDs are constant lets just try using 
+                # since laneIDs are constant lets just try using
                 # those consistently.
                 self._keys = [ x.tag.split('_')[1] for x in analysis]
             return self._keys
@@ -138,7 +138,7 @@ class Gerald(object):
         if self.tree is None or self.summary is None:
             return None
 
-        gerald = ElementTree.Element(Gerald.GERALD, 
+        gerald = ElementTree.Element(Gerald.GERALD,
                                      {'version': unicode(Gerald.XML_VERSION)})
         gerald.append(self.tree)
         gerald.append(self.summary.get_elements())
@@ -162,16 +162,18 @@ class Gerald(object):
                 self.eland_results = ELAND(xml=element)
             else:
                 logging.warn("Unrecognized tag %s" % (element.tag,))
-        
+
 
 def gerald(pathname):
     g = Gerald()
     g.pathname = pathname
     path, name = os.path.split(pathname)
+    logging.info("Parsing gerald config.xml")
     config_pathname = os.path.join(pathname, 'config.xml')
     g.tree = ElementTree.parse(config_pathname).getroot()
 
     # parse Summary.htm file
+    logging.info("Parsing Summary.htm")
     summary_pathname = os.path.join(pathname, 'Summary.htm')
     g.summary = Summary(summary_pathname)
     # parse eland files
@@ -213,7 +215,7 @@ def parse_mean_range_element(element):
     """
     Grab mean/deviation out of element
     """
-    return (tonumber(element.attrib['mean']), 
+    return (tonumber(element.attrib['mean']),
             tonumber(element.attrib['deviation']))
 
 def parse_summary_element(element):
@@ -238,7 +240,7 @@ class Summary(object):
         Mostly for the cluster number
         """
         LANE_RESULT_SUMMARY = 'LaneResultSummary'
-        TAGS = { 
+        TAGS = {
           'LaneYield': 'lane_yield',
           'Cluster': 'cluster', # Raw
           'ClusterPF': 'cluster_pass_filter',
@@ -249,7 +251,7 @@ class Summary(object):
           'AverageAlignmentScore': 'average_alignment_score',
           'PercentErrorRate': 'percent_error_rate'
         }
-                 
+
         def __init__(self, html=None, xml=None):
             self.lane = None
             self.lane_yield = None
@@ -299,7 +301,7 @@ class Summary(object):
 
         def get_elements(self):
             lane_result = ElementTree.Element(
-                            Summary.LaneResultSummary.LANE_RESULT_SUMMARY, 
+                            Summary.LaneResultSummary.LANE_RESULT_SUMMARY,
                             {'lane': self.lane})
             for tag, variable_name in Summary.LaneResultSummary.TAGS.items():
                 value = getattr(self, variable_name)
@@ -326,7 +328,7 @@ class Summary(object):
             for element in list(tree):
                 try:
                     variable_name = tags[element.tag]
-                    setattr(self, variable_name, 
+                    setattr(self, variable_name,
                             parse_summary_element(element))
                 except KeyError, e:
                     logging.warn('Unrecognized tag %s' % (element.tag,))
@@ -359,10 +361,10 @@ class Summary(object):
         flatten the children of a <tr>...</tr>
         """
         return [flatten(x) for x in row.getchildren() ]
-    
+
     def _parse_table(self, table):
         """
-        assumes the first line is the header of a table, 
+        assumes the first line is the header of a table,
         and that the remaining rows are data
         """
         rows = table.getchildren()
@@ -370,12 +372,12 @@ class Summary(object):
         for r in rows:
             data.append(self._flattened_row(r))
         return data
-    
+
     def _extract_named_tables(self, pathname):
         """
         extract all the 'named' tables from a Summary.htm file
         and return as a dictionary
-        
+
         Named tables are <h2>...</h2><table>...</table> pairs
         The contents of the h2 tag is considered to the name
         of the table.
@@ -420,7 +422,7 @@ class Summary(object):
             self.lane_results[lrs.lane] = lrs
 
     def get_elements(self):
-        summary = ElementTree.Element(Summary.SUMMARY, 
+        summary = ElementTree.Element(Summary.SUMMARY,
                                       {'version': unicode(Summary.XML_VERSION)})
         for lane in self.lane_results.values():
             summary.append(lane.get_elements())
@@ -446,6 +448,7 @@ class Summary(object):
 
 def build_genome_fasta_map(genome_dir):
     # build fasta to fasta file map
+    logging.info("Building genome map")
     genome = genome_dir.split(os.path.sep)[-1]
     fasta_map = {}
     for vld_file in glob(os.path.join(genome_dir, '*.vld')):
@@ -460,7 +463,7 @@ def build_genome_fasta_map(genome_dir):
         else:
             fasta_map[name] = os.path.join(genome, name)
     return fasta_map
-    
+
 class ElandLane(object):
     """
     Process an eland result file
@@ -487,7 +490,7 @@ class ElandLane(object):
         if genome_map is None:
             genome_map = {}
         self.genome_map = genome_map
-        
+
         if xml is not None:
             self.set_elements(xml)
 
@@ -502,10 +505,11 @@ class ElandLane(object):
         if os.stat(self.pathname)[stat.ST_SIZE] == 0:
             raise RuntimeError("Eland isn't done, try again later.")
 
+        logging.info("summarizing results for %s" % (self.pathname))
         reads = 0
         mapped_reads = {}
 
-        match_codes = {'NM':0, 'QC':0, 'RM':0, 
+        match_codes = {'NM':0, 'QC':0, 'RM':0,
                        'U0':0, 'U1':0, 'U2':0,
                        'R0':0, 'R1':0, 'R2':0,
                       }
@@ -566,8 +570,8 @@ class ElandLane(object):
     match_codes = property(_get_match_codes)
 
     def get_elements(self):
-        lane = ElementTree.Element(ElandLane.LANE, 
-                                   {'version': 
+        lane = ElementTree.Element(ElandLane.LANE,
+                                   {'version':
                                     unicode(ElandLane.XML_VERSION)})
         sample_tag = ElementTree.SubElement(lane, ElandLane.SAMPLE_NAME)
         sample_tag.text = self.sample_name
@@ -576,17 +580,17 @@ class ElandLane(object):
         genome_map = ElementTree.SubElement(lane, ElandLane.GENOME_MAP)
         for k, v in self.genome_map.items():
             item = ElementTree.SubElement(
-                genome_map, ElandLane.GENOME_ITEM, 
+                genome_map, ElandLane.GENOME_ITEM,
                 {'name':k, 'value':unicode(v)})
         mapped_reads = ElementTree.SubElement(lane, ElandLane.MAPPED_READS)
         for k, v in self.mapped_reads.items():
             item = ElementTree.SubElement(
-                mapped_reads, ElandLane.MAPPED_ITEM, 
+                mapped_reads, ElandLane.MAPPED_ITEM,
                 {'name':k, 'value':unicode(v)})
         match_codes = ElementTree.SubElement(lane, ElandLane.MATCH_CODES)
         for k, v in self.match_codes.items():
             item = ElementTree.SubElement(
-                match_codes, ElandLane.MATCH_ITEM, 
+                match_codes, ElandLane.MATCH_ITEM,
                 {'name':k, 'value':unicode(v)})
         reads = ElementTree.SubElement(lane, ElandLane.READS)
         reads.text = unicode(self.reads)
@@ -600,7 +604,7 @@ class ElandLane(object):
         # reset dictionaries
         self._mapped_reads = {}
         self._match_codes = {}
-        
+
         for element in tree:
             tag = element.tag.lower()
             if tag == ElandLane.SAMPLE_NAME.lower():
@@ -653,7 +657,7 @@ class ELAND(object):
     def __init__(self, xml=None):
         # we need information from the gerald config.xml
         self.results = {}
-        
+
         if xml is not None:
             self.set_elements(xml)
 
@@ -662,7 +666,7 @@ class ELAND(object):
 
     def keys(self):
         return self.results.keys()
-    
+
     def values(self):
         return self.results.values()
 
@@ -673,7 +677,7 @@ class ELAND(object):
         return self.results[key]
 
     def get_elements(self):
-        root = ElementTree.Element(ELAND.ELAND, 
+        root = ElementTree.Element(ELAND.ELAND,
                                    {'version': unicode(ELAND.XML_VERSION)})
         for lane_id, lane in self.results.items():
             eland_lane = lane.get_elements()
@@ -703,6 +707,7 @@ def eland(basedir, gerald=None, genome_maps=None):
         # but I needed to persist the sample_name/lane_id for
         # runfolder summary_report
         path, name = os.path.split(pathname)
+        logging.info("Adding eland file %s" %(name,))
         split_name = name.split('_')
         lane_id = split_name[1]
 
diff --git a/htsworkflow/pipelines/ipar.py b/htsworkflow/pipelines/ipar.py
new file mode 100644 (file)
index 0000000..a559229
--- /dev/null
@@ -0,0 +1,225 @@
+"""
+Extract information about the IPAR run
+
+IPAR - class holding the properties we found
+IPAR - IPAR factory function initalized from a directory name
+fromxml - IPAR factory function initalized from an xml dump from
+          the IPAR object.
+"""
+
+import datetime
+import logging
+import os
+import re
+import stat
+import time
+
+from htsworkflow.pipelines.runfolder import \
+   ElementTree, \
+   VERSION_RE, \
+   EUROPEAN_STRPTIME
+
+class Tiles(object):
+  def __init__(self, tree):
+    self.tree = tree.find("TileSelection")
+
+  def keys(self):
+    key_list = []
+    for c in self.tree.getchildren():
+      k = c.attrib.get('Index', None)
+      if k is not None:
+        key_list.append(k)
+    return key_list
+
+  def values(self):
+    value_list = []
+    for lane in self.tree.getchildren():
+      attributes = {}
+      for child in lane.getchildren():
+        if child.tag == "Sample":
+          attributes['Sample'] = child.text
+        elif child.tag == 'TileRange':
+          attributes['TileRange'] = (int(child.attrib['Min']),int(child.attrib['Max']))
+      value_list.append(attributes)
+    return value_list
+
+  def items(self):
+    return zip(self.keys(), self.values())
+
+  def __getitem__(self, key):
+    # FIXME: this is inefficient. building the dictionary be rescanning the xml.
+    v = dict(self.items())
+    return v[key]
+
+class IPAR(object):
+    XML_VERSION=1
+
+    # xml tag names
+    IPAR = 'IPAR'
+    TIMESTAMP = 'timestamp'
+    MATRIX = 'matrix'
+    RUN = 'Run'
+
+    def __init__(self, xml=None):
+        self.tree = None
+        self.date = datetime.datetime.today()
+       self._tiles = None
+        if xml is not None:
+            self.set_elements(xml)
+
+    def _get_time(self):
+        return time.mktime(self.date.timetuple())
+    def _set_time(self, value):
+        mtime_tuple = time.localtime(value)
+        self.date = datetime.datetime(*(mtime_tuple[0:7]))
+    time = property(_get_time, _set_time,
+                    doc='run time as seconds since epoch')
+
+    def _get_cycles(self):
+        if self.tree is None:
+          return None
+        cycles = self.tree.find("Cycles")
+        if cycles is None:
+          return None
+        return cycles.attrib
+
+    def _get_start(self):
+        """
+        return cycle start
+        """
+        cycles = self._get_cycles()
+        if cycles is not None:
+          return int(cycles['First'])
+        else:
+          return None
+    start = property(_get_start, doc="get cycle start")
+
+    def _get_stop(self):
+        """
+        return cycle stop
+        """
+        cycles = self._get_cycles()
+        if cycles is not None:
+          return int(cycles['Last'])
+        else:
+          return None
+    stop = property(_get_stop, doc="get cycle stop")
+
+    def _get_tiles(self):
+      if self._tiles is None:
+        self._tiles = Tiles(self.tree)
+      return self._tiles
+    tiles = property(_get_tiles)
+
+    def _get_version(self):
+      software = self.tree.find('Software')
+      if software is not None:
+        return software.attrib['Version']
+    version = property(_get_version, "IPAR software version")
+
+
+    def file_list(self):
+        """
+        Generate list of all files that should be generated by the IPAR unit
+        """
+        suffix_node = self.tree.find('RunParameters/CompressionSuffix')
+        if suffix_node is None:
+          print "find compression suffix failed"
+          return None
+        suffix = suffix_node.text
+        files = []
+        format = "%s_%s_%04d_%s.txt%s"
+        for lane, attrib in self.tiles.items():
+          for file_type in ["int","nse"]:
+            start, stop = attrib['TileRange']
+            for tile in range(start, stop+1):
+              files.append(format % (attrib['Sample'], lane, tile, file_type, suffix))
+        return files
+
+    def dump(self):
+        print "Matrix:", self.matrix
+        print "Tree:", self.tree
+
+    def get_elements(self):
+        attribs = {'version': str(IPAR.XML_VERSION) }
+        root = ElementTree.Element(IPAR.IPAR, attrib=attribs)
+        timestamp = ElementTree.SubElement(root, IPAR.TIMESTAMP)
+        timestamp.text = str(int(self.time))
+        root.append(self.tree)
+        matrix = ElementTree.SubElement(root, IPAR.MATRIX)
+        matrix.text = self.matrix
+        return root
+
+    def set_elements(self, tree):
+        if tree.tag != IPAR.IPAR:
+            raise ValueError('Expected "IPAR" SubElements')
+        xml_version = int(tree.attrib.get('version', 0))
+        if xml_version > IPAR.XML_VERSION:
+            logging.warn('IPAR XML tree is a higher version than this class')
+        for element in list(tree):
+            if element.tag == IPAR.RUN:
+                self.tree = element
+            elif element.tag == IPAR.TIMESTAMP:
+               self.time = int(element.text)
+            elif element.tag == IPAR.MATRIX:
+                self.matrix = element.text
+            else:
+                raise ValueError("Unrecognized tag: %s" % (element.tag,))
+
+def load_ipar_param_tree(paramfile):
+    """
+    look for a .param file and load it if it is an IPAR tree
+    """
+
+    tree = ElementTree.parse(paramfile).getroot()
+    run = tree.find('Run')
+    if run.attrib.has_key('Name') and run.attrib['Name'].startswith("IPAR"):
+        return run
+
+    return None
+
+def ipar(pathname):
+    """
+    Examine the directory at pathname and initalize a IPAR object
+    """
+    logging.info("Searching IPAR directory")
+    i = IPAR()
+
+    # parse firecrest directory name
+    path, name = os.path.split(pathname)
+    groups = name.split('_')
+    if groups[0] != 'IPAR':
+      raise ValueError('ipar can only process IPAR directories')
+
+    # contents of the matrix file?
+    matrix_pathname = os.path.join(pathname, 'Matrix', 's_matrix.txt')
+    i.matrix = open(matrix_pathname, 'r').read()
+
+    # look for parameter xml file
+    paramfile = os.path.join(path, '.params')
+    if os.path.exists(paramfile):
+      i.tree = load_ipar_param_tree(paramfile)
+      mtime_local = os.stat(paramfile)[stat.ST_MTIME]
+      i.time = mtime_local
+    return i
+
+def fromxml(tree):
+    """
+    Initialize a IPAR object from an element tree node
+    """
+    f = IPAR()
+    f.set_elements(tree)
+    return f
+
+if __name__ == "__main__":
+  i = ipar(os.path.expanduser('~/gec/081021_HWI-EAS229_0063_30HKUAAXX/Data/IPAR_1.01'))
+  x = i.get_elements()
+  j = fromxml(x)
+  #ElementTree.dump(x)
+  print j.date
+  print j.start
+  print j.stop
+  print i.tiles.keys()
+  print j.tiles.keys()
+  print j.tiles.items()
+  print j.file_list()
\ No newline at end of file
index dee3231068e95150e9e699d96dc3cc80b90614f5..fc2beeb425e3f1338874549e0f9b41502f392f87 100644 (file)
@@ -25,7 +25,6 @@ LANES_PER_FLOWCELL = 8
 from htsworkflow.util.alphanum import alphanum
 from htsworkflow.util.ethelp import indent, flatten
 
-
 class PipelineRun(object):
     """
     Capture "interesting" information about a pipeline run
@@ -34,20 +33,20 @@ class PipelineRun(object):
     PIPELINE_RUN = 'PipelineRun'
     FLOWCELL_ID = 'FlowcellID'
 
-    def __init__(self, pathname=None, firecrest=None, bustard=None, gerald=None, xml=None):
+    def __init__(self, pathname=None, xml=None):
         if pathname is not None:
           self.pathname = os.path.normpath(pathname)
         else:
           self.pathname = None
         self._name = None
         self._flowcell_id = None
-        self.firecrest = firecrest
-        self.bustard = bustard
-        self.gerald = gerald
+        self.image_analysis = None
+        self.bustard = None
+        self.gerald = None
 
         if xml is not None:
           self.set_elements(xml)
-    
+
     def _get_flowcell_id(self):
         # extract flowcell ID
         if self._flowcell_id is None:
@@ -63,7 +62,7 @@ class PipelineRun(object):
               flowcell_id = path_fields[-1]
             else:
               flowcell_id = 'unknown'
-              
+
            logging.warning(
              "Flowcell id was not found, guessing %s" % (
                 flowcell_id))
@@ -78,7 +77,7 @@ class PipelineRun(object):
         root = ElementTree.Element(PipelineRun.PIPELINE_RUN)
         flowcell = ElementTree.SubElement(root, PipelineRun.FLOWCELL_ID)
         flowcell.text = self.flowcell_id
-        root.append(self.firecrest.get_elements())
+        root.append(self.image_analysis.get_elements())
         root.append(self.bustard.get_elements())
         root.append(self.gerald.get_elements())
         return root
@@ -87,6 +86,7 @@ class PipelineRun(object):
         # this file gets imported by all the others,
         # so we need to hide the imports to avoid a cyclic imports
         from htsworkflow.pipelines import firecrest
+        from htsworkflow.pipelines import ipar
         from htsworkflow.pipelines import bustard
         from htsworkflow.pipelines import gerald
 
@@ -99,8 +99,11 @@ class PipelineRun(object):
           if tag == PipelineRun.FLOWCELL_ID.lower():
             self._flowcell_id = element.text
           #ok the xword.Xword.XWORD pattern for module.class.constant is lame
+          # you should only have Firecrest or IPAR, never both of them.
           elif tag == firecrest.Firecrest.FIRECREST.lower():
-            self.firecrest = firecrest.Firecrest(xml=element)
+            self.image_analysis = firecrest.Firecrest(xml=element)
+          elif tag == ipar.IPAR.IPAR.lower():
+            self.image_analysis = ipar.IPAR(xml=element)
           elif tag == bustard.Bustard.BUSTARD.lower():
             self.bustard = bustard.Bustard(xml=element)
           elif tag == gerald.Gerald.GERALD.lower():
@@ -113,7 +116,7 @@ class PipelineRun(object):
         Given a run tuple, find the latest date and use that as our name
         """
         if self._name is None:
-          tmax = max(self.firecrest.time, self.bustard.time, self.gerald.time)
+          tmax = max(self.image_analysis.time, self.bustard.time, self.gerald.time)
           timestamp = time.strftime('%Y-%m-%d', time.localtime(tmax))
           self._name = 'run_'+self.flowcell_id+"_"+timestamp+'.xml'
         return self._name
@@ -143,28 +146,48 @@ def get_runs(runfolder):
     in there gerald component.
     """
     from htsworkflow.pipelines import firecrest
+    from htsworkflow.pipelines import ipar
     from htsworkflow.pipelines import bustard
     from htsworkflow.pipelines import gerald
 
-    datadir = os.path.join(runfolder, 'Data')
-
-    logging.info('Searching for runs in ' + datadir)
-    runs = []
-    for firecrest_pathname in glob(os.path.join(datadir,"*Firecrest*")):
-        f = firecrest.firecrest(firecrest_pathname)
-        bustard_glob = os.path.join(firecrest_pathname, "Bustard*")
+    def scan_post_image_analysis(runs, runfolder, image_analysis, pathname):
+        logging.info("Looking for bustard directories in %s" % (pathname,))
+        bustard_glob = os.path.join(pathname, "Bustard*")
         for bustard_pathname in glob(bustard_glob):
+            logging.info("Found bustard directory %s" % (bustard_pathname,))
             b = bustard.bustard(bustard_pathname)
             gerald_glob = os.path.join(bustard_pathname, 'GERALD*')
+            logging.info("Looking for gerald directories in %s" % (pathname,))
             for gerald_pathname in glob(gerald_glob):
+                logging.info("Found gerald directory %s" % (gerald_pathname,))
                 try:
                     g = gerald.gerald(gerald_pathname)
-                    runs.append(PipelineRun(runfolder, f, b, g))
+                    p = PipelineRun(runfolder)
+                    p.image_analysis = image_analysis
+                    p.bustard = b
+                    p.gerald = g
+                    runs.append(p)
                 except IOError, e:
                     print "Ignoring", str(e)
+
+    datadir = os.path.join(runfolder, 'Data')
+
+    logging.info('Searching for runs in ' + datadir)
+    runs = []
+    # scan for firecrest directories
+    for firecrest_pathname in glob(os.path.join(datadir,"*Firecrest*")):
+        logging.info('Found firecrest in ' + datadir)
+        image_analysis = firecrest.firecrest(firecrest_pathname)
+        scan_post_image_analysis(runs, runfolder, image_analysis, firecrest_pathname)
+    # scan for IPAR directories
+    for ipar_pathname in glob(os.path.join(datadir,"IPAR_*")):
+        logging.info('Found ipar directories in ' + datadir)
+        image_analysis = ipar.ipar(ipar_pathname)
+        scan_post_image_analysis(runs, runfolder, image_analysis, ipar_pathname)
+
     return runs
-                
-    
+
+
 def extract_run_parameters(runs):
     """
     Search through runfolder_path for various runs and grab their parameters
@@ -190,6 +213,33 @@ def summarize_mapped_reads(mapped_reads):
     summarized_reads[genome] = genome_reads
     return summarized_reads
 
+def summarize_lane(gerald, lane_id):
+    report = []
+    summary_results = gerald.summary.lane_results
+    eland_result = gerald.eland_results.results[lane_id]
+    report.append("Sample name %s" % (eland_result.sample_name))
+    report.append("Lane id %s" % (eland_result.lane_id,))
+    cluster = summary_results[eland_result.lane_id].cluster
+    report.append("Clusters %d +/- %d" % (cluster[0], cluster[1]))
+    report.append("Total Reads: %d" % (eland_result.reads))
+    mc = eland_result._match_codes
+    nm = mc['NM']
+    nm_percent = float(nm)/eland_result.reads  * 100
+    qc = mc['QC']
+    qc_percent = float(qc)/eland_result.reads * 100
+
+    report.append("No Match: %d (%2.2g %%)" % (nm, nm_percent))
+    report.append("QC Failed: %d (%2.2g %%)" % (qc, qc_percent))
+    report.append('Unique (0,1,2 mismatches) %d %d %d' % \
+                  (mc['U0'], mc['U1'], mc['U2']))
+    report.append('Repeat (0,1,2 mismatches) %d %d %d' % \
+                  (mc['R0'], mc['R1'], mc['R2']))
+    report.append("Mapped Reads")
+    mapped_reads = summarize_mapped_reads(eland_result.mapped_reads)
+    for name, counts in mapped_reads.items():
+      report.append("  %s: %d" % (name, counts))
+    return report
+
 def summary_report(runs):
     """
     Summarize cluster numbers and mapped read counts for a runfolder
@@ -202,30 +252,8 @@ def summary_report(runs):
        eland_keys = run.gerald.eland_results.results.keys()
        eland_keys.sort(alphanum)
 
-        lane_results = run.gerald.summary.lane_results
        for lane_id in eland_keys:
-           result = run.gerald.eland_results.results[lane_id]
-            report.append("Sample name %s" % (result.sample_name))
-            report.append("Lane id %s" % (result.lane_id,))
-            cluster = lane_results[result.lane_id].cluster
-            report.append("Clusters %d +/- %d" % (cluster[0], cluster[1]))
-            report.append("Total Reads: %d" % (result.reads))
-            mc = result._match_codes
-            nm = mc['NM']
-            nm_percent = float(nm)/result.reads  * 100
-            qc = mc['QC']
-            qc_percent = float(qc)/result.reads * 100
-
-           report.append("No Match: %d (%2.2g %%)" % (nm, nm_percent))
-           report.append("QC Failed: %d (%2.2g %%)" % (qc, qc_percent))
-            report.append('Unique (0,1,2 mismatches) %d %d %d' % \
-                          (mc['U0'], mc['U1'], mc['U2']))
-            report.append('Repeat (0,1,2 mismatches) %d %d %d' % \
-                          (mc['R0'], mc['R1'], mc['R2']))
-            report.append("Mapped Reads")
-            mapped_reads = summarize_mapped_reads(result.mapped_reads)
-            for name, counts in mapped_reads.items():
-              report.append("  %s: %d" % (name, counts))
+            report.extend(summarize_lane(run.gerald, lane_id))
             report.append('---')
             report.append('')
         return os.linesep.join(report)
@@ -239,9 +267,9 @@ def extract_results(runs, output_base_dir=None):
       logging.info("Using %s as result directory" % (result_dir,))
       if not os.path.exists(result_dir):
         os.mkdir(result_dir)
-      
+
       # create cycle_dir
-      cycle = "C%d-%d" % (r.firecrest.start, r.firecrest.stop)
+      cycle = "C%d-%d" % (r.image_analysis.start, r.image_analysis.stop)
       logging.info("Filling in %s" % (cycle,))
       cycle_dir = os.path.join(result_dir, cycle)
       if os.path.exists(cycle_dir):
@@ -278,7 +306,7 @@ def extract_results(runs, output_base_dir=None):
       logging.info("Running tar: " + " ".join(tar_cmd[:10]))
       logging.info("Running bzip2: " + " ".join(bzip_cmd))
       logging.info("Writing to %s" %(tar_dest_name))
-      
+
       tar = subprocess.Popen(tar_cmd, stdout=subprocess.PIPE, shell=False, cwd=g.pathname)
       bzip = subprocess.Popen(bzip_cmd, stdin=tar.stdout, stdout=tar_dest)
       tar.wait()
diff --git a/htsworkflow/pipelines/test/__init__.py b/htsworkflow/pipelines/test/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/htsworkflow/pipelines/test/simulate_runfolder.py b/htsworkflow/pipelines/test/simulate_runfolder.py
new file mode 100644 (file)
index 0000000..c7a9961
--- /dev/null
@@ -0,0 +1,833 @@
+"""
+Create simulated solexa/illumina runfolders for testing
+"""
+
+import os
+
+
+def make_ipar_dir(data_dir):
+    """
+    Construct an artificial ipar parameter file and directory
+    """
+    params = """<?xml version="1.0"?>
+<ImageAnalysis>
+  <Run Name="IPAR_1.01">
+    <Software Name="IPAR" Version="2.01.192.0" />
+    <Cycles First="1" Last="37" Number="37" />
+    <RunParameters>
+      <ImagingReads Index="1">
+        <FirstCycle>1</FirstCycle>
+        <LastCycle>37</LastCycle>
+        <RunFolder>081021_HWI-EAS229_0063_30HKUAAXX</RunFolder>
+      </ImagingReads>
+      <Reads Index="1">
+        <FirstCycle>1</FirstCycle>
+        <LastCycle>37</LastCycle>
+        <RunFolder>081021_HWI-EAS229_0063_30HKUAAXX</RunFolder>
+      </Reads>
+      <Compression>gzip</Compression>
+      <CompressionSuffix>.p.gz</CompressionSuffix>
+      <Instrument>HWI-EAS229</Instrument>
+      <RunFolder>081021_HWI-EAS229_0063_30HKUAAXX</RunFolder>
+    </RunParameters>
+    <ImageParameters>
+      <AutoOffsetFlag>1</AutoOffsetFlag>
+      <Fwhm>2.7</Fwhm>
+      <RemappingDistance>1.5</RemappingDistance>
+      <Threshold>4</Threshold>
+    </ImageParameters>
+    <TileSelection>
+      <Lane Index="1">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+      <Lane Index="2">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+      <Lane Index="3">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+      <Lane Index="4">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+      <Lane Index="5">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+      <Lane Index="6">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+      <Lane Index="7">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+      <Lane Index="8">
+        <Sample>s</Sample>
+        <TileRange Max="100" Min="1" />
+      </Lane>
+    </TileSelection>
+  </Run>
+</ImageAnalysis>
+"""
+    f = open(os.path.join(data_dir, '.params'),'w')
+    f.write(params)
+    f.close()
+    ipar_dir = os.path.join(data_dir, 'IPAR_1.01')
+    if not os.path.exists(ipar_dir):
+      os.mkdir(ipar_dir)
+    return ipar_dir
+
+def make_flowcell_id(runfolder_dir, flowcell_id=None):
+    if flowcell_id is None:
+        flowcell_id = '207BTAAXY'
+
+    config = """<?xml version="1.0"?>
+<FlowcellId>
+  <Text>%s</Text>
+</FlowcellId>""" % (flowcell_id,)
+    config_dir = os.path.join(runfolder_dir, 'Config')
+
+    if not os.path.exists(config_dir):
+        os.mkdir(config_dir)
+    pathname = os.path.join(config_dir, 'FlowcellId.xml')
+    f = open(pathname,'w')
+    f.write(config)
+    f.close()
+
+def make_matrix(matrix_dir):
+    contents = """# Auto-generated frequency response matrix
+> A
+> C
+> G
+> T
+0.77 0.15 -0.04 -0.04
+0.76 1.02 -0.05 -0.06
+-0.10 -0.10 1.17 -0.03
+-0.13 -0.12 0.80 1.27
+"""
+    s_matrix = os.path.join(matrix_dir, 's_matrix.txt')
+    f = open(s_matrix, 'w')
+    f.write(contents)
+    f.close()
+
+def make_phasing_params(bustard_dir):
+    for lane in range(1,9):
+        pathname = os.path.join(bustard_dir, 'params%d.xml' % (lane))
+        f = open(pathname, 'w')
+        f.write("""<Parameters>
+  <Phasing>0.009900</Phasing>
+  <Prephasing>0.003500</Prephasing>
+</Parameters>
+""")
+        f.close()
+
+def make_gerald_config(gerald_dir):
+    config_xml = """<RunParameters>
+<ChipWideRunParameters>
+  <ANALYSIS>default</ANALYSIS>
+  <BAD_LANES></BAD_LANES>
+  <BAD_TILES></BAD_TILES>
+  <CONTAM_DIR></CONTAM_DIR>
+  <CONTAM_FILE></CONTAM_FILE>
+  <ELAND_GENOME>Need_to_specify_ELAND_genome_directory</ELAND_GENOME>
+  <ELAND_MULTIPLE_INSTANCES>8</ELAND_MULTIPLE_INSTANCES>
+  <ELAND_REPEAT></ELAND_REPEAT>
+  <EMAIL_DOMAIN>domain.com</EMAIL_DOMAIN>
+  <EMAIL_LIST>diane</EMAIL_LIST>
+  <EMAIL_SERVER>localhost:25</EMAIL_SERVER>
+  <EXPT_DIR>/home/diane/gec/080416_HWI-EAS229_0024_207BTAAXX/Data/C1-33_Firecrest1.8.28_19-04-2008_diane/Bustard1.8.28_19-04-2008_diane</EXPT_DIR>
+  <EXPT_DIR_ROOT>/home/diane/gec</EXPT_DIR_ROOT>
+  <FORCE>1</FORCE>
+  <GENOME_DIR>/home/diane/proj/SolexaPipeline-0.2.2.6/Goat/../Gerald/../../Genomes</GENOME_DIR>
+  <GENOME_FILE>Need_to_specify_genome_file_name</GENOME_FILE>
+  <HAMSTER_FLAG>genome</HAMSTER_FLAG>
+  <OUT_DIR>/home/diane/gec/080416_HWI-EAS229_0024_207BTAAXX/Data/C1-33_Firecrest1.8.28_19-04-2008_diane/Bustard1.8.28_19-04-2008_diane/GERALD_19-04-2008_diane</OUT_DIR>
+  <POST_RUN_COMMAND></POST_RUN_COMMAND>
+  <PRB_FILE_SUFFIX>_prb.txt</PRB_FILE_SUFFIX>
+  <PURE_BASES>12</PURE_BASES>
+  <QF_PARAMS>'((CHASTITY&gt;=0.6))'</QF_PARAMS>
+  <QHG_FILE_SUFFIX>_qhg.txt</QHG_FILE_SUFFIX>
+  <QUALITY_FORMAT>--symbolic</QUALITY_FORMAT>
+  <READ_LENGTH>32</READ_LENGTH>
+  <SEQUENCE_FORMAT>--scarf</SEQUENCE_FORMAT>
+  <SEQ_FILE_SUFFIX>_seq.txt</SEQ_FILE_SUFFIX>
+  <SIG_FILE_SUFFIX_DEPHASED>_sig2.txt</SIG_FILE_SUFFIX_DEPHASED>
+  <SIG_FILE_SUFFIX_NOT_DEPHASED>_sig.txt</SIG_FILE_SUFFIX_NOT_DEPHASED>
+  <SOFTWARE_VERSION>@(#) Id: GERALD.pl,v 1.68.2.2 2007/06/13 11:08:49 km Exp</SOFTWARE_VERSION>
+  <TILE_REGEX>s_[1-8]_[0-9][0-9][0-9][0-9]</TILE_REGEX>
+  <TILE_ROOT>s</TILE_ROOT>
+  <TIME_STAMP>Sat Apr 19 19:08:30 2008</TIME_STAMP>
+  <TOOLS_DIR>/home/diane/proj/SolexaPipeline-0.2.2.6/Goat/../Gerald</TOOLS_DIR>
+  <USE_BASES>all</USE_BASES>
+  <WEB_DIR_ROOT>http://host.domain.com/yourshare/</WEB_DIR_ROOT>
+</ChipWideRunParameters>
+<LaneSpecificRunParameters>
+  <ANALYSIS>
+    <s_1>eland</s_1>
+    <s_2>eland</s_2>
+    <s_3>eland</s_3>
+    <s_4>eland</s_4>
+    <s_5>eland</s_5>
+    <s_6>eland</s_6>
+    <s_7>eland</s_7>
+    <s_8>eland</s_8>
+  </ANALYSIS>
+  <ELAND_GENOME>
+    <s_1>/g/dm3</s_1>
+    <s_2>/g/equcab1</s_2>
+    <s_3>/g/equcab1</s_3>
+    <s_4>/g/canfam2</s_4>
+    <s_5>/g/hg18</s_5>
+    <s_6>/g/hg18</s_6>
+    <s_7>/g/hg18</s_7>
+    <s_8>/g/hg18</s_8>
+  </ELAND_GENOME>
+  <READ_LENGTH>
+    <s_1>32</s_1>
+    <s_2>32</s_2>
+    <s_3>32</s_3>
+    <s_4>32</s_4>
+    <s_5>32</s_5>
+    <s_6>32</s_6>
+    <s_7>32</s_7>
+    <s_8>32</s_8>
+  </READ_LENGTH>
+  <USE_BASES>
+    <s_1>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_1>
+    <s_2>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_2>
+    <s_3>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_3>
+    <s_4>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_4>
+    <s_5>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_5>
+    <s_6>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_6>
+    <s_7>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_7>
+    <s_8>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_8>
+  </USE_BASES>
+</LaneSpecificRunParameters>
+</RunParameters>
+"""
+    pathname = os.path.join(gerald_dir, 'config.xml')
+    f = open(pathname,'w')
+    f.write(config_xml)
+    f.close()
+
+def make_summary100_htm(gerald_dir):
+    summary_htm="""<!--RUN_TIME Wed Jul  2 06:47:44 2008 -->
+<!--SOFTWARE_VERSION @(#) $Id: jerboa.pl,v 1.94 2007/12/04 09:59:07 rshaw Exp $-->
+<html>
+<body>
+
+<a name="Top"><h2><title>080627_HWI-EAS229_0036_3055HAXX Summary</title></h2></a>
+<h1>Summary Information For Experiment 080627_HWI-EAS229_0036_3055HAXX on Machine HWI-EAS229</h1>
+<h2><br></br>Chip Summary<br></br></h2>
+<table border="1" cellpadding="5">
+<tr><td>Machine</td><td>HWI-EAS229</td></tr>
+<tr><td>Run Folder</td><td>080627_HWI-EAS229_0036_3055HAXX</td></tr>
+<tr><td>Chip ID</td><td>unknown</td></tr>
+</table>
+<h2><br></br>Chip Results Summary<br></br></h2>
+<table border="1" cellpadding="5">
+<tr>
+<td>Clusters</td>
+<td>Clusters (PF)</td>
+<td>Yield (kbases)</td>
+</tr>
+<tr><td>80933224</td>
+<td>43577803</td>
+<td>1133022</td>
+</tr>
+</table>
+<h2><br></br>Lane Parameter Summary<br></br></h2>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane</td>
+<td>Sample ID</td>
+<td>Sample Target</td>
+<td>Sample Type</td>
+<td>Length</td>
+<td>Filter</td>
+<td>Num Tiles</td>
+<td>Tiles</td>
+</tr>
+<tr>
+<td>1</td>
+<td>unknown</td>
+<td>mm9</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane1">Lane 1</a></td>
+</tr>
+<tr>
+<td>2</td>
+<td>unknown</td>
+<td>mm9</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane2">Lane 2</a></td>
+</tr>
+<tr>
+<td>3</td>
+<td>unknown</td>
+<td>mm9</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane3">Lane 3</a></td>
+</tr>
+<tr>
+<td>4</td>
+<td>unknown</td>
+<td>elegans170</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane4">Lane 4</a></td>
+</tr>
+<tr>
+<td>5</td>
+<td>unknown</td>
+<td>elegans170</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane5">Lane 5</a></td>
+</tr>
+<tr>
+<td>6</td>
+<td>unknown</td>
+<td>elegans170</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane6">Lane 6</a></td>
+</tr>
+<tr>
+<td>7</td>
+<td>unknown</td>
+<td>elegans170</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane7">Lane 7</a></td>
+</tr>
+<tr>
+<td>8</td>
+<td>unknown</td>
+<td>elegans170</td>
+<td>ELAND</td>
+<td>26</td>
+<td>'((CHASTITY>=0.6))'</td>
+<td>100</td>
+<td><a href="#Lane8">Lane 8</a></td>
+</tr>
+</table>
+<h2><br></br>Lane Results Summary<br></br></h2>
+<table border="1" cellpadding="5">
+<tr>
+<td colspan="2">Lane Info</td>
+<td colspan="8">Tile Mean +/- SD for Lane</td>
+</tr>
+<tr>
+<td>Lane </td>
+<td>Lane Yield (kbases) </td>
+<td>Clusters (raw)</td>
+<td>Clusters (PF) </td>
+<td>1st Cycle Int (PF) </td>
+<td>% intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Alignment Score (PF) </td>
+<td> % Error Rate (PF) </td>
+</tr>
+<tr>
+<td>1</td>
+<td>158046</td>
+<td>96483 +/- 9074</td>
+<td>60787 +/- 4240</td>
+<td>329 +/- 35</td>
+<td>101.88 +/- 6.03</td>
+<td>63.21 +/- 3.29</td>
+<td>70.33 +/- 0.24</td>
+<td>9054.08 +/- 59.16</td>
+<td>0.46 +/- 0.18</td>
+</tr>
+<tr>
+<td>2</td>
+<td>156564</td>
+<td>133738 +/- 7938</td>
+<td>60217 +/- 1926</td>
+<td>444 +/- 39</td>
+<td>92.62 +/- 7.58</td>
+<td>45.20 +/- 3.31</td>
+<td>51.98 +/- 0.74</td>
+<td>6692.04 +/- 92.49</td>
+<td>0.46 +/- 0.09</td>
+</tr>
+<tr>
+<td>3</td>
+<td>185818</td>
+<td>152142 +/- 10002</td>
+<td>71468 +/- 2827</td>
+<td>366 +/- 36</td>
+<td>91.53 +/- 8.66</td>
+<td>47.19 +/- 3.80</td>
+<td>82.24 +/- 0.44</td>
+<td>10598.68 +/- 64.13</td>
+<td>0.41 +/- 0.04</td>
+</tr>
+<tr>
+<td>4</td>
+<td>34953</td>
+<td>15784 +/- 2162</td>
+<td>13443 +/- 1728</td>
+<td>328 +/- 40</td>
+<td>97.53 +/- 9.87</td>
+<td>85.29 +/- 1.91</td>
+<td>80.02 +/- 0.53</td>
+<td>10368.82 +/- 71.08</td>
+<td>0.15 +/- 0.05</td>
+</tr>
+<tr>
+<td>5</td>
+<td>167936</td>
+<td>119735 +/- 8465</td>
+<td>64590 +/- 2529</td>
+<td>417 +/- 37</td>
+<td>88.69 +/- 14.79</td>
+<td>54.10 +/- 2.59</td>
+<td>76.95 +/- 0.32</td>
+<td>9936.47 +/- 65.75</td>
+<td>0.28 +/- 0.02</td>
+</tr>
+<tr>
+<td>6</td>
+<td>173463</td>
+<td>152177 +/- 8146</td>
+<td>66716 +/- 2493</td>
+<td>372 +/- 39</td>
+<td>87.06 +/- 9.86</td>
+<td>43.98 +/- 3.12</td>
+<td>78.80 +/- 0.43</td>
+<td>10162.28 +/- 49.65</td>
+<td>0.38 +/- 0.03</td>
+</tr>
+<tr>
+<td>7</td>
+<td>149287</td>
+<td>84649 +/- 7325</td>
+<td>57418 +/- 3617</td>
+<td>295 +/- 28</td>
+<td>89.40 +/- 8.23</td>
+<td>67.97 +/- 1.82</td>
+<td>33.38 +/- 0.25</td>
+<td>4247.92 +/- 32.37</td>
+<td>1.00 +/- 0.03</td>
+</tr>
+<tr>
+<td>8</td>
+<td>106953</td>
+<td>54622 +/- 4812</td>
+<td>41136 +/- 3309</td>
+<td>284 +/- 37</td>
+<td>90.21 +/- 9.10</td>
+<td>75.39 +/- 2.27</td>
+<td>48.33 +/- 0.29</td>
+<td>6169.21 +/- 169.50</td>
+<td>0.86 +/- 1.22</td>
+</tr>
+<tr><td colspan="13">Tile mean across chip</td></tr>
+<tr>
+<td>Av.</td>
+<td></td>
+<td>101166</td>
+<td>54472</td>
+<td>354</td>
+<td>92.36</td>
+<td>60.29</td>
+<td>65.25</td>
+<td>8403.69</td>
+<td>0.50</td>
+</tr>
+</table>
+<h2><br></br>Expanded Lane Summary<br></br></h2>
+<table border="1" cellpadding="5">
+<tr>
+
+<tr><td colspan="2">Lane Info</td>
+<td colspan="2">Phasing Info</td>
+<td colspan="2">Raw Data (tile mean)</td>
+<td colspan="7">Filtered Data (tile mean)</td></tr>
+<td>Lane </td>
+<td>Clusters (tile mean) (raw)</td>
+<td>% Phasing </td>
+<td>% Prephasing </td>
+<td>% Error Rate (raw) </td>
+<td> Equiv Perfect Clusters (raw) </td>
+<td>% retained </td>
+<td>Cycle 2-4 Av Int (PF) </td>
+<td>Cycle 2-10 Av % Loss (PF) </td>
+<td>Cycle 10-20 Av % Loss (PF) </td>
+<td>% Align (PF) </td>
+<td>% Error Rate (PF) </td>
+<td> Equiv Perfect Clusters (PF) </td>
+</tr>
+<tr>
+<td>1</td>
+<td>96483</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>1.00</td>
+<td>49676</td>
+<td>63.21</td>
+<td>317 +/- 32</td>
+<td>0.13 +/- 0.44</td>
+<td>-1.14 +/- 0.34</td>
+<td>70.33</td>
+<td>0.46</td>
+<td>41758</td>
+</tr>
+<tr>
+<td>2</td>
+<td>133738</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>1.22</td>
+<td>40467</td>
+<td>45.20</td>
+<td>415 +/- 33</td>
+<td>0.29 +/- 0.40</td>
+<td>-0.79 +/- 0.35</td>
+<td>51.98</td>
+<td>0.46</td>
+<td>30615</td>
+</tr>
+<tr>
+<td>3</td>
+<td>152142</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>1.30</td>
+<td>78588</td>
+<td>47.19</td>
+<td>344 +/- 26</td>
+<td>0.68 +/- 0.51</td>
+<td>-0.77 +/- 0.42</td>
+<td>82.24</td>
+<td>0.41</td>
+<td>57552</td>
+</tr>
+<tr>
+<td>4</td>
+<td>15784</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>0.29</td>
+<td>11095</td>
+<td>85.29</td>
+<td>306 +/- 34</td>
+<td>0.20 +/- 0.69</td>
+<td>-1.28 +/- 0.66</td>
+<td>80.02</td>
+<td>0.15</td>
+<td>10671</td>
+</tr>
+<tr>
+<td>5</td>
+<td>119735</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>0.85</td>
+<td>60335</td>
+<td>54.10</td>
+<td>380 +/- 32</td>
+<td>0.34 +/- 0.49</td>
+<td>-1.55 +/- 4.69</td>
+<td>76.95</td>
+<td>0.28</td>
+<td>49015</td>
+</tr>
+<tr>
+<td>6</td>
+<td>152177</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>1.21</td>
+<td>70905</td>
+<td>43.98</td>
+<td>333 +/- 27</td>
+<td>0.57 +/- 0.50</td>
+<td>-0.91 +/- 0.39</td>
+<td>78.80</td>
+<td>0.38</td>
+<td>51663</td>
+</tr>
+<tr>
+<td>7</td>
+<td>84649</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>1.38</td>
+<td>21069</td>
+<td>67.97</td>
+<td>272 +/- 20</td>
+<td>1.15 +/- 0.52</td>
+<td>-0.84 +/- 0.58</td>
+<td>33.38</td>
+<td>1.00</td>
+<td>18265</td>
+</tr>
+<tr>
+<td>8</td>
+<td>54622</td>
+<td>0.7700</td>
+<td>0.3100</td>
+<td>1.17</td>
+<td>21335</td>
+<td>75.39</td>
+<td>262 +/- 31</td>
+<td>1.10 +/- 0.59</td>
+<td>-1.01 +/- 0.47</td>
+<td>48.33</td>
+<td>0.86</td>
+<td>19104</td>
+</tr>
+</table>
+<b><br></br>IVC Plots</b>
+<p> <a href='IVC.htm' target="_blank"> IVC.htm
+ </a></p>
+<b><br></br>All Intensity Plots</b>
+<p> <a href='All.htm' target="_blank"> All.htm
+ </a></p>
+<b><br></br>Error graphs: </b>
+<p> <a href='Error.htm' target="_blank"> Error.htm
+ </a></p>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane1"><h2><br></br>Lane 1<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+<tr>
+<td>1</td>
+<td>0001</td>
+<td>114972</td>
+<td>326.48</td>
+<td>94.39</td>
+<td>57.44</td>
+<td>70.2</td>
+<td>9038.6</td>
+<td>0.44</td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane2"><h2><br></br>Lane 2<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+<tr>
+<td>2</td>
+<td>0001</td>
+<td>147793</td>
+<td>448.12</td>
+<td>83.68</td>
+<td>38.57</td>
+<td>53.7</td>
+<td>6905.4</td>
+<td>0.54</td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane3"><h2><br></br>Lane 3<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+<tr>
+<td>3</td>
+<td>0001</td>
+<td>167904</td>
+<td>374.05</td>
+<td>86.91</td>
+<td>40.36</td>
+<td>81.3</td>
+<td>10465.0</td>
+<td>0.47</td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane4"><h2><br></br>Lane 4<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+<tr>
+<td>4</td>
+<td>0001</td>
+<td>20308</td>
+<td>276.85</td>
+<td>92.87</td>
+<td>84.26</td>
+<td>80.4</td>
+<td>10413.8</td>
+<td>0.16</td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane5"><h2><br></br>Lane 5<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane6"><h2><br></br>Lane 6<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+<tr>
+<td>6</td>
+<td>0001</td>
+<td>166844</td>
+<td>348.12</td>
+<td>77.59</td>
+<td>38.13</td>
+<td>79.7</td>
+<td>10264.4</td>
+<td>0.44</td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane7"><h2><br></br>Lane 7<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+<tr>
+<td>7</td>
+<td>0001</td>
+<td>98913</td>
+<td>269.90</td>
+<td>86.66</td>
+<td>64.55</td>
+<td>33.2</td>
+<td>4217.5</td>
+<td>1.02</td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+<a name="Lane8"><h2><br></br>Lane 8<br></br></h2></a>
+<table border="1" cellpadding="5">
+<tr>
+<td>Lane </td>
+<td>Tile </td>
+<td>Clusters (raw)</td>
+<td>Av 1st Cycle Int (PF) </td>
+<td>Av % intensity after 20 cycles (PF) </td>
+<td>% PF Clusters </td>
+<td>% Align (PF) </td>
+<td>Av Alignment Score (PF) </td>
+<td>% Error Rate (PF) </td>
+</tr>
+<tr>
+<td>8</td>
+<td>0001</td>
+<td>64972</td>
+<td>243.60</td>
+<td>89.40</td>
+<td>73.17</td>
+<td>48.3</td>
+<td>6182.8</td>
+<td>0.71</td>
+</tr>
+</table>
+<td><a href="#Top">Back to top</a></td>
+</body>
+</html>
+"""
+    pathname = os.path.join(gerald_dir, 'Summary.htm')
+    f = open(pathname, 'w')
+    f.write(summary_htm)
+    f.close()
+
+def make_eland_results(gerald_dir):
+    eland_result = """>HWI-EAS229_24_207BTAAXX:1:7:599:759    ACATAGNCACAGACATAAACATAGACATAGAC U0      1       1       3       chrUextra.fa    28189829        R       D.
+>HWI-EAS229_24_207BTAAXX:1:7:205:842    AAACAANNCTCCCAAACACGTAAACTGGAAAA  U1      0       1       0       chr2L.fa        8796855 R       DD      24T
+>HWI-EAS229_24_207BTAAXX:1:7:776:582    AGCTCANCCGATCGAAAACCTCNCCAAGCAAT        NM      0       0       0
+>HWI-EAS229_24_207BTAAXX:1:7:205:842    AAACAANNCTCCCAAACACGTAAACTGGAAAA        U1      0       1       0       Lambda.fa        8796855 R       DD      24T
+"""
+    for i in range(1,9):
+        pathname = os.path.join(gerald_dir,
+                                's_%d_eland_result.txt' % (i,))
+        f = open(pathname, 'w')
+        f.write(eland_result)
+        f.close()
index 40d29cfa69caf7cb3487dbb66790157dde044e69..7b1538142d002244b074a0ac46b571b55d5839e2 100644 (file)
@@ -12,140 +12,8 @@ from htsworkflow.pipelines import gerald
 from htsworkflow.pipelines import runfolder
 from htsworkflow.pipelines.runfolder import ElementTree
 
+from htsworkflow.pipelines.test.simulate_runfolder import *
 
-def make_flowcell_id(runfolder_dir, flowcell_id=None):
-    if flowcell_id is None:
-        flowcell_id = '207BTAAXY'
-
-    config = """<?xml version="1.0"?>
-<FlowcellId>
-  <Text>%s</Text>
-</FlowcellId>""" % (flowcell_id,)
-    config_dir = os.path.join(runfolder_dir, 'Config')
-    
-    if not os.path.exists(config_dir):
-        os.mkdir(config_dir)
-    pathname = os.path.join(config_dir, 'FlowcellId.xml')
-    f = open(pathname,'w')
-    f.write(config)
-    f.close()
-
-def make_matrix(matrix_dir):
-    contents = """# Auto-generated frequency response matrix
-> A
-> C
-> G
-> T
-0.77 0.15 -0.04 -0.04 
-0.76 1.02 -0.05 -0.06 
--0.10 -0.10 1.17 -0.03 
--0.13 -0.12 0.80 1.27 
-"""
-    s_matrix = os.path.join(matrix_dir, 's_matrix.txt')
-    f = open(s_matrix, 'w')
-    f.write(contents)
-    f.close()
-    
-def make_phasing_params(bustard_dir):
-    for lane in range(1,9):
-        pathname = os.path.join(bustard_dir, 'params%d.xml' % (lane))
-        f = open(pathname, 'w')
-        f.write("""<Parameters>
-  <Phasing>0.009900</Phasing>
-  <Prephasing>0.003500</Prephasing>
-</Parameters>
-""")
-        f.close()
-
-def make_gerald_config(gerald_dir):
-    config_xml = """<RunParameters>
-<ChipWideRunParameters>
-  <ANALYSIS>default</ANALYSIS>
-  <BAD_LANES></BAD_LANES>
-  <BAD_TILES></BAD_TILES>
-  <CONTAM_DIR></CONTAM_DIR>
-  <CONTAM_FILE></CONTAM_FILE>
-  <ELAND_GENOME>Need_to_specify_ELAND_genome_directory</ELAND_GENOME>
-  <ELAND_MULTIPLE_INSTANCES>8</ELAND_MULTIPLE_INSTANCES>
-  <ELAND_REPEAT></ELAND_REPEAT>
-  <EMAIL_DOMAIN>domain.com</EMAIL_DOMAIN>
-  <EMAIL_LIST>diane</EMAIL_LIST>
-  <EMAIL_SERVER>localhost:25</EMAIL_SERVER>
-  <EXPT_DIR>/home/diane/gec/080416_HWI-EAS229_0024_207BTAAXX/Data/C1-33_Firecrest1.8.28_19-04-2008_diane/Bustard1.8.28_19-04-2008_diane</EXPT_DIR>
-  <EXPT_DIR_ROOT>/home/diane/gec</EXPT_DIR_ROOT>
-  <FORCE>1</FORCE>
-  <GENOME_DIR>/home/diane/proj/SolexaPipeline-0.2.2.6/Goat/../Gerald/../../Genomes</GENOME_DIR>
-  <GENOME_FILE>Need_to_specify_genome_file_name</GENOME_FILE>
-  <HAMSTER_FLAG>genome</HAMSTER_FLAG>
-  <OUT_DIR>/home/diane/gec/080416_HWI-EAS229_0024_207BTAAXX/Data/C1-33_Firecrest1.8.28_19-04-2008_diane/Bustard1.8.28_19-04-2008_diane/GERALD_19-04-2008_diane</OUT_DIR>
-  <POST_RUN_COMMAND></POST_RUN_COMMAND>
-  <PRB_FILE_SUFFIX>_prb.txt</PRB_FILE_SUFFIX>
-  <PURE_BASES>12</PURE_BASES>
-  <QF_PARAMS>'((CHASTITY&gt;=0.6))'</QF_PARAMS>
-  <QHG_FILE_SUFFIX>_qhg.txt</QHG_FILE_SUFFIX>
-  <QUALITY_FORMAT>--symbolic</QUALITY_FORMAT>
-  <READ_LENGTH>32</READ_LENGTH>
-  <SEQUENCE_FORMAT>--scarf</SEQUENCE_FORMAT>
-  <SEQ_FILE_SUFFIX>_seq.txt</SEQ_FILE_SUFFIX>
-  <SIG_FILE_SUFFIX_DEPHASED>_sig2.txt</SIG_FILE_SUFFIX_DEPHASED>
-  <SIG_FILE_SUFFIX_NOT_DEPHASED>_sig.txt</SIG_FILE_SUFFIX_NOT_DEPHASED>
-  <SOFTWARE_VERSION>@(#) Id: GERALD.pl,v 1.68.2.2 2007/06/13 11:08:49 km Exp</SOFTWARE_VERSION>
-  <TILE_REGEX>s_[1-8]_[0-9][0-9][0-9][0-9]</TILE_REGEX>
-  <TILE_ROOT>s</TILE_ROOT>
-  <TIME_STAMP>Sat Apr 19 19:08:30 2008</TIME_STAMP>
-  <TOOLS_DIR>/home/diane/proj/SolexaPipeline-0.2.2.6/Goat/../Gerald</TOOLS_DIR>
-  <USE_BASES>all</USE_BASES>
-  <WEB_DIR_ROOT>http://host.domain.com/yourshare/</WEB_DIR_ROOT>
-</ChipWideRunParameters>
-<LaneSpecificRunParameters>
-  <ANALYSIS>
-    <s_1>eland</s_1>
-    <s_2>eland</s_2>
-    <s_3>eland</s_3>
-    <s_4>eland</s_4>
-    <s_5>eland</s_5>
-    <s_6>eland</s_6>
-    <s_7>eland</s_7>
-    <s_8>eland</s_8>
-  </ANALYSIS>
-  <ELAND_GENOME>
-    <s_1>/g/dm3</s_1>
-    <s_2>/g/equcab1</s_2>
-    <s_3>/g/equcab1</s_3>
-    <s_4>/g/canfam2</s_4>
-    <s_5>/g/hg18</s_5>
-    <s_6>/g/hg18</s_6>
-    <s_7>/g/hg18</s_7>
-    <s_8>/g/hg18</s_8>
-  </ELAND_GENOME>
-  <READ_LENGTH>
-    <s_1>32</s_1>
-    <s_2>32</s_2>
-    <s_3>32</s_3>
-    <s_4>32</s_4>
-    <s_5>32</s_5>
-    <s_6>32</s_6>
-    <s_7>32</s_7>
-    <s_8>32</s_8>
-  </READ_LENGTH>
-  <USE_BASES>
-    <s_1>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_1>
-    <s_2>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_2>
-    <s_3>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_3>
-    <s_4>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_4>
-    <s_5>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_5>
-    <s_6>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_6>
-    <s_7>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_7>
-    <s_8>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_8>
-  </USE_BASES>
-</LaneSpecificRunParameters>
-</RunParameters>
-"""
-    pathname = os.path.join(gerald_dir, 'config.xml')
-    f = open(pathname,'w')
-    f.write(config_xml)
-    f.close()
-    
 
 def make_summary_htm(gerald_dir):
     summary_htm = """<!--RUN_TIME Mon Apr 21 11:52:25 2008 -->
@@ -588,7 +456,7 @@ class RunfolderTests(unittest.TestCase):
 
         r2 = runfolder.PipelineRun(xml=xml)
         self.failUnlessEqual(r1.name, r2.name)
-        self.failIfEqual(r2.firecrest, None)
+        self.failIfEqual(r2.image_analysis, None)
         self.failIfEqual(r2.bustard, None)
         self.failIfEqual(r2.gerald, None)
         
index 7457b129386a8275805714df88fdcaa8ff1e63ca..69227644947635b2e8c1bf870324bc290cc5ad62 100644 (file)
@@ -12,139 +12,8 @@ from htsworkflow.pipelines import gerald
 from htsworkflow.pipelines import runfolder
 from htsworkflow.pipelines.runfolder import ElementTree
 
+from htsworkflow.pipelines.test.simulate_runfolder import *
 
-def make_flowcell_id(runfolder_dir, flowcell_id=None):
-    if flowcell_id is None:
-        flowcell_id = '207BTAAXY'
-
-    config = """<?xml version="1.0"?>
-<FlowcellId>
-  <Text>%s</Text>
-</FlowcellId>""" % (flowcell_id,)
-    config_dir = os.path.join(runfolder_dir, 'Config')
-    
-    if not os.path.exists(config_dir):
-        os.mkdir(config_dir)
-    pathname = os.path.join(config_dir, 'FlowcellId.xml')
-    f = open(pathname,'w')
-    f.write(config)
-    f.close()
-
-def make_matrix(matrix_dir):
-    contents = """# Auto-generated frequency response matrix
-> A
-> C
-> G
-> T
-0.77 0.15 -0.04 -0.04 
-0.76 1.02 -0.05 -0.06 
--0.10 -0.10 1.17 -0.03 
--0.13 -0.12 0.80 1.27 
-"""
-    s_matrix = os.path.join(matrix_dir, 's_matrix.txt')
-    f = open(s_matrix, 'w')
-    f.write(contents)
-    f.close()
-    
-def make_phasing_params(bustard_dir):
-    for lane in range(1,9):
-        pathname = os.path.join(bustard_dir, 'params%d.xml' % (lane))
-        f = open(pathname, 'w')
-        f.write("""<Parameters>
-  <Phasing>0.009900</Phasing>
-  <Prephasing>0.003500</Prephasing>
-</Parameters>
-""")
-        f.close()
-
-def make_gerald_config(gerald_dir):
-    config_xml = """<RunParameters>
-<ChipWideRunParameters>
-  <ANALYSIS>default</ANALYSIS>
-  <BAD_LANES></BAD_LANES>
-  <BAD_TILES></BAD_TILES>
-  <CONTAM_DIR></CONTAM_DIR>
-  <CONTAM_FILE></CONTAM_FILE>
-  <ELAND_GENOME>Need_to_specify_ELAND_genome_directory</ELAND_GENOME>
-  <ELAND_MULTIPLE_INSTANCES>8</ELAND_MULTIPLE_INSTANCES>
-  <ELAND_REPEAT></ELAND_REPEAT>
-  <EMAIL_DOMAIN>domain.com</EMAIL_DOMAIN>
-  <EMAIL_LIST>diane</EMAIL_LIST>
-  <EMAIL_SERVER>localhost:25</EMAIL_SERVER>
-  <EXPT_DIR>/home/diane/gec/080416_HWI-EAS229_0024_207BTAAXX/Data/C1-33_Firecrest1.8.28_19-04-2008_diane/Bustard1.8.28_19-04-2008_diane</EXPT_DIR>
-  <EXPT_DIR_ROOT>/home/diane/gec</EXPT_DIR_ROOT>
-  <FORCE>1</FORCE>
-  <GENOME_DIR>/home/diane/proj/SolexaPipeline-0.2.2.6/Goat/../Gerald/../../Genomes</GENOME_DIR>
-  <GENOME_FILE>Need_to_specify_genome_file_name</GENOME_FILE>
-  <HAMSTER_FLAG>genome</HAMSTER_FLAG>
-  <OUT_DIR>/home/diane/gec/080416_HWI-EAS229_0024_207BTAAXX/Data/C1-33_Firecrest1.8.28_19-04-2008_diane/Bustard1.8.28_19-04-2008_diane/GERALD_19-04-2008_diane</OUT_DIR>
-  <POST_RUN_COMMAND></POST_RUN_COMMAND>
-  <PRB_FILE_SUFFIX>_prb.txt</PRB_FILE_SUFFIX>
-  <PURE_BASES>12</PURE_BASES>
-  <QF_PARAMS>'((CHASTITY&gt;=0.6))'</QF_PARAMS>
-  <QHG_FILE_SUFFIX>_qhg.txt</QHG_FILE_SUFFIX>
-  <QUALITY_FORMAT>--symbolic</QUALITY_FORMAT>
-  <READ_LENGTH>32</READ_LENGTH>
-  <SEQUENCE_FORMAT>--scarf</SEQUENCE_FORMAT>
-  <SEQ_FILE_SUFFIX>_seq.txt</SEQ_FILE_SUFFIX>
-  <SIG_FILE_SUFFIX_DEPHASED>_sig2.txt</SIG_FILE_SUFFIX_DEPHASED>
-  <SIG_FILE_SUFFIX_NOT_DEPHASED>_sig.txt</SIG_FILE_SUFFIX_NOT_DEPHASED>
-  <SOFTWARE_VERSION>@(#) Id: GERALD.pl,v 1.68.2.2 2007/06/13 11:08:49 km Exp</SOFTWARE_VERSION>
-  <TILE_REGEX>s_[1-8]_[0-9][0-9][0-9][0-9]</TILE_REGEX>
-  <TILE_ROOT>s</TILE_ROOT>
-  <TIME_STAMP>Sat Apr 19 19:08:30 2008</TIME_STAMP>
-  <TOOLS_DIR>/home/diane/proj/SolexaPipeline-0.2.2.6/Goat/../Gerald</TOOLS_DIR>
-  <USE_BASES>all</USE_BASES>
-  <WEB_DIR_ROOT>http://host.domain.com/yourshare/</WEB_DIR_ROOT>
-</ChipWideRunParameters>
-<LaneSpecificRunParameters>
-  <ANALYSIS>
-    <s_1>eland</s_1>
-    <s_2>eland</s_2>
-    <s_3>eland</s_3>
-    <s_4>eland</s_4>
-    <s_5>eland</s_5>
-    <s_6>eland</s_6>
-    <s_7>eland</s_7>
-    <s_8>eland</s_8>
-  </ANALYSIS>
-  <ELAND_GENOME>
-    <s_1>/g/dm3</s_1>
-    <s_2>/g/equcab1</s_2>
-    <s_3>/g/equcab1</s_3>
-    <s_4>/g/canfam2</s_4>
-    <s_5>/g/hg18</s_5>
-    <s_6>/g/hg18</s_6>
-    <s_7>/g/hg18</s_7>
-    <s_8>/g/hg18</s_8>
-  </ELAND_GENOME>
-  <READ_LENGTH>
-    <s_1>32</s_1>
-    <s_2>32</s_2>
-    <s_3>32</s_3>
-    <s_4>32</s_4>
-    <s_5>32</s_5>
-    <s_6>32</s_6>
-    <s_7>32</s_7>
-    <s_8>32</s_8>
-  </READ_LENGTH>
-  <USE_BASES>
-    <s_1>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_1>
-    <s_2>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_2>
-    <s_3>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_3>
-    <s_4>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_4>
-    <s_5>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_5>
-    <s_6>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_6>
-    <s_7>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_7>
-    <s_8>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY</s_8>
-  </USE_BASES>
-</LaneSpecificRunParameters>
-</RunParameters>
-"""
-    pathname = os.path.join(gerald_dir, 'config.xml')
-    f = open(pathname,'w')
-    f.write(config_xml)
-    f.close()
     
 def make_summary_htm(gerald_dir):
     summary_htm="""<!--RUN_TIME Wed Jul  2 06:47:44 2008 -->
@@ -1011,7 +880,7 @@ class RunfolderTests(unittest.TestCase):
 
         r2 = runfolder.PipelineRun(xml=xml)
         self.failUnlessEqual(r1.name, r2.name)
-        self.failIfEqual(r2.firecrest, None)
+        self.failIfEqual(r2.image_analysis, None)
         self.failIfEqual(r2.bustard, None)
         self.failIfEqual(r2.gerald, None)
         
diff --git a/htsworkflow/pipelines/test/test_runfolder_ipar100.py b/htsworkflow/pipelines/test/test_runfolder_ipar100.py
new file mode 100644 (file)
index 0000000..8be0db1
--- /dev/null
@@ -0,0 +1,271 @@
+#!/usr/bin/env python
+
+from datetime import datetime, date
+import os
+import tempfile
+import shutil
+import unittest
+
+from htsworkflow.pipelines import ipar
+from htsworkflow.pipelines import bustard
+from htsworkflow.pipelines import gerald
+from htsworkflow.pipelines import runfolder
+from htsworkflow.pipelines.runfolder import ElementTree
+
+from htsworkflow.pipelines.test.simulate_runfolder import *
+
+
+def make_runfolder(obj=None):
+    """
+    Make a fake runfolder, attach all the directories to obj if defined
+    """
+    # make a fake runfolder directory
+    temp_dir = tempfile.mkdtemp(prefix='tmp_runfolder_')
+
+    runfolder_dir = os.path.join(temp_dir,
+                                 '080102_HWI-EAS229_0010_207BTAAXX')
+    os.mkdir(runfolder_dir)
+
+    data_dir = os.path.join(runfolder_dir, 'Data')
+    os.mkdir(data_dir)
+
+    ipar_dir = make_ipar_dir(data_dir)
+
+    matrix_dir = os.path.join(ipar_dir, 'Matrix')
+    os.mkdir(matrix_dir)
+    make_matrix(matrix_dir)
+
+    bustard_dir = os.path.join(ipar_dir,
+                               'Bustard1.8.28_12-04-2008_diane')
+    os.mkdir(bustard_dir)
+    make_phasing_params(bustard_dir)
+
+    gerald_dir = os.path.join(bustard_dir,
+                              'GERALD_12-04-2008_diane')
+    os.mkdir(gerald_dir)
+    make_gerald_config(gerald_dir)
+    make_summary100_htm(gerald_dir)
+    make_eland_results(gerald_dir)
+
+    if obj is not None:
+        obj.temp_dir = temp_dir
+        obj.runfolder_dir = runfolder_dir
+        obj.data_dir = data_dir
+        obj.image_analysis_dir = ipar_dir
+        obj.matrix_dir = matrix_dir
+        obj.bustard_dir = bustard_dir
+        obj.gerald_dir = gerald_dir
+
+
+class RunfolderTests(unittest.TestCase):
+    """
+    Test components of the runfolder processing code
+    which includes firecrest, bustard, and gerald
+    """
+    def setUp(self):
+        # attaches all the directories to the object passed in
+        make_runfolder(self)
+
+    def tearDown(self):
+        shutil.rmtree(self.temp_dir)
+
+    def test_ipar(self):
+        """
+        Construct a firecrest object
+        """
+        i = ipar.ipar(self.image_analysis_dir)
+        self.failUnlessEqual(i.version, '2.01.192.0')
+        self.failUnlessEqual(i.start, 1)
+        self.failUnlessEqual(i.stop, 37)
+
+        xml = i.get_elements()
+        # just make sure that element tree can serialize the tree
+        xml_str = ElementTree.tostring(xml)
+
+        i2 = ipar.IPAR(xml=xml)
+        self.failUnlessEqual(i.version, i2.version)
+        self.failUnlessEqual(i.start,   i2.start)
+        self.failUnlessEqual(i.stop,    i2.stop)
+        self.failUnlessEqual(i.date,    i2.date)
+        self.failUnlessEqual(i.file_list(), i2.file_list())
+
+    def test_bustard(self):
+        """
+        construct a bustard object
+        """
+        b = bustard.bustard(self.bustard_dir)
+        self.failUnlessEqual(b.version, '1.8.28')
+        self.failUnlessEqual(b.date,    date(2008,4,12))
+        self.failUnlessEqual(b.user,    'diane')
+        self.failUnlessEqual(len(b.phasing), 8)
+        self.failUnlessAlmostEqual(b.phasing[8].phasing, 0.0099)
+
+        xml = b.get_elements()
+        b2 = bustard.Bustard(xml=xml)
+        self.failUnlessEqual(b.version, b2.version)
+        self.failUnlessEqual(b.date,    b2.date )
+        self.failUnlessEqual(b.user,    b2.user)
+        self.failUnlessEqual(len(b.phasing), len(b2.phasing))
+        for key in b.phasing.keys():
+            self.failUnlessEqual(b.phasing[key].lane,
+                                 b2.phasing[key].lane)
+            self.failUnlessEqual(b.phasing[key].phasing,
+                                 b2.phasing[key].phasing)
+            self.failUnlessEqual(b.phasing[key].prephasing,
+                                 b2.phasing[key].prephasing)
+
+    def test_gerald(self):
+        # need to update gerald and make tests for it
+        g = gerald.gerald(self.gerald_dir)
+
+        self.failUnlessEqual(g.version,
+            '@(#) Id: GERALD.pl,v 1.68.2.2 2007/06/13 11:08:49 km Exp')
+        self.failUnlessEqual(g.date, datetime(2008,4,19,19,8,30))
+        self.failUnlessEqual(len(g.lanes), len(g.lanes.keys()))
+        self.failUnlessEqual(len(g.lanes), len(g.lanes.items()))
+
+
+        # list of genomes, matches what was defined up in
+        # make_gerald_config.
+        # the first None is to offset the genomes list to be 1..9
+        # instead of pythons default 0..8
+        genomes = [None, '/g/dm3', '/g/equcab1', '/g/equcab1', '/g/canfam2',
+                         '/g/hg18', '/g/hg18', '/g/hg18', '/g/hg18', ]
+
+        # test lane specific parameters from gerald config file
+        for i in range(1,9):
+            cur_lane = g.lanes[str(i)]
+            self.failUnlessEqual(cur_lane.analysis, 'eland')
+            self.failUnlessEqual(cur_lane.eland_genome, genomes[i])
+            self.failUnlessEqual(cur_lane.read_length, '32')
+            self.failUnlessEqual(cur_lane.use_bases, 'Y'*32)
+
+        # test data extracted from summary file
+        clusters = [None,
+                    (96483, 9074), (133738, 7938),
+                    (152142, 10002), (15784, 2162),
+                    (119735, 8465), (152177, 8146),
+                    (84649, 7325), (54622, 4812),]
+
+        for i in range(1,9):
+            summary_lane = g.summary[str(i)]
+            self.failUnlessEqual(summary_lane.cluster, clusters[i])
+            self.failUnlessEqual(summary_lane.lane, str(i))
+
+        xml = g.get_elements()
+        # just make sure that element tree can serialize the tree
+        xml_str = ElementTree.tostring(xml)
+        g2 = gerald.Gerald(xml=xml)
+
+        # do it all again after extracting from the xml file
+        self.failUnlessEqual(g.version, g2.version)
+        self.failUnlessEqual(g.date, g2.date)
+        self.failUnlessEqual(len(g.lanes.keys()), len(g2.lanes.keys()))
+        self.failUnlessEqual(len(g.lanes.items()), len(g2.lanes.items()))
+
+        # test lane specific parameters from gerald config file
+        for i in range(1,9):
+            g_lane = g.lanes[str(i)]
+            g2_lane = g2.lanes[str(i)]
+            self.failUnlessEqual(g_lane.analysis, g2_lane.analysis)
+            self.failUnlessEqual(g_lane.eland_genome, g2_lane.eland_genome)
+            self.failUnlessEqual(g_lane.read_length, g2_lane.read_length)
+            self.failUnlessEqual(g_lane.use_bases, g2_lane.use_bases)
+
+        # test (some) summary elements
+        for i in range(1,9):
+            g_summary = g.summary[str(i)]
+            g2_summary = g2.summary[str(i)]
+            self.failUnlessEqual(g_summary.cluster, g2_summary.cluster)
+            self.failUnlessEqual(g_summary.lane, g2_summary.lane)
+
+            g_eland = g.eland_results
+            g2_eland = g2.eland_results
+            for lane in g_eland.keys():
+                self.failUnlessEqual(g_eland[lane].reads,
+                                     g2_eland[lane].reads)
+                self.failUnlessEqual(len(g_eland[lane].mapped_reads),
+                                     len(g2_eland[lane].mapped_reads))
+                for k in g_eland[lane].mapped_reads.keys():
+                    self.failUnlessEqual(g_eland[lane].mapped_reads[k],
+                                         g2_eland[lane].mapped_reads[k])
+
+                self.failUnlessEqual(len(g_eland[lane].match_codes),
+                                     len(g2_eland[lane].match_codes))
+                for k in g_eland[lane].match_codes.keys():
+                    self.failUnlessEqual(g_eland[lane].match_codes[k],
+                                         g2_eland[lane].match_codes[k])
+
+
+    def test_eland(self):
+        dm3_map = { 'chrUextra.fa' : 'dm3/chrUextra.fa',
+                    'chr2L.fa': 'dm3/chr2L.fa',
+                    'Lambda.fa': 'Lambda.fa'}
+        genome_maps = { '1':dm3_map, '2':dm3_map, '3':dm3_map, '4':dm3_map,
+                        '5':dm3_map, '6':dm3_map, '7':dm3_map, '8':dm3_map }
+        eland = gerald.eland(self.gerald_dir, genome_maps=genome_maps)
+
+        for i in range(1,9):
+            lane = eland[str(i)]
+            self.failUnlessEqual(lane.reads, 4)
+            self.failUnlessEqual(lane.sample_name, "s")
+            self.failUnlessEqual(lane.lane_id, unicode(i))
+            self.failUnlessEqual(len(lane.mapped_reads), 3)
+            self.failUnlessEqual(lane.mapped_reads['Lambda.fa'], 1)
+            self.failUnlessEqual(lane.mapped_reads['dm3/chr2L.fa'], 1)
+            self.failUnlessEqual(lane.match_codes['U1'], 2)
+            self.failUnlessEqual(lane.match_codes['NM'], 1)
+
+        xml = eland.get_elements()
+        # just make sure that element tree can serialize the tree
+        xml_str = ElementTree.tostring(xml)
+        e2 = gerald.ELAND(xml=xml)
+
+        for i in range(1,9):
+            l1 = eland[str(i)]
+            l2 = e2[str(i)]
+            self.failUnlessEqual(l1.reads, l2.reads)
+            self.failUnlessEqual(l1.sample_name, l2.sample_name)
+            self.failUnlessEqual(l1.lane_id, l2.lane_id)
+            self.failUnlessEqual(len(l1.mapped_reads), len(l2.mapped_reads))
+            self.failUnlessEqual(len(l1.mapped_reads), 3)
+            for k in l1.mapped_reads.keys():
+                self.failUnlessEqual(l1.mapped_reads[k],
+                                     l2.mapped_reads[k])
+
+            self.failUnlessEqual(len(l1.match_codes), 9)
+            self.failUnlessEqual(len(l1.match_codes), len(l2.match_codes))
+            for k in l1.match_codes.keys():
+                self.failUnlessEqual(l1.match_codes[k],
+                                     l2.match_codes[k])
+
+    def test_runfolder(self):
+        runs = runfolder.get_runs(self.runfolder_dir)
+
+        # do we get the flowcell id from the filename?
+        self.failUnlessEqual(len(runs), 1)
+        self.failUnlessEqual(runs[0].name, 'run_207BTAAXX_2008-10-30.xml')
+
+        # do we get the flowcell id from the FlowcellId.xml file
+        make_flowcell_id(self.runfolder_dir, '207BTAAXY')
+        runs = runfolder.get_runs(self.runfolder_dir)
+        self.failUnlessEqual(len(runs), 1)
+        self.failUnlessEqual(runs[0].name, 'run_207BTAAXY_2008-10-30.xml')
+
+        r1 = runs[0]
+        xml = r1.get_elements()
+        xml_str = ElementTree.tostring(xml)
+
+        r2 = runfolder.PipelineRun(xml=xml)
+        self.failUnlessEqual(r1.name, r2.name)
+        self.failIfEqual(r2.image_analysis, None)
+        self.failIfEqual(r2.bustard, None)
+        self.failIfEqual(r2.gerald, None)
+
+
+def suite():
+    return unittest.makeSuite(RunfolderTests,'test')
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="suite")
+