diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd9819f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +node_modules/ +docker/data/ \ No newline at end of file diff --git a/README.md b/README.md index 269748f..4111301 100644 --- a/README.md +++ b/README.md @@ -92,11 +92,10 @@ Example Track: ``` [tracks.GDC_Genes] storeClass=gdc-viewer/Store/SeqFeature/Genes -type=JBrowse/View/Track/CanvasVariants +type=JBrowse/View/Track/GeneTrack key=GDC Genes metadata.datatype=Gene unsafePopup=true -fmtDetailValue_projects=function(value) { return "
Loading...
";} ``` You can apply filters to the track too, in the same format as GDC. The below example only shows Genes whose biotype is not 'protein_coding'. @@ -115,11 +114,10 @@ Example Track: ``` [tracks.GDC_SSM] storeClass=gdc-viewer/Store/SeqFeature/SimpleSomaticMutations -type=gdc-viewer/View/Track/CanvasVariants +type=gdc-viewer/View/Track/SSMVariants key=GDC SSM metadata.datatype=SSM unsafePopup=true -fmtDetailValue_projects=function(value) { return "
Loading...
";} ``` You can apply filters to the track too, in the same format as GDC. The below example only shows SSMs whose reference allele is 'G'. diff --git a/data/tracks.conf b/data/tracks.conf index fd195ef..fd1c2f3 100644 --- a/data/tracks.conf +++ b/data/tracks.conf @@ -1,18 +1,16 @@ [tracks.GDC_SSM] storeClass=gdc-viewer/Store/SeqFeature/SimpleSomaticMutations -type=gdc-viewer/View/Track/CanvasVariants +type=gdc-viewer/View/Track/SSMTrack key=GDC SSM metadata.datatype=SSM unsafePopup=true -fmtDetailValue_projects=function(value) { return "
Loading...
";} [tracks.GDC_Genes] storeClass=gdc-viewer/Store/SeqFeature/Genes -type=gdc-viewer/View/Track/CanvasVariants +type=gdc-viewer/View/Track/GeneTrack key=GDC Genes metadata.datatype=Gene unsafePopup=true -fmtDetailValue_projects=function(value) { return "
Loading...
";} [tracks.GDC_CNV] storeClass=gdc-viewer/Store/SeqFeature/CNVs diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..1da64f6 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,35 @@ +FROM node:latest + +LABEL maintainer="andrew.duncan@oicr.on.ca" + +ENV JBROWSE_VERSION 1.16.6 + +# Install dependencies +RUN apt-get -qq update --fix-missing +RUN apt-get --no-install-recommends -y install git build-essential zlib1g-dev libxml2-dev libexpat-dev postgresql-client libpq-dev ca-certificates curl + +# Download JBrowse +RUN mkdir -p /jbrowse/ && \ + git clone --recursive https://github.com/gmod/jbrowse /jbrowse/ && \ + cd /jbrowse/ && \ + git checkout ${JBROWSE_VERSION}-release + +WORKDIR /jbrowse/ + +# Download GDC Viewer +RUN git clone https://github.com/agduncan94/gdc-viewer /gdc-viewer/ && \ + cd /gdc-viewer/ && \ + git checkout 0.1.1 && \ + cp -r gdc-viewer /jbrowse/plugins/gdc-viewer && \ + cat ./data/jbrowse.conf > /jbrowse/jbrowse.conf + +# Setup JBrowse +RUN ./setup.sh && \ + ./bin/cpanm --force JSON Hash::Merge PerlIO::gzip Devel::Size \ + Heap::Simple Heap::Simple::XS List::MoreUtils Exception::Class Test::Warn Bio::Perl \ + Bio::DB::SeqFeature::Store File::Next Bio::DB::Das::Chado && \ + rm -rf /root/.cpan/ + + +# Expose port 3000 +EXPOSE 3000 \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..c660332 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,56 @@ +# Running JBrowse with GDC Plugin in Docker +This will get JBrowse with the GDC Viewer Plugin running with Express on port 3000. + +Based on [enuggetry/docker-jbrowse](https://github.com/enuggetry/docker-jbrowse) + +## Build and Run from Dockerfile +### Setup data +*Important*: Place your track data in `./data`. This maps to `/jbrowse/data` in the container, which is where JBrowse stores reference data and track information. + +### Build the docker image +`docker build . -t jbrowse-with-gdc` + +### Run the docker image +`docker run -p 3000:3000 -v {pwd}/data:/jbrowse/data jbrowse-with-gdc utils/jb_run.js -p 3000` + +Note: You can run in the background using the detach mode (-d) + +`docker run -d -p 3000:3000 -v {pwd}/data:/jbrowse/data jbrowse-with-gdc utils/jb_run.js -p 3000` + +## Build and Run from Docker Compose +You can also use Docker Compose to build the image. Ensure you are working in the same directory as the `docker-compose.yml`. + +### Build docker-compose +`docker-compose build` + +### Run the docker-compose +`docker-compose up` + +Note: You can run in the background using the detach mode (-d) + +`docker-compose up -d` + +## Load refseq and tracks +If you already have your `tracks.conf` and `seq/`, etc., you can simply put these files into your `./data` directory. + +You will have to put the RefSeq data into the `./data` directory. Download the GRCh38 `.fa` and `.fa.fai` files online (ex. http://bioinfo.hpc.cam.ac.uk/downloads/datasets/fasta/grch38/). Then put the following in `./data/tracks.conf` (note files may be named something else). + +``` +refSeqs=GRCh38.genome.fa.fai + +[tracks.refseqs] +urlTemplate=GRCh38.genome.fa +``` + +Now go to `localhost:3000` and you should see JBrowse with your refdata and tracks! + +### Enter the Docker Container +You can enter the container by doing the following: + +``` +# Get container ID +docker ps + +# Enter container +docker exec -it /bin/bash +``` \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..66ad339 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' +services: + jbrowse: + build: . + command: utils/jb_run.js -p 3000 + ports: + - "3000:3000" + volumes: + - ./data:/jbrowse/data + \ No newline at end of file diff --git a/gdc-viewer/js/Model/GeneFeature.js b/gdc-viewer/js/Model/GeneFeature.js index d526daf..aba995a 100644 --- a/gdc-viewer/js/Model/GeneFeature.js +++ b/gdc-viewer/js/Model/GeneFeature.js @@ -85,9 +85,9 @@ get: function(name) { }).then(function(response) { return(response.json()); }).then(function(response) { - document.getElementById('projects-gdc-' + geneId).innerHTML = thisB.createProjectTable(response); + document.getElementsByClassName('value projects')[0].innerHTML = thisB.createProjectTable(response); }).catch(function(err) { - document.getElementById('projects-gdc-' + geneId).innerHTML = 'Error creating projects table'; + document.getElementsByClassName('value projects')[0].innerHTML = 'Error creating projects table'; }); return geneId; } else { diff --git a/gdc-viewer/js/Model/SSMFeature.js b/gdc-viewer/js/Model/SSMFeature.js index 3a175cd..26539e3 100644 --- a/gdc-viewer/js/Model/SSMFeature.js +++ b/gdc-viewer/js/Model/SSMFeature.js @@ -79,9 +79,9 @@ get: function(name) { }).then(function(response) { return(response.json()); }).then(function(response) { - document.getElementById('projects-gdc-' + mutationId).innerHTML = thisB.createProjectTable(response); + document.getElementsByClassName('value projects')[0].innerHTML = thisB.createProjectTable(response); }).catch(function(err) { - document.getElementById('projects-gdc-' + mutationId).innerHTML = 'Error creating projects table'; + document.getElementsByClassName('value projects')[0].innerHTML = 'Error creating projects table'; }); return mutationId; } else { diff --git a/gdc-viewer/js/Store/SeqFeature/CNVs.js b/gdc-viewer/js/Store/SeqFeature/CNVs.js index 93f3538..6b3972a 100644 --- a/gdc-viewer/js/Store/SeqFeature/CNVs.js +++ b/gdc-viewer/js/Store/SeqFeature/CNVs.js @@ -78,7 +78,7 @@ function( var bodyVal = JSON.stringify(thisB.createQuery(ref, start, end)); // Fetch CNVs and create features - fetch(thisB.graphQLUrl, { + fetch(thisB.graphQLUrl + '/CNVsTable', { method: 'post', headers: { 'X-Requested-With': null }, body: bodyVal diff --git a/gdc-viewer/js/View/Export/TrackConfig.js b/gdc-viewer/js/View/Export/TrackConfig.js index 1d5f453..a83e483 100644 --- a/gdc-viewer/js/View/Export/TrackConfig.js +++ b/gdc-viewer/js/View/Export/TrackConfig.js @@ -16,22 +16,26 @@ return declare( ExportBase, }, _printHeader: function() { - var storeArray = (this.store.config.storeClass).split('/') + var storeArray = (this.store.config.type).split('/') var trackArray = [ - '[tracks.' + this.store.config.label + ']', - 'storeClass=' + this.store.config.storeClass, + '[tracks.' + this.track.labelHTML + ']', + 'storeClass=' + this.store.config.type, 'type=' + this.track.config.type, - 'key=' + this.store.config.key, + 'key=' + this.track.key, 'metadata.datatype=' + storeArray[storeArray.length - 1], - 'unsafePopup=true', + 'unsafePopup=true' ] - if (this.store.config.storeClass === 'gdc-viewer/Store/SeqFeature/SimpleSomaticMutations' || this.store.config.storeClass === 'gdc-viewer/Store/SeqFeature/Genes') { - trackArray.push('fmtDetailValue_projects=function(value) { return "
Loading...
";}'); + if (this.store.case) { + trackArray.push('case=' + this.store.case) } - if (this.store.config.storeClass === 'gdc-viewer/Store/SeqFeature/CNVs') { + if (this.store.size) { + trackArray.push('size=' + this.store.size) + } + + if (this.store.config.type === 'gdc-viewer/Store/SeqFeature/CNVs') { trackArray.push("autoscale=local"); trackArray.push("bicolor_pivot=0"); } diff --git a/gdc-viewer/js/View/Export/TrackConfigJson.js b/gdc-viewer/js/View/Export/TrackConfigJson.js index cf2e573..fc47086 100644 --- a/gdc-viewer/js/View/Export/TrackConfigJson.js +++ b/gdc-viewer/js/View/Export/TrackConfigJson.js @@ -16,24 +16,22 @@ return declare( ExportBase, }, _printHeader: function() { - var storeArray = (this.store.config.storeClass).split('/') + var storeArray = (this.store.config.type).split('/') var trackObject = { - 'label': this.store.config.label, - 'storeClass': this.store.config.storeClass, + 'label': this.track.labelHTML, + 'storeClass': this.store.config.type, 'type': this.track.config.type, - 'key': this.store.config.key, + 'key': this.track.key, 'metadata': { 'datatype': storeArray[storeArray.length - 1] }, - 'unsafePopup': true + 'unsafePopup': true, + 'case': this.store.case, + 'size': this.store.size } - if (this.store.config.storeClass === 'gdc-viewer/Store/SeqFeature/SimpleSomaticMutations' || this.store.config.storeClass === 'gdc-viewer/Store/SeqFeature/Genes') { - trackObject['fmtDetailValue_projects'] = 'function(value) { return "
Loading...
";}'; - } - - if (this.store.config.storeClass === 'gdc-viewer/Store/SeqFeature/CNVs') { + if (this.store.config.type === 'gdc-viewer/Store/SeqFeature/CNVs') { trackObject['autoscale'] = 'local'; trackObject['bicolor_pivot'] = 0; } diff --git a/gdc-viewer/js/View/GDCDialog.js b/gdc-viewer/js/View/GDCDialog.js index 96677c3..d98fcbd 100644 --- a/gdc-viewer/js/View/GDCDialog.js +++ b/gdc-viewer/js/View/GDCDialog.js @@ -234,7 +234,7 @@ function ( var geneLoading = thisB.createLoadingIcon(thisB.geneFacetTab.containerNode); // Update the accordions with results from the GDC - fetch(thisB.baseGraphQLUrl, { + fetch(thisB.baseGraphQLUrl + '/facets', { method: 'post', headers: { 'X-Requested-With': null }, body: JSON.stringify(bodyVal) @@ -1055,7 +1055,7 @@ function ( }, /** - * Creates pagination buttons for search results in the given 'holder' using the 'pagination' object from the ICGC response + * Creates pagination buttons for search results in the given 'holder' using the 'pagination' object from the GDC response * @param {object} holder DOM location to place pagination buttons * @param {number} totalPages the total number of pages for the given query results * @param {string} type the type of results to create pagination button for @@ -1367,7 +1367,6 @@ function ( trackConf.autoscale = 'local'; trackConf.bicolor_pivot = 0; } else if (storeClass === 'Genes') { - trackConf.fmtDetailValue_projects = function(value) { return "
Loading...Loading...Loading...Loading...Loading...Loading...Open in New Tab'; + var linkElement = domConstruct.toDom(linkString); + domConstruct.place(linkElement, details); + + return details; + } }); } ); \ No newline at end of file diff --git a/gdc-viewer/js/View/Track/SSMTrack.js b/gdc-viewer/js/View/Track/SSMTrack.js index c3c5041..451726c 100644 --- a/gdc-viewer/js/View/Track/SSMTrack.js +++ b/gdc-viewer/js/View/Track/SSMTrack.js @@ -1,24 +1,24 @@ define( [ "dojo/_base/declare", - "JBrowse/View/Track/CanvasFeatures", + "JBrowse/View/Track/HTMLFeatures", 'JBrowse/View/Track/_ExportMixin', 'dojo/dom-construct' ], function( declare, - CanvasFeatures, + HTMLFeatures, ExportMixin, domConstruct) { - return declare([ CanvasFeatures, ExportMixin ], { + return declare([ HTMLFeatures, ExportMixin ], { _exportFormats: function() { return [ {name: 'gdc-viewer/View/Export/GFF3', label: 'GFF3', fileExt: 'gff3'}, {name: 'gdc-viewer/View/Export/BED', label: 'BED', fileExt: 'bed'}, {name: 'gdc-viewer/View/Export/CSV', label: 'CSV', fileExt: 'csv'}, - {name: 'icgc-viewer/View/Export/SequinTable', label: 'Sequin Table', fileExt: 'sqn'}, - {name: 'gdc-viewer/View/Export/TrackConfig', label: 'Track Config INI', fileExt: 'conf'}, + {name: 'gdc-viewer/View/Export/SequinTable', label: 'Sequin Table', fileExt: 'sqn'}, + {name: 'gdc-viewer/View/Export/TrackConfig', label: 'Track Config', fileExt: 'conf'}, {name: 'gdc-viewer/View/Export/TrackConfigJson', label: 'Track Config JSON', fileExt: 'json'}]; }, @@ -47,6 +47,64 @@ define( var projectSection = dojo.create('div', { style: 'flex-grow:1; flex-basis: 0' }, thirdRow) this.renderDetailField( projectSection, 'projects', f.get('projects'), f, undefined, track.store.getTagMetadata('projects')) }, + + _trackMenuOptions: function () { + var track = this; + var options = this.inherited(arguments); + options.push({ + label: 'Share Track as URL', + action: "contentDialog", + title: 'Share Track as URL', + content: dojo.hitch(this,'_shareableLinkContent') + }); + return options; + }, + + _shareableLinkContent: function() { + var track = this; + var details = domConstruct.create('div', { className: 'detail', style: 'display: flex; flex-direction: column; align-items: center; justify-content: center;' }); + + // Create addTracks value + var addTracksArray = []; + var addTrackConf = {}; + addTrackConf.label = track.config.label; + addTrackConf.storeClass = track.store.config.type; + addTrackConf.type = track.config.type; + addTrackConf.key = track.config.key; + addTrackConf.metadata = track.config.metadata; + addTrackConf.unsafePopup = true; + addTrackConf.filters = track.store.config.filters; + addTrackConf.case = track.store.config.case; + addTrackConf.size = track.config.size; + addTracksArray.push(addTrackConf); + addTracksArray = JSON.stringify(addTracksArray); + + // Create a shareable URL + var params = new URLSearchParams(window.location.search); + params.set("addTracks", addTracksArray); + var shareableLink = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + params.toString(); + + // Create help text + var helpString = '

Use the following link to share the selected track at the current location.

'; + var helpElement = domConstruct.toDom(helpString); + domConstruct.place(helpElement, details); + + // Create text area with shareable link + var textArea = domConstruct.create( + 'textarea',{ + rows: 10, + value: shareableLink, + style: "width: 80%", + readOnly: true + }, details ); + + // Create a DOM element for the link + var linkString = 'Open in New Tab'; + var linkElement = domConstruct.toDom(linkString); + domConstruct.place(linkElement, details); + + return details; + } }); } ); \ No newline at end of file