From bc27b813d0cdd3513323434405ee2ec5126490e5 Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Fri, 16 Sep 2011 17:26:58 -0700 Subject: [PATCH] Add support for tracking the multiplex index sequence. I had to add a new table to store the sequence Samples.MultiplexIndex its manually searched by library type and ID. There's also some extra complexity in that the multiplex id field allows entering a comma seperated list to handle the cases where there are samples that are pooled before being given to us. (As our library actually represents a single physical tube). This also adds a --sample-sheet option to attempt to create a sample sheet for demultiplexing samples. --- docs/v0.5.3_to_v0.5.4.sql | 46 + .../frontend/experiments/experiments.py | 78 +- .../experiments/fixtures/test_flowcells.json | 1264 +++++++++-------- htsworkflow/frontend/experiments/tests.py | 195 ++- htsworkflow/frontend/samples/admin.py | 61 +- .../samples/fixtures/initial_data.json | 671 ++++++++- htsworkflow/frontend/samples/models.py | 165 ++- .../frontend/templates/sample_header.html | 23 +- htsworkflow/pipelines/retrieve_config.py | 209 ++- .../pipelines/test/test_retrive_config.py | 45 +- scripts/htsw-get-config | 74 +- 11 files changed, 1920 insertions(+), 911 deletions(-) create mode 100644 docs/v0.5.3_to_v0.5.4.sql diff --git a/docs/v0.5.3_to_v0.5.4.sql b/docs/v0.5.3_to_v0.5.4.sql new file mode 100644 index 0000000..031816b --- /dev/null +++ b/docs/v0.5.3_to_v0.5.4.sql @@ -0,0 +1,46 @@ +-- Add fields to support multiplexing, and update our database + +alter table samples_library add column multiplex_id varchar(128); +alter table samples_librarytype add column can_multiplex bool not null default false; +alter table samples_librarytype add column is_paired_end bool not null default false; +update samples_librarytype set can_multiplex=0, is_paired_end=0; +update samples_librarytype set can_multiplex=1 where id in (5,7,8); +update samples_librarytype set is_paired_end=1 where id in (2,5,7,8); +update samples_library set multiplex_id=1 where library_name like "Index #1 %"; +update samples_library set multiplex_id=2 where library_name like "Index #2 %"; +update samples_library set multiplex_id=3 where library_name like "Index #3 %"; +update samples_library set multiplex_id=4 where library_name like "Index #4 %"; +update samples_library set multiplex_id=5 where library_name like "Index #5 %"; +update samples_library set multiplex_id=6 where library_name like "Index #6 %"; +update samples_library set multiplex_id=7 where library_name like "Index #7 %"; +update samples_library set multiplex_id=8 where library_name like "Index #8 %"; +update samples_library set multiplex_id=9 where library_name like "Index #9 %"; +update samples_library set multiplex_id=10 where library_name like "Index #10 %"; +update samples_library set multiplex_id=11 where library_name like "Index #11 %"; +update samples_library set multiplex_id=12 where library_name like "Index #12 %"; + +update samples_library set multiplex_id=1 where library_name like "Nextera #1 %"; +update samples_library set multiplex_id=2 where library_name like "Nextera #2 %"; +update samples_library set multiplex_id=3 where library_name like "Nextera #3 %"; +update samples_library set multiplex_id=4 where library_name like "Nextera #4 %"; +update samples_library set multiplex_id=5 where library_name like "Nextera #5 %"; +update samples_library set multiplex_id=6 where library_name like "Nextera #6 %"; +update samples_library set multiplex_id=7 where library_name like "Nextera #7 %"; +update samples_library set multiplex_id=8 where library_name like "Nextera #8 %"; +update samples_library set multiplex_id=9 where library_name like "Nextera #9 %"; +update samples_library set multiplex_id=10 where library_name like "Nextera #10 %"; +update samples_library set multiplex_id=11 where library_name like "Nextera #11 %"; +update samples_library set multiplex_id=12 where library_name like "Nextera #12 %"; + +update samples_library set multiplex_id=1 where library_name like "Nextera index1 %"; +update samples_library set multiplex_id=2 where library_name like "Nextera index2 %"; +update samples_library set multiplex_id=3 where library_name like "Nextera index3 %"; +update samples_library set multiplex_id=4 where library_name like "Nextera index4 %"; +update samples_library set multiplex_id=5 where library_name like "Nextera index5 %"; +update samples_library set multiplex_id=6 where library_name like "Nextera index6 %"; +update samples_library set multiplex_id=7 where library_name like "Nextera index7 %"; +update samples_library set multiplex_id=8 where library_name like "Nextera index8 %"; +update samples_library set multiplex_id=9 where library_name like "Nextera index9 %"; +update samples_library set multiplex_id=10 where library_name like "Nextera index10 %"; +update samples_library set multiplex_id=11 where library_name like "Nextera index11 %"; +update samples_library set multiplex_id=12 where library_name like "Nextera index12 %"; diff --git a/htsworkflow/frontend/experiments/experiments.py b/htsworkflow/frontend/experiments/experiments.py index 5dec8cf..3cdbc7b 100755 --- a/htsworkflow/frontend/experiments/experiments.py +++ b/htsworkflow/frontend/experiments/experiments.py @@ -4,7 +4,7 @@ try: import json except ImportError, e: import simplejson as json - + import os import re @@ -20,7 +20,7 @@ from htsworkflow.frontend.experiments.models import \ DataRun, \ Lane, \ LANE_STATUS_MAP -from htsworkflow.frontend.samples.models import Library, HTSUser +from htsworkflow.frontend.samples.models import Library, MultiplexIndex, HTSUser def flowcell_information(flowcell_id): """ @@ -33,13 +33,13 @@ def flowcell_information(flowcell_id): lane_set = {} for lane in fc.lane_set.all(): - lane_set[lane.lane_number] = { + lane_item = { 'cluster_estimate': lane.cluster_estimate, 'comment': lane.comment, 'experiment_type': lane.library.experiment_type.name, 'experiment_type_id': lane.library.experiment_type_id, 'flowcell': lane.flowcell.flowcell_id, - 'lane_number': int(lane.lane_number), + 'lane_number': lane.lane_number, 'library_name': lane.library.library_name, 'library_id': lane.library.id, 'library_species': lane.library.library_species.scientific_name, @@ -48,12 +48,17 @@ def flowcell_information(flowcell_id): 'status_code': lane.status, 'status': LANE_STATUS_MAP[lane.status] } + sequences = lane.library.index_sequences() + if sequences is not None: + lane_item['index_sequence'] = sequences + + lane_set.setdefault(lane.lane_number,[]).append(lane_item) if fc.control_lane is None: control_lane = None else: control_lane = int(fc.control_lane) - + info = { 'advanced_run': fc.advanced_run, 'cluster_station_id': fc.cluster_station_id, @@ -70,7 +75,7 @@ def flowcell_information(flowcell_id): 'sequencer_id': fc.sequencer_id, 'sequencer': fc.sequencer.name, } - + return info def flowcell_json(request, fc_id): @@ -78,12 +83,12 @@ def flowcell_json(request, fc_id): Return a JSON blob containing enough information to generate a config file. """ require_api_key(request) - + fc_dict = flowcell_information(fc_id) if fc_dict is None: raise Http404 - + fc_json = json.dumps(fc_dict) return HttpResponse(fc_json, mimetype = 'application/json') @@ -93,12 +98,12 @@ def lanes_for(username=None): """ query = {} if username is not None: - user = HTSUser.objects.get(username=username) + user = HTSUser.objects.get(username=username) query.update({'library__affiliations__users__id': user.id}) - + lanes = Lane.objects.filter(**query).order_by('-flowcell__run_date') - + result = [] for l in lanes: affiliations = l.library.affiliations.all() @@ -122,12 +127,13 @@ def lanes_for_json(request, username): result = lanes_for(username) except ObjectDoesNotExist, e: raise Http404 - + #convert query set to python structure - + result_json = json.dumps(result) return HttpResponse(result_json, mimetype='application/json') - + + def updStatus(request): output='' user = 'none' @@ -141,7 +147,7 @@ def updStatus(request): user = request.user #Check access permission - if not (user.is_superuser and settings.ALLOWED_IPS.has_key(ClIP)): + if not (user.is_superuser and settings.ALLOWED_IPS.has_key(ClIP)): return HttpResponse("%s access denied from %s." % (user, ClIP)) # ~~~~~~Parameters for the job ~~~~ @@ -149,22 +155,22 @@ def updStatus(request): fcid = request.REQUEST['fcid'] else: return HttpResponse('missing fcid') - + if request.REQUEST.has_key('runf'): runfolder = request.REQUEST['runf'] else: return HttpResponse('missing runf') - + if request.REQUEST.has_key('updst'): UpdatedStatus = request.REQUEST['updst'] else: return HttpResponse('missing status') - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Update Data Run status in DB - # Try get rec. If not found return 'entry not found + ', if found try update and return updated + # Try get rec. If not found return 'entry not found + ', if found try update and return updated try: rec = DataRun.objects.get(run_folder=runfolder) rec.run_status = UpdatedStatus @@ -204,15 +210,15 @@ def generateConfile(request,fcid): config += ['ELAND_MULTIPLE_INSTANCES 8'] genome_dir = 'GENOME_DIR /Volumes/Genomes/' eland_genome = 'ELAND_GENOME /Volumes/Genomes/' - - try: + + try: fc = FlowCell.objects.get(flowcell_id=fcid) for lane in fc.lane_set.all(): config += [ str(lane.lane_number) +":" + \ genome_dir + lane.library.library_species.scientific_name ] config += [ str(lane.lane_number) +":" + \ eland_genome + lane.library.library_species.scientific_name ] - + except ObjectDoesNotExist: config = 'Entry not found for fcid = '+fcid @@ -245,8 +251,8 @@ def getConfile(req): rec.config_params = cnfgfile rec.save() else: - cnfgfile = 'Failed generating config params for RunFolder = '+runfolder +', Flowcell id = '+ fcid+ ' Config Text:\n'+cnfgfile - + cnfgfile = 'Failed generating config params for RunFolder = '+runfolder +', Flowcell id = '+ fcid+ ' Config Text:\n'+cnfgfile + except ObjectDoesNotExist: cnfgfile = 'Entry not found for RunFolder = '+runfolder @@ -264,14 +270,14 @@ def getLaneLibs(req): outputfile = '' if request.has_key('fcid'): fcid = request['fcid'] - try: + try: rec = FlowCell.objects.get(flowcell_id=fcid) #Ex: 071211 year = datetime.today().year.__str__() year = replace(year,'20','') month = datetime.today().month if month < 10: month = "0"+month.__str__() - else: month = month.__str__() + else: month = month.__str__() day = datetime.today().day if day < 10: day = "0"+day.__str__() else: day = day.__str__() @@ -302,7 +308,7 @@ def estimateFlowcellDuration(flowcell): sequencing_seconds_per_cycle= 3600 * 1.5 # 800 is a rough guess pipeline_seconds_per_cycle = 800 - + cycles = flowcell.read_length if flowcell.paired_end: cycles *= 2 @@ -314,7 +320,7 @@ def estimateFlowcellDuration(flowcell): def estimateFlowcellTimeRemaining(flowcell): estimate_mid = estimateFlowcellDuration(flowcell) - + # offset for how long we've been running running_time = datetime.now() - flowcell.run_date estimate_mid -= running_time @@ -329,9 +335,9 @@ def roundToDays(estimate): estimate_low = timedelta(estimate.days, 0) # floor estimate_mid and add a day estimate_high = timedelta(estimate.days+1, 0) - + return (estimate_low, estimate_high) - + def makeUserLaneMap(flowcell): """ @@ -349,26 +355,26 @@ def makeUserLaneMap(flowcell): def getUsersForFlowcell(flowcell): users = set() - + for lane in flowcell.lane_set.all(): for affiliation in lane.library.affiliations.all(): for user in affiliation.users.all(): users.add(user) - + return users - + def makeUserLibraryMap(libraries): """ Given an interable set of libraries return a mapping or users interested in those libraries. """ users = {} - + for library in libraries: for affiliation in library.affiliations.all(): for user in affiliation.users.all(): users.setdefault(user,[]).append(library) - + return users def makeAffiliationLaneMap(flowcell): diff --git a/htsworkflow/frontend/experiments/fixtures/test_flowcells.json b/htsworkflow/frontend/experiments/fixtures/test_flowcells.json index 1afd74e..e6ad6e3 100644 --- a/htsworkflow/frontend/experiments/fixtures/test_flowcells.json +++ b/htsworkflow/frontend/experiments/fixtures/test_flowcells.json @@ -1,5 +1,5 @@ [ - {"pk": 5, "model": "auth.user", + {"pk": 5, "model": "auth.user", "fields": { "username": "test", "first_name": "", @@ -18,7 +18,7 @@ {"pk": 5, "model": "samples.htsuser", "fields" : {} }, - {"pk": 6, "model": "auth.user", + {"pk": 6, "model": "auth.user", "fields": { "username": "admintest", "first_name": "", @@ -37,7 +37,7 @@ {"pk": 6, "model": "samples.htsuser", "fields" : {} }, - {"pk": 7, "model": "auth.user", + {"pk": 7, "model": "auth.user", "fields": { "username": "supertest", "first_name": "", @@ -96,17 +96,17 @@ "email": "pk5@example.com" } }, - {"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": 3, "model": "experiments.sequencer", "fields": {"name": "Sequenced somewhere else."}}, - {"pk": 153, "model": "experiments.flowcell", + {"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": 3, "model": "experiments.sequencer", "fields": {"name": "Sequenced somewhere else."}}, + {"pk": 153, "model": "experiments.flowcell", "fields": { - "paired_end": true, - "run_date": "2007-09-27 22:12:13", - "read_length": 36, + "paired_end": true, + "run_date": "2007-09-27 22:12:13", + "read_length": 36, "notes": "", "advanced_run": false, "control_lane": 2, @@ -114,7 +114,7 @@ "sequencer": 2, "flowcell_id": "FC12150" } - }, + }, {"pk": 1193, "model": "experiments.lane", "fields": { "comment": "No change in cluster numbers, despite slight increase in pM", @@ -124,7 +124,7 @@ "lane_number": 1, "pM": "8" } - }, + }, {"pk": 1192, "model": "experiments.lane", "fields": { "comment": "Other library", @@ -134,35 +134,35 @@ "lane_number": 1, "pM": "7" } - }, + }, - {"pk": "10981", "model": "samples.library", + {"pk": "10981", "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 400, - "library_name": "Paired End Multiplexed Sp-BAC", - "creation_date": "2009-07-21", - "cell_line": 1, - "library_species": 10, - "library_type": null, - "made_by": "Igor", + "ten_nM_dilution": false, + "gel_cut_size": 400, + "library_name": "Paired End Multiplexed Sp-BAC", + "creation_date": "2009-07-21", + "cell_line": 1, + "library_species": 10, + "library_type": null, + "made_by": "Igor", "affiliations": [ 1,2 - ], - "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, + ], + "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": 1194, "model": "experiments.lane", "fields": { "comment": "", @@ -172,36 +172,36 @@ "lane_number": 2, "pM": "7" } - }, + }, { - "pk": "11016", - "model": "samples.library", + "pk": "11016", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 325, - "library_name": "Paired End Pfl #3 MP 7/24/9 a", - "creation_date": "2009-08-06", - "cell_line": 1, - "library_species": 2, - "library_type": 2, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 325, + "library_name": "Paired End Pfl #3 MP 7/24/9 a", + "creation_date": "2009-08-06", + "cell_line": 1, + "library_species": 2, + "library_type": 2, + "made_by": "Lorian", "affiliations": [ 2 - ], - "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, + ], + "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": 1195, "model": "experiments.lane", "fields": { "comment": "", @@ -211,36 +211,36 @@ "lane_number": 3, "pM": "7" } - }, + }, { - "pk": "SL039", - "model": "samples.library", + "pk": "SL039", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_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", + "ten_nM_dilution": false, + "gel_cut_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": [ 3,4 - ], - "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, + ], + "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": 1196, "model": "experiments.lane", "fields": { "comment": "This lane's library had the second lowest concentration of all the libraries built at the same time (2.05ng/ul)", @@ -250,36 +250,36 @@ "lane_number": 4, "pM": "7" } - }, + }, { - "pk": "11060", - "model": "samples.library", + "pk": "11060", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 100 VC_CN_4_M_MBB1185_s1", - "creation_date": "2009-09-01", - "cell_line": 1, - "library_species": 8, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 100 VC_CN_4_M_MBB1185_s1", + "creation_date": "2009-09-01", + "cell_line": 1, + "library_species": 8, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 5 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 24.2", - "undiluted_concentration": "2.05", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 24.2", + "undiluted_concentration": "2.05", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 1197, "model": "experiments.lane", "fields": { "comment": "stuff", @@ -290,36 +290,36 @@ "pM": "7", "status": 2 } - }, + }, { - "pk": "11061", - "model": "samples.library", + "pk": "11061", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 101 VC_CN_4_M_MBB1185_s2", - "creation_date": "2009-09-01", - "cell_line": 1, - "library_species": 8, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 101 VC_CN_4_M_MBB1185_s2", + "creation_date": "2009-09-01", + "cell_line": 1, + "library_species": 8, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 2,4 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 33.1", - "undiluted_concentration": "12.9", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 33.1", + "undiluted_concentration": "12.9", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 1198, "model": "experiments.lane", "fields": { "comment": "This lane's library had the lowest concentration of all the libraries built at the same time (1.2ng/ul)", @@ -330,36 +330,36 @@ "pM": "7", "status": 0 } - }, + }, { - "pk": "11062", - "model": "samples.library", + "pk": "11062", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 102 VC_AU_8_M_MBB4721_s1", - "creation_date": "2009-09-01", - "cell_line": 1, - "library_species": 8, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 102 VC_AU_8_M_MBB4721_s1", + "creation_date": "2009-09-01", + "cell_line": 1, + "library_species": 8, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 4,5 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 13.9", - "undiluted_concentration": "1.2", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 13.9", + "undiluted_concentration": "1.2", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 1199, "model": "experiments.lane", "fields": { "comment": "", @@ -369,36 +369,36 @@ "lane_number": 7, "pM": "7" } - }, + }, { - "pk": "11063", - "model": "samples.library", + "pk": "11063", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 103 VC_AU_8_M_MBB4721_s2", - "creation_date": "2009-09-01", - "cell_line": 1, - "library_species": 8, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 103 VC_AU_8_M_MBB4721_s2", + "creation_date": "2009-09-01", + "cell_line": 1, + "library_species": 8, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 1,3 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 46.9", - "undiluted_concentration": "24.5", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 46.9", + "undiluted_concentration": "24.5", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 1200, "model": "experiments.lane", "fields": { "comment": "This lane's library had the third lowest concentration of all the libraries built at the same time (5.21ng/ul), but gave perfectly normal cluster numbers", @@ -410,35 +410,35 @@ } }, { - "pk": "11064", - "model": "samples.library", + "pk": "11064", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 104 VC_CN_7_M_MBB4898_s1", - "creation_date": "2009-09-01", - "cell_line": 1, - "library_species": 8, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 104 VC_CN_7_M_MBB4898_s1", + "creation_date": "2009-09-01", + "cell_line": 1, + "library_species": 8, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 3,5 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 20.4", - "undiluted_concentration": "5.21", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 20.4", + "undiluted_concentration": "5.21", + "successful_pM": null, + "experiment_type": 4, "antibody": null } }, - {"pk": 152, "model": "experiments.flowcell", + {"pk": 152, "model": "experiments.flowcell", "fields": { "paired_end": false, "run_date": "2009-09-10 18:30:15", @@ -450,7 +450,7 @@ "sequencer": 1, "flowcell_id": "42JTNAAXX" } - }, + }, {"pk": 1185, "model": "experiments.lane", "fields": { "comment": "", @@ -462,34 +462,34 @@ } }, { - "pk": "11035", - "model": "samples.library", + "pk": "11035", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 95 Gilberto_d3_control_LTA", - "creation_date": "2009-08-25", - "cell_line": 1, - "library_species": 9, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 95 Gilberto_d3_control_LTA", + "creation_date": "2009-08-25", + "cell_line": 1, + "library_species": 9, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 3 - ], - "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: 67.1", - "undiluted_concentration": "28.5", - "successful_pM": null, - "experiment_type": 4, + ], + "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: 67.1", + "undiluted_concentration": "28.5", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 1186, "model": "experiments.lane", "fields": { "comment": "", @@ -501,34 +501,34 @@ } }, { - "pk": "11037", - "model": "samples.library", + "pk": "11037", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 97 Kuntz_PDHT", - "creation_date": "2009-08-25", - "cell_line": 1, - "library_species": 3, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 97 Kuntz_PDHT", + "creation_date": "2009-08-25", + "cell_line": 1, + "library_species": 3, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 4 - ], - "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: 52.7", - "undiluted_concentration": "25.5", - "successful_pM": null, - "experiment_type": 4, + ], + "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: 52.7", + "undiluted_concentration": "25.5", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 1187, "model": "experiments.lane", "fields": { "comment": "", @@ -540,34 +540,34 @@ } }, { - "pk": "11045", - "model": "samples.library", + "pk": "11045", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 250, - "library_name": "FLDN1 8/3/9 anti-AcH3 chip B6 a", - "creation_date": "2009-08-26", - "cell_line": null, - "library_species": 9, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 250, + "library_name": "FLDN1 8/3/9 anti-AcH3 chip B6 a", + "creation_date": "2009-08-26", + "cell_line": null, + "library_species": 9, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 5 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "2A", - "tags": [], - "made_for": "", - "amplified_from_sample": 11044, - "notes": "8/21/2009 11:57:54\tColor: Yellow", - "undiluted_concentration": "20.5", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "2A", + "tags": [], + "made_for": "", + "amplified_from_sample": 12044, + "notes": "8/21/2009 11:57:54\tColor: Yellow", + "undiluted_concentration": "20.5", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1188, "model": "experiments.lane", "fields": { "comment": "", @@ -578,34 +578,34 @@ "pM": "7"} }, { - "pk": "11046", - "model": "samples.library", + "pk": "11046", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 250, - "library_name": "FLDN1 7/8/9 anti-DiMeH3K4 chip B6 a", - "creation_date": "2009-08-26", - "cell_line": null, - "library_species": 9, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 250, + "library_name": "FLDN1 7/8/9 anti-DiMeH3K4 chip B6 a", + "creation_date": "2009-08-26", + "cell_line": null, + "library_species": 9, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 6 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "2A", - "tags": [], - "made_for": "", - "amplified_from_sample": 11045, - "notes": "8/21/2009 11:57:54\tColor: Blue", - "undiluted_concentration": "23.9", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "2A", + "tags": [], + "made_for": "", + "amplified_from_sample": 11045, + "notes": "8/21/2009 11:57:54\tColor: Blue", + "undiluted_concentration": "23.9", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1189, "model": "experiments.lane", "fields": { "comment": "", @@ -615,36 +615,36 @@ "lane_number": 5, "pM": "7" } - }, + }, { - "pk": "11054", - "model": "samples.library", + "pk": "11054", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 225, - "library_name": "HNDHT HLH hnd-1 strain HT115 fed anti-hlh-1 2% fix plate a", - "creation_date": "2009-08-31", - "cell_line": null, - "library_species": 3, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 225, + "library_name": "HNDHT HLH hnd-1 strain HT115 fed anti-hlh-1 2% fix plate a", + "creation_date": "2009-08-31", + "cell_line": null, + "library_species": 3, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 1 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "", - "amplified_from_sample": 11051, - "notes": "8/26/2009 14:46:56\tColor: Purple", - "undiluted_concentration": "1.47", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "", + "amplified_from_sample": 11051, + "notes": "8/26/2009 14:46:56\tColor: Purple", + "undiluted_concentration": "1.47", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1190, "model": "experiments.lane", "fields": { "comment": "", @@ -656,34 +656,34 @@ } }, { - "pk": "11056", - "model": "samples.library", + "pk": "11056", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 225, - "library_name": "HNDM3 HLH hnd-1 strain mex-3 fed anti-hlh-1 2% fix plate a", - "creation_date": "2009-08-31", - "cell_line": null, - "library_species": 3, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 225, + "library_name": "HNDM3 HLH hnd-1 strain mex-3 fed anti-hlh-1 2% fix plate a", + "creation_date": "2009-08-31", + "cell_line": null, + "library_species": 3, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 2 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "", - "amplified_from_sample": 11053, - "notes": "8/26/2009 14:46:56\tColor: Black", - "undiluted_concentration": "1.42", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "", + "amplified_from_sample": 11053, + "notes": "8/26/2009 14:46:56\tColor: Black", + "undiluted_concentration": "1.42", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1191, "model": "experiments.lane", "fields": { "comment": "", @@ -693,36 +693,36 @@ "lane_number": 7, "pM": "7" } - }, + }, { - "pk": "11057", - "model": "samples.library", + "pk": "11057", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 225, - "library_name": "HNDM3 4H8 hnd-1 strain mex-3 fed 4H8 2% fix plate a", - "creation_date": "2009-08-31", - "cell_line": null, - "library_species": 3, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 225, + "library_name": "HNDM3 4H8 hnd-1 strain mex-3 fed 4H8 2% fix plate a", + "creation_date": "2009-08-31", + "cell_line": null, + "library_species": 3, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 3 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "", - "amplified_from_sample": 11054, - "notes": "8/26/2009 14:46:56\tColor: Orange.", - "undiluted_concentration": "1.3", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "", + "amplified_from_sample": 11054, + "notes": "8/26/2009 14:46:56\tColor: Orange.", + "undiluted_concentration": "1.3", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1192, "model": "experiments.lane", "fields": { "comment": "", @@ -732,36 +732,36 @@ "lane_number": 8, "pM": "7" } - }, + }, { - "pk": "11065", - "model": "samples.library", + "pk": "11065", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 105 Kuntz PDM3", - "creation_date": "2009-09-01", - "cell_line": 1, - "library_species": 3, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 105 Kuntz PDM3", + "creation_date": "2009-09-01", + "cell_line": 1, + "library_species": 3, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 4 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 30.5", - "undiluted_concentration": "2.47", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "300 bp gel fragment, Amicon filtered\r\nnanodrop: 30.5", + "undiluted_concentration": "2.47", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 151, "model": "experiments.flowcell", "fields": { "paired_end": false, @@ -784,7 +784,7 @@ "lane_number": 1, "pM": "7" } - }, + }, {"pk": 1178, "model": "experiments.lane", "fields": { "comment": "", @@ -794,134 +794,176 @@ "lane_number": 2, "pM": "7" } - }, + }, { - "pk": "11036", - "model": "samples.library", + "pk": "11036", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 96 Kuntz_PDE1", - "creation_date": "2009-08-25", - "cell_line": 1, - "library_species": 3, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 96 Kuntz_PDE1", + "creation_date": "2009-08-25", + "cell_line": 1, + "library_species": 3, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 5 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "fragment size = 300 bp", - "undiluted_concentration": "30.6", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "fragment size = 300 bp", + "undiluted_concentration": "30.6", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, { - "pk": "11034", - "model": "samples.library", + "pk": "11034", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired ends 94 Gilberto_d3_denerv_LTA", - "creation_date": "2009-08-25", - "cell_line": 1, - "library_species": 9, - "library_type": 2, - "made_by": "Brian Williams", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired ends 94 Gilberto_d3_denerv_LTA", + "creation_date": "2009-08-25", + "cell_line": 1, + "library_species": 9, + "library_type": 2, + "made_by": "Brian Williams", "affiliations": [ 1 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "Brian Williams", - "amplified_from_sample": null, - "notes": "fragment size = 300 bp", - "undiluted_concentration": "27", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "Brian Williams", + "amplified_from_sample": null, + "notes": "fragment size = 300 bp", + "undiluted_concentration": "27", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, { - "pk": "11044", - "model": "samples.library", + "pk": "12044", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 225, - "library_name": "p300 60h C2 FA KF 12/22/8 a", - "creation_date": "2009-08-26", - "cell_line": null, - "library_species": 9, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 225, + "library_name": "Pooled Indexed Test", + "creation_date": "2009-08-26", + "cell_line": null, + "library_species": 9, + "library_type": 5, + "multiplex_id": "1,2,3", + "made_by": "Lorian", "affiliations": [ 2 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "2A", - "tags": [], - "made_for": "", - "amplified_from_sample": 11043, - "notes": "8/21/2009 11:57:54\tColor: Orange", - "undiluted_concentration": "22.4", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "2A", + "tags": [], + "made_for": "", + "amplified_from_sample": 11043, + "notes": "8/21/2009 11:57:54\tColor: Orange", + "undiluted_concentration": "22.4", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, + { + "pk": "11045", + "model": "samples.library", + "fields": { + "ten_nM_dilution": false, + "gel_cut_size": 225, + "library_name": "Simple Indexed Test", + "creation_date": "2009-08-26", + "cell_line": null, + "library_species": 9, + "library_type": 5, + "multiplex_id": "1", + "made_by": "Lorian", + "affiliations": [ + 2 + ], + "replicate": 2, + "condition": null, + "hidden": false, + "stopping_point": "2A", + "tags": [], + "made_for": "", + "amplified_from_sample": 11043, + "notes": "8/21/2009 11:57:54\tColor: Orange", + "undiluted_concentration": "22.4", + "successful_pM": null, + "experiment_type": 2, + "antibody": null + } + }, {"pk": 1179, "model": "experiments.lane", "fields": { "comment": "", - "library": "11044", + "library": "12044", + "cluster_estimate": 196000, + "flowcell": 151, + "lane_number": 3, + "pM": "7" + } + }, + {"pk": 1279, + "model": "experiments.lane", + "fields": { + "comment": "", + "library": "11045", "cluster_estimate": 196000, "flowcell": 151, "lane_number": 3, "pM": "7" } - }, + }, { - "pk": "11044", - "model": "samples.library", + "pk": "11044", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 225, - "library_name": "p300 60h C2 FA KF 12/22/8 a", - "creation_date": "2009-08-26", - "cell_line": null, - "library_species": 9, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 225, + "library_name": "p300 60h C2 FA KF 12/22/8 a", + "creation_date": "2009-08-26", + "cell_line": null, + "library_species": 9, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 3 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "2A", - "tags": [], - "made_for": "", - "amplified_from_sample": 11043, - "notes": "8/21/2009 11:57:54\tColor: Orange.", - "undiluted_concentration": "22.4", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "2A", + "tags": [], + "made_for": "", + "amplified_from_sample": 11043, + "notes": "8/21/2009 11:57:54\tColor: Orange.", + "undiluted_concentration": "22.4", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1180, "model": "experiments.lane", "fields": { "comment": "", @@ -931,36 +973,36 @@ "lane_number": 4, "pM": "7" } - }, + }, { - "pk": "11047", - "model": "samples.library", + "pk": "11047", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 250, - "library_name": "FLDN1 7/8/9 anti-TriMeH3K27 chip B6 a", - "creation_date": "2009-08-26", - "cell_line": null, - "library_species": 9, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 250, + "library_name": "FLDN1 7/8/9 anti-TriMeH3K27 chip B6 a", + "creation_date": "2009-08-26", + "cell_line": null, + "library_species": 9, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 4 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "2A", - "tags": [], - "made_for": "", - "amplified_from_sample": 11046, - "notes": "8/21/2009 11:57:54\tColor: Green", - "undiluted_concentration": "24.9", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "2A", + "tags": [], + "made_for": "", + "amplified_from_sample": 11046, + "notes": "8/21/2009 11:57:54\tColor: Green", + "undiluted_concentration": "24.9", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1181, "model": "experiments.lane", "fields": { "comment": "", @@ -970,36 +1012,36 @@ "lane_number": 5, "pM": "7" } - }, + }, { - "pk": "11055", - "model": "samples.library", + "pk": "11055", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 225, - "library_name": "HNDHT 4H8 hnd-1 strain HT115 fed 4H8 2% fix plate a", - "creation_date": "2009-08-31", - "cell_line": null, - "library_species": 3, - "library_type": 1, - "made_by": "Lorian", + "ten_nM_dilution": false, + "gel_cut_size": 225, + "library_name": "HNDHT 4H8 hnd-1 strain HT115 fed 4H8 2% fix plate a", + "creation_date": "2009-08-31", + "cell_line": null, + "library_species": 3, + "library_type": 1, + "made_by": "Lorian", "affiliations": [ 5 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "1Aa", - "tags": [], - "made_for": "", - "amplified_from_sample": 11052, - "notes": "8/26/2009 14:46:56\tColor: White.", - "undiluted_concentration": "2.17", - "successful_pM": null, - "experiment_type": 2, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "1Aa", + "tags": [], + "made_for": "", + "amplified_from_sample": 11052, + "notes": "8/26/2009 14:46:56\tColor: White.", + "undiluted_concentration": "2.17", + "successful_pM": null, + "experiment_type": 2, "antibody": null } - }, + }, {"pk": 1182, "model": "experiments.lane", "fields": { "comment": "", @@ -1009,36 +1051,36 @@ "lane_number": 6, "pM": "7" } - }, + }, { - "pk": "11067", - "model": "samples.library", + "pk": "11067", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 325, - "library_name": "Paired End SP-BAC Barcoding test 250-300 bp", - "creation_date": "2009-09-03", - "cell_line": 1, - "library_species": 10, - "library_type": 2, - "made_by": "Igor", + "ten_nM_dilution": false, + "gel_cut_size": 325, + "library_name": "Paired End SP-BAC Barcoding test 250-300 bp", + "creation_date": "2009-09-03", + "cell_line": 1, + "library_species": 10, + "library_type": 2, + "made_by": "Igor", "affiliations": [ 1 - ], - "replicate": 1, - "condition": 1, - "hidden": false, - "stopping_point": "Done", - "tags": [], - "made_for": "Andy Cameron", - "amplified_from_sample": null, - "notes": "12 SP BACs", - "undiluted_concentration": "1.45", - "successful_pM": null, - "experiment_type": 8, + ], + "replicate": 1, + "condition": 1, + "hidden": false, + "stopping_point": "Done", + "tags": [], + "made_for": "Andy Cameron", + "amplified_from_sample": null, + "notes": "12 SP BACs", + "undiluted_concentration": "1.45", + "successful_pM": null, + "experiment_type": 8, "antibody": null } - }, + }, {"pk": 1183, "model": "experiments.lane", "fields": { "comment": "", @@ -1048,36 +1090,36 @@ "lane_number": 7, "pM": "7" } - }, + }, { - "pk": "11069", - "model": "samples.library", + "pk": "11069", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired End AG-3d-1 AG domain of floral meristem day 3, rep 1", - "creation_date": "2009-09-02", - "cell_line": null, - "library_species": 6, - "library_type": 2, - "made_by": "Yuling Jiao", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired End AG-3d-1 AG domain of floral meristem day 3, rep 1", + "creation_date": "2009-09-02", + "cell_line": null, + "library_species": 6, + "library_type": 2, + "made_by": "Yuling Jiao", "affiliations": [ 2 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "Done", - "tags": [], - "made_for": "", - "amplified_from_sample": null, - "notes": "nanodrop: Xng/ul.", - "undiluted_concentration": "18.3", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "Done", + "tags": [], + "made_for": "", + "amplified_from_sample": null, + "notes": "nanodrop: Xng/ul.", + "undiluted_concentration": "18.3", + "successful_pM": null, + "experiment_type": 4, "antibody": null } - }, + }, {"pk": 1184, "model": "experiments.lane", "fields": { "comment": "", @@ -1089,39 +1131,39 @@ } }, { - "pk": "11070", - "model": "samples.library", + "pk": "11070", + "model": "samples.library", "fields": { - "ten_nM_dilution": false, - "gel_cut_size": 300, - "library_name": "Paired End AG-5d-1 AG domain of floral meristem day 5, rep 1", - "creation_date": "2009-09-02", - "cell_line": null, - "library_species": 6, - "library_type": 2, - "made_by": "Yuling Jiao", + "ten_nM_dilution": false, + "gel_cut_size": 300, + "library_name": "Paired End AG-5d-1 AG domain of floral meristem day 5, rep 1", + "creation_date": "2009-09-02", + "cell_line": null, + "library_species": 6, + "library_type": 2, + "made_by": "Yuling Jiao", "affiliations": [ 3 - ], - "replicate": 1, - "condition": null, - "hidden": false, - "stopping_point": "Done", - "tags": [], - "made_for": "", - "amplified_from_sample": null, - "notes": "nanodrop: 40ng/ul\r\nCalibrated qbit with standards.\r\nMeasured 2ul library with qbit using HS kit.\r\n", - "undiluted_concentration": "20.3", - "successful_pM": null, - "experiment_type": 4, + ], + "replicate": 1, + "condition": null, + "hidden": false, + "stopping_point": "Done", + "tags": [], + "made_for": "", + "amplified_from_sample": null, + "notes": "nanodrop: 40ng/ul\r\nCalibrated qbit with standards.\r\nMeasured 2ul library with qbit using HS kit.\r\n", + "undiluted_concentration": "20.3", + "successful_pM": null, + "experiment_type": 4, "antibody": null } }, - {"pk": 200, "model": "experiments.flowcell", + {"pk": 200, "model": "experiments.flowcell", "fields": { - "paired_end": true, - "run_date": "2007-09-27 22:12:13", - "read_length": 36, + "paired_end": true, + "run_date": "2007-09-27 22:12:13", + "read_length": 36, "notes": "", "advanced_run": false, "control_lane": 2, diff --git a/htsworkflow/frontend/experiments/tests.py b/htsworkflow/frontend/experiments/tests.py index 664547b..ef299e3 100644 --- a/htsworkflow/frontend/experiments/tests.py +++ b/htsworkflow/frontend/experiments/tests.py @@ -19,7 +19,8 @@ from htsworkflow.frontend.auth import apidata from htsworkflow.pipelines.test.simulate_runfolder import TESTDATA_DIR -LANE_SET = range(1,9) +LANE_SET = range(1, 9) + class ExperimentsTestCases(TestCase): fixtures = ['test_flowcells.json'] @@ -36,14 +37,14 @@ class ExperimentsTestCases(TestCase): 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): + 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,)) + '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')) @@ -62,50 +63,69 @@ class ExperimentsTestCases(TestCase): fc_django = models.FlowCell.objects.get(flowcell_id=fc_id) self.failUnlessEqual(fc_dict['flowcell_id'], fc_id) self.failUnlessEqual(fc_django.flowcell_id, fc_id) - self.failUnlessEqual(fc_dict['sequencer'], fc_django.sequencer.name) - self.failUnlessEqual(fc_dict['read_length'], fc_django.read_length) + self.failUnlessEqual(fc_dict['sequencer'], + fc_django.sequencer.name) + self.failUnlessEqual(fc_dict['read_length'], + fc_django.read_length) self.failUnlessEqual(fc_dict['notes'], fc_django.notes) - self.failUnlessEqual(fc_dict['cluster_station'], fc_django.cluster_station.name) + self.failUnlessEqual(fc_dict['cluster_station'], + fc_django.cluster_station.name) for lane in fc_django.lane_set.all(): - lane_dict = fc_dict['lane_set'][lane.lane_number] - self.failUnlessEqual(lane_dict['cluster_estimate'], lane.cluster_estimate) + lane_contents = fc_dict['lane_set'][lane.lane_number] + lane_dict = multi_lane_to_dict(lane_contents)[lane.library_id] + self.failUnlessEqual(lane_dict['cluster_estimate'], + lane.cluster_estimate) self.failUnlessEqual(lane_dict['comment'], lane.comment) - 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['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.failUnlessAlmostEqual(float(lane_dict['pM']), float(lane.pM)) + self.failUnlessAlmostEqual(float(lane_dict['pM']), + float(lane.pM)) self.failUnlessEqual(lane_dict['library_species'], - lane.library.library_species.scientific_name) - - response = self.client.get('/experiments/config/%s/json' % (fc_id,), apidata) + lane.library.library_species.scientific_name) + + flowcell_url = '/experiments/config/%s/json' + response = self.client.get(flowcell_url % (fc_id,), apidata) # strptime isoformat string = '%Y-%m-%dT%H:%M:%S' fc_json = json.loads(response.content) self.failUnlessEqual(fc_json['flowcell_id'], fc_id) - self.failUnlessEqual(fc_json['sequencer'], fc_django.sequencer.name) + self.failUnlessEqual(fc_json['sequencer'], + fc_django.sequencer.name) self.failUnlessEqual(fc_json['read_length'], fc_django.read_length) self.failUnlessEqual(fc_json['notes'], fc_django.notes) - self.failUnlessEqual(fc_json['cluster_station'], fc_django.cluster_station.name) - + self.failUnlessEqual(fc_json['cluster_station'], + fc_django.cluster_station.name) for lane in fc_django.lane_set.all(): - lane_dict = fc_json['lane_set'][unicode(lane.lane_number)] - self.failUnlessEqual(lane_dict['cluster_estimate'], lane.cluster_estimate) + lane_contents = fc_json['lane_set'][unicode(lane.lane_number)] + lane_dict = multi_lane_to_dict(lane_contents)[lane.library_id] + + self.failUnlessEqual(lane_dict['cluster_estimate'], + lane.cluster_estimate) self.failUnlessEqual(lane_dict['comment'], lane.comment) - 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['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.failUnlessAlmostEqual(float(lane_dict['pM']), float(lane.pM)) + self.failUnlessAlmostEqual(float(lane_dict['pM']), + float(lane.pM)) self.failUnlessEqual(lane_dict['library_species'], - lane.library.library_species.scientific_name) + lane.library.library_species.scientific_name) def test_invalid_flowcell(self): """ Make sure we get a 404 if we request an invalid flowcell ID """ - response = self.client.get('/experiments/config/nottheone/json', apidata) + flowcell_url = '/experiments/config/nottheone/json' + response = self.client.get(flowcell_url, apidata) self.failUnlessEqual(response.status_code, 404) def test_no_key(self): @@ -117,13 +137,16 @@ class ExperimentsTestCases(TestCase): def test_library_id(self): """ - Library IDs should be flexible, so make sure we can retrive a non-numeric ID + Library IDs should be flexible, retrive a non-numeric ID """ - response = self.client.get('/experiments/config/FC12150/json', apidata) + flowcell_url = '/experiments/config/FC12150/json' + response = self.client.get(flowcell_url, apidata) self.failUnlessEqual(response.status_code, 200) flowcell = json.loads(response.content) - self.failUnlessEqual(flowcell['lane_set']['3']['library_id'], 'SL039') + lane_contents = flowcell['lane_set']['3'] + lane_library = lane_contents[0] + self.failUnlessEqual(lane_library['library_id'], 'SL039') response = self.client.get('/samples/library/SL039/json', apidata) self.failUnlessEqual(response.status_code, 200) @@ -137,21 +160,22 @@ class ExperimentsTestCases(TestCase): Library's have IDs, libraries also have primary keys, we eventually had enough libraries that the drop down combo box was too - hard to filter through, unfortnately we want a field that uses our library - id and not the internal primary key, and raw_id_field uses primary keys. + hard to filter through, unfortnately we want a field that uses our + library id and not the internal primary key, and raw_id_field uses + primary keys. - This tests to make sure that the value entered in the raw library id field matches - the library id looked up. + This tests to make sure that the value entered in the raw library id + field matches the library id looked up. """ - expected_ids = [u'10981',u'11016',u'SL039',u'11060', - u'11061',u'11062',u'11063',u'11064'] + expected_ids = [u'10981', u'11016', u'SL039', u'11060', + u'11061', u'11062', u'11063', u'11064'] self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5') response = self.client.get('/admin/experiments/flowcell/153/') soup = BeautifulSoup(response.content) - for i in range(0,8): + for i in range(0, 8): input_field = soup.find(id='id_lane_set-%d-library' % (i,)) library_field = input_field.findNext('strong') - library_id, library_name = library_field.string.split(':') + library_id, library_name = library_field.text.split(':') # strip leading '#' sign from name library_id = library_id[1:] self.failUnlessEqual(library_id, expected_ids[i]) @@ -174,8 +198,19 @@ class ExperimentsTestCases(TestCase): self.failUnlessEqual(fc_response.status_code, 200) fc_lane_response = self.client.get('/flowcell/30012AAXX/8/') self.failUnlessEqual(fc_lane_response.status_code, 200) - - + + def test_pooled_multiplex_id(self): + fc_dict = experiments.flowcell_information('42JU1AAXX') + lane_contents = fc_dict['lane_set'][3] + self.assertEqual(len(lane_contents), 2) + lane_dict = multi_lane_to_dict(lane_contents) + + self.assertEqual(lane_dict['12044']['index_sequence'], + {u'1': u'ATCACG', + u'2': u'CGATGT', + u'3': u'TTAGGC'}) + self.assertEqual(lane_dict['11045']['index_sequence'], + {u'1': u'ATCACG'}) def test_lanes_for(self): """ @@ -185,24 +220,29 @@ class ExperimentsTestCases(TestCase): lanes = experiments.lanes_for(user) self.failUnlessEqual(len(lanes), 5) - response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata) + flowcell_url = '/experiments/lanes_for/%s/json' + response = self.client.get(flowcell_url % (user,), apidata) lanes_json = json.loads(response.content) self.failUnlessEqual(len(lanes), len(lanes_json)) for i in range(len(lanes)): self.failUnlessEqual(lanes[i]['comment'], lanes_json[i]['comment']) - self.failUnlessEqual(lanes[i]['lane_number'], lanes_json[i]['lane_number']) - self.failUnlessEqual(lanes[i]['flowcell'], lanes_json[i]['flowcell']) - self.failUnlessEqual(lanes[i]['run_date'], lanes_json[i]['run_date']) - + self.failUnlessEqual(lanes[i]['lane_number'], + lanes_json[i]['lane_number']) + self.failUnlessEqual(lanes[i]['flowcell'], + lanes_json[i]['flowcell']) + self.failUnlessEqual(lanes[i]['run_date'], + lanes_json[i]['run_date']) + def test_lanes_for_no_lanes(self): """ - Do we get something meaningful back when the user isn't attached to anything? + What happens to user who haven't submitted anything """ user = 'supertest' lanes = experiments.lanes_for(user) self.failUnlessEqual(len(lanes), 0) - response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata) + url = '/experiments/lanes_for/%s/json' + response = self.client.get(url % (user,), apidata) lanes_json = json.loads(response.content) def test_lanes_for_no_user(self): @@ -212,22 +252,21 @@ class ExperimentsTestCases(TestCase): user = 'not a real user' self.failUnlessRaises(ObjectDoesNotExist, experiments.lanes_for, user) - response = self.client.get('/experiments/lanes_for/%s/json' % (user,), apidata) + url = '/experiments/lanes_for/%s/json' + response = self.client.get(url % (user,), apidata) 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') @@ -254,10 +293,9 @@ class ExperimentsTestCases(TestCase): lane_files = run.lane_files() self.failUnlessEqual(lane_files[4]['srf'], srf4) - runxml= result_dict['FC12150/C1-37/run_FC12150_2007-09-27.xml'] + 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 @@ -265,20 +303,21 @@ class ExperimentsTestCases(TestCase): 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') + + #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,) + 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 @@ -286,7 +325,8 @@ class TestFileType(TestCase): file_type_object = file_type_objects.get(name=name) self.failUnlessEqual(u"", unicode(file_type_object)) - + + class TestFileType(TestCase): def test_find_file_type(self): file_type_objects = models.FileType.objects @@ -294,9 +334,9 @@ class TestFileType(TestCase): '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_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_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), @@ -319,10 +359,10 @@ class TestFileType(TestCase): '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), + ('../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), + ('/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), @@ -335,9 +375,10 @@ class TestFileType(TestCase): 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('lane', None), lane) self.failUnlessEqual(result.get('end', None), end) - + + class TestEmailNotify(TestCase): fixtures = ['test_flowcells.json'] @@ -349,21 +390,22 @@ class TestEmailNotify(TestCase): self.client.login(username='test', password='BJOKL5kAj6aFZ6A5') response = self.client.get('/experiments/started/153/') self.failUnlessEqual(response.status_code, 302) - + def test_started_email_logged_in_staff(self): - self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5') + self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5') response = self.client.get('/experiments/started/153/') self.failUnlessEqual(response.status_code, 200) def test_started_email_send(self): - self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5') + self.client.login(username='admintest', password='BJOKL5kAj6aFZ6A5') response = self.client.get('/experiments/started/153/') self.failUnlessEqual(response.status_code, 200) - + self.failUnless('pk1@example.com' in response.content) self.failUnless('Lane #8 : (11064) Paired ends 104' in response.content) - response = self.client.get('/experiments/started/153/', {'send':'1','bcc':'on'}) + response = self.client.get('/experiments/started/153/', + {'send': '1','bcc': 'on'}) self.failUnlessEqual(response.status_code, 200) self.failUnlessEqual(len(mail.outbox), 4) for m in mail.outbox: @@ -373,11 +415,16 @@ class TestEmailNotify(TestCase): """ Can we navigate between the flowcell and email forms properly? """ - self.client.login(username='supertest', password='BJOKL5kAj6aFZ6A5') + 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 FC12150', response.content)) # require that navigation back to the admin page exists - self.failUnless(re.search('[^<]+', response.content)) - + flowcell_a_re = '[^<]+' + self.failUnless(re.search(flowcell_a_re, response.content)) + +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)) diff --git a/htsworkflow/frontend/samples/admin.py b/htsworkflow/frontend/samples/admin.py index 4247c88..bcd77e2 100644 --- a/htsworkflow/frontend/samples/admin.py +++ b/htsworkflow/frontend/samples/admin.py @@ -8,7 +8,9 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from django.forms import TextInput, Textarea -from htsworkflow.frontend.samples.models import Antibody, Cellline, Condition, ExperimentType, HTSUser, LibraryType, Species, Affiliation, Library, Tag +from htsworkflow.frontend.samples.models import \ + Antibody, Cellline, Condition, ExperimentType, HTSUser, \ + LibraryType, MultiplexIndex, Species, Affiliation, Library, Tag from htsworkflow.frontend.experiments.models import Lane from htsworkflow.frontend.inventory.models import PrinterTemplate from htsworkflow.frontend.bcmagic.utils import print_zpl_socket @@ -24,7 +26,7 @@ class AffiliationOptions(admin.ModelAdmin): }), ) - # some post 1.0.2 version of django has formfield_overrides + # some post 1.0.2 version of django has formfield_overrides # which would replace this code with: # formfield_overrids = { # models.ManyToMany: { 'widget': widgets.FilteredSelectMultiple } @@ -76,7 +78,7 @@ class HTSUserCreationForm(UserCreationForm): class HTSUserChangeForm(UserChangeForm): class Meta: model = HTSUser - + class HTSUserOptions(UserAdmin): form = HTSUserChangeForm add_form = HTSUserCreationForm @@ -89,14 +91,19 @@ class Library_Inline(admin.TabularInline): model = Library class LibraryTypeOptions(admin.ModelAdmin): + list_display = ['name', 'is_paired_end', 'can_multiplex'] model = LibraryType +class MultiplexIndexOptions(admin.ModelAdmin): + model = MultiplexIndex + list_display = ['adapter_type', 'multiplex_id', 'sequence'] + class LibraryOptions(admin.ModelAdmin): class Media: css = { "all": ("css/wide_account_number.css",) } - + date_hierarchy = "creation_date" save_as = True save_on_top = True @@ -110,7 +117,7 @@ class LibraryOptions(admin.ModelAdmin): list_display = ( 'id', 'library_name', - 'public', + 'index_sequence_text', 'affiliation', 'undiluted_concentration', 'gel_cut_size', @@ -119,9 +126,9 @@ class LibraryOptions(admin.ModelAdmin): list_filter = ( 'hidden', 'affiliations', - 'library_species', - 'experiment_type', - 'made_by', + 'library_species', + 'experiment_type', + 'made_by', 'cell_line', 'stopping_point',) list_display_links = ('id', 'library_name',) @@ -129,7 +136,8 @@ class LibraryOptions(admin.ModelAdmin): (None, { 'fields': ( ('id','library_name','hidden'), - ('library_species', 'library_type', 'experiment_type'), + ('library_species', 'experiment_type'), + ('library_type', 'multiplex_id'), ) }), ('Experiment Detail:', { @@ -140,12 +148,12 @@ class LibraryOptions(admin.ModelAdmin): 'classes': ('collapse',), }), ('Creation Information:', { - 'fields' : (('made_by', 'creation_date', 'stopping_point'), - ('amplified_from_sample'), - ('gel_cut_size', 'insert_size', - 'undiluted_concentration'), + 'fields' : (('made_by', 'creation_date', 'stopping_point'), + ('amplified_from_sample'), + ('gel_cut_size', 'insert_size', + 'undiluted_concentration'), ('bioanalyzer_concentration','bioanalyzer_image_url'), - ('bioanalyzer_summary'), + ('bioanalyzer_summary'), ('notes')) }), ('Library/Project Affiliation:', { @@ -156,45 +164,45 @@ class LibraryOptions(admin.ModelAdmin): LaneLibraryInline, ] actions = ['action_print_library_labels'] - + def action_print_library_labels(self, request, queryset): """ Django action which prints labels for the selected set of labels from the Django Admin interface. """ - + #Probably should ask if the user really meant to print all selected # libraries if the count is above X. X=10 maybe? - + # Grab the library template #FIXME: Hardcoding library template name. Not a good idea... *sigh*. EVIL_HARDCODED_LIBRARY_TEMPLATE_NAME = "Library" - + try: template = PrinterTemplate.objects.get(item_type__name=EVIL_HARDCODED_LIBRARY_TEMPLATE_NAME) except PrinterTemplate.DoesNotExist: self.message_user(request, "Could not find a library template with ItemType.name of '%s'" % \ (EVIL_HARDCODED_LIBRARY_TEMPLATE_NAME)) return - + # ZPL Template t = Template(template.template) - + zpl_list = [] #Iterate over selected labels to print for library in queryset.all(): - + # Django Template Context c = Context({'library': library}) - + # Send rendered template to the printer that the template # object has been attached to in the database. zpl_list.append(t.render(c)) - + print_zpl_socket(zpl_list, host=template.printer.ip_address) - + self.message_user(request, "%s labels printed." % (len(queryset))) - + action_print_library_labels.short_description = "Print Labels" def formfield_for_dbfield(self, db_field, **kwargs): @@ -220,7 +228,7 @@ class SpeciesOptions(admin.ModelAdmin): class TagOptions(admin.ModelAdmin): list_display = ('tag_name', 'context') - fieldsets = ( + fieldsets = ( (None, { 'fields': ('tag_name', 'context') }), @@ -234,5 +242,6 @@ admin.site.register(Condition, ConditionOptions) admin.site.register(ExperimentType, ExperimentTypeOptions) #admin.site.register(HTSUser, HTSUserOptions) admin.site.register(LibraryType, LibraryTypeOptions) +admin.site.register(MultiplexIndex, MultiplexIndexOptions) admin.site.register(Species, SpeciesOptions) #admin.site.register(Tag, TagOptions) diff --git a/htsworkflow/frontend/samples/fixtures/initial_data.json b/htsworkflow/frontend/samples/fixtures/initial_data.json index 008867e..72cbaf8 100644 --- a/htsworkflow/frontend/samples/fixtures/initial_data.json +++ b/htsworkflow/frontend/samples/fixtures/initial_data.json @@ -4,7 +4,7 @@ "pk": 1, "fields": { "cellline_name": "Unknown", - "notes": "Unknown" + "notes": "Unknown" } }, { @@ -12,7 +12,7 @@ "pk": 2, "fields": { "cellline_name": "C2C12 Exponential", - "notes": "" + "notes": "" } }, { @@ -20,21 +20,34 @@ "pk": 3, "fields": { "cellline_name": "C2C12 60 hrs", - "notes": "Unknown" + "notes": "Unknown" } }, { "model": "samples.LibraryType", "pk": 1, "fields": { - "name": "Single End" + "name": "Single End", + "can_multiplex": false, + "is_paired_end": false } }, { "model": "samples.LibraryType", "pk": 2, "fields": { - "name": "Paired End" + "name": "Paired End", + "can_multiplex": false, + "is_paired_end": true + } + }, + { + "model": "samples.LibraryType", + "pk": 5, + "fields": { + "name": "Barcoded", + "can_multiplex": true, + "is_paired_end": true } }, { @@ -131,5 +144,653 @@ "fields": { "scientific_name": "Strongylocentrotus purpuratus" } + }, + { + "pk": 1, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 1, + "adapter_type": 8, + "sequence": "ATCACG" + } + }, + { + "pk": 2, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 2, + "adapter_type": 8, + "sequence": "CGATGT" + } + }, + { + "pk": 3, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 3, + "adapter_type": 8, + "sequence": "TTAGGC" + } + }, + { + "pk": 4, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 4, + "adapter_type": 8, + "sequence": "TGACCA" + } + }, + { + "pk": 5, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 5, + "adapter_type": 8, + "sequence": "ACAGTG" + } + }, + { + "pk": 6, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 6, + "adapter_type": 8, + "sequence": "GCCAAT" + } + }, + { + "pk": 7, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 7, + "adapter_type": 8, + "sequence": "CAGATC" + } + }, + { + "pk": 8, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 8, + "adapter_type": 8, + "sequence": "ACTTGA" + } + }, + { + "pk": 9, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 9, + "adapter_type": 8, + "sequence": "GATCAG" + } + }, + { + "pk": 10, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 10, + "adapter_type": 8, + "sequence": "TAGCTT" + } + }, + { + "pk": 11, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 11, + "adapter_type": 8, + "sequence": "GGCTAC" + } + }, + { + "pk": 12, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 12, + "adapter_type": 8, + "sequence": "CTTGTA" + } + }, + { + "pk": 13, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 1, + "adapter_type": 7, + "sequence": "ATCACG" + } + }, + { + "pk": 14, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 2, + "adapter_type": 7, + "sequence": "CGATGT" + } + }, + { + "pk": 15, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 3, + "adapter_type": 7, + "sequence": "TTAGGC" + } + }, + { + "pk": 16, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 4, + "adapter_type": 7, + "sequence": "TGACCA" + } + }, + { + "pk": 17, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 5, + "adapter_type": 7, + "sequence": "ACAGTG" + } + }, + { + "pk": 18, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 6, + "adapter_type": 7, + "sequence": "GCCAAT" + } + }, + { + "pk": 19, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 7, + "adapter_type": 7, + "sequence": "CAGATC" + } + }, + { + "pk": 20, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 8, + "adapter_type": 7, + "sequence": "ACTTGA" + } + }, + { + "pk": 21, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 9, + "adapter_type": 7, + "sequence": "GATCAG" + } + }, + { + "pk": 22, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 10, + "adapter_type": 7, + "sequence": "TAGCTT" + } + }, + { + "pk": 23, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 11, + "adapter_type": 7, + "sequence": "GGCTAC" + } + }, + { + "pk": 24, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 12, + "adapter_type": 7, + "sequence": "CTTGTA" + } + }, + { + "pk": 25, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 13, + "adapter_type": 7, + "sequence": "AGTCAA" + } + }, + { + "pk": 26, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 14, + "adapter_type": 7, + "sequence": "AGTTCC" + } + }, + { + "pk": 27, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 15, + "adapter_type": 7, + "sequence": "ATGTCA" + } + }, + { + "pk": 28, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 16, + "adapter_type": 7, + "sequence": "CCGTCC" + } + }, + { + "pk": 29, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 17, + "adapter_type": 7, + "sequence": "GTAGAG" + } + }, + { + "pk": 30, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 18, + "adapter_type": 7, + "sequence": "GTCCGC" + } + }, + { + "pk": 31, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 19, + "adapter_type": 7, + "sequence": "GTGAAA" + } + }, + { + "pk": 32, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 20, + "adapter_type": 7, + "sequence": "GTGGCC" + } + }, + { + "pk": 33, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 21, + "adapter_type": 7, + "sequence": "GTTTCG" + } + }, + { + "pk": 34, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 22, + "adapter_type": 7, + "sequence": "CGTACG" + } + }, + { + "pk": 35, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 23, + "adapter_type": 7, + "sequence": "GAGTGG" + } + }, + { + "pk": 36, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 24, + "adapter_type": 7, + "sequence": "GGTAGC" + } + }, + { + "pk": 37, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 25, + "adapter_type": 7, + "sequence": "ACTGAT" + } + }, + { + "pk": 38, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 26, + "adapter_type": 7, + "sequence": "ATGAGC" + } + }, + { + "pk": 39, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 27, + "adapter_type": 7, + "sequence": "ATTCCT" + } + }, + { + "pk": 40, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 28, + "adapter_type": 7, + "sequence": "CAAAAG" + } + }, + { + "pk": 41, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 29, + "adapter_type": 7, + "sequence": "CAACTA" + } + }, + { + "pk": 42, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 30, + "adapter_type": 7, + "sequence": "CACCGG" + } + }, + { + "pk": 43, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 31, + "adapter_type": 7, + "sequence": "CACGAT" + } + }, + { + "pk": 44, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 32, + "adapter_type": 7, + "sequence": "CACTCA" + } + }, + { + "pk": 45, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 33, + "adapter_type": 7, + "sequence": "CAGGCG" + } + }, + { + "pk": 46, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 34, + "adapter_type": 7, + "sequence": "CATGGC" + } + }, + { + "pk": 47, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 35, + "adapter_type": 7, + "sequence": "CATTTT" + } + }, + { + "pk": 48, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 36, + "adapter_type": 7, + "sequence": "CCAACA" + } + }, + { + "pk": 49, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 37, + "adapter_type": 7, + "sequence": "CGGAAT" + } + }, + { + "pk": 50, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 38, + "adapter_type": 7, + "sequence": "CTAGCT" + } + }, + { + "pk": 51, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 39, + "adapter_type": 7, + "sequence": "CTATAC" + } + }, + { + "pk": 52, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 40, + "adapter_type": 7, + "sequence": "CTCAGA" + } + }, + { + "pk": 53, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 41, + "adapter_type": 7, + "sequence": "GACGAC" + } + }, + { + "pk": 54, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 42, + "adapter_type": 7, + "sequence": "TAATCG" + } + }, + { + "pk": 55, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 43, + "adapter_type": 7, + "sequence": "TACAGC" + } + }, + { + "pk": 56, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 44, + "adapter_type": 7, + "sequence": "TATAAT" + } + }, + { + "pk": 57, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 45, + "adapter_type": 7, + "sequence": "TCATTC" + } + }, + { + "pk": 58, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 46, + "adapter_type": 7, + "sequence": "TCCCGA" + } + }, + { + "pk": 59, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 47, + "adapter_type": 7, + "sequence": "TCGAAG" + } + }, + { + "pk": 60, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 48, + "adapter_type": 7, + "sequence": "TCGGCA" + } + }, + { + "pk": 61, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 1, + "adapter_type": 5, + "sequence": "ATCACG" + } + }, + { + "pk": 62, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 2, + "adapter_type": 5, + "sequence": "CGATGT" + } + }, + { + "pk": 63, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 3, + "adapter_type": 5, + "sequence": "TTAGGC" + } + }, + { + "pk": 64, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 4, + "adapter_type": 5, + "sequence": "TGACCA" + } + }, + { + "pk": 65, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 5, + "adapter_type": 5, + "sequence": "ACAGTG" + } + }, + { + "pk": 66, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 6, + "adapter_type": 5, + "sequence": "GCCAAT" + } + }, + { + "pk": 67, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 7, + "adapter_type": 5, + "sequence": "CAGATC" + } + }, + { + "pk": 68, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 8, + "adapter_type": 5, + "sequence": "ACTTGA" + } + }, + { + "pk": 69, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 9, + "adapter_type": 5, + "sequence": "GATCAG" + } + }, + { + "pk": 70, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 10, + "adapter_type": 5, + "sequence": "TAGCTT" + } + }, + { + "pk": 71, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 11, + "adapter_type": 5, + "sequence": "GGCTAC" + } + }, + { + "pk": 72, + "model": "samples.MultiplexIndex", + "fields": { + "multiplex_id": 12, + "adapter_type": 5, + "sequence": "CTTGTA" + } } ] diff --git a/htsworkflow/frontend/samples/models.py b/htsworkflow/frontend/samples/models.py index 531edc6..d1ab660 100644 --- a/htsworkflow/frontend/samples/models.py +++ b/htsworkflow/frontend/samples/models.py @@ -12,12 +12,12 @@ logger = logging.getLogger(__name__) class Antibody(models.Model): antigene = models.CharField(max_length=500, db_index=True) # New field Aug/20/08 - # SQL to add column: + # SQL to add column: # alter table fctracker_antibody add column "nickname" varchar(20) NULL; nickname = models.CharField( max_length=20, blank=True, - null=True, + null=True, db_index=True ) catalog = models.CharField(max_length=50, blank=True, null=True) @@ -35,9 +35,9 @@ class Cellline(models.Model): cellline_name = models.CharField(max_length=100, unique=True, db_index=True) nickname = models.CharField(max_length=20, blank=True, - null=True, + null=True, db_index=True) - + notes = models.TextField(blank=True) def __unicode__(self): return unicode(self.cellline_name) @@ -50,7 +50,7 @@ class Condition(models.Model): max_length=2000, unique=True, db_index=True) nickname = models.CharField(max_length=20, blank=True, - null=True, + null=True, db_index=True, verbose_name = 'Short Name') notes = models.TextField(blank=True) @@ -61,34 +61,34 @@ class Condition(models.Model): class Meta: ordering = ["condition_name"] - + class ExperimentType(models.Model): name = models.CharField(max_length=50, unique=True) def __unicode__(self): return unicode(self.name) -class Tag(models.Model): - tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False) - TAG_CONTEXT = ( - #('Antibody','Antibody'), - #('Cellline', 'Cellline'), - #('Condition', 'Condition'), - ('Library', 'Library'), - ('ANY','ANY'), - ) - context = models.CharField(max_length=50, - choices=TAG_CONTEXT, default='Library') - - def __unicode__(self): - return u'%s' % (self.tag_name) - - class Meta: - ordering = ["context","tag_name"] - +class Tag(models.Model): + tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False) + TAG_CONTEXT = ( + #('Antibody','Antibody'), + #('Cellline', 'Cellline'), + #('Condition', 'Condition'), + ('Library', 'Library'), + ('ANY','ANY'), + ) + context = models.CharField(max_length=50, + choices=TAG_CONTEXT, default='Library') + + def __unicode__(self): + return u'%s' % (self.tag_name) + + class Meta: + ordering = ["context","tag_name"] + class Species(models.Model): - scientific_name = models.CharField(max_length=256, - unique=False, + scientific_name = models.CharField(max_length=256, + unique=False, db_index=True ) common_name = models.CharField(max_length=256, blank=True) @@ -96,7 +96,7 @@ class Species(models.Model): def __unicode__(self): return u'%s (%s)' % (self.scientific_name, self.common_name) - + class Meta: verbose_name_plural = "species" ordering = ["scientific_name"] @@ -104,18 +104,18 @@ class Species(models.Model): @models.permalink def get_absolute_url(self): return ('htsworkflow.frontend.samples.views.species', [str(self.id)]) - + class Affiliation(models.Model): name = models.CharField(max_length=256, db_index=True, verbose_name='Name') - contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name') + contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name') email = models.EmailField(null=True,blank=True) users = models.ManyToManyField('HTSUser', null=True, blank=True) users.admin_order_field = "username" - + def __unicode__(self): str = unicode(self.name) if self.contact is not None and len(self.contact) > 0: - str += u' ('+self.contact+u')' + str += u' ('+self.contact+u')' return str def Users(self): @@ -127,11 +127,28 @@ class Affiliation(models.Model): unique_together = (("name", "contact"),) class LibraryType(models.Model): - name = models.CharField(max_length=255, unique=True) + name = models.CharField(max_length=255, unique=True, + name="Adapter Type") + is_paired_end = models.BooleanField(default=True, + help_text="can you do a paired end run with this adapter") + can_multiplex = models.BooleanField(default=True, + help_text="Does this adapter provide multiplexing?") def __unicode__(self): - return unicode(self.name) + return unicode(self.name) + + class Meta: + ordering = ["-id"] + +class MultiplexIndex(models.Model): + """Map adapter types to the multiplex sequence""" + adapter_type = models.ForeignKey(LibraryType) + multiplex_id = models.CharField(max_length=3, null=False) + sequence = models.CharField(max_length=12, blank=True, null=True) + + class Meta: + unique_together = ('adapter_type', 'multiplex_id') class Library(models.Model): id = models.CharField(max_length=10, primary_key=True) @@ -149,14 +166,18 @@ class Library(models.Model): blank=True,null=True) REPLICATE_NUM = ((1,1),(2,2),(3,3),(4,4)) replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM, - blank=True,null=True) + blank=True,null=True) experiment_type = models.ForeignKey(ExperimentType) - library_type = models.ForeignKey(LibraryType, blank=True, null=True) + library_type = models.ForeignKey(LibraryType, blank=True, null=True, + verbose_name="Adapter Type") + multiplex_id = models.CharField(max_length=128, + blank=True, null=True, + verbose_name="Index ID") creation_date = models.DateField(blank=True, null=True) - made_for = models.CharField(max_length=50, blank=True, + made_for = models.CharField(max_length=50, blank=True, verbose_name='ChIP/DNA/RNA Made By') made_by = models.CharField(max_length=50, blank=True, default="Lorian") - + PROTOCOL_END_POINTS = ( ('?', 'Unknown'), ('Sample', 'Raw sample'), @@ -173,14 +194,14 @@ class Library(models.Model): stopping_point = models.CharField(max_length=25, choices=PROTOCOL_END_POINTS, default='Done') - + amplified_from_sample = models.ForeignKey('self', related_name='amplified_into_sample', blank=True, null=True) - - undiluted_concentration = models.DecimalField("Concentration", + + undiluted_concentration = models.DecimalField("Concentration", max_digits=5, decimal_places=2, blank=True, null=True, - help_text=u"Undiluted concentration (ng/\u00b5l)") + help_text=u"Undiluted concentration (ng/\u00b5l)") # note \u00b5 is the micro symbol in unicode successful_pM = models.DecimalField(max_digits=9, decimal_places=1, blank=True, null=True) @@ -190,27 +211,59 @@ class Library(models.Model): notes = models.TextField(blank=True) bioanalyzer_summary = models.TextField(blank=True,default="") - bioanalyzer_concentration = models.DecimalField(max_digits=5, + bioanalyzer_concentration = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True, help_text=u"(ng/\u00b5l)") bioanalyzer_image_url = models.URLField(blank=True,default="") - + def __unicode__(self): return u'#%s: %s' % (self.id, self.library_name) - + class Meta: verbose_name_plural = "libraries" - #ordering = ["-creation_date"] + #ordering = ["-creation_date"] ordering = ["-id"] - + def antibody_name(self): - str =''+self.antibody.label+'' + str =''+self.antibody.label+'' return str antibody_name.allow_tags = True def organism(self): return self.library_species.common_name + def index_sequences(self): + """Return a dictionary of multiplex index id to sequence + Return None if the library can't multiplex, + + """ + if self.library_type is None: + return None + if not self.library_type.can_multiplex: + return None + if self.multiplex_id is None or len(self.multiplex_id) == 0: + return 'Err: id empty' + sequences = {} + multiplex_ids = self.multiplex_id.split(',') + for multiplex_id in multiplex_ids: + try: + multiplex = MultiplexIndex.objects.get( + adapter_type = self.library_type.id, + multiplex_id = multiplex_id) + sequences[multiplex_id] = multiplex.sequence + except MultiplexIndex.DoesNotExist, e: + sequences[multiplex_id] = 'Err: index not found' + return sequences + + def index_sequence_text(self, seperator=' '): + """Return formatted multiplex index sequences""" + sequences = self.index_sequences() + multiplex_ids = sequences.keys() + multiplex_ids.sort() + return seperator.join(( "%s:%s" %(i,sequences[i]) for i in multiplex_ids)) + index_sequence_text.short_description = "Index" + + def affiliation(self): affs = self.affiliations.all().order_by('name') tstr = '' @@ -218,7 +271,7 @@ class Library(models.Model): for t in affs: ar.append(t.__unicode__()) return '%s' % (", ".join(ar)) - + def is_archived(self): """ returns True if archived else False @@ -235,7 +288,7 @@ class Library(models.Model): name = "Lookup Error" logger.error("protocol stopping point in database didn't match names in library model") return name - + def libtags(self): affs = self.tags.all().order_by('tag_name') @@ -245,7 +298,7 @@ class Library(models.Model): return u'%s' % ( ", ".join(ar)) def DataRun(self): - str ='Data Run' + str ='Data Run' return str DataRun.allow_tags = True @@ -273,21 +326,21 @@ class Library(models.Model): if res[1] > rc_thr[1]: bgcolor ='#00ccff' # Blue else: - if res[1] > rc_thr[2]: + if res[1] > rc_thr[2]: bgcolor ='#ffcc33' # Orange tstr = '
' tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads' tstr += '
' - else: tstr = 'not processed yet' + else: tstr = 'not processed yet' return tstr aligned_reads.allow_tags = True - + def public(self): SITE_ROOT = '/' summary_url = self.get_absolute_url() return 'S' % (summary_url,) public.allow_tags = True - + @models.permalink def get_absolute_url(self): return ('htsworkflow.frontend.samples.views.library_to_flowcells', [str(self.id)]) @@ -311,7 +364,7 @@ class HTSUser(User): def __unicode__(self): #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")" return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')' - + def HTSUserInsertID(sender, instance, **kwargs): """ Force addition of HTSUsers when someone just modifies the auth_user object @@ -321,5 +374,5 @@ def HTSUserInsertID(sender, instance, **kwargs): cursor = connection.cursor() cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,)) cursor.close() - + post_save.connect(HTSUserInsertID, sender=User) diff --git a/htsworkflow/frontend/templates/sample_header.html b/htsworkflow/frontend/templates/sample_header.html index c125a1c..1800fe5 100644 --- a/htsworkflow/frontend/templates/sample_header.html +++ b/htsworkflow/frontend/templates/sample_header.html @@ -1,11 +1,11 @@
-

