TrungLC commited on
Commit
bbbe66f
·
verified ·
1 Parent(s): 0c06fb3

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +519 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,519 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import openpyxl
3
+ from openpyxl.styles import PatternFill, Font
4
+ import re
5
+ import gradio as gr
6
+ import os
7
+ import json
8
+ import math
9
+
10
+ DB_TK_FILE = "db_tk.xlsx"
11
+ DB_DINH_FILE = "db_dinh.xlsx"
12
+ DB_DVT_FILE = "db_dvt.xlsx"
13
+ DB_LIEU_FILE = "db_lieu.xlsx"
14
+ DB_NL_FILE = "db_nl.xlsx"
15
+ EXCEPTION_FILE = "exceptions.json"
16
+
17
+ def normalize(value):
18
+ if pd.isna(value):
19
+ return ""
20
+ return str(value).strip().replace(",", ".")
21
+
22
+ def normalize_unit(unit):
23
+ if pd.isna(unit):
24
+ return ""
25
+ return str(unit).strip().lower()
26
+
27
+ def normalize_drug_name(name):
28
+ return str(name).strip().lower() if pd.notna(name) else ""
29
+
30
+ def trim_column(df_input):
31
+ if "LIEU_DUNG" in df_input.columns:
32
+ df_input["LIEU_DUNG"] = df_input["LIEU_DUNG"].astype(str).str.strip()
33
+ if "DON_VI_TINH" in df_input.columns:
34
+ df_input["DON_VI_TINH"] = df_input["DON_VI_TINH"].astype(str).str.strip()
35
+ return df_input
36
+
37
+ def extract_units(lieu_dung_str):
38
+ text = str(lieu_dung_str).strip().lower()
39
+ m1 = re.match(r"^(\d+(?:[.,]\d+|\/\d+)?)[ ]*([^\s\/\[\]\*]+)", text)
40
+ unit1 = m1.group(2) if m1 else ""
41
+ m2 = re.search(r"\[\s*(\d+(?:[.,]\d+|\/\d+)?)\s*([^\s\/\[\]]+)", text)
42
+ unit2 = m2.group(2) if m2 else ""
43
+ return unit1.strip(), unit2.strip()
44
+
45
+ def get_special_format_set_from_file(file_path):
46
+ """
47
+ Đọc file DB_DINH và trả về set các tuple (normalized_ten_thuoc, normalized_lieu_dung, normalized_so_luong).
48
+ """
49
+ if not os.path.exists(file_path):
50
+ return set()
51
+ try:
52
+ df = pd.read_excel(file_path, header=0)
53
+ if not all(col in df.columns for col in ["TEN_THUOC", "LIEU_DUNG", "SO_LUONG"]):
54
+ return set()
55
+ exceptions = set()
56
+ for _, row in df.iterrows():
57
+ ten = normalize_drug_name(row["TEN_THUOC"])
58
+ lieu = normalize(row["LIEU_DUNG"])
59
+ so_luong = normalize(row["SO_LUONG"])
60
+ if ten and lieu and so_luong:
61
+ exceptions.add((ten, lieu, so_luong))
62
+ return exceptions
63
+ except Exception:
64
+ return set()
65
+
66
+ def get_special_qty_set_from_file(file_path):
67
+ """
68
+ Đọc file DB_TK và trả về set các tuple (normalized_ten_thuoc, normalized_lieu_dung, normalized_so_luong).
69
+ """
70
+ if not os.path.exists(file_path):
71
+ return set()
72
+ try:
73
+ df = pd.read_excel(file_path, header=0)
74
+ if not all(col in df.columns for col in ["TEN_THUOC", "LIEU_DUNG", "SO_LUONG"]):
75
+ return set()
76
+ exceptions = set()
77
+ for _, row in df.iterrows():
78
+ ten = normalize_drug_name(row["TEN_THUOC"])
79
+ lieu = normalize(row["LIEU_DUNG"])
80
+ so_luong = normalize(row["SO_LUONG"])
81
+ if ten and lieu and so_luong:
82
+ exceptions.add((ten, lieu, so_luong))
83
+ return exceptions
84
+ except Exception:
85
+ return set()
86
+
87
+ def get_special_unit_set_from_file(file_path):
88
+ """
89
+ Đọc file DB_DVT và trả về set các tuple (normalized_ten_thuoc, normalized_lieu_dung, normalized_so_luong).
90
+ """
91
+ if not os.path.exists(file_path):
92
+ return set()
93
+ try:
94
+ df = pd.read_excel(file_path, header=0)
95
+ if not all(col in df.columns for col in ["TEN_THUOC", "LIEU_DUNG", "SO_LUONG"]):
96
+ return set()
97
+ exceptions = set()
98
+ for _, row in df.iterrows():
99
+ ten = normalize_drug_name(row["TEN_THUOC"])
100
+ lieu = normalize(row["LIEU_DUNG"])
101
+ so_luong = normalize(row["SO_LUONG"])
102
+ if ten and lieu and so_luong:
103
+ exceptions.add((ten, lieu, so_luong))
104
+ return exceptions
105
+ except Exception:
106
+ return set()
107
+
108
+ def get_lieu_exceptions(file_path):
109
+ """
110
+ Đọc file DB_LIEU và trả về set các tuple (normalized_ten_thuoc, normalized_lieu_dung, normalized_so_luong).
111
+ """
112
+ if not os.path.exists(file_path):
113
+ return set()
114
+ try:
115
+ df = pd.read_excel(file_path, header=0)
116
+ if not all(col in df.columns for col in ["TEN_THUOC", "LIEU_DUNG", "SO_LUONG"]):
117
+ return set()
118
+ exceptions = set()
119
+ for _, row in df.iterrows():
120
+ ten = normalize_drug_name(row["TEN_THUOC"])
121
+ lieu = normalize(row["LIEU_DUNG"])
122
+ so_luong = normalize(row["SO_LUONG"])
123
+ if ten and lieu and so_luong:
124
+ exceptions.add((ten, lieu, so_luong))
125
+ return exceptions
126
+ except Exception:
127
+ return set()
128
+
129
+ def get_allowed_exceptions_tuples(file_path):
130
+ """
131
+ Đọc file DB_NL và trả về set các tuple (normalized_ten_thuoc, normalized_lieu_dung, normalized_so_luong).
132
+ """
133
+ if not os.path.exists(file_path):
134
+ return set()
135
+ try:
136
+ df = pd.read_excel(file_path, header=0)
137
+ if not all(col in df.columns for col in ["TEN_THUOC", "LIEU_DUNG", "SO_LUONG"]):
138
+ return set()
139
+ exceptions = set()
140
+ for _, row in df.iterrows():
141
+ ten = normalize_drug_name(row["TEN_THUOC"])
142
+ lieu = normalize(row["LIEU_DUNG"])
143
+ so_luong = normalize(row["SO_LUONG"])
144
+ if ten and lieu and so_luong:
145
+ exceptions.add((ten, lieu, so_luong))
146
+ return exceptions
147
+ except Exception:
148
+ return set()
149
+
150
+ def load_exceptions():
151
+ if not os.path.exists(EXCEPTION_FILE):
152
+ return {
153
+ "format": [],
154
+ "allowed": [],
155
+ "unitAllowed": []
156
+ }
157
+ with open(EXCEPTION_FILE, "r", encoding="utf-8") as f:
158
+ data = json.load(f)
159
+ return {
160
+ "format": [normalize(x) for x in data.get("formatErrors", [])],
161
+ "allowed": [normalize(x) for x in data.get("allowedExceptions", [])],
162
+ "unitAllowed": [normalize(x) for x in data.get("unitAllowed", [])]
163
+ }
164
+
165
+ def save_exceptions_from_files(db_tk_file, db_dinh_file, db_dvt_file, db_lieu_file, db_nl_file):
166
+ file_map = [
167
+ (db_tk_file, DB_TK_FILE),
168
+ (db_dinh_file, DB_DINH_FILE),
169
+ (db_dvt_file, DB_DVT_FILE),
170
+ (db_lieu_file, DB_LIEU_FILE),
171
+ (db_nl_file, DB_NL_FILE)
172
+ ]
173
+ for fileobj, target in file_map:
174
+ if fileobj is not None:
175
+ try:
176
+ if hasattr(fileobj, "name"):
177
+ with open(fileobj.name, "rb") as src, open(target, "wb") as dst:
178
+ dst.write(src.read())
179
+ else:
180
+ fileobj.seek(0)
181
+ with open(target, "wb") as f:
182
+ f.write(fileobj.read())
183
+ except Exception as e:
184
+ return f"Lỗi lưu {target}: {e}"
185
+
186
+ def read_col_a(fileobj):
187
+ if fileobj is None:
188
+ return []
189
+ try:
190
+ if hasattr(fileobj, "name"):
191
+ df = pd.read_excel(fileobj.name, sheet_name=0, header=0)
192
+ else:
193
+ fileobj.seek(0)
194
+ df = pd.read_excel(fileobj, sheet_name=0, header=0)
195
+ return [str(x).strip() for x in df.iloc[:,0] if pd.notna(x) and str(x).strip() != ""]
196
+ except Exception:
197
+ return []
198
+ format_list = read_col_a(db_dinh_file)
199
+ allowed_list = [] # không dùng allowed_list kiểu cũ nữa
200
+ unitallowed_list = read_col_a(db_dvt_file)
201
+ if os.path.exists(EXCEPTION_FILE):
202
+ with open(EXCEPTION_FILE, "r", encoding="utf-8") as f:
203
+ data = json.load(f)
204
+ else:
205
+ data = {
206
+ "formatErrors": [],
207
+ "allowedExceptions": [],
208
+ "unitAllowed": []
209
+ }
210
+ if format_list:
211
+ data["formatErrors"] = format_list
212
+ if unitallowed_list:
213
+ data["unitAllowed"] = unitallowed_list
214
+ with open(EXCEPTION_FILE, "w", encoding="utf-8") as f:
215
+ json.dump(data, f, indent=2, ensure_ascii=False)
216
+
217
+ return "✅ Đã cập nhật DB_TK, DB_DINH, DB_DVT, DB_LIEU, DB_NL!"
218
+
219
+ def highlight_insulin_errors(df_input, insulin_file):
220
+ if insulin_file is None:
221
+ return [], []
222
+
223
+ try:
224
+ df_insulin = pd.read_excel(insulin_file.name if hasattr(insulin_file, "name") else insulin_file, header=0)
225
+ except Exception:
226
+ return [], []
227
+
228
+ insulin_map = {}
229
+ for idx, row in df_insulin.iterrows():
230
+ ten = normalize_drug_name(row.get("TEN_THUOC", ""))
231
+ try:
232
+ hamluong = float(str(row.get("HAM_LUONG", "")).replace(",", "."))
233
+ so_sanh = float(str(row.get("SO_LUONG_SO_SANH", "")).replace(",", "."))
234
+ except Exception:
235
+ continue
236
+ if ten and hamluong is not None and so_sanh is not None:
237
+ insulin_map[ten] = (hamluong, so_sanh)
238
+
239
+ error_rows_cam = []
240
+ error_rows_xanh = []
241
+ for idx, row in df_input.iterrows():
242
+ ten_thuoc = normalize_drug_name(row.get("TEN_THUOC", ""))
243
+ if ten_thuoc not in insulin_map:
244
+ continue
245
+ ham_luong, so_sanh = insulin_map[ten_thuoc]
246
+ lieu_goc = str(row.get("LIEU_DUNG", ""))
247
+ lieu = re.sub(r"\s+", "", lieu_goc)
248
+ matches = re.findall(r":(\d+)[iI][uU]\/lần", lieu)
249
+ tong_1_lan = sum(int(m) for m in matches) if matches else 0
250
+ match_ngay = re.search(r"\[(\d+)[iI][uU]\/ngày\]", lieu)
251
+ so_trong_ngay = int(match_ngay.group(1)) if match_ngay else 0
252
+
253
+ if tong_1_lan != so_trong_ngay or tong_1_lan == 0 or so_trong_ngay == 0:
254
+ error_rows_xanh.append(idx)
255
+ else:
256
+ match_son = re.search(r"\*(\d+)ngày", lieu)
257
+ so_ngay = int(match_son.group(1)) if match_son else 0
258
+ tong_chi_dinh = so_trong_ngay * so_ngay
259
+ so_luong_thuc_te = row.get("SO_LUONG", row.get("So_luong", row.get("so_luong", row.get("SỐ_LƯỢNG", row.get("M", None)))))
260
+ try:
261
+ so_luong_thuc_te = float(str(so_luong_thuc_te).replace(",", "."))
262
+ except Exception:
263
+ continue
264
+ tong_thuc_te = ham_luong * so_luong_thuc_te
265
+ hieu = tong_thuc_te - tong_chi_dinh
266
+ if hieu >= so_sanh:
267
+ error_rows_cam.append(idx)
268
+ return error_rows_cam, error_rows_xanh
269
+
270
+ def highlight_invalid_rows(df_input, exceptions, special_format_tuples, lieu_exceptions, insulin_xanh_rows=None, allowed_exceptions=None):
271
+ pattern = re.compile(
272
+ r"^(\d+(?:[.,]\d+|\/\d+)?)\s+"
273
+ r"(ml|mg|viên|gói|chai|lọ|ống|túi|giọt|iu|ui)\/lần\s*\*\s*"
274
+ r"(\d+)\s*lần\/ngày\s*\*\s*"
275
+ r"(\d+(?:[.,]\d+|\/\d+)?)\s*ngày\s*\["
276
+ r"(\d+(?:[.,]\d+|\/\d+)?)\s+"
277
+ r"(ml|mg|viên|gói|chai|lọ|ống|túi|giọt|iu|ui)\/ngày\]\.$"
278
+ )
279
+ dose_error_rows, format_error_rows = [], []
280
+ insulin_xanh_rows = set(insulin_xanh_rows) if insulin_xanh_rows else set()
281
+ allowed_exceptions = allowed_exceptions if allowed_exceptions is not None else set()
282
+ if "LIEU_DUNG" in df_input.columns:
283
+ for index, row in df_input.iterrows():
284
+ if index in insulin_xanh_rows:
285
+ dose_error_rows.append(index)
286
+ continue
287
+ ten_thuoc = normalize_drug_name(row.get("TEN_THUOC", row.get("ten_thuoc", "")))
288
+ lieu_value = normalize(row.get("LIEU_DUNG", ""))
289
+ so_luong = normalize(row.get("SO_LUONG", row.get("So_luong", row.get("so_luong", row.get("SỐ_LƯỢNG", row.get("M", None))))))
290
+ if (ten_thuoc, lieu_value, so_luong) in allowed_exceptions:
291
+ continue
292
+ if (ten_thuoc, lieu_value, so_luong) in lieu_exceptions:
293
+ continue
294
+ if (ten_thuoc, lieu_value, so_luong) in special_format_tuples:
295
+ continue
296
+ if lieu_value in exceptions.get("format", []):
297
+ format_error_rows.append(index)
298
+ continue
299
+ match = pattern.match(lieu_value)
300
+ if match:
301
+ try:
302
+ dose_per_time = float(match.group(1).replace(",", ".").replace("/", "/1.0"))
303
+ times_per_day = int(match.group(3))
304
+ actual_daily_dose = float(match.group(5).replace(",", ".").replace("/", "/1.0"))
305
+ expected = dose_per_time * times_per_day
306
+ if not math.isclose(expected, actual_daily_dose, rel_tol=1e-3):
307
+ dose_error_rows.append(index)
308
+ except Exception:
309
+ dose_error_rows.append(index)
310
+ else:
311
+ format_error_rows.append(index)
312
+ return dose_error_rows, format_error_rows
313
+
314
+ def highlight_unit_errors(df_input, exceptions, special_unit_tuples, allowed_exceptions=None):
315
+ unit_error_rows = []
316
+ allowed_unit_lieudung = set([normalize(x) for x in exceptions.get("unitAllowed", [])])
317
+ allowed_exceptions = allowed_exceptions if allowed_exceptions is not None else set()
318
+ for idx, row in df_input.iterrows():
319
+ ten_thuoc = normalize_drug_name(row.get("TEN_THUOC", row.get("ten_thuoc", "")))
320
+ lieu_dung_raw = row.get("LIEU_DUNG", "")
321
+ don_vi_tinh_raw = row.get("DON_VI_TINH", "")
322
+ lieu_dung_norm = normalize(lieu_dung_raw)
323
+ don_vi_tinh_norm = normalize_unit(don_vi_tinh_raw)
324
+ so_luong = normalize(row.get("SO_LUONG", row.get("So_luong", row.get("so_luong", row.get("SỐ_LƯỢNG", row.get("M", None))))))
325
+ if (ten_thuoc, lieu_dung_norm, so_luong) in allowed_exceptions:
326
+ continue
327
+ if (ten_thuoc, lieu_dung_norm, so_luong) in special_unit_tuples:
328
+ continue
329
+ if lieu_dung_norm in allowed_unit_lieudung:
330
+ continue
331
+ unit1, unit2 = extract_units(lieu_dung_raw)
332
+ unit1_norm = normalize_unit(unit1)
333
+ unit2_norm = normalize_unit(unit2)
334
+ if not don_vi_tinh_norm or don_vi_tinh_norm == "nan":
335
+ unit_error_rows.append(idx)
336
+ continue
337
+ if not (unit1_norm == don_vi_tinh_norm and unit2_norm == don_vi_tinh_norm):
338
+ unit_error_rows.append(idx)
339
+ return unit_error_rows
340
+
341
+ def highlight_wrong_quantity_rows(df_input, special_qty_tuples, allowed_exceptions=None):
342
+ wrong_qty_rows = []
343
+ allowed_exceptions = allowed_exceptions if allowed_exceptions is not None else set()
344
+ for idx, row in df_input.iterrows():
345
+ ten_thuoc = normalize_drug_name(row.get("TEN_THUOC", row.get("ten_thuoc", "")))
346
+ lieu_dung = str(row.get("LIEU_DUNG", ""))
347
+ lieu_dung_norm = normalize(lieu_dung)
348
+ so_luong = normalize(row.get("SO_LUONG", row.get("So_luong", row.get("so_luong", row.get("SỐ_LƯỢNG", row.get("M", None))))))
349
+ if (ten_thuoc, lieu_dung_norm, so_luong) in allowed_exceptions:
350
+ continue
351
+ if (ten_thuoc, lieu_dung_norm, so_luong) in special_qty_tuples:
352
+ continue
353
+ so_luong_thuc_te = row.get("SO_LUONG", row.get("So_luong", row.get("so_luong", row.get("SỐ_LƯỢNG", row.get("M", None)))))
354
+ if so_luong_thuc_te is None and isinstance(row, (pd.Series, dict)):
355
+ try:
356
+ so_luong_thuc_te = row.iloc[12]
357
+ except Exception:
358
+ continue
359
+ if pd.isna(so_luong_thuc_te):
360
+ continue
361
+ so_ngay_match = re.search(r"\*\s*(\d+(?:[.,]\d+|\/\d+)?)\s*ngày", lieu_dung)
362
+ so_luong_ngay_match = re.search(r"\[\s*(\d+(?:[.,]\d+|\/\d+)?)\s*[^\]]+\/ngày\]", lieu_dung)
363
+ if not so_ngay_match or not so_luong_ngay_match:
364
+ continue
365
+ try:
366
+ so_ngay = float(so_ngay_match.group(1).replace(",", "."))
367
+ so_luong_moi_ngay = float(so_luong_ngay_match.group(1).replace(",", "."))
368
+ so_luong_can_ke = so_ngay * so_luong_moi_ngay
369
+ try:
370
+ so_luong_thuc_te_val = float(str(so_luong_thuc_te).replace(",", "."))
371
+ except Exception:
372
+ continue
373
+ if not math.isclose(so_luong_can_ke, so_luong_thuc_te_val, rel_tol=1e-2):
374
+ wrong_qty_rows.append(idx)
375
+ except Exception:
376
+ continue
377
+ return wrong_qty_rows
378
+
379
+ def update_excel_with_errors(wb, insulin_errors, dose_errors, format_errors, unit_errors, wrong_qty_errors, output_path):
380
+ ws = wb.active
381
+ orange_fill = PatternFill(start_color="FFA500", end_color="FFA500", fill_type="solid") # Cam
382
+ yellow_fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
383
+ blue_fill = PatternFill(start_color="0088FF", end_color="0088FF", fill_type="solid")
384
+ red_fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
385
+ purple_fill = PatternFill(start_color="845EC2", end_color="845EC2", fill_type="solid")
386
+ white_font = Font(color="FFFFFF")
387
+ insulin_set = set(insulin_errors)
388
+ wrong_qty_set = set(wrong_qty_errors)
389
+ unit_set = set(unit_errors)
390
+ dose_set = set(dose_errors)
391
+ format_set = set(format_errors)
392
+ for idx in range(2, ws.max_row + 1):
393
+ row_idx = idx - 2
394
+ if row_idx in insulin_set:
395
+ ws[f"I{idx}"].fill = orange_fill
396
+ elif row_idx in wrong_qty_set:
397
+ ws[f"I{idx}"].fill = yellow_fill
398
+ elif row_idx in unit_set:
399
+ ws[f"I{idx}"].fill = purple_fill
400
+ ws[f"I{idx}"].font = white_font
401
+ elif row_idx in format_set:
402
+ ws[f"I{idx}"].fill = red_fill
403
+ elif row_idx in dose_set:
404
+ ws[f"I{idx}"].fill = blue_fill
405
+ wb.save(output_path)
406
+ return output_path
407
+
408
+ def process_excel(file, insulin_file=None):
409
+ input_path = file.name
410
+ try:
411
+ df = pd.read_excel(input_path)
412
+ wb = openpyxl.load_workbook(input_path)
413
+ except Exception as e:
414
+ return None, None, None, None, None, None, f"❌ Lỗi khi đọc file Excel: {e}"
415
+
416
+ df = trim_column(df)
417
+ exceptions = load_exceptions()
418
+ allowed_exceptions = get_allowed_exceptions_tuples(DB_NL_FILE)
419
+ special_qty_tuples = get_special_qty_set_from_file(DB_TK_FILE)
420
+ special_format_tuples = get_special_format_set_from_file(DB_DINH_FILE)
421
+ special_unit_tuples = get_special_unit_set_from_file(DB_DVT_FILE)
422
+ lieu_exceptions = get_lieu_exceptions(DB_LIEU_FILE)
423
+ insulin_errors_cam, insulin_errors_xanh = highlight_insulin_errors(df, insulin_file) if insulin_file else ([], [])
424
+
425
+ dose_errors, format_errors = highlight_invalid_rows(df, exceptions, special_format_tuples, lieu_exceptions, insulin_errors_xanh, allowed_exceptions)
426
+ unit_errors = highlight_unit_errors(df, exceptions, special_unit_tuples, allowed_exceptions)
427
+ wrong_qty_errors = highlight_wrong_quantity_rows(df, special_qty_tuples, allowed_exceptions)
428
+
429
+ used = set(insulin_errors_cam)
430
+ wrong_qty_errors = [i for i in wrong_qty_errors if i not in used]
431
+ used |= set(wrong_qty_errors)
432
+ unit_errors = [i for i in unit_errors if i not in used]
433
+ used |= set(unit_errors)
434
+ format_errors = [i for i in format_errors if i not in used]
435
+ used |= set(format_errors)
436
+ dose_errors = [i for i in dose_errors if i not in used]
437
+
438
+ output_path = "ketqua_" + os.path.basename(input_path)
439
+ update_excel_with_errors(wb, insulin_errors_cam, dose_errors, format_errors, unit_errors, wrong_qty_errors, output_path)
440
+
441
+ format_path = "sai_dinhdang_" + os.path.basename(input_path)
442
+ dose_path = "sai_lieu_" + os.path.basename(input_path)
443
+ unit_path = "sai_donvitinh_" + os.path.basename(input_path)
444
+ qty_path = "sai_soluong_" + os.path.basename(input_path)
445
+ insulin_path = "insulin_soluong_" + os.path.basename(input_path)
446
+ df.iloc[format_errors].to_excel(format_path, index=False)
447
+ df.iloc[dose_errors].to_excel(dose_path, index=False)
448
+ df.iloc[unit_errors].to_excel(unit_path, index=False)
449
+ df.iloc[wrong_qty_errors].to_excel(qty_path, index=False)
450
+ if insulin_errors_cam:
451
+ df.iloc[insulin_errors_cam].to_excel(insulin_path, index=False)
452
+ else:
453
+ insulin_path = None
454
+
455
+ msg = []
456
+ if insulin_errors_cam:
457
+ msg.append(f"🟧 Có {len(insulin_errors_cam)} dòng Insulin sai số lượng chỉ định (ưu tiên màu cam).")
458
+ if insulin_errors_xanh:
459
+ msg.append(f"🔵 Có {len(insulin_errors_xanh)} dòng Insulin sai liều (tô xanh, không đúng tổng IU/lần/ngày).")
460
+ if wrong_qty_errors:
461
+ msg.append(f"🟡 Có {len(wrong_qty_errors)} dòng sai số lượng th��c kê.")
462
+ if dose_errors:
463
+ msg.append(f"🔵 Có {len(dose_errors)} dòng sai liều.")
464
+ if format_errors:
465
+ msg.append(f"🔴 Có {len(format_errors)} dòng sai định dạng.")
466
+ if unit_errors:
467
+ msg.append(f"🟣 Có {len(unit_errors)} dòng sai đơn vị tính.")
468
+ if not msg:
469
+ msg.append("✅ Không phát hiện lỗi nào.")
470
+ notice = "\n".join(msg)
471
+ return (
472
+ output_path,
473
+ format_path,
474
+ dose_path,
475
+ unit_path,
476
+ qty_path,
477
+ insulin_path,
478
+ notice
479
+ )
480
+
481
+ with gr.Blocks(title="Kiểm tra định dạng & liều dùng") as demo:
482
+ gr.Markdown("# 💊 Kiểm tra đơn thuốc ngoại trú")
483
+ gr.HTML("<span style='color:#0077b6;font-weight:bold'>Được tạo bởi DSCKI. Nguyễn Thành Trung - Bệnh viện Đa khoa khu vực Bảo Thắng.</span>")
484
+ with gr.Row():
485
+ with gr.Column():
486
+ file_input = gr.File(label="Tải lên file dinhdang 130")
487
+ insulin_input = gr.File(label="Tải lên file Insulin")
488
+ check_btn = gr.Button("✅ Kiểm tra",variant="primary")
489
+ with gr.Column():
490
+ notification = gr.Textbox(label="Thông báo", interactive=False,lines=4)
491
+ file_output = gr.File(label="File tổng kiểm tra (tô màu)")
492
+ file_format = gr.File(label="File lỗi định dạng")
493
+ file_dose = gr.File(label="File lỗi liều")
494
+ file_unit = gr.File(label="File lỗi đơn vị tính")
495
+ file_qty = gr.File(label="File lỗi số lượng thực kê")
496
+ file_insulin = gr.File(label="File lỗi Insulin (cam)")
497
+
498
+ check_btn.click(
499
+ fn=process_excel,
500
+ inputs=[file_input, insulin_input],
501
+ outputs=[file_output, file_format, file_dose, file_unit, file_qty, file_insulin, notification]
502
+ )
503
+
504
+ with gr.Tab("⚙️ Chỉnh sửa ngoại lệ"):
505
+ db_dinh_file = gr.File(label="DB_DINH")
506
+ db_dvt_file = gr.File(label="DB_DVT")
507
+ db_lieu_file = gr.File(label="DB_LIEU")
508
+ db_nl_file = gr.File(label="DB_NL")
509
+ db_tk_file = gr.File(label="DB_TK")
510
+ save_btn = gr.Button("💾 Cập nhật danh sách",variant="primary")
511
+ result_text = gr.Textbox(label="Trạng thái lưu", interactive=False)
512
+ save_btn.click(
513
+ fn=save_exceptions_from_files,
514
+ inputs=[db_tk_file, db_dinh_file, db_dvt_file, db_lieu_file, db_nl_file],
515
+ outputs=result_text
516
+ )
517
+
518
+ if __name__ == "__main__":
519
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio
2
+ pandas
3
+ openpyxl