HH/apps/surveys/management/commands/mark_abandoned_surveys.py
2026-04-08 17:13:35 +03:00

98 lines
3.6 KiB
Python

"""
Management command to mark surveys as abandoned.
Marks surveys that have been opened or started but not completed
within a configurable time period as abandoned.
Usage:
python manage.py mark_abandoned_surveys
python manage.py mark_abandoned_surveys --hours 24
python manage.py mark_abandoned_surveys --dry-run
"""
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.conf import settings
from datetime import timedelta
from apps.surveys.models import SurveyInstance, SurveyTracking
class Command(BaseCommand):
help = "Mark surveys as abandoned if not completed within specified time"
def add_arguments(self, parser):
parser.add_argument(
"--hours",
type=int,
default=getattr(settings, "SURVEY_ABANDONMENT_HOURS", 24),
help="Hours after which to mark survey as abandoned (default: 24)",
)
parser.add_argument("--dry-run", action="store_true", help="Show what would be done without making changes")
def handle(self, *args, **options):
hours = options["hours"]
dry_run = options["dry_run"]
self.stdout.write(
self.style.SUCCESS(f"{'[DRY RUN] ' if dry_run else ''}Marking surveys as abandoned (after {hours} hours)")
)
cutoff_time = timezone.now() - timedelta(hours=hours)
surveys_to_abandon = SurveyInstance.objects.filter(
status__in=["viewed", "in_progress"], token_expires_at__gt=timezone.now(), last_opened_at__lt=cutoff_time
).select_related("survey_template", "patient")
count = surveys_to_abandon.count()
if count == 0:
self.stdout.write(self.style.WARNING("No surveys to mark as abandoned"))
return
self.stdout.write(f"Found {count} surveys to mark as abandoned:")
for survey in surveys_to_abandon:
time_since_open = timezone.now() - survey.last_opened_at
hours_since_open = time_since_open.total_seconds() / 3600
tracking_events = survey.tracking_events.filter(event_type="question_answered").count()
self.stdout.write(
f" - {survey.survey_template.name} | "
f"Patient: {survey.patient.get_full_name()} | "
f"Status: {survey.status} | "
f"Opened: {survey.last_opened_at.strftime('%Y-%m-%d %H:%M')} | "
f"{hours_since_open:.1f} hours ago | "
f"Questions answered: {tracking_events}"
)
if dry_run:
self.stdout.write(self.style.WARNING(f"\n[DRY RUN] Would mark {count} surveys as abandoned"))
return
updated = 0
for survey in surveys_to_abandon:
original_status = survey.status
time_since_open = timezone.now() - survey.last_opened_at
survey.status = "abandoned"
survey.save(update_fields=["status"])
tracking_events = survey.tracking_events.filter(event_type="question_answered")
SurveyTracking.objects.create(
survey_instance=survey,
event_type="survey_abandoned",
current_question=tracking_events.count(),
total_time_spent=survey.time_spent_seconds or 0,
metadata={
"time_since_open_hours": round(time_since_open.total_seconds() / 3600, 2),
"questions_answered": tracking_events.count(),
"original_status": original_status,
},
)
updated += 1
self.stdout.write(self.style.SUCCESS(f"\nSuccessfully marked {updated} surveys as abandoned"))