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 "
DataFrame must have a 'Model' or 'Model Name' column.
" if "Score" in df.columns: col_map["Score"] = "Score" elif "Overall" in df.columns: col_map["Score"] = "Overall" else: return "
DataFrame must have a 'Score' or 'Overall' column.
" # 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 = """ """ # 정렬 버튼 클릭 시에도 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 }