Closed AlexKirilov closed 3 years ago
Can you post a small service or component to reproduce the error? (remove everything not necessary to reproduce it)
Thanks
import { Component, Input } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms';
import { PortfolioEntry } from 'client/model/portfolioEntry'; import { IFileUpload } from '../../../shared/models/interfaces'; import { NoIdentifier, WrongFileFormate, NoRequiredHeaders, RequiredHeadersHelper } from '../../models/default-values';
export interface IsValid { passed: boolean; fileName?: string, errorMessage: string; }
@Component({ selector: 'csv-upload', templateUrl: './csv-upload.component.html', styleUrls: ['./csv-upload.component.scss'], }) export class CSVUploadComponent { @Input() title: string; @Input() sampleFileHref: string; @Input() requiredColumns: string[]; @Input() extraConstraint: string; @Input() checkParentValidation: (value: [string[], string[]]) => IsValid;
public files: IFileUpload[] = []; public cvsData: PortfolioEntry[] = []; public warningMessage: string; public fileName: string = '';
public form: FormGroup = new FormGroup({ file: new FormControl(), });
private uploadFiles: any; private supportedFileTypes: string[] = [ 'text/csv', 'text/x-csv', 'application/vnd.ms-excel', 'application/csv', 'application/x-csv', 'text/comma-separated-values', 'text/x-comma-separated-values', 'text/tab-separated-values', ];
private supportedExtensions: string[] = ['.csv'];
public fileBrowseHandler(files: any, input: any) { this.warningMessage = '';
if (this.checkFileType(files)) {
this.convertFile(input);
this.uploadFiles = files;
} else {
this.warningMessage = WrongFileFormate;
this.form.get('file').reset();
}
}
public deleteFile(index: number) { this.files.splice(index, 1); }
private checkFileType(files: Partial
private getFileExtension(fileName: string): string { const lastPeriod = fileName.lastIndexOf('.'); if (lastPeriod === -1) return ''; // No extension, returning an empty string will mean it won't get matched to supported list return fileName.substring(lastPeriod); // return rest of file name (should be extension) }
/**
Possible for multiple file upload */ private convertFile(input: any) { if (input.files && input.files[0]) { let files = input.files; let promises = [];
// loop through upload files to be read, set file content as string // and store promises in array for (var i = 0; i < files.length; i++) { let file = files[i]; let filePromise = new Promise((resolve) => { let reader = new FileReader(); reader.readAsText(file); reader.onload = () => resolve(reader.result as string); }); promises.push(filePromise); }
// waits till all files have been read Promise.all(promises) .then((fileContents: string[]) => { let fileText: string = fileContents[0];
// if there is more than one file, loop through the remaining files
// removing their csv headers before joining all the content together.
if (fileContents.length > 1) {
for (let i = 1; i < fileContents.length; i++) {
const data = fileContents[i].split('\n') as string[];
const lines = data.filter((_, index) => index !== 0);
fileText += lines.join('\n');
}
}
// pass all files as a string to be validate correctly
this.validateCSV(fileText);
})
.catch((error) => {
this.warningMessage = `File upload error ${error.message ? `: \n ${error.message}` : '.'
}.`;
console.error(error.message);
});
} }
/**
Run validation checks on the CSV data. */ private validateCSV(csvText?: string) { const data = csvText.split('\n') as string[]; const headers: string[] = this.convertCSVHeadersToArray(data[0]); const lines = data.filter((_, index) => index !== 0); // remove the header at index 0
if (!this.checkFileHasRequiredHeaders(headers)) {
this.warningMessage = ${NoRequiredHeaders} \n ${RequiredHeadersHelper} a ${this.requiredColumns.join( ' and ', )} column.
;
return;
}
if (!this.checkRowHasIdentifyData(headers, lines)) { this.warningMessage = NoIdentifier; return; }
const { passed, errorMessage } = this.checkParentValidation([lines, headers]);
if (!passed) { this.warningMessage = errorMessage; return; }
// passed all validation this.convertCSVtoJSON(lines, headers); this.prepareFilesList(this.uploadFiles);
// reset control this.form.get('file').reset(); }
/**
Check each row / company as at least one of the required identify filled. */ private checkRowHasIdentifyData(headers: string[], lines: string[]): boolean { const tickerIndex = headers.findIndex( (item) => item === 'tickercode' || item === 'ticker', ); const sedolIndex = headers.findIndex((item) => item === 'sedol'); const isinIndex = headers.findIndex((item) => item === 'isin');
const checkEveryCompany = (line) => { const currentLine = line.split(','); const hasNoIdentify = currentLine[tickerIndex] === '' && currentLine[sedolIndex] === '' && currentLine[isinIndex] === '';
return !hasNoIdentify; }; return lines.every(checkEveryCompany); }
/**
all of the other required columns. */ private checkFileHasRequiredHeaders(headers: string[]): boolean { let hasKeyIdentifer = false; headers.forEach((val) => { if (IDENTIFERS.includes(val)) hasKeyIdentifer = true; }); let hasShareValue = this.requiredColumns.every((val) => headers.includes(val), ); return hasKeyIdentifer && hasShareValue; }
private convertCSVHeadersToArray(csvHeader: string): string[] { return csvHeader .trim() .split(',') .map((w) => w.toLowerCase()); }
private convertCSVtoJSON(lines: string[], headers: string[]) { let entry: PortfolioEntry[] = []; for (let i = 0; i < lines.length; i++) { // Ignore empty lines. Some CSV files can have a trailing line with no data. if (lines[i].trim() === ',,,,' || !lines[i]) { continue; }
let obj: PortfolioEntry = {}; const currentLine = lines[i].split(','); for (let j = 0; j < headers.length; j++) { let header = headers[j].trim(); // skip non required columns if (!this.checkIfRequiredColumns(header)) { continue; } else { if (header === 'shares' || header === 'value') { let num = currentLine[j] ? currentLine[j].replace('"', '') : '0'; obj[header] = parseFloat(num); } else if (header === 'ticker' || header === 'tickercode') { // set ticker to correct key name for api obj.tickerCode = currentLine[j]; } else { obj[header] = currentLine[j]; } } }
entry = [...entry, obj]; }
this.cvsData = [...this.cvsData, ...entry]; }
private checkIfRequiredColumns(header: string): boolean { return IDENTIFERS.includes(header) || this.requiredColumns.includes(header); }
private uploadFilesSimulator(index: number): void { setTimeout(() => { if (index === this.files.length) { return; } else { const progressInterval = setInterval(() => { // in case item gets deleted during progress interval if (index === this.files.length || this.files.length === 0) { return; } else if (this.files[index].progress === 100) { clearInterval(progressInterval); this.uploadFilesSimulator(index + 1); } else { this.files[index].progress += 20; if (index === this.files.length || this.files.length === 0) { return; } } }, 200); } }, 500); }
/**
export const IDENTIFERS: string[] = ['isin', 'sedol', 'tickercode', 'ticker'];
Please update your SimonTest extension (to v1.9.10).
Thanks
I am using the SimonTest (Extension) for VS Code. For some of the services or components, when I use the extension, it throws me the error "Cannot read property 'text' of undefined" Tried to research it, but I could not find a solution or why it could throw me that issue. Is it possible for the next version to put some warnings which will be thrown and checked more easily for the specific issue or place!?
Thank you!