#!/usr/bin/env python """ Test script for Complaint to Appreciation Conversion Feature This script tests the conversion of appreciation-type complaints to Appreciation records. """ import os import sys import django # Setup Django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'PX360.settings') sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) django.setup() from django.contrib.auth import get_user_model from apps.complaints.models import Complaint, ComplaintUpdate from apps.organizations.models import Hospital, Department, Staff from app.models import Patient from apps.appreciation.models import Appreciation, AppreciationCategory from apps.accounts.models import User from django.utils import timezone from django.urls import reverse from django.test import TestCase, Client from rest_framework.test import APIClient import json User = get_user_model() def print_section(title): """Print a formatted section header""" print(f"\n{'='*60}") print(f" {title}") print(f"{'='*60}\n") def print_success(message): """Print success message""" print(f"✓ {message}") def print_error(message): """Print error message""" print(f"✗ {message}") def print_info(message): """Print info message""" print(f"ℹ {message}") def test_default_category(): """Test that default category exists""" print_section("Test 1: Default Category Check") try: category = AppreciationCategory.objects.get(code='patient_feedback') print_success(f"Default category exists: {category.name_en}") print_info(f"Category ID: {category.id}") print_info(f"Name (AR): {category.name_ar}") return True except AppreciationCategory.DoesNotExist: print_error("Default category 'patient_feedback' not found!") print_info("Run: python manage.py create_patient_feedback_category") return False def create_test_data(): """Create test data for conversion tests""" print_section("Test 2: Create Test Data") # Create PX Admin user try: px_admin = User.objects.get(email='test_px_admin@example.com') except User.DoesNotExist: px_admin = User.objects.create_user( email='test_px_admin@example.com', password='testpass123', first_name='Test', last_name='PX Admin', is_px_admin=True ) print_success("Created PX Admin user") # Create Hospital try: hospital = Hospital.objects.get(code='TEST_HOSPITAL') except Hospital.DoesNotExist: hospital = Hospital.objects.create( code='TEST_HOSPITAL', name_en='Test Hospital', name_ar='مستشفى تجريبي', city='Riyadh' ) print_success("Created Hospital") # Create Department try: department = Department.objects.get(code='TEST_DEPT') except Department.DoesNotExist: department = Department.objects.create( code='TEST_DEPT', hospital=hospital, name_en='Test Department', name_ar='قسم تجريبي' ) print_success("Created Department") # Create Staff try: staff = Staff.objects.get(email='test_staff@example.com') except Staff.DoesNotExist: staff = Staff.objects.create( email='test_staff@example.com', hospital=hospital, department=department, first_name='Test', last_name='Staff', first_name_ar='تجريبي', last_name_ar='موظف', job_title='Test Employee', job_title_ar='موظف تجريبي' ) # Create user account for staff staff.user = User.objects.create_user( email='test_staff@example.com', password='staffpass123', first_name='Test', last_name='Staff' ) staff.save() print_success("Created Staff with user account") # Create Patient try: patient = Patient.objects.get(mrn='TEST_PATIENT_001') except Patient.DoesNotExist: patient = Patient.objects.create( mrn='TEST_PATIENT_001', hospital=hospital, first_name='Test', last_name='Patient', first_name_ar='تجريبي', last_name_ar='مريض' ) print_success("Created Patient") return { 'px_admin': px_admin, 'hospital': hospital, 'department': department, 'staff': staff, 'patient': patient } def test_appreciation_type_complaint(test_data): """Test creating an appreciation-type complaint""" print_section("Test 3: Create Appreciation-Type Complaint") complaint = Complaint.objects.create( patient=test_data['patient'], hospital=test_data['hospital'], department=test_data['department'], staff=test_data['staff'], complaint_type='appreciation', category='quality_of_care', subcategory='Positive Feedback', severity='low', priority='medium', source='web', title='Excellent Service by Test Staff', description='I want to appreciate the excellent service provided by Test Staff. They were very helpful and professional.', short_description='Positive feedback about excellent service', short_description_ar='تغذية راجعة إيجابية عن خدمة ممتازة', status='open', created_by=test_data['px_admin'] ) print_success(f"Created appreciation-type complaint: {complaint.id}") print_info(f"Complaint type: {complaint.complaint_type}") print_info(f"Title: {complaint.title}") return complaint def test_api_endpoint(test_data, complaint): """Test the conversion API endpoint""" print_section("Test 4: Test Conversion API Endpoint") # Get default category category = AppreciationCategory.objects.get(code='patient_feedback') # Prepare request data data = { 'recipient_type': 'user', 'recipient_id': str(test_data['staff'].user.id), 'category_id': str(category.id), 'message_en': complaint.description, 'message_ar': complaint.short_description_ar, 'visibility': 'department', 'is_anonymous': True, 'close_complaint': True } # Create API client client = APIClient() client.force_authenticate(user=test_data['px_admin']) # Make API request url = reverse('complaints:convert_to_appreciation', kwargs={'pk': complaint.id}) print_info(f"API URL: {url}") print_info(f"Request data: {json.dumps(data, indent=2)}") response = client.post(url, data, format='json') print_info(f"Response status: {response.status_code}") if response.status_code == 201: print_success("Conversion API call successful!") result = response.json() print_info(f"Response: {json.dumps(result, indent=2)}") # Verify appreciation was created appreciation_id = result.get('appreciation_id') if appreciation_id: try: appreciation = Appreciation.objects.get(id=appreciation_id) print_success(f"Appreciation record created: {appreciation.id}") print_info(f"Appreciation message: {appreciation.message_en[:50]}...") # Verify complaint metadata complaint.refresh_from_db() if complaint.metadata.get('appreciation_id'): print_success("Complaint metadata updated with appreciation link") else: print_error("Complaint metadata not updated!") # Verify complaint status changed if complaint.status == 'closed': print_success("Complaint status changed to 'closed'") else: print_error(f"Complaint status is '{complaint.status}', expected 'closed'") # Verify timeline entry timeline_entries = ComplaintUpdate.objects.filter( complaint=complaint, update_type='note' ).count() if timeline_entries > 0: print_success(f"Timeline entry created ({timeline_entries} entries)") else: print_error("No timeline entry created!") return appreciation except Appreciation.DoesNotExist: print_error("Appreciation record not found in database!") else: print_error("No appreciation_id in response!") else: print_error(f"API call failed with status {response.status_code}") print_info(f"Response: {response.json()}") return None def test_prevention_of_duplicate_conversion(test_data, complaint): """Test that duplicate conversions are prevented""" print_section("Test 5: Prevent Duplicate Conversion") # Create API client client = APIClient() client.force_authenticate(user=test_data['px_admin']) # Get default category category = AppreciationCategory.objects.get(code='patient_feedback') # Prepare request data data = { 'recipient_type': 'user', 'recipient_id': str(test_data['staff'].user.id), 'category_id': str(category.id), 'message_en': 'Duplicate test', 'message_ar': 'اختبار مكرر', 'visibility': 'private', 'is_anonymous': True, 'close_complaint': False } # Make API request (should fail) url = reverse('complaints:convert_to_appreciation', kwargs={'pk': complaint.id}) response = client.post(url, data, format='json') print_info(f"Response status: {response.status_code}") if response.status_code == 400: print_success("Duplicate conversion prevented as expected!") result = response.json() print_info(f"Error message: {result.get('error')}") return True else: print_error("Duplicate conversion was not prevented!") print_info(f"Response: {response.json()}") return False def test_non_appreciation_type(test_data): """Test that non-appreciation complaints cannot be converted""" print_section("Test 6: Non-Appreciation Type Prevention") # Create regular complaint type complaint = Complaint.objects.create( patient=test_data['patient'], hospital=test_data['hospital'], department=test_data['department'], staff=test_data['staff'], complaint_type='complaint', # Not appreciation category='quality_of_care', subcategory='Wait Time', severity='medium', priority='high', source='web', title='Long Wait Time', description='I waited too long for my appointment.', status='open', created_by=test_data['px_admin'] ) print_success("Created regular complaint-type complaint") # Try to convert client = APIClient() client.force_authenticate(user=test_data['px_admin']) category = AppreciationCategory.objects.get(code='patient_feedback') data = { 'recipient_type': 'user', 'recipient_id': str(test_data['staff'].user.id), 'category_id': str(category.id), 'message_en': 'Test', 'message_ar': 'اختبار', 'visibility': 'private', 'is_anonymous': True, 'close_complaint': False } url = reverse('complaints:convert_to_appreciation', kwargs={'pk': complaint.id}) response = client.post(url, data, format='json') print_info(f"Response status: {response.status_code}") if response.status_code == 400: print_success("Non-appreciation conversion prevented as expected!") result = response.json() print_info(f"Error message: {result.get('error')}") return True else: print_error("Non-appreciation conversion was not prevented!") return False def cleanup_test_data(): """Clean up test data""" print_section("Cleanup: Remove Test Data") # Delete test appreciations Appreciation.objects.filter( recipient=test_data['staff'] ).delete() print_success("Deleted test appreciations") # Delete test complaints Complaint.objects.filter( patient=test_data['patient'] ).delete() print_success("Deleted test complaints") # Delete test patient test_data['patient'].delete() print_success("Deleted test patient") # Delete test staff user if test_data['staff'].user: test_data['staff'].user.delete() test_data['staff'].delete() print_success("Deleted test staff") # Delete test department test_data['department'].delete() print_success("Deleted test department") # Delete test hospital test_data['hospital'].delete() print_success("Deleted test hospital") # Delete test px admin test_data['px_admin'].delete() print_success("Deleted test PX admin") def main(): """Main test runner""" print_section("Complaint to Appreciation Conversion - Test Suite") print_info("This test suite validates the conversion feature functionality\n") all_passed = True # Test 1: Check default category if not test_default_category(): print_error("Cannot proceed without default category!") return False # Test 2: Create test data global test_data test_data = create_test_data() # Test 3: Create appreciation-type complaint complaint = test_appreciation_type_complaint(test_data) # Test 4: Test API endpoint appreciation = test_api_endpoint(test_data, complaint) if not appreciation: all_passed = False # Test 5: Prevent duplicate conversion if complaint.metadata.get('appreciation_id'): if not test_prevention_of_duplicate_conversion(test_data, complaint): all_passed = False # Test 6: Prevent non-appreciation conversion if not test_non_appreciation_type(test_data): all_passed = False # Cleanup cleanup_test_data() # Summary print_section("Test Summary") if all_passed: print_success("All tests passed! ✓") print_info("\nThe Complaint to Appreciation Conversion feature is working correctly.") else: print_error("Some tests failed! ✗") print_info("\nPlease review the error messages above and fix any issues.") return all_passed if __name__ == '__main__': try: success = main() sys.exit(0 if success else 1) except Exception as e: print_error(f"Unexpected error: {e}") import traceback traceback.print_exc() sys.exit(1)