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

327 lines
10 KiB
Python

"""
Celery tasks for automated consent management.
This module contains background tasks for:
- Checking consent expiry
- Sending expiry reminders
- Notifying staff about expired consents
"""
import logging
from datetime import date, timedelta
from celery import shared_task
from django.utils import timezone
from core.consent_service import ConsentManagementService, ConsentNotificationService
from core.models import Consent, Tenant
logger = logging.getLogger(__name__)
@shared_task
def check_expiring_consents() -> int:
"""
Check for consents expiring within 30 days and send reminders.
This task runs daily at 8:00 AM to check for expiring consents
and send reminders to patients/caregivers.
Returns:
int: Number of reminders sent
"""
count = 0
# Get all active tenants
tenants = Tenant.objects.filter(is_active=True)
for tenant in tenants:
# Get consents expiring in 30, 14, and 7 days
for days_threshold in [30, 14, 7]:
target_date = date.today() + timedelta(days=days_threshold)
expiring_consents = Consent.objects.filter(
tenant=tenant,
is_active=True,
expiry_date=target_date
).select_related('patient')
for consent in expiring_consents:
try:
ConsentNotificationService.send_expiry_reminder(consent)
count += 1
logger.info(f"Sent expiry reminder for consent {consent.id} ({days_threshold} days)")
except Exception as e:
logger.error(f"Failed to send expiry reminder for consent {consent.id}: {e}")
logger.info(f"Sent {count} consent expiry reminders")
return count
@shared_task
def check_expired_consents() -> int:
"""
Check for expired consents and send notifications.
This task runs daily at 9:00 AM to check for newly expired consents
and notify patients/caregivers and reception staff.
Returns:
int: Number of expired consents found
"""
total_count = 0
# Get all active tenants
tenants = Tenant.objects.filter(is_active=True)
for tenant in tenants:
# Get consents that expired yesterday (newly expired)
yesterday = date.today() - timedelta(days=1)
newly_expired = Consent.objects.filter(
tenant=tenant,
is_active=True,
expiry_date=yesterday
).select_related('patient')
expired_list = []
for consent in newly_expired:
try:
# Send notification to patient/caregiver
ConsentNotificationService.send_expired_notification(consent)
# Add to list for reception notification
expired_list.append({
'consent_id': str(consent.id),
'patient_name': consent.patient.full_name_en,
'patient_mrn': consent.patient.mrn,
'consent_type': consent.get_consent_type_display(),
})
total_count += 1
logger.info(f"Processed expired consent {consent.id}")
except Exception as e:
logger.error(f"Failed to process expired consent {consent.id}: {e}")
# Notify reception staff
if expired_list:
try:
ConsentNotificationService.notify_reception_expired_consents(tenant, expired_list)
except Exception as e:
logger.error(f"Failed to notify reception for tenant {tenant.name}: {e}")
logger.info(f"Found {total_count} newly expired consents")
return total_count
@shared_task
def generate_consent_expiry_report() -> dict:
"""
Generate weekly consent expiry report for administrators.
This task runs weekly on Monday at 8:00 AM to generate
a comprehensive report of consent status.
Returns:
dict: Report data
"""
from core.tasks import send_email_task, create_notification_task
from core.models import User
report = {
'generated_at': timezone.now().isoformat(),
'tenants': []
}
# Get all active tenants
tenants = Tenant.objects.filter(is_active=True)
for tenant in tenants:
# Get statistics
stats = ConsentManagementService.get_consent_statistics(tenant)
# Get expiring and expired consents
expiring = ConsentManagementService.get_expiring_consents(tenant, days_threshold=30)
expired = ConsentManagementService.get_expired_consents(tenant)
tenant_report = {
'tenant_name': tenant.name,
'statistics': stats,
'expiring_count': len(expiring),
'expired_count': len(expired),
'expiring_consents': expiring[:10], # Top 10
'expired_consents': expired[:10], # Top 10
}
report['tenants'].append(tenant_report)
# Send report to administrators
admins = User.objects.filter(
tenant=tenant,
role=User.Role.ADMIN,
is_active=True
)
# Prepare email message
message = f"""
Consent Expiry Report - {tenant.name}
Generated: {timezone.now().strftime('%Y-%m-%d %H:%M:%S')}
Summary:
--------
Total Active Consents: {stats['total_active']}
Expiring in 30 days: {stats['expiring_30_days']}
Expiring in 7 days: {stats['expiring_7_days']}
Expired: {stats['expired']}
Patients without consent: {stats['patients_without_consent']}
Action Required:
---------------
- {len(expired)} expired consents need immediate renewal
- {len(expiring)} consents expiring within 30 days
Please review the consent management dashboard for full details.
Best regards,
Agdar HIS System
"""
for admin in admins:
# Send email
if admin.email:
send_email_task.delay(
subject=f"Weekly Consent Report - {tenant.name}",
message=message,
recipient_list=[admin.email]
)
# Create in-app notification
create_notification_task.delay(
user_id=str(admin.id),
title="Weekly Consent Report",
message=f"{stats['expired']} expired, {stats['expiring_30_days']} expiring soon",
notification_type='INFO',
related_object_type='consent',
related_object_id=None
)
logger.info(f"Generated consent expiry report for {len(tenants)} tenants")
return report
@shared_task
def auto_deactivate_expired_consents() -> int:
"""
Automatically deactivate consents that have been expired for more than 90 days.
This task runs monthly on the 1st at 2:00 AM to clean up old expired consents.
Returns:
int: Number of consents deactivated
"""
threshold_date = date.today() - timedelta(days=90)
# Get consents expired for more than 90 days
old_expired = Consent.objects.filter(
is_active=True,
expiry_date__lt=threshold_date
)
count = old_expired.count()
# Deactivate them
old_expired.update(is_active=False)
logger.info(f"Auto-deactivated {count} consents expired for more than 90 days")
return count
@shared_task
def send_consent_renewal_batch(consent_ids: list) -> dict:
"""
Send consent renewal reminders in batch.
Args:
consent_ids: List of consent IDs to send reminders for
Returns:
dict: Results of batch operation
"""
results = {
'sent': 0,
'failed': 0,
'errors': []
}
for consent_id in consent_ids:
try:
consent = Consent.objects.get(id=consent_id)
ConsentNotificationService.send_expiry_reminder(consent)
results['sent'] += 1
except Consent.DoesNotExist:
results['errors'].append(f"Consent {consent_id} not found")
results['failed'] += 1
except Exception as e:
results['errors'].append(f"Consent {consent_id}: {str(e)}")
results['failed'] += 1
logger.info(f"Batch consent renewal: {results['sent']} sent, {results['failed']} failed")
return results
@shared_task
def check_consent_before_appointment(appointment_id: str) -> bool:
"""
Check consent validity before appointment confirmation.
This task is triggered when an appointment is being confirmed
to ensure patient has valid consent.
Args:
appointment_id: UUID of the appointment
Returns:
bool: True if consent is valid
"""
try:
from appointments.models import Appointment
appointment = Appointment.objects.select_related('patient').get(id=appointment_id)
# Validate consent
is_valid, missing = ConsentManagementService.validate_consent_before_booking(
patient=appointment.patient
)
if not is_valid:
logger.warning(
f"Appointment {appointment_id} has invalid consent. Missing: {missing}"
)
# Send notification to reception
from core.tasks import create_notification_task
from core.models import User
reception_users = User.objects.filter(
tenant=appointment.tenant,
role=User.Role.FRONT_DESK,
is_active=True
)
for user in reception_users:
create_notification_task.delay(
user_id=str(user.id),
title="Consent Required",
message=f"Patient {appointment.patient.mrn} needs consent renewal before appointment",
notification_type='WARNING',
related_object_type='appointment',
related_object_id=str(appointment_id)
)
return is_valid
except Exception as e:
logger.error(f"Failed to check consent for appointment {appointment_id}: {e}")
return False