import Papa from 'papaparse' import HtmlEntities from 'he' import unraw from 'unraw' import {parseChunks} from './fluxParsing' import { CSV_OBJECT_BASE_NAME, CSV_OBJECT_START_STRING, CSV_OBJECT_END_STRING, } from '../constants/rawFluxData' export interface ParseFilesResult { data: string[][] maxColumnCount: number } export const parseFiles = (responses: string[]): ParseFilesResult => { const chunks = parseChunks(responses.join('\n\n')) const parsedChunks = chunks.map(c => Papa.parse(c).data) const maxColumnCount = Math.max(...parsedChunks.map(c => c[0].length)) const data = [] for (let i = 0; i < parsedChunks.length; i++) { if (i !== 0) { // Seperate each chunk by an empty line, just like in the unparsed CSV data.push([]) } for (let j = 0; j < parsedChunks[i].length; j++) { // Danger zone! Since the contents of each chunk are potentially quite // large, the contents need to be concated using a loop rather than with // `concat`, a splat or similar. Otherwise we see a "Maximum call size // exceeded" error for large CSVs data.push(parsedChunks[i][j]) } } return {data, maxColumnCount} } export const parseFilesWithObjects = ( responses: string[] ): ParseFilesResult => { const chunks = parseChunks(responses.join('\n\n')) const parsedChunks = chunks.map(c => { const objList = [] let updatedString = c let startIndex = 0 let endIndex = 0 /* ******************************************************************** * Parse objects in the CSV by decoding the html and removing * the following inside the objects, so that the stringified version is * parsable by JSON.parse and becomes useful to the user: * - double double-quotes * - escaped double-quotes * - newlines * - tabs * - escaped null */ while (startIndex !== -1 && endIndex !== -1) { startIndex = updatedString.indexOf(CSV_OBJECT_START_STRING) endIndex = updatedString.indexOf(CSV_OBJECT_END_STRING) if (startIndex !== -1 && endIndex !== -1) { objList.push( unraw( HtmlEntities.decode( updatedString.slice( startIndex + CSV_OBJECT_START_STRING.length - 1, endIndex + 1 ) ) .replace(/""/g, '"') .replace(/:",/g, ':"",') .replace(/"","}/g, '","}') .replace(/\\"/g, '') .replace(/\\n/g, ' ') .replace(/\\t/g, '') .replace(/\\u0000/g, '') ) ) updatedString = updatedString.slice(0, startIndex + 1) + `${CSV_OBJECT_BASE_NAME}${objList.length - 1}` + updatedString.slice(endIndex + CSV_OBJECT_END_STRING.length - 1) } } const {data} = Papa.parse(updatedString) // After we parse the CSV, if we have a list of parsed objects // go back and re-insert them let iterator = 0 objList.forEach((obj, objIndex) => { let dataIndex = -1 while (iterator < data.length && dataIndex === -1) { dataIndex = data[iterator].findIndex( element => element === `${CSV_OBJECT_BASE_NAME}${objIndex}` ) if (dataIndex > -1) { data[iterator][dataIndex] = data[iterator][dataIndex].replace( `${CSV_OBJECT_BASE_NAME}${objIndex}`, obj ) } else { iterator += 1 } } }) return data }) const maxColumnCount = Math.max(...parsedChunks.map(c => c[0].length)) const data = [] for (let i = 0; i < parsedChunks.length; i++) { if (i !== 0) { // Separate each chunk by an empty line, just like in the unparsed CSV data.push([]) } for (let j = 0; j < parsedChunks[i].length; j++) { // Danger zone! Since the contents of each chunk are potentially quite // large, the contents need to be concated using a loop rather than with // `concat`, a splat or similar. Otherwise we see a "Maximum call size // exceeded" error for large CSVs data.push(parsedChunks[i][j]) } } return {data, maxColumnCount} }