PIA-SPACE_LeaderBoard / ui /tab /leaderboard_tab.py
irfanrah's picture
refactor: 리더보드 메인 탭만 남기고 전부 제거 (Google Sheets 표출 경량화)
3c5a6a0
Raw
History Blame Contribute Delete
9.99 kB
import tempfile
import gradio as gr
from gradio_leaderboard import ColumnFilter, Leaderboard, SearchColumns, SelectColumns
import enviroments.config as config
from sheet_manager.sheet_loader.sheet_cache import (
get_cached_df,
invalidate_cache,
refresh_if_signal_newer,
)
from utils.column_rules import (
AVG_DATASET,
apply_avg_override,
avg_categories,
build_hide_columns,
compute_display_columns,
datasets_for_category,
default_avg_map,
extra_columns,
parse_axes,
)
def _default_display_columns(df):
categories, _ = parse_axes(df.columns)
default_cats = [c for c in config.DEFAULT_CATEGORIES if c in categories]
return compute_display_columns(
list(df.columns), default_cats, config.DEFAULT_DATASETS
)
def leaderboard_tab(demo):
"""Leaderboard 탭.
gradio_leaderboard.Leaderboard 는 __init__ 시점에 headers/select_columns_config 가
frozen 되므로 @gr.render 로 컴포넌트 자체를 재빌드한다.
컬럼 선택은 내장 체크박스(벤치마크 100여 개가 한 줄씩 나열되어 화면을 잡아먹음)
대신 카테고리 × 데이터셋 2축 + 추가 정보 셀렉터로 대체 — allow=False 로 내장
UI 를 숨기고, 선택 결과(selected_state)가 바뀔 때마다 default_selection 에
반영해 재빌드한다. 축 목록은 utils/column_rules.py 가 df.columns 에서 자동
추출하므로 신규 벤치마크 등록 시 별도 설정 없이 셀렉터에 나타난다.
AVG_* 표시값은 시트 값 대신 UI 계산값으로 대체되는데, 계산 기준은
표시용 데이터셋 드롭다운과 완전히 분리된 별도 접힘 섹션(avg_sel)에서만
바뀐다 — 드롭다운으로 뭘 보든 AVG 는 항상 고정된 기준으로 계산된다.
"""
with gr.Tab("🏆Leaderboard"):
with gr.Accordion(
"🗂️ Select Columns to Display", open=True, elem_id="column-selector-acc"
):
with gr.Row():
category_sel = gr.CheckboxGroup(
choices=[],
label="카테고리",
scale=3,
)
dataset_sel = gr.Dropdown(
choices=[],
value=[],
multiselect=True,
label="데이터셋 (비우면 전체)",
scale=2,
)
with gr.Row():
extras_sel = gr.CheckboxGroup(
choices=[],
label="추가 정보",
scale=3,
)
with gr.Column(scale=2):
preset_default_btn = gr.Button("↩️ 기본 보기", size="sm")
preset_all_btn = gr.Button("📋 전체 보기", size="sm")
# AVG 계산 기준: 카테고리별 패널 (좌우 스크롤). 표시용 데이터셋
# 드롭다운과 무관하게 여기서 정한 기준으로만 AVG 가 계산된다.
# 패널 구성(카테고리 수)은 앱 시작 시점 시트 기준으로 고정 —
# AVG_<신규> 컬럼이 시트에 새로 생기면 앱 재시작 필요.
axes_columns = list(get_cached_df().columns)
avg_cat_list = avg_categories(axes_columns)
initial_avg_map = default_avg_map(axes_columns)
with gr.Accordion(
"⚙️ AVG 계산 기준 — 카테고리별 (기본: 정식 벤치마크 세트)",
open=False,
elem_id="avg-basis-acc",
):
avg_sels = {}
with gr.Row(elem_id="avg-basis-row"):
for cat in avg_cat_list:
avg_sels[cat] = gr.CheckboxGroup(
choices=datasets_for_category(axes_columns, cat),
value=initial_avg_map.get(cat, []),
label=f"AVG_{cat}",
)
avg_reset_btn = gr.Button("↩️ 기본값 복원", size="sm")
avg_sel_components = [avg_sels[c] for c in avg_cat_list]
selected_state = gr.State(None)
def _load_axes():
df = get_cached_df()
categories, datasets = parse_axes(df.columns)
default_cats = [c for c in config.DEFAULT_CATEGORIES if c in categories]
dmap = default_avg_map(df.columns)
avg_updates = [
gr.update(
choices=datasets_for_category(df.columns, cat),
value=dmap.get(cat, []),
)
for cat in avg_cat_list
]
return (
gr.update(choices=categories, value=default_cats),
gr.update(
choices=[AVG_DATASET] + datasets, value=config.DEFAULT_DATASETS
),
gr.update(choices=extra_columns(df.columns), value=[]),
*avg_updates,
)
demo.load(
_load_axes,
inputs=[],
outputs=[category_sel, dataset_sel, extras_sel, *avg_sel_components],
)
def _selection_changed(cats, datasets, extras, *avg_values):
df = get_cached_df()
return {
"columns": compute_display_columns(
list(df.columns), cats, datasets, extras
),
"avg": {
cat: list(vals) for cat, vals in zip(avg_cat_list, avg_values)
},
}
for control in (category_sel, dataset_sel, extras_sel, *avg_sel_components):
control.input(
_selection_changed,
[category_sel, dataset_sel, extras_sel, *avg_sel_components],
[selected_state],
)
def _reset_avg(cats, datasets, extras):
df = get_cached_df()
dmap = default_avg_map(df.columns)
updates = [
gr.update(value=dmap.get(cat, [])) for cat in avg_cat_list
]
state = {
"columns": compute_display_columns(
list(df.columns), cats, datasets, extras
),
"avg": dmap,
}
return (*updates, state)
avg_reset_btn.click(
_reset_avg,
inputs=[category_sel, dataset_sel, extras_sel],
outputs=[*avg_sel_components, selected_state],
)
def _apply_preset(categories, datasets, avg_values):
df = get_cached_df()
all_cats, _ = parse_axes(df.columns)
if categories is None:
cats = all_cats
else:
cats = [c for c in categories if c in all_cats]
cols = compute_display_columns(list(df.columns), cats, datasets, [])
return (
gr.update(value=cats),
gr.update(value=datasets),
gr.update(value=[]),
{
"columns": cols,
"avg": {
cat: list(vals) for cat, vals in zip(avg_cat_list, avg_values)
},
},
)
preset_default_btn.click(
lambda *avg: _apply_preset(
config.DEFAULT_CATEGORIES, config.DEFAULT_DATASETS, avg
),
inputs=avg_sel_components,
outputs=[category_sel, dataset_sel, extras_sel, selected_state],
)
preset_all_btn.click(
lambda *avg: _apply_preset(None, [], avg),
inputs=avg_sel_components,
outputs=[category_sel, dataset_sel, extras_sel, selected_state],
)
@gr.render(triggers=[demo.load, selected_state.change], inputs=[selected_state])
def _build_leaderboard(selection):
avg_map = (selection or {}).get("avg") or None
df = apply_avg_override(refresh_if_signal_newer(), avg_map)
selected_cols = (selection or {}).get("columns")
if not selected_cols:
selected_cols = _default_display_columns(df)
leaderboard = Leaderboard(
value=df,
select_columns=SelectColumns(
default_selection=selected_cols,
cant_deselect=config.OFF_LOAD_COLUMNS,
allow=False,
),
search_columns=SearchColumns(
primary_column="Model name",
secondary_columns=["TASK"],
placeholder="Search",
label="Search",
),
hide_columns=build_hide_columns(list(df.columns)),
filter_columns=[ColumnFilter(column="TASK")],
datatype=config.TYPES,
)
df_state = gr.State(value=df)
refresh_button = gr.Button("🔄 Refresh Leaderboard")
download_button = gr.Button("📥 Download Leaderboard (Excel)")
download_file = gr.File(label="Download", visible=False)
def refresh_leaderboard():
invalidate_cache()
fresh = apply_avg_override(get_cached_df(), avg_map)
return fresh, fresh
def download_leaderboard(state_df):
tmp = tempfile.NamedTemporaryFile(
delete=False,
suffix=".xlsx",
prefix="leaderboard_",
dir=tempfile.gettempdir(),
)
tmp.close()
state_df.to_excel(tmp.name, index=False, engine="openpyxl")
return gr.File(value=tmp.name, visible=True)
refresh_button.click(
refresh_leaderboard,
inputs=[],
outputs=[leaderboard, df_state],
)
download_button.click(
download_leaderboard,
inputs=[df_state],
outputs=[download_file],
)