From f065e5cb64e6125452b0aa8c67440087f30135d0 Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Wed, 6 Jun 2012 17:10:53 -0700 Subject: [PATCH] Add instrument ID and model to sequencer table Additionally added displaying that information on the flowcell page. --- htsworkflow/frontend/experiments/admin.py | 13 ++-- .../experiments/fixtures/initial_data.json | 71 ++++++++++++++++++- .../experiments/fixtures/test_flowcells.json | 9 ++- htsworkflow/frontend/experiments/models.py | 17 ++++- htsworkflow/frontend/experiments/tests.py | 40 +++++++++++ htsworkflow/frontend/experiments/views.py | 34 +++++---- .../experiments/flowcell_header.html | 12 ++++ .../templates/experiments/sequencer.html | 24 +++++++ htsworkflow/frontend/urls.py | 2 + 9 files changed, 198 insertions(+), 24 deletions(-) create mode 100644 htsworkflow/frontend/templates/experiments/sequencer.html diff --git a/htsworkflow/frontend/experiments/admin.py b/htsworkflow/frontend/experiments/admin.py index fa0f15d..318625f 100644 --- a/htsworkflow/frontend/experiments/admin.py +++ b/htsworkflow/frontend/experiments/admin.py @@ -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) diff --git a/htsworkflow/frontend/experiments/fixtures/initial_data.json b/htsworkflow/frontend/experiments/fixtures/initial_data.json index 67a6393..3b4d1d5 100644 --- a/htsworkflow/frontend/experiments/fixtures/initial_data.json +++ b/htsworkflow/frontend/experiments/fixtures/initial_data.json @@ -104,6 +104,75 @@ "name": "QSEQ tarfile", "regex": ".*_l(?P[1-8])_r(?P[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 diff --git a/htsworkflow/frontend/experiments/fixtures/test_flowcells.json b/htsworkflow/frontend/experiments/fixtures/test_flowcells.json index 63cf30b..59d5afe 100644 --- a/htsworkflow/frontend/experiments/fixtures/test_flowcells.json +++ b/htsworkflow/frontend/experiments/fixtures/test_flowcells.json @@ -99,8 +99,13 @@ {"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": { diff --git a/htsworkflow/frontend/experiments/models.py b/htsworkflow/frontend/experiments/models.py index bd92eb4..2f97f15 100644 --- a/htsworkflow/frontend/experiments/models.py +++ b/htsworkflow/frontend/experiments/models.py @@ -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) diff --git a/htsworkflow/frontend/experiments/tests.py b/htsworkflow/frontend/experiments/tests.py index 8ec46e0..e54d630 100644 --- a/htsworkflow/frontend/experiments/tests.py +++ b/htsworkflow/frontend/experiments/tests.py @@ -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') diff --git a/htsworkflow/frontend/experiments/views.py b/htsworkflow/frontend/experiments/views.py index 08ce6f0..20dd554 100644 --- a/htsworkflow/frontend/experiments/views.py +++ b/htsworkflow/frontend/experiments/views.py @@ -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 = '

Exp Track Details Page

' 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) diff --git a/htsworkflow/frontend/templates/experiments/flowcell_header.html b/htsworkflow/frontend/templates/experiments/flowcell_header.html index 15ccdae..458619b 100644 --- a/htsworkflow/frontend/templates/experiments/flowcell_header.html +++ b/htsworkflow/frontend/templates/experiments/flowcell_header.html @@ -3,6 +3,18 @@ Flowcell: {{flowcell.flowcell_id}}{% if user.is_staff %}Edit{% endif%}
+
+ Instrument: + {{ flowcell.sequencer.name }} + {% if flowcell.sequencer.instrument_name %} + ({{ flowcell.sequencer.instrument_name}}) + {% endif %} +
+ Instrument Model: + {{flowcell.sequencer.model}} +
+
Run Date: {{ flowcell.run_date }}
Type: diff --git a/htsworkflow/frontend/templates/experiments/sequencer.html b/htsworkflow/frontend/templates/experiments/sequencer.html new file mode 100644 index 0000000..e4a6e0b --- /dev/null +++ b/htsworkflow/frontend/templates/experiments/sequencer.html @@ -0,0 +1,24 @@ +{% extends "base_site.html" %} +{% load adminmedia humanize i18n %} +{% block extrahead %} + + + + + {% block additional_javascript %} + {% endblock %} +{% endblock %} + +{% block content %} +

Sequencer

+
+
Name
+
{{sequencer.name}}
+
Instrument Name
+
{{sequencer.instrument_name}}
+
Model
+
{{sequencer.model}}
+
Comment
+
+
+{% endblock %} diff --git a/htsworkflow/frontend/urls.py b/htsworkflow/frontend/urls.py index 6c3bfa6..a033304 100644 --- a/htsworkflow/frontend/urls.py +++ b/htsworkflow/frontend/urls.py @@ -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\w+)', + 'htsworkflow.frontend.experiments.views.sequencer'), # Raw result files (r'^results/(?P\w+)/(?PC[0-9]+-[0-9]+)/summary/', 'htsworkflow.frontend.samples.views.summaryhtm_fc_cnm'), -- 2.30.2