QIDNLF commited on
Commit
314d1bf
·
verified ·
1 Parent(s): 7db1459

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -25
app.py CHANGED
@@ -245,7 +245,6 @@ def fetch_database_records(standard, version, selection, table_type):
245
  conn.text_factory = decode_sqlite_text
246
 
247
  anchor_col_config, display_setting = get_table_config(table_type)
248
- # 앵커가 쉼표로 여러 개일 경우 리스트로 분할
249
  anchor_cols_config = [x.strip() for x in anchor_col_config.split(',')]
250
 
251
  tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
@@ -287,7 +286,6 @@ def fetch_database_records(standard, version, selection, table_type):
287
 
288
  df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
289
 
290
- # 대소문자 무시하고 실제 앵커 컬럼명 찾기 (다중 앵커 지원)
291
  real_anchors = []
292
  for ac in anchor_cols_config:
293
  real_ac = next((c for c in df.columns if c.lower() == ac.lower()), ac)
@@ -297,7 +295,6 @@ def fetch_database_records(standard, version, selection, table_type):
297
  cols_to_show = [c.strip() for c in display_setting.split(',')]
298
  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)]
299
 
300
- # 설정된 출력 열에 앵커들이 없으면 강제 삽입
301
  for ra in reversed(real_anchors):
302
  if ra in df.columns and ra not in actual_cols_to_show:
303
  actual_cols_to_show.insert(0, ra)
@@ -309,7 +306,7 @@ def fetch_database_records(standard, version, selection, table_type):
309
  df[col] = df[col].apply(convert_blob_to_html_img)
310
 
311
  conn.close()
312
- return df, real_anchors # 단일 값이 아닌 앵커 '리스트' 반환
313
 
314
  except Exception as e:
315
  import traceback
@@ -322,7 +319,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
322
  is_main = base_cat == "ALL" or (base_cat and base_cat[0].isdigit() and "." in base_cat)
323
  table_type = "Main" if is_main else base_cat
324
 
325
- # 시각적 중복 제거 함수 (폭포수 방식: 상위 컬럼이 같을 때만 하위 컬럼 지움)
326
  def apply_visual_merge(df, cols):
327
  if not df.empty and len(cols) > 1:
328
  is_dup = pd.Series([True] * len(df), index=df.index)
@@ -334,17 +330,13 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
334
  df.loc[is_dup, col] = ""
335
  return df
336
 
337
- # ----------------------------------------
338
- # 1. 단일 조회
339
- # ----------------------------------------
340
  if base_std and base_ver and base_cat and (not comp_std or not comp_ver or not comp_cat):
341
  df, _ = fetch_database_records(base_std, base_ver, base_cat, table_type)
342
  if "Error" in df.columns or "Info" in df.columns: return df
343
  return apply_visual_merge(df, df.columns)
344
 
345
- # ----------------------------------------
346
- # 2. 비교 조회
347
- # ----------------------------------------
348
  if all([base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat]):
349
  df_base, real_anchors_b = fetch_database_records(base_std, base_ver, base_cat, table_type)
350
  df_comp, real_anchors_c = fetch_database_records(comp_std, comp_ver, comp_cat, table_type)
@@ -357,11 +349,9 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
357
  for ra in real_anchors_c:
358
  if ra not in df_comp.columns: return pd.DataFrame({"Error": [f"비교 열 '{ra}'이 데이터에 없습니다."]})
359
 
360
- # 다중 앵커를 하이픈(-)으로 연결해 단일 merge_key 생성 (예: "5.1-M6")
361
  df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
362
  df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
363
 
364
- # 브릿지 매핑 복원
365
  conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
366
  query_fw = "SELECT Base_section, Comp_section FROM Mapping_table WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?"
