""" Workflow Integration Service This service provides integration points for automatically logging events and triggering workflows from existing system operations. """ from __future__ import annotations import asyncio from datetime import datetime, timezone from typing import Dict, Any, Optional import logging from sqlalchemy.orm import Session from app.services.workflow_engine import EventProcessor from app.core.logging import get_logger logger = get_logger("workflow_integration") class WorkflowIntegration: """ Helper service for integrating workflow automation with existing systems """ def __init__(self, db: Session): self.db = db self.event_processor = EventProcessor(db) async def log_file_status_change( self, file_no: str, old_status: str, new_status: str, user_id: Optional[int] = None, notes: Optional[str] = None ): """ Log a file status change event that may trigger workflows """ try: event_data = { 'old_status': old_status, 'new_status': new_status, 'notes': notes } previous_state = {'status': old_status} new_state = {'status': new_status} await self.event_processor.log_event( event_type="file_status_change", event_source="file_management", file_no=file_no, user_id=user_id, resource_type="file", resource_id=file_no, event_data=event_data, previous_state=previous_state, new_state=new_state ) # Log specific status events if new_status == "CLOSED": await self.log_file_closed(file_no, user_id) elif old_status in ["INACTIVE", "CLOSED"] and new_status == "ACTIVE": await self.log_file_reopened(file_no, user_id) except Exception as e: logger.error(f"Error logging file status change for {file_no}: {str(e)}") async def log_file_opened( self, file_no: str, file_type: str, client_id: str, attorney: str, user_id: Optional[int] = None ): """ Log a new file opening event """ try: event_data = { 'file_type': file_type, 'client_id': client_id, 'attorney': attorney } await self.event_processor.log_event( event_type="file_opened", event_source="file_management", file_no=file_no, client_id=client_id, user_id=user_id, resource_type="file", resource_id=file_no, event_data=event_data ) except Exception as e: logger.error(f"Error logging file opened for {file_no}: {str(e)}") async def log_file_closed( self, file_no: str, user_id: Optional[int] = None, final_balance: Optional[float] = None ): """ Log a file closure event """ try: event_data = { 'closed_by_user_id': user_id, 'final_balance': final_balance } await self.event_processor.log_event( event_type="file_closed", event_source="file_management", file_no=file_no, user_id=user_id, resource_type="file", resource_id=file_no, event_data=event_data ) except Exception as e: logger.error(f"Error logging file closed for {file_no}: {str(e)}") async def log_file_reopened( self, file_no: str, user_id: Optional[int] = None, reason: Optional[str] = None ): """ Log a file reopening event """ try: event_data = { 'reopened_by_user_id': user_id, 'reason': reason } await self.event_processor.log_event( event_type="file_reopened", event_source="file_management", file_no=file_no, user_id=user_id, resource_type="file", resource_id=file_no, event_data=event_data ) except Exception as e: logger.error(f"Error logging file reopened for {file_no}: {str(e)}") async def log_deadline_approaching( self, deadline_id: int, file_no: Optional[str] = None, client_id: Optional[str] = None, days_until_deadline: int = 0, deadline_type: Optional[str] = None ): """ Log a deadline approaching event """ try: event_data = { 'deadline_id': deadline_id, 'days_until_deadline': days_until_deadline, 'deadline_type': deadline_type } await self.event_processor.log_event( event_type="deadline_approaching", event_source="deadline_management", file_no=file_no, client_id=client_id, resource_type="deadline", resource_id=str(deadline_id), event_data=event_data ) except Exception as e: logger.error(f"Error logging deadline approaching for {deadline_id}: {str(e)}") async def log_deadline_overdue( self, deadline_id: int, file_no: Optional[str] = None, client_id: Optional[str] = None, days_overdue: int = 0, deadline_type: Optional[str] = None ): """ Log a deadline overdue event """ try: event_data = { 'deadline_id': deadline_id, 'days_overdue': days_overdue, 'deadline_type': deadline_type } await self.event_processor.log_event( event_type="deadline_overdue", event_source="deadline_management", file_no=file_no, client_id=client_id, resource_type="deadline", resource_id=str(deadline_id), event_data=event_data ) except Exception as e: logger.error(f"Error logging deadline overdue for {deadline_id}: {str(e)}") async def log_deadline_completed( self, deadline_id: int, file_no: Optional[str] = None, client_id: Optional[str] = None, completed_by_user_id: Optional[int] = None, completion_notes: Optional[str] = None ): """ Log a deadline completion event """ try: event_data = { 'deadline_id': deadline_id, 'completed_by_user_id': completed_by_user_id, 'completion_notes': completion_notes } await self.event_processor.log_event( event_type="deadline_completed", event_source="deadline_management", file_no=file_no, client_id=client_id, user_id=completed_by_user_id, resource_type="deadline", resource_id=str(deadline_id), event_data=event_data ) except Exception as e: logger.error(f"Error logging deadline completed for {deadline_id}: {str(e)}") async def log_payment_received( self, file_no: str, amount: float, payment_type: str, payment_date: Optional[datetime] = None, user_id: Optional[int] = None ): """ Log a payment received event """ try: event_data = { 'amount': amount, 'payment_type': payment_type, 'payment_date': payment_date.isoformat() if payment_date else None } await self.event_processor.log_event( event_type="payment_received", event_source="billing", file_no=file_no, user_id=user_id, resource_type="payment", resource_id=f"{file_no}_{datetime.now().isoformat()}", event_data=event_data ) except Exception as e: logger.error(f"Error logging payment received for {file_no}: {str(e)}") async def log_payment_overdue( self, file_no: str, amount_due: float, days_overdue: int, invoice_date: Optional[datetime] = None ): """ Log a payment overdue event """ try: event_data = { 'amount_due': amount_due, 'days_overdue': days_overdue, 'invoice_date': invoice_date.isoformat() if invoice_date else None } await self.event_processor.log_event( event_type="payment_overdue", event_source="billing", file_no=file_no, resource_type="invoice", resource_id=f"{file_no}_overdue", event_data=event_data ) except Exception as e: logger.error(f"Error logging payment overdue for {file_no}: {str(e)}") async def log_document_uploaded( self, file_no: str, document_id: int, filename: str, document_type: Optional[str] = None, user_id: Optional[int] = None ): """ Log a document upload event """ try: event_data = { 'document_id': document_id, 'filename': filename, 'document_type': document_type, 'uploaded_by_user_id': user_id } await self.event_processor.log_event( event_type="document_uploaded", event_source="document_management", file_no=file_no, user_id=user_id, resource_type="document", resource_id=str(document_id), event_data=event_data ) except Exception as e: logger.error(f"Error logging document uploaded for {file_no}: {str(e)}") async def log_qdro_status_change( self, qdro_id: int, file_no: str, old_status: str, new_status: str, user_id: Optional[int] = None ): """ Log a QDRO status change event """ try: event_data = { 'qdro_id': qdro_id, 'old_status': old_status, 'new_status': new_status } previous_state = {'status': old_status} new_state = {'status': new_status} await self.event_processor.log_event( event_type="qdro_status_change", event_source="qdro_management", file_no=file_no, user_id=user_id, resource_type="qdro", resource_id=str(qdro_id), event_data=event_data, previous_state=previous_state, new_state=new_state ) except Exception as e: logger.error(f"Error logging QDRO status change for {qdro_id}: {str(e)}") async def log_custom_event( self, event_type: str, event_source: str, file_no: Optional[str] = None, client_id: Optional[str] = None, user_id: Optional[int] = None, resource_type: Optional[str] = None, resource_id: Optional[str] = None, event_data: Optional[Dict[str, Any]] = None ): """ Log a custom event """ try: await self.event_processor.log_event( event_type=event_type, event_source=event_source, file_no=file_no, client_id=client_id, user_id=user_id, resource_type=resource_type, resource_id=resource_id, event_data=event_data or {} ) except Exception as e: logger.error(f"Error logging custom event {event_type}: {str(e)}") # Helper functions for easy integration def create_workflow_integration(db: Session) -> WorkflowIntegration: """ Create a workflow integration instance """ return WorkflowIntegration(db) def run_async_event_logging(coro): """ Helper to run async event logging in sync contexts """ try: # Try to get the current event loop loop = asyncio.get_event_loop() if loop.is_running(): # If loop is running, schedule the coroutine asyncio.create_task(coro) else: # If no loop is running, run the coroutine loop.run_until_complete(coro) except RuntimeError: # No event loop, create a new one asyncio.run(coro) except Exception as e: logger.error(f"Error running async event logging: {str(e)}") # Sync wrapper functions for easy integration with existing code def log_file_status_change_sync( db: Session, file_no: str, old_status: str, new_status: str, user_id: Optional[int] = None, notes: Optional[str] = None ): """ Synchronous wrapper for file status change logging """ integration = create_workflow_integration(db) coro = integration.log_file_status_change(file_no, old_status, new_status, user_id, notes) run_async_event_logging(coro) def log_file_opened_sync( db: Session, file_no: str, file_type: str, client_id: str, attorney: str, user_id: Optional[int] = None ): """ Synchronous wrapper for file opened logging """ integration = create_workflow_integration(db) coro = integration.log_file_opened(file_no, file_type, client_id, attorney, user_id) run_async_event_logging(coro) def log_deadline_approaching_sync( db: Session, deadline_id: int, file_no: Optional[str] = None, client_id: Optional[str] = None, days_until_deadline: int = 0, deadline_type: Optional[str] = None ): """ Synchronous wrapper for deadline approaching logging """ integration = create_workflow_integration(db) coro = integration.log_deadline_approaching(deadline_id, file_no, client_id, days_until_deadline, deadline_type) run_async_event_logging(coro) def log_payment_received_sync( db: Session, file_no: str, amount: float, payment_type: str, payment_date: Optional[datetime] = None, user_id: Optional[int] = None ): """ Synchronous wrapper for payment received logging """ integration = create_workflow_integration(db) coro = integration.log_payment_received(file_no, amount, payment_type, payment_date, user_id) run_async_event_logging(coro) def log_document_uploaded_sync( db: Session, file_no: str, document_id: int, filename: str, document_type: Optional[str] = None, user_id: Optional[int] = None ): """ Synchronous wrapper for document uploaded logging """ integration = create_workflow_integration(db) coro = integration.log_document_uploaded(file_no, document_id, filename, document_type, user_id) run_async_event_logging(coro)