Update app.py
Browse files
app.py
CHANGED
|
@@ -82,22 +82,22 @@ def update_base_category_dropdown(standard, version):
|
|
| 82 |
conn = sqlite3.connect(db_path)
|
| 83 |
tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
|
| 84 |
|
|
|
|
|
|
|
|
|
|
| 85 |
main_table = f"{standard}_{version}"
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
main_table = tables[0] if tables else None
|
| 89 |
|
| 90 |
-
# 1. λΆμ ν
μ΄λΈ(TableA λ±) 리μ€νΈ κ°μ Έμ€κΈ°
|
| 91 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 92 |
-
for t in
|
| 93 |
-
if t == main_table
|
| 94 |
short_name = pattern.sub("", t).strip(" _")
|
| 95 |
if short_name and short_name not in choices:
|
| 96 |
choices.append(short_name)
|
| 97 |
elif t not in choices:
|
| 98 |
choices.append(t)
|
| 99 |
|
| 100 |
-
# 2. λ³Έλ¬Έ μ‘°ν 리μ€νΈ(1. Scope λ±) κ°μ Έμ€κΈ°
|
| 101 |
if main_table:
|
| 102 |
cols = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
|
| 103 |
lower_cols = [c.lower() for c in cols]
|
|
@@ -109,7 +109,6 @@ def update_base_category_dropdown(standard, version):
|
|
| 109 |
for _, row in df.iterrows():
|
| 110 |
ch = str(row[ch_col]).strip()
|
| 111 |
ca = str(row[ca_col]).strip()
|
| 112 |
-
# [볡μ] None, nan κ° νν°λ§
|
| 113 |
if ch and ca and ch.lower() not in ['none', 'nan'] and ca.lower() not in ['none', 'nan']:
|
| 114 |
choices.append(f"{ch}.{ca}")
|
| 115 |
conn.close()
|
|
@@ -120,13 +119,11 @@ def update_base_category_dropdown(standard, version):
|
|
| 120 |
return gr.update(choices=choices, value=None, interactive=True)
|
| 121 |
|
| 122 |
def update_comp_standard_dropdown(base_std, base_ver):
|
| 123 |
-
"""κΈ°μ€ λ²κ· μ ν μ, λ§€νμ΄ νμ©λ μλ Standardλ§ λ°ν"""
|
| 124 |
if not base_std or not base_ver:
|
| 125 |
return gr.Dropdown(choices=[], value=None, interactive=False)
|
| 126 |
|
| 127 |
try:
|
| 128 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 129 |
-
# Registryμ λ±λ‘λ μλ°©ν₯(Base<->Comp) λ§€ν Standardλ§ μΆμΆ
|
| 130 |
query = """
|
| 131 |
SELECT DISTINCT Comp_std FROM Mapping_registry WHERE Base_std=? AND Base_ver=?
|
| 132 |
UNION
|
|
@@ -136,14 +133,11 @@ def update_comp_standard_dropdown(base_std, base_ver):
|
|
| 136 |
conn.close()
|
| 137 |
|
| 138 |
mapped_stds = sorted(df['Comp_std'].dropna().unique().tolist()) if not df.empty else []
|
| 139 |
-
|
| 140 |
-
# λ§€νλ κ²μ΄ μμΌλ©΄ μ ν λΆκ° μνλ‘ λ°ν
|
| 141 |
return gr.update(choices=mapped_stds, value=None, interactive=bool(mapped_stds))
|
| 142 |
except Exception:
|
| 143 |
return gr.update(choices=[], value=None, interactive=False)
|
| 144 |
|
| 145 |
def update_comp_version_dropdown(base_std, base_ver, comp_std):
|
| 146 |
-
"""μ νλ λΉκ΅ Standardμ λν΄, λ§€νμ΄ νμ©λ λ²μ λ§ λ°ν"""
|
| 147 |
if not all([base_std, base_ver, comp_std]):
|
| 148 |
return gr.update(choices=[], value=None, interactive=False)
|
| 149 |
|
|
@@ -166,12 +160,11 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
|
|
| 166 |
if not all([base_std, base_ver, base_cat, comp_std, comp_ver]):
|
| 167 |
return gr.update(choices=[], value=None, interactive=False)
|
| 168 |
|
| 169 |
-
#
|
| 170 |
-
base_type = "Main" if base_cat == "ALL" or
|
| 171 |
|
| 172 |
try:
|
| 173 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 174 |
-
# Registryμμ νμ©λ Comp_Type μ‘°ν
|
| 175 |
query = "SELECT DISTINCT Comp_Type FROM Mapping_registry WHERE Base_std=? AND Base_ver=? AND Base_Type=? AND Comp_std=? AND Comp_ver=?"
|
| 176 |
df = pd.read_sql(query, conn, params=[base_std, base_ver, base_type, comp_std, comp_ver])
|
| 177 |
conn.close()
|
|
@@ -181,8 +174,6 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
|
|
| 181 |
return gr.update(choices=[], value=None)
|
| 182 |
|
| 183 |
final_choices = []
|
| 184 |
-
|
| 185 |
-
# [ν΅μ¬ μμ ] Comp_Typeμ΄ 'Main'μΈ κ²½μ°, μ€μ DBλ₯Ό μ΄μ΄μ ALLκ³Ό μΈλΆ μ‘°νμ κ°μ Έμ΄!
|
| 186 |
if "Main" in allowed_types:
|
| 187 |
final_choices.append("ALL")
|
| 188 |
comp_db_path = os.path.join(UPLOAD_DIR, f"{comp_std}_{comp_ver}.db")
|
|
@@ -190,10 +181,11 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
|
|
| 190 |
if os.path.exists(comp_db_path):
|
| 191 |
c_conn = sqlite3.connect(comp_db_path)
|
| 192 |
c_tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", c_conn)['name'].tolist()
|
|
|
|
| 193 |
|
| 194 |
c_main_table = f"{comp_std}_{comp_ver}"
|
| 195 |
-
if c_main_table not in
|
| 196 |
-
c_main_table =
|
| 197 |
|
| 198 |
if c_main_table:
|
| 199 |
cols = pd.read_sql(f"PRAGMA table_info([{c_main_table}])", c_conn)['name'].tolist()
|
|
@@ -210,10 +202,8 @@ def update_comp_category_dropdown(base_std, base_ver, base_cat, comp_std, comp_v
|
|
| 210 |
final_choices.append(f"{ch}.{ca}")
|
| 211 |
c_conn.close()
|
| 212 |
|
| 213 |
-
# Mainμ΄ μλ λ€λ₯Έ νμ
(TableA λ±)μ κ·Έλλ‘ λͺ©λ‘μ μΆκ°
|
| 214 |
for t in allowed_types:
|
| 215 |
-
if t != "Main":
|
| 216 |
-
final_choices.append(t)
|
| 217 |
|
| 218 |
return gr.update(choices=final_choices, value=final_choices[0] if final_choices else None, interactive=True)
|
| 219 |
|
|
@@ -293,14 +283,18 @@ def fetch_database_records(standard, version, selection, table_type):
|
|
| 293 |
anchor_cols_config = [x.strip() for x in anchor_col_config.split(',')]
|
| 294 |
|
| 295 |
tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
main_table = f"{standard}_{version}"
|
| 297 |
-
if main_table not in
|
| 298 |
-
main_table =
|
| 299 |
|
| 300 |
target_table = None
|
| 301 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 302 |
|
| 303 |
-
for t in
|
| 304 |
if t == selection or pattern.sub("", t).strip(" _") == selection:
|
| 305 |
target_table = t
|
| 306 |
break
|
|
@@ -308,6 +302,9 @@ def fetch_database_records(standard, version, selection, table_type):
|
|
| 308 |
if target_table and target_table != main_table:
|
| 309 |
df = pd.read_sql(f"SELECT * FROM [{target_table}]", conn)
|
| 310 |
else:
|
|
|
|
|
|
|
|
|
|
| 311 |
cursor = conn.cursor()
|
| 312 |
cursor.execute(f"PRAGMA table_info([{main_table}])")
|
| 313 |
cols = [c[1] for c in cursor.fetchall()]
|
|
@@ -361,20 +358,18 @@ def fetch_database_records(standard, version, selection, table_type):
|
|
| 361 |
|
| 362 |
def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat):
|
| 363 |
try:
|
| 364 |
-
# [
|
| 365 |
def get_type_by_cat(cat):
|
| 366 |
-
if not cat: return "Main"
|
| 367 |
-
|
| 368 |
-
return
|
| 369 |
|
| 370 |
type_b = get_type_by_cat(base_cat)
|
| 371 |
type_c = get_type_by_cat(comp_cat)
|
| 372 |
|
| 373 |
-
# κ°κ°μ κΈ°μ€ μ΄(Anchor) μ 보λ₯Ό κ°μ Έμ΄
|
| 374 |
anchor_col_b, _ = get_table_config(type_b)
|
| 375 |
anchor_col_c, _ = get_table_config(type_c)
|
| 376 |
|
| 377 |
-
# μκ°μ μ€λ³΅ μ κ±° (νν¬μ λ°©μ)
|
| 378 |
def apply_visual_merge(df, cols):
|
| 379 |
if not df.empty and len(cols) > 1:
|
| 380 |
is_dup = pd.Series([True] * len(df), index=df.index)
|
|
@@ -386,7 +381,6 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 386 |
df.loc[is_dup, col] = " "
|
| 387 |
return df
|
| 388 |
|
| 389 |
-
# Code/Description κ²°ν© λ‘μ§
|
| 390 |
def combine_code_desc(df):
|
| 391 |
cols = list(df.columns)
|
| 392 |
new_cols = []
|
|
@@ -418,7 +412,7 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 418 |
df = combine_code_desc(df)
|
| 419 |
return apply_visual_merge(df, df.columns)
|
| 420 |
|
| 421 |
-
# 2. λΉκ΅ μ‘°ν
|
| 422 |
if all([base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat]):
|
| 423 |
df_base, real_anchors_b = fetch_database_records(base_std, base_ver, base_cat, type_b)
|
| 424 |
df_comp, real_anchors_c = fetch_database_records(comp_std, comp_ver, comp_cat, type_c)
|
|
@@ -426,11 +420,17 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 426 |
if "Error" in df_base.columns: return df_base
|
| 427 |
if "Error" in df_comp.columns: return df_comp
|
| 428 |
|
| 429 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
|
| 431 |
df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
|
| 432 |
|
| 433 |
-
# λ§€ν λΈλ¦Ώμ§ λ‘λ
|
| 434 |
conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 435 |
q = "SELECT Base_section, Comp_section FROM Mapping_table WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?"
|
| 436 |
df_fw = pd.read_sql(q, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
|
|
@@ -448,12 +448,10 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 448 |
else:
|
| 449 |
df_mapping = pd.DataFrame(columns=['Base_section', 'Comp_section'])
|
| 450 |
|
| 451 |
-
# μμμ λ§€ν (ν€κ° μμ ν λκ°μ κ²½μ°)
|
| 452 |
implicit = pd.DataFrame({'Base_section': list(set(df_base['merge_key']) & set(df_comp['merge_key'])),
|
| 453 |
'Comp_section': list(set(df_base['merge_key']) & set(df_comp['merge_key']))})
|
| 454 |
bridge = pd.concat([df_mapping, implicit], ignore_index=True).drop_duplicates()
|
| 455 |
|
| 456 |
-
# 컬λΌλͺ
μ 리 λ° λ³ν©
|
| 457 |
rename_b = {c: f"{c}_{base_ver}" for c in df_base.columns if c != 'merge_key'}
|
| 458 |
df_base = df_base.rename(columns=rename_b)
|
| 459 |
rename_c = {c: f"{c}_{comp_ver}" for c in df_comp.columns if c != 'merge_key'}
|
|
@@ -462,13 +460,17 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 462 |
df_base['base_idx'], df_comp['comp_idx'] = range(len(df_base)), range(len(df_comp))
|
| 463 |
merged = pd.merge(bridge, df_base, left_on='Base_section', right_on='merge_key', how='outer')
|
| 464 |
merged = pd.merge(merged, df_comp, left_on='Comp_section', right_on='merge_key', how='outer')
|
|
|
|
|
|
|
|
|
|
| 465 |
merged = merged.sort_values(['base_idx', 'comp_idx'])
|
| 466 |
|
| 467 |
-
# κ²°κ³Ό ν 쑰립 λ° Diff
|
| 468 |
result_rows = []
|
| 469 |
for _, row in merged.iterrows():
|
| 470 |
row_dict = {}
|
| 471 |
-
has_b
|
|
|
|
|
|
|
| 472 |
for c in rename_b.values(): row_dict[c] = str(row[c]) if has_b and not pd.isna(row[c]) else ""
|
| 473 |
for c in rename_c.values(): row_dict[c] = str(row[c]) if has_c and not pd.isna(row[c]) else ""
|
| 474 |
|
|
@@ -481,16 +483,24 @@ def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, com
|
|
| 481 |
result_rows.append(row_dict)
|
| 482 |
|
| 483 |
final_df = combine_code_desc(pd.DataFrame(result_rows))
|
| 484 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
|
| 486 |
return pd.DataFrame({"Info": ["쑰건μ μ ννμΈμ."]})
|
| 487 |
except Exception as e:
|
|
|
|
| 488 |
traceback.print_exc()
|
| 489 |
-
return pd.DataFrame({"Error": [str(e)]})
|
| 490 |
|
| 491 |
|
| 492 |
# ==========================================
|
| 493 |
-
# 4. UI Layout & Event Binding
|
| 494 |
# ==========================================
|
| 495 |
with gr.Blocks() as demo:
|
| 496 |
gr.Markdown("# π Regulation Viewer")
|
|
@@ -505,7 +515,6 @@ with gr.Blocks() as demo:
|
|
| 505 |
|
| 506 |
with gr.Accordion("π λΉκ΅ λ²κ·", open=False):
|
| 507 |
with gr.Column():
|
| 508 |
-
# μ΄κΈ°μλ μ νμ§λ₯Ό λΉμλ (κΈ°μ€ λ²κ· μ ν μ κΉμ§ οΏ½οΏ½μ ν ν΅μ )
|
| 509 |
comp_standard = gr.Dropdown(label="Standard", choices=[])
|
| 510 |
comp_version = gr.Dropdown(label="Version", choices=[])
|
| 511 |
comp_category = gr.Dropdown(label="Category", choices=[])
|
|
@@ -514,93 +523,35 @@ with gr.Blocks() as demo:
|
|
| 514 |
search_btn = gr.Button("π μ‘°ν", variant="primary")
|
| 515 |
output_df = gr.Dataframe(wrap=True, interactive=False, datatype="html", max_height=800)
|
| 516 |
|
| 517 |
-
|
| 518 |
# --------------------------------------------------------
|
| 519 |
-
# π μ΄λ²€νΈ λ°μΈλ©
|
| 520 |
# --------------------------------------------------------
|
| 521 |
-
|
| 522 |
-
# [0] μ± μ΄κΈ° λ‘λ
|
| 523 |
demo.load(fn=load_initial_standards, inputs=None, outputs=base_standard)
|
| 524 |
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
# --------------------------------------------------------
|
| 528 |
-
# β κΈ°μ€ Standard μ ν -> κΈ°μ€ Version λͺ©λ‘ κ°±μ
|
| 529 |
-
base_standard.change(
|
| 530 |
-
fn=update_version_dropdown,
|
| 531 |
-
inputs=[base_standard],
|
| 532 |
-
outputs=[base_version]
|
| 533 |
-
)
|
| 534 |
-
# β‘ κΈ°μ€ Version μ ν -> κΈ°μ€ Category λͺ©λ‘ κ°±μ (DB μ 체 λͺ©λ‘)
|
| 535 |
-
base_version.change(
|
| 536 |
-
fn=update_base_category_dropdown,
|
| 537 |
-
inputs=[base_standard, base_version],
|
| 538 |
-
outputs=[base_category]
|
| 539 |
-
)
|
| 540 |
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
# --------------------------------------------------------
|
| 544 |
-
# β’ κΈ°μ€ Versionμ΄ κ²°μ λλ©΄ -> νμ©λ 'λΉκ΅ Standard' λͺ©λ‘ κ°±μ
|
| 545 |
-
base_version.change(
|
| 546 |
-
fn=update_comp_standard_dropdown,
|
| 547 |
-
inputs=[base_standard, base_version],
|
| 548 |
-
outputs=[comp_standard]
|
| 549 |
-
)
|
| 550 |
-
# β£ λΉκ΅ Standard μ ν -> νμ©λ 'λΉκ΅ Version' λͺ©λ‘ κ°±μ
|
| 551 |
-
comp_standard.change(
|
| 552 |
-
fn=update_comp_version_dropdown,
|
| 553 |
-
inputs=[base_standard, base_version, comp_standard],
|
| 554 |
-
outputs=[comp_version]
|
| 555 |
-
)
|
| 556 |
|
| 557 |
-
# --------------------------------------------------------
|
| 558 |
-
# [3] μ΅μ’
μΉ΄ν
κ³ λ¦¬ ν΅μ (Registry νν°λ§)
|
| 559 |
-
# --------------------------------------------------------
|
| 560 |
-
# β€ κΈ°μ€ Categoryκ° λ°λκ±°λ, λΉκ΅ Versionμ΄ λ°λλ©΄ -> νμ©λ 'λΉκ΅ Category' λͺ©λ‘ κ°±μ
|
| 561 |
comp_change_triggers = [base_standard, base_version, base_category, comp_standard, comp_version]
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
fn=update_comp_category_dropdown,
|
| 565 |
-
inputs=comp_change_triggers,
|
| 566 |
-
outputs=[comp_category]
|
| 567 |
-
)
|
| 568 |
-
comp_version.change(
|
| 569 |
-
fn=update_comp_category_dropdown,
|
| 570 |
-
inputs=comp_change_triggers,
|
| 571 |
-
outputs=[comp_category]
|
| 572 |
-
)
|
| 573 |
|
| 574 |
-
# --------------------------------------------------------
|
| 575 |
-
# [4] λ²νΌ λμ (Action)
|
| 576 |
-
# --------------------------------------------------------
|
| 577 |
search_btn.click(
|
| 578 |
fn=execute_unified_search,
|
| 579 |
inputs=[base_standard, base_version, base_category, comp_standard, comp_version, comp_category],
|
| 580 |
outputs=[output_df]
|
| 581 |
)
|
| 582 |
|
| 583 |
-
base_reset_btn.click(
|
| 584 |
-
|
| 585 |
-
inputs=None,
|
| 586 |
-
outputs=[base_standard, base_version, base_category]
|
| 587 |
-
)
|
| 588 |
-
|
| 589 |
-
comp_reset_btn.click(
|
| 590 |
-
fn=reset_comp_selections,
|
| 591 |
-
inputs=None,
|
| 592 |
-
outputs=[comp_standard, comp_version, comp_category]
|
| 593 |
-
)
|
| 594 |
|
| 595 |
|
| 596 |
# ==========================================
|
| 597 |
# 5. Application Styling (CSS)
|
| 598 |
# ==========================================
|
| 599 |
css = """
|
| 600 |
-
/* ----------------------------------------------------
|
| 601 |
-
[1] κΈ°λ³Έ μ€μ (7μ΄ μ΄μ λ€μ€ μ΄ κΈ°μ€)
|
| 602 |
-
- μ΄μ΄ λ§μ κ²½μ° 100%λ₯Ό λμ΄μ μμ°μ€λ½κ² μ€ν¬λ‘€λλλ‘ μ€μ
|
| 603 |
-
---------------------------------------------------- */
|
| 604 |
table {
|
| 605 |
table-layout: auto !important;
|
| 606 |
width: max-content !important;
|
|
@@ -608,13 +559,9 @@ table {
|
|
| 608 |
}
|
| 609 |
|
| 610 |
th, td {
|
| 611 |
-
min-width: 150px;
|
| 612 |
}
|
| 613 |
|
| 614 |
-
/* ----------------------------------------------------
|
| 615 |
-
[2] 2, 4, 5, 6μ΄ μ‘°κ±΄λΆ μ€μ (μ§μ νμ λΉμ¨ μλ²½ 볡ꡬ)
|
| 616 |
-
- ν΄λΉ μ΄ κ°μμΌ λλ§ κ³ μ λ μ΄μμ(fixed) μ μ©
|
| 617 |
-
---------------------------------------------------- */
|
| 618 |
table:has(th:nth-last-child(2):first-child),
|
| 619 |
table:has(th:nth-last-child(4):first-child),
|
| 620 |
table:has(th:nth-last-child(5):first-child),
|
|
@@ -623,24 +570,20 @@ table:has(th:nth-last-child(6):first-child) {
|
|
| 623 |
width: 100% !important;
|
| 624 |
}
|
| 625 |
|
| 626 |
-
/* 2μ»¬λΌ */
|
| 627 |
table th:nth-last-child(2):first-child, table td:nth-last-child(2):first-child { width: 15% !important; min-width: 0 !important; }
|
| 628 |
table th:nth-last-child(1), table td:nth-last-child(1) { width: 85% !important; min-width: 0 !important; }
|
| 629 |
|
| 630 |
-
/* 4μ»¬λΌ */
|
| 631 |
table th:nth-child(1):nth-last-child(4), table td:nth-child(1):nth-last-child(4) { width: 10% !important; min-width: 0 !important; }
|
| 632 |
table th:nth-child(2):nth-last-child(3), table td:nth-child(2):nth-last-child(3) { width: 40% !important; min-width: 0 !important; }
|
| 633 |
table th:nth-child(3):nth-last-child(2), table td:nth-child(3):nth-last-child(2) { width: 10% !important; min-width: 0 !important; }
|
| 634 |
table th:nth-child(4):nth-last-child(1), table td:nth-child(4):nth-last-child(1) { width: 40% !important; min-width: 0 !important; }
|
| 635 |
|
| 636 |
-
/* 5μ»¬λΌ */
|
| 637 |
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; }
|
| 638 |
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; }
|
| 639 |
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; }
|
| 640 |
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; }
|
| 641 |
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; }
|
| 642 |
|
| 643 |
-
/* 6μ»¬λΌ */
|
| 644 |
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; }
|
| 645 |
table th:nth-child(2):nth-last-child(5), table td:nth-child(2):nth-last-child(5) { width: 15% !important; min-width: 0 !important; }
|
| 646 |
table th:nth-child(3):nth-last-child(4), table td:nth-child(3):nth-last-child(4) { width: 27% !important; min-width: 0 !important; }
|
|
@@ -648,9 +591,6 @@ table th:nth-child(4):nth-last-child(3), table td:nth-child(4):nth-last-child(3)
|
|
| 648 |
table th:nth-child(5):nth-last-child(2), table td:nth-child(5):nth-last-child(2) { width: 15% !important; min-width: 0 !important; }
|
| 649 |
table th:nth-child(6):nth-last-child(1), table td:nth-child(6):nth-last-child(1) { width: 27% !important; min-width: 0 !important; }
|
| 650 |
|
| 651 |
-
/* ----------------------------------------------------
|
| 652 |
-
[3] λ°μ΄ν°νλ μ λ° ν€λ κΈ°λ³Έ μ€ν¬λ‘€/ν
μ€νΈ μ€μ
|
| 653 |
-
---------------------------------------------------- */
|
| 654 |
thead th {
|
| 655 |
font-size: 18px !important;
|
| 656 |
position: sticky;
|
|
@@ -661,7 +601,7 @@ thead th {
|
|
| 661 |
.dataframe {
|
| 662 |
max-height: none !important;
|
| 663 |
overflow-y: visible !important;
|
| 664 |
-
overflow-x: auto !important;
|
| 665 |
display: block;
|
| 666 |
}
|
| 667 |
.dataframe > div {
|
|
@@ -671,7 +611,7 @@ thead th {
|
|
| 671 |
td {
|
| 672 |
font-size: 18px !important;
|
| 673 |
white-space: pre-wrap !important;
|
| 674 |
-
word-break: keep-all !important;
|
| 675 |
line-height: 1.6;
|
| 676 |
padding: 10px;
|
| 677 |
vertical-align: top !important;
|
|
|
|
| 82 |
conn = sqlite3.connect(db_path)
|
| 83 |
tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
|
| 84 |
|
| 85 |
+
# [ν¨μΉ] μμ€ν
/μ€μ ν
μ΄λΈ μλ²½ μ μΈ
|
| 86 |
+
valid_tables = [t for t in tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
|
| 87 |
+
|
| 88 |
main_table = f"{standard}_{version}"
|
| 89 |
+
if main_table not in valid_tables:
|
| 90 |
+
main_table = valid_tables[0] if valid_tables else None
|
|
|
|
| 91 |
|
|
|
|
| 92 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 93 |
+
for t in valid_tables:
|
| 94 |
+
if t == main_table: continue
|
| 95 |
short_name = pattern.sub("", t).strip(" _")
|
| 96 |
if short_name and short_name not in choices:
|
| 97 |
choices.append(short_name)
|
| 98 |
elif t not in choices:
|
| 99 |
choices.append(t)
|
| 100 |
|
|
|
|
| 101 |
if main_table:
|
| 102 |
cols = pd.read_sql(f"PRAGMA table_info([{main_table}])", conn)['name'].tolist()
|
| 103 |
lower_cols = [c.lower() for c in cols]
|
|
|
|
| 109 |
for _, row in df.iterrows():
|
| 110 |
ch = str(row[ch_col]).strip()
|
| 111 |
ca = str(row[ca_col]).strip()
|
|
|
|
| 112 |
if ch and ca and ch.lower() not in ['none', 'nan'] and ca.lower() not in ['none', 'nan']:
|
| 113 |
choices.append(f"{ch}.{ca}")
|
| 114 |
conn.close()
|
|
|
|
| 119 |
return gr.update(choices=choices, value=None, interactive=True)
|
| 120 |
|
| 121 |
def update_comp_standard_dropdown(base_std, base_ver):
|
|
|
|
| 122 |
if not base_std or not base_ver:
|
| 123 |
return gr.Dropdown(choices=[], value=None, interactive=False)
|
| 124 |
|
| 125 |
try:
|
| 126 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
|
|
|
| 127 |
query = """
|
| 128 |
SELECT DISTINCT Comp_std FROM Mapping_registry WHERE Base_std=? AND Base_ver=?
|
| 129 |
UNION
|
|
|
|
| 133 |
conn.close()
|
| 134 |
|
| 135 |
mapped_stds = sorted(df['Comp_std'].dropna().unique().tolist()) if not df.empty else []
|
|
|
|
|
|
|
| 136 |
return gr.update(choices=mapped_stds, value=None, interactive=bool(mapped_stds))
|
| 137 |
except Exception:
|
| 138 |
return gr.update(choices=[], value=None, interactive=False)
|
| 139 |
|
| 140 |
def update_comp_version_dropdown(base_std, base_ver, comp_std):
|
|
|
|
| 141 |
if not all([base_std, base_ver, comp_std]):
|
| 142 |
return gr.update(choices=[], value=None, interactive=False)
|
| 143 |
|
|
|
|
| 160 |
if not all([base_std, base_ver, base_cat, comp_std, comp_ver]):
|
| 161 |
return gr.update(choices=[], value=None, interactive=False)
|
| 162 |
|
| 163 |
+
# [ν¨μΉ] κ΅λ¬Έ μ‘°ν("μ 1μ₯.μ΄μΉ") λ±λ μλ²½ν MainμΌλ‘ νλ³
|
| 164 |
+
base_type = "Main" if not base_cat or base_cat == "ALL" or "." in base_cat else base_cat
|
| 165 |
|
| 166 |
try:
|
| 167 |
conn = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
|
|
|
| 168 |
query = "SELECT DISTINCT Comp_Type FROM Mapping_registry WHERE Base_std=? AND Base_ver=? AND Base_Type=? AND Comp_std=? AND Comp_ver=?"
|
| 169 |
df = pd.read_sql(query, conn, params=[base_std, base_ver, base_type, comp_std, comp_ver])
|
| 170 |
conn.close()
|
|
|
|
| 174 |
return gr.update(choices=[], value=None)
|
| 175 |
|
| 176 |
final_choices = []
|
|
|
|
|
|
|
| 177 |
if "Main" in allowed_types:
|
| 178 |
final_choices.append("ALL")
|
| 179 |
comp_db_path = os.path.join(UPLOAD_DIR, f"{comp_std}_{comp_ver}.db")
|
|
|
|
| 181 |
if os.path.exists(comp_db_path):
|
| 182 |
c_conn = sqlite3.connect(comp_db_path)
|
| 183 |
c_tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", c_conn)['name'].tolist()
|
| 184 |
+
c_valid_tables = [t for t in c_tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
|
| 185 |
|
| 186 |
c_main_table = f"{comp_std}_{comp_ver}"
|
| 187 |
+
if c_main_table not in c_valid_tables:
|
| 188 |
+
c_main_table = c_valid_tables[0] if c_valid_tables else None
|
| 189 |
|
| 190 |
if c_main_table:
|
| 191 |
cols = pd.read_sql(f"PRAGMA table_info([{c_main_table}])", c_conn)['name'].tolist()
|
|
|
|
| 202 |
final_choices.append(f"{ch}.{ca}")
|
| 203 |
c_conn.close()
|
| 204 |
|
|
|
|
| 205 |
for t in allowed_types:
|
| 206 |
+
if t != "Main": final_choices.append(t)
|
|
|
|
| 207 |
|
| 208 |
return gr.update(choices=final_choices, value=final_choices[0] if final_choices else None, interactive=True)
|
| 209 |
|
|
|
|
| 283 |
anchor_cols_config = [x.strip() for x in anchor_col_config.split(',')]
|
| 284 |
|
| 285 |
tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table';", conn)['name'].tolist()
|
| 286 |
+
|
| 287 |
+
# [ν¨μΉ] SQLite μμ€ν
ν
μ΄λΈ 무μ λ‘μ§ μ μ©
|
| 288 |
+
valid_tables = [t for t in tables if t.lower() not in ['mapping_registry', 'mapping_table', 'table_config', 'sqlite_sequence']]
|
| 289 |
+
|
| 290 |
main_table = f"{standard}_{version}"
|
| 291 |
+
if main_table not in valid_tables:
|
| 292 |
+
main_table = valid_tables[0] if valid_tables else None
|
| 293 |
|
| 294 |
target_table = None
|
| 295 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 296 |
|
| 297 |
+
for t in valid_tables:
|
| 298 |
if t == selection or pattern.sub("", t).strip(" _") == selection:
|
| 299 |
target_table = t
|
| 300 |
break
|
|
|
|
| 302 |
if target_table and target_table != main_table:
|
| 303 |
df = pd.read_sql(f"SELECT * FROM [{target_table}]", conn)
|
| 304 |
else:
|
| 305 |
+
if not main_table:
|
| 306 |
+
return pd.DataFrame({"Error": ["λ°μ΄ν°λ² μ΄μ€ λ΄μμ λ©μΈ ν
μ΄λΈμ μ°Ύμ μ μμ΅λλ€."]}), []
|
| 307 |
+
|
| 308 |
cursor = conn.cursor()
|
| 309 |
cursor.execute(f"PRAGMA table_info([{main_table}])")
|
| 310 |
cols = [c[1] for c in cursor.fetchall()]
|
|
|
|
| 358 |
|
| 359 |
def execute_unified_search(base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat):
|
| 360 |
try:
|
| 361 |
+
# [ν¨μΉ] κ΅λ¬Έ μ‘°ν μλ³ κ°ν
|
| 362 |
def get_type_by_cat(cat):
|
| 363 |
+
if not cat or cat == "ALL": return "Main"
|
| 364 |
+
if "." in cat: return "Main"
|
| 365 |
+
return cat
|
| 366 |
|
| 367 |
type_b = get_type_by_cat(base_cat)
|
| 368 |
type_c = get_type_by_cat(comp_cat)
|
| 369 |
|
|
|
|
| 370 |
anchor_col_b, _ = get_table_config(type_b)
|
| 371 |
anchor_col_c, _ = get_table_config(type_c)
|
| 372 |
|
|
|
|
| 373 |
def apply_visual_merge(df, cols):
|
| 374 |
if not df.empty and len(cols) > 1:
|
| 375 |
is_dup = pd.Series([True] * len(df), index=df.index)
|
|
|
|
| 381 |
df.loc[is_dup, col] = " "
|
| 382 |
return df
|
| 383 |
|
|
|
|
| 384 |
def combine_code_desc(df):
|
| 385 |
cols = list(df.columns)
|
| 386 |
new_cols = []
|
|
|
|
| 412 |
df = combine_code_desc(df)
|
| 413 |
return apply_visual_merge(df, df.columns)
|
| 414 |
|
| 415 |
+
# 2. λΉκ΅ μ‘°ν
|
| 416 |
if all([base_std, base_ver, base_cat, comp_std, comp_ver, comp_cat]):
|
| 417 |
df_base, real_anchors_b = fetch_database_records(base_std, base_ver, base_cat, type_b)
|
| 418 |
df_comp, real_anchors_c = fetch_database_records(comp_std, comp_ver, comp_cat, type_c)
|
|
|
|
| 420 |
if "Error" in df_base.columns: return df_base
|
| 421 |
if "Error" in df_comp.columns: return df_comp
|
| 422 |
|
| 423 |
+
# [ν¨μΉ] Anchor κ²°μΈ‘ μ UI μΉνμ μλ¬ λ©μμ§ λ³΅μ
|
| 424 |
+
for ra in real_anchors_b:
|
| 425 |
+
if ra not in df_base.columns:
|
| 426 |
+
return pd.DataFrame({"Error": [f"κΈ°μ€ μ΄(Anchor) '{ra}'μ΄(κ°) κΈ°μ€ λ°μ΄ν°μ μ‘΄μ¬νμ§ μμ΅λλ€. Table_Configλ₯Ό νμΈνμΈμ."]})
|
| 427 |
+
for ra in real_anchors_c:
|
| 428 |
+
if ra not in df_comp.columns:
|
| 429 |
+
return pd.DataFrame({"Error": [f"λΉκ΅ μ΄(Anchor) '{ra}'μ΄(κ°) λΉκ΅ λ°μ΄ν°μ μ‘΄μ¬νμ§ μμ΅λλ€. Table_Configλ₯Ό νμΈνμΈμ."]})
|
| 430 |
+
|
| 431 |
df_base['merge_key'] = df_base[real_anchors_b].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
|
| 432 |
df_comp['merge_key'] = df_comp[real_anchors_c].apply(lambda row: '-'.join(row.values.astype(str)), axis=1).str.replace(" ", "")
|
| 433 |
|
|
|
|
| 434 |
conn_map = sqlite3.connect(os.path.join(UPLOAD_DIR, "mapping.db"))
|
| 435 |
q = "SELECT Base_section, Comp_section FROM Mapping_table WHERE TRIM(Base_std)=? AND TRIM(Base_ver)=? AND TRIM(Comp_std)=? AND TRIM(Comp_ver)=?"
|
| 436 |
df_fw = pd.read_sql(q, conn_map, params=[base_std.strip(), base_ver.strip(), comp_std.strip(), comp_ver.strip()])
|
|
|
|
| 448 |
else:
|
| 449 |
df_mapping = pd.DataFrame(columns=['Base_section', 'Comp_section'])
|
| 450 |
|
|
|
|
| 451 |
implicit = pd.DataFrame({'Base_section': list(set(df_base['merge_key']) & set(df_comp['merge_key'])),
|
| 452 |
'Comp_section': list(set(df_base['merge_key']) & set(df_comp['merge_key']))})
|
| 453 |
bridge = pd.concat([df_mapping, implicit], ignore_index=True).drop_duplicates()
|
| 454 |
|
|
|
|
| 455 |
rename_b = {c: f"{c}_{base_ver}" for c in df_base.columns if c != 'merge_key'}
|
| 456 |
df_base = df_base.rename(columns=rename_b)
|
| 457 |
rename_c = {c: f"{c}_{comp_ver}" for c in df_comp.columns if c != 'merge_key'}
|
|
|
|
| 460 |
df_base['base_idx'], df_comp['comp_idx'] = range(len(df_base)), range(len(df_comp))
|
| 461 |
merged = pd.merge(bridge, df_base, left_on='Base_section', right_on='merge_key', how='outer')
|
| 462 |
merged = pd.merge(merged, df_comp, left_on='Comp_section', right_on='merge_key', how='outer')
|
| 463 |
+
|
| 464 |
+
merged['base_idx'] = merged['base_idx'].fillna(float('inf'))
|
| 465 |
+
merged['comp_idx'] = merged['comp_idx'].fillna(float('inf'))
|
| 466 |
merged = merged.sort_values(['base_idx', 'comp_idx'])
|
| 467 |
|
|
|
|
| 468 |
result_rows = []
|
| 469 |
for _, row in merged.iterrows():
|
| 470 |
row_dict = {}
|
| 471 |
+
has_b = not pd.isna(row.get('base_idx')) and row.get('base_idx') != float('inf')
|
| 472 |
+
has_c = not pd.isna(row.get('comp_idx')) and row.get('comp_idx') != float('inf')
|
| 473 |
+
|
| 474 |
for c in rename_b.values(): row_dict[c] = str(row[c]) if has_b and not pd.isna(row[c]) else ""
|
| 475 |
for c in rename_c.values(): row_dict[c] = str(row[c]) if has_c and not pd.isna(row[c]) else ""
|
| 476 |
|
|
|
|
| 483 |
result_rows.append(row_dict)
|
| 484 |
|
| 485 |
final_df = combine_code_desc(pd.DataFrame(result_rows))
|
| 486 |
+
|
| 487 |
+
b_cols_final = [c for c in final_df.columns if c.endswith(f"_{base_ver}")]
|
| 488 |
+
c_cols_final = [c for c in final_df.columns if c.endswith(f"_{comp_ver}")]
|
| 489 |
+
|
| 490 |
+
final_df = apply_visual_merge(final_df, b_cols_final)
|
| 491 |
+
final_df = apply_visual_merge(final_df, c_cols_final)
|
| 492 |
+
|
| 493 |
+
return final_df
|
| 494 |
|
| 495 |
return pd.DataFrame({"Info": ["쑰건μ μ ννμΈμ."]})
|
| 496 |
except Exception as e:
|
| 497 |
+
import traceback
|
| 498 |
traceback.print_exc()
|
| 499 |
+
return pd.DataFrame({"Error": [f"μμ€ν
μ€λ₯ λ°μ: {str(e)}"]})
|
| 500 |
|
| 501 |
|
| 502 |
# ==========================================
|
| 503 |
+
# 4. UI Layout & Event Binding
|
| 504 |
# ==========================================
|
| 505 |
with gr.Blocks() as demo:
|
| 506 |
gr.Markdown("# π Regulation Viewer")
|
|
|
|
| 515 |
|
| 516 |
with gr.Accordion("π λΉκ΅ λ²κ·", open=False):
|
| 517 |
with gr.Column():
|
|
|
|
| 518 |
comp_standard = gr.Dropdown(label="Standard", choices=[])
|
| 519 |
comp_version = gr.Dropdown(label="Version", choices=[])
|
| 520 |
comp_category = gr.Dropdown(label="Category", choices=[])
|
|
|
|
| 523 |
search_btn = gr.Button("π μ‘°ν", variant="primary")
|
| 524 |
output_df = gr.Dataframe(wrap=True, interactive=False, datatype="html", max_height=800)
|
| 525 |
|
|
|
|
| 526 |
# --------------------------------------------------------
|
| 527 |
+
# π μ΄λ²€νΈ λ°μΈλ©
|
| 528 |
# --------------------------------------------------------
|
|
|
|
|
|
|
| 529 |
demo.load(fn=load_initial_standards, inputs=None, outputs=base_standard)
|
| 530 |
|
| 531 |
+
base_standard.change(fn=update_version_dropdown, inputs=[base_standard], outputs=[base_version])
|
| 532 |
+
base_version.change(fn=update_base_category_dropdown, inputs=[base_standard, base_version], outputs=[base_category])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 533 |
|
| 534 |
+
base_version.change(fn=update_comp_standard_dropdown, inputs=[base_standard, base_version], outputs=[comp_standard])
|
| 535 |
+
comp_standard.change(fn=update_comp_version_dropdown, inputs=[base_standard, base_version, comp_standard], outputs=[comp_version])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 536 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 537 |
comp_change_triggers = [base_standard, base_version, base_category, comp_standard, comp_version]
|
| 538 |
+
base_category.change(fn=update_comp_category_dropdown, inputs=comp_change_triggers, outputs=[comp_category])
|
| 539 |
+
comp_version.change(fn=update_comp_category_dropdown, inputs=comp_change_triggers, outputs=[comp_category])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
|
|
|
|
|
|
|
|
|
|
| 541 |
search_btn.click(
|
| 542 |
fn=execute_unified_search,
|
| 543 |
inputs=[base_standard, base_version, base_category, comp_standard, comp_version, comp_category],
|
| 544 |
outputs=[output_df]
|
| 545 |
)
|
| 546 |
|
| 547 |
+
base_reset_btn.click(fn=reset_base_selections, inputs=None, outputs=[base_standard, base_version, base_category])
|
| 548 |
+
comp_reset_btn.click(fn=reset_comp_selections, inputs=None, outputs=[comp_standard, comp_version, comp_category])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
|
| 550 |
|
| 551 |
# ==========================================
|
| 552 |
# 5. Application Styling (CSS)
|
| 553 |
# ==========================================
|
| 554 |
css = """
|
|
|
|
|
|
|
|
|
|
|
|
|
| 555 |
table {
|
| 556 |
table-layout: auto !important;
|
| 557 |
width: max-content !important;
|
|
|
|
| 559 |
}
|
| 560 |
|
| 561 |
th, td {
|
| 562 |
+
min-width: 150px;
|
| 563 |
}
|
| 564 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 565 |
table:has(th:nth-last-child(2):first-child),
|
| 566 |
table:has(th:nth-last-child(4):first-child),
|
| 567 |
table:has(th:nth-last-child(5):first-child),
|
|
|
|
| 570 |
width: 100% !important;
|
| 571 |
}
|
| 572 |
|
|
|
|
| 573 |
table th:nth-last-child(2):first-child, table td:nth-last-child(2):first-child { width: 15% !important; min-width: 0 !important; }
|
| 574 |
table th:nth-last-child(1), table td:nth-last-child(1) { width: 85% !important; min-width: 0 !important; }
|
| 575 |
|
|
|
|
| 576 |
table th:nth-child(1):nth-last-child(4), table td:nth-child(1):nth-last-child(4) { width: 10% !important; min-width: 0 !important; }
|
| 577 |
table th:nth-child(2):nth-last-child(3), table td:nth-child(2):nth-last-child(3) { width: 40% !important; min-width: 0 !important; }
|
| 578 |
table th:nth-child(3):nth-last-child(2), table td:nth-child(3):nth-last-child(2) { width: 10% !important; min-width: 0 !important; }
|
| 579 |
table th:nth-child(4):nth-last-child(1), table td:nth-child(4):nth-last-child(1) { width: 40% !important; min-width: 0 !important; }
|
| 580 |
|
|
|
|
| 581 |
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; }
|
| 582 |
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; }
|
| 583 |
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; }
|
| 584 |
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; }
|
| 585 |
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; }
|
| 586 |
|
|
|
|
| 587 |
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; }
|
| 588 |
table th:nth-child(2):nth-last-child(5), table td:nth-child(2):nth-last-child(5) { width: 15% !important; min-width: 0 !important; }
|
| 589 |
table th:nth-child(3):nth-last-child(4), table td:nth-child(3):nth-last-child(4) { width: 27% !important; min-width: 0 !important; }
|
|
|
|
| 591 |
table th:nth-child(5):nth-last-child(2), table td:nth-child(5):nth-last-child(2) { width: 15% !important; min-width: 0 !important; }
|
| 592 |
table th:nth-child(6):nth-last-child(1), table td:nth-child(6):nth-last-child(1) { width: 27% !important; min-width: 0 !important; }
|
| 593 |
|
|
|
|
|
|
|
|
|
|
| 594 |
thead th {
|
| 595 |
font-size: 18px !important;
|
| 596 |
position: sticky;
|
|
|
|
| 601 |
.dataframe {
|
| 602 |
max-height: none !important;
|
| 603 |
overflow-y: visible !important;
|
| 604 |
+
overflow-x: auto !important;
|
| 605 |
display: block;
|
| 606 |
}
|
| 607 |
.dataframe > div {
|
|
|
|
| 611 |
td {
|
| 612 |
font-size: 18px !important;
|
| 613 |
white-space: pre-wrap !important;
|
| 614 |
+
word-break: keep-all !important;
|
| 615 |
line-height: 1.6;
|
| 616 |
padding: 10px;
|
| 617 |
vertical-align: top !important;
|