Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,7 @@ import pandas as pd
|
|
| 3 |
import numpy as np
|
| 4 |
from datetime import datetime
|
| 5 |
import os
|
| 6 |
-
import
|
| 7 |
|
| 8 |
def process_files(thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file):
|
| 9 |
if not all([thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file]):
|
|
@@ -130,6 +130,8 @@ def process_files(thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file):
|
|
| 130 |
final_analysis['Nguy_Hiem_Baseline'] = final_analysis['PLT_baseline'].apply(get_nguy_hiem)
|
| 131 |
final_analysis['Nguy_Hiem_Nadir'] = final_analysis['PLT_nadir'].apply(get_nguy_hiem)
|
| 132 |
|
|
|
|
|
|
|
| 133 |
# Xuất các file check
|
| 134 |
file1 = "1_Check_Thuoc_Nghi_Ngo.xlsx"
|
| 135 |
check_thuoc.to_excel(file1, sheet_name='Sheet1', index=False)
|
|
@@ -140,16 +142,16 @@ def process_files(thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file):
|
|
| 140 |
file3 = "3_Check_PLT_Duoi_100.xlsx"
|
| 141 |
check_plt_low.to_excel(file3, sheet_name='Sheet1', index=False)
|
| 142 |
|
| 143 |
-
file4 = "4_Check_Giao_Thoa_Thuoc_PLT.xlsx"
|
| 144 |
inter_df = df_plt[df_plt['Mã KCB'].isin(inter_ids)].copy()
|
| 145 |
inter_df['CTCAE_Grade'] = inter_df['Kết quả XN'].apply(get_ctcae_plt)
|
| 146 |
inter_df['Nguy_Hiem'] = inter_df['Kết quả XN'].apply(get_nguy_hiem)
|
|
|
|
| 147 |
inter_df.to_excel(file4, sheet_name='Sheet1', index=False)
|
| 148 |
|
| 149 |
file5 = "5_Check_Baseline_Nadir_Full.xlsx"
|
| 150 |
final_analysis.to_excel(file5, sheet_name='Sheet1', index=False)
|
| 151 |
|
| 152 |
-
# Function export_realtime_reports
|
| 153 |
def export_realtime_reports(final_report, df_xetnghiem):
|
| 154 |
today = datetime.now()
|
| 155 |
filerealtime = f"Danh_Sach_Can_Thiep_DLS_{today.year}.xlsx"
|
|
@@ -167,41 +169,44 @@ def process_files(thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file):
|
|
| 167 |
include_priority = True
|
| 168 |
include_recommendations = True
|
| 169 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
with pd.ExcelWriter(filerealtime) as writer:
|
| 171 |
sheet_written = False
|
| 172 |
|
| 173 |
if not current_patients.empty:
|
| 174 |
-
current_patients['So_Ngay_Nam_Vien'] = ((today - current_patients['Ngày vào']).dt.days).fillna(0).astype(int)
|
| 175 |
-
|
| 176 |
-
if include_priority:
|
| 177 |
-
def assess_urgency(row):
|
| 178 |
-
days = row['So_Ngay_Nam_Vien']
|
| 179 |
-
if days >= 7: return "🔴 Rất cao (>= 7 ngày chưa XN lại)"
|
| 180 |
-
if days >= 3: return "🟠 Cao (3-6 ngày chưa XN lại)"
|
| 181 |
-
return "🟡 Trung bình"
|
| 182 |
-
current_patients['Muc_Do_Cap_Bach'] = current_patients.apply(assess_urgency, axis=1)
|
| 183 |
-
current_patients = current_patients.sort_values('So_Ngay_Nam_Vien', ascending=False)
|
| 184 |
-
|
| 185 |
-
if include_recommendations:
|
| 186 |
-
def compose_message(row):
|
| 187 |
-
thuoc = row['tenhh'] if pd.notna(row['tenhh']) else row['hoatchat']
|
| 188 |
-
plt_nen = row['PLT_baseline'] if pd.notna(row['PLT_baseline']) else 'N/A (thiếu baseline)'
|
| 189 |
-
ngay = row['So_Ngay_Nam_Vien']
|
| 190 |
-
nadir = row.get('PLT_nadir', 'N/A')
|
| 191 |
-
ctcae_nadir = row['CTCAE_Nadir']
|
| 192 |
-
nguy_hiem_nadir = row['Nguy_Hiem_Nadir']
|
| 193 |
-
if "Nghi ngờ (thiếu baseline)" in row['Signal']:
|
| 194 |
-
return (f"CẢNH BÁO ADR: Tiểu cầu bệnh nhân thấp ({nadir} G/L - {ctcae_nadir} - {nguy_hiem_nadir}) khi dùng {thuoc}, thiếu baseline. "
|
| 195 |
-
f"Đề xuất theo dõi và làm thêm XN Công thức máu (PLT) trong ngày hôm nay để xác nhận xu hướng.")
|
| 196 |
-
elif "sụt giảm" in row['Signal'] or "Trầm trọng" in row['Signal'] or "Nghi ngờ" in row['Signal']:
|
| 197 |
-
return (f"CẢNH BÁO ADR: Tiểu cầu bệnh nhân sụt giảm rõ rệt từ {plt_nen} G/L xuống còn {nadir} G/L "
|
| 198 |
-
f"({ctcae_nadir} - {nguy_hiem_nadir}) sau khi dùng {thuoc}. Đề xuất bác sĩ xem xét tạm ngưng hoặc đổi thuốc nếu không có nguyên nhân khác.")
|
| 199 |
-
else:
|
| 200 |
-
return (f"Bệnh nhân đã điều trị {ngay} ngày và đang sử dụng {thuoc} (có nguy cơ giảm PLT). "
|
| 201 |
-
f"Kết quả PLT nền thấp ({plt_nen} G/L) và chưa có xét nghiệm kiểm tra lại. "
|
| 202 |
-
f"Dược lâm sàng đề xuất làm thêm XN Công thức máu (PLT) trong ngày hôm nay.")
|
| 203 |
-
current_patients['Mau_Cau_Khuyen_Cao'] = current_patients.apply(compose_message, axis=1)
|
| 204 |
-
|
| 205 |
current_patients[cols + ['So_Ngay_Nam_Vien', 'Muc_Do_Cap_Bach', 'Mau_Cau_Khuyen_Cao']].to_excel(writer, sheet_name='CAN_THIEP_TRUC_TIEP', index=False)
|
| 206 |
sheet_written = True
|
| 207 |
|
|
@@ -214,13 +219,17 @@ def process_files(thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file):
|
|
| 214 |
|
| 215 |
num_current = len(current_patients)
|
| 216 |
num_hoicuu = len(hoi_cuu)
|
| 217 |
-
return filerealtime, num_current, num_hoicuu
|
| 218 |
|
| 219 |
-
filerealtime, num_current, num_hoicuu = export_realtime_reports(final_analysis, df_xetnghiem)
|
| 220 |
|
| 221 |
-
output_text = f"✅
|
| 222 |
-
output_text += f"
|
| 223 |
-
output_text += f"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
|
| 225 |
return output_text, file1, file2, file3, file4, file5, filerealtime
|
| 226 |
|
|
|
|
| 3 |
import numpy as np
|
| 4 |
from datetime import datetime
|
| 5 |
import os
|
| 6 |
+
import io
|
| 7 |
|
| 8 |
def process_files(thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file):
|
| 9 |
if not all([thuoc_file, xetnghiem_file, thuoc_plt_file, icd_plt_file]):
|
|
|
|
| 130 |
final_analysis['Nguy_Hiem_Baseline'] = final_analysis['PLT_baseline'].apply(get_nguy_hiem)
|
| 131 |
final_analysis['Nguy_Hiem_Nadir'] = final_analysis['PLT_nadir'].apply(get_nguy_hiem)
|
| 132 |
|
| 133 |
+
num_drop_30 = len(final_analysis[(final_analysis['Percent_Drop'] >= 30) & (final_analysis['Signal'] != "Không rõ rệt")]['makcb'].unique())
|
| 134 |
+
|
| 135 |
# Xuất các file check
|
| 136 |
file1 = "1_Check_Thuoc_Nghi_Ngo.xlsx"
|
| 137 |
check_thuoc.to_excel(file1, sheet_name='Sheet1', index=False)
|
|
|
|
| 142 |
file3 = "3_Check_PLT_Duoi_100.xlsx"
|
| 143 |
check_plt_low.to_excel(file3, sheet_name='Sheet1', index=False)
|
| 144 |
|
|
|
|
| 145 |
inter_df = df_plt[df_plt['Mã KCB'].isin(inter_ids)].copy()
|
| 146 |
inter_df['CTCAE_Grade'] = inter_df['Kết quả XN'].apply(get_ctcae_plt)
|
| 147 |
inter_df['Nguy_Hiem'] = inter_df['Kết quả XN'].apply(get_nguy_hiem)
|
| 148 |
+
file4 = "4_Check_Giao_Thoa_Thuoc_PLT.xlsx"
|
| 149 |
inter_df.to_excel(file4, sheet_name='Sheet1', index=False)
|
| 150 |
|
| 151 |
file5 = "5_Check_Baseline_Nadir_Full.xlsx"
|
| 152 |
final_analysis.to_excel(file5, sheet_name='Sheet1', index=False)
|
| 153 |
|
| 154 |
+
# Function export_realtime_reports, trả về thêm num_need_plt
|
| 155 |
def export_realtime_reports(final_report, df_xetnghiem):
|
| 156 |
today = datetime.now()
|
| 157 |
filerealtime = f"Danh_Sach_Can_Thiep_DLS_{today.year}.xlsx"
|
|
|
|
| 169 |
include_priority = True
|
| 170 |
include_recommendations = True
|
| 171 |
|
| 172 |
+
if not current_patients.empty:
|
| 173 |
+
current_patients['So_Ngay_Nam_Vien'] = ((today - current_patients['Ngày vào']).dt.days).fillna(0).astype(int)
|
| 174 |
+
|
| 175 |
+
if include_priority:
|
| 176 |
+
def assess_urgency(row):
|
| 177 |
+
days = row['So_Ngay_Nam_Vien']
|
| 178 |
+
if days >= 7: return "🔴 Rất cao (>= 7 ngày chưa XN lại)"
|
| 179 |
+
if days >= 3: return "🟠 Cao (3-6 ngày chưa XN lại)"
|
| 180 |
+
return "🟡 Trung bình"
|
| 181 |
+
current_patients['Muc_Do_Cap_Bach'] = current_patients.apply(assess_urgency, axis=1)
|
| 182 |
+
current_patients = current_patients.sort_values('So_Ngay_Nam_Vien', ascending=False)
|
| 183 |
+
|
| 184 |
+
if include_recommendations:
|
| 185 |
+
def compose_message(row):
|
| 186 |
+
thuoc = row['tenhh'] if pd.notna(row['tenhh']) else row['hoatchat']
|
| 187 |
+
plt_nen = row['PLT_baseline'] if pd.notna(row['PLT_baseline']) else 'N/A (thiếu baseline)'
|
| 188 |
+
ngay = row['So_Ngay_Nam_Vien']
|
| 189 |
+
nadir = row.get('PLT_nadir', 'N/A')
|
| 190 |
+
ctcae_nadir = row['CTCAE_Nadir']
|
| 191 |
+
nguy_hiem_nadir = row['Nguy_Hiem_Nadir']
|
| 192 |
+
if "Nghi ngờ (thiếu baseline)" in row['Signal']:
|
| 193 |
+
return (f"CẢNH BÁO ADR: Tiểu cầu bệnh nhân thấp ({nadir} G/L - {ctcae_nadir} - {nguy_hiem_nadir}) khi dùng {thuoc}, thiếu baseline. "
|
| 194 |
+
f"Đề xuất theo dõi và làm thêm XN Công thức máu (PLT) trong ngày hôm nay để xác nhận xu hướng.")
|
| 195 |
+
elif "sụt giảm" in row['Signal'] or "Trầm trọng" in row['Signal'] or "Nghi ngờ" in row['Signal']:
|
| 196 |
+
return (f"CẢNH BÁO ADR: Tiểu cầu bệnh nhân sụt giảm rõ rệt từ {plt_nen} G/L xuống còn {nadir} G/L "
|
| 197 |
+
f"({ctcae_nadir} - {nguy_hiem_nadir}) sau khi dùng {thuoc}. Đề xuất bác sĩ xem xét tạm ngưng hoặc đổi thuốc nếu không có nguyên nhân khác.")
|
| 198 |
+
else:
|
| 199 |
+
return (f"Bệnh nhân đã điều trị {ngay} ngày và đang sử dụng {thuoc} (có nguy cơ giảm PLT). "
|
| 200 |
+
f"Kết quả PLT nền thấp ({plt_nen} G/L) và chưa có xét nghiệm kiểm tra lại. "
|
| 201 |
+
f"Dược lâm sàng đề xuất làm thêm XN Công thức máu (PLT) trong ngày hôm nay.")
|
| 202 |
+
current_patients['Mau_Cau_Khuyen_Cao'] = current_patients.apply(compose_message, axis=1)
|
| 203 |
+
|
| 204 |
+
num_need_plt = len(current_patients[current_patients['Mau_Cau_Khuyen_Cao'].str.contains("làm thêm XN")])
|
| 205 |
+
|
| 206 |
with pd.ExcelWriter(filerealtime) as writer:
|
| 207 |
sheet_written = False
|
| 208 |
|
| 209 |
if not current_patients.empty:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
current_patients[cols + ['So_Ngay_Nam_Vien', 'Muc_Do_Cap_Bach', 'Mau_Cau_Khuyen_Cao']].to_excel(writer, sheet_name='CAN_THIEP_TRUC_TIEP', index=False)
|
| 211 |
sheet_written = True
|
| 212 |
|
|
|
|
| 219 |
|
| 220 |
num_current = len(current_patients)
|
| 221 |
num_hoicuu = len(hoi_cuu)
|
| 222 |
+
return filerealtime, num_current, num_hoicuu, num_need_plt
|
| 223 |
|
| 224 |
+
filerealtime, num_current, num_hoicuu, num_need_plt = export_realtime_reports(final_analysis, df_xetnghiem)
|
| 225 |
|
| 226 |
+
output_text = f"✅ Số bệnh nhân có PLT dưới 100: {num_plt_low}\n"
|
| 227 |
+
output_text += f"✅ Số bệnh nhân sử dụng thuốc nghi ngờ và có PLT dưới 100: {num_inter}\n"
|
| 228 |
+
output_text += f"✅ Số bệnh nhân sử dụng thuốc nghi ngờ và có PLT giảm >=30%: {num_drop_30}\n"
|
| 229 |
+
output_text += f"✅ Số bệnh nhân cần làm thêm xét nghiệm PLT: {num_need_plt}\n"
|
| 230 |
+
output_text += f"✅ Số bệnh nhân cần can thiệp: {num_current}\n"
|
| 231 |
+
output_text += f"✅ Số bệnh nhân cần hồi cứu báo cáo ADR: {num_hoicuu}\n"
|
| 232 |
+
output_text += f"✅ Đã xuất file '{os.path.basename(filerealtime)}'.\n"
|
| 233 |
|
| 234 |
return output_text, file1, file2, file3, file4, file5, filerealtime
|
| 235 |
|