272 lines
10 KiB
Python
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}')>" |