Add instrument ID and model to sequencer table
authorDiane Trout <diane@caltech.edu>
Thu, 7 Jun 2012 00:10:53 +0000 (17:10 -0700)
committerDiane Trout <diane@caltech.edu>
Thu, 7 Jun 2012 00:10:53 +0000 (17:10 -0700)
Additionally added displaying that information on the flowcell page.

htsworkflow/frontend/experiments/admin.py
htsworkflow/frontend/experiments/fixtures/initial_data.json
htsworkflow/frontend/experiments/fixtures/test_flowcells.json
htsworkflow/frontend/experiments/models.py
htsworkflow/frontend/experiments/tests.py
htsworkflow/frontend/experiments/views.py
htsworkflow/frontend/templates/experiments/flowcell_header.html
htsworkflow/frontend/templates/experiments/sequencer.html [new file with mode: 0644]
htsworkflow/frontend/urls.py

index fa0f15d0141a4473fc02098049d47bea1c3f2900..318625f9405d96cb39c88f985bc89a933f02d2f0 100644 (file)
@@ -45,12 +45,12 @@ 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):
     comment = CharField(widget=TextInput(attrs={'size':'80'}), required=False)
-    
+
     class Meta:
         model = Lane
 
@@ -89,7 +89,7 @@ class LaneOptions(admin.ModelAdmin):
       }),
     )
 admin.site.register(Lane, LaneOptions)
-    
+
 class FlowCellOptions(admin.ModelAdmin):
     date_hierarchy = "run_date"
     save_on_top = True
@@ -126,6 +126,9 @@ class ClusterStationOptions(admin.ModelAdmin):
 admin.site.register(ClusterStation, ClusterStationOptions)
 
 class SequencerOptions(admin.ModelAdmin):
-    list_display = ('name', )
-    fieldsets = ( ( None, { 'fields': ( 'name', ) } ), )
+    list_display = ('name', 'instrument_name', 'model')
+    fieldsets = ( ( None,
+                    { 'fields': (
+                        'name', 'instrument_name', 'serial_number',
+                        'model', 'comment') } ), )
 admin.site.register(Sequencer, SequencerOptions)
index 67a6393cf0f1b939dfb46950c5755c354e3192ca..3b4d1d57fec7df03be3723188c44d66a2d87aae3 100644 (file)
       "name": "QSEQ tarfile",
       "regex": ".*_l(?P<lane>[1-8])_r(?P<end>[1-4])\\.tar\\.bz2\\Z(?ms)"
     }
+  },
+  { "model": "experiments.Sequencer",
+    "pk": 1,
+    "fields": {
+      "name": "Rotifer",
+      "instrument_name": "ILLUMINA-33A494",
+      "serial_number": "",
+      "model": "Illumina Genome Analyzer II",
+      "comment": "after 2010 pipeline name, was exchanged for hiseq"
+    }
+  },
+  { "model": "experiments.Sequencer",
+    "pk": 2,
+    "fields": {
+      "name": "Tardigrade",
+      "instrument_name": "ILLUMINA-EC5D15",
+      "serial_number": "",
+      "model": "Illumina Genome Analyzer IIx",
+      "comment": "after 2010 pipeline name"
+    }
+  },
+  { "model": "experiments.Sequencer",
+    "pk": 3,
+    "fields": {
+      "name": "Elsewhere",
+      "instrument_name": "",
+      "serial_number": "",
+      "model": "Unknown",
+      "comment": "Sequenced somewhere else"
+    }
+  },
+  { "model": "experiments.Sequencer",
+    "pk": 4,
+    "fields": {
+      "name": "Volvox",
+      "instrument_name": "HWI-ST0787",
+      "serial_number": "",
+      "model": "Illumina HiSeq 2000",
+      "comment": ""
+    }
+  },
+  { "model": "experiments.Sequencer",
+    "pk": 5,
+    "fields": {
+      "name": "Tardigrade (older)",
+      "instrument_name": "HWUSI-EAS627",
+      "serial_number": "",
+      "model": "Illumina Genome Analyzer II",
+      "comment": "earlier version of tardigrade"
+    }
+  },
+  { "model": "experiments.Sequencer",
+    "pk": 6,
+    "fields": {
+      "name": "Rotifer (older)",
+      "instrument_name": "HWUSI-EAS229",
+      "serial_number": "",
+      "model": "Illumina Genome Analyzer II",
+      "comment": "earlier rotifer name"
+    }
+  },
+  { "model": "experiments.Sequencer",
+    "pk": 7,
+    "fields": {
+      "name": "First sequencer",
+      "instrument_name": "USI-EAS44",
+      "serial_number": "",
+      "model": "Illumina Genome Analyzer I",
+      "comment": "our first sequencer"
+    }
   }
