Files
delphi-database/app/models/deadlines.py
HotSwapp bac8cc4bd5 changes
2025-08-18 20:20:04 -05:00

272 lines
10 KiB
Python

"""
Deadline management models for legal practice deadlines and court dates
"""
from sqlalchemy import Column, Integer, String, DateTime, Date, Text, ForeignKey, Boolean, Enum
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from enum import Enum as PyEnum
from app.models.base import BaseModel
class DeadlineType(PyEnum):
"""Types of deadlines in legal practice"""
COURT_FILING = "court_filing"
COURT_HEARING = "court_hearing"
DISCOVERY = "discovery"
STATUTE_OF_LIMITATIONS = "statute_of_limitations"
CONTRACT = "contract"
ADMINISTRATIVE = "administrative"
CLIENT_MEETING = "client_meeting"
INTERNAL = "internal"
OTHER = "other"
class DeadlinePriority(PyEnum):
"""Priority levels for deadlines"""
CRITICAL = "critical" # Statute of limitations, court filings
HIGH = "high" # Court hearings, important discovery
MEDIUM = "medium" # Client meetings, administrative
LOW = "low" # Internal deadlines, optional items
class DeadlineStatus(PyEnum):
"""Status of deadline completion"""
PENDING = "pending"
COMPLETED = "completed"
MISSED = "missed"
CANCELLED = "cancelled"
EXTENDED = "extended"
class NotificationFrequency(PyEnum):
"""How often to send deadline reminders"""
NONE = "none"
DAILY = "daily"
WEEKLY = "weekly"
MONTHLY = "monthly"
CUSTOM = "custom"
class Deadline(BaseModel):
"""
Legal deadlines and important dates
Tracks court dates, filing deadlines, statute of limitations, etc.
"""
__tablename__ = "deadlines"
id = Column(Integer, primary_key=True, autoincrement=True)
# File association
file_no = Column(String(45), ForeignKey("files.file_no"), nullable=True, index=True)
client_id = Column(String(80), ForeignKey("rolodex.id"), nullable=True, index=True)
# Deadline details
title = Column(String(200), nullable=False)
description = Column(Text)
deadline_date = Column(Date, nullable=False, index=True)
deadline_time = Column(DateTime(timezone=True), nullable=True) # For specific times
# Classification
deadline_type = Column(Enum(DeadlineType), nullable=False, default=DeadlineType.OTHER)
priority = Column(Enum(DeadlinePriority), nullable=False, default=DeadlinePriority.MEDIUM)
status = Column(Enum(DeadlineStatus), nullable=False, default=DeadlineStatus.PENDING)
# Assignment and ownership
assigned_to_user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
assigned_to_employee_id = Column(String(10), ForeignKey("employees.empl_num"), nullable=True)
created_by_user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
# Court/external details
court_name = Column(String(200)) # Which court if applicable
case_number = Column(String(100)) # Court case number
judge_name = Column(String(100))
opposing_counsel = Column(String(200))
# Notification settings
notification_frequency = Column(Enum(NotificationFrequency), default=NotificationFrequency.WEEKLY)
advance_notice_days = Column(Integer, default=7) # Days before deadline to start notifications
last_notification_sent = Column(DateTime(timezone=True))
# Completion tracking
completed_date = Column(DateTime(timezone=True))
completed_by_user_id = Column(Integer, ForeignKey("users.id"))
completion_notes = Column(Text)
# Extension tracking
original_deadline_date = Column(Date) # Track if deadline was extended
extension_reason = Column(Text)
extension_granted_by = Column(String(100)) # Court, opposing counsel, etc.
# Metadata
created_at = Column(DateTime(timezone=True), default=func.now())
updated_at = Column(DateTime(timezone=True), default=func.now(), onupdate=func.now())
# Relationships
file = relationship("File", back_populates="deadlines")
client = relationship("Rolodex")
assigned_to_user = relationship("User", foreign_keys=[assigned_to_user_id])
assigned_to_employee = relationship("Employee")
created_by = relationship("User", foreign_keys=[created_by_user_id])
completed_by = relationship("User", foreign_keys=[completed_by_user_id])
reminders = relationship("DeadlineReminder", back_populates="deadline", cascade="all, delete-orphan")
def __repr__(self):
return f"<Deadline(id={self.id}, title='{self.title}', date='{self.deadline_date}', status='{self.status.value}')>"
@property
def is_overdue(self) -> bool:
"""Check if deadline is overdue"""
from datetime import date
return self.status == DeadlineStatus.PENDING and self.deadline_date < date.today()
@property
def days_until_deadline(self) -> int:
"""Calculate days until deadline (negative if overdue)"""
from datetime import date
return (self.deadline_date - date.today()).days
class DeadlineReminder(BaseModel):
"""
Automatic reminders for deadlines
Tracks when notifications were sent and their status
"""
__tablename__ = "deadline_reminders"
id = Column(Integer, primary_key=True, autoincrement=True)
deadline_id = Column(Integer, ForeignKey("deadlines.id"), nullable=False, index=True)
# Reminder scheduling
reminder_date = Column(Date, nullable=False, index=True)
reminder_time = Column(DateTime(timezone=True))
days_before_deadline = Column(Integer, nullable=False) # How many days before deadline
# Notification details
notification_sent = Column(Boolean, default=False)
sent_at = Column(DateTime(timezone=True))
notification_method = Column(String(50), default="email") # email, sms, in_app
recipient_user_id = Column(Integer, ForeignKey("users.id"))
recipient_email = Column(String(255))
# Message content
subject = Column(String(200))
message = Column(Text)
# Status tracking
delivery_status = Column(String(50), default="pending") # pending, sent, delivered, failed
error_message = Column(Text)
# Metadata
created_at = Column(DateTime(timezone=True), default=func.now())
# Relationships
deadline = relationship("Deadline", back_populates="reminders")
recipient = relationship("User")
def __repr__(self):
return f"<DeadlineReminder(id={self.id}, deadline_id={self.deadline_id}, date='{self.reminder_date}', sent={self.notification_sent})>"
class DeadlineTemplate(BaseModel):
"""
Templates for common deadline types
Helps standardize deadline creation for common legal processes
"""
__tablename__ = "deadline_templates"
id = Column(Integer, primary_key=True, autoincrement=True)
# Template details
name = Column(String(200), nullable=False, unique=True)
description = Column(Text)
deadline_type = Column(Enum(DeadlineType), nullable=False)
priority = Column(Enum(DeadlinePriority), nullable=False, default=DeadlinePriority.MEDIUM)
# Default settings
default_title_template = Column(String(200)) # Template with placeholders like {file_no}, {client_name}
default_description_template = Column(Text)
default_advance_notice_days = Column(Integer, default=7)
default_notification_frequency = Column(Enum(NotificationFrequency), default=NotificationFrequency.WEEKLY)
# Timing defaults
days_from_file_open = Column(Integer) # Auto-calculate deadline based on file open date
days_from_event = Column(Integer) # Days from some triggering event
# Status and metadata
active = Column(Boolean, default=True)
created_by_user_id = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), default=func.now())
updated_at = Column(DateTime(timezone=True), default=func.now(), onupdate=func.now())
# Relationships
created_by = relationship("User")
def __repr__(self):
return f"<DeadlineTemplate(id={self.id}, name='{self.name}', type='{self.deadline_type.value}')>"
class DeadlineHistory(BaseModel):
"""
History of deadline changes and updates
Maintains audit trail for deadline modifications
"""
__tablename__ = "deadline_history"
id = Column(Integer, primary_key=True, autoincrement=True)
deadline_id = Column(Integer, ForeignKey("deadlines.id"), nullable=False, index=True)
# Change details
change_type = Column(String(50), nullable=False) # created, updated, completed, extended, cancelled
field_changed = Column(String(100)) # Which field was changed
old_value = Column(Text)
new_value = Column(Text)
# Change context
change_reason = Column(Text)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
change_date = Column(DateTime(timezone=True), default=func.now())
# Relationships
deadline = relationship("Deadline")
user = relationship("User")
def __repr__(self):
return f"<DeadlineHistory(id={self.id}, deadline_id={self.deadline_id}, change='{self.change_type}')>"
class CourtCalendar(BaseModel):
"""
Court calendar entries and hearing schedules
Specialized deadline type for court appearances
"""
__tablename__ = "court_calendar"
id = Column(Integer, primary_key=True, autoincrement=True)
deadline_id = Column(Integer, ForeignKey("deadlines.id"), nullable=False, unique=True)
# Court details
court_name = Column(String(200), nullable=False)
courtroom = Column(String(50))
judge_name = Column(String(100))
case_number = Column(String(100))
# Hearing details
hearing_type = Column(String(100)) # Motion hearing, trial, conference, etc.
estimated_duration = Column(Integer) # Minutes
appearance_required = Column(Boolean, default=True)
# Preparation tracking
preparation_deadline = Column(Date) # When prep should be completed
documents_filed = Column(Boolean, default=False)
client_notified = Column(Boolean, default=False)
# Outcome tracking
hearing_completed = Column(Boolean, default=False)
outcome = Column(Text)
next_hearing_date = Column(Date)
# Relationships
deadline = relationship("Deadline")
def __repr__(self):
return f"<CourtCalendar(id={self.id}, court='{self.court_name}', hearing='{self.hearing_type}')>"