684 lines
25 KiB
Python
684 lines
25 KiB
Python
"""
|
|
Deadline management service
|
|
Handles deadline creation, tracking, notifications, and reporting
|
|
"""
|
|
from typing import List, Dict, Any, Optional, Tuple
|
|
from datetime import datetime, date, timedelta, timezone
|
|
from sqlalchemy.orm import Session, joinedload
|
|
from sqlalchemy import and_, func, or_, desc, asc
|
|
from decimal import Decimal
|
|
|
|
from app.models import (
|
|
Deadline, DeadlineReminder, DeadlineTemplate, DeadlineHistory, CourtCalendar,
|
|
DeadlineType, DeadlinePriority, DeadlineStatus, NotificationFrequency,
|
|
File, Rolodex, Employee, User
|
|
)
|
|
from app.utils.logging import app_logger
|
|
|
|
logger = app_logger
|
|
|
|
|
|
class DeadlineManagementError(Exception):
|
|
"""Exception raised when deadline management operations fail"""
|
|
pass
|
|
|
|
|
|
class DeadlineService:
|
|
"""Service for deadline management operations"""
|
|
|
|
def __init__(self, db: Session):
|
|
self.db = db
|
|
|
|
def create_deadline(
|
|
self,
|
|
title: str,
|
|
deadline_date: date,
|
|
created_by_user_id: int,
|
|
deadline_type: DeadlineType = DeadlineType.OTHER,
|
|
priority: DeadlinePriority = DeadlinePriority.MEDIUM,
|
|
description: Optional[str] = None,
|
|
file_no: Optional[str] = None,
|
|
client_id: Optional[str] = None,
|
|
assigned_to_user_id: Optional[int] = None,
|
|
assigned_to_employee_id: Optional[str] = None,
|
|
deadline_time: Optional[datetime] = None,
|
|
court_name: Optional[str] = None,
|
|
case_number: Optional[str] = None,
|
|
advance_notice_days: int = 7,
|
|
notification_frequency: NotificationFrequency = NotificationFrequency.WEEKLY
|
|
) -> Deadline:
|
|
"""Create a new deadline"""
|
|
|
|
# Validate file exists if provided
|
|
if file_no:
|
|
file_obj = self.db.query(File).filter(File.file_no == file_no).first()
|
|
if not file_obj:
|
|
raise DeadlineManagementError(f"File {file_no} not found")
|
|
|
|
# Validate client exists if provided
|
|
if client_id:
|
|
client_obj = self.db.query(Rolodex).filter(Rolodex.id == client_id).first()
|
|
if not client_obj:
|
|
raise DeadlineManagementError(f"Client {client_id} not found")
|
|
|
|
# Validate assigned employee if provided
|
|
if assigned_to_employee_id:
|
|
employee_obj = self.db.query(Employee).filter(Employee.empl_num == assigned_to_employee_id).first()
|
|
if not employee_obj:
|
|
raise DeadlineManagementError(f"Employee {assigned_to_employee_id} not found")
|
|
|
|
# Create deadline
|
|
deadline = Deadline(
|
|
title=title,
|
|
description=description,
|
|
deadline_date=deadline_date,
|
|
deadline_time=deadline_time,
|
|
deadline_type=deadline_type,
|
|
priority=priority,
|
|
file_no=file_no,
|
|
client_id=client_id,
|
|
assigned_to_user_id=assigned_to_user_id,
|
|
assigned_to_employee_id=assigned_to_employee_id,
|
|
created_by_user_id=created_by_user_id,
|
|
court_name=court_name,
|
|
case_number=case_number,
|
|
advance_notice_days=advance_notice_days,
|
|
notification_frequency=notification_frequency
|
|
)
|
|
|
|
self.db.add(deadline)
|
|
self.db.flush() # Get the ID
|
|
|
|
# Create history record
|
|
self._create_deadline_history(
|
|
deadline.id, "created", None, None, None, created_by_user_id, "Deadline created"
|
|
)
|
|
|
|
# Schedule automatic reminders
|
|
if notification_frequency != NotificationFrequency.NONE:
|
|
self._schedule_reminders(deadline)
|
|
|
|
self.db.commit()
|
|
self.db.refresh(deadline)
|
|
|
|
logger.info(f"Created deadline {deadline.id}: '{title}' for {deadline_date}")
|
|
return deadline
|
|
|
|
def update_deadline(
|
|
self,
|
|
deadline_id: int,
|
|
user_id: int,
|
|
**updates
|
|
) -> Deadline:
|
|
"""Update an existing deadline"""
|
|
|
|
deadline = self.db.query(Deadline).filter(Deadline.id == deadline_id).first()
|
|
if not deadline:
|
|
raise DeadlineManagementError(f"Deadline {deadline_id} not found")
|
|
|
|
# Track changes for history
|
|
changes = []
|
|
for field, new_value in updates.items():
|
|
if hasattr(deadline, field):
|
|
old_value = getattr(deadline, field)
|
|
if old_value != new_value:
|
|
changes.append((field, old_value, new_value))
|
|
setattr(deadline, field, new_value)
|
|
|
|
# Update timestamp
|
|
deadline.updated_at = datetime.now(timezone.utc)
|
|
|
|
# Create history records for changes
|
|
for field, old_value, new_value in changes:
|
|
self._create_deadline_history(
|
|
deadline_id, "updated", field, str(old_value), str(new_value), user_id
|
|
)
|
|
|
|
# If deadline date changed, reschedule reminders
|
|
if any(field == 'deadline_date' for field, _, _ in changes):
|
|
self._reschedule_reminders(deadline)
|
|
|
|
self.db.commit()
|
|
self.db.refresh(deadline)
|
|
|
|
logger.info(f"Updated deadline {deadline_id} - {len(changes)} changes made")
|
|
return deadline
|
|
|
|
def complete_deadline(
|
|
self,
|
|
deadline_id: int,
|
|
user_id: int,
|
|
completion_notes: Optional[str] = None
|
|
) -> Deadline:
|
|
"""Mark a deadline as completed"""
|
|
|
|
deadline = self.db.query(Deadline).filter(Deadline.id == deadline_id).first()
|
|
if not deadline:
|
|
raise DeadlineManagementError(f"Deadline {deadline_id} not found")
|
|
|
|
if deadline.status != DeadlineStatus.PENDING:
|
|
raise DeadlineManagementError(f"Only pending deadlines can be completed")
|
|
|
|
# Update deadline
|
|
deadline.status = DeadlineStatus.COMPLETED
|
|
deadline.completed_date = datetime.now(timezone.utc)
|
|
deadline.completed_by_user_id = user_id
|
|
deadline.completion_notes = completion_notes
|
|
|
|
# Create history record
|
|
self._create_deadline_history(
|
|
deadline_id, "completed", "status", "pending", "completed", user_id, completion_notes
|
|
)
|
|
|
|
# Cancel pending reminders
|
|
self._cancel_pending_reminders(deadline_id)
|
|
|
|
self.db.commit()
|
|
self.db.refresh(deadline)
|
|
|
|
logger.info(f"Completed deadline {deadline_id}")
|
|
return deadline
|
|
|
|
def extend_deadline(
|
|
self,
|
|
deadline_id: int,
|
|
new_deadline_date: date,
|
|
user_id: int,
|
|
extension_reason: Optional[str] = None,
|
|
extension_granted_by: Optional[str] = None
|
|
) -> Deadline:
|
|
"""Extend a deadline to a new date"""
|
|
|
|
deadline = self.db.query(Deadline).filter(Deadline.id == deadline_id).first()
|
|
if not deadline:
|
|
raise DeadlineManagementError(f"Deadline {deadline_id} not found")
|
|
|
|
if deadline.status not in [DeadlineStatus.PENDING, DeadlineStatus.EXTENDED]:
|
|
raise DeadlineManagementError("Only pending or previously extended deadlines can be extended")
|
|
|
|
# Store original deadline if this is the first extension
|
|
if not deadline.original_deadline_date:
|
|
deadline.original_deadline_date = deadline.deadline_date
|
|
|
|
old_date = deadline.deadline_date
|
|
deadline.deadline_date = new_deadline_date
|
|
deadline.status = DeadlineStatus.EXTENDED
|
|
deadline.extension_reason = extension_reason
|
|
deadline.extension_granted_by = extension_granted_by
|
|
|
|
# Create history record
|
|
self._create_deadline_history(
|
|
deadline_id, "extended", "deadline_date", str(old_date), str(new_deadline_date),
|
|
user_id, f"Extension reason: {extension_reason or 'Not specified'}"
|
|
)
|
|
|
|
# Reschedule reminders for new date
|
|
self._reschedule_reminders(deadline)
|
|
|
|
self.db.commit()
|
|
self.db.refresh(deadline)
|
|
|
|
logger.info(f"Extended deadline {deadline_id} from {old_date} to {new_deadline_date}")
|
|
return deadline
|
|
|
|
def cancel_deadline(
|
|
self,
|
|
deadline_id: int,
|
|
user_id: int,
|
|
cancellation_reason: Optional[str] = None
|
|
) -> Deadline:
|
|
"""Cancel a deadline"""
|
|
|
|
deadline = self.db.query(Deadline).filter(Deadline.id == deadline_id).first()
|
|
if not deadline:
|
|
raise DeadlineManagementError(f"Deadline {deadline_id} not found")
|
|
|
|
deadline.status = DeadlineStatus.CANCELLED
|
|
|
|
# Create history record
|
|
self._create_deadline_history(
|
|
deadline_id, "cancelled", "status", deadline.status.value, "cancelled",
|
|
user_id, cancellation_reason
|
|
)
|
|
|
|
# Cancel pending reminders
|
|
self._cancel_pending_reminders(deadline_id)
|
|
|
|
self.db.commit()
|
|
self.db.refresh(deadline)
|
|
|
|
logger.info(f"Cancelled deadline {deadline_id}")
|
|
return deadline
|
|
|
|
def get_deadlines_by_file(self, file_no: str) -> List[Deadline]:
|
|
"""Get all deadlines for a specific file"""
|
|
|
|
return self.db.query(Deadline).filter(
|
|
Deadline.file_no == file_no
|
|
).options(
|
|
joinedload(Deadline.assigned_to_user),
|
|
joinedload(Deadline.assigned_to_employee),
|
|
joinedload(Deadline.created_by)
|
|
).order_by(Deadline.deadline_date.asc()).all()
|
|
|
|
def get_upcoming_deadlines(
|
|
self,
|
|
days_ahead: int = 30,
|
|
user_id: Optional[int] = None,
|
|
employee_id: Optional[str] = None,
|
|
priority: Optional[DeadlinePriority] = None,
|
|
deadline_type: Optional[DeadlineType] = None
|
|
) -> List[Deadline]:
|
|
"""Get upcoming deadlines within specified timeframe"""
|
|
|
|
end_date = date.today() + timedelta(days=days_ahead)
|
|
|
|
query = self.db.query(Deadline).filter(
|
|
Deadline.status == DeadlineStatus.PENDING,
|
|
Deadline.deadline_date <= end_date
|
|
)
|
|
|
|
if user_id:
|
|
query = query.filter(Deadline.assigned_to_user_id == user_id)
|
|
|
|
if employee_id:
|
|
query = query.filter(Deadline.assigned_to_employee_id == employee_id)
|
|
|
|
if priority:
|
|
query = query.filter(Deadline.priority == priority)
|
|
|
|
if deadline_type:
|
|
query = query.filter(Deadline.deadline_type == deadline_type)
|
|
|
|
return query.options(
|
|
joinedload(Deadline.file),
|
|
joinedload(Deadline.client),
|
|
joinedload(Deadline.assigned_to_user),
|
|
joinedload(Deadline.assigned_to_employee)
|
|
).order_by(Deadline.deadline_date.asc(), Deadline.priority.desc()).all()
|
|
|
|
def get_overdue_deadlines(
|
|
self,
|
|
user_id: Optional[int] = None,
|
|
employee_id: Optional[str] = None
|
|
) -> List[Deadline]:
|
|
"""Get overdue deadlines"""
|
|
|
|
query = self.db.query(Deadline).filter(
|
|
Deadline.status == DeadlineStatus.PENDING,
|
|
Deadline.deadline_date < date.today()
|
|
)
|
|
|
|
if user_id:
|
|
query = query.filter(Deadline.assigned_to_user_id == user_id)
|
|
|
|
if employee_id:
|
|
query = query.filter(Deadline.assigned_to_employee_id == employee_id)
|
|
|
|
return query.options(
|
|
joinedload(Deadline.file),
|
|
joinedload(Deadline.client),
|
|
joinedload(Deadline.assigned_to_user),
|
|
joinedload(Deadline.assigned_to_employee)
|
|
).order_by(Deadline.deadline_date.asc()).all()
|
|
|
|
def get_deadline_statistics(
|
|
self,
|
|
user_id: Optional[int] = None,
|
|
employee_id: Optional[str] = None,
|
|
start_date: Optional[date] = None,
|
|
end_date: Optional[date] = None
|
|
) -> Dict[str, Any]:
|
|
"""Get deadline statistics for reporting"""
|
|
|
|
base_query = self.db.query(Deadline)
|
|
|
|
if user_id:
|
|
base_query = base_query.filter(Deadline.assigned_to_user_id == user_id)
|
|
|
|
if employee_id:
|
|
base_query = base_query.filter(Deadline.assigned_to_employee_id == employee_id)
|
|
|
|
if start_date:
|
|
base_query = base_query.filter(Deadline.deadline_date >= start_date)
|
|
|
|
if end_date:
|
|
base_query = base_query.filter(Deadline.deadline_date <= end_date)
|
|
|
|
# Calculate statistics
|
|
total_deadlines = base_query.count()
|
|
pending_deadlines = base_query.filter(Deadline.status == DeadlineStatus.PENDING).count()
|
|
completed_deadlines = base_query.filter(Deadline.status == DeadlineStatus.COMPLETED).count()
|
|
overdue_deadlines = base_query.filter(
|
|
Deadline.status == DeadlineStatus.PENDING,
|
|
Deadline.deadline_date < date.today()
|
|
).count()
|
|
|
|
# Deadlines by priority
|
|
priority_counts = {}
|
|
for priority in DeadlinePriority:
|
|
count = base_query.filter(Deadline.priority == priority).count()
|
|
priority_counts[priority.value] = count
|
|
|
|
# Deadlines by type
|
|
type_counts = {}
|
|
for deadline_type in DeadlineType:
|
|
count = base_query.filter(Deadline.deadline_type == deadline_type).count()
|
|
type_counts[deadline_type.value] = count
|
|
|
|
# Upcoming deadlines (next 7, 14, 30 days)
|
|
today = date.today()
|
|
upcoming_7_days = base_query.filter(
|
|
Deadline.status == DeadlineStatus.PENDING,
|
|
Deadline.deadline_date.between(today, today + timedelta(days=7))
|
|
).count()
|
|
|
|
upcoming_14_days = base_query.filter(
|
|
Deadline.status == DeadlineStatus.PENDING,
|
|
Deadline.deadline_date.between(today, today + timedelta(days=14))
|
|
).count()
|
|
|
|
upcoming_30_days = base_query.filter(
|
|
Deadline.status == DeadlineStatus.PENDING,
|
|
Deadline.deadline_date.between(today, today + timedelta(days=30))
|
|
).count()
|
|
|
|
return {
|
|
"total_deadlines": total_deadlines,
|
|
"pending_deadlines": pending_deadlines,
|
|
"completed_deadlines": completed_deadlines,
|
|
"overdue_deadlines": overdue_deadlines,
|
|
"completion_rate": (completed_deadlines / total_deadlines * 100) if total_deadlines > 0 else 0,
|
|
"priority_breakdown": priority_counts,
|
|
"type_breakdown": type_counts,
|
|
"upcoming": {
|
|
"next_7_days": upcoming_7_days,
|
|
"next_14_days": upcoming_14_days,
|
|
"next_30_days": upcoming_30_days
|
|
}
|
|
}
|
|
|
|
def create_deadline_from_template(
|
|
self,
|
|
template_id: int,
|
|
user_id: int,
|
|
file_no: Optional[str] = None,
|
|
client_id: Optional[str] = None,
|
|
deadline_date: Optional[date] = None,
|
|
**overrides
|
|
) -> Deadline:
|
|
"""Create a deadline from a template"""
|
|
|
|
template = self.db.query(DeadlineTemplate).filter(DeadlineTemplate.id == template_id).first()
|
|
if not template:
|
|
raise DeadlineManagementError(f"Deadline template {template_id} not found")
|
|
|
|
if not template.active:
|
|
raise DeadlineManagementError("Template is not active")
|
|
|
|
# Calculate deadline date if not provided
|
|
if not deadline_date:
|
|
if template.days_from_file_open and file_no:
|
|
file_obj = self.db.query(File).filter(File.file_no == file_no).first()
|
|
if file_obj:
|
|
deadline_date = file_obj.opened + timedelta(days=template.days_from_file_open)
|
|
else:
|
|
deadline_date = date.today() + timedelta(days=template.days_from_event or 30)
|
|
|
|
# Get file and client info for template substitution
|
|
file_obj = None
|
|
client_obj = None
|
|
|
|
if file_no:
|
|
file_obj = self.db.query(File).filter(File.file_no == file_no).first()
|
|
if file_obj and file_obj.owner:
|
|
client_obj = file_obj.owner
|
|
elif client_id:
|
|
client_obj = self.db.query(Rolodex).filter(Rolodex.id == client_id).first()
|
|
|
|
# Process template strings with substitutions
|
|
title = self._process_template_string(
|
|
template.default_title_template, file_obj, client_obj
|
|
)
|
|
|
|
description = self._process_template_string(
|
|
template.default_description_template, file_obj, client_obj
|
|
) if template.default_description_template else None
|
|
|
|
# Create deadline with template defaults and overrides
|
|
deadline_data = {
|
|
"title": title,
|
|
"description": description,
|
|
"deadline_date": deadline_date,
|
|
"deadline_type": template.deadline_type,
|
|
"priority": template.priority,
|
|
"file_no": file_no,
|
|
"client_id": client_id,
|
|
"advance_notice_days": template.default_advance_notice_days,
|
|
"notification_frequency": template.default_notification_frequency,
|
|
"created_by_user_id": user_id
|
|
}
|
|
|
|
# Apply any overrides
|
|
deadline_data.update(overrides)
|
|
|
|
return self.create_deadline(**deadline_data)
|
|
|
|
def get_pending_reminders(self, reminder_date: date = None) -> List[DeadlineReminder]:
|
|
"""Get pending reminders that need to be sent"""
|
|
|
|
if reminder_date is None:
|
|
reminder_date = date.today()
|
|
|
|
return self.db.query(DeadlineReminder).join(Deadline).filter(
|
|
DeadlineReminder.reminder_date <= reminder_date,
|
|
DeadlineReminder.notification_sent == False,
|
|
Deadline.status == DeadlineStatus.PENDING
|
|
).options(
|
|
joinedload(DeadlineReminder.deadline),
|
|
joinedload(DeadlineReminder.recipient)
|
|
).all()
|
|
|
|
def mark_reminder_sent(
|
|
self,
|
|
reminder_id: int,
|
|
delivery_status: str = "sent",
|
|
error_message: Optional[str] = None
|
|
):
|
|
"""Mark a reminder as sent"""
|
|
|
|
reminder = self.db.query(DeadlineReminder).filter(DeadlineReminder.id == reminder_id).first()
|
|
if reminder:
|
|
reminder.notification_sent = True
|
|
reminder.sent_at = datetime.now(timezone.utc)
|
|
reminder.delivery_status = delivery_status
|
|
if error_message:
|
|
reminder.error_message = error_message
|
|
|
|
self.db.commit()
|
|
|
|
# Private helper methods
|
|
|
|
def _create_deadline_history(
|
|
self,
|
|
deadline_id: int,
|
|
change_type: str,
|
|
field_changed: Optional[str],
|
|
old_value: Optional[str],
|
|
new_value: Optional[str],
|
|
user_id: int,
|
|
change_reason: Optional[str] = None
|
|
):
|
|
"""Create a deadline history record"""
|
|
|
|
history_record = DeadlineHistory(
|
|
deadline_id=deadline_id,
|
|
change_type=change_type,
|
|
field_changed=field_changed,
|
|
old_value=old_value,
|
|
new_value=new_value,
|
|
user_id=user_id,
|
|
change_reason=change_reason
|
|
)
|
|
|
|
self.db.add(history_record)
|
|
|
|
def _schedule_reminders(self, deadline: Deadline):
|
|
"""Schedule automatic reminders for a deadline"""
|
|
|
|
if deadline.notification_frequency == NotificationFrequency.NONE:
|
|
return
|
|
|
|
# Calculate reminder dates
|
|
reminder_dates = []
|
|
advance_days = deadline.advance_notice_days or 7
|
|
|
|
if deadline.notification_frequency == NotificationFrequency.DAILY:
|
|
# Daily reminders starting from advance notice days
|
|
for i in range(advance_days, 0, -1):
|
|
reminder_date = deadline.deadline_date - timedelta(days=i)
|
|
if reminder_date >= date.today():
|
|
reminder_dates.append((reminder_date, i))
|
|
|
|
elif deadline.notification_frequency == NotificationFrequency.WEEKLY:
|
|
# Weekly reminders
|
|
weeks_ahead = max(1, advance_days // 7)
|
|
for week in range(weeks_ahead, 0, -1):
|
|
reminder_date = deadline.deadline_date - timedelta(weeks=week)
|
|
if reminder_date >= date.today():
|
|
reminder_dates.append((reminder_date, week * 7))
|
|
|
|
elif deadline.notification_frequency == NotificationFrequency.MONTHLY:
|
|
# Monthly reminder
|
|
reminder_date = deadline.deadline_date - timedelta(days=30)
|
|
if reminder_date >= date.today():
|
|
reminder_dates.append((reminder_date, 30))
|
|
|
|
# Create reminder records
|
|
for reminder_date, days_before in reminder_dates:
|
|
recipient_user_id = deadline.assigned_to_user_id or deadline.created_by_user_id
|
|
|
|
reminder = DeadlineReminder(
|
|
deadline_id=deadline.id,
|
|
reminder_date=reminder_date,
|
|
days_before_deadline=days_before,
|
|
recipient_user_id=recipient_user_id,
|
|
subject=f"Deadline Reminder: {deadline.title}",
|
|
message=f"Reminder: {deadline.title} is due on {deadline.deadline_date} ({days_before} days from now)"
|
|
)
|
|
|
|
self.db.add(reminder)
|
|
|
|
def _reschedule_reminders(self, deadline: Deadline):
|
|
"""Reschedule reminders after deadline date change"""
|
|
|
|
# Delete existing unsent reminders
|
|
self.db.query(DeadlineReminder).filter(
|
|
DeadlineReminder.deadline_id == deadline.id,
|
|
DeadlineReminder.notification_sent == False
|
|
).delete()
|
|
|
|
# Schedule new reminders
|
|
self._schedule_reminders(deadline)
|
|
|
|
def _cancel_pending_reminders(self, deadline_id: int):
|
|
"""Cancel all pending reminders for a deadline"""
|
|
|
|
self.db.query(DeadlineReminder).filter(
|
|
DeadlineReminder.deadline_id == deadline_id,
|
|
DeadlineReminder.notification_sent == False
|
|
).delete()
|
|
|
|
def _process_template_string(
|
|
self,
|
|
template_string: Optional[str],
|
|
file_obj: Optional[File],
|
|
client_obj: Optional[Rolodex]
|
|
) -> Optional[str]:
|
|
"""Process template string with variable substitutions"""
|
|
|
|
if not template_string:
|
|
return None
|
|
|
|
result = template_string
|
|
|
|
# File substitutions
|
|
if file_obj:
|
|
result = result.replace("{file_no}", file_obj.file_no or "")
|
|
result = result.replace("{regarding}", file_obj.regarding or "")
|
|
result = result.replace("{attorney}", file_obj.empl_num or "")
|
|
|
|
# Client substitutions
|
|
if client_obj:
|
|
client_name = f"{client_obj.first or ''} {client_obj.last or ''}".strip()
|
|
result = result.replace("{client_name}", client_name)
|
|
result = result.replace("{client_id}", client_obj.id or "")
|
|
|
|
# Date substitutions
|
|
today = date.today()
|
|
result = result.replace("{today}", today.strftime("%Y-%m-%d"))
|
|
result = result.replace("{today_formatted}", today.strftime("%B %d, %Y"))
|
|
|
|
return result
|
|
|
|
|
|
class DeadlineTemplateService:
|
|
"""Service for managing deadline templates"""
|
|
|
|
def __init__(self, db: Session):
|
|
self.db = db
|
|
|
|
def create_template(
|
|
self,
|
|
name: str,
|
|
deadline_type: DeadlineType,
|
|
user_id: int,
|
|
description: Optional[str] = None,
|
|
priority: DeadlinePriority = DeadlinePriority.MEDIUM,
|
|
default_title_template: Optional[str] = None,
|
|
default_description_template: Optional[str] = None,
|
|
default_advance_notice_days: int = 7,
|
|
default_notification_frequency: NotificationFrequency = NotificationFrequency.WEEKLY,
|
|
days_from_file_open: Optional[int] = None,
|
|
days_from_event: Optional[int] = None
|
|
) -> DeadlineTemplate:
|
|
"""Create a new deadline template"""
|
|
|
|
# Check for duplicate name
|
|
existing = self.db.query(DeadlineTemplate).filter(DeadlineTemplate.name == name).first()
|
|
if existing:
|
|
raise DeadlineManagementError(f"Template with name '{name}' already exists")
|
|
|
|
template = DeadlineTemplate(
|
|
name=name,
|
|
description=description,
|
|
deadline_type=deadline_type,
|
|
priority=priority,
|
|
default_title_template=default_title_template,
|
|
default_description_template=default_description_template,
|
|
default_advance_notice_days=default_advance_notice_days,
|
|
default_notification_frequency=default_notification_frequency,
|
|
days_from_file_open=days_from_file_open,
|
|
days_from_event=days_from_event,
|
|
created_by_user_id=user_id
|
|
)
|
|
|
|
self.db.add(template)
|
|
self.db.commit()
|
|
self.db.refresh(template)
|
|
|
|
logger.info(f"Created deadline template: {name}")
|
|
return template
|
|
|
|
def get_active_templates(
|
|
self,
|
|
deadline_type: Optional[DeadlineType] = None
|
|
) -> List[DeadlineTemplate]:
|
|
"""Get all active deadline templates"""
|
|
|
|
query = self.db.query(DeadlineTemplate).filter(DeadlineTemplate.active == True)
|
|
|
|
if deadline_type:
|
|
query = query.filter(DeadlineTemplate.deadline_type == deadline_type)
|
|
|
|
return query.order_by(DeadlineTemplate.name).all() |