diff --git a/varify/samples/resources.py b/varify/samples/resources.py index 94c1f55c..16b3e007 100644 --- a/varify/samples/resources.py +++ b/varify/samples/resources.py @@ -16,7 +16,7 @@ from varify.variants.resources import VariantResource from varify import api from varify.assessments.models import Assessment -from .models import Sample, Result, ResultScore +from .models import Sample, SampleManifest, Result, ResultScore from restlib2.http import codes log = logging.getLogger(__name__) @@ -218,6 +218,34 @@ def _cache_data(self, request, pk, key): data['variant'] = VariantResource.get(request, data['variant_id']) data.pop('variant_id') + # Integrate the SampleManifest data + path = SampleManifest.objects.get(sample__id=data['sample']['id']).path + manifest = open(path, 'r') + found = False + filestem = '' + + # Add JBrowse data requirements, including sample_manifest which is + # constructed by appending batch, sample, and version + for line in manifest: + if '[sample]' in line: + found = True + + if found: + if 'batch' in line or 'sample' in line or 'version' in line: + chunks = line.split('=') + + if len(chunks) > 1: + if 'batch' in line: + filestem += chunks[1].strip() + else: + filestem += '_' + chunks[1].strip() + + if 'version' in line: + break + + data['sample_manifest'] = filestem + data['jbrowse_host'] = settings.JBROWSE_HOST + try: score = ResultScore.objects.get(result=result) data['score'] = { @@ -259,14 +287,16 @@ def post(self, request): if ids_not_found or not_a_list: return self.render( + request, {'message': 'An array of "ids" is required'}, - status=codes.unprocessable_entity) + status=codes.unprocessable_entity + ) data = [] resource = SampleResultResource() for id in request.data['ids']: data.append(resource.get(request, id)) - + return data @@ -345,6 +375,43 @@ def get(self, request, year, month, day, name): return response + +class JbrowseResource(ThrottledResource): + def get(self, request): + data = request.GET + + if 'id' not in data or 'filename' not in data: + return self.render( + request, + {'message': 'An "id" and "filename" are required'}, + status=codes.unprocessable_entity + ) + + try: + if Sample.objects.get(id=data['id']): + response = HttpResponse() + f = data['filename'] + + if '.bai' in f or '.tbi' in f: + response['Content-Type'] = 'application/octet-stream' + else: + response['Content-Type'] = 'text/plain' + + # Control access to files hosted by nginx + response['X-Accel-Redirect'] = '/files/' + f + # Control access to files hosted by Apache + response['X-Sendfile'] = '/files/' + f + + response['Content-Disposition'] = 'attachment;filename=' + f + except Exception: + return self.render( + request, + {'message': 'No sample found for "id"'}, + status=codes.unprocessable_entity + ) + + return response + sample_resource = never_cache(SampleResource()) samples_resource = never_cache(SamplesResource()) named_sample_resource = never_cache(NamedSampleResource()) @@ -353,6 +420,7 @@ def get(self, request, year, month, day, name): results_resource = never_cache(ResultsResource()) phenotype_resource = never_cache(PhenotypeResource()) pedigree_resource = never_cache(PedigreeResource()) +jbrowse_resource = never_cache(JbrowseResource()) urlpatterns = patterns( '', @@ -363,6 +431,7 @@ def get(self, request, year, month, day, name): url(r'^(?P\d+)/variants/$', sample_results_resource, name='variants'), url(r'^variants/(?P\d+)/$', sample_result_resource, name='variant'), url(r'^variants/$', results_resource, name='results_resource'), + url(r'^jbrowse/$', jbrowse_resource, name='jbrowse_resource'), url(r'^(?P.+)/phenotypes/$', phenotype_resource, name='phenotype'), url(r'^pedigrees/(?P\d+)/(?P\d+)/(?P\d+)/(?P.+)$', diff --git a/varify/static/js/src/ui/modals/result.js b/varify/static/js/src/ui/modals/result.js index f49a7b4c..89141bd2 100644 --- a/varify/static/js/src/ui/modals/result.js +++ b/varify/static/js/src/ui/modals/result.js @@ -134,6 +134,41 @@ define([ return content.join(''); }, + jbrowseUrl: function(variantAttrs, resultAttrs) { + var host = resultAttrs.jbrowse_host; + var url = 'http://' + host + '/jbrowse/?loc=chr' + variantAttrs.chr + ':'; + + url += parseInt(variantAttrs.pos-25) + '..' + parseInt(variantAttrs.pos+25); + url += '&tracks=DNA,Cosmic,VCF,BAM&highlight=&addTracks='; + + var addTracks ='[{"label":"BAM","store":"testStore","type":'; + + addTracks += '"JBrowse/View/Track/Alignments2"},{"label":"VCF","store":'; + addTracks += '"testStoreTwo","type":"JBrowse/View/Track/CanvasVariants"}]'; + + url += encodeURIComponent(addTracks) + '&addStores='; + + var sm = resultAttrs.sample_manifest; + var sid = resultAttrs.sample.id; + var addStores = '{"testStore":{"baiUrlTemplate"'; + + addStores += ':"http://' + host + '/api/samples/jbrowse/?id='; + addStores += sid + "&filename=" + sm + '.sorted.mdup.bai","type"'; + addStores += ':"JBrowse/Store/SeqFeature/BAM","urlTemplate":'; + addStores += '"http://' + host + '/api/samples/jbrowse/?id='; + addStores += sid + "&filename=" + sm; + addStores += '.sorted.mdup.bam"},"testStoreTwo":{"tbiUrlTemplate"'; + addStores += ':"http://' + host + '/api/samples/jbrowse/?id='; + addStores += sid + "&filename=" + sm + '.var_raw.vcf.gz.tbi","type"'; + addStores += ':"JBrowse/Store/SeqFeature/VCFTabix","urlTemplate":'; + addStores += '"http://' + host + '/api/samples/jbrowse/?id='; + addStores += sid + "&filename=" + sm + '.var_raw.vcf.gz"}}'; + + url += encodeURIComponent(addStores); + + return url; + }, + renderSummary: function(resultAttrs, variantAttrs) { var bases, content, hgmdLinks, key, labelClass; @@ -200,10 +235,13 @@ define([ } content.push(''); - content.push('Query Alamut'); + + url = this.jbrowseUrl(variantAttrs, resultAttrs); + + html = 'View JBrowse'; + + content.push(html); return content.join(''); },