From c077be894e37342f0f4437afd60859a7fe2f80d3 Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Sat, 19 Sep 2009 01:15:48 +0000 Subject: [PATCH] Allow grabbing library information via json. Also make sure that we refer to libraries by our official "library_id" instead of django's primary key library.id. I needed alter what was being returned by the flowcell json code in order to support this. --- .../frontend/experiments/experiments.py | 2 +- .../experiments/fixtures/test_flowcells.json | 2 +- htsworkflow/frontend/experiments/tests.py | 21 ++- htsworkflow/frontend/experiments/urls.py | 2 +- .../samples/fixtures/initial_data.json | 106 ++++++++++++++ .../samples/fixtures/test_samples.json | 138 ++++++++++++++++++ htsworkflow/frontend/samples/tests.py | 80 +++++++++- htsworkflow/frontend/samples/urls.py | 6 + htsworkflow/frontend/samples/views.py | 71 ++++++++- htsworkflow/frontend/urls.py | 2 + 10 files changed, 419 insertions(+), 11 deletions(-) create mode 100644 htsworkflow/frontend/samples/fixtures/test_samples.json create mode 100644 htsworkflow/frontend/samples/urls.py diff --git a/htsworkflow/frontend/experiments/experiments.py b/htsworkflow/frontend/experiments/experiments.py index d0dd764..f9144b0 100755 --- a/htsworkflow/frontend/experiments/experiments.py +++ b/htsworkflow/frontend/experiments/experiments.py @@ -34,7 +34,7 @@ def flowcell_information(flowcell_id): 'flowcell': lane.flowcell.flowcell_id, 'lane_number': int(lane.lane_number), 'library_name': lane.library.library_name, - 'library_id': lane.library_id, + 'library_id': lane.library.library_id, 'pM': float(lane.pM), } info = { diff --git a/htsworkflow/frontend/experiments/fixtures/test_flowcells.json b/htsworkflow/frontend/experiments/fixtures/test_flowcells.json index 74f438d..0987679 100644 --- a/htsworkflow/frontend/experiments/fixtures/test_flowcells.json +++ b/htsworkflow/frontend/experiments/fixtures/test_flowcells.json @@ -126,7 +126,7 @@ "pk": 11042, "model": "samples.library", "fields": { - "library_id": "11039", + "library_id": "SL039", "ten_nM_dilution": false, "avg_lib_size": 300, "library_name": "Paired ends 99 GM12892", diff --git a/htsworkflow/frontend/experiments/tests.py b/htsworkflow/frontend/experiments/tests.py index 6ac669e..6f4bc3d 100644 --- a/htsworkflow/frontend/experiments/tests.py +++ b/htsworkflow/frontend/experiments/tests.py @@ -34,7 +34,7 @@ class ExperimentsTestCases(TestCase): self.failUnlessEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id) self.failUnlessEqual(lane_dict['lane_number'], lane.lane_number) self.failUnlessEqual(lane_dict['library_name'], lane.library.library_name) - self.failUnlessEqual(lane_dict['library_id'], lane.library_id) + self.failUnlessEqual(lane_dict['library_id'], lane.library.library_id) self.failUnlessAlmostEqual(lane_dict['pM'], float(lane.pM)) self.client.login(username='test', password='BJOKL5kAj6aFZ6A5') @@ -55,7 +55,7 @@ class ExperimentsTestCases(TestCase): self.failUnlessEqual(lane_dict['flowcell'], lane.flowcell.flowcell_id) self.failUnlessEqual(lane_dict['lane_number'], lane.lane_number) self.failUnlessEqual(lane_dict['library_name'], lane.library.library_name) - self.failUnlessEqual(lane_dict['library_id'], lane.library_id) + self.failUnlessEqual(lane_dict['library_id'], lane.library.library_id) self.failUnlessAlmostEqual(lane_dict['pM'], float(lane.pM)) def test_invalid_flowcell(self): @@ -72,3 +72,20 @@ class ExperimentsTestCases(TestCase): """ response = self.client.get(u'/experiments/config/303TUAAXX/json') self.failUnlessEqual(response.status_code, 302) + + def test_library_id(self): + """ + Library IDs should be flexible, so make sure we can retrive a non-numeric ID + """ + self.client.login(username='test', password='BJOKL5kAj6aFZ6A5') + response = self.client.get('/experiments/config/303TUAAXX/json') + self.failUnlessEqual(response.status_code, 200) + flowcell = json.loads(response.content) + + self.failUnlessEqual(flowcell['lane_set']['3']['library_id'], 'SL039') + + response = self.client.get('/samples/library/SL039/json') + self.failUnlessEqual(response.status_code, 200) + library_sl039 = json.loads(response.content) + + self.failUnlessEqual(library_sl039['library_id'], 'SL039') diff --git a/htsworkflow/frontend/experiments/urls.py b/htsworkflow/frontend/experiments/urls.py index 67eabab..bc41e04 100755 --- a/htsworkflow/frontend/experiments/urls.py +++ b/htsworkflow/frontend/experiments/urls.py @@ -4,7 +4,7 @@ urlpatterns = patterns('', (r'^$', 'htsworkflow.frontend.experiments.views.index'), #(r'^liblist$', 'htsworkflow.frontend.experiments.views.test_Libs'), #(r'^(?P.+)/$', 'gaworkflow.frontend.experiments.views.detail'), - (r'^config/(?P.+)/json', 'htsworkflow.frontend.experiments.experiments.flowcell_json'), + (r'^config/(?P.+)/json$', 'htsworkflow.frontend.experiments.experiments.flowcell_json'), (r'^fcsheet/(?P.+)/$', 'htsworkflow.frontend.experiments.views.makeFCSheet'), (r'^updStatus$', 'htsworkflow.frontend.experiments.experiments.updStatus'), (r'^getConfile$', 'htsworkflow.frontend.experiments.experiments.getConfile'), diff --git a/htsworkflow/frontend/samples/fixtures/initial_data.json b/htsworkflow/frontend/samples/fixtures/initial_data.json index 7094141..dbdcc7a 100644 --- a/htsworkflow/frontend/samples/fixtures/initial_data.json +++ b/htsworkflow/frontend/samples/fixtures/initial_data.json @@ -1,4 +1,28 @@ [ + { + "model": "samples.Cellline", + "pk": 1, + "fields": { + "cellline_name": "Unknown", + "notes": "Unknown" + } + }, + { + "model": "samples.Cellline", + "pk": 2, + "fields": { + "cellline_name": "C2C12 Exponential", + "notes": "" + } + }, + { + "model": "samples.Cellline", + "pk": 3, + "fields": { + "cellline_name": "C2C12 60 hrs", + "notes": "Unknown" + } + }, { "model": "samples.LibraryType", "pk": 1, @@ -12,5 +36,87 @@ "fields": { "name": "Paired End" } + }, + { + "model": "samples.ExperimentType", + "pk": 1, + "fields": { + "name": "Unknown" + } + }, + { + "model": "samples.ExperimentType", + "pk": 2, + "fields": { + "name": "ChIP-seq" + } + }, + { + "model": "samples.ExperimentType", + "pk": 4, + "fields": { + "name": "RNA-seq" + } + }, + { + "model": "samples.ExperimentType", + "pk": 7, + "fields": { + "name": "De Novo" + } + }, + { + "model": "samples.ExperimentType", + "pk": 8, + "fields": { + "name": "Whole Genome" + } + }, + { + "model": "samples.ExperimentType", + "pk": 9, + "fields": { + "name": "Small RNA" + } + }, + { + "model": "samples.ExperimentType", + "pk": 10, + "fields": { + "name": "Multiplexed" + } + }, + { + "model": "samples.Species", + "pk": 1, + "fields": { + "scientific_name": "Mus musculus", + "common_name": "mouse" + } + }, + { + "model": "samples.Species", + "pk": 2, + "fields": { + "scientific_name": "Drosophila melanogaster", + "common_name": "fruit fly" + } + }, + { + "model": "samples.Species", + "pk": 3, + "fields": { + "scientific_name": "Caenorhabditis elegans", + "common_name": "" + } + }, + { + "model": "samples.Species", + "pk": 8, + "fields": { + "scientific_name": "Homo sapiens", + "common_name": "" + } } + ] diff --git a/htsworkflow/frontend/samples/fixtures/test_samples.json b/htsworkflow/frontend/samples/fixtures/test_samples.json new file mode 100644 index 0000000..dba08b6 --- /dev/null +++ b/htsworkflow/frontend/samples/fixtures/test_samples.json @@ -0,0 +1,138 @@ +[ + {"pk": 5, "model": "auth.user", + "fields": { + "username": "test", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "last_login": "2009-01-01 00:00:01", + "groups": [], + "user_permissions": [], + "password": "sha1$foo$5e4eefec1144a04becfb7da79244f07c487fc345", + "email": "", + "date_joined": "2009-01-01 00:01:01" + } + }, + {"pk": 10984, "model": "samples.library", + "fields": { + "library_id": "10981", + "ten_nM_dilution": false, + "avg_lib_size": 400, + "library_name": "Paired End Multiplexed Sp-BAC", + "creation_date": "2009-07-21", + "cell_line": 1, + "library_species": 2, + "library_type": null, + "made_by": "Igor", + "affiliations": [ + 40 + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "Done", + "tags": [], + "made_for": "Andy Cameron", + "amplified_from_sample": null, + "notes": "Combined 10957-10968", + "undiluted_concentration": "5.1", + "successful_pM": null, + "experiment_type": 10, + "antibody": null + } + }, + { + "pk": 11019, + "model": "samples.library", + "fields": { + "library_id": "11016", + "ten_nM_dilution": false, + "avg_lib_size": 325, + "library_name": "Paired End Pfl #3 MP 7/24/9 a", + "creation_date": "2009-08-06", + "cell_line": 1, + "library_species": 1, + "library_type": 2, + "made_by": "Lorian", + "affiliations": [ + 41 + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "", + "amplified_from_sample": 11006, + "notes": "7/31/2009 16:08:22\tColor: Blue", + "undiluted_concentration": "35.5", + "successful_pM": null, + "experiment_type": 8, + "antibody": null + } + }, + { + "pk": 11042, + "model": "samples.library", + "fields": { + "library_id": "11039", + "ten_nM_dilution": false, + "avg_lib_size": 300, + "library_name": "Paired ends 99 GM12892", + "creation_date": "2009-08-25", + "cell_line": 1, + "library_species": 8, + "library_type": 2, + "made_by": "Brian Williams", + "affiliations": [ + 4, + 8, + 12 + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "fragment size = 300 bp, Amicon filtered\r\nnanodrop: 56.3", + "undiluted_concentration": "28.7", + "successful_pM": null, + "experiment_type": 4, + "antibody": null + } + }, + { + "pk": 11006, + "model": "samples.library", + "fields": { + "library_id": "11003", + "ten_nM_dilution": false, + "avg_lib_size": 325, + "library_name": "Paired End Pfl #3 MP 7/24/9", + "creation_date": "2009-08-05", + "cell_line": 1, + "library_species": 1, + "library_type": 2, + "made_by": "Lorian", + "affiliations": [ + 41 + ], + "replicate": 1, + "condition": 1, + "hidden": true, + "stopping_point": "1A", + "tags": [], + "made_for": "", + "amplified_from_sample": null, + "notes": "7/31/2009 16:08:22\tColor: Blue", + "undiluted_concentration": null, + "successful_pM": null, + "experiment_type": 8, + "antibody": null + } + } +] diff --git a/htsworkflow/frontend/samples/tests.py b/htsworkflow/frontend/samples/tests.py index 9e8a2a3..595727e 100644 --- a/htsworkflow/frontend/samples/tests.py +++ b/htsworkflow/frontend/samples/tests.py @@ -1,11 +1,23 @@ import datetime import unittest + +try: + import json +except ImportError, e: + import simplejson as json + +from django.test import TestCase + from htsworkflow.frontend.samples.models import \ Affiliation, \ ExperimentType, \ Species, \ Library +from htsworkflow.frontend.samples.views import \ + library_dict, \ + library_json + # The django test runner flushes the database between test suites not cases, # so to be more compatible with running via nose we flush the database tables # of interest before creating our sample data. @@ -81,7 +93,7 @@ def create_db(obj): ) obj.library_10002.save() -class LibraryTestCase(unittest.TestCase): +class LibraryTestCase(TestCase): def setUp(self): create_db(self) @@ -100,3 +112,69 @@ class LibraryTestCase(unittest.TestCase): self.failUnless(len(self.library_10002.affiliations.all()), 2) self.failUnless(self.library_10001.affiliation(), 'Alice, Bob') +class SampleWebTestCase(TestCase): + """ + Test returning data from our database in rest like ways. + (like returning json objects) + """ + fixtures = ['test_samples.json'] + + def test_library_info(self): + + for lib in Library.objects.all(): + lib_dict = library_dict(lib.library_id) + self.client.login(username='test', password='BJOKL5kAj6aFZ6A5') + url = '/samples/library/%s/json' % (lib.library_id,) + lib_response = self.client.get(url) + self.failUnlessEqual(lib_response.status_code, 200) + lib_json = json.loads(lib_response.content) + + for d in [lib_dict, lib_json]: + # amplified_from_sample is a link to the library table, + # I want to use the "library_id" for the data lookups not + # the embedded primary key. + # It gets slightly confusing on how to implement sending the right id + # since amplified_from_sample can be null + #self.failUnlessEqual(d['amplified_from_sample'], lib.amplified_from_sample) + self.failUnlessEqual(d['antibody_id'], lib.antibody_id) + self.failUnlessEqual(d['avg_lib_size'], lib.avg_lib_size) + self.failUnlessEqual(d['cell_line'], lib.cell_line.cellline_name) + self.failUnlessEqual(d['cell_line_id'], lib.cell_line_id) + self.failUnlessEqual(d['experiment_type'], lib.experiment_type.name) + self.failUnlessEqual(d['experiment_type_id'], lib.experiment_type_id) + self.failUnlessEqual(d['id'], lib.id) + self.failUnlessEqual(d['library_id'], lib.library_id) + self.failUnlessEqual(d['library_name'], lib.library_name) + self.failUnlessEqual(d['library_species'], lib.library_species.scientific_name) + self.failUnlessEqual(d['library_species_id'], lib.library_species_id) + self.failUnlessEqual(d['library_type_id'], lib.library_type_id) + if lib.library_type_id is not None: + self.failUnlessEqual(d['library_type'], lib.library_type.name) + else: + self.failUnlessEqual(d['library_type'], None) + self.failUnlessEqual(d['made_for'], lib.made_for) + self.failUnlessEqual(d['made_by'], lib.made_by) + self.failUnlessEqual(d['notes'], lib.notes) + self.failUnlessEqual(d['replicate'], lib.replicate) + self.failUnlessEqual(d['stopping_point'], lib.stopping_point) + self.failUnlessEqual(d['successful_pM'], lib.successful_pM) + self.failUnlessEqual(d['undiluted_concentration'], + unicode(lib.undiluted_concentration)) + def test_invalid_library(self): + """ + Make sure we get a 404 if we request an invalid library id + """ + self.client.login(username='test', password='BJOKL5kAj6aFZ6A5') + response = self.client.get('/samples/library/nottheone/json') + self.failUnlessEqual(response.status_code, 404) + + + def test_library_not_logged_in(self): + """ + Make sure we get a 302 if we're not logged in + """ + response = self.client.get('/samples/library/10981/json') + self.failUnlessEqual(response.status_code, 302) + self.client.login(username='test', password='BJOKL5kAj6aFZ6A5') + response = self.client.get('/samples/library/10981/json') + self.failUnlessEqual(response.status_code, 200) diff --git a/htsworkflow/frontend/samples/urls.py b/htsworkflow/frontend/samples/urls.py new file mode 100644 index 0000000..470ab93 --- /dev/null +++ b/htsworkflow/frontend/samples/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls.defaults import * + +urlpatterns = patterns('', + (r"^library/(?P\w+)/json", 'htsworkflow.frontend.samples.views.library_json'), + (r"^species/(?P\w+)/json", 'htsworkflow.frontend.samples.views.species_json'), +) diff --git a/htsworkflow/frontend/samples/views.py b/htsworkflow/frontend/samples/views.py index 0b7aab4..7e865ac 100644 --- a/htsworkflow/frontend/samples/views.py +++ b/htsworkflow/frontend/samples/views.py @@ -1,4 +1,12 @@ # Create your views here. +import StringIO +import logging +import os +try: + import json +except ImportError, e: + import simplejson as json + from htsworkflow.frontend.experiments.models import FlowCell from htsworkflow.frontend.samples.changelist import ChangeList from htsworkflow.frontend.samples.models import Library @@ -12,16 +20,12 @@ from htsworkflow.util import makebed from htsworkflow.util import opener from django.core.exceptions import ObjectDoesNotExist -from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import render_to_response from django.template import RequestContext from django.template.loader import get_template from django.contrib.auth.decorators import login_required -import StringIO -import logging -import os - LANE_LIST = [1,2,3,4,5,6,7,8] SAMPLES_CONTEXT_DEFAULTS = { 'app_name': 'Flowcell/Library Tracker', @@ -444,6 +448,63 @@ def library_id_to_admin_url(request, lib_id): lib = Library.objects.get(library_id=lib_id) return HttpResponseRedirect('/admin/samples/library/%s' % (lib.id,)) +def library_dict(library_id): + """ + Given a library id construct a dictionary containing important information + return None if nothing was found + """ + try: + lib = Library.objects.get(library_id = library_id) + except Library.DoesNotExist, e: + return None + + info = { + # 'affiliations'? + # 'aligned_reads': lib.aligned_reads, + #'amplified_into_sample': lib.amplified_into_sample, # into is a colleciton... + #'amplified_from_sample_id': lib.amplified_from_sample, + #'antibody_name': lib.antibody_name(), # we have no antibodies. + 'antibody_id': lib.antibody_id, + 'avg_lib_size': lib.avg_lib_size, + 'cell_line': lib.cell_line.cellline_name, + 'cell_line_id': lib.cell_line_id, + 'experiment_type': lib.experiment_type.name, + 'experiment_type_id': lib.experiment_type_id, + 'id': lib.id, + 'library_id': lib.library_id, + 'library_name': lib.library_name, + 'library_species': lib.library_species.scientific_name, + 'library_species_id': lib.library_species_id, + #'library_type': lib.library_type.name, + 'library_type_id': lib.library_type_id, + 'made_for': lib.made_for, + 'made_by': lib.made_by, + 'notes': lib.notes, + 'replicate': lib.replicate, + 'stopping_point': lib.stopping_point, + 'successful_pM': lib.successful_pM, + 'undiluted_concentration': unicode(lib.undiluted_concentration) + } + if lib.library_type_id is None: + info['library_type'] = None + else: + info['library_type'] = lib.library_type.name + return info + +@login_required +def library_json(request, library_id): + """ + Return a json formatted library dictionary + """ + # what validation should we do on library_id? + + lib = library_dict(library_id) + if lib is None: + raise Http404 + + lib_json = json.dumps(lib) + return HttpResponse(lib_json, mimetype='application/json') + @login_required def user_profile(request): """ diff --git a/htsworkflow/frontend/urls.py b/htsworkflow/frontend/urls.py index b0423bd..e822fa1 100644 --- a/htsworkflow/frontend/urls.py +++ b/htsworkflow/frontend/urls.py @@ -38,6 +38,8 @@ urlpatterns = patterns('', # library id to admin url (r'^library_id_to_admin_url/(?P\w+)/$', 'htsworkflow.frontend.samples.views.library_id_to_admin_url'), + # sample / library information + (r'^samples/', include('htsworkflow.frontend.samples.urls')), # Raw result files (r'^results/(?P\w+)/(?PC[1-9]-[0-9]+)/summary/', 'htsworkflow.frontend.samples.views.summaryhtm_fc_cnm'), -- 2.30.2