QIDNLF commited on
Commit
7db1459
ยท
verified ยท
1 Parent(s): 97a00b7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -53
app.py CHANGED
@@ -234,17 +234,19 @@ def get_table_config(table_type):
234
 
235
  def fetch_database_records(standard, version, selection, table_type):
236
  if not all([standard, version, selection]):
237
- return pd.DataFrame({"Info": ["์„ ํƒ ํ•„์š”"]}), None
238
 
239
  try:
240
  db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
241
  if not os.path.exists(db_path):
242
- return pd.DataFrame({"Error": [f"ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {db_path}"]}), None
243
 
244
  conn = sqlite3.connect(db_path)
245
  conn.text_factory = decode_sqlite_text
246
 
247
  anchor_col_config, display_setting = get_table_config(table_type)
 
 
248
 
249
  tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
250
  main_table = f"{standard}_{version}"
@@ -255,11 +257,7 @@ def fetch_database_records(standard, version, selection, table_type):
255
  pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
256
 
257
  for t in tables:
258
- if t == selection:
259
- target_table = t
260
- break
261
- short_name = pattern.sub("", t).strip(" _")
262
- if short_name == selection:
263
  target_table = t
264
  break
265
 
@@ -289,15 +287,20 @@ def fetch_database_records(standard, version, selection, table_type):
289
 
290
  df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
291
 
292
- # ๋Œ€์†Œ๋ฌธ์ž ๋ฌด์‹œํ•˜๊ณ  ์‹ค์ œ ์•ต์ปค ์ปฌ๋Ÿผ๋ช… ์ฐพ๊ธฐ
293
- real_anchor = next((c for c in df.columns if c.lower() == anchor_col_config.lower()), anchor_col_config)
 
 
 
294
 
295
  if display_setting:
296
  cols_to_show = [c.strip() for c in display_setting.split(',')]
297
  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)]
298
 
299
- if real_anchor in df.columns and real_anchor not in actual_cols_to_show:
300
- actual_cols_to_show.insert(0, real_anchor)
 
 
301
 
302
  if actual_cols_to_show:
303
  df = df[actual_cols_to_show]
@@ -306,12 +309,12 @@ def fetch_database_records(standard, version, selection, table_type):
306
  df[col] = df[col].apply(convert_blob_to_html_img)
307
 
308
  conn.close()
309
- return df, real_anchor
310
 
311
  except Exception as e:
312
  import traceback
313
  traceback.print_exc()
314
- return pd.DataFrame({"Error": [str(e)]}), None
315
 
316
 
317
  def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat):
@@ -319,39 +322,46 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
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
  # ----------------------------------------
323
- # 1. ๋‹จ์ผ ์กฐํšŒ (์›๋ณธ์˜ ์ค‘๋ณต ๊ณต๋ฐฑ ์ฒ˜๋ฆฌ ๋ณต์›)
324
  # ----------------------------------------
325
  if base_std and base_ver and base_cat and (not comp_std or not comp_ver or not comp_cat):
326
  df, _ = fetch_database_records(base_std, base_ver, base_cat, table_type)
327
  if "Error" in df.columns or "Info" in df.columns: return df
328
-
329
- if not df.empty and len(df.columns) > 1:
330
- is_duplicate = pd.Series([True] * len(df), index=df.index)
331
- for col in df.columns[:-1]:
332
- current_col_str = df[col].astype(str).str.strip()
333
- current_match = (current_col_str == current_col_str.shift(1)) & (~current_col_str.isin(["", "nan", "None", " "]))
334
- is_duplicate = is_duplicate & current_match
335
- df.loc[is_duplicate, col] = " "
336
- return df
337
 
338
  # ----------------------------------------
339
- # 2. ๋น„๊ต ์กฐํšŒ (์›๋ณธ์˜ ์™„๋ฒฝํ•œ ๋ธŒ๋ฆฟ์ง€ ๋งคํ•‘ ๋ณต์› + ๋™์  N์—ด)
340
  # ----------------------------------------
341
  if all([base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat]):
342
- df_base, real_anchor_b = fetch_database_records(base_std, base_ver, base_cat, table_type)
343
- df_comp, real_anchor_c = fetch_database_records(comp_std, comp_ver, comp_cat, table_type)
344
 
345
  if "Error" in df_base.columns: return df_base
346
  if "Error" in df_comp.columns: return df_comp
347
- if real_anchor_b not in df_base.columns or real_anchor_c not in df_comp.columns:
348
- return pd.DataFrame({"Error": ["์„ค์ •๋œ ๊ธฐ์ค€ ์—ด(Anchor)์ด ๋ฐ์ดํ„ฐ์— ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."]})
349
 
