agdar/core/documentation_tasks.py
Marwan Alwali 2f1681b18c update
2025-11-11 13:44:48 +03:00

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"