Library Name

- Library ID: +

Library Name

+ Library ID: {{ lib.id }} {% if user.is_staff %}{% endif %}
- Name: + Name: {{ lib.library_name }}
Affiliations: @@ -19,7 +19,7 @@

Sample Details

- Species: + Species: {{ lib.library_species.scientific_name }}
Experiment Type: @@ -47,7 +47,7 @@
{% endif %} {% if lib.replicate %} - Replicate: + Replicate: {{ lib.replicate }}
{% endif %} @@ -57,24 +57,27 @@ Library Type: {{ lib.library_type }}
+ Multiplex Index: + {{ lib.index_sequence_text }} +
Creation Date {{ lib.creation_date }} -
- Made By: +
+ Made By: {{ lib.made_by }}
{% if lib.gel_cut_size %} - Gel Cut Size: + Gel Cut Size: {{ lib.gel_cut_size }}
{% endif %} {% if lib.insert_size %} - Insert Size: + Insert Size: {{ lib.insert_size }}
{% endif %} {% if lib.undiluted_concentration %} - Concentration: + Concentration: {{ lib.undiluted_concentration }} ng/µl
{% endif %} diff --git a/htsworkflow/pipelines/retrieve_config.py b/htsworkflow/pipelines/retrieve_config.py index dd893e1..888da7d 100644 --- a/htsworkflow/pipelines/retrieve_config.py +++ b/htsworkflow/pipelines/retrieve_config.py @@ -1,10 +1,12 @@ #!/usr/bin/env python +import csv from ConfigParser import RawConfigParser import logging from optparse import OptionParser, IndentedHelpFormatter import os import sys +import types import urllib import urllib2 @@ -16,8 +18,12 @@ except ImportError, e: from htsworkflow.frontend.auth import apidata from htsworkflow.util import api from htsworkflow.util.url import normalize_url -from htsworkflow.pipelines.genome_mapper import getAvailableGenomes -from htsworkflow.pipelines.genome_mapper import constructMapperDict +from htsworkflow.pipelines.genome_mapper import \ + getAvailableGenomes, \ + constructMapperDict +from htsworkflow.pipelines.runfolder import LANE_LIST +# JSON dictionaries use strings +LANE_LIST_JSON = [ str(l) for l in LANE_LIST ] __docformat__ = "restructredtext en" @@ -28,17 +34,15 @@ GERALD_CONFIG_SECTION = 'gerald_config' #Disable or enable commandline arg parsing; disabled by default. DISABLE_CMDLINE = True -LANE_LIST = ['1','2','3','4','5','6','7','8'] - class FlowCellNotFound(Exception): pass class WebError404(Exception): pass def retrieve_flowcell_info(base_host_url, flowcell): """ - Return a dictionary describing a + Return a dictionary describing a """ url = api.flowcell_url(base_host_url, flowcell) - + try: apipayload = urllib.urlencode(apidata) web = urllib2.urlopen(url, apipayload) @@ -47,19 +51,19 @@ def retrieve_flowcell_info(base_host_url, flowcell): logging.error(errmsg) logging.error('opened %s' % (url,)) raise IOError(errmsg) - + contents = web.read() headers = web.info() if web.code == 403: msg = "403 - Forbbidden, probably need api key" raise FlowCellNotFound(msg) - + if web.code == 404: msg = "404 - Not Found: Flowcell (%s); base_host_url (%s);\n full url(%s)\n " \ "Did you get right port #?" % (flowcell, base_host_url, url) raise FlowCellNotFound(msg) - + if len(contents) == 0: msg = "No information for flowcell (%s) returned; full url(%s)" % (flowcell, url) raise FlowCellNotFound(msg) @@ -75,7 +79,7 @@ def is_sequencing(lane_info): return True else: return False - + def group_lane_parameters(flowcell_info): """ goup lanes that can share GERALD configuration blocks. @@ -83,11 +87,12 @@ def group_lane_parameters(flowcell_info): (The same species, read length, and eland vs sequencing) """ lane_groups = {} - for lane_number, lane_info in flowcell_info['lane_set'].items(): - index = (lane_info['read_length'], - lane_info['library_species'], - is_sequencing(lane_info)) - lane_groups.setdefault(index, []).append(lane_number) + for lane_number, lane_contents in flowcell_info['lane_set'].items(): + for lane_info in lane_contents: + index = (lane_info['read_length'], + lane_info['library_species'], + is_sequencing(lane_info)) + lane_groups.setdefault(index, []).append(lane_number) return lane_groups def format_gerald_header(flowcell_info): @@ -103,10 +108,12 @@ def format_gerald_header(flowcell_info): config += ['Flowcell Notes:'] config.extend(flowcell_info['notes'].split('\r\n')) config += [''] - for lane_number in LANE_LIST: - lane_info = flowcell_info['lane_set'][lane_number] - config += ['Lane%s: %s | %s' % (lane_number, lane_info['library_id'], - lane_info['library_name'])] + for lane_number in LANE_LIST_JSON: + lane_contents = flowcell_info['lane_set'][lane_number] + for lane_info in lane_contents: + config += ['Lane%s: %s | %s' % (lane_number, + lane_info['library_id'], + lane_info['library_name'])] config += [''] return "\n# ".join(config) @@ -134,14 +141,14 @@ def format_gerald_config(options, flowcell_info, genome_map): read_length, species, is_sequencing = lane_index lane_numbers.sort() lane_prefix = u"".join(lane_numbers) - + species_path = genome_map.get(species, None) logging.debug("Looked for genome '%s' got location '%s'" % (species, species_path)) if not is_sequencing and species_path is None: no_genome_msg = "Forcing lanes %s to sequencing as there is no genome for %s" logging.warning(no_genome_msg % (lane_numbers, species)) is_sequencing = True - + if is_sequencing: config += ['%s:ANALYSIS sequence%s' % (lane_prefix, sequence_suffix)] else: @@ -150,16 +157,16 @@ def format_gerald_config(options, flowcell_info, genome_map): #config += ['%s:READ_LENGTH %s' % ( lane_prefix, read_length ) ] config += ['%s:USE_BASES Y%s' % ( lane_prefix, read_length ) ] - # add in option for running script after + # add in option for running script after if not (options.post_run is None or options.runfolder is None): runfolder = os.path.abspath(options.runfolder) post_run = options.post_run % {'runfolder': runfolder} config += ['POST_RUN_COMMAND %s' % (post_run,) ] - + config += [''] # force trailing newline - + return "\n".join(config) - + class DummyOptions: """ Used when command line parsing is disabled; default @@ -171,14 +178,14 @@ class DummyOptions: self.genome_dir = None class PreformattedDescriptionFormatter(IndentedHelpFormatter): - + #def format_description(self, description): - # + # # if description: # return description + "\n" # else: # return "" - + def format_epilog(self, epilog): """ It was removing my preformated epilog, so this should override @@ -197,33 +204,33 @@ def constructOptionParser(): parser = OptionParser(formatter=PreformattedDescriptionFormatter()) parser.set_description('Retrieves eland config file from hts_frontend web frontend.') - + parser.epilog = """ Config File: * %s (System wide) * %s (User specific; overrides system) * command line overrides all config file options - + Example Config File: - + [%s] config_host: http://somewhere.domain:port genome_dir: /path to search for genomes post_run: runfolder -o %%(runfolder)s - + """ % (CONFIG_SYSTEM, CONFIG_USER, GERALD_CONFIG_SECTION) - + #Special formatter for allowing preformatted description. ##parser.format_epilog(PreformattedDescriptionFormatter()) parser.add_option("-u", "--url", action="store", type="string", dest="url") - + parser.add_option("-o", "--output-file", action="store", type="string", dest="output_filepath", help="config file destination. If runfolder is specified defaults " "to /config-auto.txt" ) - + parser.add_option("-f", "--flowcell", action="store", type="string", dest="flowcell") @@ -234,10 +241,17 @@ Config File: action="store", type="string", help="specify runfolder for post_run command ") + parser.add_option("--sample-sheet", default=None, + help="path to save demultiplexing sample sheet") + + parser.add_option("--operator", default='', help="Name of sequencer operator") + parser.add_option("--recipe", default="Unknown", + help="specify recipe name") + parser.add_option('-v', '--verbose', action='store_true', default=False, help='increase logging verbosity') return parser - + def constructConfigParser(): """ returns a pre-setup config parser @@ -246,7 +260,7 @@ def constructConfigParser(): parser.read([CONFIG_SYSTEM, CONFIG_USER]) if not parser.has_section(GERALD_CONFIG_SECTION): parser.add_section(GERALD_CONFIG_SECTION) - + return parser @@ -264,13 +278,13 @@ def getCombinedOptions(argv=None): options = DummyOptions() else: options, args = cl_parser.parse_args(argv) - + if options.url is None: if conf_parser.has_option(GERALD_CONFIG_SECTION, 'config_host'): options.url = conf_parser.get(GERALD_CONFIG_SECTION, 'config_host') - + options.url = normalize_url(options.url) - + if options.genome_dir is None: if conf_parser.has_option(GERALD_CONFIG_SECTION, 'genome_dir'): options.genome_dir = conf_parser.get(GERALD_CONFIG_SECTION, 'genome_dir') @@ -283,7 +297,7 @@ def getCombinedOptions(argv=None): if options.output_filepath is None: if options.runfolder is not None: options.output_filepath = os.path.join(options.runfolder, 'config-auto.txt') - + return options @@ -298,7 +312,7 @@ def saveConfigFile(options): logging.info(u' FC: %s' % (options.flowcell,)) #logging.info(': %s' % (options.genome_dir,)) logging.info(u'post_run: %s' % ( unicode(options.post_run),)) - + flowcell_info = retrieve_flowcell_info(options.url, options.flowcell) logging.debug('genome_dir: %s' % ( options.genome_dir, )) @@ -306,16 +320,107 @@ def saveConfigFile(options): genome_map = constructMapperDict(available_genomes) logging.debug('available genomes: %s' % ( unicode( genome_map.keys() ),)) - config = format_gerald_config(options, flowcell_info, genome_map) - - if options.output_filepath is not None: - outstream = open(options.output_filepath, 'w') - logging.info('Writing config file to %s' % (options.output_filepath,)) + #config = format_gerald_config(options, flowcell_info, genome_map) + # + #if options.output_filepath is not None: + # outstream = open(options.output_filepath, 'w') + # logging.info('Writing config file to %s' % (options.output_filepath,)) + #else: + # outstream = sys.stdout + # + #outstream.write(config) + + if options.sample_sheet is None: + pass + elif options.sample_sheet == '-': + save_sample_sheet(sys.stdout, options, flowcell_info) else: - outstream = sys.stdout - - outstream.write(config) - + stream = open(options.sample_sheet,'w') + save_sample_sheet(stream, options, flowcell_info) + + +def save_sample_sheet(outstream, options, flowcell_info): + sample_sheet_fields = ['FCID', 'Lane', 'SampleID', 'SampleRef', 'Index', + 'Description', 'Control', 'Recipe', 'Operator', + 'SampleProject'] + illumina_to_htsw_map = {'FCID': 'flowcell', + 'Lane': 'lane_number', + 'SampleID': 'library_id', + 'SampleRef': format_sampleref, + 'Description': 'library_name', + 'Control': format_control_lane, + 'Recipe': format_recipe_name, + 'Operator': format_operator_name} + out = csv.DictWriter(outstream, sample_sheet_fields) + out.writeheader() + for lane_number in LANE_LIST: + lane_contents = flowcell_info['lane_set'][str(lane_number)] + + pooled_lane_contents = [] + for library in lane_contents: + # build common attributes + renamed = {} + for illumina_name in sample_sheet_fields: + htsw_field = illumina_to_htsw_map.get(illumina_name, None) + if htsw_field is None: + continue + if callable(htsw_field): + renamed[illumina_name] = htsw_field(options, + flowcell_info, + library) + else: + renamed[illumina_name] = library[htsw_field] + + pooled_lane_contents.extend(format_pooled_libraries(renamed, library)) + + if len(pooled_lane_contents) > 1: + for row in pooled_lane_contents: + out.writerow(row) + + +def format_sampleref(options, flowcell_info, sample): + return sample['library_species'].replace(' ', '_') + + +def format_control_lane(options, flowcell_info, sample): + if sample['lane_number'] == flowcell_info['control_lane']: + return 'Y' + else: + return 'N' + + +def format_recipe_name(options, flowcell_info, sample): + return options.recipe + + +def format_operator_name(options, flowcell_info, sample): + return options.operator + + +def format_pooled_libraries(shared, library): + sequences = library.get('index_sequence', None) + if sequences is None: + return [] + elif type(sequences) in types.StringTypes: + shared['Index'] = sequences + shared['SampleProject'] = library['library_id'] + return [shared] + else: + pooled = [] + multiplex_ids = sequences.keys() + multiplex_ids.sort(key=int) + for multiplex_id in multiplex_ids: + sample = {} + sample.update(shared) + sample['Index'] = sequences[multiplex_id] + sample['SampleProject'] = format_project_name(library, + multiplex_id) + pooled.append(sample) + return pooled + + +def format_project_name(library, multiplex_id): + library_id = library['library_id'] + return "%s_index%s" % (library_id, multiplex_id) - diff --git a/htsworkflow/pipelines/test/test_retrive_config.py b/htsworkflow/pipelines/test/test_retrive_config.py index f655779..cb56501 100644 --- a/htsworkflow/pipelines/test/test_retrive_config.py +++ b/htsworkflow/pipelines/test/test_retrive_config.py @@ -1,15 +1,20 @@ +import csv import os import re +from StringIO import StringIO try: import json except ImportError, e: import simplejson as json - + from django.test import TestCase from htsworkflow.frontend.auth import apidata -from htsworkflow.pipelines.retrieve_config import format_gerald_config, getCombinedOptions +from htsworkflow.pipelines.retrieve_config import \ + format_gerald_config, \ + getCombinedOptions, \ + save_sample_sheet class RetrieveTestCases(TestCase): fixtures = ['test_flowcells.json'] @@ -20,13 +25,11 @@ class RetrieveTestCases(TestCase): def test_format_gerald(self): 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','FC12150','-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) config_lines = config.split('\n') lane3 = [ line for line in config_lines if re.search('Lane3', line) ] @@ -39,7 +42,31 @@ class RetrieveTestCases(TestCase): sequencing = [ line for line in config_lines if re.search('sequence_pair', line) ] self.failUnlessEqual(len(sequencing), 2) - - - + def test_format_sample_sheet(self): + fcid = '42JU1AAXX' + url = '/experiments/config/%s/json' % (fcid,) + flowcell_request = self.client.get(url, apidata) + self.failUnlessEqual(flowcell_request.status_code, 200) + flowcell_info = json.loads(flowcell_request.content) + + options = getCombinedOptions(['-f',fcid,'-g',os.getcwd(),]) + + output = StringIO() + save_sample_sheet(output, options, flowcell_info) + output.seek(0) + sheet = list(csv.DictReader(output)) + expected = [{'SampleProject': '12044_index1', 'Index': 'ATCACG'}, + {'SampleProject': '12044_index2', 'Index': 'CGATGT'}, + {'SampleProject': '12044_index3', 'Index': 'TTAGGC'}, + {'SampleProject': '11045_index1', 'Index': 'ATCACG'}, + ] + for i in range(4): + self.assertEqual(sheet[i]['SampleProject'], + expected[i]['SampleProject']) + self.assertEqual(sheet[i]['Index'], + expected[i]['Index']) + self.assertEqual(sheet[i]['FCID'], fcid) + self.assertEqual(sheet[i]['Lane'], '3') + + diff --git a/scripts/htsw-get-config b/scripts/htsw-get-config index e4fdff1..1417baf 100755 --- a/scripts/htsw-get-config +++ b/scripts/htsw-get-config @@ -1,4 +1,8 @@ #!/usr/bin/env python +import os +if not 'DJANGO_SETTINGS_MODULE' in os.environ: + os.environ['DJANGO_SETTINGS_MODULE'] = 'htsworkflow.settings' + import logging import sys from htsworkflow.pipelines.retrieve_config import * @@ -7,37 +11,43 @@ from htsworkflow.pipelines import retrieve_config #Turn on built-in command-line parsing. retrieve_config.DISABLE_CMDLINE = False + def main(argv=None): - if argv is None: - argv = sys.argv - - #Display help if no args are presented - options = getCombinedOptions(argv) - - if options.verbose: - logging.basicConfig(level=logging.DEBUG) - else: - logging.basicConfig(level=logging.INFO) - - msg_list = ['ERROR MESSAGES:'] - if options.flowcell is None: - msg_list.append(" Flow cell argument required. -f or --flowcell=") - - if options.url is None: - msg_list.append(" URL argument required (-u or --url=), or entry\n" \ - " in /etc/ga_frontend/ga_frontend.conf or ~/.ga_frontend.conf") - if options.genome_dir is None: - msg_list.append(" genome_dir argument required (-g or \n" \ - " --genome_dir=, or entry in \n" \ - " /etc/ga_frontend/ga_frontend.conf or ~/.ga_frontend.conf") - - if len(msg_list) > 1: - print '\n'.join(msg_list) - return 1 - - saveConfigFile(options) - - return 0 - + if argv is None: + argv = sys.argv + + #Display help if no args are presented + options = getCombinedOptions(argv) + + if options.verbose: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + + msg_list = ['ERROR MESSAGES:'] + if options.flowcell is None: + msg_list.append( + " Flow cell argument required. -f or "\ + "--flowcell=") + + if options.url is None: + msg_list.append( + " URL argument required (-u or --url=), or entry\n" \ + " in /etc/ga_frontend/ga_frontend.conf or ~/.ga_frontend.conf") + + if options.genome_dir is None: + msg_list.append( + " genome_dir argument required (-g or \n" \ + " --genome_dir=, or entry in \n" \ + " /etc/ga_frontend/ga_frontend.conf or ~/.ga_frontend.conf") + + if len(msg_list) > 1: + print '\n'.join(msg_list) + return 1 + + saveConfigFile(options) + + return 0 + if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv[1:])) -- 2.30.2