Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -52,9 +52,12 @@ def parse_dosage_to_numeric(dose_str):
|
|
| 52 |
def update_component_choices(search_text):
|
| 53 |
if not ALL_COMPONENTS:
|
| 54 |
return gr.update(choices=[])
|
| 55 |
-
if not search_text or search_text.strip() == "":
|
| 56 |
-
return gr.update(choices=ALL_COMPONENTS)
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
search_text = search_text.strip().lower()
|
| 59 |
filtered = [c for c in ALL_COMPONENTS if search_text in c.lower()]
|
| 60 |
return gr.update(choices=filtered)
|
|
@@ -65,15 +68,15 @@ def generate_standard_excel(selected_components):
|
|
| 65 |
return None, "❌ 系統未成功載入 data.csv 資料庫,請檢查 Space 中的檔案是否存在。"
|
| 66 |
if not selected_components:
|
| 67 |
return None, "❌ 請至少勾選一個成分品項!"
|
| 68 |
-
|
| 69 |
# 篩選選定的成分資料
|
| 70 |
df_filtered = df_global[df_global['成分'].isin(selected_components)].copy()
|
| 71 |
if df_filtered.empty:
|
| 72 |
return None, "❌ 找不到相關資料!"
|
| 73 |
-
|
| 74 |
# 自動判斷數量欄位名稱
|
| 75 |
qty_col = '數量(顆)' if '數量(顆)' in df_filtered.columns else [col for col in df_filtered.columns if '數量' in col][0]
|
| 76 |
-
|
| 77 |
# 進行樞紐分析 (Pivot Table)
|
| 78 |
pivot_df = df_filtered.groupby(['成分', '劑型', '劑量', '廠商', '年度'])[qty_col].sum().unstack(fill_value=0)
|
| 79 |
|
|
@@ -83,17 +86,17 @@ def generate_standard_excel(selected_components):
|
|
| 83 |
pivot_df[year_col] = 0
|
| 84 |
|
| 85 |
pivot_df = pivot_df.reindex(columns=['2022年', '2023年', '2024年']).reset_index()
|
| 86 |
-
|
| 87 |
# 高階排序:劑量由小到大 -> 2024年數量由大到小
|
| 88 |
pivot_df['dose_numeric'] = pivot_df['劑量'].apply(parse_dosage_to_numeric)
|
| 89 |
pivot_df = pivot_df.sort_values(by=['dose_numeric', '2024年'], ascending=[True, False]).drop(columns=['dose_numeric'])
|
| 90 |
-
|
| 91 |
# 建立全新 Excel 工作簿
|
| 92 |
wb = Workbook()
|
| 93 |
ws = wb.active
|
| 94 |
ws.title = "廠商排名報表"
|
| 95 |
ws.views.sheetView[0].showGridLines = True
|
| 96 |
-
|
| 97 |
# 樣式與底色定義
|
| 98 |
font_family = "微軟正黑體"
|
| 99 |
header_fill = PatternFill(start_color="1F497D", end_color="1F497D", fill_type="solid") # 標題深藍
|
|
@@ -110,7 +113,7 @@ def generate_standard_excel(selected_components):
|
|
| 110 |
|
| 111 |
thin_side = Side(border_style="thin", color="D9D9D9")
|
| 112 |
cell_border = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side)
|
| 113 |
-
|
| 114 |
# 寫入標題列 (欄位依序排列,包含換行)
|
| 115 |
headers = ["成分", "劑型", "劑量", "廠商", "2022年\n數量", "2023年\n數量", "2024年\n數量", "2024年\n占比(%)"]
|
| 116 |
ws.append(headers)
|
|
@@ -121,7 +124,7 @@ def generate_standard_excel(selected_components):
|
|
| 121 |
cell.fill = header_fill
|
| 122 |
cell.alignment = center_align
|
| 123 |
cell.border = cell_border
|
| 124 |
-
|
| 125 |
unique_doses = sorted(pivot_df['劑量'].unique(), key=parse_dosage_to_numeric)
|
| 126 |
|
| 127 |
grand_total_2022 = 0
|
|
@@ -129,7 +132,6 @@ def generate_standard_excel(selected_components):
|
|
| 129 |
grand_total_2024 = 0
|
| 130 |
|
| 131 |
current_row = 2
|
| 132 |
-
|
| 133 |
# 按劑量拆分群組寫入
|
| 134 |
for dose in unique_doses:
|
| 135 |
dose_group = pivot_df[pivot_df['劑量'] == dose]
|
|
@@ -196,7 +198,7 @@ def generate_standard_excel(selected_components):
|
|
| 196 |
grand_total_2023 += dose_subtotal_2023
|
| 197 |
grand_total_2024 += dose_subtotal_2024
|
| 198 |
current_row += 1
|
| 199 |
-
|
| 200 |
# 寫入「總計」列
|
| 201 |
ws.append(["", "", "總計", "", grand_total_2022, grand_total_2023, grand_total_2024, 1.0])
|
| 202 |
for col_idx in range(1, 9):
|
|
@@ -212,7 +214,7 @@ def generate_standard_excel(selected_components):
|
|
| 212 |
elif col_idx == 8:
|
| 213 |
cell.alignment = right_align
|
| 214 |
cell.number_format = '0.0%'
|
| 215 |
-
|
| 216 |
# 自動調整欄寬
|
| 217 |
for col in ws.columns:
|
| 218 |
max_len = 0
|
|
@@ -225,7 +227,7 @@ def generate_standard_excel(selected_components):
|
|
| 225 |
if line_len > max_len:
|
| 226 |
max_len = line_len
|
| 227 |
ws.column_dimensions[col_letter].width = max(max_len + 4, 12)
|
| 228 |
-
|
| 229 |
# 專業列印與邊界設定 (公分轉英吋)
|
| 230 |
ws.sheet_properties.pageSetUpPr.fitToPage = True
|
| 231 |
ws.page_setup.fitToWidth = 1 # 欄位強迫縮放放入單頁
|
|
@@ -236,7 +238,7 @@ def generate_standard_excel(selected_components):
|
|
| 236 |
ws.page_margins.right = 1.5 / 2.54
|
| 237 |
ws.page_margins.header = 1.5 / 2.54
|
| 238 |
ws.page_margins.footer = 1.0 / 2.54
|
| 239 |
-
|
| 240 |
# 動態變數計算 (用於頁首)
|
| 241 |
comp_names = "、".join(df_filtered['成分'].unique())
|
| 242 |
form_mapping = {"注射劑": "Inj.", "一般錠劑膠囊劑": "Tab./Cap.", "膜衣錠": "F.C. Tab.", "膠囊劑": "Cap.", "錠劑": "Tab."}
|
|
@@ -252,7 +254,7 @@ def generate_standard_excel(selected_components):
|
|
| 252 |
|
| 253 |
ws.page_setup.scaleWithDoc = True
|
| 254 |
ws.page_setup.alignWithMargins = True
|
| 255 |
-
|
| 256 |
# 儲存 Excel
|
| 257 |
output_filename = "Standardized_Report.xlsx"
|
| 258 |
wb.save(output_filename)
|
|
@@ -273,9 +275,11 @@ with gr.Blocks(title="健保申報量標準化 Excel 產出工具") as demo:
|
|
| 273 |
value=""
|
| 274 |
)
|
| 275 |
|
|
|
|
|
|
|
| 276 |
component_choices = gr.CheckboxGroup(
|
| 277 |
label="📋 第二步:勾選欲納入報表的成分品項 (可多選)",
|
| 278 |
-
choices=
|
| 279 |
value=[]
|
| 280 |
)
|
| 281 |
|
|
|
|
| 52 |
def update_component_choices(search_text):
|
| 53 |
if not ALL_COMPONENTS:
|
| 54 |
return gr.update(choices=[])
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
# 修正:如果使用者沒有輸入任何關鍵字,保持空的選項不顯示,避免畫面太混亂
|
| 57 |
+
if not search_text or search_text.strip() == "":
|
| 58 |
+
return gr.update(choices=[])
|
| 59 |
+
|
| 60 |
+
# 修正原本放在 return 後面的縮排錯誤
|
| 61 |
search_text = search_text.strip().lower()
|
| 62 |
filtered = [c for c in ALL_COMPONENTS if search_text in c.lower()]
|
| 63 |
return gr.update(choices=filtered)
|
|
|
|
| 68 |
return None, "❌ 系統未成功載入 data.csv 資料庫,請檢查 Space 中的檔案是否存在。"
|
| 69 |
if not selected_components:
|
| 70 |
return None, "❌ 請至少勾選一個成分品項!"
|
| 71 |
+
|
| 72 |
# 篩選選定的成分資料
|
| 73 |
df_filtered = df_global[df_global['成分'].isin(selected_components)].copy()
|
| 74 |
if df_filtered.empty:
|
| 75 |
return None, "❌ 找不到相關資料!"
|
| 76 |
+
|
| 77 |
# 自動判斷數量欄位名稱
|
| 78 |
qty_col = '數量(顆)' if '數量(顆)' in df_filtered.columns else [col for col in df_filtered.columns if '數量' in col][0]
|
| 79 |
+
|
| 80 |
# 進行樞紐分析 (Pivot Table)
|
| 81 |
pivot_df = df_filtered.groupby(['成分', '劑型', '劑量', '廠商', '年度'])[qty_col].sum().unstack(fill_value=0)
|
| 82 |
|
|
|
|
| 86 |
pivot_df[year_col] = 0
|
| 87 |
|
| 88 |
pivot_df = pivot_df.reindex(columns=['2022年', '2023年', '2024年']).reset_index()
|
| 89 |
+
|
| 90 |
# 高階排序:劑量由小到大 -> 2024年數量由大到小
|
| 91 |
pivot_df['dose_numeric'] = pivot_df['劑量'].apply(parse_dosage_to_numeric)
|
| 92 |
pivot_df = pivot_df.sort_values(by=['dose_numeric', '2024年'], ascending=[True, False]).drop(columns=['dose_numeric'])
|
| 93 |
+
|
| 94 |
# 建立全新 Excel 工作簿
|
| 95 |
wb = Workbook()
|
| 96 |
ws = wb.active
|
| 97 |
ws.title = "廠商排名報表"
|
| 98 |
ws.views.sheetView[0].showGridLines = True
|
| 99 |
+
|
| 100 |
# 樣式與底色定義
|
| 101 |
font_family = "微軟正黑體"
|
| 102 |
header_fill = PatternFill(start_color="1F497D", end_color="1F497D", fill_type="solid") # 標題深藍
|
|
|
|
| 113 |
|
| 114 |
thin_side = Side(border_style="thin", color="D9D9D9")
|
| 115 |
cell_border = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side)
|
| 116 |
+
|
| 117 |
# 寫入標題列 (欄位依序排列,包含換行)
|
| 118 |
headers = ["成分", "劑型", "劑量", "廠商", "2022年\n數量", "2023年\n數量", "2024年\n數量", "2024年\n占比(%)"]
|
| 119 |
ws.append(headers)
|
|
|
|
| 124 |
cell.fill = header_fill
|
| 125 |
cell.alignment = center_align
|
| 126 |
cell.border = cell_border
|
| 127 |
+
|
| 128 |
unique_doses = sorted(pivot_df['劑量'].unique(), key=parse_dosage_to_numeric)
|
| 129 |
|
| 130 |
grand_total_2022 = 0
|
|
|
|
| 132 |
grand_total_2024 = 0
|
| 133 |
|
| 134 |
current_row = 2
|
|
|
|
| 135 |
# 按劑量拆分群組寫入
|
| 136 |
for dose in unique_doses:
|
| 137 |
dose_group = pivot_df[pivot_df['劑量'] == dose]
|
|
|
|
| 198 |
grand_total_2023 += dose_subtotal_2023
|
| 199 |
grand_total_2024 += dose_subtotal_2024
|
| 200 |
current_row += 1
|
| 201 |
+
|
| 202 |
# 寫入「總計」列
|
| 203 |
ws.append(["", "", "總計", "", grand_total_2022, grand_total_2023, grand_total_2024, 1.0])
|
| 204 |
for col_idx in range(1, 9):
|
|
|
|
| 214 |
elif col_idx == 8:
|
| 215 |
cell.alignment = right_align
|
| 216 |
cell.number_format = '0.0%'
|
| 217 |
+
|
| 218 |
# 自動調整欄寬
|
| 219 |
for col in ws.columns:
|
| 220 |
max_len = 0
|
|
|
|
| 227 |
if line_len > max_len:
|
| 228 |
max_len = line_len
|
| 229 |
ws.column_dimensions[col_letter].width = max(max_len + 4, 12)
|
| 230 |
+
|
| 231 |
# 專業列印與邊界設定 (公分轉英吋)
|
| 232 |
ws.sheet_properties.pageSetUpPr.fitToPage = True
|
| 233 |
ws.page_setup.fitToWidth = 1 # 欄位強迫縮放放入單頁
|
|
|
|
| 238 |
ws.page_margins.right = 1.5 / 2.54
|
| 239 |
ws.page_margins.header = 1.5 / 2.54
|
| 240 |
ws.page_margins.footer = 1.0 / 2.54
|
| 241 |
+
|
| 242 |
# 動態變數計算 (用於頁首)
|
| 243 |
comp_names = "、".join(df_filtered['成分'].unique())
|
| 244 |
form_mapping = {"注射劑": "Inj.", "一般錠劑膠囊劑": "Tab./Cap.", "膜衣錠": "F.C. Tab.", "膠囊劑": "Cap.", "錠劑": "Tab."}
|
|
|
|
| 254 |
|
| 255 |
ws.page_setup.scaleWithDoc = True
|
| 256 |
ws.page_setup.alignWithMargins = True
|
| 257 |
+
|
| 258 |
# 儲存 Excel
|
| 259 |
output_filename = "Standardized_Report.xlsx"
|
| 260 |
wb.save(output_filename)
|
|
|
|
| 275 |
value=""
|
| 276 |
)
|
| 277 |
|
| 278 |
+
# 【主要修改處】:將原本的 choices=ALL_COMPONENTS 改為 choices=[]
|
| 279 |
+
# 這樣一打開網頁時,第二步就會是乾淨的,只有輸入關鍵字後才會顯示對應成分。
|
| 280 |
component_choices = gr.CheckboxGroup(
|
| 281 |
label="📋 第二步:勾選欲納入報表的成分品項 (可多選)",
|
| 282 |
+
choices=[],
|
| 283 |
value=[]
|
| 284 |
)
|
| 285 |
|