-
 ]
\ No newline at end of file
index 63cf30bcab89bd77b788fd296140ba95e7f712f7..59d5afee450538635e336256d2e044df312fb161 100644 (file)
  {"pk": 1, "model": "experiments.clusterstation", "fields": {"name": "old"}},
  {"pk": 2, "model": "experiments.clusterstation", "fields": {"name": "loaner"}},
  {"pk": 3, "model": "experiments.clusterstation", "fields": {"name": "new"}},
- {"pk": 1, "model": "experiments.sequencer", "fields": {"name": "Rotifer (HWI-EAS229)"}},
- {"pk": 2, "model": "experiments.sequencer", "fields": {"name": "Tardigrade"}},
+ {"pk": 1, "model": "experiments.sequencer",
+  "fields": { "name": "Rotifer (HWI-EAS229)" }},
+ {"pk": 2, "model": "experiments.sequencer",
+  "fields": {"name": "Tardigrade",
+             "instrument_name": "ILLUMINA-EC5D15",
+             "model": "Illumina Genome Analyzer IIx"}
+ },
  {"pk": 3, "model": "experiments.sequencer", "fields": {"name": "Sequenced somewhere else."}},
  {"pk": 153, "model": "experiments.flowcell",
   "fields": {
index bd92eb4037269bf8a9e5c24ca70b2671057a1878..2f97f155de06e50cca7924422a69e916f14263ac 100644 (file)
@@ -51,10 +51,23 @@ class ClusterStation(models.Model):
     return unicode(self.name)
 
 class Sequencer(models.Model):
-  name = models.CharField(max_length=50, unique=True)
+  name = models.CharField(max_length=50, db_index=True)
+  instrument_name = models.CharField(max_length=50, db_index=True)
+  serial_number = models.CharField(max_length=50, db_index=True)
+  model = models.CharField(max_length=255)
+  comment = models.CharField(max_length=255)
 
   def __unicode__(self):
-    return unicode(self.name)
+      name = [unicode(self.name)]
+      if self.instrument_name is not None:
+          name.append("(%s)" % (unicode(self.instrument_name),))
+      return " ".join(name)
+
+  @models.permalink
+  def get_absolute_url(self):
+      return ('htsworkflow.frontend.experiments.views.sequencer',
+              [self.id])
+
 
 class FlowCell(models.Model):
   flowcell_id = models.CharField(max_length=20, unique=True, db_index=True)
index 8ec46e0619aa6749bd0b7e631c27d2b226b77bac..e54d630497e2f3b9c412b7364f50d06dcf659cd3 100644 (file)
@@ -409,3 +409,43 @@ def multi_lane_to_dict(lane):
     """Convert a list of lane entries into a dictionary indexed by library ID
     """
     return dict( ((x['library_id'],x) for x in lane) )
+
+class TestSequencer(TestCase):
+    fixtures = ['test_flowcells.json',
+                ]
+
+    def test_name_generation(self):
+        seq = models.Sequencer()
+        seq.name = "Seq1"
+        seq.instrument_name = "HWI-SEQ1"
+        seq.model = "Imaginary 5000"
+
+        self.failUnlessEqual(unicode(seq), "Seq1 (HWI-SEQ1)")
+
+    def test_lookup(self):
+        fc = models.FlowCell.objects.get(pk=153)
+        self.failUnlessEqual(fc.sequencer.model,
+                             "Illumina Genome Analyzer IIx")
+        self.failUnlessEqual(fc.sequencer.instrument_name,
+                             "ILLUMINA-EC5D15")
+
+    def test_rdf(self):
+        response = self.client.get('/flowcell/FC12150/', apidata)
+        tree = fromstring(response.content)
+        divs = tree.xpath('//div[@rel="libns:sequenced_by"]',
+                          namespaces=NSMAP)
+        self.failUnlessEqual(len(divs), 1)
+        self.failUnlessEqual(divs[0].attrib['rel'], 'libns:sequenced_by')
+        self.failUnlessEqual(divs[0].attrib['resource'], '/sequencer/2')
+
+        name = divs[0].xpath('./span[@property="libns:sequencer_name"]')
+        self.failUnlessEqual(len(name), 1)
+        self.failUnlessEqual(name[0].text, 'Tardigrade')
+        instrument = divs[0].xpath(
+            './span[@property="libns:sequencer_instrument"]')
+        self.failUnlessEqual(len(instrument), 1)
+        self.failUnlessEqual(instrument[0].text, 'ILLUMINA-EC5D15')
+        model = divs[0].xpath(
+            './span[@property="libns:sequencer_model"]')
+        self.failUnlessEqual(len(model), 1)
+        self.failUnlessEqual(model[0].text, 'Illumina Genome Analyzer IIx')
index 08ce6f0e93d0255b72fac17b9ddb39655d081d57..20dd554217547b5476579decf39188d06f77fbb4 100644 (file)
@@ -17,7 +17,8 @@ from htsworkflow.frontend.experiments.models import \
      DataRun, \
      DataFile, \
      FlowCell, \
-     Lane
+     Lane, \
+     Sequencer
 from htsworkflow.frontend.experiments.experiments import \
      estimateFlowcellDuration, \
      estimateFlowcellTimeRemaining, \
@@ -31,10 +32,10 @@ def index(request):
     #c = Context({
     #    'data_run_list': all_runs,
     #})
-    #return HttpResponse(t.render(c)) 
+    #return HttpResponse(t.render(c))
     # shortcut to the above module usage
-    return render_to_response('experiments/index.html',{'data_run_list': all_runs}) 
-    
+    return render_to_response('experiments/index.html',{'data_run_list': all_runs})
+
 def detail(request, run_folder):
     html_str = '<h2>Exp Track Details Page</h2>'
     html_str += 'Run Folder: '+run_folder
@@ -89,7 +90,7 @@ def startedEmail(request, pk):
         if user.email is None or len(user.email) == 0:
             warnings.append((user.admin_url(), user.username))
     user=None
-    
+
     for user_email in email_lane.keys():
         sending = ""
         # build body
@@ -99,7 +100,7 @@ def startedEmail(request, pk):
                                   u'runfolder': 'blank',
                                   u'finish_low': estimate_low,
                                   u'finish_high': estimate_high,
-                                  u'now': datetime.now(),        
+                                  u'now': datetime.now(),
                                   })
 
         # build view
