This commit is contained in:
HotSwapp
2025-08-18 20:20:04 -05:00
parent 89b2bc0aa2
commit bac8cc4bd5
114 changed files with 30258 additions and 1341 deletions

View File

@@ -0,0 +1,519 @@
"""
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)