QIDNLF commited on
Commit
79fb2e8
ยท
verified ยท
1 Parent(s): f602d9f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +12 -48
app.py CHANGED
@@ -5,7 +5,6 @@ import os
5
  import re
6
  import base64
7
  import difflib
8
- import traceback
9
 
10
  # ==========================================
11
  # 1. Environment Setup & Data Helpers
@@ -57,15 +56,12 @@ def fetch_available_standards():
57
 
58
  return sorted(list(set([d["standard"] for d in db_registry])))
59
 
60
- # ==========================================
61
- # ๐Ÿ’ก [๋ณต๊ตฌ๋œ ํ•ต์‹ฌ ํ•จ์ˆ˜] ์ด ๋ถ€๋ถ„์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”!
62
- # ==========================================
63
  def fetch_database_records(std, ver, cat, table_type):
64
  try:
65
  db_path = os.path.join(UPLOAD_DIR, f"{std}_{ver}.db")
66
  conn = sqlite3.connect(db_path)
 
67
 
68
- # Table_Config ์ฝ์–ด์˜ค๊ธฐ
69
  conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
70
  config_df = pd.read_sql("SELECT * FROM Table_Config WHERE TRIM(Table_Type)=?", conn_map, params=[table_type.strip()])
71
  conn_map.close()
@@ -76,7 +72,6 @@ def fetch_database_records(std, ver, cat, table_type):
76
  anchors = [x.strip() for x in config_df.iloc[0]['Anchor_Column'].split(',')]
77
  displays = [x.strip() for x in config_df.iloc[0]['Display_Columns'].split(',')] if pd.notna(config_df.iloc[0]['Display_Columns']) else anchors
78
 
79
- # ๋ฉ”์ธ ํ…Œ์ด๋ธ” ์ฐพ๊ธฐ
80
  tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