@@ -126,7 +127,7 @@ def startedEmail(request, pk):
           'warnings': warnings,
         })
     return HttpResponse(email_verify.render(verify_context))
-    
+
 def finishedEmail(request, pk):
     """
     """
@@ -137,7 +138,7 @@ def flowcell_detail(request, flowcell_id, lane_number=None):
     fc = get_object_or_404(FlowCell, flowcell_id__startswith=flowcell_id)
     fc.update_data_runs()
 
-    
+
     if lane_number is not None:
         lanes = fc.lane_set.filter(lane_number=lane_number)
     else:
@@ -146,7 +147,7 @@ def flowcell_detail(request, flowcell_id, lane_number=None):
     context = RequestContext(request,
                              {'flowcell': fc,
                               'lanes': lanes})
-    
+
     return render_to_response('experiments/flowcell_detail.html',
                               context)
 
@@ -157,13 +158,13 @@ def flowcell_lane_detail(request, lane_pk):
     dataruns = []
     for run in lane.flowcell.datarun_set.all():
         dataruns.append((run, lane.lane_number, run.lane_files()[lane.lane_number]))
-        
+
     context = RequestContext(request,
                              {'lib': lane.library,
                               'lane': lane,
                               'flowcell': lane.flowcell,
                               'filtered_dataruns': dataruns})
