Skip to content

Commit

Permalink
Integrate JBrowse into Varify
Browse files Browse the repository at this point in the history
Replace Alamut with JBrowse as Varify's genome browser
Fixes chop-dbhi#338

Signed-off-by: Ryan O'Hara <[email protected]>
  • Loading branch information
ryanjohara committed Aug 11, 2014
1 parent 638302d commit 2add168
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 38 deletions.
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

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 to build filenames in the JBrowse URL

```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
Expand Down
3 changes: 1 addition & 2 deletions varify/conf/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@
TEMPLATE_CONTEXT_PROCESSORS += (
'django.core.context_processors.request',
'varify.context_processors.static',
'varify.context_processors.alamut',
)


Expand All @@ -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.
Expand Down
6 changes: 0 additions & 6 deletions varify/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
21 changes: 0 additions & 21 deletions varify/samples/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,6 @@
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 '<a target=_blank href="{href}">{label}</a>'.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):
def to_html(self, values, **kwargs):
return values['sample__label']
Expand Down Expand Up @@ -105,7 +85,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')
82 changes: 77 additions & 5 deletions varify/samples/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from varify.variants.resources import VariantResource
from varify import api
from vdw.assessments.models import Assessment
from vdw.samples.models import Sample, Result, ResultScore
from vdw.samples.models import Sample, SampleManifest, Result, ResultScore
from restlib2.http import codes

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -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'] = {
Expand Down Expand Up @@ -254,10 +282,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()
Expand Down Expand Up @@ -342,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())
Expand All @@ -350,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(
'',
Expand All @@ -360,6 +431,7 @@ def get(self, request, year, month, day, name):
url(r'^(?P<pk>\d+)/variants/$', sample_results_resource, name='variants'),
url(r'^variants/(?P<pk>\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<sample_id>.+)/phenotypes/$', phenotype_resource,
name='phenotype'),
url(r'^pedigrees/(?P<year>\d+)/(?P<month>\d+)/(?P<day>\d+)/(?P<name>.+)$',
Expand Down
46 changes: 42 additions & 4 deletions varify/static/js/src/ui/modals/result.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -200,10 +235,13 @@ define([
}

content.push('</ul>');
content.push('<a href="http://localhost:10000/show?request=chr' +
variantAttrs.chr + ':g.' + variantAttrs.pos +
variantAttrs.ref + '>' + variantAttrs.alt +
'" target=_blank class="btn btn-primary btn-small alamut-button">Query Alamut</a>');

url = this.jbrowseUrl(variantAttrs, resultAttrs);

html = '<a href="' + url + '" target=_blank class="btn btn-primary';
html += ' btn-small">View JBrowse</a>';

content.push(html);

return content.join('');
},
Expand Down

0 comments on commit 2add168

Please sign in to comment.