# Copyright (c) 2025 Patrick Motsch # All rights reserved. import logging import json import base64 import pandas as pd import csv as csv_module from io import BytesIO from datetime import datetime, UTC from typing import Dict, Any from modules.datamodels.datamodelChatbot import ActionResult, ActionDocument logger = logging.getLogger(__name__) async def createExcelContent(self, parameters: Dict[str, Any]) -> ActionResult: try: dataParam = parameters.get("data") if not dataParam: return ActionResult.isFailure(error="data parameter is required") headersParam = parameters.get("headers") columnsParam = parameters.get("columns") taskSyncDefinitionParam = parameters.get("taskSyncDefinition") # Get data from document dataJson = self.documentParsing.parseJsonFromDocument(dataParam) if dataJson is None: return ActionResult.isFailure(error="Could not parse data from document reference") # Extract data array if wrapped in object if isinstance(dataJson, dict) and "data" in dataJson: dataList = dataJson["data"] elif isinstance(dataJson, list): dataList = dataJson else: return ActionResult.isFailure(error="Data must be a JSON array or object with 'data' field") # Get headers headers = {"header1": "Header 1", "header2": "Header 2"} if headersParam: headersJson = self.documentParsing.parseJsonFromDocument(headersParam) if headersJson and isinstance(headersJson, dict) and "headers" in headersJson: headers = headersJson["headers"] elif headersJson and isinstance(headersJson, dict): headers = headersJson # Get columns if columnsParam: if isinstance(columnsParam, str): try: columns = json.loads(columnsParam) if columnsParam.startswith('[') or columnsParam.startswith('{') else columnsParam.split(',') except: columns = columnsParam.split(',') elif isinstance(columnsParam, list): columns = columnsParam else: columns = None elif taskSyncDefinitionParam: # Extract columns from taskSyncDefinition if isinstance(taskSyncDefinitionParam, str): taskSyncDefinition = json.loads(taskSyncDefinitionParam) else: taskSyncDefinition = taskSyncDefinitionParam columns = list(taskSyncDefinition.keys()) elif dataList and len(dataList) > 0: columns = list(dataList[0].keys()) else: columns = [] # Create DataFrame if not dataList: df = pd.DataFrame(columns=columns) else: df = pd.DataFrame(dataList) # Ensure all columns exist for col in columns: if col not in df.columns: df[col] = "" # Reorder columns df = df[columns] # Clean data for column in df.columns: df[column] = df[column].astype("object").fillna("") df[column] = df[column].astype(str).str.replace('\n', '\\n', regex=False).str.replace('"', '""', regex=False) # Create headers with timestamp timestamp = datetime.fromtimestamp(self.services.utils.timestampGetUtc(), UTC).strftime("%Y-%m-%d %H:%M:%S UTC") header1Row = next(csv_module.reader([headers.get("header1", "Header 1")]), []) header2Row = next(csv_module.reader([headers.get("header2", "Header 2")]), []) if len(header2Row) > 1: header2Row[1] = timestamp headerRow1 = pd.DataFrame([header1Row + [""] * (len(df.columns) - len(header1Row))], columns=df.columns) headerRow2 = pd.DataFrame([header2Row + [""] * (len(df.columns) - len(header2Row))], columns=df.columns) tableHeaders = pd.DataFrame([df.columns.tolist()], columns=df.columns) finalDf = pd.concat([headerRow1, headerRow2, tableHeaders, df], ignore_index=True) # Convert to Excel bytes buf = BytesIO() finalDf.to_excel(buf, index=False, header=False, engine='openpyxl') excelBytes = buf.getvalue() logger.info(f"Created Excel content: {len(dataList)} rows, {len(columns)} columns") # Generate filename workflowContext = self.services.chat.getWorkflowContext() if hasattr(self.services, 'chat') else None filename = self._generateMeaningfulFileName( "ticket_sync", "xlsx", workflowContext, "createExcelContent" ) validationMetadata = self._createValidationMetadata( "createExcelContent", rowCount=len(dataList), columnCount=len(columns) ) # Store as base64 for document excelBase64 = base64.b64encode(excelBytes).decode('utf-8') document = ActionDocument( documentName=filename, documentData=excelBase64, mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", validationMetadata=validationMetadata ) return ActionResult.isSuccess(documents=[document]) except Exception as e: errorMsg = f"Error creating Excel content: {str(e)}" logger.error(errorMsg) return ActionResult.isFailure(error=errorMsg)