Allow grabbing library information via json.
authorDiane Trout <diane@caltech.edu>
Sat, 19 Sep 2009 01:15:48 +0000 (01:15 +0000)
committerDiane Trout <diane@caltech.edu>
Sat, 19 Sep 2009 01:15:48 +0000 (01:15 +0000)
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.

htsworkflow/frontend/experiments/experiments.py
htsworkflow/frontend/experiments/fixtures/test_flowcells.json
htsworkflow/frontend/experiments/tests.py
htsworkflow/frontend/experiments/urls.py
htsworkflow/frontend/samples/fixtures/initial_data.json
htsworkflow/frontend/samples/fixtures/test_samples.json [new file with mode: 0644]
htsworkflow/frontend/samples/tests.py
htsworkflow/frontend/samples/urls.py [new file with mode: 0644]
htsworkflow/frontend/samples/views.py
htsworkflow/frontend/urls.py

index d0dd7641383d0c2e3bba8d7a69104e4903fa7692..f9144b01095e979a96a83cc7d09f91878df7b2d3 100755 (executable)
@@ -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 = {
index 74f438d934c86c62b880893a6cae8775620cbd0a..09876795ac3aa0d3219e083ae590ca1948db8a79 100644 (file)
         "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", 
index 6ac669ef3c3448bbb92964a8da24affc224ca74d..6f4bc3dbf5e602299dd7d0d6d4ae3d468a9fa37c 100644 (file)
@@ -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')
index 67eabab1a026d6cae02d860e9bd420fb768e4801..bc41e04ee3e7390f8abbb3fb7b3803babd99b2ca 100755 (executable)
@@ -4,7 +4,7 @@ urlpatterns = patterns('',
     (r'^$', 'htsworkflow.frontend.experiments.views.index'),
     #(r'^liblist$', 'htsworkflow.frontend.experiments.views.test_Libs'),
     #(r'^(?P<run_folder>.+)/$', 'gaworkflow.frontend.experiments.views.detail'),
-    (r'^config/(?P<fc_id>.+)/json', 'htsworkflow.frontend.experiments.experiments.flowcell_json'),
+    (r'^config/(?P<fc_id>.+)/json$', 'htsworkflow.frontend.experiments.experiments.flowcell_json'),
     (r'^fcsheet/(?P<fcid>.+)/$', 'htsworkflow.frontend.experiments.views.makeFCSheet'),
     (r'^updStatus$', 'htsworkflow.frontend.experiments.experiments.updStatus'),
     (r'^getConfile$', 'htsworkflow.frontend.experiments.experiments.getConfile'),
index 7094141034e135e08a23245f8af00edde9fc0c2d..dbdcc7a27049b40371c285314c3ff4034b99693f 100644 (file)
@@ -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,
      "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 (file)
index 0000000..dba08b6
--- /dev/null
@@ -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
+        }
+    }
+]
index 9e8a2a33d7fe67e22f7f5ade9efd84210dc16acc..595727e13510c0eaa369e668056cef5c005b988d 100644 (file)
@@ -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 (file)
index 0000000..470ab93
--- /dev/null
@@ -0,0 +1,6 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('',
+    (r"^library/(?P<library_id>\w+)/json", 'htsworkflow.frontend.samples.views.library_json'),
+    (r"^species/(?P<species_id>\w+)/json", 'htsworkflow.frontend.samples.views.species_json'),
+)
index 0b7aab4975fb853ac445311410766a497866fa7b..7e865ac62228fa2559442e45782bcd5dd40b0308 100644 (file)
@@ -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):
     """
index b0423bdbfd1c7eca65871092249886ebcc20c89c..e822fa15615ca525ba39fe1a2c9ca7e3f1e7410b 100644 (file)
@@ -38,6 +38,8 @@ urlpatterns = patterns('',
     # library id to admin url
     (r'^library_id_to_admin_url/(?P<lib_id>\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<flowcell_id>\w+)/(?P<cnm>C[1-9]-[0-9]+)/summary/',
       'htsworkflow.frontend.samples.views.summaryhtm_fc_cnm'),