350
- # [๋ณต์›] ์›๋ณธ์˜ ๋„์–ด์“ฐ๊ธฐ ๋ฌด์‹œ ํ‚ค ์ƒ์„ฑ
351
- df_base['merge_key'] = df_base[real_anchor_b].astype(str).str.replace(" ", "")
352
- df_comp['merge_key'] = df_comp[real_anchor_c].astype(str).str.replace(" ", "")
 
 
 
 
 
353
 
354
- # [๋ณต์›] ์›๋ณธ ๋งคํ•‘ ๋กœ์ง (์ •๋ฐฉํ–ฅ, ์—ญ๋ฐฉํ–ฅ, Explode)
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()])
@@ -365,15 +375,11 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
365
  if not df_mapping.empty:
366
  df_mapping['Base_section'] = df_mapping['Base_section'].astype(str).str.replace('\n', ',').str.replace('\r', '')
367
  df_mapping['Comp_section'] = df_mapping['Comp_section'].astype(str).str.replace('\n', ',').str.replace('\r', '')
368
-
369
  df_mapping['Base_section'] = df_mapping['Base_section'].str.split(',')
370
  df_mapping['Comp_section'] = df_mapping['Comp_section'].str.split(',')
371
-
372
  df_mapping = df_mapping.explode('Base_section').explode('Comp_section')
373
-
374
  df_mapping['Base_section'] = df_mapping['Base_section'].astype(str).str.strip().str.replace(" ", "")
375
  df_mapping['Comp_section'] = df_mapping['Comp_section'].astype(str).str.strip().str.replace(" ", "")
376
-
377
  df_mapping = df_mapping[(df_mapping['Base_section'] != '') & (df_mapping['Base_section'] != 'nan')]
378
  df_mapping = df_mapping[(df_mapping['Comp_section'] != '') & (df_mapping['Comp_section'] != 'nan')]
379
  df_mapping = df_mapping.drop_duplicates()
@@ -390,7 +396,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
390
  df_implicit = pd.DataFrame({'Base_section': list(implicit_keys), 'Comp_section': list(implicit_keys)})
391
  bridge = pd.concat([df_mapping, df_implicit], ignore_index=True)
392
 
393
- # ๋ธŒ๋ฆฟ์ง€ ๋ณ‘ํ•ฉ ์ „ ์ด๋ฆ„ ๋ณ€๊ฒฝ (๋ฒ„์ „๋ณ„ ์ ‘๋ฏธ์‚ฌ ์ถ”๊ฐ€)
394
  rename_b = {c: f"{c}_{base_ver}" for c in df_base.columns if c != 'merge_key'}
395
  df_base = df_base.rename(columns=rename_b)
396
 
@@ -400,7 +405,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
400
  df_base['base_idx'] = range(len(df_base))
401
  df_comp['comp_idx'] = range(len(df_comp))
402
 
403
- # ๋ธŒ๋ฆฟ์ง€๋ฅผ ์ด์šฉํ•ด ์กฐ์ธ
404
  merged = pd.merge(bridge, df_base, left_on='Base_section', right_on='merge_key', how='outer')
405
  merged = pd.merge(merged, df_comp, left_on='Comp_section', right_on='merge_key', how='outer')
406
 
@@ -412,22 +416,17 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
412
  result_rows = []
413
  b_cols_renamed = list(rename_b.values())
414
  c_cols_renamed = list(rename_c.values())
415
-
416
- b_anchor_renamed = f"{real_anchor_b}_{base_ver}"
417
- c_anchor_renamed = f"{real_anchor_c}_{comp_ver}"
418
 
419
  for _, row in merged.iterrows():
420
  row_dict = {}
421
- has_b = not pd.isna(row.get(b_anchor_renamed))
422
- has_c = not pd.isna(row.get(c_anchor_renamed))
423
 
424
  for c in b_cols_renamed:
425
  row_dict[c] = str(row.get(c)) if has_b and not pd.isna(row.get(c)) else ""
426
-
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
- # Main ํ…Œ์ด๋ธ”์ธ ๊ฒฝ์šฐ์—๋งŒ Description์— ํ•œํ•ด Diff ์ ์šฉ
431
  if table_type == "Main" and has_b and has_c:
432
  desc_b_col = next((c for c in b_cols_renamed if 'description' in c.lower()), None)
433
  desc_c_col = next((c for c in c_cols_renamed if 'description' in c.lower()), None)
@@ -435,19 +434,15 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
435
  if desc_b_col and desc_c_col:
436
  b_val, c_val = row_dict[desc_b_col], row_dict[desc_c_col]
437
  if b_val and c_val and "<img" not in b_val and "<img" not in c_val and b_val != c_val:
438
- b_diff, c_diff = generate_html_diff(b_val, c_val)
439
- row_dict[desc_b_col] = b_diff
440
- row_dict[desc_c_col] = c_diff
441
 
442
  result_rows.append(row_dict)
443
 
444
  final_df = pd.DataFrame(result_rows)
445
 
