HH/test_scenario_1_successful_explanation.py

295 lines
12 KiB
Python

#!/usr/bin/env python
"""
Scenario 1: Successful Explanation Submission
Tests the happy path where a staff member submits their explanation
before the SLA deadline. No escalation should occur.
Time Compression: 1 second = 1 hour
SLA Deadline: 10 hours (10 seconds)
"""
import os
import sys
import django
# Setup Django
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.dev')
django.setup()
from scenario_test_base import ScenarioTestBase
from django.utils import timezone
from apps.complaints.models import Complaint, ComplaintCategory, ComplaintExplanation, ComplaintSLAConfig
from apps.organizations.models import Hospital, Department, Staff
from apps.accounts.models import User, UserManager
class Scenario1SuccessfulExplanation(ScenarioTestBase):
"""Test successful explanation submission before SLA deadline"""
def run(self):
"""Run the complete scenario"""
self.print_header("SCENARIO 1: SUCCESSFUL EXPLANATION SUBMISSION")
successful_steps = 0
total_steps = 7
try:
# Step 1: Setup
self.print_step("Setting up test environment", 0)
hospital, department, staff = self.setup_environment()
successful_steps += 1
# Step 2: Create complaint
self.print_step("Creating complaint", 2) # Wait 2s (simulates 2 hours)
complaint = self.create_complaint(hospital, department, staff)
successful_steps += 1
# Step 3: Request explanation
self.print_step("Requesting explanation from staff", 1) # Wait 1s (simulates 1 hour)
explanation = self.request_explanation(complaint, staff)
successful_steps += 1
# Step 4: Verify initial state (pending, no reminder, no overdue)
self.print_step("Verifying initial explanation state", 0)
if self.verify_explanation_state(explanation, 'pending'):
successful_steps += 1
else:
self.print_error("Initial state verification failed")
# Step 5: Staff submits explanation (before deadline)
self.print_step("Staff submits explanation (before deadline)", 3) # Wait 3s (simulates 3 hours)
self.submit_explanation(explanation, "This is my explanation about the complaint.")
successful_steps += 1
# Step 6: Verify explanation submitted
self.print_step("Verifying explanation submitted successfully", 0)
if self.verify_explanation_state(explanation, 'submitted'):
successful_steps += 1
else:
self.print_error("Submission verification failed")
# Step 7: Verify no escalation occurred
self.print_step("Verifying no escalation occurred", 0)
explanation.refresh_from_db()
if explanation.escalated_to_manager is None:
self.print_success("No escalation occurred (as expected)")
successful_steps += 1
else:
self.print_error("Unexpected escalation occurred")
except Exception as e:
self.print_error(f"Test failed with exception: {str(e)}")
import traceback
traceback.print_exc()
self.print_summary(total_steps, successful_steps)
return successful_steps == total_steps
def setup_environment(self):
"""Setup hospital, department, and staff"""
# Get or create hospital
hospital = Hospital.objects.filter(name__icontains="Al Hammadi").first()
if not hospital:
hospital = Hospital.objects.create(
name="Al Hammadi Hospital",
name_ar="مستشفى الحمادي",
code="ALH",
phone="+966114444444",
email="info@alhammadi.sa",
status='active'
)
self.print_success(f"Created hospital: {hospital.name}")
# Get or create department
department = Department.objects.filter(
hospital=hospital,
name__icontains="Emergency"
).first()
if not department:
department = Department.objects.create(
hospital=hospital,
name="Emergency Department",
name_ar="قسم الطوارئ",
code="ED",
status='active'
)
self.print_success(f"Created department: {department.name}")
# Create or get staff member
staff, created = Staff.objects.get_or_create(
email=f"omar.harbi@{hospital.name.lower().replace(' ', '')}.test",
defaults={
'hospital': hospital,
'department': department,
'first_name': "Omar",
'last_name': "Al-Harbi",
'first_name_ar': "عمر",
'last_name_ar': "الحربي",
'job_title': "Nurse",
'specialization': "Patient Care",
'phone': "+966513333333",
'status': 'active'
}
)
if created:
self.print_success(f"Created staff member: {staff.get_full_name()}")
else:
self.print_info(f"Using existing staff member: {staff.get_full_name()}")
# Configure SLA
self.create_explanation_sla_config(
hospital=hospital,
response_hours=10, # 10 hours = 10 seconds in test
reminder_hours_before=5, # Reminder 5 hours before
auto_escalate_enabled=True,
escalation_hours_overdue=0
)
self.create_complaint_sla_config(
hospital=hospital,
severity='medium',
priority='medium',
sla_hours=72,
reminder_hours_before=24
)
return hospital, department, staff
def create_complaint(self, hospital, department, staff):
"""Create a test complaint"""
# Get or create category
category = ComplaintCategory.objects.filter(
hospitals=hospital,
name_en__icontains="Service"
).first()
if not category:
category = ComplaintCategory.objects.create(
code="SRV",
name_en="Service Quality",
name_ar="جودة الخدمة",
description_en="Complaints about service quality",
description_ar="شكاوى حول جودة الخدمة"
)
category.hospitals.add(hospital)
# Create complaint
complaint = Complaint.objects.create(
hospital=hospital,
department=department,
staff=staff,
title="Poor response time in emergency",
description="Nurse Omar Al-Harbi took too long to respond to my emergency situation. The delay was unacceptable and caused unnecessary distress.",
severity='medium',
priority='medium',
source_id=None,
status='open',
contact_name="Test Patient",
contact_phone="+966599999999",
contact_email="patient@test.com"
)
self.print_success(
f"Created complaint: {complaint.title[:50]}... "
f"(ID: {complaint.id})"
)
self.print_info(f" Severity: {complaint.severity}")
self.print_info(f" Priority: {complaint.priority}")
self.print_info(f" Status: {complaint.status}")
self.print_info(f" Staff: {staff.get_full_name()}")
return complaint
def request_explanation(self, complaint, staff):
"""Request explanation from staff"""
# Get admin user
admin = User.objects.filter(
hospital=complaint.hospital,
groups__name='Hospital Admin'
).first()
if not admin:
# Create admin user using User.objects.create_user
admin = User.objects.create_user(
email=f"admin@{complaint.hospital.name.lower().replace(' ', '')}.test",
password="test123",
first_name="Hospital",
last_name="Admin",
hospital=complaint.hospital,
phone="+966500000000"
)
from django.contrib.auth.models import Group
admin_group, _ = Group.objects.get_or_create(name='Hospital Admin')
admin.groups.add(admin_group)
# Create explanation request
import secrets
explanation = ComplaintExplanation.objects.create(
complaint=complaint,
staff=staff,
explanation='', # Will be filled by staff
token=secrets.token_urlsafe(64),
is_used=False,
submitted_via='email_link',
requested_by=admin,
request_message="Please provide your explanation about this complaint.",
email_sent_at=None, # Will be set by email task
sla_due_at=None # Will be set by email task
)
self.print_success(f"Created explanation request for {staff.get_full_name()}")
self.print_info(f" Token: {explanation.token[:20]}...")
# Send email (simulate by calling the task directly)
from apps.complaints.tasks import send_explanation_request_email
result = send_explanation_request_email(str(explanation.id))
if result['status'] == 'sent':
self.print_success(f"Explanation request email sent")
explanation.refresh_from_db()
self.print_info(f" SLA Due At: {explanation.sla_due_at}")
hours_until_due = (explanation.sla_due_at - timezone.now()).total_seconds() / 3600
self.print_info(f" Hours until deadline: {hours_until_due:.1f}")
else:
self.print_error(f"Failed to send email: {result.get('reason', 'Unknown')}")
return explanation
def submit_explanation(self, explanation, explanation_text):
"""Simulate staff submitting explanation"""
# Update explanation
explanation.explanation = explanation_text
explanation.is_used = True
explanation.responded_at = timezone.now()
explanation.save(update_fields=['explanation', 'is_used', 'responded_at'])
self.print_success("Staff submitted explanation")
self.print_info(f" Response time: {explanation.responded_at}")
self.print_info(f" SLA deadline: {explanation.sla_due_at}")
# Check if submitted before deadline
if explanation.responded_at < explanation.sla_due_at:
hours_before_deadline = (explanation.sla_due_at - explanation.responded_at).total_seconds() / 3600
self.print_success(f"Submitted {hours_before_deadline:.1f} hours BEFORE deadline ✓")
else:
hours_after_deadline = (explanation.responded_at - explanation.sla_due_at).total_seconds() / 3600
self.print_warning(f"Submitted {hours_after_deadline:.1f} hours AFTER deadline")
return explanation
def main():
"""Main entry point"""
test = Scenario1SuccessfulExplanation(time_compression_ratio=1) # 1s = 1 hour
success = test.run()
if success:
print("\n✓ Scenario 1 completed successfully!")
sys.exit(0)
else:
print("\n✗ Scenario 1 failed!")
sys.exit(1)
if __name__ == '__main__':
main()