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,303 @@
"""
Document Workflow Automation Models
This module provides automated document generation workflows triggered by case events,
deadlines, file status changes, and other system events.
"""
from sqlalchemy import Column, Integer, String, Text, ForeignKey, Boolean, JSON, DateTime, Date, Enum
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from enum import Enum as PyEnum
from typing import Dict, Any, List, Optional
import json
from app.models.base import BaseModel
class WorkflowTriggerType(PyEnum):
"""Types of events that can trigger workflows"""
FILE_STATUS_CHANGE = "file_status_change"
DEADLINE_APPROACHING = "deadline_approaching"
DEADLINE_OVERDUE = "deadline_overdue"
DEADLINE_COMPLETED = "deadline_completed"
PAYMENT_RECEIVED = "payment_received"
PAYMENT_OVERDUE = "payment_overdue"
FILE_OPENED = "file_opened"
FILE_CLOSED = "file_closed"
DOCUMENT_UPLOADED = "document_uploaded"
QDRO_STATUS_CHANGE = "qdro_status_change"
TIME_BASED = "time_based"
MANUAL_TRIGGER = "manual_trigger"
CUSTOM_EVENT = "custom_event"
class WorkflowStatus(PyEnum):
"""Workflow execution status"""
ACTIVE = "active"
INACTIVE = "inactive"
PAUSED = "paused"
ARCHIVED = "archived"
class WorkflowActionType(PyEnum):
"""Types of actions a workflow can perform"""
GENERATE_DOCUMENT = "generate_document"
SEND_EMAIL = "send_email"
CREATE_DEADLINE = "create_deadline"
UPDATE_FILE_STATUS = "update_file_status"
CREATE_LEDGER_ENTRY = "create_ledger_entry"
SEND_NOTIFICATION = "send_notification"
EXECUTE_CUSTOM = "execute_custom"
class ExecutionStatus(PyEnum):
"""Status of workflow execution"""
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
RETRYING = "retrying"
class DocumentWorkflow(BaseModel):
"""
Defines automated workflows for document generation and case management
"""
__tablename__ = "document_workflows"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
# Basic workflow information
name = Column(String(200), nullable=False, index=True)
description = Column(Text, nullable=True)
status = Column(Enum(WorkflowStatus), default=WorkflowStatus.ACTIVE, nullable=False)
# Trigger configuration
trigger_type = Column(Enum(WorkflowTriggerType), nullable=False, index=True)
trigger_conditions = Column(JSON, nullable=True) # JSON conditions for when to trigger
# Execution settings
delay_minutes = Column(Integer, default=0) # Delay before execution
max_retries = Column(Integer, default=3)
retry_delay_minutes = Column(Integer, default=30)
timeout_minutes = Column(Integer, default=60)
# Filtering conditions
file_type_filter = Column(JSON, nullable=True) # Array of file types to include
status_filter = Column(JSON, nullable=True) # Array of file statuses to include
attorney_filter = Column(JSON, nullable=True) # Array of attorney IDs to include
client_filter = Column(JSON, nullable=True) # Array of client IDs to include
# Schedule settings (for time-based triggers)
schedule_cron = Column(String(100), nullable=True) # Cron expression for scheduling
schedule_timezone = Column(String(50), default="UTC")
next_run_time = Column(DateTime(timezone=True), nullable=True)
# Priority and organization
priority = Column(Integer, default=5) # 1-10, higher = more important
category = Column(String(100), nullable=True, index=True)
tags = Column(JSON, nullable=True) # Array of tags for organization
# Metadata
created_by = Column(String(150), ForeignKey("users.username"), nullable=True)
created_at = Column(DateTime(timezone=True), default=func.now(), nullable=False)
updated_at = Column(DateTime(timezone=True), default=func.now(), onupdate=func.now())
last_triggered_at = Column(DateTime(timezone=True), nullable=True)
# Statistics
execution_count = Column(Integer, default=0)
success_count = Column(Integer, default=0)
failure_count = Column(Integer, default=0)
# Relationships
actions = relationship("WorkflowAction", back_populates="workflow", cascade="all, delete-orphan")
executions = relationship("WorkflowExecution", back_populates="workflow", cascade="all, delete-orphan")
def __repr__(self):
return f"<DocumentWorkflow(id={self.id}, name='{self.name}', trigger='{self.trigger_type.value}')>"
class WorkflowAction(BaseModel):
"""
Individual actions within a workflow
"""
__tablename__ = "workflow_actions"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
workflow_id = Column(Integer, ForeignKey("document_workflows.id"), nullable=False, index=True)
# Action configuration
action_type = Column(Enum(WorkflowActionType), nullable=False)
action_order = Column(Integer, default=1) # Order of execution within workflow
action_name = Column(String(200), nullable=True) # Optional descriptive name
# Action parameters (specific to action type)
parameters = Column(JSON, nullable=True)
# Document generation specific fields
template_id = Column(Integer, ForeignKey("document_templates.id"), nullable=True)
output_format = Column(String(50), default="DOCX") # DOCX, PDF, HTML
custom_filename_template = Column(String(500), nullable=True) # Template for filename
# Email action specific fields
email_template_id = Column(Integer, nullable=True) # Reference to email template
email_recipients = Column(JSON, nullable=True) # Array of recipient types/addresses
email_subject_template = Column(String(500), nullable=True)
# Conditional execution
condition = Column(JSON, nullable=True) # Conditions for this action to execute
continue_on_failure = Column(Boolean, default=False) # Whether to continue if this action fails
# Relationships
workflow = relationship("DocumentWorkflow", back_populates="actions")
template = relationship("DocumentTemplate")
def __repr__(self):
return f"<WorkflowAction(id={self.id}, workflow_id={self.workflow_id}, type='{self.action_type.value}')>"
class WorkflowExecution(BaseModel):
"""
Tracks individual workflow executions
"""
__tablename__ = "workflow_executions"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
workflow_id = Column(Integer, ForeignKey("document_workflows.id"), nullable=False, index=True)
# Execution context
triggered_by_event_id = Column(String(100), nullable=True, index=True) # Reference to triggering event
triggered_by_event_type = Column(String(50), nullable=True)
context_file_no = Column(String(45), nullable=True, index=True)
context_client_id = Column(String(80), nullable=True)
context_user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
# Execution details
status = Column(Enum(ExecutionStatus), default=ExecutionStatus.PENDING, nullable=False)
started_at = Column(DateTime(timezone=True), nullable=True)
completed_at = Column(DateTime(timezone=True), nullable=True)
# Input data and context
trigger_data = Column(JSON, nullable=True) # Data from the triggering event
execution_context = Column(JSON, nullable=True) # Variables and context for execution
# Results and outputs
generated_documents = Column(JSON, nullable=True) # Array of generated document info
action_results = Column(JSON, nullable=True) # Results from each action
error_message = Column(Text, nullable=True)
error_details = Column(JSON, nullable=True)
# Performance metrics
execution_duration_seconds = Column(Integer, nullable=True)
retry_count = Column(Integer, default=0)
next_retry_at = Column(DateTime(timezone=True), nullable=True)
# Relationships
workflow = relationship("DocumentWorkflow", back_populates="executions")
user = relationship("User")
def __repr__(self):
return f"<WorkflowExecution(id={self.id}, workflow_id={self.workflow_id}, status='{self.status.value}')>"
class WorkflowTemplate(BaseModel):
"""
Pre-defined workflow templates for common scenarios
"""
__tablename__ = "workflow_templates"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
# Template information
name = Column(String(200), nullable=False, unique=True)
description = Column(Text, nullable=True)
category = Column(String(100), nullable=True, index=True)
# Workflow definition
workflow_definition = Column(JSON, nullable=False) # Complete workflow configuration
# Metadata
created_by = Column(String(150), ForeignKey("users.username"), nullable=True)
created_at = Column(DateTime(timezone=True), default=func.now(), nullable=False)
is_system_template = Column(Boolean, default=False) # Built-in vs user-created
usage_count = Column(Integer, default=0) # How many times this template has been used
# Version control
version = Column(String(20), default="1.0.0")
template_tags = Column(JSON, nullable=True) # Tags for categorization
def __repr__(self):
return f"<WorkflowTemplate(id={self.id}, name='{self.name}', version='{self.version}')>"
class EventLog(BaseModel):
"""
Unified event log for workflow triggering
"""
__tablename__ = "event_log"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
# Event identification
event_id = Column(String(100), nullable=False, unique=True, index=True) # UUID
event_type = Column(String(50), nullable=False, index=True)
event_source = Column(String(100), nullable=False) # Which system/module generated the event
# Event context
file_no = Column(String(45), nullable=True, index=True)
client_id = Column(String(80), nullable=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
resource_type = Column(String(50), nullable=True) # deadline, file, payment, etc.
resource_id = Column(String(100), nullable=True)
# Event data
event_data = Column(JSON, nullable=True) # Event-specific data
previous_state = Column(JSON, nullable=True) # Previous state before event
new_state = Column(JSON, nullable=True) # New state after event
# Workflow processing
processed = Column(Boolean, default=False, index=True)
processed_at = Column(DateTime(timezone=True), nullable=True)
triggered_workflows = Column(JSON, nullable=True) # Array of workflow IDs triggered
processing_errors = Column(JSON, nullable=True)
# Timing
occurred_at = Column(DateTime(timezone=True), default=func.now(), nullable=False, index=True)
# Relationships
user = relationship("User")
def __repr__(self):
return f"<EventLog(id={self.id}, event_type='{self.event_type}', file_no='{self.file_no}')>"
class WorkflowSchedule(BaseModel):
"""
Scheduled workflow executions (for time-based triggers)
"""
__tablename__ = "workflow_schedules"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
workflow_id = Column(Integer, ForeignKey("document_workflows.id"), nullable=False, index=True)
# Schedule configuration
schedule_name = Column(String(200), nullable=True)
cron_expression = Column(String(100), nullable=False)
timezone = Column(String(50), default="UTC")
# Execution tracking
next_run_time = Column(DateTime(timezone=True), nullable=False, index=True)
last_run_time = Column(DateTime(timezone=True), nullable=True)
# Status
active = Column(Boolean, default=True, nullable=False)
# Metadata
created_at = Column(DateTime(timezone=True), default=func.now(), nullable=False)
# Relationships
workflow = relationship("DocumentWorkflow")
def __repr__(self):
return f"<WorkflowSchedule(id={self.id}, workflow_id={self.workflow_id}, next_run='{self.next_run_time}')>"