448 lines
22 KiB
Python
448 lines
22 KiB
Python
from django.core.management.base import BaseCommand
|
||
from django.db import transaction
|
||
|
||
from apps.surveys.models import SurveyTemplate, SurveyQuestion
|
||
from apps.organizations.models import Hospital
|
||
|
||
|
||
LIKERT_YES_NO = [
|
||
{"value": "1", "label": "Definitely No", "label_ar": "بالتأكيد لا"},
|
||
{"value": "2", "label": "Probably No", "label_ar": "ربما لا"},
|
||
{"value": "3", "label": "Not Sure", "label_ar": "لست متأكداً"},
|
||
{"value": "4", "label": "Probably Yes", "label_ar": "ربما نعم"},
|
||
{"value": "5", "label": "Definitely Yes", "label_ar": "نعم بالتأكيد"},
|
||
]
|
||
|
||
LIKERT_AGREE = [
|
||
{"value": "1", "label": "Strongly Disagree", "label_ar": "أعترض بشدة"},
|
||
{"value": "2", "label": "Disagree", "label_ar": "أعترض"},
|
||
{"value": "3", "label": "Neutral", "label_ar": "محايد"},
|
||
{"value": "4", "label": "Agree", "label_ar": "أوافق"},
|
||
{"value": "5", "label": "Strongly Agree", "label_ar": "أوافق بشدة"},
|
||
]
|
||
|
||
|
||
def Q(order, text, text_ar, qtype="rating", choices=None, required=True):
|
||
return {
|
||
"order": order,
|
||
"text": text,
|
||
"text_ar": text_ar,
|
||
"question_type": qtype,
|
||
"choices_json": choices or [],
|
||
"is_required": required,
|
||
}
|
||
|
||
|
||
IP_QUESTIONS = [
|
||
Q(
|
||
1,
|
||
"Who is completing this survey?",
|
||
"من الذي يقوم بالإجابة على هذا الاستبيان؟",
|
||
"multiple_choice",
|
||
[
|
||
{"value": "1", "label": "Patient", "label_ar": "المريض"},
|
||
{"value": "2", "label": "Parent/Guardian", "label_ar": "أحد الوالدين/المسؤول عن رعاية المريض"},
|
||
{"value": "6", "label": "Others", "label_ar": "شخص آخر"},
|
||
],
|
||
),
|
||
Q(2, "Was this your first admission to this hospital?", "هل هذا أول تنويم لك في هذا المستشفى؟", "yes_no"),
|
||
Q(
|
||
3,
|
||
"During this hospital stay, were you admitted to this hospital through the Emergency Room?",
|
||
"هل دخلت هذه المستشفى عن طريق غرفة الطوارىء؟",
|
||
"yes_no",
|
||
),
|
||
Q(4, "Speed of admission process", "سرعة إجراءات الدخول إلى المستشفى"),
|
||
Q(5, "Room cleanliness", "نظافة الغرفة"),
|
||
Q(6, "Room temperature", "درجة حرارة الغرفة"),
|
||
Q(7, "Noise level in and around room", "هدوء الغرفة وما حولها"),
|
||
Q(8, "Quality of the food", "جودة الطعام"),
|
||
Q(9, "Friendliness/courtesy of the nurses", "اهتمام وحسن تعامل فريق التمريض"),
|
||
Q(10, "Nurses promptness in responding to the call button", "سرعة فريق التمريض في الاستجابة لزر النداء"),
|
||
Q(
|
||
11,
|
||
"Amount of attention nurses paid to your special or personal needs",
|
||
"مدى مراعاة فريق التمريض لاحتياجاتك الخاصة أو الشخصية",
|
||
),
|
||
Q(
|
||
12,
|
||
"How well the nurses explained your daily plan of care",
|
||
"مدى قيام فريق التمريض بشرح خطة الرعاية اليومية الخاصة بك",
|
||
),
|
||
Q(13, "How well the nurses kept you informed", "مدى قيام فريق التمريض بتزويدك بالمعلومات الخاصة عن حالتك الصحية"),
|
||
Q(14, "Nurses took the time to answer your questions", "مدى إجابة فريق التمريض على أسئلتك واستفساراتك"),
|
||
Q(15, "Doctors took the time to answer your questions", "تخصيص الأطباء الوقت الكافي للإجابة على أسئلتك"),
|
||
Q(16, "Doctors' concern for your questions and worries", "اهتمام الأطباء بأسئلتك وطمأنة مخاوفك"),
|
||
Q(
|
||
17,
|
||
"Doctors' efforts to include you in decision about your treatment",
|
||
"حرص الأطباء على إشراكك في القرارات الخاصة بعلاجك",
|
||
),
|
||
Q(18, "Staff concern for your privacy", "مراعاة الموظفين لخصوصيتك"),
|
||
Q(
|
||
19,
|
||
"Response to concerns/complaints made during your stay",
|
||
"الاستجابة للمخاوف والشكاوى التي أعربت عنها خلال إقامتك",
|
||
),
|
||
Q(20, "How well your pain was addressed", "مدى تخفيف شعورك بالألم"),
|
||
Q(
|
||
21,
|
||
"How well staff introduced themselves and explained their roles in your care",
|
||
"تعريف الموظفين بأنفسهم ودورهم في تقديم الرعاية لك",
|
||
),
|
||
Q(
|
||
22,
|
||
"Instructions given about how to care for yourself at home (Diet, Medications, Lifestyle… etc)",
|
||
"التعليمات التي حصلت عليها عن العناية بنفسك في المنزل (نظامك الغذائي، الأدوية، نمط الحياة وغيرها)",
|
||
),
|
||
Q(23, "How well staff worked together to care for you", "مدى تعاون الموظفين في تقديم الرعاية لك"),
|
||
Q(
|
||
24,
|
||
"Would you recommend this hospital to your friends and family?",
|
||
"هل توصي بهذا المستشفى لأصدقائك وأسرتك؟",
|
||
"likert",
|
||
LIKERT_YES_NO,
|
||
),
|
||
Q(25, "Likelihood of you recommending this hospital to others", "احتمالية أن توصي بهذا المستشفى للآخرين"),
|
||
Q(
|
||
26,
|
||
"Using any number from 0 to 10, where 0 is the worst hospital possible and 10 is the best hospital possible, what number would you use to rate this hospital during your stay?",
|
||
"باستخدام رقم من 0 إلى 10، بحيث يكون 0 أسوأ مستشفى ممكن و10 أفضل مستشفى ممكن، أي رقم تستخدمه لتصنيف هذا المستشفى خلال فترة تنويمك؟",
|
||
"nps",
|
||
),
|
||
Q(
|
||
27,
|
||
"I received exactly the help I want (and need) exactly when I want (and need) it",
|
||
"تلقيت المساعدة التي أريدها (وأحتاجها) فور طلبي (واحتياجي) لها",
|
||
"likert",
|
||
LIKERT_AGREE,
|
||
),
|
||
Q(
|
||
28,
|
||
"Would you like to share your contact information along the comments you gave with the hospital?",
|
||
"سيتم إرسال تعليقاتك للمستشفى، هل ترغب بإرفاق اسمك ورقم هاتفك معها؟",
|
||
"yes_no",
|
||
),
|
||
]
|
||
|
||
OP_QUESTIONS = [
|
||
Q(1, "Was this your first visit here?", "هل هذه هي زيارتك الأولى لنا؟", "yes_no"),
|
||
Q(
|
||
2,
|
||
"How did you get to the hospital?",
|
||
"كيف حضرت إلى المستشفى؟",
|
||
"multiple_choice",
|
||
[
|
||
{"value": "1", "label": "Booked appointment thru call center", "label_ar": "بموعد مسبق عبر مركز الاتصال"},
|
||
{
|
||
"value": "4",
|
||
"label": "Booked appointment at the hospital / clinic reception",
|
||
"label_ar": "بموعد مسبق عبر الاستقبال في المستشفى",
|
||
},
|
||
{
|
||
"value": "9",
|
||
"label": "Booked appointment online / application",
|
||
"label_ar": "بموعد مسبق عبر التطبيق / الموقع الالكتروني",
|
||
},
|
||
{"value": "5", "label": "Without an appointment", "label_ar": "بدون موعد"},
|
||
],
|
||
),
|
||
Q(3, "Ease of scheduling your appointment", "سهولة حجز الموعد"),
|
||
Q(
|
||
4,
|
||
"Ease of contacting (e.g., email, phone, web portal) the hospital",
|
||
"سهولة التواصل مع المستشفى من خلال (البريد الإلكتروني، الهاتف، الموقع الإلكتروني)",
|
||
),
|
||
Q(5, "Courtesy of the receptionists", "حسن تعامل موظفي الاستقبال"),
|
||
Q(6, "Ease of registration upon arrival", "سهولة التسجيل عند الاستقبال"),
|
||
Q(7, "Degree to which you were informed about any delays", "مدى إخبارك بأي تأخير في الإجراءات"),
|
||
Q(8, "Wait time at clinic (from arriving to leaving)", "فترة الانتظار في العيادة (من وقت الوصول وحتى المغادرة)"),
|
||
Q(9, "How well the nurse listened to you", "مدى إصغاء الممرض/الممرضة لك"),
|
||
Q(10, "Concern the nurse showed for your problem", "اهتمام الممرض/الممرضة بمشكلتك الصحية"),
|
||
Q(11, "Concern the physician showed for your questions or worries", "اهتمام طبيبك بأسئلتك أو مخاوفك"),
|
||
Q(
|
||
12,
|
||
"Explanations the physician gave you about your problem or condition",
|
||
"الشرح الذي قدمه طبيبك عن مشكلتك أو حالتك الصحية",
|
||
),
|
||
Q(
|
||
13,
|
||
"Physician's efforts to include you in decisions about your care",
|
||
"حرص طبيبك على إشراكك في القرارات المتعلقة برعايتك",
|
||
),
|
||
Q(
|
||
14,
|
||
"Physician's discussion of any proposed treatment (options, risks, benefits, etc.)",
|
||
"قيام الطبيب بمناقشة طرق العلاج المقترحة (الخيارات، المخاطر، الفوائد، وغيرها)",
|
||
),
|
||
Q(15, "Likelihood of your recommending this physician to others", "احتمالية أن توصي بهذا الطبيب للآخرين"),
|
||
Q(16, "Did you receive laboratory services?", "هل تلقيت خدمات من المختبر؟", "yes_no"),
|
||
Q(17, "Waiting time to get your blood drawn", "مدة الانتظار قبل سحب عينة الدم"),
|
||
Q(18, "Concern shown for your comfort when your blood was drawn", "الحرص على راحتك خلال سحب عينة الدم"),
|
||
Q(
|
||
19,
|
||
"Skill of the person who took your blood (e.g., did it quickly, with minimal pain)",
|
||
"مهارة فنّي المختبر الذي قام بسحب عينة الدم (قام بسحبها بسرعة وبأقل ألم)",
|
||
),
|
||
Q(20, "Did you receive radiology services?", "هل تلقيت خدمات من قسم الأشعة؟", "yes_no"),
|
||
Q(21, "Waiting time for radiology test", "مدة الانتظار لإجراء الأشعة"),
|
||
Q(
|
||
22,
|
||
"Explanations from the staff about what would happen during your radiology test",
|
||
"شرح الفني لإجراءات الأشعة",
|
||
),
|
||
Q(
|
||
24,
|
||
"Did you receive your medications from the hospital's pharmacy?",
|
||
"هل صرفت أدويتك من صيدلية المستشفى؟",
|
||
"yes_no",
|
||
),
|
||
Q(25, "Waiting time to get your medications", "مدة الانتظار قبل صرف أدويتك"),
|
||
Q(26, "Pharmacist's explanation of your prescription", "شرح الصيدلي لتعليمات استخدام الأدوية"),
|
||
Q(27, "Availability of prescribed medications", "توفر الأدوية الموصوفة"),
|
||
Q(
|
||
28,
|
||
"Did you visit the insurance approval office for approval?",
|
||
"هل زرت مكتب موافقات التأمين للحصول على موافقة؟",
|
||
"yes_no",
|
||
),
|
||
Q(29, "Waiting time to get your insurance approval", "مدة الانتظار للحصول على موافقة التأمين"),
|
||
Q(
|
||
30,
|
||
"Promptness with which the hospital processed your insurance request",
|
||
"سرعة المستشفى بمعالجة طلب التأمين الخاص بك",
|
||
),
|
||
Q(
|
||
31,
|
||
"Promptness with which the insurance company responded to insurance request",
|
||
"سرعة استجابة شركة التأمين لطلبك",
|
||
),
|
||
Q(32, "Our concern for your privacy", "مراعاتنا لخصوصيتك"),
|
||
Q(
|
||
33,
|
||
"How well the staff protected your safety (by washing hands, wearing ID, etc.)",
|
||
"مدى حرص الطاقم الطبي على سلامتك (بغسل أيديهم، ارتداء بطاقة التعريف الخاصة بهم ... إلخ)",
|
||
),
|
||
Q(34, "How well the staff worked together to care for you", "مدى تعاون الموظفين في تقديم الرعاية لك"),
|
||
Q(35, "Likelihood of your recommending our practice to others", "احتمالية أن توصي بهذه المستشفى للآخرين"),
|
||
Q(
|
||
36,
|
||
"Overall rating of care received during your visit",
|
||
"التقييم العام للرعاية الطبية التي تلقيتها أثناء زيارتك",
|
||
),
|
||
Q(
|
||
37,
|
||
"I received exactly the help I want (and need) exactly when I want (and need) it",
|
||
"تلقيت المساعدة التي أريدها (وأحتاجها) فور طلبي (واحتياجي) لها",
|
||
"likert",
|
||
LIKERT_AGREE,
|
||
),
|
||
Q(
|
||
38,
|
||
"I agree on sharing my contact information along the comments with the hospital",
|
||
"أوافق على إرفاق اسمي ورقم هاتفي مع التعليقات التي سيتم إرسالها إلى المستشفى",
|
||
"yes_no",
|
||
),
|
||
]
|
||
|
||
ED_QUESTIONS = [
|
||
Q(
|
||
1,
|
||
"Who is completing this survey?",
|
||
"من الذي يقوم بالإجابة على هذا الاستبيان؟",
|
||
"multiple_choice",
|
||
[
|
||
{"value": "1", "label": "Patient", "label_ar": "المريض"},
|
||
{"value": "7", "label": "Parent/Guardian", "label_ar": "أحد الوالدين/المسؤول عن رعاية المريض"},
|
||
{"value": "3", "label": "Others", "label_ar": "شخص آخر"},
|
||
],
|
||
),
|
||
Q(2, "Comfort of the waiting area", "الراحة في منطقة الانتظار"),
|
||
Q(3, "Waiting time before you were brought to the treatment area", "مدة الانتظار قبل دخولك لمنطقة العلاج"),
|
||
Q(4, "Courtesy of the nurses", "حسن تعامل فريق التمريض"),
|
||
Q(5, "How well the nurses took the time to listen to you", "مدى إصغاء فريق التمريض إليك"),
|
||
Q(6, "Nurses' attention to your needs", "مراعاة فريق التمريض لاحتياجاتك"),
|
||
Q(7, "Nurses' responses to your questions/concerns", "إجابة فريق التمريض على أسئلتك وملاحظاتك"),
|
||
Q(8, "Nurses' concern for your privacy", "مراعاة فريق التمريض لخصوصيتك"),
|
||
Q(9, "Courtesy of the doctors", "حسن تعامل الطبيب"),
|
||
Q(10, "How well the doctors took the time to listen to you", "مدى إصغاء الأطباء لك"),
|
||
Q(
|
||
11,
|
||
"How well the doctors included you in decisions about your treatment",
|
||
"مدى قيام الأطباء بإشراكك في القرارات الخاصة بعلاجك",
|
||
),
|
||
Q(12, "Doctors' concern to keep you informed about your treatment", "حرص الأطباء على إطلاعك على تفاصيل علاجك"),
|
||
Q(13, "Doctors' concern for your comfort while treating you", "حرص الأطباء على راحتك خلال مراحل علاجك"),
|
||
Q(14, "How well you were kept informed about delays", "مدى إخبارك بأي تأخير في الإجراءات"),
|
||
Q(15, "How well your pain was addressed", "مدى تخفيف شعورك بالألم"),
|
||
Q(
|
||
16,
|
||
"Information you were given about caring for yourself at home (e.g., taking medications, getting follow-up medical care)",
|
||
"المعلومات التي قدمت لك حول كيفية الاعتناء بنفسك في المنزل (على سبيل المثال: أخذ الدواء، موعد المتابعة)",
|
||
),
|
||
Q(17, "How well the staff cared about you as a person", "مدى تعامل الموظفين معك بإنسانية"),
|
||
Q(18, "How well the staff worked together to care for you", "مدى تعاون الموظفين في تقديم الرعاية لك"),
|
||
Q(19, "Overall cleanliness of the Emergency Department", "نظافة قسم الطوارئ"),
|
||
Q(
|
||
20,
|
||
"Overall rating of care received during your visit",
|
||
"التقييم العام للرعاية التي تلقيتها خلال زيارتك لقسم الطوارئ",
|
||
),
|
||
Q(21, "Likelihood of you recommending our Emergency Department to others", "احتمالية أن توصي بقسم الطوارئ للآخرين"),
|
||
Q(
|
||
22,
|
||
"I received exactly the help I want (and need) exactly when I want (and need) it",
|
||
"تلقيت المساعدة التي أريدها (وأحتاجها) فور طلبي (واحتياجي) لها",
|
||
"likert",
|
||
LIKERT_AGREE,
|
||
),
|
||
Q(
|
||
23,
|
||
"I agree on sharing my contact information along the comments with the hospital",
|
||
"أوافق على إرفاق اسمي ورقم هاتفي مع التعليقات التي سيتم إرسالها إلى المستشفى",
|
||
"yes_no",
|
||
),
|
||
]
|
||
|
||
SURVEYS = [
|
||
{
|
||
"key": "IP",
|
||
"name": "Inpatient (IP) Survey",
|
||
"name_ar": "استبيان المرضى المنومين",
|
||
"questions": IP_QUESTIONS,
|
||
},
|
||
{
|
||
"key": "OP",
|
||
"name": "Outpatient (MD) Survey",
|
||
"name_ar": "استبيان مرضى العيادات الخارجية",
|
||
"questions": OP_QUESTIONS,
|
||
},
|
||
{
|
||
"key": "ED",
|
||
"name": "Emergency Department (ED) Survey",
|
||
"name_ar": "استبيان مرضى الطوارىء",
|
||
"questions": ED_QUESTIONS,
|
||
},
|
||
]
|
||
|
||
INSTRUCTIONS_EN = (
|
||
"SURVEY INSTRUCTIONS:\n\n"
|
||
"Please rate the services you received on your most recent visit to Facility Name.\n\n"
|
||
"Select the response that best describes your experience. "
|
||
"If a question does not apply to you, please skip to the next question. "
|
||
'When you finish, please click on "Submit".\n\n'
|
||
"Your identity will remain confidential.\n\n"
|
||
"This survey is to evaluate your visit on [Date]. All answers should relate to this visit only."
|
||
)
|
||
|
||
INSTRUCTIONS_AR = (
|
||
"تعليمات الاستبيان:\n\n"
|
||
"الرجاء تقييم الخدمات التي حصلت عليها خلال زيارتك الأخيرة لـ Facility Name\n\n"
|
||
"اختر الإجابة التي تنطبق بشكل أفضل على تجربتك. وإذا كان هناك أي سؤال لا ينطبق على حالتك، "
|
||
'يرجى تخطيه والانتقال إلى السؤال التالي. عند الانتهاء، الرجاء الضغط على زر "إرسال".\n\n'
|
||
"نضمن لك المحافظة على سرية هويتك.\n\n"
|
||
"هذا الاستبيان لتقييم زيارتك في [Date]، جميع أجوبتكم ستكون خاصة بهذه الزيارة فقط."
|
||
)
|
||
|
||
CONSENT_EN = (
|
||
"I grant permission for my personal contact details to be shared with Third party business affiliates "
|
||
"solely for the purposes of (i) assessing clinical performance or (ii) assessing patient satisfaction."
|
||
)
|
||
|
||
CONSENT_AR = (
|
||
"أنا أمنح الإذن لمشاركة معلومات الاتصال الشخصية لأي طرف ثالث أو شريك في تطوير المعلومات "
|
||
"لاستخدامها فقط في (١) لتقييم الأداء السريري (٢) تقييم رضا المرضى."
|
||
)
|
||
|
||
|
||
class Command(BaseCommand):
|
||
help = "Import Alhammadi survey templates (IP, OP, ED) for all hospitals from the xlsx spec"
|
||
|
||
def add_arguments(self, parser):
|
||
parser.add_argument("--hospital", type=str, help="Hospital code (default: all active hospitals)")
|
||
parser.add_argument(
|
||
"--update", action="store_true", help="Update existing templates (delete old questions, re-import)"
|
||
)
|
||
|
||
def handle(self, *args, **options):
|
||
if options["hospital"]:
|
||
hospitals = Hospital.objects.filter(code=options["hospital"])
|
||
if not hospitals.exists():
|
||
self.stdout.write(self.style.ERROR(f"No hospital found with code '{options['hospital']}'"))
|
||
return
|
||
else:
|
||
hospitals = Hospital.objects.filter(status="active")
|
||
|
||
if not hospitals.exists():
|
||
self.stdout.write(self.style.ERROR("No hospitals found"))
|
||
return
|
||
|
||
for hospital in hospitals:
|
||
self.stdout.write(f"\n{'=' * 60}")
|
||
self.stdout.write(f"Hospital: {hospital.name} ({hospital.code})")
|
||
self.stdout.write(f"{'=' * 60}")
|
||
|
||
for survey_def in SURVEYS:
|
||
self._import_survey(hospital, survey_def, update=options["update"])
|
||
|
||
def _import_survey(self, hospital, survey_def, update=False):
|
||
key = survey_def["key"]
|
||
name = survey_def["name"]
|
||
name_ar = survey_def["name_ar"]
|
||
questions = survey_def["questions"]
|
||
|
||
existing = SurveyTemplate.objects.filter(name=name, hospital=hospital).first()
|
||
|
||
if existing and not update:
|
||
self.stdout.write(f" [{key}] SKIP (already exists): {name}")
|
||
self.stdout.write(f" Questions: {existing.questions.count()}")
|
||
return
|
||
|
||
with transaction.atomic():
|
||
if existing:
|
||
existing.questions.all().delete()
|
||
template = existing
|
||
action = "UPDATED"
|
||
else:
|
||
template = SurveyTemplate.objects.create(
|
||
name=name,
|
||
name_ar=name_ar,
|
||
hospital=hospital,
|
||
survey_type="general",
|
||
scoring_method="average",
|
||
negative_threshold=3.0,
|
||
is_active=True,
|
||
)
|
||
action = "CREATED"
|
||
|
||
template.instructions_en = INSTRUCTIONS_EN
|
||
template.instructions_ar = INSTRUCTIONS_AR
|
||
template.consent_text_en = CONSENT_EN
|
||
template.consent_text_ar = CONSENT_AR
|
||
template.requires_consent = True
|
||
template.save(
|
||
update_fields=[
|
||
"instructions_en",
|
||
"instructions_ar",
|
||
"consent_text_en",
|
||
"consent_text_ar",
|
||
"requires_consent",
|
||
]
|
||
)
|
||
|
||
for q_data in questions:
|
||
SurveyQuestion.objects.create(
|
||
survey_template=template,
|
||
text=q_data["text"],
|
||
text_ar=q_data["text_ar"],
|
||
question_type=q_data["question_type"],
|
||
order=q_data["order"],
|
||
is_required=q_data["is_required"],
|
||
choices_json=q_data["choices_json"],
|
||
is_base=True,
|
||
)
|
||
|
||
self.stdout.write(self.style.SUCCESS(f" [{key}] {action}: {name}"))
|
||
self.stdout.write(f" Name (AR): {name_ar}")
|
||
self.stdout.write(f" Questions: {len(questions)}")
|