HH/apps/surveys/management/commands/import_alhammadi_surveys.py
2026-04-08 17:13:35 +03:00

448 lines
22 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)}")