Update app.py
Browse files
app.py
CHANGED
|
@@ -39,9 +39,6 @@ def display_data(standard, version, selection):
|
|
| 39 |
if main_t not in tables:
|
| 40 |
main_t = tables[0]
|
| 41 |
|
| 42 |
-
# --------------------------
|
| 43 |
-
# 🔹 테이블 명칭 매칭 (selection이 테이블 명칭인지 확인)
|
| 44 |
-
# --------------------------
|
| 45 |
full_table_name = None
|
| 46 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 47 |
|
|
@@ -54,28 +51,19 @@ def display_data(standard, version, selection):
|
|
| 54 |
full_table_name = t
|
| 55 |
break
|
| 56 |
|
| 57 |
-
|
| 58 |
-
# 🌟 부속 테이블 선택 시 (TableA, TableB_C 등)
|
| 59 |
-
# 원본 열 구조(4열, 5열 등)를 그대로 유지
|
| 60 |
-
# ==========================================
|
| 61 |
if full_table_name and full_table_name != main_t:
|
| 62 |
df = pd.read_sql(f"SELECT * FROM [{full_table_name}]", conn)
|
| 63 |
|
| 64 |
-
# version 열 제외 및 컬럼명 정리
|
| 65 |
df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
|
| 66 |
df.columns = [c.replace("_", " ").title() for c in df.columns]
|
| 67 |
|
| 68 |
-
# 모든 셀에 대해 이미지 렌더링(BLOB 변환) 적용
|
| 69 |
for col in df.columns:
|
| 70 |
df[col] = df[col].apply(blob_to_base64_html)
|
| 71 |
|
| 72 |
conn.close()
|
| 73 |
return df
|
| 74 |
|
| 75 |
-
# --------------------------
|
| 76 |
-
# 🔹 메인 테이블 (ALL 또는 특정 Category)
|
| 77 |
-
# 기존 규격(section, description) 유지
|
| 78 |
-
# --------------------------
|
| 79 |
cursor = conn.cursor()
|
| 80 |
cursor.execute(f"PRAGMA table_info([{main_t}])")
|
| 81 |
cols_info = cursor.fetchall()
|
|
@@ -103,7 +91,7 @@ def display_data(standard, version, selection):
|
|
| 103 |
rows = cursor.fetchall()
|
| 104 |
df = pd.DataFrame(rows, columns=cols)
|
| 105 |
|
| 106 |
-
|
| 107 |
df.columns = [c.lower().strip() for c in df.columns]
|
| 108 |
if 'section' in df.columns and 'description' in df.columns:
|
| 109 |
df = df[['section', 'description']]
|
|
@@ -117,7 +105,9 @@ def display_data(standard, version, selection):
|
|
| 117 |
except Exception as e:
|
| 118 |
traceback.print_exc()
|
| 119 |
return pd.DataFrame({"Error": [str(e)]})
|
|
|
|
| 120 |
# --------------------------
|
|
|
|
| 121 |
def blob_to_base64_html(blob_data):
|
| 122 |
if blob_data is None or pd.isna(blob_data):
|
| 123 |
return ""
|
|
@@ -199,21 +189,19 @@ def update_category_dd(standard, version):
|
|
| 199 |
if main_t not in tables:
|
| 200 |
main_t = tables[0] if tables else None
|
| 201 |
|
| 202 |
-
|
| 203 |
-
# 🌟 [추가된 로직] TableA, TableB_C 등 부속 테이블을 드롭다운에 추가
|
| 204 |
-
# ==========================================
|
| 205 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 206 |
for t in tables:
|
| 207 |
# 메인 테이블과 매핑 관련 테이블은 목록에서 제외
|
| 208 |
if t == main_t or t.lower() in ['mapping_registry', 'mapping_table']:
|
| 209 |
continue
|
| 210 |
|
| 211 |
-
# 'UNR155_2101_TableA' -> 'TableA' 로 깔끔하게 자르기
|
| 212 |
short_name = pattern.sub("", t).strip(" _")
|
| 213 |
if short_name and short_name not in choices:
|
| 214 |
choices.append(short_name)
|
| 215 |
elif t not in choices:
|
| 216 |
choices.append(t)
|
|
|
|
| 217 |
# ==========================================
|
| 218 |
|
| 219 |
if not main_t:
|
|
@@ -231,7 +219,7 @@ def update_category_dd(standard, version):
|
|
| 231 |
for _, r in df.iterrows():
|
| 232 |
chapter = str(r[ch]).strip()
|
| 233 |
category = str(r[ca]).strip()
|
| 234 |
-
|
| 235 |
if chapter and category and chapter.lower() != 'none' and category.lower() != 'none':
|
| 236 |
choices.append(f"{chapter}.{category}")
|
| 237 |
|
|
@@ -250,6 +238,7 @@ def update_category_dd(standard, version):
|
|
| 250 |
# --------------------------
|
| 251 |
# 초기화
|
| 252 |
# --------------------------
|
|
|
|
| 253 |
def reset_base():
|
| 254 |
return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None)
|
| 255 |
|
|
@@ -298,6 +287,7 @@ def highlight_diff(base, comp):
|
|
| 298 |
# --------------------------
|
| 299 |
# 데이터 조회
|
| 300 |
# --------------------------
|
|
|
|
| 301 |
def display_data(standard, version, selection):
|
| 302 |
if not all([standard, version, selection]):
|
| 303 |
return pd.DataFrame({"Info": ["선택 필요"]})
|
|
@@ -320,9 +310,7 @@ def display_data(standard, version, selection):
|
|
| 320 |
if main_t not in tables:
|
| 321 |
main_t = tables[0]
|
| 322 |
|
| 323 |
-
|
| 324 |
-
# 🔹 테이블 명칭 매칭 (selection이 테이블 명칭인지 확인)
|
| 325 |
-
# --------------------------
|
| 326 |
full_table_name = None
|
| 327 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 328 |
|
|
@@ -335,10 +323,6 @@ def display_data(standard, version, selection):
|
|
| 335 |
full_table_name = t
|
| 336 |
break
|
| 337 |
|
| 338 |
-
# ==========================================
|
| 339 |
-
# 🌟 부속 테이블 선택 시 (TableA, TableB_C 등)
|
| 340 |
-
# 원본 열 구조(4열, 5열 등)를 그대로 유지
|
| 341 |
-
# ==========================================
|
| 342 |
if full_table_name and full_table_name != main_t:
|
| 343 |
df = pd.read_sql(f"SELECT * FROM [{full_table_name}]", conn)
|
| 344 |
|
|
@@ -353,10 +337,7 @@ def display_data(standard, version, selection):
|
|
| 353 |
conn.close()
|
| 354 |
return df
|
| 355 |
|
| 356 |
-
|
| 357 |
-
# 🔹 메인 테이블 (ALL 또는 특정 Category)
|
| 358 |
-
# 기존 규격(section, description) 유지
|
| 359 |
-
# --------------------------
|
| 360 |
cursor = conn.cursor()
|
| 361 |
cursor.execute(f"PRAGMA table_info([{main_t}])")
|
| 362 |
cols_info = cursor.fetchall()
|
|
@@ -402,6 +383,7 @@ def display_data(standard, version, selection):
|
|
| 402 |
# --------------------------
|
| 403 |
# 통합 조회 (apply로 속도 최적화)
|
| 404 |
# --------------------------
|
|
|
|
| 405 |
def apply_diff_row(row, base_col, comp_col):
|
| 406 |
"""Pandas apply를 위한 Diff 연산 래퍼 함수"""
|
| 407 |
base_val = str(row.get(base_col, ""))
|
|
@@ -458,9 +440,7 @@ def unified_search(bs, bv, bc, cs, cv, cc):
|
|
| 458 |
df_b = df_base[['merge_key', 'section', 'description']].rename(columns={'section': 'sec_b', 'description': 'desc_b'})
|
| 459 |
df_c = df_comp[['merge_key', 'section', 'description']].rename(columns={'section': 'sec_c', 'description': 'desc_c'})
|
| 460 |
|
| 461 |
-
|
| 462 |
-
# 🌟 매핑 DB 가져오기 (가장 안전한 Pandas 양방향 병합)
|
| 463 |
-
# ==========================================
|
| 464 |
conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 465 |
|
| 466 |
# 1. 정방향 매칭 (UI 기준 법규 == DB Base 법규)
|
|
@@ -490,9 +470,7 @@ def unified_search(bs, bv, bc, cs, cv, cc):
|
|
| 490 |
else:
|
| 491 |
df_mapping = pd.DataFrame(columns=['Base_section', 'Comp_section'])
|
| 492 |
|
| 493 |
-
|
| 494 |
-
# 🌟 핵심 로직: 하이브리드 매핑 (명시적 + 암묵적)
|
| 495 |
-
# ==========================================
|
| 496 |
# A. 명시적 매핑 (Mapping DB에 있는 데이터)
|
| 497 |
explicit_b = set(df_mapping['Base_section'].dropna())
|
| 498 |
explicit_c = set(df_mapping['Comp_section'].dropna())
|
|
@@ -510,9 +488,7 @@ def unified_search(bs, bv, bc, cs, cv, cc):
|
|
| 510 |
# C. 두 조건을 하나로 합친 브릿지(다리) 테이블
|
| 511 |
bridge = pd.concat([df_mapping, df_implicit], ignore_index=True)
|
| 512 |
|
| 513 |
-
|
| 514 |
-
# 🌟 병합(Merge) 연산 - KeyError 방지 및 원본 순서 보존
|
| 515 |
-
# ==========================================
|
| 516 |
df_b_clean = df_b.rename(columns={'merge_key': 'Base_section'})
|
| 517 |
df_c_clean = df_c.rename(columns={'merge_key': 'Comp_section'})
|
| 518 |
|
|
@@ -568,7 +544,6 @@ def update_comp_standard(base_std, base_ver):
|
|
| 568 |
try:
|
| 569 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 570 |
|
| 571 |
-
# 🌟 핵심: 정방향 매칭과 역방향 매칭을 합침 (UNION)
|
| 572 |
query = """
|
| 573 |
SELECT DISTINCT Comp_std AS mapped_std
|
| 574 |
FROM Mapping_registry
|
|
@@ -578,7 +553,7 @@ def update_comp_standard(base_std, base_ver):
|
|
| 578 |
FROM Mapping_registry
|
| 579 |
WHERE Comp_std=? AND Comp_ver=?
|
| 580 |
"""
|
| 581 |
-
|
| 582 |
df = pd.read_sql(query, conn, params=[base_std, base_ver, base_std, base_ver])
|
| 583 |
conn.close()
|
| 584 |
|
|
|
|
| 39 |
if main_t not in tables:
|
| 40 |
main_t = tables[0]
|
| 41 |
|
|
|
|
|
|
|
|
|
|
| 42 |
full_table_name = None
|
| 43 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 44 |
|
|
|
|
| 51 |
full_table_name = t
|
| 52 |
break
|
| 53 |
|
| 54 |
+
|
|
|
|
|
|
|
|
|
|
| 55 |
if full_table_name and full_table_name != main_t:
|
| 56 |
df = pd.read_sql(f"SELECT * FROM [{full_table_name}]", conn)
|
| 57 |
|
|
|
|
| 58 |
df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
|
| 59 |
df.columns = [c.replace("_", " ").title() for c in df.columns]
|
| 60 |
|
|
|
|
| 61 |
for col in df.columns:
|
| 62 |
df[col] = df[col].apply(blob_to_base64_html)
|
| 63 |
|
| 64 |
conn.close()
|
| 65 |
return df
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
cursor = conn.cursor()
|
| 68 |
cursor.execute(f"PRAGMA table_info([{main_t}])")
|
| 69 |
cols_info = cursor.fetchall()
|
|
|
|
| 91 |
rows = cursor.fetchall()
|
| 92 |
df = pd.DataFrame(rows, columns=cols)
|
| 93 |
|
| 94 |
+
|
| 95 |
df.columns = [c.lower().strip() for c in df.columns]
|
| 96 |
if 'section' in df.columns and 'description' in df.columns:
|
| 97 |
df = df[['section', 'description']]
|
|
|
|
| 105 |
except Exception as e:
|
| 106 |
traceback.print_exc()
|
| 107 |
return pd.DataFrame({"Error": [str(e)]})
|
| 108 |
+
|
| 109 |
# --------------------------
|
| 110 |
+
|
| 111 |
def blob_to_base64_html(blob_data):
|
| 112 |
if blob_data is None or pd.isna(blob_data):
|
| 113 |
return ""
|
|
|
|
| 189 |
if main_t not in tables:
|
| 190 |
main_t = tables[0] if tables else None
|
| 191 |
|
| 192 |
+
|
|
|
|
|
|
|
| 193 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 194 |
for t in tables:
|
| 195 |
# 메인 테이블과 매핑 관련 테이블은 목록에서 제외
|
| 196 |
if t == main_t or t.lower() in ['mapping_registry', 'mapping_table']:
|
| 197 |
continue
|
| 198 |
|
|
|
|
| 199 |
short_name = pattern.sub("", t).strip(" _")
|
| 200 |
if short_name and short_name not in choices:
|
| 201 |
choices.append(short_name)
|
| 202 |
elif t not in choices:
|
| 203 |
choices.append(t)
|
| 204 |
+
|
| 205 |
# ==========================================
|
| 206 |
|
| 207 |
if not main_t:
|
|
|
|
| 219 |
for _, r in df.iterrows():
|
| 220 |
chapter = str(r[ch]).strip()
|
| 221 |
category = str(r[ca]).strip()
|
| 222 |
+
|
| 223 |
if chapter and category and chapter.lower() != 'none' and category.lower() != 'none':
|
| 224 |
choices.append(f"{chapter}.{category}")
|
| 225 |
|
|
|
|
| 238 |
# --------------------------
|
| 239 |
# 초기화
|
| 240 |
# --------------------------
|
| 241 |
+
|
| 242 |
def reset_base():
|
| 243 |
return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None)
|
| 244 |
|
|
|
|
| 287 |
# --------------------------
|
| 288 |
# 데이터 조회
|
| 289 |
# --------------------------
|
| 290 |
+
|
| 291 |
def display_data(standard, version, selection):
|
| 292 |
if not all([standard, version, selection]):
|
| 293 |
return pd.DataFrame({"Info": ["선택 필요"]})
|
|
|
|
| 310 |
if main_t not in tables:
|
| 311 |
main_t = tables[0]
|
| 312 |
|
| 313 |
+
|
|
|
|
|
|
|
| 314 |
full_table_name = None
|
| 315 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 316 |
|
|
|
|
| 323 |
full_table_name = t
|
| 324 |
break
|
| 325 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
if full_table_name and full_table_name != main_t:
|
| 327 |
df = pd.read_sql(f"SELECT * FROM [{full_table_name}]", conn)
|
| 328 |
|
|
|
|
| 337 |
conn.close()
|
| 338 |
return df
|
| 339 |
|
| 340 |
+
|
|
|
|
|
|
|
|
|
|
| 341 |
cursor = conn.cursor()
|
| 342 |
cursor.execute(f"PRAGMA table_info([{main_t}])")
|
| 343 |
cols_info = cursor.fetchall()
|
|
|
|
| 383 |
# --------------------------
|
| 384 |
# 통합 조회 (apply로 속도 최적화)
|
| 385 |
# --------------------------
|
| 386 |
+
|
| 387 |
def apply_diff_row(row, base_col, comp_col):
|
| 388 |
"""Pandas apply를 위한 Diff 연산 래퍼 함수"""
|
| 389 |
base_val = str(row.get(base_col, ""))
|
|
|
|
| 440 |
df_b = df_base[['merge_key', 'section', 'description']].rename(columns={'section': 'sec_b', 'description': 'desc_b'})
|
| 441 |
df_c = df_comp[['merge_key', 'section', 'description']].rename(columns={'section': 'sec_c', 'description': 'desc_c'})
|
| 442 |
|
| 443 |
+
|
|
|
|
|
|
|
| 444 |
conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 445 |
|
| 446 |
# 1. 정방향 매칭 (UI 기준 법규 == DB Base 법규)
|
|
|
|
| 470 |
else:
|
| 471 |
df_mapping = pd.DataFrame(columns=['Base_section', 'Comp_section'])
|
| 472 |
|
| 473 |
+
|
|
|
|
|
|
|
| 474 |
# A. 명시적 매핑 (Mapping DB에 있는 데이터)
|
| 475 |
explicit_b = set(df_mapping['Base_section'].dropna())
|
| 476 |
explicit_c = set(df_mapping['Comp_section'].dropna())
|
|
|
|
| 488 |
# C. 두 조건을 하나로 합친 브릿지(다리) 테이블
|
| 489 |
bridge = pd.concat([df_mapping, df_implicit], ignore_index=True)
|
| 490 |
|
| 491 |
+
|
|
|
|
|
|
|
| 492 |
df_b_clean = df_b.rename(columns={'merge_key': 'Base_section'})
|
| 493 |
df_c_clean = df_c.rename(columns={'merge_key': 'Comp_section'})
|
| 494 |
|
|
|
|
| 544 |
try:
|
| 545 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 546 |
|
|
|
|
| 547 |
query = """
|
| 548 |
SELECT DISTINCT Comp_std AS mapped_std
|
| 549 |
FROM Mapping_registry
|
|
|
|
| 553 |
FROM Mapping_registry
|
| 554 |
WHERE Comp_std=? AND Comp_ver=?
|
| 555 |
"""
|
| 556 |
+
|
| 557 |
df = pd.read_sql(query, conn, params=[base_std, base_ver, base_std, base_ver])
|
| 558 |
conn.close()
|
| 559 |
|