304 lines
12 KiB
Python
304 lines
12 KiB
Python
"""
|
|
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}')>"
|