Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,6 @@ import gradio as gr
|
|
| 3 |
import pandas as pd
|
| 4 |
from math import isnan
|
| 5 |
|
| 6 |
-
# ===== DATA của bạn (có 3 task + Final Result) =====
|
| 7 |
ROWS = [
|
| 8 |
{"Team_name":"Nguyen Quang Thao","vi-law-nli (4-shot)":0.5816,"vi-law-qa (4 shot)":0.8217,"vilaw-syllo scale":0.38,"Final Result":0.5944333333},
|
| 9 |
{"Team_name":"NHK","vi-law-nli (4-shot)":0.9333,"vi-law-qa (4 shot)":0.8683,"vilaw-syllo scale":0.3275,"Final Result":0.7097},
|
|
@@ -20,36 +19,22 @@ BASE_DF = pd.DataFrame(ROWS)
|
|
| 20 |
|
| 21 |
NUM_COLS = ["vi-law-nli (4-shot)", "vi-law-qa (4 shot)", "vilaw-syllo scale", "Final Result"]
|
| 22 |
|
| 23 |
-
# ====== Helpers ======
|
| 24 |
def _prep_df(df: pd.DataFrame) -> pd.DataFrame:
|
| 25 |
out = df.copy()
|
| 26 |
-
# đảm bảo float, làm gọn:
|
| 27 |
for c in NUM_COLS:
|
| 28 |
-
out[c] = pd.to_numeric(out[c], errors="coerce").astype(float)
|
| 29 |
-
out[c] = out[c].round(6)
|
| 30 |
-
# sort mặc định theo Final Result desc
|
| 31 |
out = out.sort_values("Final Result", ascending=False, kind="mergesort").reset_index(drop=True)
|
| 32 |
-
|
| 33 |
-
out.insert(0, "Rank", range(1, len(out) + 1))
|
| 34 |
return out
|
| 35 |
|
| 36 |
def _bar_html(v: float) -> str:
|
| 37 |
if v is None or (isinstance(v, float) and isnan(v)):
|
| 38 |
return "-"
|
| 39 |
pct = max(0.0, min(1.0, float(v))) * 100
|
| 40 |
-
|
| 41 |
-
return f"""
|
| 42 |
-
<div class="cell">
|
| 43 |
-
<div class="bar" style="width:{pct:.2f}%"></div>
|
| 44 |
-
<span class="val">{v:.4f}</span>
|
| 45 |
-
</div>
|
| 46 |
-
"""
|
| 47 |
|
| 48 |
def _render_table(df: pd.DataFrame) -> str:
|
| 49 |
-
|
| 50 |
-
cols = ["Rank", "Team_name"] + NUM_COLS
|
| 51 |
-
df = df[cols].copy()
|
| 52 |
-
# build HTML
|
| 53 |
header = "".join([f"<th>{c}</th>" for c in cols])
|
| 54 |
rows_html = []
|
| 55 |
for _, row in df.iterrows():
|
|
@@ -62,115 +47,58 @@ def _render_table(df: pd.DataFrame) -> str:
|
|
| 62 |
f"<td>{_bar_html(row['Final Result'])}</td>",
|
| 63 |
]
|
| 64 |
rows_html.append(f"<tr>{''.join(tds)}</tr>")
|
| 65 |
-
table =
|
| 66 |
-
<table class="lb-table">
|
| 67 |
-
<thead><tr>{header}</tr></thead>
|
| 68 |
-
<tbody>{"".join(rows_html)}</tbody>
|
| 69 |
-
</table>
|
| 70 |
-
"""
|
| 71 |
-
return table
|
| 72 |
|
| 73 |
-
def _filter_and_sort(
|
| 74 |
-
search: str,
|
| 75 |
-
min_nli: float,
|
| 76 |
-
min_qa: float,
|
| 77 |
-
min_syllo: float,
|
| 78 |
-
quick: str
|
| 79 |
-
) -> pd.DataFrame:
|
| 80 |
df = BASE_DF.copy()
|
| 81 |
-
|
| 82 |
-
# search theo team (case-insensitive, hỗ trợ nhiều từ cách nhau bởi ;)
|
| 83 |
if search:
|
| 84 |
terms = [t.strip() for t in search.split(";") if t.strip()]
|
| 85 |
for t in terms:
|
| 86 |
df = df[df["Team_name"].str.contains(t, case=False, na=False)]
|
| 87 |
-
|
| 88 |
-
# bộ lọc điểm
|
| 89 |
-
if min_nli is not None:
|
| 90 |
-
df = df[df["vi-law-nli (4-shot)"] >= float(min_nli)]
|
| 91 |
-
if min_qa is not None:
|
| 92 |
-
df = df[df["vi-law-qa (4 shot)"] >= float(min_qa)]
|
| 93 |
-
if min_syllo is not None:
|
| 94 |
-
df = df[df["vilaw-syllo scale"] >= float(min_syllo)]
|
| 95 |
-
|
| 96 |
-
# quick filter theo Final Result
|
| 97 |
if quick == "Top 3":
|
| 98 |
-
df = df.sort_values("Final Result", ascending=False
|
| 99 |
elif quick == "Top 5":
|
| 100 |
-
df = df.sort_values("Final Result", ascending=False
|
| 101 |
else:
|
| 102 |
-
df = df.sort_values("Final Result", ascending=False
|
| 103 |
-
|
| 104 |
return _prep_df(df)
|
| 105 |
|
| 106 |
-
def _controller(search,
|
| 107 |
-
df = _filter_and_sort(search,
|
| 108 |
-
|
| 109 |
-
return html, df # trả cả CSV để user tải
|
| 110 |
|
| 111 |
-
# ====== CSS (dark style giống bản vẽ) ======
|
| 112 |
CUSTOM_CSS = """
|
| 113 |
:root {
|
| 114 |
-
--bg: #0e1116;
|
| 115 |
-
--panel: #12161f;
|
| 116 |
-
--text: #e6e6e6;
|
| 117 |
-
--muted: #9aa3b2;
|
| 118 |
-
--accent: #f5c84b;
|
| 119 |
-
--bar: #2ea043;
|
| 120 |
-
--bar-bg: #1f2a36;
|
| 121 |
}
|
| 122 |
-
.gradio-container {background: var(--bg)
|
| 123 |
-
#title { text-align:center; padding: 16px 0 8px; }
|
| 124 |
-
#subtitle { text-align:center; color: var(--muted); margin-bottom: 18px;}
|
| 125 |
-
.controls .gradio-row { gap: 12px; }
|
| 126 |
.lb-table { width:100%; border-collapse: collapse; }
|
| 127 |
-
.lb-table thead th {
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
}
|
| 131 |
-
.
|
| 132 |
-
.
|
| 133 |
-
.
|
| 134 |
-
.lb-table td.team { min-width: 220px; }
|
| 135 |
-
.cell { position: relative; height: 20px; background: var(--bar-bg); border-radius: 6px; overflow: hidden; }
|
| 136 |
-
.cell .bar { position:absolute; left:0; top:0; bottom:0; background: var(--bar); }
|
| 137 |
-
.cell .val { position:absolute; right:8px; top:0; bottom:0; display:flex; align-items:center; font-variant-numeric: tabular-nums; }
|
| 138 |
-
.quick-btn .gr-button { background:#1a2030; border:1px solid #2b3445; }
|
| 139 |
-
.quick-btn .gr-button:hover { filter: brightness(1.1); }
|
| 140 |
-
.footer-note { color: var(--muted); font-size: 0.9rem; text-align:center; }
|
| 141 |
"""
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
gr.Markdown("<h1 id='title'>🏆 VI-Law Leaderboard</h1>", elem_id="title")
|
| 146 |
-
gr.Markdown("<div id='subtitle'>So sánh kết quả theo cách <b>đơn giản & tái lập</b>. Mặc định xếp hạng theo <b>Final Result</b> (giảm dần).</div>", elem_id="subtitle")
|
| 147 |
-
|
| 148 |
-
with gr.Row(elem_classes="controls"):
|
| 149 |
-
search = gr.Textbox(placeholder='Tìm theo tên team. Hỗ trợ nhiều từ; phân tách bằng ";"', label="Search")
|
| 150 |
with gr.Row():
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
min_syllo = gr.Slider(0.0, 1.0, value=None, step=0.01, label="Min vilaw-syllo scale", interactive=True)
|
| 154 |
-
|
| 155 |
-
with gr.Row(elem_classes="quick-btn"):
|
| 156 |
-
quick = gr.Radio(choices=["All", "Top 3", "Top 5"], value="All", label="Quick Filters")
|
| 157 |
-
|
| 158 |
html_table = gr.HTML(value=_render_table(_prep_df(BASE_DF)))
|
| 159 |
-
csv_state = gr.State(_prep_df(BASE_DF))
|
| 160 |
-
download = gr.DownloadButton("⬇️ Tải CSV
|
| 161 |
|
| 162 |
def _download(df):
|
| 163 |
-
path = "/tmp/
|
| 164 |
-
df.to_csv(path,
|
| 165 |
return path
|
| 166 |
-
|
| 167 |
download.click(_download, inputs=[csv_state], outputs=download)
|
| 168 |
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
comp.change(_controller, [search, min_nli, min_qa, min_syllo, quick], [html_table, csv_state])
|
| 172 |
-
|
| 173 |
-
gr.Markdown("<div class='footer-note'>Gợi ý: dùng Quick Filters để xem Top 3 / Top 5 theo Final Result.</div>")
|
| 174 |
|
| 175 |
if __name__ == "__main__":
|
| 176 |
demo.launch()
|
|
|
|
| 3 |
import pandas as pd
|
| 4 |
from math import isnan
|
| 5 |
|
|
|
|
| 6 |
ROWS = [
|
| 7 |
{"Team_name":"Nguyen Quang Thao","vi-law-nli (4-shot)":0.5816,"vi-law-qa (4 shot)":0.8217,"vilaw-syllo scale":0.38,"Final Result":0.5944333333},
|
| 8 |
{"Team_name":"NHK","vi-law-nli (4-shot)":0.9333,"vi-law-qa (4 shot)":0.8683,"vilaw-syllo scale":0.3275,"Final Result":0.7097},
|
|
|
|
| 19 |
|
| 20 |
NUM_COLS = ["vi-law-nli (4-shot)", "vi-law-qa (4 shot)", "vilaw-syllo scale", "Final Result"]
|
| 21 |
|
|
|
|
| 22 |
def _prep_df(df: pd.DataFrame) -> pd.DataFrame:
|
| 23 |
out = df.copy()
|
|
|
|
| 24 |
for c in NUM_COLS:
|
| 25 |
+
out[c] = pd.to_numeric(out[c], errors="coerce").astype(float).round(6)
|
|
|
|
|
|
|
| 26 |
out = out.sort_values("Final Result", ascending=False, kind="mergesort").reset_index(drop=True)
|
| 27 |
+
out.insert(0, "Rank", range(1, len(out)+1))
|
|
|
|
| 28 |
return out
|
| 29 |
|
| 30 |
def _bar_html(v: float) -> str:
|
| 31 |
if v is None or (isinstance(v, float) and isnan(v)):
|
| 32 |
return "-"
|
| 33 |
pct = max(0.0, min(1.0, float(v))) * 100
|
| 34 |
+
return f"<div class='cell'><div class='bar' style='width:{pct:.2f}%'></div><span class='val'>{v:.4f}</span></div>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
def _render_table(df: pd.DataFrame) -> str:
|
| 37 |
+
cols = ["Rank","Team_name"]+NUM_COLS
|
|
|
|
|
|
|
|
|
|
| 38 |
header = "".join([f"<th>{c}</th>" for c in cols])
|
| 39 |
rows_html = []
|
| 40 |
for _, row in df.iterrows():
|
|
|
|
| 47 |
f"<td>{_bar_html(row['Final Result'])}</td>",
|
| 48 |
]
|
| 49 |
rows_html.append(f"<tr>{''.join(tds)}</tr>")
|
| 50 |
+
return f"<table class='lb-table'><thead><tr>{header}</tr></thead><tbody>{''.join(rows_html)}</tbody></table>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
+
def _filter_and_sort(search: str, quick: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
df = BASE_DF.copy()
|
|
|
|
|
|
|
| 54 |
if search:
|
| 55 |
terms = [t.strip() for t in search.split(";") if t.strip()]
|
| 56 |
for t in terms:
|
| 57 |
df = df[df["Team_name"].str.contains(t, case=False, na=False)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
if quick == "Top 3":
|
| 59 |
+
df = df.sort_values("Final Result", ascending=False).head(3)
|
| 60 |
elif quick == "Top 5":
|
| 61 |
+
df = df.sort_values("Final Result", ascending=False).head(5)
|
| 62 |
else:
|
| 63 |
+
df = df.sort_values("Final Result", ascending=False)
|
|
|
|
| 64 |
return _prep_df(df)
|
| 65 |
|
| 66 |
+
def _controller(search, quick):
|
| 67 |
+
df = _filter_and_sort(search, quick)
|
| 68 |
+
return _render_table(df), df
|
|
|
|
| 69 |
|
|
|
|
| 70 |
CUSTOM_CSS = """
|
| 71 |
:root {
|
| 72 |
+
--bg: #0e1116; --panel:#12161f; --text:#e6e6e6; --muted:#9aa3b2; --bar:#2ea043; --bar-bg:#1f2a36;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
}
|
| 74 |
+
.gradio-container {background: var(--bg)!important; color: var(--text);}
|
|
|
|
|
|
|
|
|
|
| 75 |
.lb-table { width:100%; border-collapse: collapse; }
|
| 76 |
+
.lb-table thead th { background: var(--panel); padding: 10px; text-align:left; }
|
| 77 |
+
.lb-table tbody td { padding: 10px; border-bottom: 1px solid #1d2430; }
|
| 78 |
+
.lb-table td.rank { width:60px; font-weight:700; }
|
| 79 |
+
.lb-table td.team { min-width:200px; }
|
| 80 |
+
.cell { position:relative; height:20px; background: var(--bar-bg); border-radius:6px; }
|
| 81 |
+
.cell .bar { position:absolute; left:0; top:0; bottom:0; background: var(--bar);}
|
| 82 |
+
.cell .val { position:absolute; right:8px; top:0; bottom:0; display:flex; align-items:center; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
"""
|
| 84 |
|
| 85 |
+
with gr.Blocks(css=CUSTOM_CSS) as demo:
|
| 86 |
+
gr.Markdown("# 🏆 VI-Law Leaderboard")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
with gr.Row():
|
| 88 |
+
search = gr.Textbox(placeholder="Tìm team...", label="Search")
|
| 89 |
+
quick = gr.Radio(choices=["All","Top 3","Top 5"], value="All", label="Quick Filter")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
html_table = gr.HTML(value=_render_table(_prep_df(BASE_DF)))
|
| 91 |
+
csv_state = gr.State(_prep_df(BASE_DF))
|
| 92 |
+
download = gr.DownloadButton("⬇️ Tải CSV")
|
| 93 |
|
| 94 |
def _download(df):
|
| 95 |
+
path = "/tmp/leaderboard.csv"
|
| 96 |
+
df.to_csv(path,index=False)
|
| 97 |
return path
|
|
|
|
| 98 |
download.click(_download, inputs=[csv_state], outputs=download)
|
| 99 |
|
| 100 |
+
for comp in [search, quick]:
|
| 101 |
+
comp.change(_controller, [search, quick], [html_table, csv_state])
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
if __name__ == "__main__":
|
| 104 |
demo.launch()
|