diff --git a/deploy/datasets/index.js b/deploy/datasets/index.js index 0e884865bde8479f96771220ec0d6e75d06e9e3c..420a2d251539240302c3e472dba1bf95f045ad5f 100644 --- a/deploy/datasets/index.js +++ b/deploy/datasets/index.js @@ -120,15 +120,17 @@ datasetsRouter.post("/downloadParcellationThemself", (req,res, next) => { var zip = new JSZip(); - zip.file("Publications.txt", req.body['publicationsText']) + zip.file("credits.txt", req.body['publicationsText']) zip.file("Terms of use.txt", termsOfUse) //ToDo: Need to download files dynamicly. Nii folder should remove - if (req.body['fileName'].includes("JuBrain Cytoarchitectonic Atlas")) { + if (req.body['niiFiles']) { var nii = zip.folder("nifti") - nii.file('jubrain-max-pmap-v22c_space-mnicolin27.nii', fs.readFileSync(path.join(__dirname, 'nii') + '/' + 'jubrain-max-pmap-v22c_space-mnicolin27.nii')) + req.body['niiFiles'].forEach(file => { + nii.file(file['file'], fs.readFileSync(path.join(__dirname, 'nii') + '/' + file['file'])) + }) } zip.generateAsync({type:"base64"}) diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index 4da6ccceeb0dc86bf906afde651fc022e53da50d..e5255e8182643a782aecc7d01718c077c399230c 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -21,6 +21,7 @@ import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS } from "src/services/state/uiSt import { TabsetComponent } from "ngx-bootstrap/tabs"; import { ToastService } from "src/services/toastService.service"; import { ZipFileDownloadService } from "src/services/zipFileDownload.service"; +import {forEach} from "@angular/router/src/utils/collection"; @Component({ selector: 'atlas-viewer', @@ -92,6 +93,8 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { handleToast tPublication pPublication + downloadingProcess = false + niiFileSize = 0 get toggleMessage(){ return this.constantsService.toggleMessage @@ -221,6 +224,8 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { this.subscriptions.push( this.selectedParcellation$.subscribe(parcellation => { this.selectedParcellation = parcellation + this.niiFileSize = 0 + if (this.selectedTemplate && this.selectedParcellation) { if (this.selectedTemplate['properties'] && this.selectedTemplate['properties']['publications']) { @@ -233,6 +238,12 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } else { this.pPublication = null } + if(this.selectedParcellation['properties'] && this.selectedParcellation['properties']['nifty']) { + this.selectedParcellation['properties']['nifty'].forEach(nii => { + this.niiFileSize += nii['size'] + }) + } + } else { this.tPublication = null this.pPublication = null @@ -247,7 +258,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { this.handleToast = this.toastService.showToast(this.publications, { timeout: 7000 }) - } }) ) @@ -256,6 +266,8 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } downloadPublications() { + this.downloadingProcess = true + const fileName = this.selectedTemplate.name + ' - ' + this.selectedParcellation.name let publicationsText = '' @@ -274,7 +286,12 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { }); } - this.zipFileDownloadService.downloadZip(publicationsText, fileName) + this.zipFileDownloadService.downloadZip( + publicationsText, + fileName, + this.selectedParcellation['properties'] && this.selectedParcellation['properties']['nifty']? this.selectedParcellation['properties']['nifty'] : 0).subscribe(data => { + this.downloadingProcess = false + }) publicationsText = '' } diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index 283df246c121ad228e514660757f1cbff1c8a20b..8b7ec26edc1f8eb6f5ac60e7aba51b152d67d074 100644 --- a/src/atlasViewer/atlasViewer.style.css +++ b/src/atlasViewer/atlasViewer.style.css @@ -183,14 +183,37 @@ mat-sidenav { max-width: 700px; display: flex; flex-direction: column; - justify-content: center; + max-height: 500px; + padding-right: 10px; + overflow-y: auto; } -.downloadPublications { +.download-buttons-panel { align-self: flex-end; +} +.downloadPublications { + margin: 5px; outline: none; } .textPartInPublications { - text-align: left; + text-align: justify; +} + + +.timerToast:hover::-webkit-scrollbar-thumb{ + background: #888; + border-radius: 10px; +} +.timerToast::-webkit-scrollbar { + width: 7px; +} +.timerToast::-webkit-scrollbar-track { + background: transparent; +} +.timerToast::-webkit-scrollbar-thumb { + background: #CCC; +} +.timerToast::-webkit-scrollbar-thumb:hover { + background: #555; } \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 894dca0dfb1d30f87f7e724d70b007bc1714a9d7..474312047f0dfc309b02a47c0ddab6e4bb95f2b9 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -261,20 +261,37 @@ <ng-template #publications > <div *ngIf="tPublication || pPublication" class="timerToast"> - <button mat-mini-fab color="primary" class="downloadPublications" (click)="downloadPublications()">↓</button> +<!-- <button mat-mini-fab color="primary" class="downloadPublications" (click)="downloadPublications()">↓</button>--> - <div *ngIf="tPublication"> - <p>{{selectedTemplate['name']}} Publication(s)</p> + + + <div *ngIf="tPublication && selectedTemplate['name']"> + <p>{{selectedTemplate['name']}}</p> + <p class="textPartInPublications">{{selectedTemplate['properties']['description']}}</p> + + <p>Publication(s)</p> <div *ngFor="let tp of tPublication" class="textPartInPublications"> <a [href]="tp['doi']" target="_blank">{{tp['citation']}}</a> </div> <hr *ngIf="pPublication"> </div> <div *ngIf="pPublication"> - <p>{{selectedParcellation['name']}} Publication(s)</p> + <p>{{selectedParcellation['name']}}</p> + <p class="textPartInPublications">{{selectedParcellation['properties']['description']}}</p> + + <p>Publication(s)</p> <div *ngFor="let pp of pPublication" class="textPartInPublications"> <a [href]="pp['doi']" target="_blank">{{pp['citation']}}</a> </div> </div> + + <div class="download-buttons-panel"> + <button mat-raised-button color="primary" class="downloadPublications" (click)="downloadPublications()">Explore <i class="fas fa-external-link-alt"></i></button> + <button mat-raised-button color="primary" class="downloadPublications" (click)="downloadPublications()" [disabled] = "downloadingProcess" > + Download + <span *ngIf="niiFileSize > 0">(.nii {{niiFileSize/10000 | number:'.1-2'}} Mb) </span> + <i class="fas" [ngClass]="!downloadingProcess? 'fa-download' :'fa-spinner fa-pulse'"></i> + </button> + </div> </div> </ng-template> \ No newline at end of file diff --git a/src/components/radiolist/radiolist.component.ts b/src/components/radiolist/radiolist.component.ts index bd5fd328cf7cb19dadead6c973cbf7d297cc8ffa..109cda7a557234e74fe6d819b826edaf6f6a474b 100644 --- a/src/components/radiolist/radiolist.component.ts +++ b/src/components/radiolist/radiolist.component.ts @@ -9,20 +9,20 @@ import { ZipFileDownloadService } from "src/services/zipFileDownload.service"; './radiolist.style.css' ], styles: [ - ` - ul > li.selected > .textSpan:before - { - content: '\u2022'; - width : 1em; - display:inline-block; - } - ul > li:not(.selected) > .textSpan:before - { - content: ' '; - width : 1em; - display:inline-block; - } - ` + // ` + // ul > li.selected > .textSpan:before + // { + // content: '\u2022'; + // width : 1em; + // display:inline-block; + // } + // ul > li:not(.selected) > .textSpan:before + // { + // content: ' '; + // width : 1em; + // display:inline-block; + // } + // ` ], changeDetection: ChangeDetectionStrategy.OnPush }) @@ -50,12 +50,21 @@ export class RadioList{ @ViewChild('publicationTemplate') publicationTemplate: TemplateRef<any> + downloadingProcess = false handleToast + niiFileSize = 0 constructor(private toastService: ToastService, private zipFileDownloadService: ZipFileDownloadService) {} showToast(item) { + this.niiFileSize = 0 + if(item['properties']['nifty']) { + item['properties']['nifty'].forEach(nii => { + this.niiFileSize += nii['size'] + }) + } + if (this.handleToast) { this.handleToast() this.handleToast = null @@ -63,17 +72,24 @@ export class RadioList{ this.handleToast = this.toastService.showToast(this.publicationTemplate, { timeout: 7000 }) - } downloadPublications(item) { + this.downloadingProcess = true + const filename = item['name'] let publicationsText = item['name'] + ' Publications:\r\n' item['properties']['publications'].forEach((p, i) => { publicationsText += '\t' + (i+1) + '. ' + p['citation'] + ' - ' + p['doi'] + '\r\n' }); - this.zipFileDownloadService.downloadZip(publicationsText, filename) + this.zipFileDownloadService.downloadZip(publicationsText, filename, item['properties']['nifty']? item['properties']['nifty'] : 0).subscribe(data => { + this.downloadingProcess = false + }) publicationsText = '' } + + overflowText(event) { + return (event.offsetWidth < event.scrollWidth); + } } \ No newline at end of file diff --git a/src/components/radiolist/radiolist.style.css b/src/components/radiolist/radiolist.style.css index c3fcf157b57217734cf562400ddcc17697a67ba2..af97671263ad49485ebd06b815d7ddb623430773 100644 --- a/src/components/radiolist/radiolist.style.css +++ b/src/components/radiolist/radiolist.style.css @@ -1,3 +1,4 @@ +/*@import "~@angular/material/prebuilt-themes/indigo-pink.css";*/ :host-context([darktheme="true"]) ul { @@ -54,10 +55,13 @@ ul,span.dropdown-item-1 border-radius:0px; white-space: nowrap; border:none; + overflow: hidden; + text-overflow: ellipsis } + .infoIcon { - margin-right: 5px; + margin-left: 5px; display: inline-block; border: 1px solid gray; border-radius: 15px; @@ -71,14 +75,37 @@ ul,span.dropdown-item-1 max-width: 700px; display: flex; flex-direction: column; - justify-content: center; + max-height: 500px; + padding-right: 10px; + overflow-y: auto; } -.downloadPublications { +.download-buttons-panel { align-self: flex-end; +} +.downloadPublications { + margin: 5px; outline: none; } .textPartInPublications { - text-align: left; + text-align: justify; +} + + +.timerToast:hover::-webkit-scrollbar-thumb{ + background: #888; + border-radius: 10px; +} +.timerToast::-webkit-scrollbar { + width: 7px; +} +.timerToast::-webkit-scrollbar-track { + background: transparent; +} +.timerToast::-webkit-scrollbar-thumb { + background: #CCC; +} +.timerToast::-webkit-scrollbar-thumb:hover { + background: #555; } \ No newline at end of file diff --git a/src/components/radiolist/radiolist.template.html b/src/components/radiolist/radiolist.template.html index ffa5c4fc1d7ed5c044bbbbc2926d2acc706796b5..617e9801ff206b5f68ddf6a5f3fe11ab932b73b3 100644 --- a/src/components/radiolist/radiolist.template.html +++ b/src/components/radiolist/radiolist.template.html @@ -2,30 +2,46 @@ [ngClass]="ulClass" [ngStyle]="{'opacity': isMobile? '1' : '0.8', 'border-color': darktheme? 'white' : 'black'}" role="menu"> - <li - *ngFor="let input of inputArray" + + <li + *ngFor="let input of inputArray" [ngClass]="checkSelected(selectedItem, input) ? 'selected' : 'notselected'" role="menuitem" - (click)="itemSelected.emit({previous: selectedItem, current: input})"> - - <span *ngIf="input['properties'] && input['properties']['publications']" class="infoIcon" (click)="showToast(input);$event.stopPropagation()"> - i - </span> + (click)="itemSelected.emit({previous: selectedItem, current: input})" + style="display: flex; justify-content: space-between;"> + - <span class="dropdown-item-1 textSpan" - [innerHTML] = "listDisplay(input)"> + + <span class="dropdown-item-1 textSpan" + #DropDownText + [innerHTML] = "listDisplay(input)" + [style.fontWeight] = "checkSelected(selectedItem, input)? 'bold' : ''" + [ngStyle]="input['properties'] && input['properties']['publications'] ? {'width': 'calc(100% - 30px)', 'overflow': 'hidden'} : {}" + [matTooltip]="overflowText(DropDownText)? DropDownText.innerText: ''"> </span> + <span *ngIf="input['properties'] && input['properties']['publications']" class="infoIcon" (click)="showToast(input);$event.stopPropagation()" + style="align-self: flex-end;"> + i + </span> - -<ng-template #publicationTemplate> - <div *ngIf="input['properties'] && input['properties']['publications']" class="timerToast"> - <button mat-mini-fab color="primary" class="downloadPublications" (click)="downloadPublications(input)">↓</button> - <p>{{input['name']}} Publication(s)</p> +<ng-template #publicationTemplate *ngIf="input['properties'] && input['properties']['publications']"> + <div class="timerToast"> + <p>{{input['name']}}</p> + <p class="textPartInPublications">{{input['properties']['description']}}</p> + <p>Publication(s)</p> <div *ngFor="let p of input['properties']['publications']" class="textPartInPublications"> <a [href]="p['doi']" target="_blank">{{p['citation']}}</a> </div> + <div class="download-buttons-panel"> + <button mat-raised-button color="primary" class="downloadPublications" (click)="downloadPublications(input)">Explore <i class="fas fa-external-link-alt"></i></button> + <button mat-raised-button color="primary" class="downloadPublications" (click)="downloadPublications(input)" [disabled] = "downloadingProcess" > + Download + <span *ngIf="niiFileSize > 0">(.nii {{niiFileSize/10000 | number:'.1-2'}} Mb) </span> + <i class="fas" [ngClass]="!downloadingProcess? 'fa-download' :'fa-spinner fa-pulse'"></i> + </button> + </div> </div> </ng-template> diff --git a/src/index.html b/src/index.html index 3a45da9bca5a8f7038f93470f1089bdfe3d696b6..3cd64e66e8db5f00b99f0cdf2a6a97b985fc9a92 100644 --- a/src/index.html +++ b/src/index.html @@ -8,6 +8,8 @@ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"> <link rel = "stylesheet" href = "extra_styles.css"> <link rel = "stylesheet" href = "plugin_styles.css"> + <link rel = "stylesheet" href = "../node_modules/@angular/material/prebuilt-themes/indigo-pink.css"> + <title>Interactive Atlas Viewer</title> </head> <body> diff --git a/src/services/zipFileDownload.service.ts b/src/services/zipFileDownload.service.ts index 434a11338646477647249970c75463c7b17fd2c2..f6b9e164faeb25c84d7590d9085fb4edd81de708 100644 --- a/src/services/zipFileDownload.service.ts +++ b/src/services/zipFileDownload.service.ts @@ -1,21 +1,25 @@ import { Injectable } from "@angular/core"; import {HttpClient} from "@angular/common/http"; import {AtlasViewerConstantsServices} from "src/atlasViewer/atlasViewer.constantService.service"; +import {map} from "rxjs/operators"; @Injectable({ providedIn: 'root' }) export class ZipFileDownloadService { constructor(private httpClient: HttpClient, private constantService: AtlasViewerConstantsServices) {} - downloadZip(publicationsText, fileName) { + downloadZip(publicationsText, fileName, niiFiles) { const correctedName = fileName.replace(/[|&;$%@"<>()+,/]/g, "") - this.httpClient.post(this.constantService.backendUrl + 'datasets/downloadParcellationThemself', { + return this.httpClient.post(this.constantService.backendUrl + 'datasets/downloadParcellationThemself', { fileName: correctedName, publicationsText: publicationsText, + niiFiles: niiFiles === 0 ? null : niiFiles },{responseType: "text"} - ).subscribe(data => { - this.downloadFile(data, correctedName) - }) + ).pipe( + map (data => { + this.downloadFile(data, correctedName) + }) + ) } downloadFile(data, fileName) { diff --git a/src/ui/sharedModules/angularMaterial.module.ts b/src/ui/sharedModules/angularMaterial.module.ts index 969b6f39c326c3fbef83e86ed797ec0fc7b03abd..b63e2dfaa5a37e5caa567efebe4bf1c135e00a4b 100644 --- a/src/ui/sharedModules/angularMaterial.module.ts +++ b/src/ui/sharedModules/angularMaterial.module.ts @@ -4,12 +4,12 @@ import { MatSidenavModule, MatCardModule, MatTabsModule, - MatMenuModule + MatTooltipModule } from '@angular/material'; import { NgModule } from '@angular/core'; @NgModule({ - imports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatMenuModule], - exports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatMenuModule], + imports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatTooltipModule], + exports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatTooltipModule], }) export class AngularMaterialModule { } \ No newline at end of file