""" Seed today's test data for HIS integration cron testing. Creates ~20 patients with recent timestamps so the 5-minute fetch_his_surveys cron will pick them up. Usage: python manage.py seed_his_today_data [--clear] """ import copy import random import string from django.core.management.base import BaseCommand from django.utils import timezone SEED_PREFIX = "SEED" ED_EVENTS = [ ("Consultation", 0), ("Doctor assignment", 5), ("Drug Prescription", 10), ("End Of the Episode", 15), ] IP_EVENTS = [ ("IP Admissions", 0), ("Bed Allocation", 2), ("Fit for discharge", 60), ("Discharge date", 65), ("Discharge followUp", 63), ("IP Bill", 67), ] OP_EVENTS = [ ("Consultation", 0), ("Doctor Visited", 3), ("Rad Prescription", 8), ("Radiology Bill", 10), ("Radiology Token", 10), ("Radiology Patient Arrived", 15), ("Radiology Examination completed", 20), ] PATIENT_SCENARIOS = [ {"type": "ED", "discharged": True, "admit_min_ago": 90, "discharge_min_ago": 75}, {"type": "ED", "discharged": True, "admit_min_ago": 60, "discharge_min_ago": 45}, {"type": "ED", "discharged": True, "admit_min_ago": 30, "discharge_min_ago": 15}, {"type": "ED", "discharged": True, "admit_min_ago": 15, "discharge_min_ago": 10}, {"type": "ED", "discharged": False, "admit_min_ago": 20, "discharge_min_ago": None}, {"type": "ED", "discharged": False, "admit_min_ago": 8, "discharge_min_ago": None}, {"type": "IP", "discharged": True, "admit_min_ago": 120, "discharge_min_ago": 60}, {"type": "IP", "discharged": True, "admit_min_ago": 90, "discharge_min_ago": 30}, {"type": "IP", "discharged": True, "admit_min_ago": 60, "discharge_min_ago": 10}, {"type": "IP", "discharged": True, "admit_min_ago": 45, "discharge_min_ago": 5}, {"type": "IP", "discharged": False, "admit_min_ago": 90, "discharge_min_ago": None}, {"type": "IP", "discharged": False, "admit_min_ago": 30, "discharge_min_ago": None}, {"type": "OP", "discharged": True, "admit_min_ago": 200, "discharge_min_ago": None, "last_event_min_ago": 180}, {"type": "OP", "discharged": True, "admit_min_ago": 150, "discharge_min_ago": None, "last_event_min_ago": 120}, {"type": "OP", "discharged": True, "admit_min_ago": 120, "discharge_min_ago": None, "last_event_min_ago": 90}, {"type": "OP", "discharged": False, "admit_min_ago": 30, "discharge_min_ago": None, "last_event_min_ago": 10}, {"type": "OP", "discharged": False, "admit_min_ago": 15, "discharge_min_ago": None, "last_event_min_ago": 5}, {"type": "OP", "discharged": False, "admit_min_ago": 8, "discharge_min_ago": None, "last_event_min_ago": 3}, ] def _generate_admission_id(index): suffix = "".join(random.choices(string.digits, k=6)) return f"{SEED_PREFIX}-{index}-{suffix}" def _format_his_date(dt): return dt.strftime("%d-%b-%Y %H:%M") class Command(BaseCommand): help = "Seed today's test data for HIS cron testing" def add_arguments(self, parser): parser.add_argument("--clear", action="store_true", help="Remove previously seeded data") def handle(self, *args, **options): from apps.integrations.models import HISTestPatient, HISTestVisit if options["clear"]: deleted_p, _ = HISTestPatient.objects.filter(admission_id__startswith=SEED_PREFIX).delete() deleted_v, _ = HISTestVisit.objects.filter(admission_id__startswith=SEED_PREFIX).delete() self.stdout.write(f"Cleared {deleted_p} patients, {deleted_v} visits") return self.stdout.write(self.style.MIGRATE_HEADING("=" * 60)) self.stdout.write(self.style.MIGRATE_HEADING("Seed Today's HIS Test Data")) self.stdout.write(self.style.MIGRATE_HEADING("=" * 60)) now = timezone.now() existing = list(HISTestPatient.objects.exclude(admission_id__startswith=SEED_PREFIX).order_by("?")[:18]) if len(existing) < 18: self.stderr.write(self.style.ERROR("Not enough source patients. Run load_his_test_data first.")) return self.stdout.write(f"Source patients available: {len(existing)}") patients_batch = [] visits_batch = [] summary = {"ED": 0, "IP": 0, "OP": 0} for i, scenario in enumerate(PATIENT_SCENARIOS): source = existing[i % len(existing)] pt_type = scenario["type"] summary[pt_type] = summary.get(pt_type, 0) + 1 admit_date = now - timezone.timedelta(minutes=scenario["admit_min_ago"]) admission_id = _generate_admission_id(i) patient_data = copy.deepcopy(source.patient_data) patient_data["AdmissionID"] = admission_id patient_data["AdmitDate"] = _format_his_date(admit_date) patient_data["PatientType"] = pt_type patient_data["PatientTypeID"] = {"ED": "3", "IP": "2", "OP": "1"}[pt_type] if scenario["discharged"] and scenario.get("discharge_min_ago"): discharge_date = now - timezone.timedelta(minutes=scenario["discharge_min_ago"]) patient_data["DischargeDate"] = _format_his_date(discharge_date) else: discharge_date = None patient_data["DischargeDate"] = None patients_batch.append( HISTestPatient( admission_id=admission_id, patient_id=source.patient_id, patient_type=pt_type, reg_code=source.reg_code, ssn=source.ssn, mobile_no=source.mobile_no, admit_date=admit_date, discharge_date=discharge_date, patient_data=patient_data, hospital_id=source.hospital_id, hospital_name=source.hospital_name, patient_name=source.patient_name, ) ) visit_category = {"ED": "ED", "IP": "IP", "OP": "OP"}[pt_type] patient_type_visit = {"ED": "ER", "IP": "IP", "OP": "OP"}[pt_type] events = {"ED": ED_EVENTS, "IP": IP_EVENTS, "OP": OP_EVENTS}[pt_type] last_event_min_ago = scenario.get("last_event_min_ago") if last_event_min_ago is None: if scenario["discharged"] and scenario.get("discharge_min_ago"): last_event_min_ago = scenario["discharge_min_ago"] else: last_event_min_ago = max(scenario["admit_min_ago"] - 5, 1) for j, (event_name, offset_min) in enumerate(events): if not scenario["discharged"] and event_name in ( "Fit for discharge", "Discharge date", "Discharge followUp", "IP Bill", "End Of the Episode", ): continue if pt_type == "OP" and not scenario["discharged"] and j == len(events) - 1: event_date = now - timezone.timedelta(minutes=last_event_min_ago) else: event_date = admit_date + timezone.timedelta(minutes=offset_min) visit_data = { "PatientType": patient_type_visit, "Type": event_name, "BillDate": _format_his_date(event_date), "AdmissionID": admission_id, "PatientID": source.patient_id, "RegCode": source.reg_code, "SSN": source.ssn, "MobileNo": source.mobile_no, } visits_batch.append( HISTestVisit( admission_id=admission_id, patient_id=source.patient_id, visit_category=visit_category, event_type=event_name, bill_date=event_date, reg_code=source.reg_code, ssn=source.ssn, mobile_no=source.mobile_no, visit_data=visit_data, ) ) HISTestPatient.objects.bulk_create(patients_batch, batch_size=1000) self.stdout.write(f"Created {len(patients_batch)} patients") HISTestVisit.objects.bulk_create(visits_batch, batch_size=2000) self.stdout.write(f"Created {len(visits_batch)} visits") self.stdout.write(self.style.MIGRATE_HEADING("\nSummary")) self.stdout.write(f" ED: {summary['ED']} (4 discharged, 2 active)") self.stdout.write(f" IP: {summary['IP']} (4 discharged, 2 active)") self.stdout.write(f" OP: {summary['OP']} (3 complete, 3 in progress)") self.stdout.write(self.style.SUCCESS("\nDone! Run fetch_his_surveys to test."))