diff --git a/src/main/services/excel-converter.service.ts b/src/main/services/excel-converter.service.ts new file mode 100644 index 0000000..b09024d --- /dev/null +++ b/src/main/services/excel-converter.service.ts @@ -0,0 +1,125 @@ +import ExcelJS from 'exceljs'; +import path from 'path'; +import { LoggerService } from './logger.service'; + +export class ExcelConverterService { + constructor(private verbose: boolean = true) {} + + async convert(inputPath: string, outputPath?: string): Promise { + try { + // Validate input file + const fs = await import('fs/promises'); + + // Check file exists + try { + await fs.access(inputPath, fs.constants.R_OK); + } catch { + throw new Error(`File not found: ${inputPath}`); + } + + // Check file extension + if (!inputPath.match(/\.(xlsx|xls)$/i)) { + throw new Error(`Invalid file type. Expected .xlsx or .xls file: ${inputPath}`); + } + + LoggerService.info(`Converting Excel file: ${inputPath}`); + + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.readFile(inputPath); + + const worksheet = workbook.worksheets[0]; + if (!worksheet) { + throw new Error('No worksheet found in Excel file'); + } + + const data: any[] = []; + const headers: string[] = []; + + // Extract headers + const headerRow = worksheet.getRow(1); + headerRow.eachCell((cell, colNumber) => { + headers[colNumber - 1] = cell.text; + }); + + // Extract data rows + worksheet.eachRow((row, rowNumber) => { + if (rowNumber === 1) return; // Skip header row + + const rowData: any = {}; + row.eachCell((cell, colNumber) => { + const header = headers[colNumber - 1]; + if (header) { + // Extract actual value from cell (handles formulas, rich text, etc.) + const value = cell.value; + const extractedValue = typeof value === 'object' && value !== null + ? (value as any).result || (value as any).text || value + : value; + rowData[header] = extractedValue; + } + }); + data.push(rowData); + }); + + // Validate we got data + if (data.length === 0) { + LoggerService.warn('Excel file contains no data rows'); + } + + LoggerService.info(`Extracted ${data.length} rows from Excel file`); + + // Save to output path if provided + if (outputPath) { + await this.saveAsJson(data, outputPath); + } + + return data; + } catch (error) { + LoggerService.error('Excel conversion failed', error); + throw error; + } + } + + async mergeExcelFiles(filePaths: string[], outputPath: string): Promise { + try { + LoggerService.info(`Merging ${filePaths.length} Excel files...`); + + const allData: any[] = []; + + for (const filePath of filePaths) { + const data = await this.convert(filePath); + allData.push(...data); + } + + // Create new Excel workbook with merged data + const workbook = new ExcelJS.Workbook(); + const worksheet = workbook.addWorksheet('Merged Data'); + + if (allData.length > 0) { + const headers = Object.keys(allData[0]); + worksheet.addRow(headers); + + allData.forEach((row) => { + const values = headers.map((header) => row[header]); + worksheet.addRow(values); + }); + } + + await workbook.xlsx.writeFile(outputPath); + LoggerService.info(`Merged data saved to: ${outputPath}`); + } catch (error) { + LoggerService.error('Excel merge failed', error); + throw error; + } + } + + private async saveAsJson(data: any[], outputPath: string): Promise { + try { + const fs = await import('fs/promises'); + await fs.writeFile(outputPath, JSON.stringify(data, null, 2), 'utf-8'); + LoggerService.info(`Data saved to JSON: ${outputPath}`); + } catch (error) { + LoggerService.error(`Failed to save JSON to ${outputPath}`, error); + throw new Error(`Failed to save JSON output: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } +}