Merge branch 'master' of mus.cacr.caltech.edu:htsworkflow
authorDiane Trout <diane@caltech.edu>
Sat, 11 Jun 2011 00:24:52 +0000 (17:24 -0700)
committerDiane Trout <diane@caltech.edu>
Sat, 11 Jun 2011 00:24:52 +0000 (17:24 -0700)
21 files changed:
htsworkflow/frontend/experiments/admin.py
htsworkflow/frontend/experiments/fixtures/initial_data.json [new file with mode: 0644]
htsworkflow/frontend/experiments/fixtures/test_flowcells.json
htsworkflow/frontend/experiments/models.py
htsworkflow/frontend/experiments/tests.py
htsworkflow/frontend/experiments/urls.py
htsworkflow/frontend/experiments/views.py
htsworkflow/frontend/samples/models.py
htsworkflow/frontend/samples/views.py
htsworkflow/frontend/static/css/app.css
htsworkflow/frontend/templates/experiments/flowcell_detail.html
htsworkflow/frontend/templates/experiments/flowcell_lane_detail.html
htsworkflow/frontend/templates/sample_header.html [new file with mode: 0644]
htsworkflow/frontend/templates/samples/library_detail.html
htsworkflow/frontend/urls.py
htsworkflow/pipelines/test/test_retrive_config.py
htsworkflow/pipelines/test/testdata/woldlab_070829_USI-EAS44_0017_FC11055_1.srf [new file with mode: 0644]
htsworkflow/pipelines/test/testdata/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf [new file with mode: 0644]
test/test_srf2fastq.py
test/woldlab_070829_USI-EAS44_0017_FC11055_1.srf [deleted file]
test/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf [deleted file]

index eb243a70b264ebc371b81235652a39d383eeab00..4181e5c91aef2aa0d2a7af9b2d0bddcc37ad65e6 100644 (file)
@@ -1,4 +1,5 @@
-from htsworkflow.frontend.experiments.models import FlowCell, DataRun, ClusterStation, Sequencer, Lane
+from htsworkflow.frontend.experiments.models import \
+     FlowCell, DataRun, DataFile, FileType, ClusterStation, Sequencer, Lane
 from django.contrib import admin
 from django.contrib.admin.widgets import FilteredSelectMultiple
 from django.forms import ModelForm
@@ -6,20 +7,45 @@ from django.forms.fields import Field, CharField
 from django.forms.widgets import TextInput
 from django.utils.translation import ugettext_lazy as _
 
+class DataFileForm(ModelForm):
+    class Meta:
+        model = DataFile
+
+class DataFileInline(admin.TabularInline):
+    model = DataFile
+    form = DataFileForm
+    raw_id_fields = ('library',)
+    extra = 0
+
 class DataRunOptions(admin.ModelAdmin):
   search_fields = [
+      'flowcell_id',
       'run_folder',
       'run_note',
-      'config_params', ]
+      ]
   list_display = [
-      'run_folder', 
-      'Flowcell_Info', 
+      'runfolder_name',
+      'result_dir',
       'run_start_time',
-      'main_status', 
-      'run_note',
   ]
-  list_filter = ('run_status', 'run_start_time')
+  fieldsets = (
+      (None, {
+        'fields': (('flowcell', 'run_status'),
+                   ('runfolder_name', 'cycle_start', 'cycle_stop'),
+                   ('result_dir',),
+                   ('last_update_time'),
+                   ('comment',))
+      }),
+    )
+  inlines = [ DataFileInline ]
+  #list_filter = ('run_status', 'run_start_time')
+admin.site.register(DataRun, DataRunOptions)
+
 
+class FileTypeAdmin(admin.ModelAdmin):
+    list_display = ('name', 'mimetype', 'regex')
+admin.site.register(FileType, FileTypeAdmin)
+  
 # lane form setup needs to come before Flowcell form config
 # as flowcell refers to the LaneInline class
 class LaneForm(ModelForm):
@@ -63,6 +89,7 @@ class LaneOptions(admin.ModelAdmin):
         'fields': ('comment', )
       }),
     )
+admin.site.register(Lane, LaneOptions)
     
 class FlowCellOptions(admin.ModelAdmin):
     date_hierarchy = "run_date"
@@ -92,18 +119,14 @@ class FlowCellOptions(admin.ModelAdmin):
         if db_field.name == "notes":
             field.widget.attrs["rows"] = "3"
         return field
+admin.site.register(FlowCell, FlowCellOptions)
 
 class ClusterStationOptions(admin.ModelAdmin):
     list_display = ('name', )
     fieldsets = ( ( None, { 'fields': ( 'name', ) } ), )
+admin.site.register(ClusterStation, ClusterStationOptions)
 
 class SequencerOptions(admin.ModelAdmin):
     list_display = ('name', )
     fieldsets = ( ( None, { 'fields': ( 'name', ) } ), )
-    
-
-#admin.site.register(DataRun, DataRunOptions)
-admin.site.register(FlowCell, FlowCellOptions)
-admin.site.register(ClusterStation, ClusterStationOptions)
 admin.site.register(Sequencer, SequencerOptions)