-    
+
     return render_to_response('experiments/flowcell_lane_detail.html',
                               context)
 
@@ -171,7 +172,7 @@ 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
@@ -181,5 +182,10 @@ def read_result_file(self, key):
                             mimetype=mimetype)
 
     raise Http404
-      
-  
+
+
+def sequencer(request, sequencer_id):
+    sequencer = get_object_or_404(Sequencer, id=sequencer_id)
+    context = RequestContext(request,
+                             {'sequencer': sequencer})
+    return render_to_response('experiments/sequencer.html', context)
index 15ccdae05e5cd0eae02476fe8d36d193c5832c15..458619b93efe12def2f0d09850177f40baf78545 100644 (file)
@@ -3,6 +3,18 @@
   <b>Flowcell</b>:
     <a href="{{flowcell.get_absolute_url}}" property="libns:flowcell_id">{{flowcell.flowcell_id}}</a>{% if user.is_staff %}<a href="{{flowcell.get_admin_url}}"><img class="icon_button" src="/media/img/admin/icon_changelink.gif" alt="Edit"/></a>{% endif%}
   <br property="rdf:type" content="libns:illumina_flowcell"/>
+  <div rel="libns:sequenced_by"
+       resource="{{flowcell.sequencer.get_absolute_url}}">
+  <b>Instrument</b>:
+    <span property="libns:sequencer_name">{{ flowcell.sequencer.name }}</span>
+    {% if flowcell.sequencer.instrument_name %}
+    (<span property="libns:sequencer_instrument">{{ flowcell.sequencer.instrument_name}}</span>)
+    {% endif %}
+    <br/>
+  <b>Instrument Model</b>:
+    <span property="libns:sequencer_model">{{flowcell.sequencer.model}}</span>
+    <br/>
+  </div>
   <b>Run Date</b>:
     <span property="libns:date" content="{{flowcell.run_date|date:'Y-m-d\TH:i:s'}}" datatype="xsd:dateTime">{{ flowcell.run_date }}</span><br/>
   <b>Type</b>:
diff --git a/htsworkflow/frontend/templates/experiments/sequencer.html b/htsworkflow/frontend/templates/experiments/sequencer.html
new file mode 100644 (file)
index 0000000..e4a6e0b
--- /dev/null
@@ -0,0 +1,24 @@
+{% extends "base_site.html" %}
+{% load adminmedia humanize i18n %}
+{% block extrahead %}
+    <!-- App Stuff -->
+    <link type="text/css" rel="stylesheet" href="/static/css/app.css" />
+    <script type="text/javascript" src="/static/js/jquery.min.js"></script>
+
+    {% block additional_javascript %}
+    {% endblock %}
+{% endblock %}
+
+{% block content %}
+    <h1>Sequencer</h1>
+    <dl about="{{sequencer.get_absolute_url}}" typeof="libns:sequencer">
+      <dt>Name</dt>
+      <dl property="libns:sequencer_name">{{sequencer.name}}</dl>
+      <dt>Instrument Name</dt>
+      <dl property="libns:insrument_name">{{sequencer.instrument_name}}</dl>
+      <dt>Model</dt>
+      <dl property="libns:model">{{sequencer.model}}</dl>
+      <dt>Comment</dt>
+      <dl></dl>
+    </dl>
+{% endblock %}
index 6c3bfa6da6bf12d98f6e0eaa61454a1aa57fc369..a03330436e72343d668911aeda5bcdac5659263a 100644 (file)
@@ -49,6 +49,8 @@ urlpatterns = patterns('',
      'htsworkflow.frontend.samples.views.library_id_to_admin_url'),
     # sample / library information
     (r'^samples/', include('htsworkflow.frontend.samples.urls')),
+    (r'^sequencer/(?P<sequencer_id>\w+)',
+       'htsworkflow.frontend.experiments.views.sequencer'),
     # Raw result files
     (r'^results/(?P<flowcell_id>\w+)/(?P<cnm>C[0-9]+-[0-9]+)/summary/',
       'htsworkflow.frontend.samples.views.summaryhtm_fc_cnm'),