# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ CSV code renderer for code generation. """ from .codeRendererBaseTemplate import BaseCodeRenderer from modules.datamodels.datamodelDocument import RenderedDocument from typing import Dict, Any, List, Optional import csv import io class RendererCodeCsv(BaseCodeRenderer): """Renders CSV code files.""" @classmethod def getSupportedFormats(cls) -> List[str]: """Return supported CSV formats.""" return ['csv'] @classmethod def getFormatAliases(cls) -> List[str]: """Return format aliases.""" return [] @classmethod def getPriority(cls) -> int: """Return priority for CSV code renderer.""" return 75 # Higher than document renderer (70) for code generation @classmethod def getOutputStyle(cls, formatName: Optional[str] = None) -> str: """Return output style classification: CSV requires specific structure.""" return 'code' async def renderCodeFiles( self, codeFiles: List[Dict[str, Any]], metadata: Dict[str, Any], userPrompt: str = None ) -> List[RenderedDocument]: """ Render CSV code files. For single file: output as-is (validate structure) For multiple files: output separately (each is independent CSV) """ renderedDocs = [] for codeFile in codeFiles: if not self._validateCodeFile(codeFile): self.logger.warning(f"Invalid code file: {codeFile.get('filename', 'unknown')}") continue filename = codeFile['filename'] content = codeFile['content'] # Validate CSV structure (header row, consistent columns) validatedContent = self._validateAndFixCsv(content) renderedDocs.append( RenderedDocument( documentData=validatedContent.encode('utf-8'), mimeType="text/csv", filename=filename, metadata=metadata ) ) return renderedDocs async def render(self, extractedContent: Dict[str, Any], title: str, userPrompt: str = None, aiService=None) -> List[RenderedDocument]: """ Render method for document generation compatibility. Delegates to document renderer if needed, or handles code files directly. """ # Check if this is code generation (has files array) or document generation (has documents array) if "files" in extractedContent: # Code generation path - use renderCodeFiles files = extractedContent.get("files", []) metadata = extractedContent.get("metadata", {}) return await self.renderCodeFiles(files, metadata, userPrompt) else: # Document generation path - delegate to document renderer from .rendererCsv import RendererCsv documentRenderer = RendererCsv(self.services) return await documentRenderer.render(extractedContent, title, userPrompt, aiService) def _validateAndFixCsv(self, content: str) -> str: """Validate CSV structure and fix common issues.""" try: # Parse CSV to validate structure reader = csv.reader(io.StringIO(content)) rows = list(reader) if not rows: return content # Empty CSV # Check header row exists headerRow = rows[0] headerCount = len(headerRow) # Validate all rows have same column count fixedRows = [headerRow] # Start with header for i, row in enumerate(rows[1:], 1): if len(row) != headerCount: self.logger.warning(f"Row {i} has {len(row)} columns, expected {headerCount}. Fixing...") # Pad or truncate to match header if len(row) < headerCount: row.extend([''] * (headerCount - len(row))) else: row = row[:headerCount] fixedRows.append(row) # Convert back to CSV string output = io.StringIO() writer = csv.writer(output) for row in fixedRows: writer.writerow(row) return output.getvalue() except Exception as e: self.logger.warning(f"CSV validation failed: {e}, returning original content") return content