Update app.py
Browse files
app.py
CHANGED
|
@@ -249,147 +249,6 @@ def reset_comp_selections():
|
|
| 249 |
return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
|
| 250 |
|
| 251 |
|
| 252 |
-
# ==========================================
|
| 253 |
-
# 3. Core Logic & Data Processing
|
| 254 |
-
# ==========================================
|
| 255 |
-
def generate_html_diff(base_text, comp_text):
|
| 256 |
-
base_text = "" if pd.isna(base_text) else str(base_text)
|
| 257 |
-
comp_text = "" if pd.isna(comp_text) else str(comp_text)
|
| 258 |
-
|
| 259 |
-
b_words = base_text.split()
|
| 260 |
-
c_words = comp_text.split()
|
| 261 |
-
|
| 262 |
-
diff_generator = list(difflib.ndiff(b_words, c_words))
|
| 263 |
-
b_result, c_result = [], []
|
| 264 |
-
i = 0
|
| 265 |
-
|
| 266 |
-
while i < len(diff_generator):
|
| 267 |
-
code = diff_generator[i][0]
|
| 268 |
-
word = diff_generator[i][2:]
|
| 269 |
-
|
| 270 |
-
if code == ' ':
|
| 271 |
-
b_result.append(word)
|
| 272 |
-
c_result.append(word)
|
| 273 |
-
elif code == '-' and i+1 < len(diff_generator) and diff_generator[i+1][0] == '+':
|
| 274 |
-
new_word = diff_generator[i+1][2:]
|
| 275 |
-
b_result.append(f"<span style='color:#ff4d4f;font-weight:600'>{word}</span>")
|
| 276 |
-
c_result.append(f"<span style='color:#ff4d4f;font-weight:600'>{new_word}</span>")
|
| 277 |
-
i += 1
|
| 278 |
-
elif code == '-':
|
| 279 |
-
b_result.append(f"<span style='color:#ff4d4f;font-weight:600'>{word}</span>")
|
| 280 |
-
elif code == '+':
|
| 281 |
-
c_result.append(f"<span style='color:#2ecc71;font-weight:600'>{word}</span>")
|
| 282 |
-
|
| 283 |
-
i += 1
|
| 284 |
-
|
| 285 |
-
return " ".join(b_result), " ".join(c_result)
|
| 286 |
-
|
| 287 |
-
def fetch_database_records(standard, version, selection, table_type):
|
| 288 |
-
if not all([standard, version, selection]):
|
| 289 |
-
return pd.DataFrame({"Info": ["์ ํ ํ์"]}), []
|
| 290 |
-
|
| 291 |
-
try:
|
| 292 |
-
db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
|
| 293 |
-
if not os.path.exists(db_path):
|
| 294 |
-
return pd.DataFrame({"Error": [f"ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค: {db_path}"]}), []
|
| 295 |
-
|
| 296 |
-
conn = sqlite3.connect(db_path)
|
| 297 |
-
conn.text_factory = decode_sqlite_text
|
| 298 |
-
|
| 299 |
-
tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
|
| 300 |
-
valid_tables = [t for t in tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
|
| 301 |
-
|
| 302 |
-
main_table = f"{standard}_{version}"
|
| 303 |
-
if main_table not in valid_tables:
|
| 304 |
-
main_table = valid_tables[0] if valid_tables else None
|
| 305 |
-
|
| 306 |
-
target_table = None
|
| 307 |
-
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 308 |
-
|
| 309 |
-
for t in valid_tables:
|
| 310 |
-
if t == selection or pattern.sub("", t).strip(" _") == selection:
|
| 311 |
-
target_table = t
|
| 312 |
-
break
|
| 313 |
-
|
| 314 |
-
if target_table and target_table != main_table:
|
| 315 |
-
df = pd.read_sql(f"SELECT * FROM [{target_table}]", conn)
|
| 316 |
-
else:
|
| 317 |
-
if not main_table:
|
| 318 |
-
return pd.DataFrame({"Error": ["๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์์ ๋ฉ์ธ ํ
์ด๋ธ์ ์ฐพ์ ์ ์์ต๋๋ค."]}), []
|
| 319 |
-
|
| 320 |
-
cursor = conn.cursor()
|
| 321 |
-
cursor.execute(f"PRAGMA table_info([{main_table}])")
|
| 322 |
-
cols = [c[1] for c in cursor.fetchall()]
|
| 323 |
-
lower_cols = [c.lower().strip() for c in cols]
|
| 324 |
-
|
| 325 |
-
real_ch, real_cat = None, None
|
| 326 |
-
for c, lc in zip(cols, lower_cols):
|
| 327 |
-
if lc == "chapter": real_ch = c
|
| 328 |
-
elif lc == "category": real_cat = c
|
| 329 |
-
|
| 330 |
-
if selection == "ALL":
|
| 331 |
-
cursor.execute(f"SELECT * FROM [{main_table}]")
|
| 332 |
-
elif "." in selection and real_ch and real_cat:
|
| 333 |
-
ch, ca = selection.split(".", 1)
|
| 334 |
-
query = f"SELECT * FROM [{main_table}] WHERE REPLACE(TRIM(CAST([{real_ch}] AS TEXT)), ' ', '') = ? AND REPLACE(TRIM(CAST([{real_cat}] AS TEXT)), ' ', '') = ?"
|
| 335 |
-
cursor.execute(query, (ch.replace(" ", ""), ca.replace(" ", "")))
|
| 336 |
-
else:
|
| 337 |
-
cursor.execute(f"SELECT * FROM [{main_table}]")
|
| 338 |
-
|
| 339 |
-
df = pd.DataFrame(cursor.fetchall(), columns=cols)
|
| 340 |
-
|
| 341 |
-
df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
|
| 342 |
-
|
| 343 |
-
# =========================================================
|
| 344 |
-
# ๐ก [์ค๋งํธ ์ค์บ ๋ก์ง] DB ์ปฌ๋ผ์ ๋ณด๊ณ ๋ง๋ Main ์ค์ ์ ์์์ ์ฐพ์ต๋๋ค.
|
| 345 |
-
# =========================================================
|
| 346 |
-
conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 347 |
-
config_df = pd.read_sql("SELECT Anchor_Column, Display_Columns FROM Table_Config WHERE Table_Type=?", conn_map, params=[table_type])
|
| 348 |
-
conn_map.close()
|
| 349 |
-
|
| 350 |
-
anchor_col_config = "Section"
|
| 351 |
-
display_setting = "Description"
|
| 352 |
-
|
| 353 |
-
if not config_df.empty:
|
| 354 |
-
df_cols_lower = [c.lower() for c in df.columns]
|
| 355 |
-
for _, row in config_df.iterrows():
|
| 356 |
-
# DB์ ์ ํ Anchor ์ด์ด ์ค์ ๋ฐ์ดํฐํ๋ ์์ ์กด์ฌํ๋์ง ํ์ธ
|
| 357 |
-
first_anchor = str(row['Anchor_Column']).split(',')[0].strip().lower()
|
| 358 |
-
if first_anchor in df_cols_lower:
|
| 359 |
-
anchor_col_config = str(row['Anchor_Column'])
|
| 360 |
-
raw_disp = row['Display_Columns']
|
| 361 |
-
display_setting = str(raw_disp) if pd.notna(raw_disp) and str(raw_disp).strip() != "" else None
|
| 362 |
-
break
|
| 363 |
-
# =========================================================
|
| 364 |
-
|
| 365 |
-
anchor_cols_config = [x.strip() for x in anchor_col_config.split(',')]
|
| 366 |
-
|
| 367 |
-
real_anchors = []
|
| 368 |
-
for ac in anchor_cols_config:
|
| 369 |
-
real_ac = next((c for c in df.columns if c.lower() == ac.lower()), ac)
|
| 370 |
-
real_anchors.append(real_ac)
|
| 371 |
-
|
| 372 |
-
if display_setting:
|
| 373 |
-
cols_to_show = [c.strip() for c in display_setting.split(',')]
|
| 374 |
-
actual_cols_to_show = [c for c in df.columns if next((True for req in cols_to_show if c.lower() == req.lower()), False)]
|
| 375 |
-
|
| 376 |
-
for ra in reversed(real_anchors):
|
| 377 |
-
if ra in df.columns and ra not in actual_cols_to_show:
|
| 378 |
-
actual_cols_to_show.insert(0, ra)
|
| 379 |
-
|
| 380 |
-
if actual_cols_to_show:
|
| 381 |
-
df = df[actual_cols_to_show]
|
| 382 |
-
|
| 383 |
-
for col in df.columns:
|
| 384 |
-
df[col] = df[col].apply(convert_blob_to_html_img)
|
| 385 |
-
|
| 386 |
-
return df, real_anchors
|
| 387 |
-
|
| 388 |
-
except Exception as e:
|
| 389 |
-
import traceback
|
| 390 |
-
traceback.print_exc()
|
| 391 |
-
return pd.DataFrame({"Error": [str(e)]}), []
|
| 392 |
-
|
| 393 |
def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat, mapped_only, diff_only):
|
| 394 |
try:
|
| 395 |
def get_type_by_cat(cat):
|
|
@@ -463,14 +322,28 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 463 |
df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
|
| 464 |
df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
|
| 465 |
|
|
|
|
|
|
|
|
|
|
| 466 |
conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 467 |
|
| 468 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
df_fw = pd.read_sql(q_fw, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
|
| 470 |
|
| 471 |
-
q_rv = "SELECT * FROM
|
| 472 |
df_rv = pd.read_sql(q_rv, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
|
| 473 |
conn_map.close()
|
|
|
|
| 474 |
|
| 475 |
cols_fw_lower = {c.lower(): c for c in df_fw.columns}
|
| 476 |
|
|
@@ -535,17 +408,21 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 535 |
row_dict = {}
|
| 536 |
has_b = not pd.isna(row.get('base_idx')) and row.get('base_idx') != float('inf')
|
| 537 |
has_c = not pd.isna(row.get('comp_idx')) and row.get('comp_idx') != float('inf')
|
| 538 |
-
|
| 539 |
for c in rename_b.values(): row_dict[c] = str(row[c]) if has_b and not pd.isna(row[c]) else ""
|
| 540 |
for c in rename_c.values(): row_dict[c] = str(row[c]) if has_c and not pd.isna(row[c]) else ""
|
| 541 |
-
|
|
|
|
|
|
|
|
|
|
| 542 |
if mapped_only:
|
| 543 |
b_has_val = any(str(row_dict[c]).strip() not in ["", "nan", "None", " "] for c in rename_b.values())
|
| 544 |
c_has_val = any(str(row_dict[c]).strip() not in ["", "nan", "None", " "] for c in rename_c.values())
|
| 545 |
|
| 546 |
if not (b_has_val and c_has_val):
|
| 547 |
continue
|
| 548 |
-
|
|
|
|
| 549 |
if has_b and has_c:
|
| 550 |
for orig_col in rename_b.keys():
|
| 551 |
if ('description' in orig_col.lower() or '๋ด์ฉ' in orig_col) and rename_c.get(orig_col) in row_dict:
|
|
@@ -562,6 +439,8 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 562 |
if diff_only:
|
| 563 |
mask = final_df.astype(str).apply(lambda col: col.str.contains('color:#ff4d4f|color:#2ecc71', case=False, regex=True)).any(axis=1)
|
| 564 |
final_df = final_df[mask]
|
|
|
|
|
|
|
| 565 |
|
| 566 |
b_cols_final = [c for c in final_df.columns if c.endswith(f"_{base_ver}")]
|
| 567 |
c_cols_final = [c for c in final_df.columns if c.endswith(f"_{comp_ver}")]
|
|
|
|
| 249 |
return gr.update(value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(value="")
|
| 250 |
|
| 251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat, mapped_only, diff_only):
|
| 253 |
try:
|
| 254 |
def get_type_by_cat(cat):
|
|
|
|
| 322 |
df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
|
| 323 |
df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join([clean_key_val(x) for x in row]), axis=1)
|
| 324 |
|
| 325 |
+
# =========================================================
|
| 326 |
+
# ๐ก [ํต์ฌ ์
๊ทธ๋ ์ด๋] ๋งคํ ๋ผ์ฐํฐ: ์ด๋ค ์ฅ๋ถ๋ฅผ ์ฝ์์ง ๊ฒฐ์ ํฉ๋๋ค!
|
| 327 |
+
# =========================================================
|
| 328 |
conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 329 |
|
| 330 |
+
registry_query = """
|
| 331 |
+
SELECT Target_Table FROM Mapping_registry
|
| 332 |
+
WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?
|
| 333 |
+
LIMIT 1
|
| 334 |
+
"""
|
| 335 |
+
reg_df = pd.read_sql(registry_query, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
|
| 336 |
+
|
| 337 |
+
# DB์ Target_Table์ด ์ ํ์์ผ๋ฉด ๊ทธ๊ฑธ ์ฐ๊ณ , ์๊ฑฐ๋ ๋น์ด์์ผ๋ฉด ๊ธฐ๋ณธ ์ฅ๋ถ(Mapping_table)๋ฅผ ์๋๋ค.
|
| 338 |
+
target_table_name = reg_df.iloc[0]['Target_Table'] if not reg_df.empty and pd.notna(reg_df.iloc[0]['Target_Table']) else "Mapping_table"
|
| 339 |
+
|
| 340 |
+
q_fw = f"SELECT * FROM [{target_table_name}] WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?"
|
| 341 |
df_fw = pd.read_sql(q_fw, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
|
| 342 |
|
| 343 |
+
q_rv = f"SELECT * FROM [{target_table_name}] WHERE TRIM(Comp_std)=? AND TRIM(Comp_ver)=? AND TRIM(Base_std)=? AND TRIM(Base_ver)=?"
|
| 344 |
df_rv = pd.read_sql(q_rv, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
|
| 345 |
conn_map.close()
|
| 346 |
+
# =========================================================
|
| 347 |
|
| 348 |
cols_fw_lower = {c.lower(): c for c in df_fw.columns}
|
| 349 |
|
|
|
|
| 408 |
row_dict = {}
|
| 409 |
has_b = not pd.isna(row.get('base_idx')) and row.get('base_idx') != float('inf')
|
| 410 |
has_c = not pd.isna(row.get('comp_idx')) and row.get('comp_idx') != float('inf')
|
| 411 |
+
|
| 412 |
for c in rename_b.values(): row_dict[c] = str(row[c]) if has_b and not pd.isna(row[c]) else ""
|
| 413 |
for c in rename_c.values(): row_dict[c] = str(row[c]) if has_c and not pd.isna(row[c]) else ""
|
| 414 |
+
|
| 415 |
+
# =======================================================
|
| 416 |
+
# ๐ก [๊น๊นํ ๋น์นธ ๊ฒ์ฌ] ์์ชฝ ๋ค ๊ธ์๊ฐ ์ฑ์์ ธ ์๋์ง ํ์ธ!
|
| 417 |
+
# =======================================================
|
| 418 |
if mapped_only:
|
| 419 |
b_has_val = any(str(row_dict[c]).strip() not in ["", "nan", "None", " "] for c in rename_b.values())
|
| 420 |
c_has_val = any(str(row_dict[c]).strip() not in ["", "nan", "None", " "] for c in rename_c.values())
|
| 421 |
|
| 422 |
if not (b_has_val and c_has_val):
|
| 423 |
continue
|
| 424 |
+
# =======================================================
|
| 425 |
+
|
| 426 |
if has_b and has_c:
|
| 427 |
for orig_col in rename_b.keys():
|
| 428 |
if ('description' in orig_col.lower() or '๋ด์ฉ' in orig_col) and rename_c.get(orig_col) in row_dict:
|
|
|
|
| 439 |
if diff_only:
|
| 440 |
mask = final_df.astype(str).apply(lambda col: col.str.contains('color:#ff4d4f|color:#2ecc71', case=False, regex=True)).any(axis=1)
|
| 441 |
final_df = final_df[mask]
|
| 442 |
+
if final_df.empty:
|
| 443 |
+
return pd.DataFrame({"Info": ["๐ก ์ ํํ์ ์กฐ๊ฑด ๊ฐ์ ๋ณ๊ฒฝ๋ ๋ด์ฉ์ด ์์ต๋๋ค. (100% ๋์ผ)"]})
|
| 444 |
|
| 445 |
b_cols_final = [c for c in final_df.columns if c.endswith(f"_{base_ver}")]
|
| 446 |
c_cols_final = [c for c in final_df.columns if c.endswith(f"_{comp_ver}")]
|