Skip to content
Snippets Groups Projects
files.component.ts 7.64 KiB
Newer Older
  • Learn to ignore specific revisions
  • paf's avatar
    paf committed
    import {
      Component, OnInit, Inject, ViewChild
    } from '@angular/core';
    
    import { MatTableDataSource } from '@angular/material/table';
    
    paf's avatar
    paf committed
    import { MatSnackBar } from '@angular/material/snack-bar';
    import { MatDialog } from '@angular/material/dialog';
    
    import { Sort } from '@angular/material/sort';
    
    rhenck's avatar
    rhenck committed
    
    
    import { saveAs } from 'file-saver';
    
    rhenck's avatar
    rhenck committed
    import {
      ConfirmDialogComponent, ConfirmDialogData, MessageDialogComponent,
      MessageDialogData, MessageType
    } from 'iqb-components';
    
    import { map } from 'rxjs/operators';
    
    rhenck's avatar
    rhenck committed
    import { WorkspaceDataService } from '../workspacedata.service';
    
    import {
      IQBFileType, GetFileResponseData, IQBFile, IQBFileTypes
    } from '../workspace.interfaces';
    
    rhenck's avatar
    rhenck committed
    import { BackendService, FileDeletionReport } from '../backend.service';
    import { MainDataService } from '../../maindata.service';
    
    paf's avatar
    paf committed
    import { IqbFilesUploadQueueComponent } from './iqb-files';
    
    paflov's avatar
    paflov committed
    interface FileStats {
    
      invalid: {
        [type in IQBFileType]?: number;
    
    paflov's avatar
    paflov committed
      }
    
      total: {
        count: number;
        invalid: number;
      };
    
    Martin Mechtel's avatar
    Martin Mechtel committed
      templateUrl: './files.component.html',
      styleUrls: ['./files.component.css']
    
    export class FilesComponent implements OnInit {
    
      public files: {[type in IQBFileType]?: MatTableDataSource<IQBFile>} = {};
      public fileTypes = IQBFileTypes;
      public displayedColumns = ['checked', 'name', 'size', 'modificationTime'];
    
      public uploadUrl = '';
    
      public fileNameAlias = 'fileforvo';
    
      public lastSort:Sort = {
        active: 'name',
        direction: 'asc'
    
      public typeLabels = {
        Testtakers: 'Teilnehmerlisten',
        Booklet: 'Testhefte',
        SysCheck: 'System-Check-Definitionen',
        Resource: 'Ressourcen',
        Unit: 'Units'
      };
    
    paf's avatar
    paf committed
    
    
    paflov's avatar
    paflov committed
      public fileStats: FileStats = {
    
        total: {
          count: 0,
          invalid: 0
        },
        invalid: {},
    
    paflov's avatar
    paflov committed
      };
    
    paf's avatar
    paf committed
      @ViewChild('fileUploadQueue', { static: true }) public uploadQueue: IqbFilesUploadQueueComponent;
    
    
      constructor(
        @Inject('SERVER_URL') private serverUrl: string,
    
        @Inject('VERONA_API_VERSION_SUPPORTED') private veronaApiVersionSupported: string,
    
        private bs: BackendService,
    
    mechtelm's avatar
    mechtelm committed
        public wds: WorkspaceDataService,
    
        public confirmDialog: MatDialog,
    
        public messageDialog: MatDialog,
    
        private mds: MainDataService,
    
        public snackBar: MatSnackBar
    
    rhenck's avatar
    rhenck committed
      ngOnInit(): void {
    
        this.uploadUrl = `${this.serverUrl}workspace/${this.wds.wsId}/file`;
    
    mechtelm's avatar
    mechtelm committed
        setTimeout(() => {
    
          this.mds.setSpinnerOn();
    
    mechtelm's avatar
    mechtelm committed
          this.updateFileList();
    
    paf's avatar
    paf committed
        });
    
      public checkAll(isChecked: boolean, type: IQBFileType): void {
        this.files[type].data = this.files[type].data.map(file => {
    
    paflov's avatar
    paflov committed
          // eslint-disable-next-line no-param-reassign
    
          file.isChecked = isChecked;
          return file;
    
      public deleteFiles(): void {
    
        if (this.wds.wsRole !== 'RW') {
          return;
        }
    
        const filesToDelete = [];
    
    paf's avatar
    paf committed
        Object.keys(this.files).forEach(type => {
          this.files[type].data.forEach(file => {
    
            if (file.isChecked) {
              filesToDelete.push(`${file.type}/${file.name}`);
    
        if (filesToDelete.length > 0) {
          const p = filesToDelete.length > 1;
          const dialogRef = this.confirmDialog.open(ConfirmDialogComponent, {
            width: '400px',
            data: <ConfirmDialogData>{
              title: 'Löschen von Dateien',
              content: `Sie haben ${p ? filesToDelete.length : 'eine'} Datei${p ? 'en' : ''}\` 
                ausgewählt. Soll${p ? 'en' : ''}  diese gelöscht werden?`,
              confirmbuttonlabel: 'Löschen',
              showcancel: true
            }
          });
    
          dialogRef.afterClosed().subscribe(result => {
            if (result !== false) {
              this.mds.setSpinnerOn();
              this.bs.deleteFiles(filesToDelete).subscribe((fileDeletionReport: FileDeletionReport) => {
                const message = [];
                if (fileDeletionReport.deleted.length > 0) {
                  message.push(`${fileDeletionReport.deleted.length} Dateien erfolgreich gelöscht.`);
                }
                if (fileDeletionReport.not_allowed.length > 0) {
                  message.push(`${fileDeletionReport.not_allowed.length} Dateien konnten nicht gelöscht werden.`);
                }
                this.snackBar.open(message.join('<br>'), message.length > 1 ? 'Achtung' : '', { duration: 1000 });
                this.updateFileList();
              });
            }
          });
        } else {
          this.messageDialog.open(MessageDialogComponent, {
            width: '400px',
            data: <MessageDialogData>{
              title: 'Löschen von Dateien',
              content: 'Bitte markieren Sie erst Dateien!',
              type: MessageType.error
            }
          });
    
      public updateFileList(empty = false): void {
    
          this.files = {};
    
          this.mds.setSpinnerOff();
    
          this.bs.getFiles()
            .pipe(map(fileList => this.addFrontendChecksToFiles(fileList)))
    
            .subscribe(fileList => {
              this.files = {};
              Object.keys(fileList)
                .forEach(type => {
                  this.files[type] = new MatTableDataSource(fileList[type]);
                });
    
    paf's avatar
    paf committed
              this.fileStats = FilesComponent.getStats(fileList);
    
              this.setTableSorting(this.lastSort);
    
              this.mds.setSpinnerOff();
    
      private static getStats(fileList: GetFileResponseData): FileStats {
    
    paflov's avatar
    paflov committed
        const stats: FileStats = {
    
          total: {
            count: 0,
            invalid: 0
          },
          invalid: {},
    
    paflov's avatar
    paflov committed
        };
    
        Object.keys(fileList)
          .forEach(type => {
            fileList[type].forEach(file => {
              if (typeof stats.invalid[type] === 'undefined') {
                stats.invalid[type] = 0;
              }
              stats.total.count += 1;
              if (file.report.error && file.report.error.length) {
                stats.invalid[type] += 1;
                stats.total.invalid += 1;
                stats.testtakers += (typeof file.info.testtakers === 'number') ? file.info.testtakers : 0;
              }
            });
          });
    
    paflov's avatar
    paflov committed
        return stats;
    
    paf's avatar
    paf committed
      }
    
    
      private addFrontendChecksToFiles(fileList: GetFileResponseData): GetFileResponseData {
        Object.keys(fileList).forEach(type => {
          // eslint-disable-next-line no-param-reassign
          fileList[type] = fileList[type].map(files => this.addFrontendChecksToFile(files));
        });
        return fileList;
    
      private addFrontendChecksToFile(file: IQBFile): IQBFile {
    
        if (typeof file.info['verona-version'] !== 'undefined') {
          const fileMayor = file.info['verona-version'].toString().split('.').shift();
          const systemMayor = this.veronaApiVersionSupported.split('.').shift();
          if (fileMayor !== systemMayor) {
            if (typeof file.report.error === 'undefined') {
              // eslint-disable-next-line no-param-reassign
              file.report.error = [];
            }
            file.report.error.push(`Verona Version of this Player is not compatible 
              with this system's version (\`${this.veronaApiVersionSupported}\`)!`);
          }
        }
        return file;
      }
    
    
      public download(file: IQBFile): void {
    
        this.mds.setSpinnerOn();
    
        this.bs.downloadFile(file.type, file.name)
    
              this.mds.setSpinnerOff();
    
                saveAs(fileData as Blob, file.name);
    
    
      setTableSorting(sort: Sort): void {
        this.lastSort = sort;
        function compare(a: number | string, b: number | string, isAsc: boolean) {
          if ((typeof a === 'string') && (typeof b === 'string')) {
            return a.localeCompare(b) * (isAsc ? 1 : -1);
          }
          return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
        }
        Object.keys(this.files).forEach(type => {
          this.files[type].data = this.files[type].data
            .sort((a, b) => compare(a[sort.active], b[sort.active], (sort.direction === 'asc')));
        });
      }