81
  valid_tables = [t for t in tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
82
  main_table = f"{std}_{ver}"
@@ -88,7 +83,6 @@ def fetch_database_records(std, ver, cat, table_type):
88
 
89
  query = f"SELECT * FROM [{main_table}]"
90
 
91
- # ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ๋ง
92
  cols = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
93
  lower_cols = [c.lower() for c in cols]
94
 
@@ -109,7 +103,6 @@ def fetch_database_records(std, ver, cat, table_type):
109
  df = pd.read_sql(query, conn)
110
  conn.close()
111
 
112
- # ํ™”๋ฉด์— ๋ณด์—ฌ์ค„ ์—ด ์ •๋ฆฌ
113
  final_cols = []
114
  for d in displays:
115
  for c in df.columns:
@@ -122,6 +115,9 @@ def fetch_database_records(std, ver, cat, table_type):
122
  if not final_cols:
123
  return df, real_anchors
124
 
 
 
 
125
  return df[final_cols], real_anchors
126
 
127
  except Exception as e:
@@ -133,9 +129,6 @@ def generate_html_diff(text1, text2):
133
  try:
134
  s1, s2 = str(text1), str(text2)
135
 
136
- # ========================================================
137
- # ๐Ÿ›ก๏ธ [์•ˆ์ „ ์žฅ์น˜ 1] ๊ธ€์ž๊ฐ€ ๋„ˆ๋ฌด ๊ธธ๋ฉด (์˜ˆ: 1000์ž ์ด์ƒ) ์ปดํ“จํ„ฐ ํ„ฐ์ง ๋ฐฉ์ง€
138
- # ========================================================
139
  if len(s1) > 1000 or len(s2) > 1000:
140
  return s1, s2
141
 
@@ -143,17 +136,13 @@ def generate_html_diff(text1, text2):
143
  if not words1 or not words2:
144
  return s1, s2
145
 
146
- # ========================================================
147
- # ๐Ÿ›ก๏ธ [์•ˆ์ „ ์žฅ์น˜ 2] ํ•œ๊ธ€ vs ์˜์–ด์ฒ˜๋Ÿผ ๊ฒน์น˜๋Š” ๋‹จ์–ด๊ฐ€ 5%๋„ ์•ˆ ๋˜๋ฉด ๋น„๊ต ์ƒ๋žต
148
- # ========================================================
149
  common_words = set(words1) & set(words2)
150
  if len(common_words) / min(len(words1), len(words2)) < 0.05:
151
  return s1, s2
152
 
153
- # ์•ˆ์ „ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•œ ์ง„์งœ ๋น„์Šทํ•œ ํ…์ŠคํŠธ๋“ค๋งŒ ์ดˆ๊ณ ์† ๋น„๊ต!
154
  matcher = difflib.SequenceMatcher(None, words1, words2)
155
-
156
  res1, res2 = [], []
 
157
  for tag, i1, i2, j1, j2 in matcher.get_opcodes():
158
  if tag == 'replace':
159
  res1.append(f"<span style='color:#ff4d4f; font-weight:bold;'>{' '.join(words1[i1:i2])}</span>")
@@ -169,10 +158,10 @@ def generate_html_diff(text1, text2):
169
  return " ".join(res1), " ".join(res2)
170
  except Exception:
171
  return text1, text2
 
172
  # ==========================================
173
  # 2. UI Component Handlers
174
  # ==========================================
175
-
176
  def load_initial_standards():
177
  return gr.Dropdown(choices=fetch_available_standards())
178
 
@@ -182,13 +171,12 @@ def update_version_dropdown(standard):
182
  versions = sorted(set(d["version"] for d in db_registry if d["standard"] == standard))
183
  return gr.Dropdown(choices=versions)
184
 
185
- # โฌ‡๏ธ [์ˆ˜์ •] Version์ด ์„ ํƒ๋˜๋ฉด Category์™€ ํ•จ๊ป˜ Status ๊ฐ’๋„ ๊ฐ™์ด ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.
186
  def update_base_category_dropdown(standard, version):
187
  if not standard or not version:
188
  return gr.update(choices=[], value=None, interactive=False), gr.update(value="")
189
 
190
  choices = ["ALL"]
191
- status_value = "" # Status ๊ธฐ๋ณธ๊ฐ’
192
 
193
  db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
194
  if not os.path.exists(db_path):
@@ -197,15 +185,12 @@ def update_base_category_dropdown(standard, version):
197
  try:
198
  conn = sqlite3.connect(db_path)
199
  tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
200
-
201
  valid_tables = [t for t in tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
202
-
203
  main_table = f"{standard}_{version}"
204
  if main_table not in valid_tables:
205
  main_table = valid_tables[0] if valid_tables else None
206
 
207
  if main_table:
208
- # ๐Ÿ’ก [ํ•ต์‹ฌ ์ถ”๊ฐ€] DB์—์„œ Status ๊ฐ’์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.
209
  try:
210
  cols_check = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
211
  if "Status" in cols_check or "status" in cols_check:
@@ -239,8 +224,7 @@ def update_base_category_dropdown(standard, version):
239
 
240
  conn.close()
241
  except Exception:
242
- import traceback
243
- traceback.print_exc()
244
 
245
  return gr.update(choices=choices, value=None, interactive=True), gr.update(value=status_value)
246
 
@@ -274,7 +258,6 @@ def update_comp_version_dropdown(base_std, base_ver, comp_std):
274
  except Exception:
275
  return gr.update(choices=[], value=None, interactive=False)
276
 
277
- # โฌ‡๏ธ [์ˆ˜์ •] ๋น„๊ต ๋ฒ•๊ทœ์—์„œ๋„ Category ๊ฐฑ์‹  ์‹œ Status๋ฅผ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.
278
  def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_ver):
279
  if not all([base_std, base_ver, base_cat, comp_std, comp_ver]):
280
  return gr.update(choices=[], value=None, interactive=False), gr.update(value="")
@@ -283,7 +266,6 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
283
  status_value = ""
284
 
285
  try:
286
- # Status ์ฝ๊ธฐ
287
  comp_db_path = os.path.join(UPLOAD_DIR, f"{comp_std}_{comp_ver}.db")
288
  if os.path.exists(comp_db_path):
289
  c_conn = sqlite3.connect(comp_db_path)
@@ -324,7 +306,6 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
324
  if "Main" in allowed_types:
325
  final_choices.append("ALL")
326
  if os.path.exists(comp_db_path):
327
- # c_conn is already closed above, reopen if needed
328
  c_conn = sqlite3.connect(comp_db_path)
329
  if c_main_table:
330
  cols = pd.read_sql(f"PRAGMA table_info([{c_main_table}])", c_conn)['name'].tolist()
@@ -346,20 +327,18 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
346
 
347
  return gr.update(choices=final_choices, value=final_choices[0] if final_choices else None, interactive=True), gr.update(value=status_value)
348
 
349
- except Exception as e:
350
- import traceback
351
- traceback.print_exc()
352
  return gr.update(choices=[], value=None), gr.update(value="")
353
 
354
  def reset_base_selections():
355
- # Status๊นŒ์ง€ 4๊ฐœ๋ฅผ ๋ฆฌ์…‹
356
  return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
357
 
358
  def reset_comp_selections():
359
- # Status๊นŒ์ง€ 4๊ฐœ๋ฅผ ๋ฆฌ์…‹
360
  return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
361
 
362
-
 
 
363
  def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat, mapped_only, diff_only):
