diff --git a/README.md b/README.md index 8910b439..05a4a6d9 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,65 @@ or run a `uwsgi` process: uwsgi --ini server/uwsgi/local.ini --protocol http --socket 127.0.0.1:8000 --check-static _site ``` +## Optional JBrowse Setup + +Install JBrowse in $project_root/jbrowse + +```bash +curl -O http://jbrowse.org/releases/JBrowse-x.x.x.zip (ie 1.11.3) +unzip JBrowse-x.x.x.zip -d jbrowse +cd jbrowse +./setup.sh +``` + +Download data files (ie BAM, GFF, VCF) to /your/directory/of/files/ and add the following to nginx.conf + +```bash +location /files/ { + alias /your/directory/of/files/; + internal; +} +``` + +Load reference sequences and load features tracks (in that order) using JBrowse loading scripts + +```bash +sudo ./bin/prepare-refseqs.pl --fasta ../files/chr1.fa +... +sudo ./bin/prepare-refseqs.pl --fasta ../files/chrY.fa +sudo ./bin/flatfile-to-json.pl --gff ../files/ALL_COSMIC_POINT_MUTS_v65.gff3 --trackLabel Cosmic --trackType CanvasFeatures +... + +Note: Segment large Cosmic GFF files with synchronization marks, a line containing '###', to prevent (really) slow loading. Once a loading script executes, a data directory will exist in $project_root/jbrowse. +``` + +Run bgzip and tabix (via samtools/htslib) on VCF files + +```bash +git clone git://github.com/samtools/samtools.git +bgzip my.vcf +tabix -p vcf my.vcf.gz +``` + +Make one edit to JBrowse source (BAM.js), regex change related to custom URL + +```bash +/\/([^/\#\?]+)($|[\#\?])/ + +to + +/.*filename=([^&]+)/ +``` + +Lastly, save and load filenames correctly! Currently, the sample section key values of the manifest are concatenated (and '_' delimited) to build filename roots in the JBrowse URL. BAM, BAI, VCF, and TBI files are hard-codedly suffixed '.sorted.mdup.bam','.sorted.mdup.bai','.var_raw.vcf.gz', and '.var_raw.vcf.gz.tbi', respectively. + +```bash +[sample] +batch = Pseq_batch9 +sample = P-Pseq_0019-P-A +version = 1 +``` + ## Makefile Commands - `build` - builds and initializes all submodules, compiles SCSS and optimizes JavaScript diff --git a/varify/conf/global_settings.py b/varify/conf/global_settings.py index 47f73349..04fd22c2 100644 --- a/varify/conf/global_settings.py +++ b/varify/conf/global_settings.py @@ -167,7 +167,6 @@ TEMPLATE_CONTEXT_PROCESSORS += ( 'django.core.context_processors.request', 'varify.context_processors.static', - 'varify.context_processors.alamut', ) @@ -187,7 +186,7 @@ LOGOUT_URL = '/logout/' LOGIN_REDIRECT_URL = '/workspace/' -ALAMUT_URL = 'http://localhost:10000' +JBROWSE_HOST = 'localhost' # For non-publicly accessible applications, the siteauth app can be used to # restrict access site-wide. diff --git a/varify/context_processors.py b/varify/context_processors.py index 5424b8c8..6c787e13 100644 --- a/varify/context_processors.py +++ b/varify/context_processors.py @@ -14,9 +14,3 @@ def static(request): 'IMAGES_URL': os.path.join(static_url, 'images'), 'JAVASCRIPT_URL': os.path.join(static_url, 'js', prefix), } - - -def alamut(request): - return { - 'ALAMUT_URL': settings.ALAMUT_URL, - } diff --git a/varify/samples/formatters.py b/varify/samples/formatters.py index 933e3bcd..2ade6415 100644 --- a/varify/samples/formatters.py +++ b/varify/samples/formatters.py @@ -5,27 +5,6 @@ from django.db.models import Q from avocado.formatters import registry as formatters from serrano.formatters import HTMLFormatter -from django.conf import settings - - -class AlamutFormatter(HTMLFormatter): - href = settings.ALAMUT_URL + '/show?request={0}' - request = 'chr{chr}:g.{pos}{ref}>{alt}' - - def to_html(self, values, **context): - request = self.request.format(**values) - href = self.href.format(request) - return '{label}'.format( - href=href, label=request) - to_html.process_multiple = True - - def to_excel(self, values, **context): - request = self.request.format(**values) - href = self.href.format(request) - - return '=HYPERLINK("{href}", "{label}")'.format( - href=href, label=request) - to_excel.process_multiple = True class SampleFormatter(HTMLFormatter): @@ -105,7 +84,6 @@ def to_excel(self, value, **context): to_csv = to_excel -formatters.register(AlamutFormatter, 'Alamut Query Link') formatters.register(SampleFormatter, 'Sample') formatters.register(ReadDepthFormatter, 'Read Depth') formatters.register(CohortsFormatter, 'Cohorts') diff --git a/varify/samples/resources.py b/varify/samples/resources.py index fae62a4b..c784bcb2 100644 --- a/varify/samples/resources.py +++ b/varify/samples/resources.py @@ -22,10 +22,12 @@ from varify import api from vdw.assessments.models import Assessment from vdw.genome.models import Chromosome -from vdw.samples.models import Sample, Result, ResultScore, ResultSet +from vdw.samples.models import (Sample, SampleManifest, Result, ResultScore, + ResultSet) from vdw.variants.models import Variant from .forms import ResultSetForm + log = logging.getLogger(__name__) OPENPYXL_MAJOR_VERSION = int(openpyxl.__version__[0]) @@ -274,6 +276,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'] = { @@ -310,10 +340,15 @@ class ResultsResource(ThrottledResource): template = api.templates.SampleResultVariant def post(self, request): - if (not request.data.get('ids') or - not isinstance(request.data['ids'], list)): - return HttpResponse(status=codes.unprocessable_entity, - content='Array of "ids" is required') + ids_not_found = 'ids' not in request.data + not_a_list = not isinstance(request.data['ids'], list) + + if ids_not_found or not_a_list: + return self.render( + request, + {'message': 'An array of "ids" is required'}, + status=codes.unprocessable_entity + ) data = [] resource = SampleResultResource() @@ -686,6 +721,43 @@ def get(self, request, pk): return data +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()) @@ -699,6 +771,7 @@ def get(self, request, pk): phenotype_resource = never_cache(PhenotypeResource()) pedigree_resource = never_cache(PedigreeResource()) +jbrowse_resource = never_cache(JbrowseResource()) urlpatterns = patterns( '', @@ -731,6 +804,10 @@ def get(self, request, pk): sample_result_set_resource, name='variant-set'), + url(r'^jbrowse/$', + jbrowse_resource, + name='jbrowse_resource'), + url(r'^(?P.+)/phenotypes/$', phenotype_resource, name='phenotype'), diff --git a/varify/static/templates/variant/summary.html b/varify/static/templates/variant/summary.html index 73aaef88..472d0efc 100644 --- a/varify/static/templates/variant/summary.html +++ b/varify/static/templates/variant/summary.html @@ -48,4 +48,4 @@

<%= data.sample.label %> in <%= data.sample.project %>

-Query Alamut +Query JBrowse