446
- # [๋ณต์›] ์•ต์ปค(Section) ์ค‘๋ณต ์‹œ ๊ณต๋ฐฑ ์ฒ˜๋ฆฌ
447
- if b_anchor_renamed in final_df.columns:
448
- duplicate_mask = final_df.duplicated(subset=[b_anchor_renamed], keep='first') & (final_df[b_anchor_renamed] != "")
449
- for c in b_cols_renamed:
450
- final_df.loc[duplicate_mask, c] = ""
451
 
452
  return final_df
453
 
 
234
 
235
  def fetch_database_records(standard, version, selection, table_type):
236
  if not all([standard, version, selection]):
237
+ return pd.DataFrame({"Info": ["์„ ํƒ ํ•„์š”"]}), []
238
 
239
  try:
240
  db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
241
  if not os.path.exists(db_path):
242
+ return pd.DataFrame({"Error": [f"ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {db_path}"]}), []
243
 
244
  conn = sqlite3.connect(db_path)
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()
252
  main_table = f"{standard}_{version}"
 
257
  pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
258
 
259
  for t in tables:
260
+ if t == selection or pattern.sub("", t).strip(" _") == selection:
 
 
 
 
261
  target_table = t
262
  break
263
 
 
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)
294
+ real_anchors.append(real_ac)
295
 
296
  if display_setting:
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)
304
 
305
  if actual_cols_to_show:
306
  df = df[actual_cols_to_show]
 
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
316
  traceback.print_exc()
317
+ return pd.DataFrame({"Error": [str(e)]}), []
318
 
319
 
320
  def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat):
 
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)
329
+ for col in cols:
330
+ if col in df.columns:
331
+ curr = df[col].astype(str).str.strip()
332
+ match = (curr == curr.shift(1)) & (~curr.isin(["", "nan", "None"]))
333
+ is_dup = is_dup & match
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)
351
 
352
  if "Error" in df_base.columns: return df_base
353
  if "Error" in df_comp.columns: return df_comp
 
 
354
 
355
+ for ra in real_anchors_b:
356
+ if ra not in df_base.columns: return pd.DataFrame({"Error": [f"๊ธฐ์ค€ ์—ด '{ra}'์ด ๋ฐ์ดํ„ฐ์— ์—†์Šต๋‹ˆ๋‹ค."]})
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()])
 
375
  if not df_mapping.empty:
376
  df_mapping['Base_section'] = df_mapping['Base_section'].astype(str).str.replace('\n', ',').str.replace('\r', '')
377
  df_mapping['Comp_section'] = df_mapping['Comp_section'].astype(str).str.replace('\n', ',').str.replace('\r', '')
 
378
  df_mapping['Base_section'] = df_mapping['Base_section'].str.split(',')
379
  df_mapping['Comp_section'] = df_mapping['Comp_section'].str.split(',')
 
380
  df_mapping = df_mapping.explode('Base_section').explode('Comp_section')
 
381
  df_mapping['Base_section'] = df_mapping['Base_section'].astype(str).str.strip().str.replace(" ", "")
382
  df_mapping['Comp_section'] = df_mapping['Comp_section'].astype(str).str.strip().str.replace(" ", "")
 
383
  df_mapping = df_mapping[(df_mapping['Base_section'] != '') & (df_mapping['Base_section'] != 'nan')]
384
  df_mapping = df_mapping[(df_mapping['Comp_section'] != '') & (df_mapping['Comp_section'] != 'nan')]
385
  df_mapping = df_mapping.drop_duplicates()
 
396
  df_implicit = pd.DataFrame({'Base_section': list(implicit_keys), 'Comp_section': list(implicit_keys)})
397
  bridge = pd.concat([df_mapping, df_implicit], ignore_index=True)
398
 
 
399
  rename_b = {c: f"{c}_{base_ver}" for c in df_base.columns if c != 'merge_key'}
400
  df_base = df_base.rename(columns=rename_b)
401
 
 
405
  df_base['base_idx'] = range(len(df_base))
406
  df_comp['comp_idx'] = range(len(df_comp))
407
 
 
408
  merged = pd.merge(bridge, df_base, left_on='Base_section', right_on='merge_key', how='outer')
409
  merged = pd.merge(merged, df_comp, left_on='Comp_section', right_on='merge_key', how='outer')
410
 
 
416
  result_rows = []
417
  b_cols_renamed = list(rename_b.values())
418
  c_cols_renamed = list(rename_c.values())
 
 
 
419
 
420
  for _, row in merged.iterrows():
421
  row_dict = {}
422
+ has_b = not pd.isna(row.get('base_idx')) and row.get('base_idx') != float('inf')
423
+ has_c = not pd.isna(row.get('comp_idx')) and row.get('comp_idx') != float('inf')
424
 
425
  for c in b_cols_renamed:
426
  row_dict[c] = str(row.get(c)) if has_b and not pd.isna(row.get(c)) else ""
 
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)
 
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
 
447
  return final_df
448