HH/apps/integrations/management/commands/seed_his_today_data.py
2026-03-28 14:03:56 +03:00

212 lines
8.7 KiB
Python

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