diff --git a/listener/app.py b/listener/app.py index 4c0f9200..765c3636 100644 --- a/listener/app.py +++ b/listener/app.py @@ -1312,6 +1312,39 @@ def split_patient_name(full_name): last_name = (last_name or "NAME").upper()[:20] return first_name, last_name +def sanitize_astm_field(value, *, uppercase=False, max_len=None, allow_component_sep=False): + """ + Sanitasi field agar aman untuk ASTM: + - Buang karakter NULL/control (termasuk CR/LF) + - Hilangkan delimiter ASTM pada field biasa + - Trim dan optional uppercase/truncate + """ + if value is None: + text = "" + else: + text = str(value) + + # Hilangkan NULL byte yang sering muncul dari CHAR/VARCHAR bermasalah. + text = text.replace("\x00", "") + # Buang karakter kontrol non-printable. + text = re.sub(r"[\x00-\x1f\x7f]", " ", text) + + if allow_component_sep: + text = text.replace("|", " ") + else: + text = text.replace("|", " ").replace("^", " ") + + text = re.sub(r"\s+", " ", text).strip() + + # ASTM payload dikirim dengan latin-1; karakter di luar rentang ini diganti aman. + text = text.encode("latin-1", errors="replace").decode("latin-1") + + if uppercase: + text = text.upper() + if max_len is not None: + text = text[:max_len] + return text + def create_vitek_order_message(order): """ Membuat Frame Order Vitek sesuai Manual Ref 514937. @@ -1325,7 +1358,8 @@ def create_vitek_order_message(order): p_name = f"{last_name}^{first_name}" specimen = str(order.kd_spesimen).upper() if order.kd_spesimen else "BLOOD" room = str(getattr(order, 'ruangan', "") or "RSSA MALANG").strip().upper() - room = room.replace("|", " ").replace("^", " ")[:30] + # VITEK: Patient Location Code menggunakan tag "pl" (maks 40 char pada spec yang dipakai) + room = room.replace("|", " ").replace("^", " ")[:40] now = datetime.datetime.now() date_str = now.strftime("%m/%d/%Y") @@ -1336,7 +1370,7 @@ def create_vitek_order_message(order): content_body = ( f"mtmpr|pi{pid}|pn{p_name}" f"|si|ss{specimen}" - f"|lo{room}" + f"|pl{room}" f"|s1{date_str}|s2{time_str}" f"|ci{sid}|t11|zz" ) @@ -1688,27 +1722,32 @@ def create_astm_order_message(order): Menghindari pergeseran kolom (shifting error). """ # --- 1. PERSIAPAN DATA --- - pid = str(order.norm).strip() if order.norm else "" - sid = str(order.rnoreg).strip() if order.rnoreg else "" + pid = sanitize_astm_field(order.norm, max_len=32) + sid = sanitize_astm_field(order.rnoreg, max_len=32) # Nama pasien dipisah agar mengikuti format ASTM: Last^First - first_name, last_name = split_patient_name(order.nama) + first_name, last_name = split_patient_name(sanitize_astm_field(order.nama, max_len=80)) + first_name = sanitize_astm_field(first_name, uppercase=True, max_len=20) + last_name = sanitize_astm_field(last_name, uppercase=True, max_len=20) p_name = f"{last_name}^{first_name}" - sex = "M" if str(order.rjenis).upper().startswith("L") else "F" + sex_raw = sanitize_astm_field(order.rjenis, uppercase=True, max_len=10) + sex = "M" if sex_raw.startswith("L") else "F" - # Lokasi / Ruangan (Field 26) - location = getattr(order, 'ruangan', "RSSA Malang") - if not location: location = "RSSA Malang" + # Lokasi / Ruangan (Field 26) sebaiknya berupa kode singkat agar tidak ditrunkasi LIS. + raw_location = sanitize_astm_field(getattr(order, 'ruangan', "UT"), uppercase=True, max_len=40) + location = re.sub(r"[^A-Z0-9]", "", raw_location)[:10] + if not location: + location = "UT" # Diagnosis (Clinical Info - Field 14 di ASTM standar atau 13 di beberapa varian) # Kita pasang di Index 13 (Field 14) agar aman - diagnosis = getattr(order, 'diagnosa', "Unspecified") + diagnosis = sanitize_astm_field(getattr(order, 'diagnosa', "Unspecified"), max_len=60) if not diagnosis: diagnosis = "Unspecified" # Specimen Info (Field 16) # Format: SpecimenType^BodySite^Container^Condition - specimen_type = str(order.kd_spesimen).upper() if order.kd_spesimen else "BLOOD" + specimen_type = sanitize_astm_field(order.kd_spesimen, uppercase=True, max_len=20) if order.kd_spesimen else "BLOOD" body_site = "VENA" # Site - condition = "Baik" # Condition + condition = "BAIK" # Condition specimen_field = f"{specimen_type}^{body_site}^^{condition}" # --- 2. KONSTRUKSI RECORD DENGAN INDEX PASTI --- @@ -1739,7 +1778,7 @@ def create_astm_order_message(order): o_rec[1] = "1" # Field 2 o_rec[2] = sid # Field 3: Sample ID o_rec[3] = "" # Field 4: Instrument Specimen ID - o_rec[4] = "^^^BD_BACTEC" # Field 5: Universal Test ID + o_rec[4] = "^^^" # Field 5: Universal Test ID o_rec[5] = "R" # Field 6: Priority # ... Field 7-11 ... o_rec[11] = "A" # Field 12: Action Code (A=Add, N=New) (Index 11)