367
  df_fw = pd.read_sql(query_fw, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
@@ -412,7 +402,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
412
  merged['comp_idx'] = merged['comp_idx'].fillna(float('inf'))
413
  merged = merged.sort_values(['base_idx', 'comp_idx'])
414
 
415
- # N열 동적 조립 및 Diff 처리
416
  result_rows = []
417
  b_cols_renamed = list(rename_b.values())
418
  c_cols_renamed = list(rename_c.values())
@@ -427,20 +416,22 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
427
  for c in c_cols_renamed:
428
  row_dict[c] = str(row.get(c)) if has_c and not pd.isna(row.get(c)) else ""
429
 
430
- if table_type == "Main" and has_b and has_c:
431
- desc_b_col = next((c for c in b_cols_renamed if 'description' in c.lower()), None)
432
- desc_c_col = next((c for c in c_cols_renamed if 'description' in c.lower()), None)
433
-
434
- if desc_b_col and desc_c_col:
435
- b_val, c_val = row_dict[desc_b_col], row_dict[desc_c_col]
436
- if b_val and c_val and "<img" not in b_val and "<img" not in c_val and b_val != c_val:
437
- row_dict[desc_b_col], row_dict[desc_c_col] = generate_html_diff(b_val, c_val)
 
 
 
438
 
439
  result_rows.append(row_dict)
440
 
441
  final_df = pd.DataFrame(result_rows)
442
 
443
- # 좌/우 각각 시각적 병합(Cascading Blanking) 적용
444
  final_df = apply_visual_merge(final_df, b_cols_renamed)
445
  final_df = apply_visual_merge(final_df, c_cols_renamed)
446
 
@@ -512,14 +503,41 @@ with gr.Blocks() as demo:
512
  css = """
513
  table {
514
  table-layout: fixed !important;
515
- width: auto !important;
516
- min-width: 100%;
517
  }
518
 
 
519
  th, td {
520
  min-width: 150px;
521
  }
522
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  thead th {
524
  font-size: 18px !important;
525
  position: sticky;
 
245
  conn.text_factory = decode_sqlite_text
246
 
247
  anchor_col_config, display_setting = get_table_config(table_type)
 
248
  anchor_cols_config = [x.strip() for x in anchor_col_config.split(',')]
249
 
250
  tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
 
286
 
287
  df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
288
 
 
289
  real_anchors = []
290
  for ac in anchor_cols_config:
291
  real_ac = next((c for c in df.columns if c.lower() == ac.lower()), ac)
 
295
  cols_to_show = [c.strip() for c in display_setting.split(',')]
296
  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)]
297
 
 
298
  for ra in reversed(real_anchors):
299
  if ra in df.columns and ra not in actual_cols_to_show:
300
  actual_cols_to_show.insert(0, ra)
 
306
  df[col] = df[col].apply(convert_blob_to_html_img)
307
 
308
  conn.close()
309
+ return df, real_anchors
310
 
311
  except Exception as e:
312
  import traceback
 
319
  is_main = base_cat == "ALL" or (base_cat and base_cat[0].isdigit() and "." in base_cat)
320
  table_type = "Main" if is_main else base_cat
321
 
 
322
  def apply_visual_merge(df, cols):
323
  if not df.empty and len(cols) > 1:
324
  is_dup = pd.Series([True] * len(df), index=df.index)
 
330
  df.loc[is_dup, col] = ""
331
  return df
332
 
333
+ # 단일 조회
 
 
334
  if base_std and base_ver and base_cat and (not comp_std or not comp_ver or not comp_cat):
335
  df, _ = fetch_database_records(base_std, base_ver, base_cat, table_type)
336
  if "Error" in df.columns or "Info" in df.columns: return df
337
  return apply_visual_merge(df, df.columns)
338
 
339
+ # 비교 조회
 
 
340
  if all([base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat]):
341
  df_base, real_anchors_b = fetch_database_records(base_std, base_ver, base_cat, table_type)
342
  df_comp, real_anchors_c = fetch_database_records(comp_std, comp_ver, comp_cat, table_type)
 
349
  for ra in real_anchors_c:
350
  if ra not in df_comp.columns: return pd.DataFrame({"Error": [f"비교 열 '{ra}'이 데이터에 없습니다."]})
351
 
 
352
  df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
353
  df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
354
 
 
355
  conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
356
  query_fw = "SELECT Base_section, Comp_section FROM Mapping_table WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?"
357
  df_fw = pd.read_sql(query_fw, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
 
402
  merged['comp_idx'] = merged['comp_idx'].fillna(float('inf'))
403
  merged = merged.sort_values(['base_idx', 'comp_idx'])
404
 
 
405
  result_rows = []
406
  b_cols_renamed = list(rename_b.values())
407
  c_cols_renamed = list(rename_c.values())
 
416
  for c in c_cols_renamed:
417
  row_dict[c] = str(row.get(c)) if has_c and not pd.isna(row.get(c)) else ""
418
 
419
+ # [해결 1] Diff 적용: 원본 컬럼명에 'description'이 포함되어 있으면 모두 적용!
420
+ if has_b and has_c:
421
+ for orig_col in rename_b.keys():
422
+ if 'description' in orig_col.lower():
423
+ b_col_name = rename_b[orig_col]
424
+ c_col_name = rename_c.get(orig_col)
425
+
426
+ if c_col_name and c_col_name in row_dict:
427
+ b_val, c_val = row_dict[b_col_name], row_dict[c_col_name]
428
+ if b_val and c_val and "<img" not in b_val and "<img" not in c_val and b_val != c_val:
429
+ row_dict[b_col_name], row_dict[c_col_name] = generate_html_diff(b_val, c_val)
430
 
431
  result_rows.append(row_dict)
432
 
433
  final_df = pd.DataFrame(result_rows)
434
 
 
435
  final_df = apply_visual_merge(final_df, b_cols_renamed)
436
  final_df = apply_visual_merge(final_df, c_cols_renamed)
437
 
 
503
  css = """
504
  table {
505
  table-layout: fixed !important;
506
+ width: 100% !important;
 
507
  }
508
 
509
+ /* 7열 이상의 다중 열에 대한 기본 Fallback (가로 스크롤 허용) */
510
  th, td {
511
  min-width: 150px;
512
  }
513
 
514
+ /* [해결 2] 작성하셨던 기존 2~6 컬럼 비율 완전 복구 (min-width 무시 설정 추가) */
515
+ /* 2컬럼 */
516
+ table th:nth-last-child(2):first-child, table td:nth-last-child(2):first-child { width: 15% !important; min-width: 0 !important; }
517
+ table th:nth-last-child(1), table td:nth-last-child(1) { width: 85% !important; min-width: 0 !important; }
518
+
519
+ /* 4컬럼 */
520
+ table th:nth-child(1):nth-last-child(4), table td:nth-child(1):nth-last-child(4) { width: 7% !important; min-width: 0 !important; }
521
+ table th:nth-child(2):nth-last-child(3), table td:nth-child(2):nth-last-child(3) { width: 43% !important; min-width: 0 !important; }
522
+ table th:nth-child(3):nth-last-child(2), table td:nth-child(3):nth-last-child(2) { width: 7% !important; min-width: 0 !important; }
523
+ table th:nth-child(4):nth-last-child(1), table td:nth-child(4):nth-last-child(1) { width: 43% !important; min-width: 0 !important; }
524
+
525
+ /* 5컬럼 */
526
+ table th:nth-child(1):nth-last-child(5), table td:nth-child(1):nth-last-child(5) { width: 15% !important; min-width: 0 !important; }
527
+ table th:nth-child(2):nth-last-child(4), table td:nth-child(2):nth-last-child(4) { width: 10% !important; min-width: 0 !important; }
528
+ table th:nth-child(3):nth-last-child(3), table td:nth-child(3):nth-last-child(3) { width: 30% !important; min-width: 0 !important; }
529
+ table th:nth-child(4):nth-last-child(2), table td:nth-child(4):nth-last-child(2) { width: 10% !important; min-width: 0 !important; }
530
+ table th:nth-child(5):nth-last-child(1), table td:nth-child(5):nth-last-child(1) { width: 35% !important; min-width: 0 !important; }
531
+
532
+ /* 6컬럼 */
533
+ table th:nth-child(1):nth-last-child(6), table td:nth-child(1):nth-last-child(6) { width: 8% !important; min-width: 0 !important; }
534
+ table th:nth-child(2):nth-last-child(5), table td:nth-child(2):nth-last-child(5) { width: 25% !important; min-width: 0 !important; }
535
+ table th:nth-child(3):nth-last-child(4), table td:nth-child(3):nth-last-child(4) { width: 8% !important; min-width: 0 !important; }
536
+ table th:nth-child(4):nth-last-child(3), table td:nth-child(4):nth-last-child(3) { width: 25% !important; min-width: 0 !important; }
537
+ table th:nth-child(5):nth-last-child(2), table td:nth-child(5):nth-last-child(2) { width: 8% !important; min-width: 0 !important; }
538
+ table th:nth-child(6):nth-last-child(1), table td:nth-child(6):nth-last-child(1) { width: 26% !important; min-width: 0 !important; }
539
+
540
+ /* 기본 테이블 & 스크롤 설정 */
541
  thead th {
542
  font-size: 18px !important;
543
  position: sticky;