1718 lines
72 KiB
Python
1718 lines
72 KiB
Python
|
|
"""
|
|
Saudi-influenced data generator for PX360
|
|
|
|
This script generates realistic test data for the PX360 system with:
|
|
- Saudi hospital names
|
|
- Arabic names for patients and staff
|
|
- Saudi cities and regions
|
|
- Realistic medical specializations
|
|
- Sample complaints, surveys, and actions
|
|
"""
|
|
import os
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
|
|
import django
|
|
|
|
# Setup Django
|
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.dev')
|
|
# Disable Celery tasks during data generation
|
|
os.environ.setdefault('CELERY_TASK_ALWAYS_EAGER', 'True')
|
|
django.setup()
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.utils import timezone
|
|
|
|
from apps.accounts.models import User
|
|
from apps.appreciation.models import (
|
|
Appreciation,
|
|
AppreciationCategory,
|
|
AppreciationBadge,
|
|
UserBadge,
|
|
AppreciationStats,
|
|
AppreciationStatus,
|
|
AppreciationVisibility,
|
|
)
|
|
from apps.observations.models import (
|
|
Observation,
|
|
ObservationCategory,
|
|
ObservationNote,
|
|
ObservationStatusLog,
|
|
ObservationSeverity,
|
|
ObservationStatus,
|
|
)
|
|
from apps.complaints.models import Complaint, ComplaintCategory, ComplaintUpdate
|
|
from apps.journeys.models import (
|
|
PatientJourneyInstance,
|
|
PatientJourneyStageInstance,
|
|
PatientJourneyStageTemplate,
|
|
PatientJourneyTemplate,
|
|
)
|
|
from apps.organizations.models import Department, Hospital, Organization, Patient, Staff
|
|
from apps.projects.models import QIProject
|
|
from apps.px_action_center.models import PXAction
|
|
from apps.surveys.models import SurveyInstance, SurveyQuestion, SurveyResponse, SurveyTemplate
|
|
|
|
# Saudi-specific data
|
|
SAUDI_ORGANIZATIONS = [
|
|
{
|
|
'name': 'Alhammadi Group',
|
|
'name_ar': 'مجموعة الحمادي',
|
|
'code': 'AHG',
|
|
'phone': '+9661123456789',
|
|
'email': 'info@alhammadi.sa',
|
|
'website': 'https://alhammadi.sa',
|
|
'city': 'Riyadh',
|
|
}
|
|
]
|
|
|
|
SAUDI_HOSPITALS = [
|
|
{'name': 'Alhammadi Hospital', 'name_ar': 'مستشفى الحمادي', 'city': 'Riyadh', 'code': 'HH'},
|
|
# {'name': 'King Faisal Specialist Hospital', 'name_ar': 'مستشفى الملك فيصل التخصصي', 'city': 'Riyadh', 'code': 'KFSH'},
|
|
# {'name': 'King Abdulaziz Medical City', 'name_ar': 'مدينة الملك عبدالعزيز الطبية', 'city': 'Riyadh', 'code': 'KAMC'},
|
|
# {'name': 'King Khalid University Hospital', 'name_ar': 'مستشفى الملك خالد الجامعي', 'city': 'Riyadh', 'code': 'KKUH'},
|
|
# {'name': 'King Abdullah Medical Complex', 'name_ar': 'مجمع الملك عبدالله الطبي', 'city': 'Jeddah', 'code': 'KAMC-JED'},
|
|
]
|
|
|
|
SAUDI_CITIES = ['Riyadh', 'Jeddah', 'Mecca', 'Medina', 'Dammam', 'Khobar', 'Dhahran', 'Taif', 'Buraidah', 'Tabuk']
|
|
|
|
ARABIC_FIRST_NAMES_MALE = ['محمد', 'أحمد', 'عبدالله', 'خالد', 'سعود', 'فهد', 'عبدالعزيز', 'سلطان', 'فيصل', 'عمر']
|
|
ARABIC_FIRST_NAMES_FEMALE = ['فاطمة', 'عائشة', 'مريم', 'نورة', 'سارة', 'هند', 'لطيفة', 'منى', 'ريم', 'جواهر']
|
|
ARABIC_LAST_NAMES = ['العتيبي', 'الدوسري', 'القحطاني', 'الشمري', 'الحربي', 'المطيري', 'العنزي', 'الزهراني', 'الغامدي', 'الشهري']
|
|
|
|
ENGLISH_FIRST_NAMES_MALE = ['Mohammed', 'Ahmed', 'Abdullah', 'Khalid', 'Saud', 'Fahd', 'Abdulaziz', 'Sultan', 'Faisal', 'Omar']
|
|
ENGLISH_FIRST_NAMES_FEMALE = ['Fatimah', 'Aisha', 'Maryam', 'Noura', 'Sarah', 'Hind', 'Latifa', 'Mona', 'Reem', 'Jawaher']
|
|
ENGLISH_LAST_NAMES = ['Al-Otaibi', 'Al-Dosari', 'Al-Qahtani', 'Al-Shammari', 'Al-Harbi', 'Al-Mutairi', 'Al-Anazi', 'Al-Zahrani', 'Al-Ghamdi', 'Al-Shehri']
|
|
|
|
DEPARTMENTS = [
|
|
{'name': 'Emergency Department', 'name_ar': 'قسم الطوارئ', 'code': 'ER'},
|
|
{'name': 'Outpatient Department', 'name_ar': 'قسم العيادات الخارجية', 'code': 'OPD'},
|
|
{'name': 'Internal Medicine', 'name_ar': 'الطب الباطني', 'code': 'IM'},
|
|
{'name': 'Surgery', 'name_ar': 'الجراحة', 'code': 'SURG'},
|
|
{'name': 'Pediatrics', 'name_ar': 'طب الأطفال', 'code': 'PEDS'},
|
|
{'name': 'Obstetrics & Gynecology', 'name_ar': 'النساء والولادة', 'code': 'OBGYN'},
|
|
{'name': 'Radiology', 'name_ar': 'الأشعة', 'code': 'RAD'},
|
|
{'name': 'Laboratory', 'name_ar': 'المختبر', 'code': 'LAB'},
|
|
{'name': 'Pharmacy', 'name_ar': 'الصيدلية', 'code': 'PHARM'},
|
|
{'name': 'Cardiology', 'name_ar': 'أمراض القلب', 'code': 'CARD'},
|
|
]
|
|
|
|
SPECIALIZATIONS = [
|
|
'Internal Medicine', 'General Surgery', 'Pediatrics', 'Obstetrics & Gynecology',
|
|
'Cardiology', 'Orthopedics', 'Neurology', 'Dermatology', 'Ophthalmology',
|
|
'ENT', 'Urology', 'Nephrology', 'Gastroenterology', 'Pulmonology'
|
|
]
|
|
|
|
COMPLAINT_TITLES = [
|
|
'Long waiting time in emergency department',
|
|
'Poor communication from nursing staff',
|
|
'Delayed lab results',
|
|
'Billing discrepancy',
|
|
'Unclean patient room',
|
|
'Medication error',
|
|
'Rude behavior from reception staff',
|
|
'Difficulty scheduling appointment',
|
|
'Lost medical records',
|
|
'Inadequate pain management',
|
|
]
|
|
|
|
COMPLAINT_TITLES_AR = [
|
|
'وقت انتظار طويل في قسم الطوارئ',
|
|
'ضعف التواصل من طاقم التمريض',
|
|
'تأخر نتائج المختبر',
|
|
'خطأ في الفاتورة',
|
|
'غرفة المريض غير نظيفة',
|
|
'خطأ في الدواء',
|
|
'سلوك غير لائق من موظف الاستقبال',
|
|
'صعوبة في حجز موعد',
|
|
'فقدان السجلات الطبية',
|
|
'إدارة غير كافية للألم',
|
|
]
|
|
|
|
|
|
def clear_existing_data():
|
|
"""Clear all existing data from the database"""
|
|
print("\n" + "="*60)
|
|
print("Clearing Existing Data...")
|
|
print("="*60 + "\n")
|
|
|
|
from apps.feedback.models import Feedback, FeedbackAttachment, FeedbackResponse
|
|
from apps.social.models import SocialMediaComment
|
|
from apps.callcenter.models import CallCenterInteraction
|
|
|
|
# Delete in reverse order of dependencies
|
|
print("Deleting survey instances...")
|
|
SurveyResponse.objects.all().delete()
|
|
SurveyInstance.objects.all().delete()
|
|
|
|
print("Deleting journey instances...")
|
|
PatientJourneyStageInstance.objects.all().delete()
|
|
PatientJourneyInstance.objects.all().delete()
|
|
|
|
print("Deleting PX actions...")
|
|
PXAction.objects.all().delete()
|
|
|
|
print("Deleting QI projects...")
|
|
QIProject.objects.all().delete()
|
|
|
|
print("Deleting social comments...")
|
|
SocialMediaComment.objects.all().delete()
|
|
|
|
print("Deleting call center interactions...")
|
|
CallCenterInteraction.objects.all().delete()
|
|
|
|
print("Deleting feedback...")
|
|
FeedbackResponse.objects.all().delete()
|
|
FeedbackAttachment.objects.all().delete()
|
|
Feedback.objects.all().delete()
|
|
|
|
print("Deleting complaints...")
|
|
ComplaintUpdate.objects.all().delete()
|
|
Complaint.objects.all().delete()
|
|
|
|
print("Deleting survey templates...")
|
|
SurveyQuestion.objects.all().delete()
|
|
SurveyTemplate.objects.all().delete()
|
|
|
|
print("Deleting journey templates...")
|
|
PatientJourneyStageTemplate.objects.all().delete()
|
|
PatientJourneyTemplate.objects.all().delete()
|
|
|
|
print("Deleting patients...")
|
|
Patient.objects.all().delete()
|
|
|
|
print("Deleting physician ratings...")
|
|
from apps.physicians.models import PhysicianMonthlyRating
|
|
PhysicianMonthlyRating.objects.all().delete()
|
|
|
|
print("Deleting staff...")
|
|
Staff.objects.all().delete()
|
|
|
|
print("Deleting departments...")
|
|
Department.objects.all().delete()
|
|
|
|
print("Deleting hospitals...")
|
|
Hospital.objects.all().delete()
|
|
|
|
print("Deleting users (except superusers)...")
|
|
User.objects.filter(is_superuser=False).delete()
|
|
|
|
print("\n✓ All existing data cleared successfully!\n")
|
|
|
|
|
|
def generate_saudi_phone():
|
|
"""Generate Saudi phone number"""
|
|
return f"+966{random.choice(['50', '53', '54', '55', '56', '58'])}{random.randint(1000000, 9999999)}"
|
|
|
|
|
|
def generate_mrn():
|
|
"""Generate Medical Record Number"""
|
|
return f"{random.randint(100000, 999999)}"
|
|
|
|
|
|
def generate_national_id():
|
|
"""Generate Saudi National ID"""
|
|
return f"{random.randint(1000000000, 2999999999)}"
|
|
|
|
|
|
def create_hospitals():
|
|
"""Create organization and Saudi hospitals"""
|
|
print("Creating organization and hospitals...")
|
|
|
|
# Create organization first
|
|
org_data = SAUDI_ORGANIZATIONS[0]
|
|
organization, created = Organization.objects.get_or_create(
|
|
code=org_data['code'],
|
|
defaults={
|
|
'name': org_data['name'],
|
|
'name_ar': org_data['name_ar'],
|
|
'phone': org_data['phone'],
|
|
'email': org_data['email'],
|
|
'website': org_data['website'],
|
|
'city': org_data['city'],
|
|
'status': 'active',
|
|
}
|
|
)
|
|
if created:
|
|
print(f" Created organization: {organization.name}")
|
|
else:
|
|
print(f" Organization already exists: {organization.name}")
|
|
|
|
# Create hospitals linked to organization
|
|
hospitals = []
|
|
for hosp_data in SAUDI_HOSPITALS:
|
|
hospital, created = Hospital.objects.get_or_create(
|
|
code=hosp_data['code'],
|
|
defaults={
|
|
'organization': organization,
|
|
'name': hosp_data['name'],
|
|
'name_ar': hosp_data['name_ar'],
|
|
'city': hosp_data['city'],
|
|
'phone': generate_saudi_phone(),
|
|
'status': 'active',
|
|
'capacity': random.randint(200, 800),
|
|
}
|
|
)
|
|
hospitals.append(hospital)
|
|
if created:
|
|
print(f" Created hospital: {hospital.name}")
|
|
else:
|
|
print(f" Hospital already exists: {hospital.name}")
|
|
|
|
print(f"\n Total: {len(hospitals)} hospitals in {organization.name}")
|
|
return hospitals
|
|
|
|
|
|
def create_departments(hospitals):
|
|
"""Create departments for each hospital"""
|
|
print("Creating departments...")
|
|
departments = []
|
|
for hospital in hospitals:
|
|
for dept_data in DEPARTMENTS:
|
|
dept, created = Department.objects.get_or_create(
|
|
hospital=hospital,
|
|
code=dept_data['code'],
|
|
defaults={
|
|
'name': dept_data['name'],
|
|
'name_ar': dept_data['name_ar'],
|
|
'status': 'active',
|
|
}
|
|
)
|
|
departments.append(dept)
|
|
if created:
|
|
print(f" Created: {hospital.name} - {dept.name}")
|
|
return departments
|
|
|
|
|
|
def create_staff(hospitals, departments):
|
|
"""Create staff"""
|
|
print("Creating staff...")
|
|
staff = []
|
|
for i in range(50):
|
|
hospital = random.choice(hospitals)
|
|
dept = random.choice([d for d in departments if d.hospital == hospital])
|
|
|
|
first_name_ar = random.choice(ARABIC_FIRST_NAMES_MALE)
|
|
last_name_ar = random.choice(ARABIC_LAST_NAMES)
|
|
first_name = random.choice(ENGLISH_FIRST_NAMES_MALE)
|
|
last_name = random.choice(ENGLISH_LAST_NAMES)
|
|
|
|
staff_member, created = Staff.objects.get_or_create(
|
|
license_number=f"LIC{random.randint(10000, 99999)}",
|
|
defaults={
|
|
'first_name': first_name,
|
|
'last_name': last_name,
|
|
'first_name_ar': first_name_ar,
|
|
'last_name_ar': last_name_ar,
|
|
'specialization': random.choice(SPECIALIZATIONS),
|
|
'hospital': hospital,
|
|
'department': dept,
|
|
# 'phone': generate_saudi_phone(),
|
|
'status': 'active',
|
|
'employee_id': f"EMP{random.randint(10000, 99999)}",
|
|
}
|
|
)
|
|
staff.append(staff_member)
|
|
print(f" Created {len(staff)} staff members")
|
|
return staff
|
|
|
|
|
|
def create_patients(hospitals):
|
|
"""Create patients"""
|
|
print("Creating patients...")
|
|
patients = []
|
|
for i in range(100):
|
|
gender = random.choice(['male', 'female'])
|
|
if gender == 'male':
|
|
first_name_ar = random.choice(ARABIC_FIRST_NAMES_MALE)
|
|
first_name = random.choice(ENGLISH_FIRST_NAMES_MALE)
|
|
else:
|
|
first_name_ar = random.choice(ARABIC_FIRST_NAMES_FEMALE)
|
|
first_name = random.choice(ENGLISH_FIRST_NAMES_FEMALE)
|
|
|
|
last_name_ar = random.choice(ARABIC_LAST_NAMES)
|
|
last_name = random.choice(ENGLISH_LAST_NAMES)
|
|
|
|
patient, created = Patient.objects.get_or_create(
|
|
mrn=generate_mrn(),
|
|
defaults={
|
|
'first_name': first_name,
|
|
'last_name': last_name,
|
|
'first_name_ar': first_name_ar,
|
|
'last_name_ar': last_name_ar,
|
|
'national_id': generate_national_id(),
|
|
'phone': generate_saudi_phone(),
|
|
'email': f"{first_name.lower()}.{last_name.lower()}@example.com",
|
|
'gender': gender,
|
|
'date_of_birth': datetime.now().date() - timedelta(days=random.randint(365*18, 365*80)),
|
|
'city': random.choice(SAUDI_CITIES),
|
|
'primary_hospital': random.choice(hospitals),
|
|
'status': 'active',
|
|
}
|
|
)
|
|
patients.append(patient)
|
|
print(f" Created {len(patients)} patients")
|
|
return patients
|
|
|
|
|
|
def create_users(hospitals):
|
|
"""Create system users"""
|
|
print("Creating users...")
|
|
from django.contrib.auth.models import Group
|
|
|
|
# Create or get groups
|
|
px_admin_group, _ = Group.objects.get_or_create(name='PX Admin')
|
|
hospital_admin_group, _ = Group.objects.get_or_create(name='Hospital Admin')
|
|
|
|
# Create PX Admin
|
|
px_admin, created = User.objects.get_or_create(
|
|
username='px_admin',
|
|
defaults={
|
|
'email': 'px.admin@alhammadi.sa',
|
|
'first_name': 'PX',
|
|
'last_name': 'Administrator',
|
|
'is_staff': True,
|
|
'is_superuser': True,
|
|
}
|
|
)
|
|
if created:
|
|
px_admin.set_password('admin123')
|
|
px_admin.save()
|
|
px_admin.groups.add(px_admin_group)
|
|
print(" Created PX Admin")
|
|
|
|
# Create Hospital Admins
|
|
for hospital in hospitals:
|
|
user, created = User.objects.get_or_create(
|
|
username=f'admin_{hospital.code.lower()}',
|
|
defaults={
|
|
'email': f'admin@{hospital.code.lower()}.sa',
|
|
'first_name': 'Hospital',
|
|
'last_name': 'Admin',
|
|
'hospital': hospital,
|
|
'is_staff': True,
|
|
}
|
|
)
|
|
if created:
|
|
user.set_password('admin123')
|
|
user.save()
|
|
user.groups.add(hospital_admin_group)
|
|
print(f" Created Hospital Admin for {hospital.name}")
|
|
|
|
|
|
def create_complaint_categories(hospitals):
|
|
"""Create complaint categories"""
|
|
print("Creating complaint categories...")
|
|
|
|
# System-wide categories (no hospitals in ManyToMany)
|
|
system_categories = [
|
|
{'code': 'CLINICAL', 'name_en': 'Clinical Care', 'name_ar': 'الرعاية السريرية', 'order': 1},
|
|
{'code': 'STAFF', 'name_en': 'Staff Behavior', 'name_ar': 'سلوك الموظفين', 'order': 2},
|
|
{'code': 'FACILITY', 'name_en': 'Facility Issues', 'name_ar': 'مشاكل المرافق', 'order': 3},
|
|
{'code': 'WAIT_TIME', 'name_en': 'Wait Time', 'name_ar': 'وقت الانتظار', 'order': 4},
|
|
{'code': 'BILLING', 'name_en': 'Billing & Insurance', 'name_ar': 'الفوترة والتأمين', 'order': 5},
|
|
{'code': 'COMM', 'name_en': 'Communication', 'name_ar': 'التواصل', 'order': 6},
|
|
]
|
|
|
|
categories = []
|
|
|
|
# Create system-wide categories (parent__isnull=True and no hospitals)
|
|
for cat_data in system_categories:
|
|
category, created = ComplaintCategory.objects.get_or_create(
|
|
code=cat_data['code'],
|
|
parent__isnull=True,
|
|
defaults={
|
|
'name_en': cat_data['name_en'],
|
|
'name_ar': cat_data['name_ar'],
|
|
'order': cat_data['order'],
|
|
'is_active': True,
|
|
}
|
|
)
|
|
if created:
|
|
print(f" Created system-wide category: {category.name_en}")
|
|
categories.append(category)
|
|
|
|
# Create hospital-specific categories for each hospital
|
|
hospital_specific_categories = [
|
|
{'code': 'ER', 'name_en': 'Emergency Services', 'name_ar': 'خدمات الطوارئ'},
|
|
{'code': 'SURGERY', 'name_en': 'Surgery Department', 'name_ar': 'قسم الجراحة'},
|
|
{'code': 'LAB', 'name_en': 'Laboratory Services', 'name_ar': 'خدمات المختبر'},
|
|
]
|
|
|
|
for hospital in hospitals:
|
|
for cat_data in hospital_specific_categories:
|
|
category, created = ComplaintCategory.objects.get_or_create(
|
|
code=f"{cat_data['code']}_{hospital.code}",
|
|
defaults={
|
|
'name_en': cat_data['name_en'],
|
|
'name_ar': cat_data['name_ar'],
|
|
'order': random.randint(10, 20),
|
|
'is_active': True,
|
|
}
|
|
)
|
|
if created:
|
|
category.hospitals.add(hospital)
|
|
print(f" Created hospital category for {hospital.name}: {category.name_en}")
|
|
else:
|
|
# Ensure hospital is in the hospitals ManyToMany
|
|
if hospital not in category.hospitals.all():
|
|
category.hospitals.add(hospital)
|
|
categories.append(category)
|
|
|
|
print(f" Created {len(categories)} complaint categories")
|
|
return categories
|
|
|
|
|
|
def create_complaints(patients, hospitals, staff, users):
|
|
"""Create sample complaints with 2 years of data"""
|
|
print("Creating complaints (2 years of data)...")
|
|
|
|
# Get available complaint categories
|
|
categories = list(ComplaintCategory.objects.filter(is_active=True))
|
|
if not categories:
|
|
print(" ERROR: No complaint categories found! Please create categories first.")
|
|
return []
|
|
|
|
complaints = []
|
|
now = timezone.now()
|
|
|
|
# Generate complaints over 2 years (730 days)
|
|
# Average 3-5 complaints per day = ~1200-1800 total
|
|
for day_offset in range(30):
|
|
# Random number of complaints per day (0-8, weighted towards 2-4)
|
|
num_complaints = random.choices([0, 1, 2, 3, 4, 5, 6, 7, 8], weights=[5, 10, 20, 25, 20, 10, 5, 3, 2])[0]
|
|
|
|
for _ in range(num_complaints):
|
|
patient = random.choice(patients)
|
|
hospital = patient.primary_hospital or random.choice(hospitals)
|
|
created_date = now - timedelta(days=day_offset, hours=random.randint(0, 23), minutes=random.randint(0, 59))
|
|
|
|
# Status distribution based on age
|
|
if day_offset < 7: # Last week - more open/in_progress
|
|
status = random.choices(['open', 'in_progress', 'resolved', 'closed'], weights=[30, 40, 20, 10])[0]
|
|
elif day_offset < 30: # Last month
|
|
status = random.choices(['open', 'in_progress', 'resolved', 'closed'], weights=[10, 30, 40, 20])[0]
|
|
else: # Older - mostly resolved/closed
|
|
status = random.choices(['open', 'in_progress', 'resolved', 'closed'], weights=[2, 5, 30, 63])[0]
|
|
|
|
# Select appropriate category (system-wide or hospital-specific)
|
|
hospital_categories = [c for c in categories if hospital in c.hospitals.all()]
|
|
system_categories_list = [c for c in categories if c.hospitals.count() == 0]
|
|
|
|
# Prefer hospital-specific categories if available, otherwise use system-wide
|
|
available_categories = hospital_categories if hospital_categories else system_categories_list
|
|
|
|
complaint = Complaint.objects.create(
|
|
patient=patient,
|
|
hospital=hospital,
|
|
department=random.choice(hospital.departments.all()) if hospital.departments.exists() else None,
|
|
staff=random.choice(staff) if random.random() > 0.5 else None,
|
|
title=random.choice(COMPLAINT_TITLES),
|
|
description=f"Detailed description of the complaint. Patient experienced issues during their visit.",
|
|
category=random.choice(available_categories) if available_categories else None,
|
|
priority=random.choice(['low', 'medium', 'high']),
|
|
severity=random.choice(['low', 'medium', 'high', 'critical']),
|
|
status=status,
|
|
encounter_id=f"ENC{random.randint(100000, 999999)}",
|
|
assigned_to=random.choice(users) if random.random() > 0.5 else None,
|
|
)
|
|
|
|
# Override created_at
|
|
complaint.created_at = created_date
|
|
|
|
# Set resolved/closed dates if applicable
|
|
if status in ['resolved', 'closed']:
|
|
complaint.resolved_at = created_date + timedelta(hours=random.randint(24, 168))
|
|
complaint.resolved_by = random.choice(users)
|
|
if status == 'closed':
|
|
complaint.closed_at = complaint.resolved_at + timedelta(hours=random.randint(1, 48))
|
|
complaint.closed_by = random.choice(users)
|
|
|
|
complaint.save()
|
|
complaints.append(complaint)
|
|
|
|
print(f" Created {len(complaints)} complaints over 2 years")
|
|
return complaints
|
|
|
|
|
|
def create_inquiries(patients, hospitals, users):
|
|
"""Create inquiries with 2 years of data"""
|
|
print("Creating inquiries (2 years of data)...")
|
|
from apps.complaints.models import Inquiry
|
|
|
|
inquiries = []
|
|
now = timezone.now()
|
|
|
|
inquiry_subjects = [
|
|
'Question about appointment scheduling',
|
|
'Billing inquiry',
|
|
'Request for medical records',
|
|
'General information request',
|
|
'Pharmacy hours inquiry',
|
|
'Lab results inquiry',
|
|
'Insurance coverage question',
|
|
'Visitor policy question',
|
|
'Parking information',
|
|
'Department location inquiry',
|
|
]
|
|
|
|
# Generate inquiries over 2 years (730 days)
|
|
# Average 1-2 inquiries per day = ~500-700 total
|
|
for day_offset in range(30):
|
|
num_inquiries = random.choices([0, 1, 2, 3], weights=[30, 40, 25, 5])[0]
|
|
|
|
for _ in range(num_inquiries):
|
|
patient = random.choice(patients) if random.random() > 0.3 else None
|
|
hospital = (patient.primary_hospital if patient else None) or random.choice(hospitals)
|
|
created_date = now - timedelta(days=day_offset, hours=random.randint(0, 23), minutes=random.randint(0, 59))
|
|
|
|
# Status distribution based on age
|
|
if day_offset < 7: # Last week
|
|
status = random.choices(['open', 'in_progress', 'resolved', 'closed'], weights=[40, 35, 20, 5])[0]
|
|
elif day_offset < 30: # Last month
|
|
status = random.choices(['open', 'in_progress', 'resolved', 'closed'], weights=[15, 30, 40, 15])[0]
|
|
else: # Older
|
|
status = random.choices(['open', 'in_progress', 'resolved', 'closed'], weights=[3, 7, 40, 50])[0]
|
|
|
|
inquiry = Inquiry.objects.create(
|
|
patient=patient,
|
|
contact_name=f"{random.choice(ENGLISH_FIRST_NAMES_MALE)} {random.choice(ENGLISH_LAST_NAMES)}" if not patient else '',
|
|
contact_phone=generate_saudi_phone() if not patient else '',
|
|
contact_email=f"inquiry{random.randint(1000,9999)}@example.com" if not patient else '',
|
|
hospital=hospital,
|
|
department=random.choice(hospital.departments.all()) if hospital.departments.exists() and random.random() > 0.6 else None,
|
|
subject=random.choice(inquiry_subjects),
|
|
message=f"I would like to inquire about {random.choice(['appointment', 'billing', 'services', 'procedures'])}. Please provide information.",
|
|
category=random.choice(['appointment', 'billing', 'medical_records', 'general', 'other']),
|
|
status=status,
|
|
assigned_to=random.choice(users) if random.random() > 0.5 else None,
|
|
)
|
|
|
|
# Override created_at
|
|
inquiry.created_at = created_date
|
|
|
|
# Set response if resolved/closed
|
|
if status in ['resolved', 'closed']:
|
|
inquiry.response = "Thank you for your inquiry. We have addressed your question."
|
|
inquiry.responded_at = created_date + timedelta(hours=random.randint(2, 72))
|
|
inquiry.responded_by = random.choice(users)
|
|
|
|
inquiry.save()
|
|
inquiries.append(inquiry)
|
|
|
|
print(f" Created {len(inquiries)} inquiries over 2 years")
|
|
return inquiries
|
|
|
|
|
|
def create_feedback(patients, hospitals, staff, users):
|
|
"""Create sample feedback"""
|
|
print("Creating feedback...")
|
|
from apps.feedback.models import Feedback, FeedbackResponse
|
|
|
|
feedback_titles = [
|
|
'Excellent care from Dr. Ahmed',
|
|
'Very satisfied with the service',
|
|
'Suggestion to improve waiting area',
|
|
'Great experience at the hospital',
|
|
'Staff was very helpful and kind',
|
|
'Clean and well-maintained facility',
|
|
'Quick and efficient service',
|
|
'Appreciate the professionalism',
|
|
'Suggestion for better parking',
|
|
'Outstanding nursing care',
|
|
]
|
|
|
|
feedback_messages = [
|
|
'I had a wonderful experience. The staff was very professional and caring.',
|
|
'The doctor took time to explain everything clearly. Very satisfied.',
|
|
'I suggest adding more seating in the waiting area for better comfort.',
|
|
'Everything was excellent from registration to discharge.',
|
|
'The nurses were extremely helpful and answered all my questions.',
|
|
'The facility is very clean and well-organized.',
|
|
'I was seen quickly and the process was very smooth.',
|
|
'I appreciate the high level of professionalism shown by all staff.',
|
|
'The parking area could be improved with better signage.',
|
|
'The nursing staff provided outstanding care during my stay.',
|
|
]
|
|
|
|
feedbacks = []
|
|
for i in range(40):
|
|
patient = random.choice(patients)
|
|
hospital = patient.primary_hospital or random.choice(hospitals)
|
|
is_anonymous = random.random() < 0.2 # 20% anonymous
|
|
|
|
feedback = Feedback.objects.create(
|
|
patient=None if is_anonymous else patient,
|
|
is_anonymous=is_anonymous,
|
|
contact_name=f"{random.choice(ENGLISH_FIRST_NAMES_MALE)} {random.choice(ENGLISH_LAST_NAMES)}" if is_anonymous else '',
|
|
contact_email=f"anonymous{i}@example.com" if is_anonymous else '',
|
|
contact_phone=generate_saudi_phone() if is_anonymous else '',
|
|
hospital=hospital,
|
|
department=random.choice(hospital.departments.all()) if hospital.departments.exists() and random.random() > 0.5 else None,
|
|
staff=random.choice(staff) if random.random() > 0.6 else None,
|
|
feedback_type=random.choice(['compliment', 'suggestion', 'general', 'inquiry']),
|
|
title=random.choice(feedback_titles),
|
|
message=random.choice(feedback_messages),
|
|
category=random.choice(['clinical_care', 'staff_service', 'facility', 'communication', 'appointment', 'cleanliness']),
|
|
rating=random.randint(3, 5) if random.random() > 0.3 else None,
|
|
priority=random.choice(['low', 'medium', 'high']),
|
|
sentiment=random.choice(['positive', 'neutral', 'negative']),
|
|
sentiment_score=random.uniform(0.3, 1.0) if random.random() > 0.7 else None,
|
|
status=random.choice(['submitted', 'reviewed', 'acknowledged', 'closed']),
|
|
encounter_id=f"ENC{random.randint(100000, 999999)}" if random.random() > 0.5 else '',
|
|
assigned_to=random.choice(users) if random.random() > 0.5 else None,
|
|
is_featured=random.random() < 0.15, # 15% featured
|
|
requires_follow_up=random.random() < 0.2, # 20% require follow-up
|
|
)
|
|
|
|
# Add initial response
|
|
FeedbackResponse.objects.create(
|
|
feedback=feedback,
|
|
response_type='note',
|
|
message=f"Feedback received and logged in the system.",
|
|
created_by=random.choice(users),
|
|
is_internal=True,
|
|
)
|
|
|
|
# Add additional responses for some feedback
|
|
if feedback.status in ['reviewed', 'acknowledged', 'closed'] and random.random() > 0.5:
|
|
FeedbackResponse.objects.create(
|
|
feedback=feedback,
|
|
response_type='response',
|
|
message="Thank you for your feedback. We appreciate your input and will work to improve our services.",
|
|
created_by=random.choice(users),
|
|
is_internal=False,
|
|
)
|
|
|
|
feedbacks.append(feedback)
|
|
|
|
print(f" Created {len(feedbacks)} feedback items")
|
|
return feedbacks
|
|
|
|
|
|
def create_survey_templates(hospitals):
|
|
"""Create survey templates"""
|
|
print("Creating survey templates...")
|
|
# Check if templates already exist
|
|
existing_count = SurveyTemplate.objects.count()
|
|
if existing_count > 0:
|
|
print(f" Survey templates already exist ({existing_count} found), skipping creation...")
|
|
return
|
|
|
|
for hospital in hospitals[:2]: # Create for first 2 hospitals
|
|
# OPD Survey
|
|
template = SurveyTemplate.objects.create(
|
|
name=f'OPD Patient Satisfaction Survey - {hospital.code}',
|
|
name_ar='استبيان رضا مرضى العيادات الخارجية',
|
|
description='Survey for outpatient department visits',
|
|
description_ar='استبيان لزيارات قسم العيادات الخارجية',
|
|
hospital=hospital,
|
|
survey_type='stage',
|
|
scoring_method='average',
|
|
negative_threshold=3.0,
|
|
is_active=True,
|
|
)
|
|
|
|
# Create questions
|
|
questions_data = [
|
|
{'text': 'How would you rate your overall experience?', 'text_ar': 'كيف تقيم تجربتك الإجمالية؟', 'type': 'rating'},
|
|
{'text': 'How likely are you to recommend us?', 'text_ar': 'ما مدى احتمالية توصيتك بنا؟', 'type': 'nps'},
|
|
{'text': 'Was the staff courteous and helpful?', 'text_ar': 'هل كان الموظفون مهذبين ومتعاونين؟', 'type': 'yes_no'},
|
|
{'text': 'Any additional comments?', 'text_ar': 'أي تعليقات إضافية؟', 'type': 'textarea'},
|
|
]
|
|
|
|
for idx, q_data in enumerate(questions_data):
|
|
SurveyQuestion.objects.create(
|
|
survey_template=template,
|
|
text=q_data['text'],
|
|
text_ar=q_data['text_ar'],
|
|
question_type=q_data['type'],
|
|
order=idx,
|
|
is_required=True if idx < 2 else False,
|
|
)
|
|
|
|
print(f" Created survey template for {hospital.name}")
|
|
|
|
|
|
def create_journey_templates(hospitals):
|
|
"""Create journey templates"""
|
|
print("Creating journey templates...")
|
|
for hospital in hospitals[:2]:
|
|
# OPD Journey
|
|
journey_template, created = PatientJourneyTemplate.objects.get_or_create(
|
|
hospital=hospital,
|
|
journey_type='opd',
|
|
name='OPD Journey',
|
|
defaults={
|
|
'name_ar': 'رحلة العيادات الخارجية',
|
|
'is_active': True,
|
|
'is_default': True,
|
|
}
|
|
)
|
|
|
|
if created:
|
|
# Create stages
|
|
stages_data = [
|
|
{'name': 'Registration', 'name_ar': 'التسجيل', 'code': 'REG', 'trigger': 'OPD_REGISTRATION'},
|
|
{'name': 'MD Consultation', 'name_ar': 'استشارة الطبيب', 'code': 'MD', 'trigger': 'OPD_VISIT_COMPLETED'},
|
|
{'name': 'Laboratory', 'name_ar': 'المختبر', 'code': 'LAB', 'trigger': 'LAB_COMPLETED'},
|
|
{'name': 'Pharmacy', 'name_ar': 'الصيدلية', 'code': 'PHARM', 'trigger': 'PHARMACY_DISPENSED'},
|
|
]
|
|
|
|
for idx, stage_data in enumerate(stages_data):
|
|
PatientJourneyStageTemplate.objects.create(
|
|
journey_template=journey_template,
|
|
name=stage_data['name'],
|
|
name_ar=stage_data['name_ar'],
|
|
code=stage_data['code'],
|
|
order=idx,
|
|
trigger_event_code=stage_data['trigger'],
|
|
is_active=True,
|
|
)
|
|
|
|
print(f" Created journey template for {hospital.name}")
|
|
|
|
|
|
def create_qi_projects(hospitals):
|
|
"""Create QI projects"""
|
|
print("Creating QI projects...")
|
|
project_names = [
|
|
('Reduce ER Wait Times', 'تقليل أوقات الانتظار في الطوارئ'),
|
|
('Improve Patient Satisfaction Scores', 'تحسين درجات رضا المرضى'),
|
|
('Enhance Medication Safety', 'تعزيز سلامة الأدوية'),
|
|
('Streamline Discharge Process', 'تبسيط عملية الخروج'),
|
|
]
|
|
|
|
projects = []
|
|
for hospital in hospitals[:2]:
|
|
for name_en, name_ar in project_names[:2]:
|
|
project = QIProject.objects.create(
|
|
name=name_en,
|
|
name_ar=name_ar,
|
|
description=f"Quality improvement initiative to {name_en.lower()}",
|
|
hospital=hospital,
|
|
status=random.choice(['active', 'pending', 'completed']),
|
|
start_date=datetime.now().date() - timedelta(days=random.randint(30, 180)),
|
|
target_completion_date=datetime.now().date() + timedelta(days=random.randint(30, 180)),
|
|
)
|
|
projects.append(project)
|
|
print(f" Created {len(projects)} QI projects")
|
|
return projects
|
|
|
|
|
|
def create_px_actions(complaints, hospitals, users):
|
|
"""Create PX actions"""
|
|
print("Creating PX actions...")
|
|
actions = []
|
|
for i in range(20):
|
|
hospital = random.choice(hospitals)
|
|
action = PXAction.objects.create(
|
|
source_type=random.choice(['survey', 'complaint', 'social_media', 'call_center']),
|
|
title=f"Action: {random.choice(COMPLAINT_TITLES)}",
|
|
description="Action created to address patient feedback and improve experience.",
|
|
hospital=hospital,
|
|
department=random.choice(hospital.departments.all()) if hospital.departments.exists() else None,
|
|
category=random.choice(['clinical_quality', 'patient_safety', 'service_quality', 'staff_behavior']),
|
|
priority=random.choice(['low', 'medium', 'high', 'urgent']),
|
|
severity=random.choice(['low', 'medium', 'high', 'critical']),
|
|
status=random.choice(['open', 'in_progress', 'pending_approval', 'approved', 'closed']),
|
|
assigned_to=random.choice(users) if random.random() > 0.3 else None,
|
|
)
|
|
actions.append(action)
|
|
print(f" Created {len(actions)} PX actions")
|
|
return actions
|
|
|
|
|
|
def create_journey_instances(journey_templates, patients):
|
|
"""Create journey instances"""
|
|
print("Creating journey instances...")
|
|
from apps.journeys.models import PatientJourneyTemplate
|
|
|
|
templates = PatientJourneyTemplate.objects.filter(is_active=True)
|
|
if not templates.exists():
|
|
print(" No journey templates found, skipping...")
|
|
return []
|
|
|
|
instances = []
|
|
for i in range(20):
|
|
template = random.choice(templates)
|
|
patient = random.choice(patients)
|
|
|
|
instance = PatientJourneyInstance.objects.create(
|
|
journey_template=template,
|
|
patient=patient,
|
|
encounter_id=f"ENC{random.randint(100000, 999999)}",
|
|
hospital=template.hospital,
|
|
status=random.choice(['active', 'completed']),
|
|
)
|
|
|
|
# Create stage instances
|
|
for stage_template in template.stages.all():
|
|
PatientJourneyStageInstance.objects.create(
|
|
journey_instance=instance,
|
|
stage_template=stage_template,
|
|
status=random.choice(['pending', 'completed']),
|
|
)
|
|
|
|
instances.append(instance)
|
|
print(f" Created {len(instances)} journey instances")
|
|
return instances
|
|
|
|
|
|
def create_survey_instances(survey_templates, patients, staff):
|
|
"""Create survey instances"""
|
|
print("Creating survey instances...")
|
|
from apps.surveys.models import SurveyTemplate
|
|
|
|
templates = SurveyTemplate.objects.filter(is_active=True)
|
|
if not templates.exists():
|
|
print(" No survey templates found, skipping...")
|
|
return []
|
|
|
|
instances = []
|
|
for i in range(30):
|
|
template = random.choice(templates)
|
|
patient = random.choice(patients)
|
|
staff_member = random.choice(staff) if random.random() > 0.3 else None
|
|
|
|
instance = SurveyInstance.objects.create(
|
|
survey_template=template,
|
|
hospital=template.hospital,
|
|
patient=patient,
|
|
delivery_channel=random.choice(['sms', 'whatsapp', 'email']),
|
|
recipient_phone=patient.phone,
|
|
recipient_email=patient.email,
|
|
status=random.choice(['sent', 'completed']),
|
|
sent_at=timezone.now() - timedelta(days=random.randint(1, 30)),
|
|
metadata={'staff_id': str(staff_member.id)} if staff_member else {},
|
|
)
|
|
|
|
# If completed, add responses
|
|
if instance.status == 'completed':
|
|
instance.completed_at = timezone.now() - timedelta(days=random.randint(0, 29))
|
|
for question in template.questions.all():
|
|
if question.question_type in ['rating', 'likert']:
|
|
SurveyResponse.objects.create(
|
|
survey_instance=instance,
|
|
question=question,
|
|
numeric_value=random.randint(1, 5),
|
|
)
|
|
elif question.question_type == 'nps':
|
|
SurveyResponse.objects.create(
|
|
survey_instance=instance,
|
|
question=question,
|
|
numeric_value=random.randint(0, 10),
|
|
)
|
|
instance.calculate_score()
|
|
instance.save()
|
|
|
|
instances.append(instance)
|
|
print(f" Created {len(instances)} survey instances")
|
|
return instances
|
|
|
|
|
|
def create_call_center_interactions(patients, hospitals, users):
|
|
"""Create call center interactions"""
|
|
print("Creating call center interactions...")
|
|
from apps.callcenter.models import CallCenterInteraction
|
|
|
|
interactions = []
|
|
for i in range(25):
|
|
patient = random.choice(patients)
|
|
hospital = patient.primary_hospital or random.choice(hospitals)
|
|
|
|
interaction = CallCenterInteraction.objects.create(
|
|
patient=patient,
|
|
hospital=hospital,
|
|
agent=random.choice(users) if random.random() > 0.3 else None,
|
|
call_type=random.choice(['inquiry', 'complaint', 'appointment', 'follow_up', 'feedback']),
|
|
subject=f"Call regarding {random.choice(['appointment', 'billing', 'test results', 'medication'])}",
|
|
notes="Patient called with inquiry. Issue resolved during call.",
|
|
satisfaction_rating=random.randint(1, 5),
|
|
wait_time_seconds=random.randint(30, 600),
|
|
call_duration_seconds=random.randint(120, 900),
|
|
resolved=random.choice([True, False]),
|
|
)
|
|
interactions.append(interaction)
|
|
print(f" Created {len(interactions)} call center interactions")
|
|
return interactions
|
|
|
|
|
|
def create_social_mentions(hospitals):
|
|
"""Create social media comments"""
|
|
print("Creating social media comments...")
|
|
|
|
comments = []
|
|
for i in range(15):
|
|
hospital = random.choice(hospitals)
|
|
|
|
comment = SocialMediaComment.objects.create(
|
|
platform=random.choice(['twitter', 'facebook', 'instagram']),
|
|
comment_id=f"COMMENT{random.randint(100000, 999999)}",
|
|
comments=f"Great experience at {hospital.name}! The staff was very professional.",
|
|
author=f"{random.choice(ENGLISH_FIRST_NAMES_MALE)} {random.choice(ENGLISH_LAST_NAMES)}",
|
|
media_url=f"https://twitter.com/user/status/{random.randint(1000000, 9999999)}",
|
|
post_id=f"POST{random.randint(100000, 999999)}",
|
|
hospital=hospital,
|
|
sentiment=random.choice(['positive', 'neutral', 'negative']),
|
|
sentiment_score=random.uniform(-1, 1),
|
|
like_count=random.randint(0, 100),
|
|
reply_count=random.randint(0, 30),
|
|
published_at=timezone.now() - timedelta(days=random.randint(1, 30)),
|
|
)
|
|
comments.append(comment)
|
|
print(f" Created {len(comments)} social media comments")
|
|
return comments
|
|
|
|
|
|
def create_staff_monthly_ratings(staff):
|
|
"""Create staff monthly ratings for last 6 months"""
|
|
print("Creating staff monthly ratings...")
|
|
from apps.physicians.models import PhysicianMonthlyRating
|
|
from decimal import Decimal
|
|
|
|
ratings = []
|
|
now = datetime.now()
|
|
|
|
# Generate ratings for last 6 months
|
|
for staff_member in staff:
|
|
for months_ago in range(6):
|
|
target_date = now - timedelta(days=30 * months_ago)
|
|
year = target_date.year
|
|
month = target_date.month
|
|
|
|
# Generate realistic ratings (mostly good, some variation)
|
|
base_rating = random.uniform(3.5, 4.8)
|
|
total_surveys = random.randint(5, 25)
|
|
|
|
# Calculate sentiment counts based on rating
|
|
if base_rating >= 4.0:
|
|
positive_count = int(total_surveys * random.uniform(0.7, 0.9))
|
|
negative_count = int(total_surveys * random.uniform(0.05, 0.15))
|
|
elif base_rating >= 3.0:
|
|
positive_count = int(total_surveys * random.uniform(0.4, 0.6))
|
|
negative_count = int(total_surveys * random.uniform(0.15, 0.3))
|
|
else:
|
|
positive_count = int(total_surveys * random.uniform(0.2, 0.4))
|
|
negative_count = int(total_surveys * random.uniform(0.3, 0.5))
|
|
|
|
neutral_count = total_surveys - positive_count - negative_count
|
|
|
|
rating, created = PhysicianMonthlyRating.objects.get_or_create(
|
|
staff=staff_member,
|
|
year=year,
|
|
month=month,
|
|
defaults={
|
|
'average_rating': Decimal(str(round(base_rating, 2))),
|
|
'total_surveys': total_surveys,
|
|
'positive_count': positive_count,
|
|
'neutral_count': neutral_count,
|
|
'negative_count': negative_count,
|
|
'md_consult_rating': Decimal(str(round(base_rating + random.uniform(-0.3, 0.3), 2))),
|
|
'metadata': {
|
|
'generated': True,
|
|
'generated_at': now.isoformat()
|
|
}
|
|
}
|
|
)
|
|
ratings.append(rating)
|
|
|
|
print(f" Created {len(ratings)} staff monthly ratings")
|
|
|
|
# Update rankings for each month
|
|
print(" Updating staff rankings...")
|
|
from apps.physicians.models import PhysicianMonthlyRating
|
|
from apps.organizations.models import Hospital, Department
|
|
|
|
# Get unique year-month combinations
|
|
periods = PhysicianMonthlyRating.objects.values_list('year', 'month').distinct()
|
|
|
|
for year, month in periods:
|
|
# Update hospital rankings
|
|
hospitals = Hospital.objects.filter(status='active')
|
|
for hospital in hospitals:
|
|
hospital_ratings = PhysicianMonthlyRating.objects.filter(
|
|
staff__hospital=hospital,
|
|
year=year,
|
|
month=month
|
|
).order_by('-average_rating')
|
|
|
|
for rank, rating in enumerate(hospital_ratings, start=1):
|
|
rating.hospital_rank = rank
|
|
rating.save(update_fields=['hospital_rank'])
|
|
|
|
# Update department rankings
|
|
departments = Department.objects.filter(status='active')
|
|
for department in departments:
|
|
dept_ratings = PhysicianMonthlyRating.objects.filter(
|
|
staff__department=department,
|
|
year=year,
|
|
month=month
|
|
).order_by('-average_rating')
|
|
|
|
for rank, rating in enumerate(dept_ratings, start=1):
|
|
rating.department_rank = rank
|
|
rating.save(update_fields=['department_rank'])
|
|
|
|
print(" ✓ Rankings updated successfully")
|
|
return ratings
|
|
|
|
|
|
def create_appreciations(users, staff, hospitals, departments, categories):
|
|
"""Create appreciations with 2 years of historical data"""
|
|
print("Creating appreciations (2 years of data)...")
|
|
|
|
# Get ContentType for User and Staff
|
|
user_ct = ContentType.objects.get_for_model(User)
|
|
staff_ct = ContentType.objects.get_for_model(Staff)
|
|
|
|
# Message templates for generating realistic appreciations
|
|
message_templates_en = [
|
|
"Thank you for {action}. Your {quality} made a real difference in our patient's care.",
|
|
"I want to express my sincere appreciation for {action}. Your {quality} is truly exceptional.",
|
|
"Outstanding work! Your {action} and {quality} have not gone unnoticed.",
|
|
"Grateful for your {action}. The {quality} you demonstrate every day inspires us all.",
|
|
"A big thank you for {action}. Your {quality} sets an excellent example.",
|
|
"Exceptional job on {action}. Your {quality} is invaluable to our team.",
|
|
"Words cannot express how much I appreciate your {action}. Your {quality} shines through.",
|
|
"Thank you for going above and beyond with {action}. Your {quality} makes our hospital better.",
|
|
"Deeply grateful for your {action}. The {quality} you show is remarkable.",
|
|
"Your {action} has made a significant impact. Your {quality} is truly appreciated.",
|
|
]
|
|
|
|
message_templates_ar = [
|
|
"شكراً لك على {action}. {quality} الخاص بك أحدث فرقاً حقيقياً في رعاية مرضانا.",
|
|
"أود أن أعرب عن تقديري الخالص لـ {action}. {quality} استثنائي حقاً.",
|
|
"عمل رائع! {action} و{quality} لم يمرا مرور الكرام.",
|
|
"ممتن لـ {action}. {quality} الذي تظهره كل يوم يلهمنا جميعاً.",
|
|
"شكراً جزيلاً على {action}. {quality} يضع مثالاً ممتازاً.",
|
|
"عمل استثنائي في {action}. {quality} لا يقدر بثمن لفريقنا.",
|
|
"الكلمات لا تعبر عن مدى تقديري لـ {action}. {quality} يظهر بوضوح.",
|
|
"شكراً لتجاوز التوقعات مع {action}. {quality} يجعل مستشفانا أفضل.",
|
|
"عميق الامتنان لـ {action}. {quality} الذي تظهره مذهل.",
|
|
"لقد حدث {action} تأثيراً كبيراً. {quality} حقاً مقدر.",
|
|
]
|
|
|
|
actions = [
|
|
"providing excellent patient care", "outstanding teamwork", "quick response in emergency",
|
|
"thorough diagnosis", "compassionate patient interaction", "efficient workflow management",
|
|
"mentoring junior staff", "innovative solution implementation", "attention to detail",
|
|
"going the extra mile", "maintaining patient safety", "clear communication",
|
|
"timely treatment", "excellent bedside manner", "professional conduct",
|
|
]
|
|
|
|
qualities = [
|
|
"professionalism", "dedication", "expertise", "compassion", "attention to detail",
|
|
"efficiency", "leadership", "teamwork", "reliability", "positive attitude",
|
|
"patience", "kindness", "commitment", "excellence", "integrity",
|
|
]
|
|
|
|
appreciations = []
|
|
now = timezone.now()
|
|
|
|
# Weighted category distribution
|
|
category_weights = {
|
|
'excellent_care': 30,
|
|
'team_player': 20,
|
|
'going_extra_mile': 15,
|
|
'positive_attitude': 12,
|
|
'leadership': 10,
|
|
'innovation': 8,
|
|
'reliability': 3,
|
|
'mentorship': 2,
|
|
}
|
|
|
|
# Create category code mapping
|
|
category_map = {cat.code: cat for cat in categories}
|
|
|
|
# Generate appreciations over 2 years (730 days)
|
|
# Average 1-2 appreciations per day = ~800-1200 total
|
|
for day_offset in range(730):
|
|
# Recency bias: more appreciations in recent months
|
|
recency_factor = max(0.5, 1.0 - (day_offset / 1460)) # 0.5 to 1.0
|
|
|
|
# Number of appreciations per day (weighted by recency)
|
|
base_count = random.choices([0, 1, 2], weights=[30, 50, 20])[0]
|
|
num_appreciations = max(0, int(base_count * recency_factor))
|
|
|
|
for _ in range(num_appreciations):
|
|
# Select sender (users only - users send appreciations)
|
|
sender = random.choice(users)
|
|
|
|
# Select recipient (70% staff, 30% user)
|
|
is_staff_recipient = random.random() < 0.7
|
|
|
|
if is_staff_recipient:
|
|
recipient = random.choice(staff)
|
|
recipient_ct = staff_ct
|
|
else:
|
|
# User recipients (excluding sender)
|
|
potential_recipients = [u for u in users if u.id != sender.id]
|
|
if not potential_recipients:
|
|
continue
|
|
recipient = random.choice(potential_recipients)
|
|
recipient_ct = user_ct
|
|
|
|
# Ensure sender ≠ recipient
|
|
if sender.id == recipient.id:
|
|
continue
|
|
|
|
# Determine hospital context
|
|
if is_staff_recipient:
|
|
hospital = recipient.hospital
|
|
else:
|
|
hospital = recipient.hospital if recipient.hospital else sender.hospital
|
|
|
|
if not hospital:
|
|
continue
|
|
|
|
# Determine department context
|
|
department = None
|
|
if is_staff_recipient and recipient.department:
|
|
department = recipient.department
|
|
elif random.random() < 0.3:
|
|
# Some appreciations have department context
|
|
hospital_depts = [d for d in departments if d.hospital == hospital]
|
|
if hospital_depts:
|
|
department = random.choice(hospital_depts)
|
|
|
|
# Select category (weighted distribution)
|
|
category_codes = list(category_map.keys())
|
|
weights = [category_weights[code] for code in category_codes]
|
|
category_code = random.choices(category_codes, weights=weights, k=1)[0]
|
|
category = category_map.get(category_code)
|
|
|
|
if not category:
|
|
continue
|
|
|
|
# Generate message
|
|
action = random.choice(actions)
|
|
quality = random.choice(qualities)
|
|
message_en = random.choice(message_templates_en).format(action=action, quality=quality)
|
|
message_ar = random.choice(message_templates_ar).format(action=action, quality=quality)
|
|
|
|
# Select visibility (40% private, 25% dept, 25% hospital, 10% public)
|
|
visibility = random.choices(
|
|
list(AppreciationVisibility.values),
|
|
weights=[40, 25, 25, 10],
|
|
k=1
|
|
)[0]
|
|
|
|
# Determine status based on age
|
|
if day_offset < 1: # Last 24 hours
|
|
status = random.choices(
|
|
list(AppreciationStatus.values),
|
|
weights=[20, 50, 30], # draft, sent, acknowledged
|
|
k=1
|
|
)[0]
|
|
elif day_offset < 7: # Last week
|
|
status = random.choices(
|
|
list(AppreciationStatus.values),
|
|
weights=[5, 40, 55],
|
|
k=1
|
|
)[0]
|
|
else: # Older
|
|
status = random.choices(
|
|
list(AppreciationStatus.values),
|
|
weights=[1, 10, 89],
|
|
k=1
|
|
)[0]
|
|
|
|
# Calculate timestamps
|
|
created_date = now - timedelta(
|
|
days=day_offset,
|
|
hours=random.randint(0, 23),
|
|
minutes=random.randint(0, 59)
|
|
)
|
|
|
|
sent_at = None
|
|
acknowledged_at = None
|
|
|
|
if status != AppreciationStatus.DRAFT:
|
|
# Sent time: 0-24 hours after creation (for older appreciations)
|
|
if day_offset < 1:
|
|
sent_delay = random.randint(0, 23)
|
|
else:
|
|
sent_delay = random.randint(1, 24)
|
|
sent_at = created_date + timedelta(hours=sent_delay)
|
|
|
|
if status == AppreciationStatus.ACKNOWLEDGED:
|
|
# Acknowledged time: 1-72 hours after sent
|
|
acknowledge_delay = random.randint(1, 72)
|
|
acknowledged_at = sent_at + timedelta(hours=acknowledge_delay)
|
|
|
|
# Anonymous option (15% anonymous)
|
|
is_anonymous = random.random() < 0.15
|
|
|
|
# Create appreciation
|
|
appreciation = Appreciation(
|
|
sender=sender if not is_anonymous else None,
|
|
recipient_content_type=recipient_ct,
|
|
recipient_object_id=recipient.id,
|
|
hospital=hospital,
|
|
department=department,
|
|
category=category,
|
|
message_en=message_en,
|
|
message_ar=message_ar,
|
|
visibility=visibility,
|
|
status=status,
|
|
is_anonymous=is_anonymous,
|
|
sent_at=sent_at,
|
|
acknowledged_at=acknowledged_at,
|
|
)
|
|
|
|
# Override created_at for historical data
|
|
appreciation.created_at = created_date
|
|
appreciation.save()
|
|
|
|
appreciations.append(appreciation)
|
|
|
|
print(f" Created {len(appreciations)} appreciations over 2 years")
|
|
return appreciations
|
|
|
|
|
|
def award_badges(badges, users, staff, categories):
|
|
"""Award badges to users and staff based on appreciations"""
|
|
print("Awarding badges...")
|
|
user_badges = []
|
|
|
|
# Get ContentType for User and Staff
|
|
user_ct = ContentType.objects.get_for_model(User)
|
|
staff_ct = ContentType.objects.get_for_model(Staff)
|
|
|
|
# Badge criteria mapping (using codes from seed command)
|
|
badge_criteria = {
|
|
'first_appreciation': {'min_count': 1, 'categories': None},
|
|
'appreciated_5': {'min_count': 5, 'categories': None},
|
|
'appreciated_10': {'min_count': 10, 'categories': None},
|
|
'appreciated_25': {'min_count': 25, 'categories': None},
|
|
'appreciated_50': {'min_count': 50, 'categories': None},
|
|
# Monthly champion and other badges have complex criteria, award randomly
|
|
'monthly_champion': {'min_count': 10, 'categories': None},
|
|
'streak_4_weeks': {'min_count': 15, 'categories': None},
|
|
'diverse_appreciation': {'min_count': 20, 'categories': None},
|
|
}
|
|
|
|
badge_map = {badge.code: badge for badge in badges}
|
|
|
|
# Award badges to users (30% get badges)
|
|
for user in users:
|
|
if random.random() > 0.7: # 30% of users get badges
|
|
continue
|
|
|
|
# Count appreciations received by this user
|
|
received_count = Appreciation.objects.filter(
|
|
recipient_content_type=user_ct,
|
|
recipient_object_id=user.id,
|
|
status=AppreciationStatus.ACKNOWLEDGED
|
|
).count()
|
|
|
|
# Award appropriate badges
|
|
for badge_code, criteria in badge_criteria.items():
|
|
if badge_code not in badge_map:
|
|
continue
|
|
|
|
if received_count >= criteria['min_count']:
|
|
# Check if already has this badge
|
|
existing = UserBadge.objects.filter(
|
|
recipient_content_type=user_ct,
|
|
recipient_object_id=user.id,
|
|
badge=badge_map[badge_code]
|
|
).first()
|
|
|
|
if not existing:
|
|
# Random chance to award (not guaranteed even if criteria met)
|
|
if random.random() < 0.6:
|
|
user_badges.append(UserBadge(
|
|
recipient_content_type=user_ct,
|
|
recipient_object_id=user.id,
|
|
badge=badge_map[badge_code],
|
|
appreciation_count=received_count,
|
|
))
|
|
|
|
# Award badges to staff (60% get badges)
|
|
for staff_member in staff:
|
|
if random.random() > 0.6: # 60% of staff get badges
|
|
continue
|
|
|
|
# Count appreciations received by this staff member
|
|
received_count = Appreciation.objects.filter(
|
|
recipient_content_type=staff_ct,
|
|
recipient_object_id=staff_member.id,
|
|
status=AppreciationStatus.ACKNOWLEDGED
|
|
).count()
|
|
|
|
# Award appropriate badges
|
|
for badge_code, criteria in badge_criteria.items():
|
|
if badge_code not in badge_map:
|
|
continue
|
|
|
|
if received_count >= criteria['min_count']:
|
|
# Check if already has this badge
|
|
existing = UserBadge.objects.filter(
|
|
recipient_content_type=staff_ct,
|
|
recipient_object_id=staff_member.id,
|
|
badge=badge_map[badge_code]
|
|
).first()
|
|
|
|
if not existing:
|
|
# Higher chance for staff
|
|
if random.random() < 0.7:
|
|
user_badges.append(UserBadge(
|
|
recipient_content_type=staff_ct,
|
|
recipient_object_id=staff_member.id,
|
|
badge=badge_map[badge_code],
|
|
appreciation_count=received_count,
|
|
))
|
|
|
|
# Bulk create user badges
|
|
UserBadge.objects.bulk_create(user_badges)
|
|
|
|
print(f" Awarded {len(user_badges)} badges to users and staff")
|
|
return user_badges
|
|
|
|
|
|
def generate_appreciation_stats(users, staff, hospitals):
|
|
"""Generate appreciation statistics for users and staff"""
|
|
print("Generating appreciation statistics...")
|
|
stats = []
|
|
now = timezone.now()
|
|
|
|
# Get ContentType for User and Staff
|
|
user_ct = ContentType.objects.get_for_model(User)
|
|
staff_ct = ContentType.objects.get_for_model(Staff)
|
|
|
|
# Get current year and month
|
|
year = now.year
|
|
month = now.month
|
|
|
|
# Generate stats for users (70% have stats)
|
|
for user in users:
|
|
if random.random() > 0.7:
|
|
continue
|
|
|
|
# Generate realistic stats
|
|
received_count = random.randint(0, 30)
|
|
sent_count = random.randint(0, 50)
|
|
acknowledged_count = int(received_count * random.uniform(0.6, 1.0))
|
|
|
|
stat, created = AppreciationStats.objects.get_or_create(
|
|
recipient_content_type=user_ct,
|
|
recipient_object_id=user.id,
|
|
year=year,
|
|
month=month,
|
|
defaults={
|
|
'hospital': user.hospital if user.hospital else random.choice(hospitals),
|
|
'received_count': received_count,
|
|
'sent_count': sent_count,
|
|
'acknowledged_count': acknowledged_count,
|
|
'hospital_rank': random.randint(1, 20) if received_count > 0 else None,
|
|
}
|
|
)
|
|
if created:
|
|
stats.append(stat)
|
|
|
|
# Generate stats for staff (90% have stats)
|
|
for staff_member in staff:
|
|
if random.random() > 0.1: # 90% have stats
|
|
# Staff typically receive more appreciations
|
|
received_count = random.randint(5, 50)
|
|
sent_count = random.randint(0, 20) # Staff send less
|
|
acknowledged_count = int(received_count * random.uniform(0.7, 1.0))
|
|
|
|
stat, created = AppreciationStats.objects.get_or_create(
|
|
recipient_content_type=staff_ct,
|
|
recipient_object_id=staff_member.id,
|
|
year=year,
|
|
month=month,
|
|
defaults={
|
|
'hospital': staff_member.hospital,
|
|
'department': staff_member.department,
|
|
'received_count': received_count,
|
|
'sent_count': sent_count,
|
|
'acknowledged_count': acknowledged_count,
|
|
'hospital_rank': random.randint(1, 10) if received_count > 5 else None,
|
|
}
|
|
)
|
|
if created:
|
|
stats.append(stat)
|
|
|
|
print(f" Generated {len(stats)} appreciation statistics")
|
|
return stats
|
|
|
|
|
|
def create_observations(hospitals, departments, users):
|
|
"""Create observations with 2 years of historical data"""
|
|
print("Creating observations (2 years of data)...")
|
|
|
|
# Get observation categories (should be seeded)
|
|
obs_categories = list(ObservationCategory.objects.filter(is_active=True))
|
|
if not obs_categories:
|
|
print(" WARNING: No observation categories found. Run: python manage.py seed_observation_categories")
|
|
return []
|
|
|
|
# Observation descriptions
|
|
observation_descriptions = [
|
|
"Noticed a wet floor near the elevator without any warning signs. This could be a slip hazard for patients and visitors.",
|
|
"Observed expired hand sanitizer in the dispenser near the main entrance. The expiration date was 3 months ago.",
|
|
"Fire extinguisher in corridor B appears to be missing its inspection tag. Last visible inspection was over a year ago.",
|
|
"Patient call button in room 205 is not functioning properly. Patient had to wait for assistance.",
|
|
"Medication cart was left unattended in the hallway for approximately 15 minutes during shift change.",
|
|
"Emergency exit sign on the 3rd floor is not illuminated. This could be a safety concern during power outages.",
|
|
"Observed staff member not wearing proper PPE while handling biohazard materials.",
|
|
"Temperature in the medication storage room seems higher than normal. Thermometer shows 28°C.",
|
|
"Wheelchair in the lobby has a broken wheel lock mechanism. Could be dangerous for patients.",
|
|
"Noticed a strong chemical smell in the laboratory area. Ventilation may not be working properly.",
|
|
"Patient identification wristband was found on the floor in the waiting area.",
|
|
"Sharps container in treatment room 3 is overfilled and needs immediate replacement.",
|
|
"Observed water leak from ceiling tiles in the radiology waiting area.",
|
|
"Emergency shower station in the lab has not been tested recently according to the log.",
|
|
"Noticed outdated patient safety posters in the pediatric ward.",
|
|
]
|
|
|
|
observation_titles = [
|
|
"Wet floor hazard",
|
|
"Expired sanitizer",
|
|
"Fire safety concern",
|
|
"Equipment malfunction",
|
|
"Medication security",
|
|
"Emergency signage issue",
|
|
"PPE compliance",
|
|
"Temperature control",
|
|
"Equipment maintenance",
|
|
"Ventilation concern",
|
|
"Patient safety",
|
|
"Waste disposal",
|
|
"Facility maintenance",
|
|
"Safety equipment",
|
|
"Signage update needed",
|
|
]
|
|
|
|
locations = [
|
|
"Main Entrance", "Emergency Department", "ICU", "Pediatric Ward", "Surgery Floor",
|
|
"Radiology Department", "Laboratory", "Pharmacy", "Cafeteria", "Parking Garage",
|
|
"Outpatient Clinic", "Rehabilitation Center", "Administration Building", "Staff Lounge",
|
|
"Patient Rooms Floor 2", "Patient Rooms Floor 3", "Waiting Area", "Reception",
|
|
]
|
|
|
|
observations = []
|
|
now = timezone.now()
|
|
|
|
# Generate observations over 2 years (730 days)
|
|
# Average 1-3 observations per day
|
|
for day_offset in range(730):
|
|
num_observations = random.choices([0, 1, 2, 3, 4], weights=[20, 35, 30, 10, 5])[0]
|
|
|
|
for _ in range(num_observations):
|
|
hospital = random.choice(hospitals)
|
|
hospital_depts = [d for d in departments if d.hospital == hospital]
|
|
|
|
created_date = now - timedelta(
|
|
days=day_offset,
|
|
hours=random.randint(0, 23),
|
|
minutes=random.randint(0, 59)
|
|
)
|
|
|
|
# Status distribution based on age
|
|
if day_offset < 7: # Last week
|
|
status = random.choices(
|
|
[ObservationStatus.NEW, ObservationStatus.TRIAGED, ObservationStatus.ASSIGNED,
|
|
ObservationStatus.IN_PROGRESS, ObservationStatus.RESOLVED, ObservationStatus.CLOSED],
|
|
weights=[25, 20, 20, 20, 10, 5]
|
|
)[0]
|
|
elif day_offset < 30: # Last month
|
|
status = random.choices(
|
|
[ObservationStatus.NEW, ObservationStatus.TRIAGED, ObservationStatus.ASSIGNED,
|
|
ObservationStatus.IN_PROGRESS, ObservationStatus.RESOLVED, ObservationStatus.CLOSED],
|
|
weights=[5, 10, 15, 20, 30, 20]
|
|
)[0]
|
|
else: # Older
|
|
status = random.choices(
|
|
[ObservationStatus.NEW, ObservationStatus.TRIAGED, ObservationStatus.ASSIGNED,
|
|
ObservationStatus.IN_PROGRESS, ObservationStatus.RESOLVED, ObservationStatus.CLOSED,
|
|
ObservationStatus.REJECTED, ObservationStatus.DUPLICATE],
|
|
weights=[2, 3, 5, 5, 40, 40, 3, 2]
|
|
)[0]
|
|
|
|
# Severity distribution
|
|
severity = random.choices(
|
|
[ObservationSeverity.LOW, ObservationSeverity.MEDIUM,
|
|
ObservationSeverity.HIGH, ObservationSeverity.CRITICAL],
|
|
weights=[30, 45, 20, 5]
|
|
)[0]
|
|
|
|
# Anonymous vs identified (60% anonymous)
|
|
is_anonymous = random.random() < 0.6
|
|
|
|
# Generate tracking code
|
|
import secrets
|
|
tracking_code = f"OBS-{secrets.token_hex(3).upper()}"
|
|
|
|
observation = Observation(
|
|
tracking_code=tracking_code,
|
|
category=random.choice(obs_categories),
|
|
title=random.choice(observation_titles),
|
|
description=random.choice(observation_descriptions),
|
|
severity=severity,
|
|
location_text=random.choice(locations),
|
|
incident_datetime=created_date - timedelta(hours=random.randint(0, 48)),
|
|
reporter_staff_id=f"EMP{random.randint(1000, 9999)}" if not is_anonymous else '',
|
|
reporter_name=f"{random.choice(ENGLISH_FIRST_NAMES_MALE)} {random.choice(ENGLISH_LAST_NAMES)}" if not is_anonymous else '',
|
|
reporter_phone=generate_saudi_phone() if not is_anonymous and random.random() > 0.5 else '',
|
|
reporter_email=f"staff{random.randint(100, 999)}@alhammadi.sa" if not is_anonymous and random.random() > 0.5 else '',
|
|
status=status,
|
|
assigned_department=random.choice(hospital_depts) if hospital_depts and status not in [ObservationStatus.NEW] else None,
|
|
assigned_to=random.choice(users) if status in [ObservationStatus.ASSIGNED, ObservationStatus.IN_PROGRESS, ObservationStatus.RESOLVED, ObservationStatus.CLOSED] and random.random() > 0.3 else None,
|
|
)
|
|
|
|
# Set timestamps based on status
|
|
if status not in [ObservationStatus.NEW]:
|
|
observation.triaged_at = created_date + timedelta(hours=random.randint(1, 24))
|
|
observation.triaged_by = random.choice(users)
|
|
|
|
if status in [ObservationStatus.RESOLVED, ObservationStatus.CLOSED]:
|
|
observation.resolved_at = created_date + timedelta(hours=random.randint(24, 168))
|
|
|
|
if status == ObservationStatus.CLOSED:
|
|
observation.closed_at = (observation.resolved_at or created_date) + timedelta(hours=random.randint(1, 48))
|
|
|
|
observation.save()
|
|
|
|
# Override created_at
|
|
Observation.objects.filter(pk=observation.pk).update(created_at=created_date)
|
|
|
|
# Add status log entries
|
|
if status != ObservationStatus.NEW:
|
|
ObservationStatusLog.objects.create(
|
|
observation=observation,
|
|
from_status=ObservationStatus.NEW,
|
|
to_status=ObservationStatus.TRIAGED,
|
|
changed_by=random.choice(users),
|
|
comment="Observation triaged and categorized."
|
|
)
|
|
|
|
if status in [ObservationStatus.ASSIGNED, ObservationStatus.IN_PROGRESS, ObservationStatus.RESOLVED, ObservationStatus.CLOSED]:
|
|
ObservationStatusLog.objects.create(
|
|
observation=observation,
|
|
from_status=ObservationStatus.TRIAGED,
|
|
to_status=ObservationStatus.ASSIGNED,
|
|
changed_by=random.choice(users),
|
|
comment="Assigned to responsible department."
|
|
)
|
|
|
|
if status in [ObservationStatus.RESOLVED, ObservationStatus.CLOSED]:
|
|
ObservationStatusLog.objects.create(
|
|
observation=observation,
|
|
from_status=ObservationStatus.IN_PROGRESS if status != ObservationStatus.RESOLVED else ObservationStatus.ASSIGNED,
|
|
to_status=ObservationStatus.RESOLVED,
|
|
changed_by=random.choice(users),
|
|
comment="Issue has been addressed and resolved."
|
|
)
|
|
|
|
# Add internal notes for some observations
|
|
if random.random() > 0.6:
|
|
ObservationNote.objects.create(
|
|
observation=observation,
|
|
note="Initial assessment completed. Following up with relevant department.",
|
|
created_by=random.choice(users),
|
|
is_internal=True
|
|
)
|
|
|
|
observations.append(observation)
|
|
|
|
print(f" Created {len(observations)} observations over 2 years")
|
|
return observations
|
|
|
|
|
|
def main():
|
|
"""Main data generation function"""
|
|
print("\n" + "="*60)
|
|
print("PX360 - Saudi-Influenced Data Generator")
|
|
print("="*60 + "\n")
|
|
|
|
# Clear existing data first
|
|
clear_existing_data()
|
|
|
|
# Create base data
|
|
hospitals = create_hospitals()
|
|
# hospitals = Hospital.objects.all()
|
|
|
|
departments = create_departments(hospitals)
|
|
staff = create_staff(hospitals, departments)
|
|
patients = create_patients(hospitals)
|
|
users = create_users(hospitals)
|
|
|
|
# Get all users for assignments
|
|
users_list = list(User.objects.all())
|
|
|
|
# Create complaint categories first
|
|
# categories = create_complaint_categories(hospitals)
|
|
|
|
# Create operational data
|
|
# complaints = create_complaints(patients, hospitals, staff, users_list)
|
|
# inquiries = create_inquiries(patients, hospitals, users_list)
|
|
# feedbacks = create_feedback(patients, hospitals, staff, users_list)
|
|
# create_survey_templates(hospitals)
|
|
# create_journey_templates(hospitals)
|
|
# projects = create_qi_projects(hospitals)
|
|
# actions = create_px_actions(complaints, hospitals, users_list)
|
|
# journey_instances = create_journey_instances(None, patients)
|
|
# survey_instances = create_survey_instances(None, patients, staff)
|
|
# call_interactions = create_call_center_interactions(patients, hospitals, users_list)
|
|
# social_comments = create_social_mentions(hospitals)
|
|
# staff_ratings = create_staff_monthly_ratings(staff)
|
|
|
|
# Seed appreciation categories and badges
|
|
print("\nSeeding appreciation data...")
|
|
from django.core.management import call_command
|
|
call_command('seed_appreciation_data', verbosity=0)
|
|
print(" ✓ Appreciation categories and badges seeded")
|
|
|
|
# Get appreciation categories and badges
|
|
appreciation_categories = list(AppreciationCategory.objects.filter(is_active=True))
|
|
badges = list(AppreciationBadge.objects.filter(is_active=True))
|
|
|
|
# Create appreciation data
|
|
appreciations = create_appreciations(users_list, staff, hospitals, departments, appreciation_categories)
|
|
user_badges = award_badges(badges, users_list, staff, appreciation_categories)
|
|
appreciation_stats = generate_appreciation_stats(users_list, staff, hospitals)
|
|
observations = create_observations(hospitals, departments, users_list)
|
|
|
|
print("\n" + "="*60)
|
|
print("Data Generation Complete!")
|
|
print("="*60)
|
|
print(f"\nCreated:")
|
|
print(f" - {len(hospitals)} Hospitals")
|
|
print(f" - {len(departments)} Departments")
|
|
print(f" - {len(staff)} Staff")
|
|
print(f" - {len(patients)} Patients")
|
|
print(f" - {len(users_list)} Users")
|
|
# print(f" - {len(complaints)} Complaints (2 years)")
|
|
# print(f" - {len(inquiries)} Inquiries (2 years)")
|
|
# print(f" - {len(feedbacks)} Feedback Items")
|
|
# print(f" - {len(actions)} PX Actions")
|
|
# print(f" - {len(journey_instances)} Journey Instances")
|
|
# print(f" - {len(survey_instances)} Survey Instances")
|
|
# print(f" - {len(call_interactions)} Call Center Interactions")
|
|
# print(f" - {len(social_comments)} Social Media Comments")
|
|
# print(f" - {len(projects)} QI Projects")
|
|
# print(f" - {len(staff_ratings)} Staff Monthly Ratings")
|
|
# print(f" - {len(appreciations)} Appreciations (2 years)")
|
|
# print(f" - {len(user_badges)} Badges Awarded")
|
|
# print(f" - {len(appreciation_stats)} Appreciation Statistics")
|
|
# print(f" - {len(observations)} Observations (2 years)")
|
|
print(f"\nYou can now login with:")
|
|
print(f" Username: px_admin")
|
|
print(f" Password: admin123")
|
|
print(f"\nOr hospital admins:")
|
|
print(f" Username: admin_hh")
|
|
print(f" Password: admin123")
|
|
print(f"\nAccess the system at: http://127.0.0.1:8000/")
|
|
print("\n")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|