""" 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"