Skip to content
This repository has been archived by the owner on Jul 9, 2024. It is now read-only.

Commit

Permalink
feat(file): bam slicing ui
Browse files Browse the repository at this point in the history
Closes #1432
Christine Yu committed Nov 19, 2015
1 parent 21ed658 commit 1817704
Showing 8 changed files with 178 additions and 38 deletions.
2 changes: 1 addition & 1 deletion app/scripts/core/core.controller.ts
Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@ module ngApp.core.controllers {

class WarningController {
/* @ngInject */
constructor(private $modalInstance){}
constructor(private $modalInstance) {}

acceptWarning(): void {
this.$modalInstance.close();
48 changes: 48 additions & 0 deletions app/scripts/files/files.controllers.ts
Original file line number Diff line number Diff line change
@@ -67,12 +67,60 @@ module ngApp.files.controllers {
}
}

canBAMSlice(): boolean {
return this.file.data_type.toLowerCase() === "raw sequencing data" &&
this.file.data_subtype.toLowerCase() === "aligned reads" &&
_.indexOf(_.pluck(this.file.related_files, 'type'), "bai") !== -1;
}

}

class BAMSlicingController {

/* @ngInject */
constructor (private $modalInstance,
private $scope: ng.IScope,
private FilesService: IFilesService,
public fileID: string,
public completeCallback: any) {}

submit(): void {
if(this.$scope.bedModel) {
this.FilesService.sliceBAM(this.fileID, this.$scope.bedModel, this.completeCallback);
this.$modalInstance.dismiss('slicing');
} else {
this.$modalInstance.dismiss('cancelled');
}
}

closeModal(): void {
this.$modalInstance.dismiss('cancelled');
}
}

class BAMFailedModalController {
public errorBlobString: string;
/* @ngInject */
constructor(private $modalInstance,
public errorStatus: string,
public errorMsg: string,
private errorBlob: any) {
this.errorBlobString = "";
var reader = new FileReader();
reader.addEventListener("loadend", () => {
this.errorBlobString = _.get(JSON.parse(reader.result), "error", "Error slicing");
});
reader.readAsText(errorBlob);
}
}


angular
.module("files.controller", [
"files.services"
])
.controller("BAMSlicingController", BAMSlicingController)
.controller("BAMFailedModalController", BAMFailedModalController)
.controller("FileController", FileController);
}