364
  try:
365
  def get_type_by_cat(cat):
@@ -433,9 +412,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
433
  df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
434
  df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
435
 
436
- # =========================================================
437
- # ๐Ÿ’ก [๋ฐฉ์–ด๋ง‰ 1] Target_Table ์ด๋ฆ„ ๊น”๋”ํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ค๊ธฐ
438
- # =========================================================
439
  conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
440
  registry_query = """
441
  SELECT Target_Table FROM Mapping_registry
@@ -450,9 +426,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
450
  if val and val.lower() not in ["none", "nan"]:
451
  target_table_name = val
452
 
453
- # =========================================================
454
- # ๐Ÿ’ก [๋ฐฉ์–ด๋ง‰ 2] ๋งคํ•‘ ์žฅ๋ถ€ ์•ˆ์ „ํ•˜๊ฒŒ ์ฝ์–ด์˜ค๊ธฐ (์˜คํƒ€๋‚˜๋ฉด ์—๋Ÿฌ ๋„์›€)
455
- # =========================================================
456
  try:
457
  q_fw = f"SELECT * FROM [{target_table_name}] WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?"
458
  df_fw = pd.read_sql(q_fw, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
@@ -501,9 +474,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
501
  df_mapping['Base_section'] = df_mapping['Base_section'].apply(clean_key_val)
502
  df_mapping['Comp_section'] = df_mapping['Comp_section'].apply(clean_key_val)
503
 
504
- # =========================================================
505
- # ๐Ÿ’ฃ [๋ฐฉ์–ด๋ง‰ 3] ์นดํ…Œ์‹œ์•ˆ ํญ๋ฐœ ๋ฐฉ์ง€: ๋นˆ์นธ๋ผ๋ฆฌ ์—ฐ๊ฒฐ๋œ ๊ฐ€์งœ ๋งคํ•‘ ์‚ญ์ œ
506
- # =========================================================
507
  df_mapping = df_mapping[(df_mapping['Base_section'] != "") & (df_mapping['Comp_section'] != "")]
508
  df_mapping = df_mapping.dropna().drop_duplicates()
509
  else:
@@ -575,9 +545,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
575
 
576
  return pd.DataFrame({"Info": ["์กฐ๊ฑด์„ ์„ ํƒํ•˜์„ธ์š”."]})
577
  except Exception as e:
578
- import traceback
579
- traceback.print_exc()
580
- # ์‹œ์Šคํ…œ ์—๋Ÿฌ ๋Œ€์‹  ํ™”๋ฉด์— ๊น”๋”ํ•˜๊ฒŒ ์›์ธ์„ ๋„์›Œ์ค๋‹ˆ๋‹ค.
581
  error_msg = str(e)
582
  if "database is locked" in error_msg.lower():
583
  return pd.DataFrame({"Error": ["๐Ÿšจ DB๊ฐ€ ์ž ๊ฒจ์žˆ์Šต๋‹ˆ๋‹ค! ์ผœ๋†“์œผ์‹  'DB Browser' ํ”„๋กœ๊ทธ๋žจ์„ ์™„์ „ํžˆ ์ข…๋ฃŒํ•œ ๋’ค ๋‹ค์‹œ ์กฐํšŒํ•ด ์ฃผ์„ธ์š”."]})
@@ -598,7 +565,6 @@ with gr.Blocks() as demo:
598
 
599
  with gr.Row(elem_classes="reset-row"):
600
  base_category = gr.Dropdown(label="Category", scale=4)
601
- # โฌ‡๏ธ elem_classes="reset-btn" ์ถ”๊ฐ€!
602
  base_reset_btn = gr.Button("โ†บ ์ดˆ๊ธฐํ™”", scale=1, elem_classes="reset-btn")
603
 
604
  with gr.Accordion("๐Ÿ”„ ๋น„๊ต ๋ฒ•๊ทœ", open=False):
@@ -609,12 +575,10 @@ with gr.Blocks() as demo:
609
 
610
  with gr.Row(elem_classes="reset-row"):
611
  comp_category = gr.Dropdown(label="Category", choices=[], scale=4)
612
- # โฌ‡๏ธ elem_classes="reset-btn" ์ถ”๊ฐ€!
613
  comp_reset_btn = gr.Button("โ†บ ์ดˆ๊ธฐํ™”", scale=1, elem_classes="reset-btn")
614
 
615
  with gr.Row(elem_id="search_row"):
616
  search_btn = gr.Button("๐Ÿ” ์กฐํšŒ", variant="primary", scale=10)
617
- # โฌ‡๏ธ [์ถ”๊ฐ€] ๋งคํ•‘ ์ฒดํฌ๋ฐ•์Šค ์ถ”๊ฐ€!
618
  mapped_only_cb = gr.Checkbox(label="๐Ÿ”— ๋งคํ•‘๋œ ํ•ญ๋ชฉ๋งŒ ๋ณด๊ธฐ", value=False, elem_id="mapped_cb_item", container=False, scale=1)
619
  diff_filter_cb = gr.Checkbox(label="๐Ÿ’ก ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ๋งŒ ๋ณด๊ธฐ", value=False, elem_id="diff_cb_item", container=False, scale=1)
620
 
 
5
  import re
6
  import base64
7
  import difflib
 
8
 
9
  # ==========================================
10
  # 1. Environment Setup & Data Helpers
 
56
 
57
  return sorted(list(set([d["standard"] for d in db_registry])))
58
 
 
 
 
59
  def fetch_database_records(std, ver, cat, table_type):
60
  try:
61
  db_path = os.path.join(UPLOAD_DIR, f"{std}_{ver}.db")
62
  conn = sqlite3.connect(db_path)
63
+ conn.text_factory = decode_sqlite_text
64
 
 
65
  conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
66
  config_df = pd.read_sql("SELECT * FROM Table_Config WHERE TRIM(Table_Type)=?", conn_map, params=[table_type.strip()])
67
  conn_map.close()
 
72
  anchors = [x.strip() for x in config_df.iloc[0]['Anchor_Column'].split(',')]
73
  displays = [x.strip() for x in config_df.iloc[0]['Display_Columns'].split(',')] if pd.notna(config_df.iloc[0]['Display_Columns']) else anchors
74
 
 
75
  tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
76
  valid_tables = [t for t in tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
77
  main_table = f"{std}_{ver}"
 
83
 
84
  query = f"SELECT * FROM [{main_table}]"
85
 
 
86
  cols = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
87
  lower_cols = [c.lower() for c in cols]
88
 
 
103
  df = pd.read_sql(query, conn)
104
  conn.close()
105
 
 
106
  final_cols = []
107
  for d in displays:
108
  for c in df.columns:
 
115
  if not final_cols:
116
  return df, real_anchors
117
 
118
+ for c in final_cols:
119
+ df[c] = df[c].apply(convert_blob_to_html_img)
120
+
121
  return df[final_cols], real_anchors
122
 
123
  except Exception as e:
 
129
  try:
130
  s1, s2 = str(text1), str(text2)
131
 
 
 
 
132
  if len(s1) > 1000 or len(s2) > 1000:
133
  return s1, s2
134
 
 
136
  if not words1 or not words2:
137
  return s1, s2
138
 
 
 
 
139
  common_words = set(words1) & set(words2)
140
  if len(common_words) / min(len(words1), len(words2)) < 0.05:
141
  return s1, s2
142
 
 
143
  matcher = difflib.SequenceMatcher(None, words1, words2)
 
144
  res1, res2 = [], []
145
+
146
  for tag, i1, i2, j1, j2 in matcher.get_opcodes():
147
  if tag == 'replace':
148
  res1.append(f"<span style='color:#ff4d4f; font-weight:bold;'>{' '.join(words1[i1:i2])}</span>")
 
158
  return " ".join(res1), " ".join(res2)
159
  except Exception:
160
  return text1, text2
161
+
162
  # ==========================================
163
  # 2. UI Component Handlers
164
  # ==========================================
 
165
  def load_initial_standards():
166
  return gr.Dropdown(choices=fetch_available_standards())
167
 
 
171
  versions = sorted(set(d["version"] for d in db_registry if d["standard"] == standard))
172
  return gr.Dropdown(choices=versions)
173
 
 
174
  def update_base_category_dropdown(standard, version):
175
  if not standard or not version:
176
  return gr.update(choices=[], value=None, interactive=False), gr.update(value="")
177
 
178
  choices = ["ALL"]
179
+ status_value = ""
180
 
181
  db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
182
  if not os.path.exists(db_path):
 
185
  try:
186
  conn = sqlite3.connect(db_path)
187
  tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
 
188
  valid_tables = [t for t in tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
 
189
  main_table = f"{standard}_{version}"
190
  if main_table not in valid_tables:
191
  main_table = valid_tables[0] if valid_tables else None
192
 
193
  if main_table:
 
194
  try:
195
  cols_check = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
196
  if "Status" in cols_check or "status" in cols_check:
 
224
 
225
  conn.close()
226
  except Exception:
227
+ pass
 
228
 
229
  return gr.update(choices=choices, value=None, interactive=True), gr.update(value=status_value)
230
 
 
258
  except Exception:
259
  return gr.update(choices=[], value=None, interactive=False)
260
 
 
261
  def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_ver):
262
  if not all([base_std, base_ver, base_cat, comp_std, comp_ver]):
263
  return gr.update(choices=[], value=None, interactive=False), gr.update(value="")
 
266
  status_value = ""
267
 
268
  try:
 
269
  comp_db_path = os.path.join(UPLOAD_DIR, f"{comp_std}_{comp_ver}.db")
270
  if os.path.exists(comp_db_path):
271
  c_conn = sqlite3.connect(comp_db_path)
 
306
  if "Main" in allowed_types:
307
  final_choices.append("ALL")
308
  if os.path.exists(comp_db_path):
 
309
  c_conn = sqlite3.connect(comp_db_path)
310
  if c_main_table:
311
  cols = pd.read_sql(f"PRAGMA table_info([{c_main_table}])", c_conn)['name'].tolist()
 
327
 
328
  return gr.update(choices=final_choices, value=final_choices[0] if final_choices else None, interactive=True), gr.update(value=status_value)
329
 
330
+ except Exception:
 
 
331
  return gr.update(choices=[], value=None), gr.update(value="")
332
 
333
  def reset_base_selections():
 
334
  return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
335
 
336
  def reset_comp_selections():
 
337
  return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
338
 
339
+ # ==========================================
340
+ # 3. Core Search Logic
341
+ # ==========================================
342
  def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat, mapped_only, diff_only):
343
  try:
344
  def get_type_by_cat(cat):
 
412
  df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
413
  df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
414
 
 
 
 
415
  conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
416
  registry_query = """
