Update app.py
Browse files
app.py
CHANGED
|
@@ -61,6 +61,7 @@ def fetch_available_standards():
|
|
| 61 |
# ==========================================
|
| 62 |
# 2. UI Component Handlers
|
| 63 |
# ==========================================
|
|
|
|
| 64 |
def load_initial_standards():
|
| 65 |
return gr.Dropdown(choices=fetch_available_standards())
|
| 66 |
|
|
@@ -70,13 +71,17 @@ def update_version_dropdown(standard):
|
|
| 70 |
versions = sorted(set(d["version"] for d in db_registry if d["standard"] == standard))
|
| 71 |
return gr.Dropdown(choices=versions)
|
| 72 |
|
|
|
|
| 73 |
def update_base_category_dropdown(standard, version):
|
| 74 |
if not standard or not version:
|
| 75 |
-
return gr.update(choices=[], value=None, interactive=False)
|
| 76 |
|
| 77 |
choices = ["ALL"]
|
|
|
|
|
|
|
| 78 |
db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
|
| 79 |
-
if not os.path.exists(db_path):
|
|
|
|
| 80 |
|
| 81 |
try:
|
| 82 |
conn = sqlite3.connect(db_path)
|
|
@@ -88,16 +93,17 @@ def update_base_category_dropdown(standard, version):
|
|
| 88 |
if main_table not in valid_tables:
|
| 89 |
main_table = valid_tables[0] if valid_tables else None
|
| 90 |
|
| 91 |
-
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 92 |
-
for t in valid_tables:
|
| 93 |
-
if t == main_table: continue
|
| 94 |
-
short_name = pattern.sub("", t).strip(" _")
|
| 95 |
-
if short_name and short_name not in choices:
|
| 96 |
-
choices.append(short_name)
|
| 97 |
-
elif t not in choices:
|
| 98 |
-
choices.append(t)
|
| 99 |
-
|
| 100 |
if main_table:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
cols = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
|
| 102 |
lower_cols = [c.lower() for c in cols]
|
| 103 |
if 'chapter' in lower_cols and 'category' in lower_cols:
|
|
@@ -110,12 +116,22 @@ def update_base_category_dropdown(standard, version):
|
|
| 110 |
ca = str(row[ca_col]).strip()
|
| 111 |
if ch and ca and ch.lower() not in ['none', 'nan'] and ca.lower() not in ['none', 'nan']:
|
| 112 |
choices.append(f"{ch}.{ca}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
conn.close()
|
| 114 |
except Exception:
|
| 115 |
import traceback
|
| 116 |
traceback.print_exc()
|
| 117 |
|
| 118 |
-
return gr.update(choices=choices, value=None, interactive=True)
|
| 119 |
|
| 120 |
def update_comp_standard_dropdown(base_std, base_ver):
|
| 121 |
if not base_std or not base_ver:
|
|
@@ -147,13 +163,35 @@ def update_comp_version_dropdown(base_std, base_ver, comp_std):
|
|
| 147 |
except Exception:
|
| 148 |
return gr.update(choices=[], value=None, interactive=False)
|
| 149 |
|
|
|
|
| 150 |
def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_ver):
|
| 151 |
if not all([base_std, base_ver, base_cat, comp_std, comp_ver]):
|
| 152 |
-
return gr.update(choices=[], value=None, interactive=False)
|
| 153 |
|
| 154 |
base_type = "Main" if not base_cat or base_cat == "ALL" or "." in base_cat else base_cat
|
|
|
|
| 155 |
|
| 156 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 158 |
query = """
|
| 159 |
SELECT DISTINCT TRIM(Comp_Type) AS Comp_Type
|
|
@@ -169,22 +207,14 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
|
|
| 169 |
|
| 170 |
allowed_types = df['Comp_Type'].dropna().tolist()
|
| 171 |
if not allowed_types:
|
| 172 |
-
return gr.update(choices=[], value=None)
|
| 173 |
|
| 174 |
final_choices = []
|
| 175 |
if "Main" in allowed_types:
|
| 176 |
final_choices.append("ALL")
|
| 177 |
-
comp_db_path = os.path.join(UPLOAD_DIR, f"{comp_std}_{comp_ver}.db")
|
| 178 |
-
|
| 179 |
if os.path.exists(comp_db_path):
|
|
|
|
| 180 |
c_conn = sqlite3.connect(comp_db_path)
|
| 181 |
-
c_tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", c_conn)['name'].tolist()
|
| 182 |
-
c_valid_tables = [t for t in c_tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
|
| 183 |
-
|
| 184 |
-
c_main_table = f"{comp_std}_{comp_ver}"
|
| 185 |
-
if c_main_table not in c_valid_tables:
|
| 186 |
-
c_main_table = c_valid_tables[0] if c_valid_tables else None
|
| 187 |
-
|
| 188 |
if c_main_table:
|
| 189 |
cols = pd.read_sql(f"PRAGMA table_info([{c_main_table}])", c_conn)['name'].tolist()
|
| 190 |
lower_cols = [c.lower() for c in cols]
|
|
@@ -203,18 +233,20 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
|
|
| 203 |
for t in allowed_types:
|
| 204 |
if t != "Main": final_choices.append(t)
|
| 205 |
|
| 206 |
-
return gr.update(choices=final_choices, value=final_choices[0] if final_choices else None, interactive=True)
|
| 207 |
|
| 208 |
except Exception as e:
|
| 209 |
import traceback
|
| 210 |
traceback.print_exc()
|
| 211 |
-
return gr.update(choices=[], value=None)
|
| 212 |
|
| 213 |
def reset_base_selections():
|
| 214 |
-
|
|
|
|
| 215 |
|
| 216 |
def reset_comp_selections():
|
| 217 |
-
|
|
|
|
| 218 |
|
| 219 |
|
| 220 |
# ==========================================
|
|
@@ -536,9 +568,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 536 |
return pd.DataFrame({"Error": [f"시스템 오류 발생: {str(e)}"]})
|
| 537 |
|
| 538 |
|
| 539 |
-
# ==========================================
|
| 540 |
-
# 4. UI Layout & Event Binding
|
| 541 |
-
# ==========================================
|
| 542 |
with gr.Blocks() as demo:
|
| 543 |
gr.Markdown("# 📜 Regulation Viewer")
|
| 544 |
|
|
@@ -547,6 +576,8 @@ with gr.Blocks() as demo:
|
|
| 547 |
with gr.Column():
|
| 548 |
base_standard = gr.Dropdown(label="Standard")
|
| 549 |
base_version = gr.Dropdown(label="Version")
|
|
|
|
|
|
|
| 550 |
|
| 551 |
with gr.Row(elem_classes="reset-row"):
|
| 552 |
base_category = gr.Dropdown(label="Category", scale=4)
|
|
@@ -556,12 +587,13 @@ with gr.Blocks() as demo:
|
|
| 556 |
with gr.Column():
|
| 557 |
comp_standard = gr.Dropdown(label="Standard", choices=[])
|
| 558 |
comp_version = gr.Dropdown(label="Version", choices=[])
|
|
|
|
|
|
|
| 559 |
|
| 560 |
with gr.Row(elem_classes="reset-row"):
|
| 561 |
comp_category = gr.Dropdown(label="Category", choices=[], scale=4)
|
| 562 |
comp_reset_btn = gr.Button("↺ 초기화", scale=1)
|
| 563 |
|
| 564 |
-
# 누락되었던 조회 버튼과 필터 체크박스 복구
|
| 565 |
with gr.Row(elem_id="search_row"):
|
| 566 |
search_btn = gr.Button("🔍 조회", variant="primary", scale=10)
|
| 567 |
diff_filter_cb = gr.Checkbox(label="💡 변경된 내용만 보기", value=False, elem_id="diff_cb_item", container=False, scale=1)
|
|
@@ -570,15 +602,18 @@ with gr.Blocks() as demo:
|
|
| 570 |
|
| 571 |
demo.load(fn=load_initial_standards, inputs=None, outputs=base_standard)
|
| 572 |
|
|
|
|
| 573 |
base_standard.change(fn=update_version_dropdown, inputs=[base_standard], outputs=[base_version])
|
| 574 |
-
base_version.change(fn=update_base_category_dropdown, inputs=[base_standard, base_version], outputs=[base_category])
|
| 575 |
|
| 576 |
base_version.change(fn=update_comp_standard_dropdown, inputs=[base_standard, base_version], outputs=[comp_standard])
|
| 577 |
comp_standard.change(fn=update_comp_version_dropdown, inputs=[base_standard, base_version, comp_standard], outputs=[comp_version])
|
| 578 |
|
| 579 |
comp_change_triggers = [base_standard, base_version, base_category, comp_standard, comp_version]
|
| 580 |
-
|
| 581 |
-
|
|
|
|
|
|
|
| 582 |
|
| 583 |
search_btn.click(
|
| 584 |
fn=execute_unified_search,
|
|
@@ -592,8 +627,9 @@ with gr.Blocks() as demo:
|
|
| 592 |
outputs=[output_df]
|
| 593 |
)
|
| 594 |
|
| 595 |
-
|
| 596 |
-
|
|
|
|
| 597 |
|
| 598 |
|
| 599 |
# ==========================================
|
|
|
|
| 61 |
# ==========================================
|
| 62 |
# 2. UI Component Handlers
|
| 63 |
# ==========================================
|
| 64 |
+
|
| 65 |
def load_initial_standards():
|
| 66 |
return gr.Dropdown(choices=fetch_available_standards())
|
| 67 |
|
|
|
|
| 71 |
versions = sorted(set(d["version"] for d in db_registry if d["standard"] == standard))
|
| 72 |
return gr.Dropdown(choices=versions)
|
| 73 |
|
| 74 |
+
# ⬇️ [수정] Version이 선택되면 Category와 함께 Status 값도 같이 리턴합니다.
|
| 75 |
def update_base_category_dropdown(standard, version):
|
| 76 |
if not standard or not version:
|
| 77 |
+
return gr.update(choices=[], value=None, interactive=False), gr.update(value="")
|
| 78 |
|
| 79 |
choices = ["ALL"]
|
| 80 |
+
status_value = "" # Status 기본값
|
| 81 |
+
|
| 82 |
db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
|
| 83 |
+
if not os.path.exists(db_path):
|
| 84 |
+
return gr.update(choices=choices), gr.update(value="")
|
| 85 |
|
| 86 |
try:
|
| 87 |
conn = sqlite3.connect(db_path)
|
|
|
|
| 93 |
if main_table not in valid_tables:
|
| 94 |
main_table = valid_tables[0] if valid_tables else None
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
if main_table:
|
| 97 |
+
# 💡 [핵심 추가] DB에서 Status 값을 읽어옵니다.
|
| 98 |
+
try:
|
| 99 |
+
cols_check = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
|
| 100 |
+
if "Status" in cols_check or "status" in cols_check:
|
| 101 |
+
status_df = pd.read_sql(f"SELECT Status FROM [{main_table}] WHERE Status IS NOT NULL AND Status != '' LIMIT 1", conn)
|
| 102 |
+
if not status_df.empty:
|
| 103 |
+
status_value = str(status_df.iloc[0]['Status'])
|
| 104 |
+
except Exception:
|
| 105 |
+
pass
|
| 106 |
+
|
| 107 |
cols = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
|
| 108 |
lower_cols = [c.lower() for c in cols]
|
| 109 |
if 'chapter' in lower_cols and 'category' in lower_cols:
|
|
|
|
| 116 |
ca = str(row[ca_col]).strip()
|
| 117 |
if ch and ca and ch.lower() not in ['none', 'nan'] and ca.lower() not in ['none', 'nan']:
|
| 118 |
choices.append(f"{ch}.{ca}")
|
| 119 |
+
|
| 120 |
+
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 121 |
+
for t in valid_tables:
|
| 122 |
+
if t == main_table: continue
|
| 123 |
+
short_name = pattern.sub("", t).strip(" _")
|
| 124 |
+
if short_name and short_name not in choices:
|
| 125 |
+
choices.append(short_name)
|
| 126 |
+
elif t not in choices:
|
| 127 |
+
choices.append(t)
|
| 128 |
+
|
| 129 |
conn.close()
|
| 130 |
except Exception:
|
| 131 |
import traceback
|
| 132 |
traceback.print_exc()
|
| 133 |
|
| 134 |
+
return gr.update(choices=choices, value=None, interactive=True), gr.update(value=status_value)
|
| 135 |
|
| 136 |
def update_comp_standard_dropdown(base_std, base_ver):
|
| 137 |
if not base_std or not base_ver:
|
|
|
|
| 163 |
except Exception:
|
| 164 |
return gr.update(choices=[], value=None, interactive=False)
|
| 165 |
|
| 166 |
+
# ⬇️ [수정] 비교 법규에서도 Category 갱신 시 Status를 읽어옵니다.
|
| 167 |
def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_ver):
|
| 168 |
if not all([base_std, base_ver, base_cat, comp_std, comp_ver]):
|
| 169 |
+
return gr.update(choices=[], value=None, interactive=False), gr.update(value="")
|
| 170 |
|
| 171 |
base_type = "Main" if not base_cat or base_cat == "ALL" or "." in base_cat else base_cat
|
| 172 |
+
status_value = ""
|
| 173 |
|
| 174 |
try:
|
| 175 |
+
# Status 읽기
|
| 176 |
+
comp_db_path = os.path.join(UPLOAD_DIR, f"{comp_std}_{comp_ver}.db")
|
| 177 |
+
if os.path.exists(comp_db_path):
|
| 178 |
+
c_conn = sqlite3.connect(comp_db_path)
|
| 179 |
+
c_tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", c_conn)['name'].tolist()
|
| 180 |
+
c_valid_tables = [t for t in c_tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
|
| 181 |
+
c_main_table = f"{comp_std}_{comp_ver}"
|
| 182 |
+
if c_main_table not in c_valid_tables:
|
| 183 |
+
c_main_table = c_valid_tables[0] if c_valid_tables else None
|
| 184 |
+
|
| 185 |
+
if c_main_table:
|
| 186 |
+
try:
|
| 187 |
+
cols_check = pd.read_sql(f"PRAGMA table_info([{c_main_table}])", c_conn)['name'].tolist()
|
| 188 |
+
if "Status" in cols_check or "status" in cols_check:
|
| 189 |
+
status_df = pd.read_sql(f"SELECT Status FROM [{c_main_table}] WHERE Status IS NOT NULL AND Status != '' LIMIT 1", c_conn)
|
| 190 |
+
if not status_df.empty:
|
| 191 |
+
status_value = str(status_df.iloc[0]['Status'])
|
| 192 |
+
except Exception:
|
| 193 |
+
pass
|
| 194 |
+
|
| 195 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 196 |
query = """
|
| 197 |
SELECT DISTINCT TRIM(Comp_Type) AS Comp_Type
|
|
|
|
| 207 |
|
| 208 |
allowed_types = df['Comp_Type'].dropna().tolist()
|
| 209 |
if not allowed_types:
|
| 210 |
+
return gr.update(choices=[], value=None), gr.update(value=status_value)
|
| 211 |
|
| 212 |
final_choices = []
|
| 213 |
if "Main" in allowed_types:
|
| 214 |
final_choices.append("ALL")
|
|
|
|
|
|
|
| 215 |
if os.path.exists(comp_db_path):
|
| 216 |
+
# c_conn is already closed above, reopen if needed
|
| 217 |
c_conn = sqlite3.connect(comp_db_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
if c_main_table:
|
| 219 |
cols = pd.read_sql(f"PRAGMA table_info([{c_main_table}])", c_conn)['name'].tolist()
|
| 220 |
lower_cols = [c.lower() for c in cols]
|
|
|
|
| 233 |
for t in allowed_types:
|
| 234 |
if t != "Main": final_choices.append(t)
|
| 235 |
|
| 236 |
+
return gr.update(choices=final_choices, value=final_choices[0] if final_choices else None, interactive=True), gr.update(value=status_value)
|
| 237 |
|
| 238 |
except Exception as e:
|
| 239 |
import traceback
|
| 240 |
traceback.print_exc()
|
| 241 |
+
return gr.update(choices=[], value=None), gr.update(value="")
|
| 242 |
|
| 243 |
def reset_base_selections():
|
| 244 |
+
# Status까지 4개를 리셋
|
| 245 |
+
return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
|
| 246 |
|
| 247 |
def reset_comp_selections():
|
| 248 |
+
# Status까지 4개를 리셋
|
| 249 |
+
return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
|
| 250 |
|
| 251 |
|
| 252 |
# ==========================================
|
|
|
|
| 568 |
return pd.DataFrame({"Error": [f"시스템 오류 발생: {str(e)}"]})
|
| 569 |
|
| 570 |
|
|
|
|
|
|
|
|
|
|
| 571 |
with gr.Blocks() as demo:
|
| 572 |
gr.Markdown("# 📜 Regulation Viewer")
|
| 573 |
|
|
|
|
| 576 |
with gr.Column():
|
| 577 |
base_standard = gr.Dropdown(label="Standard")
|
| 578 |
base_version = gr.Dropdown(label="Version")
|
| 579 |
+
# ⬇️ [UI 추가] Version과 Category 사이에 Status 칸 추가 (읽기 전용)
|
| 580 |
+
base_status = gr.Textbox(label="Status", interactive=False, lines=1)
|
| 581 |
|
| 582 |
with gr.Row(elem_classes="reset-row"):
|
| 583 |
base_category = gr.Dropdown(label="Category", scale=4)
|
|
|
|
| 587 |
with gr.Column():
|
| 588 |
comp_standard = gr.Dropdown(label="Standard", choices=[])
|
| 589 |
comp_version = gr.Dropdown(label="Version", choices=[])
|
| 590 |
+
# ⬇️ [UI 추가] Version과 Category 사이에 Status 칸 추가 (읽기 전용)
|
| 591 |
+
comp_status = gr.Textbox(label="Status", interactive=False, lines=1)
|
| 592 |
|
| 593 |
with gr.Row(elem_classes="reset-row"):
|
| 594 |
comp_category = gr.Dropdown(label="Category", choices=[], scale=4)
|
| 595 |
comp_reset_btn = gr.Button("↺ 초기화", scale=1)
|
| 596 |
|
|
|
|
| 597 |
with gr.Row(elem_id="search_row"):
|
| 598 |
search_btn = gr.Button("🔍 조회", variant="primary", scale=10)
|
| 599 |
diff_filter_cb = gr.Checkbox(label="💡 변경된 내용만 보기", value=False, elem_id="diff_cb_item", container=False, scale=1)
|
|
|
|
| 602 |
|
| 603 |
demo.load(fn=load_initial_standards, inputs=None, outputs=base_standard)
|
| 604 |
|
| 605 |
+
# ⬇️ [이벤트 수정] Version이 변경될 때 base_status에도 값을 보냅니다.
|
| 606 |
base_standard.change(fn=update_version_dropdown, inputs=[base_standard], outputs=[base_version])
|
| 607 |
+
base_version.change(fn=update_base_category_dropdown, inputs=[base_standard, base_version], outputs=[base_category, base_status])
|
| 608 |
|
| 609 |
base_version.change(fn=update_comp_standard_dropdown, inputs=[base_standard, base_version], outputs=[comp_standard])
|
| 610 |
comp_standard.change(fn=update_comp_version_dropdown, inputs=[base_standard, base_version, comp_standard], outputs=[comp_version])
|
| 611 |
|
| 612 |
comp_change_triggers = [base_standard, base_version, base_category, comp_standard, comp_version]
|
| 613 |
+
|
| 614 |
+
# ⬇️ [이벤트 수정] 비교 법규쪽 갱신 시에도 comp_status에 값을 보냅니다.
|
| 615 |
+
base_category.change(fn=update_comp_category_dropdown, inputs=comp_change_triggers, outputs=[comp_category, comp_status])
|
| 616 |
+
comp_version.change(fn=update_comp_category_dropdown, inputs=comp_change_triggers, outputs=[comp_category, comp_status])
|
| 617 |
|
| 618 |
search_btn.click(
|
| 619 |
fn=execute_unified_search,
|
|
|
|
| 627 |
outputs=[output_df]
|
| 628 |
)
|
| 629 |
|
| 630 |
+
# ⬇️ [이벤트 수정] 초기화 버튼 누를 때 status도 리셋되도록 4개의 outputs 지정
|
| 631 |
+
base_reset_btn.click(fn=reset_base_selections, inputs=None, outputs=[base_standard, base_version, base_category, base_status])
|
| 632 |
+
comp_reset_btn.click(fn=reset_comp_selections, inputs=None, outputs=[comp_standard, comp_version, comp_category, comp_status])
|
| 633 |
|
| 634 |
|
| 635 |
# ==========================================
|