import { PrismaClient } from '@prisma/client'; import { ExcelReaderService } from './excel-reader'; import { BulkInserter } from './bulk-inserter'; import { ImportProgressServer } from './websocket-server'; import { ImportProgress, ImportResult, ProcessedSection } from './types'; export class ImportProcessor { private prisma: PrismaClient; private reader: ExcelReaderService; private inserter: BulkInserter; private progressServer: ImportProgressServer; constructor() { this.prisma = new PrismaClient(); this.reader = new ExcelReaderService(); this.inserter = new BulkInserter(); this.progressServer = ImportProgressServer.getInstance(); } async processImport(importId: number): Promise { try { // Get import record with layout configuration const importRecord = await this.prisma.import.findUnique({ where: { id: importId }, include: { layout: { include: { sections: { include: { fields: true } } } } } }); if (!importRecord) { throw new Error('Import not found'); } if (!importRecord.fileId) { throw new Error('No file attached to import'); } // Get the actual file const fileRecord = await this.prisma.file.findUnique({ where: { id: importRecord.fileId } }); if (!fileRecord) { throw new Error('File not found'); } // Initialize progress tracking const progress: ImportProgress = { importId, status: 'processing', currentSection: '', currentRow: 0, totalRows: 0, errors: [], processedSections: 0, totalSections: importRecord.layout?.sections?.length ?? 0 }; // Read Excel file const sections = await this.reader.readExcelFile( fileRecord.data, importRecord.layout, (sectionProgress: ImportProgress) => { this.progressServer.broadcastProgress(importId, sectionProgress); } ); // Process each section const processedSections: ProcessedSection[] = []; let totalInserted = 0; for (let i = 0; i < sections.length; i++) { const section = sections[i]; if (section) { progress.currentSection = section.name ?? ''; progress.processedSections = i + 1; this.progressServer.broadcastProgress(importId, progress); try { const insertedRows = await this.inserter.insertSectionData( section, importId, (rows: number) => { progress.currentRow = rows; this.progressServer.broadcastProgress(importId, progress); } ); processedSections.push({ sectionData: section, insertedRows }); totalInserted += insertedRows; } catch (error: unknown) { const errorMessage = `Error processing section ${section.name ?? 'unknown'}: ${error instanceof Error ? error.message : 'Unknown error'}`; progress.errors.push(errorMessage); this.progressServer.broadcastProgress(importId, progress); } } } // Import processing completed progress.status = 'completed'; this.progressServer.broadcastProgress(importId, progress); return { success: true, totalInserted, sections: processedSections }; } catch (error) { // Import processing failed const progress: ImportProgress = { importId, status: 'failed', currentSection: '', currentRow: 0, totalRows: 0, errors: [error instanceof Error ? error.message : 'Unknown error'], processedSections: 0, totalSections: 0 }; this.progressServer.broadcastProgress(importId, progress); return { success: false, totalInserted: 0, sections: [], errors: [error instanceof Error ? error.message : 'Unknown error'] }; } } async validateImport(importId: number): Promise<{ valid: boolean; errors: string[] }> { const errors: string[] = []; try { const importRecord = await this.prisma.import.findUnique({ where: { id: importId }, include: { layout: true } }); if (!importRecord) { errors.push('Import record not found'); return { valid: false, errors }; } if (!importRecord.fileId) { errors.push('No file attached to import'); } if (!importRecord.layout) { errors.push('No layout configuration found'); } return { valid: errors.length === 0, errors }; } catch (error) { errors.push(`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`); return { valid: false, errors }; } } }