OpenSlides/client/src/app/site/base/base-import-list.ts

293 lines
8.6 KiB
TypeScript
Raw Normal View History

import { OnInit, ViewChild } from '@angular/core';
import { MatSelectChange } from '@angular/material/select';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTable, MatTableDataSource } from '@angular/material/table';
2019-01-11 18:55:09 +01:00
import { Title } from '@angular/platform-browser';
2019-01-11 18:55:09 +01:00
import { TranslateService } from '@ngx-translate/core';
2019-02-25 13:29:20 +01:00
import { auditTime } from 'rxjs/operators';
2019-01-11 18:55:09 +01:00
import { BaseImportService, NewEntry, ValueLabelCombination } from 'app/core/ui-services/base-import.service';
import { getLongPreview, getShortPreview } from 'app/shared/utils/previewStrings';
import { BaseViewComponent } from './base-view';
import { BaseViewModel } from './base-view-model';
2019-01-11 18:55:09 +01:00
export abstract class BaseImportListComponent<V extends BaseViewModel> extends BaseViewComponent implements OnInit {
/**
* The data source for a table. Requires to be initialised with a BaseViewModel
*/
public dataSource: MatTableDataSource<NewEntry<V>>;
2019-02-25 15:34:11 +01:00
/**
* Helper function for previews
*/
public getLongPreview = getLongPreview;
/**
* Helper function for previews
*/
public getShortPreview = getShortPreview;
2019-01-11 18:55:09 +01:00
/**
* Switch that turns true if a file has been selected in the input
*/
public hasFile = false;
/**
* Currently selected encoding. Is set and changed by the config's available
* encodings and user mat-select input
*/
public selectedEncoding = 'utf-8';
/**
* indicator on which elements to display
*/
public shown: 'all' | 'error' | 'noerror' = 'all';
/**
* The table itself
*/
@ViewChild(MatTable, { static: false })
2019-01-11 18:55:09 +01:00
protected table: MatTable<NewEntry<V>>;
/**
* @returns the amount of total item successfully parsed
*/
public get totalCount(): number {
return this.importer && this.hasFile ? this.importer.summary.total : null;
}
/**
* @returns the encodings available and their labels
*/
public get encodings(): ValueLabelCombination[] {
return this.importer.encodings;
}
/**
* @returns the available column separators and their labels
*/
public get columnSeparators(): ValueLabelCombination[] {
return this.importer.columnSeparators;
}
/**
* @eturns the available text separators and their labels
*/
public get textSeparators(): ValueLabelCombination[] {
return this.importer.textSeparators;
}
/**
* @returns the amount of import items that will be imported
*/
public get newCount(): number {
return this.importer && this.hasFile ? this.importer.summary.new : 0;
}
/**
* @returns the number of import items that cannot be imported
*/
public get nonImportableCount(): number {
if (this.importer && this.hasFile) {
return this.importer.summary.errors + this.importer.summary.duplicates;
}
return 0;
}
/**
* @returns the number of import items that have been successfully imported
*/
public get doneCount(): number {
return this.importer && this.hasFile ? this.importer.summary.done : 0;
}
/**
* Constructor. Initializes the table and subscribes to import errors
*
* @param importer The import service, depending on the implementation
* @param titleService A title service
* @param translate TranslationService for translating strings
* @param matSnackBar MatSnackBar for displaying errors
*/
public constructor(
protected importer: BaseImportService<V>,
titleService: Title,
translate: TranslateService,
matSnackBar: MatSnackBar
) {
super(titleService, translate, matSnackBar);
this.initTable();
this.importer.errorEvent.subscribe(this.raiseError);
}
/**
* Starts with a clean preview (removing any previously existing import previews)
*/
public ngOnInit(): void {
this.importer.clearPreview();
}
/**
* Initializes the table
*/
public initTable(): void {
this.dataSource = new MatTableDataSource();
this.setFilter();
2019-02-25 13:29:20 +01:00
this.importer
.getNewEntries()
.pipe(auditTime(100))
.subscribe(newEntries => {
this.dataSource.data = [];
this.dataSource.data = newEntries;
this.hasFile = newEntries.length > 0;
});
2019-01-11 18:55:09 +01:00
}
/**
* Returns the table column definition. Fetches all headers from
* {@link MotionImportService} and an additional status column
*
* @returns An array of the columns forming the import header, and an additional 'status' bar on the front
*/
public getColumnDefinition(): string[] {
return ['status'].concat(this.importer.expectedHeader);
}
/**
* triggers the importer's onSelectFile after a file has been chosen
*/
public onSelectFile(event: any): void {
this.importer.onSelectFile(event);
}
/**
* Triggers the importer's import
*
*/
public async doImport(): Promise<void> {
await this.importer.doImport();
this.setFilter();
}
/**
* Updates and manually triggers the filter function.
* See {@link hidden} for options
* (changed from default mat-table filter)
*/
public setFilter(): void {
this.dataSource.filter = '';
if (this.shown === 'all') {
this.dataSource.filterPredicate = (data, filter) => {
return true;
};
} else if (this.shown === 'noerror') {
this.dataSource.filterPredicate = (data, filter) => {
if (data.status === 'done') {
return true;
} else if (data.status !== 'error') {
return true;
}
};
} else if (this.shown === 'error') {
this.dataSource.filterPredicate = (data, filter) => {
2019-07-17 16:13:49 +02:00
return !!data.errors.length || data.hasDuplicates;
2019-01-11 18:55:09 +01:00
};
}
this.dataSource.filter = 'X'; // TODO: This is just a bogus non-null string to trigger the filter
}
/**
* Get the appropiate css class for a row according to the import state
*
* @param row a newEntry object with a current status
* @returns a css class name
*/
public getStateClass(row: NewEntry<V>): string {
switch (row.status) {
case 'done':
return 'import-done import-decided';
case 'error':
return 'import-error';
default:
return '';
}
}
/**
* Get the icon for the action of the item
* @param entry a newEntry object with a current status
* @eturn the icon for the action of the item
*/
public getActionIcon(entry: NewEntry<V>): string {
switch (entry.status) {
case 'error': // no import possible
return 'block';
case 'new':
return '';
case 'done': // item has been imported
return 'done';
default:
// fallback: Error
return 'block';
}
}
/**
* A function to trigger the csv example download.
*/
public abstract downloadCsvExample(): void;
/**
* Trigger for the column separator selection.
*
* @param event
*/
public selectColSep(event: MatSelectChange): void {
this.importer.columnSeparator = event.value;
this.importer.refreshFile();
}
/**
* Trigger for the column separator selection
*
* @param event
*/
public selectTextSep(event: MatSelectChange): void {
this.importer.textSeparator = event.value;
this.importer.refreshFile();
}
/**
* Trigger for the encoding selection
*
* @param event
*/
public selectEncoding(event: MatSelectChange): void {
this.importer.encoding = event.value;
this.importer.refreshFile();
}
/**
* Returns a descriptive string for an import error
*
* @param error The short string for the error as listed in the {@lilnk errorList}
* @returns a predefined descriptive error string from the importer
*/
public getVerboseError(error: string): string {
return this.importer.verbose(error);
}
/**
* Checks if an error is present in a new entry
*
* @param row the NewEntry
* @param error An error as defined as key of {@link errorList}
* @returns true if the error is present in the entry described in the row
*/
public hasError(row: NewEntry<V>, error: string): boolean {
return this.importer.hasError(row, error);
}
}