417
  SELECT Target_Table FROM Mapping_registry
 
426
  if val and val.lower() not in ["none", "nan"]:
427
  target_table_name = val
428
 
 
 
 
429
  try:
430
  q_fw = f"SELECT * FROM [{target_table_name}] WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?"
431
  df_fw = pd.read_sql(q_fw, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
 
474
  df_mapping['Base_section'] = df_mapping['Base_section'].apply(clean_key_val)
475
  df_mapping['Comp_section'] = df_mapping['Comp_section'].apply(clean_key_val)
476
 
 
 
 
477
  df_mapping = df_mapping[(df_mapping['Base_section'] != "") & (df_mapping['Comp_section'] != "")]
478
  df_mapping = df_mapping.dropna().drop_duplicates()
479
  else:
 
545
 
546
  return pd.DataFrame({"Info": ["์กฐ๊ฑด์„ ์„ ํƒํ•˜์„ธ์š”."]})
547
  except Exception as e:
 
 
 
548
  error_msg = str(e)
549
  if "database is locked" in error_msg.lower():
550
  return pd.DataFrame({"Error": ["๐Ÿšจ DB๊ฐ€ ์ž ๊ฒจ์žˆ์Šต๋‹ˆ๋‹ค! ์ผœ๋†“์œผ์‹  'DB Browser' ํ”„๋กœ๊ทธ๋žจ์„ ์™„์ „ํžˆ ์ข…๋ฃŒํ•œ ๋’ค ๋‹ค์‹œ ์กฐํšŒํ•ด ์ฃผ์„ธ์š”."]})
 
565
 
566
  with gr.Row(elem_classes="reset-row"):
567
  base_category = gr.Dropdown(label="Category", scale=4)
 
568
  base_reset_btn = gr.Button("โ†บ ์ดˆ๊ธฐํ™”", scale=1, elem_classes="reset-btn")
569
 
570
  with gr.Accordion("๐Ÿ”„ ๋น„๊ต ๋ฒ•๊ทœ", open=False):
 
575
 
576
  with gr.Row(elem_classes="reset-row"):
577
  comp_category = gr.Dropdown(label="Category", choices=[], scale=4)
 
578
  comp_reset_btn = gr.Button("โ†บ ์ดˆ๊ธฐํ™”", scale=1, elem_classes="reset-btn")
579
 
580
  with gr.Row(elem_id="search_row"):
581
  search_btn = gr.Button("๐Ÿ” ์กฐํšŒ", variant="primary", scale=10)
 
582
  mapped_only_cb = gr.Checkbox(label="๐Ÿ”— ๋งคํ•‘๋œ ํ•ญ๋ชฉ๋งŒ ๋ณด๊ธฐ", value=False, elem_id="mapped_cb_item", container=False, scale=1)
583
  diff_filter_cb = gr.Checkbox(label="๐Ÿ’ก ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ๋งŒ ๋ณด๊ธฐ", value=False, elem_id="diff_cb_item", container=False, scale=1)
584