65 changes: 43 additions & 22 deletions app/scripts/files/files.directives.ts
Original file line number Diff line number Diff line change
@@ -8,39 +8,60 @@ module ngApp.files.directives {
files:"=",
copy: "@",
dlcopy: "@",
classes: "@"
classes: "@",
icon: "@"
},
template: "<a ng-class=\"[classes || 'btn btn-primary']\">" +
"<i class=\"fa fa-download\" ng-class=\"{'fa-spinner': active, 'fa-pulse': active}\"></i>" +
"<span ng-if=\"copy\"><span ng-if=\"!active\">&nbsp;{{copy}}</span><span ng-if=\"active\">&nbsp;{{dlcopy}}</span></span></a>",
"<i class=\"fa {{icon || 'fa-download'}}\" ng-class=\"{'fa-spinner': active, 'fa-pulse': active}\"></i>" +
"<span ng-if=\"copy\"><span ng-if=\"!active\">&nbsp;{{copy}}</span><span ng-if=\"active\">&nbsp;{{dlcopy}}</span></span></a>",
link: function($scope, $element, $attrs){

var files = $scope.files;
$scope.active = false;

$element.on("click", function(a) {

if (!_.isArray(files)) {
files = [files];
}
if (UserService.userCanDownloadFiles(files)) {

$scope.active = true;
$attrs.$set("disabled", "disabled");

FilesService.downloadFiles(_.pluck(files, "file_id"), (complete)=>{

if(complete){
$scope.active = false;
$element.removeAttr("disabled");
} else {
//Download Failed
$scope.active = false;
$element.removeAttr("disabled");
}

});

var turnSpinnerOff = function() {
$scope.active = false;
$element.removeAttr("disabled");
};
if ($scope.copy === "Download") {
FilesService.downloadFiles(_.pluck(files, "file_id"), (complete)=>{
if(complete){
turnSpinnerOff();
} else {
//Download Failed
turnSpinnerOff();
}
});
} else {
var bamModal = $modal.open({
templateUrl: "files/templates/bam-slicing.html",
controller: "BAMSlicingController",
controllerAs: "bamc",
backdrop: true,
keyboard: true,
animation: false,
size: "lg",
resolve: {
fileID: function () {
return _.first(_.pluck(files, "file_id"));
},
completeCallback: function() {
return turnSpinnerOff;
}
}
});
bamModal.result.then(turnSpinnerOff, function(reason) {
if (reason !== 'slicing') {
turnSpinnerOff();
}
});
}
} else {
var template = UserService.currentUser ?
"core/templates/request-access-to-download-single.html" :
@@ -63,6 +84,6 @@ module ngApp.files.directives {
}

angular
.module("files.directives", ["restangular", "components.location", "user.services", "core.services", "ui.bootstrap"])
.module("files.directives", ["restangular", "components.location", "user.services", "core.services", "ui.bootstrap", "files.controller"])
.directive("downloadButton", DownloadButton);
}
51 changes: 49 additions & 2 deletions app/scripts/files/files.services.ts
Original file line number Diff line number Diff line change
@@ -51,7 +51,6 @@ module ngApp.files.services {
download(endpoint: string, ids: Array<string>, callback: any) {
var abort = this.$q.defer();
var params = { "ids": ids };

this.RestFullResponse.all(endpoint + "?annotations=true&related_files=true")
.withHttpConfig({
timeout: abort.promise,
@@ -62,7 +61,7 @@ module ngApp.files.services {
.then((response) => {
var filename: string = response.headers['content-disposition'].match(/filename=(.*)/i)[1];
this.$window.saveAs(response.data, filename);
if(callback) callback(true);
if(callback) callback(true);
}, (response)=>{
//Download Failed
this.$modal.open({
@@ -78,6 +77,54 @@ module ngApp.files.services {
});
}

processBED(bedTSV: string): Object {
var lines = bedTSV.split("\n");
return {"regions": _.map(lines, (line) => {
var region = line.split("\t");
var regionString = region[0];
if (region.length > 1) {
regionString += ":" + region[1];
if (region.length > 2) {
regionString += "-" + region[2];
}
}
return regionString;
})};
}

sliceBAM(fileID: string, bedTSV: string, callback: any) {
var abort = this.$q.defer();
var params = this.processBED(bedTSV);
this.RestFullResponse.all("/v0/slicing/view/" + fileID)
.withHttpConfig({
timeout: abort.promise,
responseType: "blob",
withCredentials: true,
})
.post(params, undefined, { 'Content-Type': 'application/json' })
.then((response) => {
this.$window.saveAs(response, fileID + '-sliced.bam');
if(callback) callback(true);
}, (response)=>{
//Slicing Failed
this.$modal.open({
templateUrl: 'files/templates/bam-slicing-failed.html',
controller: "BAMFailedModalController",
controllerAs: "bamfc",
backdrop: true,
keyboard: true,
animation: false,
size: "lg",
resolve: {
errorStatus: () => { return response.status; },
errorMsg: () => { return response.statusText; },
errorBlob: () => { return response.data; }
}
});
if(callback) callback(false);
});
}

getFiles(params: Object = {}): ng.IPromise<IFiles> {
if (params.hasOwnProperty("fields")) {
params["fields"] = params["fields"].join();
12 changes: 12 additions & 0 deletions app/scripts/files/templates/bam-slicing-failed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="modal-header">
<h4 data-translate>BAM Slicing Failed</h4>
</div>
<div class="modal-body text-danger">
<h5>{{bamfc.errorStatus}}: {{ bamfc.errorMsg }}</h5>
<p data-translate>
{{ bamfc.errorBlobString }}
</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-ng-click="bamfc.$modalInstance.close()" data-translate>OK</button>
</div>
13 changes: 13 additions & 0 deletions app/scripts/files/templates/bam-slicing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="modal-header">
<h4 data-translate>BAM Slicing {{bamc.fileID}}</h4>
</div>
<div class="modal-body">
Please enter a genome coordinate below (in <a href="https://genome.ucsc.edu/FAQ/FAQformat.html#format1">BED</a> format).
<textarea id="bed" class="form-control" data-ng-model="bedModel"></textarea>
</div>
<div class="modal-footer">
<span style="float: left">
<!--or <button class="btn btn-warning" ng-click="bamc.cancel()" data-translate>Upload a BED file</button>-->
</span>
<button class="btn btn-warning" ng-click="bamc.submit()" data-translate>Submit</button>
</div>
3 changes: 2 additions & 1 deletion app/scripts/files/templates/file.html
Original file line number Diff line number Diff line change
@@ -9,7 +9,8 @@ <h1 class="col-lg-12 col-md-12">
<span data-ng-if="fc.isInCart(fc.file.file_id)" data-translate>Remove from Cart</span>
<span data-ng-if="!fc.isInCart(fc.file.file_id)" data-translate>Add to Cart</span>
</button>
<download-button data-files=fc.file data-copy="Download" data-dlcopy="Downloading"></download-button>
<download-button data-icon="fa-download" data-files=fc.file data-copy="Download" data-dlcopy="Downloading"></download-button>
<download-button data-icon="fa-cutlery" data-files=fc.file data-copy="BAM Slicing" data-dlcopy="Slicing BAM" data-ng-if="fc.canBAMSlice()"></download-button>
</span>
</h1>
</div>
22 changes: 10 additions & 12 deletions app/scripts/files/tests/files.tests.js
Original file line number Diff line number Diff line change
@@ -17,41 +17,34 @@ describe('Files:', function () {
beforeEach(inject(function ($httpBackend) {
httpBackend = $httpBackend;
}));

describe('Directives: ', function(){

describe("download-button", function(){

var scope, $compile, element;

beforeEach(inject(function ($injector) {

$compile = $injector.get('$compile');
var $rootScope = $injector.get('$rootScope');

scope = $rootScope.$new();
element = angular.element('<download-button data-files=fc.file data-copy="Download" data-classes="fa fa-download"><span ng-transclude></span></download-button>');
$compile(element)(scope);

}));

afterEach(function () {
scope.$destroy();
});

it('should compile', function () {
scope.$digest();
expect(element).to.be.an('object');
});

it('should contain fa-download class', function () {
scope.$digest();
expect(element.html()).to.have.string('fa fa-download');
});
});

});

describe('Service:', function () {

it('should get all files', inject(function (FilesService) {
@@ -79,5 +72,10 @@ describe('Files:', function () {
expect(FilesService.ds.get).to.have.been.calledOnce;
expect(FilesService.ds.get).to.have.been.calledWith(1);
}));

it('should process tsv files', inject(function (FilesService) {
var regions = FilesService.processBED('chr1\nchr2\t123\t321\nchr3\t5');
expect(regions).to.deep.equal({regions: ['chr1', 'chr2:123-321', 'chr3:5']});
}));
});
});

0 comments on commit 1817704

Please sign in to comment.