205 lines
7.3 KiB
Python
205 lines
7.3 KiB
Python
"""
|
|
Celery tasks for documentation delay tracking and notifications.
|
|
|
|
These tasks run periodically to check for overdue documentation
|
|
and send alerts to senior therapists.
|
|
"""
|
|
|
|
from celery import shared_task
|
|
from django.utils import timezone
|
|
from django.utils.translation import gettext as _
|
|
|
|
from core.documentation_tracking import DocumentationDelayTracker
|
|
from notifications.models import Notification
|
|
|
|
|
|
@shared_task(name='core.check_documentation_delays')
|
|
def check_documentation_delays():
|
|
"""
|
|
Check all documentation trackers and update their status.
|
|
Runs daily to calculate days overdue and update statuses.
|
|
"""
|
|
trackers = DocumentationDelayTracker.objects.filter(
|
|
status__in=[
|
|
DocumentationDelayTracker.Status.PENDING,
|
|
DocumentationDelayTracker.Status.OVERDUE,
|
|
]
|
|
)
|
|
|
|
updated_count = 0
|
|
for tracker in trackers:
|
|
tracker.update_status()
|
|
updated_count += 1
|
|
|
|
return f"Updated {updated_count} documentation trackers"
|
|
|
|
|
|
@shared_task(name='core.send_documentation_delay_alerts')
|
|
def send_documentation_delay_alerts():
|
|
"""
|
|
Send alerts to senior therapists for overdue documentation (>5 working days).
|
|
Runs daily to notify seniors of pending documentation.
|
|
"""
|
|
from notifications.models import Notification
|
|
|
|
# Get documentation that needs alerts
|
|
pending_alerts = DocumentationDelayTracker.get_pending_alerts()
|
|
|
|
alerts_sent = 0
|
|
for tracker in pending_alerts:
|
|
# Create in-app notification for senior therapist
|
|
notification = Notification.objects.create(
|
|
user=tracker.senior_therapist,
|
|
title=_("Overdue Documentation Alert"),
|
|
message=_(
|
|
f"{tracker.assigned_to.get_full_name()} has overdue documentation: "
|
|
f"{tracker.get_document_type_display()} is {tracker.days_overdue} working days overdue. "
|
|
f"Due date was {tracker.due_date.strftime('%Y-%m-%d')}."
|
|
),
|
|
notification_type=Notification.NotificationType.WARNING,
|
|
related_object_type='documentation_tracker',
|
|
related_object_id=tracker.id,
|
|
)
|
|
|
|
# Record that alert was sent
|
|
tracker.send_alert()
|
|
alerts_sent += 1
|
|
|
|
# Escalate if >10 days overdue
|
|
if tracker.days_overdue >= 10 and not tracker.escalated_at:
|
|
# Find Clinical Coordinator or Admin to escalate to
|
|
from core.models import User
|
|
|
|
coordinator = User.objects.filter(
|
|
tenant=tracker.tenant,
|
|
role='ADMIN', # Or CLINICAL_COORDINATOR if that role exists
|
|
is_active=True
|
|
).first()
|
|
|
|
if coordinator:
|
|
tracker.escalate(coordinator)
|
|
|
|
# Send escalation notification
|
|
Notification.objects.create(
|
|
user=coordinator,
|
|
title=_("Documentation Escalation"),
|
|
message=_(
|
|
f"Documentation has been escalated: {tracker.assigned_to.get_full_name()} "
|
|
f"has {tracker.get_document_type_display()} that is {tracker.days_overdue} "
|
|
f"working days overdue. Senior therapist: {tracker.senior_therapist.get_full_name()}."
|
|
),
|
|
notification_type=Notification.NotificationType.ERROR,
|
|
related_object_type='documentation_tracker',
|
|
related_object_id=tracker.id,
|
|
)
|
|
|
|
return f"Sent {alerts_sent} documentation delay alerts"
|
|
|
|
|
|
@shared_task(name='core.send_documentation_reminder_to_therapist')
|
|
def send_documentation_reminder_to_therapist(tracker_id):
|
|
"""
|
|
Send reminder to therapist about pending documentation.
|
|
|
|
Args:
|
|
tracker_id: UUID of DocumentationDelayTracker
|
|
"""
|
|
try:
|
|
tracker = DocumentationDelayTracker.objects.get(id=tracker_id)
|
|
|
|
# Create notification for assigned therapist
|
|
Notification.objects.create(
|
|
user=tracker.assigned_to,
|
|
title=_("Documentation Reminder"),
|
|
message=_(
|
|
f"Reminder: You have pending {tracker.get_document_type_display()} "
|
|
f"due on {tracker.due_date.strftime('%Y-%m-%d')}. "
|
|
f"Please complete it as soon as possible."
|
|
),
|
|
notification_type=Notification.NotificationType.INFO,
|
|
related_object_type='documentation_tracker',
|
|
related_object_id=tracker.id,
|
|
)
|
|
|
|
return f"Sent reminder to {tracker.assigned_to.get_full_name()}"
|
|
|
|
except DocumentationDelayTracker.DoesNotExist:
|
|
return f"Tracker {tracker_id} not found"
|
|
|
|
|
|
@shared_task(name='core.generate_senior_weekly_summary')
|
|
def generate_senior_weekly_summary():
|
|
"""
|
|
Generate weekly summary for senior therapists showing:
|
|
- Pending documentation from their team
|
|
- Overdue items
|
|
- Completion statistics
|
|
|
|
Runs every Monday morning.
|
|
"""
|
|
from core.models import User
|
|
from django.db.models import Count, Q
|
|
|
|
# Get all senior therapists
|
|
seniors = User.objects.filter(
|
|
role__in=['DOCTOR', 'ADMIN'], # Senior roles
|
|
is_active=True
|
|
)
|
|
|
|
summaries_sent = 0
|
|
for senior in seniors:
|
|
# Get documentation stats for this senior's team
|
|
all_docs = DocumentationDelayTracker.objects.filter(
|
|
senior_therapist=senior
|
|
)
|
|
|
|
pending = all_docs.filter(status=DocumentationDelayTracker.Status.PENDING).count()
|
|
overdue = all_docs.filter(status=DocumentationDelayTracker.Status.OVERDUE).count()
|
|
escalated = all_docs.filter(status=DocumentationDelayTracker.Status.ESCALATED).count()
|
|
completed_this_week = all_docs.filter(
|
|
status=DocumentationDelayTracker.Status.COMPLETED,
|
|
completed_at__gte=timezone.now() - timedelta(days=7)
|
|
).count()
|
|
|
|
# Create weekly summary notification
|
|
message = _(
|
|
f"Weekly Documentation Summary:\n\n"
|
|
f"✓ Completed this week: {completed_this_week}\n"
|
|
f"⏳ Pending: {pending}\n"
|
|
f"⚠️ Overdue (>5 days): {overdue}\n"
|
|
f"🔴 Escalated (>10 days): {escalated}\n\n"
|
|
f"Please review overdue items and follow up with your team."
|
|
)
|
|
|
|
Notification.objects.create(
|
|
user=senior,
|
|
title=_("Weekly Documentation Summary"),
|
|
message=message,
|
|
notification_type=Notification.NotificationType.INFO,
|
|
)
|
|
|
|
summaries_sent += 1
|
|
|
|
return f"Sent weekly summaries to {summaries_sent} senior therapists"
|
|
|
|
|
|
@shared_task(name='core.cleanup_completed_trackers')
|
|
def cleanup_completed_trackers(days=90):
|
|
"""
|
|
Archive or delete completed documentation trackers older than specified days.
|
|
|
|
Args:
|
|
days: Number of days to keep completed trackers (default: 90)
|
|
"""
|
|
cutoff_date = timezone.now() - timedelta(days=days)
|
|
|
|
old_trackers = DocumentationDelayTracker.objects.filter(
|
|
status=DocumentationDelayTracker.Status.COMPLETED,
|
|
completed_at__lt=cutoff_date
|
|
)
|
|
|
|
count = old_trackers.count()
|
|
old_trackers.delete()
|
|
|
|
return f"Cleaned up {count} old documentation trackers"
|