98 lines
3.6 KiB
Python
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"))
|