test_space / ui.py
Kyuho Heo
spacerank
e74285c
import gradio as gr
import src.config as configs
from constants import TAB_NAMES, MODEL_TYPE_MAP, OUTPUT_FORM_MAP
from src.display.formatting import render_leaderboard_html
from src.display.css_html_js import get_leaderboard_table_html, custom_css
import pandas as pd
from constants import LEADERBOARD_REQUIRED_COLUMNS
def render_pretty_leaderboard_html(df):
"""
Renders a pretty leaderboard table using badge and gauge.
Supports both ['Model', 'Score'] and ['Model Name', 'Overall'] columns.
Sorts by score descending and rounds for display.
"""
# Flexible column mapping
col_map = {}
if "Model" in df.columns:
col_map["Model"] = "Model"
elif "Model Name" in df.columns:
col_map["Model"] = "Model Name"
else:
return "<div style='color:red'>DataFrame must have a 'Model' or 'Model Name' column.</div>"
if "Score" in df.columns:
col_map["Score"] = "Score"
elif "Overall" in df.columns:
col_map["Score"] = "Overall"
else:
return "<div style='color:red'>DataFrame must have a 'Score' or 'Overall' column.</div>"
# Example mappings for demonstration (expand as needed)
model_type_map = MODEL_TYPE_MAP
output_form_map = OUTPUT_FORM_MAP
# Copy and rename for uniformity
df2 = df.copy()
df2 = df2.rename(columns={col_map["Model"]: "Model", col_map["Score"]: "Score"})
# ๋งคํ•‘ ์ „ํ›„๋กœ ๋ˆ„๋ฝ๋œ ๋ชจ๋ธ๋ช…์„ ์ถœ๋ ฅ (๋””๋ฒ„๊น…์šฉ)
missing_type = set(df2["Model"]) - set(model_type_map.keys())
missing_output = set(df2["Model"]) - set(output_form_map.keys())
if missing_type:
print("Model Type ๋งคํ•‘ ๋ˆ„๋ฝ:", missing_type)
if missing_output:
print("Output Form ๋งคํ•‘ ๋ˆ„๋ฝ:", missing_output)
# Add badge columns
df2["Model Type"] = df2["Model"].map(model_type_map).fillna("open")
df2["Output Form"] = df2["Model"].map(output_form_map).fillna("normal")
# Drop NA, sort, round
df2 = df2[["Model", "Score", "Model Type", "Output Form"]].dropna()
df2["Score"] = pd.to_numeric(df2["Score"], errors="coerce").round(2)
df2 = df2.sort_values("Score", ascending=False).reset_index(drop=True)
return get_leaderboard_table_html(df2)
def create_leaderboard_tab(df, key, search_leaderboard, update_modelselector_group, update_leaderboard, column_selector_value):
"""
df: DataFrame to display
key: "Category" or "Language"
search_leaderboard, update_modelselector_group, update_leaderboard: handler functions
column_selector_value: default columns to select
"""
with gr.TabItem(
TAB_NAMES[key],
visible=True
):
df_state = gr.State(df)
with gr.Row():
with gr.Column():
search_box = gr.Textbox(label="Search Model by Name")
group_list = df["Group"].unique().tolist()
group_selector = gr.CheckboxGroup(
choices=df["Group"].unique().tolist(),
value=group_list,
label="Select Model Group"
)
# ํ•„์ˆ˜ ์ปฌ๋Ÿผ ํ•ญ์ƒ ํฌํ•จ, ์ฒดํฌ ํ•ด์ œ ๋ถˆ๊ฐ€(disabled)
# ์„ ํƒ์ง€์—์„œ "Model Name", "Group", "Overall" ์ œ์™ธ
exclude_cols = {"Model Name", "Group", "Overall"}
selectable_columns = [col for col in df.columns.tolist()[3:] if col not in exclude_cols]
all_columns = list(dict.fromkeys(LEADERBOARD_REQUIRED_COLUMNS + selectable_columns))
column_selector = gr.CheckboxGroup(
choices=selectable_columns,
value=[col for col in column_selector_value if col in selectable_columns],
label="Select Columns"
)
with gr.Column():
with gr.Accordion("Model List", open=False):
model_group = df["Model Name"].tolist()
model_selector = gr.CheckboxGroup(
choices=df["Model Name"].tolist(),
value=model_group,
label="Select Models"
)
# badge ์ •๋ณด ํฌํ•จ DataFrame ์ƒ์„ฑ (์œ„์ชฝ ํ…Œ์ด๋ธ”์šฉ)
df_badge = df.copy()
# Model ์ปฌ๋Ÿผ๋ช… ํ†ต์ผ
if "Model Name" in df_badge.columns:
df_badge["Model"] = df_badge["Model Name"]
# ์˜ˆ์‹œ ๋งคํ•‘ (์•„๋ž˜์ชฝ๊ณผ ๋™์ผํ•˜๊ฒŒ ํ™•์žฅ)
model_type_map = MODEL_TYPE_MAP
output_form_map = OUTPUT_FORM_MAP
df_badge["Model Type"] = df_badge["Model"].map(model_type_map).fillna("open")
df_badge["Output Form"] = df_badge["Model"].map(output_form_map).fillna("normal")
df_badge = df_badge.sort_values("Overall" if "Overall" in df_badge.columns else "Score", ascending=False).reset_index(drop=True)
df_badge["Rank"] = df_badge.index + 1
# ์ •๋ ฌ ์ƒํƒœ ๊ด€๋ฆฌ์šฉ State (ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑ, ์ดํ›„ ์žฌ์‚ฌ์šฉ)
default_sort_col = "Overall" if "Overall" in df_badge.columns else "Score"
sort_col_state = gr.State(default_sort_col)
sort_asc_state = gr.State(False) # ๋‚ด๋ฆผ์ฐจ์ˆœ์ด ๊ธฐ๋ณธ๊ฐ’
# ์ •๋ ฌ ํ•จ์ˆ˜ (JS์—์„œ ๋„˜๊ธด asc ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ)
def sort_and_render(col, asc, models, columns, df_):
print(f"[sort_and_render] called: col={col}, asc={asc}, models={models}, columns={columns}")
filtered_df = update_leaderboard(models, columns, df_, col, asc)
# ์ •๋ ฌ ์ƒํƒœ๋ฅผ DataFrame์— ์ž„์‹œ๋กœ ์ €์žฅํ•ด ํ—ค๋”์— ๋ฐ˜์˜
filtered_df._sort_col = col
filtered_df._sort_asc = asc
return render_leaderboard_html(filtered_df.round(3)), col, asc
leaderboard_html = render_leaderboard_html(df_badge.round(3))
leaderboard_html_comp = gr.HTML(value=leaderboard_html, elem_id="leaderboard-table")
# ์ •๋ ฌ ํŠธ๋ฆฌ๊ฑฐ์šฉ hidden textbox ์ถ”๊ฐ€
sort_trigger = gr.Textbox(visible=False, elem_id="sort-leaderboard-trigger")
# sort-arrow ํด๋ฆญ ์‹œ ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ value๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” JS ์‚ฝ์ž… (์ •๋ ฌ ๋ฐฉํ–ฅ ํฌํ•จ)
sort_js = """
<script>
(function() {
document.addEventListener('DOMContentLoaded', function() {
const table = document.getElementById('leaderboard-table');
if (!table) return;
table.addEventListener('click', function(e) {
const arrow = e.target.closest('.sort-arrow');
if (arrow) {
const col = arrow.getAttribute('data-col');
const asc = arrow.getAttribute('data-asc');
// ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ value๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ change ์ด๋ฒคํŠธ ๊ฐ•์ œ ๋ฐœ์ƒ
const trigger = document.querySelector('#sort-leaderboard-trigger input');
if (trigger) {
trigger.value = col + '|' + asc + '|' + Date.now();
trigger.dispatchEvent(new Event('input', { bubbles: true }));
trigger.dispatchEvent(new Event('change', { bubbles: true }));
}
}
});
});
})();
</script>
"""
# ์ •๋ ฌ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ์—๋„ update_leaderboard๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก wiring
def sort_trigger_change(col_val, models, columns, df_, prev_col, prev_asc):
print(f"[sort_trigger.change] col_val={col_val}, prev_col={prev_col}, prev_asc={prev_asc}")
col, asc = col_val.split('|')[0], col_val.split('|')[1].lower() == "true"
return sort_and_render(col, asc, models, columns, df_)
sort_trigger.change(
fn=sort_trigger_change,
inputs=[sort_trigger, model_selector, column_selector, df_state, sort_col_state, sort_asc_state],
outputs=[leaderboard_html_comp, sort_col_state, sort_asc_state]
)
# ์ปค์Šคํ…€ JS๋ฅผ ์ƒ๋‹จ ํ…Œ์ด๋ธ”์— ์‚ฝ์ž…
leaderboard_html_comp.style = None # gr.HTML์—๋Š” style ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†์œผ๋ฏ€๋กœ, ์•„๋ž˜์—์„œ ์‚ฝ์ž…
leaderboard_html_comp.value += sort_js
# Pretty leaderboard preview (uses only 'Model' and 'Score' columns)
pretty_html = gr.HTML(value=render_pretty_leaderboard_html(df.round(3)))
# Define change functions for user interaction
# ๋ชจ๋“  UI ์ด๋ฒคํŠธ์—์„œ update_leaderboard โ†’ sort_and_render โ†’ render_leaderboard_html ์ˆœ์œผ๋กœ ๊ฐฑ์‹ 
def filter_and_sort_search(query, df, sort_col, sort_asc):
print(f"[filter_and_sort_search] sort_col={sort_col}, sort_asc={sort_asc}")
filtered_df = search_leaderboard(query, df, sort_col, sort_asc)
# ์ •๋ ฌ ์ƒํƒœ๋ฅผ DataFrame์— ์ž„์‹œ๋กœ ์ €์žฅํ•ด ํ—ค๋”์— ๋ฐ˜์˜
filtered_df._sort_col = sort_col
filtered_df._sort_asc = sort_asc
return render_leaderboard_html(filtered_df), sort_col, sort_asc
def filter_and_sort_model(models, columns, df, sort_col, sort_asc):
print(f"[filter_and_sort_model] sort_col={sort_col}, sort_asc={sort_asc}")
filtered_df = update_leaderboard(models, columns, df, sort_col, sort_asc)
filtered_df._sort_col = sort_col
filtered_df._sort_asc = sort_asc
return render_leaderboard_html(filtered_df), sort_col, sort_asc
def filter_and_sort_column(models, columns, df, sort_col, sort_asc):
print(f"[filter_and_sort_column] sort_col={sort_col}, sort_asc={sort_asc}")
filtered_df = update_leaderboard(models, columns, df, sort_col, sort_asc)
filtered_df._sort_col = sort_col
filtered_df._sort_asc = sort_asc
return render_leaderboard_html(filtered_df), sort_col, sort_asc
search_box.change(
fn=filter_and_sort_search,
inputs=[search_box, df_state, sort_col_state, sort_asc_state],
outputs=[leaderboard_html_comp, sort_col_state, sort_asc_state]
)
group_selector.change(fn=update_modelselector_group, inputs=[group_selector, df_state], outputs=model_selector)
model_selector.change(
fn=filter_and_sort_model,
inputs=[model_selector, column_selector, df_state, sort_col_state, sort_asc_state],
outputs=[leaderboard_html_comp, sort_col_state, sort_asc_state]
)
# column_selector ๋ณ€๊ฒฝ ์‹œ์—๋„ ํ•ญ์ƒ ์ตœ์‹  sort_col, sort_asc๋ฅผ ์œ ์ง€
column_selector.change(
fn=filter_and_sort_column,
inputs=[model_selector, column_selector, df_state, sort_col_state, sort_asc_state],
outputs=[leaderboard_html_comp, sort_col_state, sort_asc_state]
)
return {
"search_box": search_box,
"group_selector": group_selector,
"column_selector": column_selector,
"model_selector": model_selector,
"leaderboard_html_comp": leaderboard_html_comp,
"sort_trigger": sort_trigger,
"df_state": df_state,
"pretty_html": pretty_html
}