-admin.site.register(Lane, LaneOptions)
diff --git a/htsworkflow/frontend/experiments/fixtures/initial_data.json b/htsworkflow/frontend/experiments/fixtures/initial_data.json
new file mode 100644 (file)
index 0000000..67a6393
--- /dev/null
@@ -0,0 +1,109 @@
+[
+  { "model": "experiments.FileType",
+    "pk": 1,
+    "fields": {
+      "name": "run_xml",
+      "mimetype": "application/vnd.htsworkflow-run-xml",
+      "regex": "run.*\\.xml\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 2,
+    "fields": {
+      "name": "Summary.htm",
+      "mimetype": "text/html",
+      "regex": "Summary\\.htm\\Z(?ms)"
+    }
+  },
+
+  { "model": "experiments.FileType",
+    "pk": 3,
+    "fields": {
+      "name": "IVC All",
+      "mimetype": "image/png",
+      "regex": "s_(?P<lane>[0-9])_all\\.png\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 4,
+    "fields": {
+      "name": "IVC Call",
+      "mimetype": "image/png",
+      "regex": "s_(?P<lane>[0-9])_call\\.png\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 5,
+    "fields": {
+      "name": "IVC Percent All",
+      "mimetype": "image/png",
+      "regex": "s_(?P<lane>[0-9])_percent_all\\.png\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 6,
+    "fields": {
+      "name": "IVC Percent Base",
+      "mimetype": "image/png",
+      "regex": "s_(?P<lane>[0-9])_percent_base\\.png\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 7,
+    "fields": {
+      "name": "IVC Percent Call",
+      "mimetype": "image/png",
+      "regex": "s_(?P<lane>[0-9])_percent_call\\.png\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 8,
+    "fields": {
+      "name": "GERALD Scores",
+      "regex": "scores\\.tar\\.bz2\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 9,
+    "fields": {
+      "name": "ELAND Result",
+      "regex": "s_(?P<lane>[0-9])((?P<end>[1-4])_)?_eland_result\\.txt\\.bz2\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 10,
+    "fields": {
+      "name": "ELAND Multi",
+      "regex": "s_(?P<lane>[0-9])((?P<end>[1-4])_)?_eland_multi\\.txt\\.bz2\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 11,
+    "fields": {
+      "name": "ELAND Extended",
+      "regex": "s_(?P<lane>[0-9])((?P<end>[1-4])_)?_eland_extended\\.txt\\.bz2\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 12,
+    "fields": {
+      "name": "ELAND Export",
+      "regex": "s_(?P<lane>[0-9])((?P<end>[1-4])_)?_export\\.txt\\.bz2\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 13,
+    "fields": {
+      "name": "SRF",
+      "regex": ".*_(?P<lane>[1-8])\\.srf\\Z(?ms)"
+    }
+  },
+  { "model": "experiments.FileType",
+    "pk": 14,
+    "fields": {
+      "name": "QSEQ tarfile",
+      "regex": ".*_l(?P<lane>[1-8])_r(?P<end>[1-4])\\.tar\\.bz2\\Z(?ms)"
+    }
+  }
+
+]
\ No newline at end of file
index 37adf5ed03a2645035286e6092f4d2200f54a1f6..a37fced3f197134abeffebb5eb3c7b56b0dd837a 100644 (file)
  {"pk": 153, "model": "experiments.flowcell", 
   "fields": {
       "paired_end": true, 
-      "run_date": "2009-09-11 22:12:13", 
-      "read_length": 75
+      "run_date": "2007-09-27 22:12:13", 
+      "read_length": 36
       "notes": "",
       "advanced_run": false,
       "control_lane": 2,
       "cluster_station": 3,
       "sequencer": 2,
-      "flowcell_id": "303TUAAXX"
+      "flowcell_id": "FC12150"
       }
   }, 
   {"pk": 1193, "model": "experiments.lane",
index 8a71e252331c600ce12eeaea8b20511f337ad98c..4e060f9330229ed5125313ffadc5f2a7e2ff4121 100755 (executable)
@@ -1,12 +1,39 @@
+import datetime
+import glob
 import logging
+import os
+import re
+import types
+import uuid
 
+from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist
 from django.core import urlresolvers
 from django.db import models
+from django.db.models.signals import post_init
 
-from htsworkflow.frontend.samples.models import *
-#from htsworkflow.frontend.settings import options
-from django.conf import settings
+from htsworkflow.frontend.samples.models import Library
+from htsworkflow.frontend.samples.results import parse_flowcell_id
+from htsworkflow.pipelines import runfolder
+
+default_pM = 5
+try:
+  default_pM = int(settings.DEFAULT_PM)
+except ValueError,e:
+  logging.error("invalid value for frontend.default_pm")
+
+RUN_STATUS_CHOICES = (
+    (0, 'Sequencer running'), ##Solexa Data Pipeline Not Yet Started'),
+    (1, 'Data Pipeline Started'),
+    (2, 'Data Pipeline Interrupted'),
+    (3, 'Data Pipeline Finished'),
+    (4, 'Collect Results Started'),
+    (5, 'Collect Results Finished'),
+    (6, 'QC Started'),
+    (7, 'QC Finished'),
+    (255, 'DONE'),
+  )
+RUN_STATUS_REVERSE_MAP = dict(((v,k) for k,v in RUN_STATUS_CHOICES))
 
 class ClusterStation(models.Model):
   name = models.CharField(max_length=50, unique=True)
@@ -20,20 +47,13 @@ class Sequencer(models.Model):
   def __unicode__(self):
     return unicode(self.name)
 
-default_pM = 5
-try:
-  default_pM = int(settings.DEFAULT_PM)
-except ValueError,e:
-  logging.error("invalid value for frontend.default_pm")
-
 class FlowCell(models.Model):
-  
   flowcell_id = models.CharField(max_length=20, unique=True, db_index=True)
   run_date = models.DateTimeField()
   advanced_run = models.BooleanField(default=False)
   paired_end = models.BooleanField(default=False)
   read_length = models.IntegerField(default=32) #Stanford is currenlty 25
-  control_lane = models.IntegerField(choices=[(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(0,'All Lanes')], null=True)
+  control_lane = models.IntegerField(choices=[(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(0,'All Lanes')], null=True, blank=True)
 
   cluster_station = models.ForeignKey(ClusterStation, default=3)
   sequencer = models.ForeignKey(Sequencer, default=1)
@@ -43,21 +63,8 @@ class FlowCell(models.Model):
   def __unicode__(self):
       return unicode(self.flowcell_id) 
 
-  def Create_LOG(self):
-    str = ''
-    str +='<a target=_balnk href="/experiments/'+self.flowcell_id+'" title="Create XLS like sheet for this Flowcell ..." ">Create LOG</a>'
-    try:
-      t = DataRun.objects.get(fcid=self.id)
-      str +='<br/><a target=_self href="/admin/experiments/datarun/?q='+self.flowcell_id+'" title="Check Data Runs ..." ">DataRun ..</a>'
-    except ObjectDoesNotExist:
-      str += '<br/><span style="color:red">not sequenced</span>'
-    return str
-  Create_LOG.allow_tags = True 
-
   def Lanes(self):
-    library_url = '/admin/samples/library/%s' 
     html = ['<table>']
-    #for i in range(1,9):
     for lane in self.lane_set.all():
         cluster_estimate = lane.cluster_estimate
         if cluster_estimate is not None:
@@ -67,8 +74,10 @@ class FlowCell(models.Model):
         library_id = lane.library_id
         library = lane.library
         element = '<tr><td>%d</td><td><a href="%s">%s</a></td><td>%s</td></tr>'
-        expanded_library_url = library_url %(library_id,)
-        html.append(element % (lane.lane_number, expanded_library_url, library, cluster_estimate))
+        html.append(element % (lane.lane_number,
+                               library.get_admin_url(),
+                               library,
+                               cluster_estimate))
     html.append('</table>')
     return "\n".join(html)
   Lanes.allow_tags = True
@@ -78,8 +87,8 @@ class FlowCell(models.Model):
 
   def get_admin_url(self):
     # that's the django way... except it didn't work
-    #return urlresolvers.reverse('admin_experiments_FlowCell_change', args=(self.id,))
-    return '/admin/experiments/flowcell/%s/' % (self.id,)
+    return urlresolvers.reverse('admin:experiments_flowcell_change',
+                                args=(self.id,))
 
   def flowcell_type(self):
     """
@@ -95,59 +104,63 @@ class FlowCell(models.Model):
       return ('htsworkflow.frontend.experiments.views.flowcell_detail',
               [str(self.flowcell_id)])
     
-### -----------------------
-class DataRun(models.Model):
-  ConfTemplate = "CONFIG PARAMS WILL BE GENERATED BY THE PIPELINE SCRIPT.\nYOU'LL BE ABLE TO EDIT AFTER IF NEEDED."
-  run_folder = models.CharField(max_length=50,unique=True, db_index=True)
-  fcid = models.ForeignKey(FlowCell,verbose_name="Flowcell Id")
-  config_params = models.TextField(default=ConfTemplate)
-  run_start_time = models.DateTimeField()
-  RUN_STATUS_CHOICES = (
-      (0, 'Sequencer running'), ##Solexa Data Pipeline Not Yet Started'),
-      (1, 'Data Pipeline Started'),
-      (2, 'Data Pipeline Interrupted'),
-      (3, 'Data Pipeline Finished'),
-      (4, 'CollectReads Started'),
-      (5, 'CollectReads Finished'),
-      (6, 'QC Finished'),
-      (7, 'DONE'),
-    )
-  run_status = models.IntegerField(choices=RUN_STATUS_CHOICES, default=0)
-  run_note = models.TextField(blank=True)
-
-
-  def main_status(self):
-    str = '<div'
-    if self.run_status >= 5:
-      str += ' style="color:green">'
-      str += '<b>'+self.RUN_STATUS_CHOICES[self.run_status][1]+'</b>'
-      str += '<br/><br/>' #<span style="color:red;font-size:80%;">New!</span>'
-      str +='<br/><a target=_balnk href="'+settings.TASKS_PROJS_SERVER+'/Flowcells/'+self.fcid.flowcell_id+'/'+self.fcid.flowcell_id+'_QC_Summary.html" title="View QC Summaries of this run ..." ">View QC Page</a>'
-    else:
-      str += '>'+self.RUN_STATUS_CHOICES[self.run_status][1]
+  def get_raw_data_directory(self):
+      """Return location of where the raw data is stored"""
+      flowcell_id, status = parse_flowcell_id(self.flowcell_id)
 
-    str += '</div>'
-    return str
-  main_status.allow_tags = True
+      return os.path.join(settings.RESULT_HOME_DIR, flowcell_id)
 
-  main_status.allow_tags = True
+  def update_data_runs(self):
+      result_root = self.get_raw_data_directory()
+      if result_root is None:
+          return
   
-  def Flowcell_Info(self):
-    str = '<b>'+self.fcid.__str__()+'</b>'
-    str += '  (c: '+self.fcid.cluster_mac_id+',  s: '+self.fcid.seq_mac_id+')'
-    str += '<div style="margin-top:5px;">'    
-    str +='<a title="View Lane List here ..."  onClick="el = document.getElementById(\'LanesOf'+self.fcid.__str__()+'\');if(el) (el.style.display==\'none\'?el.style.display=\'block\':el.style.display=\'none\')" style="cursor:pointer;color: #5b80b2;">View/hide lanes</a>'
-    str += '<div id="LanesOf'+self.fcid.__str__()+'" style="display:block;border:solid #cccccc 1px;width:350px">'
-    LanesList = '1: '+self.fcid.lane_1_library.__str__()+' ('+self.fcid.lane_1_library.library_species.use_genome_build+')<br/>2: '+self.fcid.lane_2_library.__str__()+' ('+self.fcid.lane_2_library.library_species.use_genome_build+')<br/>3: '+self.fcid.lane_3_library.__str__()+' ('+self.fcid.lane_3_library.library_species.use_genome_build+')<br/>4: '+self.fcid.lane_4_library.__str__()+' ('+self.fcid.lane_4_library.library_species.use_genome_build+')<br/>5: '+self.fcid.lane_5_library.__str__()+' ('+self.fcid.lane_5_library.library_species.use_genome_build+')<br/>6: '+self.fcid.lane_6_library.__str__()+' ('+self.fcid.lane_6_library.library_species.use_genome_build+')<br/>7: '+self.fcid.lane_7_library.__str__()+' ('+self.fcid.lane_7_library.library_species.use_genome_build+')<br/>8: '+self.fcid.lane_8_library.__str__()+' ('+self.fcid.lane_8_library.library_species.use_genome_build+')'
-    str += LanesList ## self.fcid.Lanes()
-    str += '</div>'
-    str += '<div><a title="open Flowcell record" href="/admin/exp_track/flowcell/'+self.fcid.id.__str__()+'/" target=_self>Edit Flowcell record</a>'
-    #str += '<span style="color:red;font-size:80%;margin-left:15px;margin-right:3px">New!</span>'
-    str +='<a style="margin-left:15px;" target=_balnk href="/exp_track/'+self.fcid.flowcell_id+'" title="View XLS like sheet for this Flowcell LOG ..." ">GA LOG Page</a>'
-    str += '</div>'
-    str += '</div>'    
-    return str
-  Flowcell_Info.allow_tags = True
+      result_home_dir = os.path.join(settings.RESULT_HOME_DIR,'')
+      run_xml_re = re.compile(glob.fnmatch.translate('run*.xml'))
+      
+      dataruns = self.datarun_set.all()
+      datarun_result_dirs = [ x.result_dir for x in dataruns ]
+
+      result_dirs = []
+      for dirpath, dirnames, filenames in os.walk(result_root):
+          for filename in filenames:
+              if run_xml_re.match(filename):
+                  # we have a run directory
+                  relative_pathname = get_relative_pathname(dirpath)
+                  if relative_pathname not in datarun_result_dirs:
+                      self.import_data_run(relative_pathname, filename)
+                
+  def import_data_run(self, relative_pathname, run_xml_name):
+      """Given a result directory import files"""
+      run_dir = get_absolute_pathname(relative_pathname)
+      run_xml_path = os.path.join(run_dir, run_xml_name)
+      run_xml_data = runfolder.load_pipeline_run_xml(run_xml_path)
+                                  
+      run = DataRun()
+      run.flowcell = self
+      run.status = RUN_STATUS_REVERSE_MAP['DONE']
+      run.result_dir = relative_pathname
+      run.runfolder_name = run_xml_data.runfolder_name
+      run.cycle_start = run_xml_data.image_analysis.start
+      run.cycle_stop = run_xml_data.image_analysis.stop
+      run.run_start_time = run_xml_data.image_analysis.date
+      run.last_update_time = datetime.datetime.now()
+      run.save()
+
+      run.update_result_files()
+      
+# FIXME: should we automatically update dataruns?
+#        Or should we expect someone to call update_data_runs?
+#def update_flowcell_dataruns(sender, instance, *args, **kwargs):
+#    """Update our dataruns
+#    """
+#    if not os.path.exists(settings.RESULT_HOME_DIR):
+#       return
+#
+#    instance.update_data_runs()    
+#post_init.connect(update_flowcell_dataruns, sender=FlowCell)
+
+
 
 LANE_STATUS_CODES = [(0, 'Failed'),
                     (1, 'Marginal'),
@@ -168,3 +181,156 @@ class Lane(models.Model):
   def get_absolute_url(self):
        return ('htsworkflow.frontend.experiments.views.flowcell_lane_detail',
                [str(self.flowcell.flowcell_id), str(self.lane_number)])
+
+                        
+### -----------------------
+class DataRun(models.Model):
+    flowcell = models.ForeignKey(FlowCell,verbose_name="Flowcell Id")
+    runfolder_name = models.CharField(max_length=50)
+    result_dir = models.CharField(max_length=255)
+    last_update_time = models.DateTimeField()
+    run_start_time = models.DateTimeField()
+    cycle_start = models.IntegerField(null=True, blank=True)
+    cycle_stop = models.IntegerField(null=True, blank=True)
+    run_status = models.IntegerField(choices=RUN_STATUS_CHOICES, 
+                                     null=True, blank=True)
+    comment = models.TextField(blank=True)
+
+    def update_result_files(self):
+        abs_result_dir = get_absolute_pathname(self.result_dir)
+        
+        for dirname, dirnames, filenames in os.walk(abs_result_dir):
+            for filename in filenames:
+                pathname = os.path.join(dirname, filename)
+                relative_pathname = get_relative_pathname(pathname)
+                datafiles = self.datafile_set.filter(
+                  data_run = self,
+                  relative_pathname=relative_pathname)
+                if len(datafiles) > 0:
+                    continue
+                  
+                metadata = find_file_type_metadata_from_filename(filename)
+                if metadata is not None:
+                    metadata['filename'] = filename
+                    newfile = DataFile()
+                    newfile.data_run = self
+                    newfile.file_type = metadata['file_type']
+                    newfile.relative_pathname = relative_pathname
+
+                    lane_number = metadata.get('lane', None)
+                    if lane_number is not None:
+                        lane = self.flowcell.lane_set.get(lane_number = lane_number)
+                        newfile.library = lane.library
+                    
+                    self.datafile_set.add(newfile)
+                    
+        self.last_update_time = datetime.datetime.now()
+
+    def lane_files(self):
+        lanes = {}
+        
+        for datafile in self.datafile_set.all():
+            metadata = datafile.attributes
+            if metadata is not None:
+                lane = metadata.get('lane', None)
+                if lane is not None:
+                    lane_file_set = lanes.setdefault(lane, {})
+                    lane_file_set[datafile.file_type.normalized_name] = datafile
+        return lanes
+
+    def ivc_plots(self, lane):
+        ivc_name = ['IVC All', 'IVC Call',
+                    'IVC Percent Base', 'IVC Percent All', 'IVC Percent Call']
+
+        plots = {}
+        for rel_filename, metadata in self.get_result_files():
+            if metadata.file_type.name in ivc_name:
+                plots[metadata.file_type.name] = (rel_filename, metadata)
+                
+class FileType(models.Model):
+    """Represent potential file types
+
+    regex is a pattern used to detect if a filename matches this type
+    data run currently assumes that there may be a (?P<lane>) and
+    (?P<end>) pattern in the regular expression.
+    """
+    name = models.CharField(max_length=50)
+    mimetype = models.CharField(max_length=50, null=True, blank=True)
+    # regular expression from glob.fnmatch.translate
+    regex = models.CharField(max_length=50, null=True, blank=True)
+
+    def parse_filename(self, pathname):
+        """Does filename match our pattern?
+
+        Returns None if not, or dictionary of match variables if we do.
+        """
+        path, filename = os.path.split(pathname)
+        if len(self.regex) > 0:
+            match = re.match(self.regex, filename)
+            if match is not None:
+                # These are (?P<>) names we know about from our default regexes.
+                results = match.groupdict()
+
+                # convert int parameters
+                for attribute_name in ['lane', 'end']:
+                    value = results.get(attribute_name, None)
+                    if value is not None:
+                        results[attribute_name] = int(value)
+                    
+                return results
+
+    def _get_normalized_name(self):
+        """Crush data file name into identifier friendly name"""
+        return self.name.replace(' ', '_').lower()
+    normalized_name = property(_get_normalized_name)
+              
+    def __unicode__(self):
+        #return u"<FileType: %s>" % (self.name,)
+        return self.name
+
+
+class DataFile(models.Model):
+    """Store map from random ID to filename"""
+    random_key = models.CharField(max_length=16,
+                                  db_index=True,
+                                  default=uuid.uuid1)
+    data_run = models.ForeignKey(DataRun, db_index=True)
+    library = models.ForeignKey(Library, db_index=True, null=True, blank=True)
+    file_type = models.ForeignKey(FileType)
+    relative_pathname = models.CharField(max_length=255, db_index=True)
+
+    def _get_attributes(self):
+        return self.file_type.parse_filename(self.relative_pathname)
+    attributes = property(_get_attributes)
+
+    def _get_pathname(self):
+        return get_absolute_pathname(self.relative_pathname)
+    pathname = property(_get_pathname)
+
+    @models.permalink
+    def get_absolute_url(self):
+        return ('htsworkflow.frontend.experiments.views.read_result_file',
+                (), {'key': self.random_key })
+
+def find_file_type_metadata_from_filename(pathname):
+    path, filename = os.path.split(pathname)
+    result = None
+    for file_type in FileType.objects.all():
+        result = file_type.parse_filename(filename)
+        if result is not None:
+            result['file_type'] = file_type
+            return result
+
+    return None
+  
+def get_relative_pathname(abspath):
+    """Strip off the result home directory from a path
+    """
+    result_home_dir = os.path.join(settings.RESULT_HOME_DIR,'')
+    relative_pathname = abspath.replace(result_home_dir,'')
+    return relative_pathname
+
+def get_absolute_pathname(relative_pathname):
+    """Attach relative path to  results home directory"""
+    return os.path.join(settings.RESULT_HOME_DIR, relative_pathname)
+
index 7bfebfec3f9a5190ef40dac3dacbcb51dcd3bdd9..16e31eef114d1cb562859c84bc00c0362ddde30d 100644 (file)
@@ -5,8 +5,11 @@ try:
 except ImportError, e:
     import simplejson as json
 import os
+import shutil
 import sys
+import tempfile
 
+from django.conf import settings
 from django.core import mail
 from django.core.exceptions import ObjectDoesNotExist
 from django.test import TestCase
@@ -14,19 +17,47 @@ from htsworkflow.frontend.experiments import models
 from htsworkflow.frontend.experiments import experiments
 from htsworkflow.frontend.auth import apidata
 
+from htsworkflow.pipelines.test.simulate_runfolder import TESTDATA_DIR
+
 LANE_SET = range(1,9)
 
 class ExperimentsTestCases(TestCase):
     fixtures = ['test_flowcells.json']
 
     def setUp(self):
-        pass
+        self.tempdir = tempfile.mkdtemp(prefix='htsw-test-experiments-')
+        settings.RESULT_HOME_DIR = self.tempdir
+
+        self.fc1_id = 'FC12150'
+        self.fc1_root = os.path.join(self.tempdir, self.fc1_id)
+        os.mkdir(self.fc1_root)
+        self.fc1_dir = os.path.join(self.fc1_root, 'C1-37')
+        os.mkdir(self.fc1_dir)
+        runxml = 'run_FC12150_2007-09-27.xml'
+        shutil.copy(os.path.join(TESTDATA_DIR, runxml),
+                    os.path.join(self.fc1_dir, runxml))
+        for i in range(1,9):
+            shutil.copy(
+                os.path.join(TESTDATA_DIR,
+                             'woldlab_070829_USI-EAS44_0017_FC11055_1.srf'),
+                os.path.join(self.fc1_dir,
+                             'woldlab_070829_SERIAL_FC12150_%d.srf' %(i,))
+                )
+        
+        self.fc2_dir = os.path.join(self.tempdir, '42JTNAAXX')
+        os.mkdir(self.fc2_dir)
+        os.mkdir(os.path.join(self.fc2_dir, 'C1-25'))
+        os.mkdir(os.path.join(self.fc2_dir, 'C1-37'))
+        os.mkdir(os.path.join(self.fc2_dir, 'C1-37', 'Plots'))
+
+    def tearDown(self):
+        shutil.rmtree(self.tempdir)
 
     def test_flowcell_information(self):
         """
         Check the code that packs the django objects into simple types.
         """
-        for fc_id in [u'303TUAAXX', u"42JTNAAXX", "42JU1AAXX"]:
+        for fc_id in [u'FC12150', u"42JTNAAXX", "42JU1AAXX"]:
             fc_dict = experiments.flowcell_information(fc_id)
             fc_django = models.FlowCell.objects.get(flowcell_id=fc_id)
             self.failUnlessEqual(fc_dict['flowcell_id'], fc_id)
@@ -81,14 +112,14 @@ class ExperimentsTestCases(TestCase):
         """
         Require logging in to retrieve meta data
         """
-        response = self.client.get(u'/experiments/config/303TUAAXX/json')
+        response = self.client.get(u'/experiments/config/FC12150/json')
         self.failUnlessEqual(response.status_code, 403)
 
     def test_library_id(self):
         """
         Library IDs should be flexible, so make sure we can retrive a non-numeric ID
         """
-        response = self.client.get('/experiments/config/303TUAAXX/json', apidata)
+        response = self.client.get('/experiments/config/FC12150/json', apidata)
         self.failUnlessEqual(response.status_code, 200)
         flowcell = json.loads(response.content)
 
@@ -165,6 +196,128 @@ class ExperimentsTestCases(TestCase):
         self.failUnlessEqual(response.status_code, 404)
 
 
+    def test_raw_data_dir(self):
+        """Raw data path generator check"""
+        flowcell_id = self.fc1_id
+        raw_dir = os.path.join(settings.RESULT_HOME_DIR, flowcell_id)
+        
+        fc = models.FlowCell.objects.get(flowcell_id=flowcell_id)
+        self.failUnlessEqual(fc.get_raw_data_directory(), raw_dir)
+
+        fc.flowcell_id = flowcell_id + " (failed)"
+        self.failUnlessEqual(fc.get_raw_data_directory(), raw_dir)
+
+
+    def test_data_run_import(self):
+        srf_file_type = models.FileType.objects.get(name='SRF')
+        runxml_file_type = models.FileType.objects.get(name='run_xml')
+        flowcell_id = self.fc1_id
+        flowcell = models.FlowCell.objects.get(flowcell_id=flowcell_id)
+        flowcell.update_data_runs()
+        self.failUnlessEqual(len(flowcell.datarun_set.all()), 1)
+
+        run = flowcell.datarun_set.all()[0]
+        result_files = run.datafile_set.all()
+        result_dict = dict(((rf.relative_pathname, rf) for rf in result_files))
+
+        srf4 = result_dict['FC12150/C1-37/woldlab_070829_SERIAL_FC12150_4.srf']
+        self.failUnlessEqual(srf4.file_type, srf_file_type)
+        self.failUnlessEqual(srf4.library_id, '11060')
+        self.failUnlessEqual(srf4.data_run.flowcell.flowcell_id, 'FC12150')
+        self.failUnlessEqual(
+            srf4.data_run.flowcell.lane_set.get(lane_number=4).library_id,
+            '11060')
+        self.failUnlessEqual(
+            srf4.pathname,
+            os.path.join(settings.RESULT_HOME_DIR, srf4.relative_pathname))
+
+        lane_files = run.lane_files()
+        self.failUnlessEqual(lane_files[4]['srf'], srf4)
+
+        runxml= result_dict['FC12150/C1-37/run_FC12150_2007-09-27.xml']
+        self.failUnlessEqual(runxml.file_type, runxml_file_type)
+        self.failUnlessEqual(runxml.library_id, None)
+            
+
+    def test_read_result_file(self):
+        """make sure we can return a result file
+        """
+        flowcell_id = self.fc1_id
+        flowcell = models.FlowCell.objects.get(flowcell_id=flowcell_id)
+        flowcell.update_data_runs()
+        
+        #self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5') 
+
+        result_files = flowcell.datarun_set.all()[0].datafile_set.all()
+        for f in result_files:
+            url = '/experiments/file/%s' % ( f.random_key,)
+            response = self.client.get(url)
+            self.failUnlessEqual(response.status_code, 200)
+            mimetype = f.file_type.mimetype
+            if mimetype is None:
+                mimetype = 'application/octet-stream'
+            
+            self.failUnlessEqual(mimetype, response['content-type'])
+        
+class TestFileType(TestCase):
+    def test_file_type_unicode(self):
+        file_type_objects = models.FileType.objects
+        name = 'QSEQ tarfile'
+        file_type_object = file_type_objects.get(name=name)
+        self.failUnlessEqual(u"<FileType: QSEQ tarfile>",
+                             unicode(file_type_object))
+    
+class TestFileType(TestCase):
+    def test_find_file_type(self):
+        file_type_objects = models.FileType.objects
+        cases = [('woldlab_090921_HWUSI-EAS627_0009_42FC3AAXX_l7_r1.tar.bz2',
+                  'QSEQ tarfile', 7, 1),
+                 ('woldlab_091005_HWUSI-EAS627_0010_42JT2AAXX_1.srf',
+                  'SRF', 1, None),
+                 ('s_1_eland_extended.txt.bz2','ELAND Extended', 1, None),
+                 ('s_7_eland_multi.txt.bz2', 'ELAND Multi', 7, None),
+                 ('s_3_eland_result.txt.bz2','ELAND Result', 3, None),
+                 ('s_1_export.txt.bz2','ELAND Export', 1, None),
+                 ('s_1_percent_call.png', 'IVC Percent Call', 1, None),
+                 ('s_2_percent_base.png', 'IVC Percent Base', 2, None),
+                 ('s_3_percent_all.png', 'IVC Percent All', 3, None),
+                 ('s_4_call.png', 'IVC Call', 4, None),
+                 ('s_5_all.png', 'IVC All', 5, None),
+                 ('Summary.htm', 'Summary.htm', None, None),
+                 ('run_42JT2AAXX_2009-10-07.xml', 'run_xml', None, None),
+         ]
+        for filename, typename, lane, end in cases:
+            ft = models.find_file_type_metadata_from_filename(filename)
+            self.failUnlessEqual(ft['file_type'],
+                                 file_type_objects.get(name=typename))
+            self.failUnlessEqual(ft.get('lane', None), lane)
+            self.failUnlessEqual(ft.get('end', None), end)
+
+    def test_assign_file_type_complex_path(self):
+        file_type_objects = models.FileType.objects
+        cases = [('/a/b/c/woldlab_090921_HWUSI-EAS627_0009_42FC3AAXX_l7_r1.tar.bz2',
+                  'QSEQ tarfile', 7, 1),
+                 ('foo/woldlab_091005_HWUSI-EAS627_0010_42JT2AAXX_1.srf',
+                  'SRF', 1, None),
+                 ('../s_1_eland_extended.txt.bz2','ELAND Extended', 1, None),
+                 ('/bleem/s_7_eland_multi.txt.bz2', 'ELAND Multi', 7, None),
+                 ('/qwer/s_3_eland_result.txt.bz2','ELAND Result', 3, None),
+                 ('/ty///1/s_1_export.txt.bz2','ELAND Export', 1, None),
+                 ('/help/s_1_percent_call.png', 'IVC Percent Call', 1, None),
+                 ('/bored/s_2_percent_base.png', 'IVC Percent Base', 2, None),
+                 ('/example1/s_3_percent_all.png', 'IVC Percent All', 3, None),
+                 ('amonkey/s_4_call.png', 'IVC Call', 4, None),
+                 ('fishie/s_5_all.png', 'IVC All', 5, None),
+                 ('/random/Summary.htm', 'Summary.htm', None, None),
+                 ('/notrandom/run_42JT2AAXX_2009-10-07.xml', 'run_xml', None, None),
+         ]
+        for filename, typename, lane, end in cases:
+            result = models.find_file_type_metadata_from_filename(filename)
+            self.failUnlessEqual(result['file_type'],
+                                 file_type_objects.get(name=typename))
+            self.failUnlessEqual(result.get('lane',None), lane)
+            self.failUnlessEqual(result.get('end', None), end)
+                             
 class TestEmailNotify(TestCase):
     fixtures = ['test_flowcells.json']
 
@@ -203,7 +356,7 @@ class TestEmailNotify(TestCase):
         self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5') 
         response = self.client.get('/experiments/started/153/')
         self.failUnlessEqual(response.status_code, 200)
-        self.failUnless(re.search('Flowcell 303TUAAXX', response.content))
+        self.failUnless(re.search('Flowcell FC12150', response.content))
         # require that navigation back to the admin page exists
         self.failUnless(re.search('<a href="/admin/experiments/flowcell/153/">[^<]+</a>', response.content))
         
index 5119e9d3c14006b741586f400bae4fef5f6a6b35..91202a867bd76bad92dad516d81869ba70c953b3 100755 (executable)
@@ -6,14 +6,8 @@ urlpatterns = patterns('',
     #(r'^(?P<run_folder>.+)/$', 'gaworkflow.frontend.experiments.views.detail'),
     (r'^config/(?P<fc_id>.+)/json$', 'htsworkflow.frontend.experiments.experiments.flowcell_json'),
     (r'^lanes_for/(?P<username>.+)/json$', 'htsworkflow.frontend.experiments.experiments.lanes_for_json'),
-    (r'^fcsheet/(?P<fcid>.+)/$', 'htsworkflow.frontend.experiments.views.makeFCSheet'),
-    (r'^updStatus$', 'htsworkflow.frontend.experiments.experiments.updStatus'),
-    (r'^getConfile$', 'htsworkflow.frontend.experiments.experiments.getConfile'),
-    (r'^getLanesNames$', 'htsworkflow.frontend.experiments.experiments.getLaneLibs'),
-    # for the following two URLS I have to pass in the primary key
-    # because I link to the page from an overridden version of the admin change_form
-    # which only makes the object primary key available in the form.
-    # (Or at least as far as I could tell)
+    (r'^file/(?P<key>.+)/?$', 'htsworkflow.frontend.experiments.views.read_result_file'),
     (r'^started/(?P<pk>.+)/$', 'htsworkflow.frontend.experiments.views.startedEmail'),
     (r'^finished/(?P<pk>.+)/$', 'htsworkflow.frontend.experiments.views.finishedEmail'),
+                        
 )
index 342438db356b4140a04c59b3195c2ee3252bb158..827f5219ef05870f65735a82077e9b373def1028 100755 (executable)
@@ -1,9 +1,11 @@
 # Create your views here.
 from datetime import datetime
+import os
 
 #from django.template import Context, loader
 #shortcut to the above modules
 from django.contrib.auth.decorators import user_passes_test
+from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.mail import EmailMessage, mail_managers
 from django.http import HttpResponse
@@ -11,7 +13,7 @@ from django.shortcuts import render_to_response, get_object_or_404
 from django.template import RequestContext
 from django.template.loader import get_template
 
-from htsworkflow.frontend.experiments.models import *
+from htsworkflow.frontend.experiments.models import DataRun, DataFile, FlowCell
 from htsworkflow.frontend.experiments.experiments import \
      estimateFlowcellDuration, \
      estimateFlowcellTimeRemaining, \
@@ -129,6 +131,7 @@ def finishedEmail(request, pk):
 
 def flowcell_detail(request, flowcell_id):
     fc = get_object_or_404(FlowCell, flowcell_id=flowcell_id)
+    fc.update_data_runs()
 
     context = RequestContext(request,
                              {'flowcell': fc})
@@ -139,6 +142,8 @@ def flowcell_detail(request, flowcell_id):
 def flowcell_lane_detail(request, flowcell_id, lane_number):
     fc = get_object_or_404(FlowCell, flowcell_id=flowcell_id)
     lane = get_object_or_404(fc.lane_set, lane_number=lane_number)
+    
+    fc.update_data_runs()
 
     context = RequestContext(request,
                              {'lib': lane.library,
@@ -147,3 +152,20 @@ def flowcell_lane_detail(request, flowcell_id, lane_number):
     
     return render_to_response('experiments/flowcell_lane_detail.html',
                               context)
+
+def read_result_file(self, key):
+    """Return the contents of filename if everything is approved
+    """
+    data_file = get_object_or_404(DataFile, random_key = key)
+    
+    mimetype = 'application/octet-stream'
+    if data_file.file_type.mimetype is not None:
+        mimetype = data_file.file_type.mimetype
+
+    if os.path.exists(data_file.pathname):
+        return HttpResponse(open(data_file.pathname,'r'),
+                            mimetype=mimetype)
+
+    raise Http404
+      
+  
index e49d23b16e98cde532377ae7ab08d24bfb7bb7f1..838bea62cfcb9004449d759f1abed644a6f13c8c 100644 (file)
@@ -2,12 +2,11 @@ import logging
 import urlparse
 from django.db import models
 from django.contrib.auth.models import User, UserManager
+from django.core import urlresolvers
 from django.db.models.signals import pre_save, post_save
 from django.db import connection
 from htsworkflow.frontend.reports.libinfopar import *
 
-
-# Create your models here.
 logger = logging.getLogger(__name__)
 
 class Antibody(models.Model):
@@ -289,6 +288,9 @@ class Library(models.Model):
   def get_absolute_url(self):
     return ('htsworkflow.frontend.samples.views.library_to_flowcells', [str(self.id)])
 
+  def get_admin_url(self):
+      return urlresolvers.reverse('admin:samples_library_change',
+                                  args=(self.id,))
 
 class HTSUser(User):
     """
index 2bbcc894a9c28ea5733f3ff62cdbebe1222bb480..c48826c0e2b884109f73d8c3aeddd15ea3c51351 100644 (file)
@@ -128,9 +128,9 @@ def library_to_flowcells(request, lib_id):
     eland_results = []
     for fc, lane_number in flowcell_list:
         lane_summary, err_list = _summary_stats(fc, lane_number)
-
-        eland_results.extend(_make_eland_results(fc, lane_number, flowcell_run_results))
         lane_summary_list.extend(lane_summary)
+        
+        eland_results.extend(_make_eland_results(fc, lane_number, flowcell_run_results))
 
     context = {
         'page_name': 'Library Details',
@@ -308,7 +308,8 @@ def _summary_stats(flowcell_id, lane_id):
             flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
             #pm_field = 'lane_%d_pM' % (lane_id)
             lane_obj = flowcell.lane_set.get(lane_number=lane_id)
-            eland_summary.successful_pm = lane_obj.pM
+            eland_summary.flowcell = flowcell
+            eland_summary.lane = lane_obj
 
             summary_list.append(eland_summary)
 
@@ -318,65 +319,6 @@ def _summary_stats(flowcell_id, lane_id):
     
     return (summary_list, err_list)
 
-def _summary_stats_old(flowcell_id, lane):
-    """
-    return a dictionary of summary stats for a given flowcell_id & lane.
-    """
-    fc_id, status = parse_flowcell_id(flowcell_id)
-    fc_result_dict = get_flowcell_result_dict(fc_id)
-    
-    dict_list = []
-    err_list = []
-    summary_list = []
-    
-    if fc_result_dict is None:
-        err_list.append('Results for Flowcell %s not found.' % (fc_id))
-        return (dict_list, err_list, summary_list)
-    
-    for cnm in fc_result_dict:
-    
-        xmlpath = fc_result_dict[cnm]['run_xml']
-        
-        if xmlpath is None:
-            err_list.append('Run xml for Flowcell %s(%s) not found.' % (fc_id, cnm))
-            continue
-        
-        tree = ElementTree.parse(xmlpath).getroot()
-        results = runfolder.PipelineRun(pathname='', xml=tree)
-        try:
-            lane_report = runfolder.summarize_lane(results.gerald, lane)
-            summary_list.append(os.linesep.join(lane_report))
-        except Exception, e:
-            summary_list.append("Summary report needs to be updated.")
-            logging.error("Exception: " + str(e))
-       
-        print >>sys.stderr, "----------------------------------"
-        print >>sys.stderr, "-- DOES NOT SUPPORT PAIRED END ---"
-        print >>sys.stderr, "----------------------------------"
-        lane_results = results.gerald.summary[0][lane]
-        lrs = lane_results
-        
-        d = {}
-        
-        d['average_alignment_score'] = lrs.average_alignment_score
-        d['average_first_cycle_intensity'] = lrs.average_first_cycle_intensity
-        d['cluster'] = lrs.cluster
-        d['lane'] = lrs.lane
-        d['flowcell'] = flowcell_id
-        d['cnm'] = cnm
-        d['percent_error_rate'] = lrs.percent_error_rate
-        d['percent_intensity_after_20_cycles'] = lrs.percent_intensity_after_20_cycles
-        d['percent_pass_filter_align'] = lrs.percent_pass_filter_align
-        d['percent_pass_filter_clusters'] = lrs.percent_pass_filter_clusters
-        
-        #FIXME: function finished, but need to take advantage of
-        #   may need to take in a list of lanes so we only have to
-        #   load the xml file once per flowcell rather than once
-        #   per lane.
-        dict_list.append(d)
-    
-    return (dict_list, err_list, summary_list)
-    
     
 def get_eland_result_type(pathname):
     """
index fec1733829d7739f9f71fad34f8a858f58657a36..0a86f8ab2645f7554e74a2354e8989b9818745eb 100644 (file)
@@ -66,20 +66,76 @@ div.htswdetail h3 {
      margin: 0;
 }
 
-  div.htswdetail h4,
-  div.htswdetail h5,
-  div.htswdetail ul,
-  div.htswdetail ol,
-  div.htswdetail li
-  {
-    list-style: none;
-    margin: 0;   
-  }
+div.htswdetail h4,
+div.htswdetail h5,
+div.htswdetail ul,
+div.htswdetail ol,
+div.htswdetail li
+{
+  list-style: none;
+  margin: 0;   
+}
+
+div.htswdetail ul,
+div.htswdetail ol
+{
+  margin-bottom: .5em;
+}
 
-  div.htswdetail ul,
-  div.htswdetail ol
-  {
-    margin-bottom: .5em;
+/* style library detail headers */ 
+div#librarydetail {
+  margin: 0;
+  padding: 0;
+}
+div#librarydetail table, div#librarydetail td {
+  border-style: solid;
+}
+div#librarydetail table {
+  border-width: 0 0 1px 1px;
+  border-spacing: 0;
+  border-collapse: collapse;
+}
+div#librarydetail td {
+  margin: 0;
+  padding: 3px;
+  border-width: 1px 1px 0 0;
+}
+div#librarydetail thead {
+  text-align: center;
   }
- /* ]]> */
-</style>
+div#librarydetail tbody {
+  text-align: right;
+}
+div#librarydetail h1,
+div#librarydetail h2
+{
+  font-size: 150%;
+}
+
+div#librarydetail h3 {
+   font-size: 125%;
+   margin: 0;
+}
+
+div#librarydetail h4,
+div#librarydetail h5,
+div#librarydetail ul,
+div#librarydetail ol,
+div#librarydetail li
+{
+  list-style: none;
+  margin: 0;   
+}
+
+div#librarydetail ul,
+div#librarydetail ol
+{
+  margin-bottom: .5em;
+}
+
+div.library_identity { 
+  float: left; margin: 5px; }
+div.library_sample_detail { float: left; margin: 5px; }
+div.library_library_detail { float: left; margin: 5px; }
+div.library_statistics { clear: both; border: 1px; }
+
index 3c9329cbb873f8444d06d6ccc5a1ad8c6ab92858..b238f8b95f1177575476aae28a76bdb964366807 100644 (file)
@@ -24,7 +24,7 @@
     <span property="libns:control_lane">{{flowcell.control_lane}}</span><br/>
 
   <b>Notes</b>:
-    <p property="libns:flowcell_notes">{{flowcell.notes}}</p>
+    <pre property="libns:flowcell_notes">{{flowcell.notes}}</pre>
   <div class="htswdetail">
     <h2>Lanes</h2>
     <table>
       </tbody>
     </table>
     </div>
+    <div class="htsw_flowcell_ivc">
+    {% for run in flowcell.datarun_set.all %}
+       <h2>Run {{ run.runfolder_name }}</h2>
+       <table>
+         <thead>
+           <tr>
+             <td>Lane</td>
+             <td>IVC All</td>
+             <td>IVC Call</td>
+             <td>IVC Percent Base</td>
+             <td>IVC Percent Base All</td>
+             <td>IVC Percent Base Called</td>
+         </thead>
+         <tbody>
+            {% for lane_id, lane_file_set in run.lane_files.items %}
+            <tr>
+              <td>{{ lane_id }}</td>
+              <td>
+                <a href="{{ lane_file_set.ivc_all.get_absolute_url }}">
+                <img height="84" width="126" src="{{ lane_file_set.ivc_all.get_absolute_url }}"/></a>
+              </td>
+              <td>
+                <a href="{{ lane_file_set.ivc_call.get_absolute_url }}">
+                <img height="84" width="126" src="{{ lane_file_set.ivc_call.get_absolute_url }}"/>
+                </a>
+              </td>
+              <td>
+                <a href="{{ lane_file_set.ivc_percent_base.get_absolute_url }}">
+                <img height="84" width="126" src="{{ lane_file_set.ivc_percent_base.get_absolute_url }}"/>
+                </a>
+              </td>
+              <td>
+                <a href="{{ lane_file_set.ivc_percent_all.get_absolute_url }}">
+                <img height="84" width="126" src="{{ lane_file_set.ivc_percent_all.get_absolute_url }}"/>
+                </a>
+              </td>
+              <td>
+                <a href="{{ lane_file_set.ivc_percent_call.get_absolute_url }}">
+                <img height="84" width="126" src="{{ lane_file_set.ivc_percent_call.get_absolute_url }}"/>
+                </a>
+              </td>
+            </tr>
+            {% endfor %} 
+         </tbody>
+       </table>
+    {% endfor %}
+    </div>
 </div>  
 {% endblock %}
index aceb186dcadcc9ca4e946345fc2d0bd186455f08..f65c9e930cf95b96ddd0e34166e821664e4234b6 100644 (file)
@@ -10,7 +10,7 @@
 {% endblock %}
 
 {% block content %}
-<div id="lane_detail">
+<div id="lane_detail" class="htswdetail">
   <h2>About this lane</h2>
   <div rel="libns:flowcell" resource="{{flowcell.get_absolute_url}}">
   <b>Flowcell</b>: 
@@ -22,6 +22,8 @@
   </div>
   <b>Lane</b>: 
     <span property="libns:lane_number" datatype="xsd:decimal">{{lane.lane_number}}</span><br/>
+  <b>Cycles</b>: 
+    <span property="libns:read_length">{{lane.flowcell.read_length}}</span><br/>
   <b>pM</b>
     <span property="libns:pM" datatype="xsd:decimal">{{ lane.pM }}</span><br/>
   <b>Cluster Estimate</b>
     <span property="libns:status">{{ lane.status }}</span><br/>
   <b>Comment</b>: 
     <span property="libns:comment">{{ lane.comment }}</span><br/>
-
-
-  <div rel="libns:library" resource="{{lib.get_absolute_url}}">
-  <h2>About the library</h2>
-  <b>Library ID</b>: 
-    <a href="{{lib.get_absolute_url}}" property="libns:library_id">{{ lib.id }}</a><br/>
-  <b>Name</b>: 
-    <span property="libns:name">{{ lib.library_name }}</span>
-  <br/>
-  <b>Species</b>: 
-    <a href="{{lib.library_species.get_absolute_url}}" rel="libns:species"><span property="libns:species_name">{{ lib.library_species.scientific_name }}</span></a>
-  <br/>
-  <b>Concentration</b>: 
-    <span property="libns:concentration">{{ lib.undiluted_concentration }} ng/µl</span>
-  <br/>
-  <b>Gel Cut Size</b>: 
-    <span property="libns:gel_cut">{{ lib.gel_cut_size }}</span>
-  <br/>
-  <b>Insert Size</b>: 
-    <span property="libns:insert_size">{{ lib.insert_size }}</span>
-  <br/>
-  <b>Background or Cell Line</b>:
-     <span property="libns:cell_line">{{ lib.cell_line }}</span>
-  <br/>
-  <b>Replicate</b>: 
-     <span property="libns:replicate">{{ lib.replicate }}</span>
-  <br/>
-  <b>Library Type</b>:
-     <span property="libns:library_type">{{ lib.library_type }}</span>
-  <br/>
-  <b>Experiment Type</b>:
-     <span property="libns:experiment_type">{{ lib.experiment_type }}</span>
-  <br/>
-  <b>Made By</b>: 
-    <span property="libns:made_by">{{ lib.made_by }}</span>
-  <br/>
-  <b>Creation Date</b>
-    <span property="libns:date" content="{{lib.creation_date|date:'Y-m-d'}}T00:00:00" datatype="xsd:dateTime">{{ lib.creation_date }}</span>
-  <br/> 
-  <b>Protocol Stopping Point</b>
-    <span property="libns:stopping_point">{{ lib.stopping_point_name }}</span>
-  <br/> 
-  <b>Affiliations</b>:
-  <ul>
-    {% for individual in lib.affiliations.all %}
-      <li property="libns:affliation" content="{{individual.name}}">
-        {{ individual.name }} ( {{ individual.contact }} )
-      </li>
-    {% endfor %}
-  </ul>
+  <hr/>
+  {% include "sample_header.html" %}
+  <hr/>
+  <div class="htsw_flowcell_ivc">
+  {% for run in flowcell.datarun_set.all %}
+     <h2>Run {{ run.runfolder_name }}</h2>
+     <table>
+       <thead>
+         <tr>
+           <td>Lane</td>
+           <td>IVC All</td>
+           <td>IVC Call</td>
+           <td>IVC Percent Base</td>
+           <td>IVC Percent Base All</td>
+           <td>IVC Percent Base Called</td>
+       </thead>
+       <tbody>
+          {% for lane_id, lane_file_set in run.lane_files.items %}
+          {% if lane_id == lane.lane_number %}
+          <tr>
+            <td>{{lane_id}}</td>
+            <td>
+              <a href="{{ lane_file_set.ivc_all.get_absolute_url }}">
+              <img height="84" width="126" src="{{ lane_file_set.ivc_all.get_absolute_url }}"/></a>
+            </td>
+            <td>
+              <a href="{{ lane_file_set.ivc_call.get_absolute_url }}">
+              <img height="84" width="126" src="{{ lane_file_set.ivc_call.get_absolute_url }}"/>
+              </a>
+            </td>
+            <td>
+              <a href="{{ lane_file_set.ivc_percent_base.get_absolute_url }}">
+              <img height="84" width="126" src="{{ lane_file_set.ivc_percent_base.get_absolute_url }}"/>
+              </a>
+            </td>
+            <td>
+              <a href="{{ lane_file_set.ivc_percent_all.get_absolute_url }}">
+              <img height="84" width="126" src="{{ lane_file_set.ivc_percent_all.get_absolute_url }}"/>
+              </a>
+            </td>
+            <td>
+              <a href="{{ lane_file_set.ivc_percent_call.get_absolute_url }}">
+              <img height="84" width="126" src="{{ lane_file_set.ivc_percent_call.get_absolute_url }}"/>
+              </a>
+            </td>
+          </tr>
+          {% endif %}
+          {% endfor %} 
+       </tbody>
+     </table>
+  {% endfor %}
   </div>
-</div>  
 {% endblock %}
diff --git a/htsworkflow/frontend/templates/sample_header.html b/htsworkflow/frontend/templates/sample_header.html
new file mode 100644 (file)
index 0000000..8839d71
--- /dev/null
@@ -0,0 +1,83 @@
+<div id="librarydetail">
+  <div class="library_identity">
+    <h2>Library Name</h2>  
+    <b>Library ID</b>: <a href="{{lib.get_absolute_url}}">{{ lib.id }}</a><br/>
+    <b>Name</b>: 
+      <span property="libns:name">{{ lib.library_name }}</span>
+    <br/>
+    <b>Affiliations</b>:
+    <ul>
+      {% for individual in lib.affiliations.all %}
+        <li property="libns:affiliation" content="{{individual.name}}">
+          {{ individual.name }} ( {{ individual.contact }} )
+        </li>
+      {% endfor %}
+    </ul>
+  </div>
+  <div class="library_sample_detail">
+    <h2>Sample Details</h2>
+    <b>Species</b>: 
+      <a href="{{lib.library_species.get_absolute_url}}" property="libns:species">{{ lib.library_species.scientific_name }}</a>
+    <br/>
+    <b>Experiment Type</b>:
+       <span property="libns:experiment_type">{{ lib.experiment_type }}</span>
+    <br/>
+    {% if lib.antibody %}
+    <b>Antibody</b>:
+       <span property="libns:antibody">{{ lib.antibody.antibodies }}</span>
+       {% if lib.antibody.antibodies.nuckname %}
+       (<span property="libns:antibody_term">{{ lib.antibody.nickname }}</span>)
+       {% endif %}
+    <br/>
+    {% endif %}
+    {% if lib.cell_line %}
+    <b>Background or Cell Line</b>:
+       <span property="libns:cell_line">{{ lib.cell_line }}</span>
+    <br/>
+    {% endif %}
+    {% if lib.condition %}
+    <b>Condition</b>:
+       <span property="libns:condition">{{ lib.condition.condition_name }}</span>
+       {% if lib.condition.nickname %}
+       (<span property="libns:condition_term">{{ lib.condition.nickname }}</span>)
+       {% endif %}
+    <br/>
+    {% endif %}
+    {% if lib.replicate %}
+    <b>Replicate</b>: 
+       <span property="libns:replicate">{{ lib.replicate }}</span>
+    <br/>
+    {% endif %}
+  </div>
+  <div class="library_library_detail">
+    <h2>Library Details</h2>
+    <b>Library Type</b>:
+       <span property="libns:library_type">{{ lib.library_type }}</span>
+    <br/>
+    <b>Creation Date</b>
+      <span property="libns:date" content="{{lib.creation_date|date:'Y-m-d'}}T00:00:00" datatype="xsd:dateTime">{{ lib.creation_date }}</span>
+    <br/> 
+    <b>Made By</b>: 
+      <span property="libns:made_by">{{ lib.made_by }}</span>
+    <br/>
+    {% if lib.gel_cut_size %}
+    <b>Gel Cut Size</b>: 
+      <span property="libns:gel_cut">{{ lib.gel_cut_size }}</span>
+    <br/>
+    {% endif %}
+    {% if lib.insert_size %}
+    <b>Insert Size</b>: 
+      <span property="libns:insert_size">{{ lib.insert_size }}</span>
+    <br/>
+    {% endif %}
+    {% if lib.undiluted_concentration %}
+    <b>Concentration</b>: 
+      <span property="libns:concentration">{{ lib.undiluted_concentration }} ng/µl</span>
+    <br/>
+    {% endif %}
+    {% if lib.stopping_point_name %}
+    <b>Protocol Stopping Point</b>
+      <span property="libns:stopping_point">{{ lib.stopping_point_name }}</span>
+    <br/>
+    {% endif %}
+  </div>
index f5c4176855cd071950e2819add3e4f5a3d6693e6..dbab436671e60d0cfd4b007f85634d2830dedc07 100644 (file)
     
     {% block additional_javascript %}
     {% endblock %}
-
-<style type="text/css">
-  /* <![CDATA[ */
-  div#librarydetail {
-    margin: 0;
-    padding: 0;
-  }
-  div#librarydetail table, div#librarydetail td {
-    border-style: solid;
-  }
-  div#librarydetail table {
-    border-width: 0 0 1px 1px;
-    border-spacing: 0;
-    border-collapse: collapse;
-  }
-  div#librarydetail td {
-    margin: 0;
-    padding: 3px;
-    border-width: 1px 1px 0 0;
-  }
-  div#librarydetail thead {
-    text-align: center;
-    }
-  div#librarydetail tbody {
-    text-align: right;
-  }
-  div#librarydetail h1,
-  div#librarydetail h2
-  {
-    font-size: 150%;
-  }
-
-  div#librarydetail h3 {
-     font-size: 125%;
-     margin: 0;
-  }
-
-  div#librarydetail h4,
-  div#librarydetail h5,
-  div#librarydetail ul,
-  div#librarydetail ol,
-  div#librarydetail li
-  {
-    list-style: none;
-    margin: 0;   
-  }
-
-  div#librarydetail ul,
-  div#librarydetail ol
-  {
-    margin-bottom: .5em;
-  }
-
-  div.library_identity { 
-    float: left; margin: 5px; }
-  div.library_sample_detail { float: left; margin: 5px; }
-  div.library_library_detail { float: left; margin: 5px; }
-  div.library_statistics { clear: both; border: 1px; }
- /* ]]> */
-</style>
 {% endblock %}
 
 {% block content %}
-<div id="librarydetail">
-  <div class="library_identity">
-    <h2>Library Name</h2>  
-    <b>Library ID</b>: {{ lib.id }}<br/>
-    <b>Name</b>: 
-      <span property="libns:name">{{ lib.library_name }}</span>
-    <br/>
-    <b>Affiliations</b>:
-    <ul>
-      {% for individual in lib.affiliations.all %}
-        <li property="libns:affiliation" content="{{individual.name}}">
-          {{ individual.name }} ( {{ individual.contact }} )
-        </li>
-      {% endfor %}
-    </ul>
-  </div>
-  <div class="library_sample_detail">
-    <h2>Sample Details</h2>
-    <b>Species</b>: 
-      <span property="libns:species">{{ lib.library_species.scientific_name }}</span>
-    <br/>
-    <b>Experiment Type</b>:
-       <span property="libns:experiment_type">{{ lib.experiment_type }}</span>
-    <br/>
-    {% if lib.antibody %}
-    <b>Antibody</b>:
-       <span property="libns:antibody">{{ lib.antibody.antibodies }}</span>
-       {% if lib.antibody.antibodies.nuckname %}
-       (<span property="libns:antibody_term">{{ lib.antibody.nickname }}</span>)
-       {% endif %}
-    <br/>
-    {% endif %}
-    {% if lib.cell_line %}
-    <b>Background or Cell Line</b>:
-       <span property="libns:cell_line">{{ lib.cell_line }}</span>
-    <br/>
-    {% endif %}
-    {% if lib.condition %}
-    <b>Condition</b>:
-       <span property="libns:condition">{{ lib.condition.condition_name }}</span>
-       {% if lib.condition.nickname %}
-       (<span property="libns:condition_term">{{ lib.condition.nickname }}</span>)
-       {% endif %}
-    <br/>
-    {% endif %}
-    {% if lib.replicate %}
-    <b>Replicate</b>: 
-       <span property="libns:replicate">{{ lib.replicate }}</span>
-    <br/>
-    {% endif %}
-  </div>
-  <div class="library_library_detail">
-    <h2>Library Details</h2>
-    <b>Library Type</b>:
-       <span property="libns:library_type">{{ lib.library_type }}</span>
-    <br/>
-    <b>Creation Date</b>
-      <span property="libns:date" content="{{lib.creation_date|date:'Y-m-d'}}T00:00:00" datatype="xsd:dateTime">{{ lib.creation_date }}</span>
-    <br/> 
-    <b>Made By</b>: 
-      <span property="libns:made_by">{{ lib.made_by }}</span>
-    <br/>
-    {% if lib.gel_cut_size %}
-    <b>Gel Cut Size</b>: 
-      <span property="libns:gel_cut">{{ lib.gel_cut_size }}</span>
-    <br/>
-    {% endif %}
-    {% if lib.insert_size %}
-    <b>Insert Size</b>: 
-      <span property="libns:insert_size">{{ lib.insert_size }}</span>
-    <br/>
-    {% endif %}
-    {% if lib.undiluted_concentration %}
-    <b>Concentration</b>: 
-      <span property="libns:concentration">{{ lib.undiluted_concentration }} ng/µl</span>
-    <br/>
-    {% endif %}
-    {% if lib.stopping_point_name %}
-    <b>Protocol Stopping Point</b>
-      <span property="libns:stopping_point">{{ lib.stopping_point_name }}</span>
-    <br/>
-    {% endif %}
-  </div>
+  {% include "sample_header.html" %}
+  <hr/>
   <div class="library_statistics">
   <h2>Raw Result Files</h2>
   <table>
     </thead>
     <tbody>
   
-      {% for lane in lane_summary_list %}
-      <tr about="/flowcell/{{lane.flowcell_id}}/{{lane.lane_id}}/{% if lane.end %}#end{{ lane.end }}{% endif %}">
-        <td>{{ lane.cycle_width }}</td>
-        <td>{{ lane.flowcell_id }}</td>
-        <td>{{ lane.lane_id }}</td>
-        <td>{% if lane.end %}{{ lane.end }}{% endif %}</td>
-        <td>{{ lane.clusters.0|intcomma }}</td>
-        <td>{{ lane.successful_pm }}</td>
-        <td>{{ lane.reads|intcomma }}</td>
-        <td>{{ lane.no_match|intcomma }}</td>
-        <td>{{ lane.no_match_percent|stringformat:".2f" }}</td>
-        <td>{{ lane.qc_failed|intcomma }}</td>
-        <td>{{ lane.qc_failed_percent|stringformat:".2f" }}</td>
-        <td>{{ lane.match_codes.U0|intcomma }}</td>
-        <td>{{ lane.match_codes.U1|intcomma }}</td>
-        <td>{{ lane.match_codes.U2|intcomma }}</td>
-        <td {% if lane.unique_reads %}property="libns:total_unique_locations" content="{{lane.unique_reads}}" datatype="xsd:decimal"{% endif %}>{{ lane.unique_reads|intcomma }}</td>
-        <td>{{ lane.match_codes.R0|intcomma }}</td>
-        <td>{{ lane.match_codes.R1|intcomma }}</td>
-        <td>{{ lane.match_codes.R2|intcomma }}</td>
-        <td>{{ lane.repeat_reads|intcomma }}</td>
+      {# ls short for lane summary #}
+      {% for ls in lane_summary_list %}
+      <tr about="{{ls.lane.get_absolute_url">
+        <td>{{ ls.cycle_width }}</td>
+        <td><a href="{{ls.flowcell.get_absolute_url}}">{{ ls.flowcell_id }}</a></td>
+        <td><a href="{{ls.lane.get_absolute_url}}">{{ ls.lane_id }}</a></td>
+        <td>{% if ls.end %}{{ ls.end }}{% endif %}</td>
+        <td>{{ ls.clusters.0|intcomma }}</td>
+        <td>{{ ls.successful_pm }}</td>
+        <td>{{ ls.reads|intcomma }}</td>
+        <td>{{ ls.no_match|intcomma }}</td>
+        <td>{{ ls.no_match_percent|stringformat:".2f" }}</td>
+        <td>{{ ls.qc_failed|intcomma }}</td>
+        <td>{{ ls.qc_failed_percent|stringformat:".2f" }}</td>
+        <td>{{ ls.match_codes.U0|intcomma }}</td>
+        <td>{{ ls.match_codes.U1|intcomma }}</td>
+        <td>{{ ls.match_codes.U2|intcomma }}</td>
+        <td {% if ls.unique_reads %}property="libns:total_unique_locations" content="{{ls.unique_reads}}" datatype="xsd:decimal"{% endif %}>{{ ls.unique_reads|intcomma }}</td>
+        <td>{{ ls.match_codes.R0|intcomma }}</td>
+        <td>{{ ls.match_codes.R1|intcomma }}</td>
+        <td>{{ ls.match_codes.R2|intcomma }}</td>
+        <td>{{ ls.repeat_reads|intcomma }}</td>
       </tr>
       {% endfor %}
     </tbody>
index 8cdb82c83936fb1c0859765d9bf09b3d31c1ef65..9495ad98c08103b22a05046d8c9a7de58f362d4d 100644 (file)
@@ -28,7 +28,7 @@ urlpatterns = patterns('',
     # Flowcell:
     (r'^flowcell/(?P<flowcell_id>\w+)/(?P<lane_number>\w+)/',
      'htsworkflow.frontend.experiments.views.flowcell_lane_detail'),
-    (r'^flowcell/(?P<flowcell_id>\w+)/',
+    (r'^flowcell/(?P<flowcell_id>\w+)/$',
      'htsworkflow.frontend.experiments.views.flowcell_detail'),
     # AnalysTrack:
     #(r'^analysis/', include('htsworkflow.frontend.analysis.urls')),
index 9847ffd4aea53d7a613bfe266cc882584c1c3a7d..f655779bbb6edaf2c00a56e66284331f19215b4b 100644 (file)
@@ -18,13 +18,13 @@ class RetrieveTestCases(TestCase):
         pass
 
     def test_format_gerald(self):
-        flowcell_request = self.client.get('/experiments/config/303TUAAXX/json', apidata)
+        flowcell_request = self.client.get('/experiments/config/FC12150/json', apidata)
         self.failUnlessEqual(flowcell_request.status_code, 200)
 
         print dir(flowcell_request)
         flowcell_info = json.loads(flowcell_request.content)
 
-        options = getCombinedOptions(['-f','303TUAAXX','-g',os.getcwd()])        
+        options = getCombinedOptions(['-f','FC12150','-g',os.getcwd()])        
         genome_map = {u'Homo sapiens': '/tmp/hg18' }
         
         config = format_gerald_config(options, flowcell_info, genome_map)
diff --git a/htsworkflow/pipelines/test/testdata/woldlab_070829_USI-EAS44_0017_FC11055_1.srf b/htsworkflow/pipelines/test/testdata/woldlab_070829_USI-EAS44_0017_FC11055_1.srf
new file mode 100644 (file)
index 0000000..dd802a7
Binary files /dev/null and b/htsworkflow/pipelines/test/testdata/woldlab_070829_USI-EAS44_0017_FC11055_1.srf differ
diff --git a/htsworkflow/pipelines/test/testdata/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf b/htsworkflow/pipelines/test/testdata/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf
new file mode 100644 (file)
index 0000000..b5d145f
Binary files /dev/null and b/htsworkflow/pipelines/test/testdata/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf differ
index eee4152f65082cc244165bb8e5dee4892cf865da..74e031ca7324174362de8695fb99ba4c50d830c7 100644 (file)
@@ -6,6 +6,8 @@ import unittest
 _module_path, _module_name = os.path.split(__file__)
 sys.path.append(os.path.join(_module_path, '..', 'scripts'))
 
+from htsworkflow.pipelines.test.simulate_runfolder import TESTDATA_DIR
+
 from htsworkflow.pipelines import srf2fastq
 
 class testSrf2Fastq(unittest.TestCase):
@@ -113,9 +115,9 @@ IIIIB+++
 
     def test_is_srf(self):        
         cnf4_srf = 'woldlab_070829_USI-EAS44_0017_FC11055_1.srf'
-        cnf4_path = os.path.join(_module_path, cnf4_srf)
+        cnf4_path = os.path.join(TESTDATA_DIR, cnf4_srf)
         cnf1_srf = 'woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf'
-        cnf1_path = os.path.join(_module_path, cnf1_srf)
+        cnf1_path = os.path.join(TESTDATA_DIR, cnf1_srf)
         
         is_srf = srf2fastq.is_srf
         self.failUnlessEqual(is_srf(__file__), False)
@@ -124,9 +126,9 @@ IIIIB+++
 
     def test_is_cnf1(self):        
         cnf4_srf = 'woldlab_070829_USI-EAS44_0017_FC11055_1.srf'
-        cnf4_path = os.path.join(_module_path, cnf4_srf)
+        cnf4_path = os.path.join(TESTDATA_DIR, cnf4_srf)
         cnf1_srf = 'woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf'
-        cnf1_path = os.path.join(_module_path, cnf1_srf)
+        cnf1_path = os.path.join(TESTDATA_DIR, cnf1_srf)
         
         is_cnf1 = srf2fastq.is_cnf1
         self.failUnlessRaises(ValueError, is_cnf1, __file__)
diff --git a/test/woldlab_070829_USI-EAS44_0017_FC11055_1.srf b/test/woldlab_070829_USI-EAS44_0017_FC11055_1.srf
deleted file mode 100644 (file)
index dd802a7..0000000
Binary files a/test/woldlab_070829_USI-EAS44_0017_FC11055_1.srf and /dev/null differ
diff --git a/test/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf b/test/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf
deleted file mode 100644 (file)
index b5d145f..0000000
Binary files a/test/woldlab_090512_HWI-EAS229_0114_428NNAAXX_5.srf and /dev/null differ