Anonumous commited on
Commit
9db6d22
·
1 Parent(s): 23668fb

Update style

Browse files
Files changed (3) hide show
  1. app.py +75 -16
  2. styles.py +26 -6
  3. utils.py +8 -22
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import gradio as gr
2
  import json
3
 
4
- from constants import INTRODUCTION_TEXT
5
  from utils import (
6
  init_repo,
7
  load_data,
@@ -10,10 +10,34 @@ from utils import (
10
  get_metrics_html,
11
  compute_wer_cer,
12
  get_submit_html,
13
- DATASETS,
14
  )
15
  from styles import LEADERBOARD_CSS
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  init_repo()
18
  gr.set_static_paths(paths=["."])
19
 
@@ -22,7 +46,7 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
22
  '<img src="/gradio_api/file=Logo.png" '
23
  'style="display:block; margin:0 auto; width:34%; height:auto;">'
24
  )
25
-
26
  gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
27
 
28
  with gr.Tabs():
@@ -34,16 +58,8 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
34
  with gr.Group():
35
  gr.Markdown("### Песочница: посчитайте WER/CER на своих строках")
36
  with gr.Row():
37
- ref = gr.Textbox(
38
- label="Референсный текст",
39
- placeholder="например: я люблю машинное обучение",
40
- lines=2,
41
- )
42
- hyp = gr.Textbox(
43
- label="Гипотеза (распознанный текст)",
44
- placeholder="например: я люблю мощинное обучение",
45
- lines=2,
46
- )
47
  with gr.Row():
48
  normalize = gr.Checkbox(
49
  value=True,
@@ -88,7 +104,7 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
88
  lines=16,
89
  )
90
 
91
- submit_btn = gr.Button("🚀 Отправить", elem_classes="full-width-btn")
92
  output_msg = gr.HTML()
93
 
94
  def _alert(kind, text):
@@ -102,6 +118,9 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
102
  return (
103
  gr.update(),
104
  _alert("error", "Укажите название модели."),
 
 
 
105
  metrics_str,
106
  )
107
  if not link_ or not (
@@ -112,12 +131,18 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
112
  _alert(
113
  "error", "Ссылка должна начинаться с http:// или https://"
114
  ),
 
 
 
115
  metrics_str,
116
  )
117
  if not lic:
118
  return (
119
  gr.update(),
120
  _alert("error", "Укажите лицензию модели."),
 
 
 
121
  metrics_str,
122
  )
123
  try:
@@ -126,6 +151,9 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
126
  return (
127
  gr.update(),
128
  _alert("error", f"Невалидный JSON метрик: {e}"),
 
 
 
129
  metrics_str,
130
  )
131
  if not isinstance(metrics, dict):
@@ -135,6 +163,9 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
135
  "error",
136
  "Метрики ��олжны быть объектом JSON с датасетами верхнего уровня.",
137
  ),
 
 
 
138
  metrics_str,
139
  )
140
  missing = [ds for ds in DATASETS if ds not in metrics]
@@ -143,12 +174,18 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
143
  return (
144
  gr.update(),
145
  _alert("error", f"Отсутствуют датасеты: {', '.join(missing)}"),
 
 
 
146
  metrics_str,
147
  )
148
  if extra:
149
  return (
150
  gr.update(),
151
  _alert("error", f"Лишние ключи в метриках: {', '.join(extra)}"),
 
 
 
152
  metrics_str,
153
  )
154
  for ds in DATASETS:
@@ -160,6 +197,9 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
160
  "error",
161
  f"{ds}: значение должно быть объектом с полями wer и cer",
162
  ),
 
 
 
163
  metrics_str,
164
  )
165
  for k in ("wer", "cer"):
@@ -168,6 +208,9 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
168
  return (
169
  gr.update(),
170
  _alert("error", f"{ds}: поле {k} должно быть числом"),
 
 
 
171
  metrics_str,
172
  )
173
  if not (0 <= float(v) <= 1):
@@ -177,6 +220,9 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
177
  "error",
178
  f"{ds}: поле {k} должно быть в диапазоне [0, 1]",
179
  ),
 
 
 
180
  metrics_str,
181
  )
182
  payload = json.dumps(
@@ -188,24 +234,37 @@ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
188
  },
189
  ensure_ascii=False,
190
  )
191
- updated_html, status_msg, cleared = process_submit(payload)
192
  if updated_html is None:
193
  msg = status_msg.replace("Ошибка:", "").strip()
194
  return (
195
  gr.update(),
196
  _alert("error", f"Не удалось добавить: {msg}"),
 
 
 
197
  metrics_str,
198
  )
199
  return (
200
  updated_html,
201
  _alert("success", "✅ Результат добавлен в лидерборд."),
202
  "",
 
 
 
203
  )
204
 
205
  submit_btn.click(
206
  build_json_and_submit,
207
  inputs=[model_name, link, license_field, metrics_json],
208
- outputs=[leaderboard_html, output_msg, metrics_json],
 
 
 
 
 
 
 
209
  )
210
 
211
  demo.launch()
 
1
  import gradio as gr
2
  import json
3
 
4
+ from constants import INTRODUCTION_TEXT, DATASETS
5
  from utils import (
6
  init_repo,
7
  load_data,
 
10
  get_metrics_html,
11
  compute_wer_cer,
12
  get_submit_html,
 
13
  )
14
  from styles import LEADERBOARD_CSS
15
 
16
+ ASSETS_HTML = """
17
+ <link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
18
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
19
+ <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
20
+ <script>
21
+ window.initLeaderboardDT = function(){
22
+ try{
23
+ if (!window.jQuery || !$.fn.DataTable) return;
24
+ document.querySelectorAll('.leaderboard-table table:not(.dt-ready)').forEach(function(tbl){
25
+ $(tbl).addClass('dt-ready').DataTable({
26
+ pageLength: 25,
27
+ order: [[3, 'asc']],
28
+ language: { url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/ru.json' }
29
+ });
30
+ });
31
+ }catch(e){}
32
+ };
33
+ const tryInit = () => window.initLeaderboardDT && window.initLeaderboardDT();
34
+ document.addEventListener('DOMContentLoaded', tryInit);
35
+ const mo = new MutationObserver(tryInit);
36
+ mo.observe(document.documentElement, { childList: true, subtree: true });
37
+ setTimeout(tryInit, 300);
38
+ </script>
39
+ """
40
+
41
  init_repo()
42
  gr.set_static_paths(paths=["."])
43
 
 
46
  '<img src="/gradio_api/file=Logo.png" '
47
  'style="display:block; margin:0 auto; width:34%; height:auto;">'
48
  )
49
+ gr.HTML(ASSETS_HTML)
50
  gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
51
 
52
  with gr.Tabs():
 
58
  with gr.Group():
59
  gr.Markdown("### Песочница: посчитайте WER/CER на своих строках")
60
  with gr.Row():
61
+ ref = gr.Textbox(label="Референсный текст", lines=2)
62
+ hyp = gr.Textbox(label="Гипотеза (распознанный текст)", lines=2)
 
 
 
 
 
 
 
 
63
  with gr.Row():
64
  normalize = gr.Checkbox(
65
  value=True,
 
104
  lines=16,
105
  )
106
 
107
+ submit_btn = gr.Button("🚀 Отправить")
108
  output_msg = gr.HTML()
109
 
110
  def _alert(kind, text):
 
118
  return (
119
  gr.update(),
120
  _alert("error", "Укажите название модели."),
121
+ name,
122
+ link_,
123
+ lic,
124
  metrics_str,
125
  )
126
  if not link_ or not (
 
131
  _alert(
132
  "error", "Ссылка должна начинаться с http:// или https://"
133
  ),
134
+ name,
135
+ link_,
136
+ lic,
137
  metrics_str,
138
  )
139
  if not lic:
140
  return (
141
  gr.update(),
142
  _alert("error", "Укажите лицензию модели."),
143
+ name,
144
+ link_,
145
+ lic,
146
  metrics_str,
147
  )
148
  try:
 
151
  return (
152
  gr.update(),
153
  _alert("error", f"Невалидный JSON метрик: {e}"),
154
+ name,
155
+ link_,
156
+ lic,
157
  metrics_str,
158
  )
159
  if not isinstance(metrics, dict):
 
163
  "error",
164
  "Метрики ��олжны быть объектом JSON с датасетами верхнего уровня.",
165
  ),
166
+ name,
167
+ link_,
168
+ lic,
169
  metrics_str,
170
  )
171
  missing = [ds for ds in DATASETS if ds not in metrics]
 
174
  return (
175
  gr.update(),
176
  _alert("error", f"Отсутствуют датасеты: {', '.join(missing)}"),
177
+ name,
178
+ link_,
179
+ lic,
180
  metrics_str,
181
  )
182
  if extra:
183
  return (
184
  gr.update(),
185
  _alert("error", f"Лишние ключи в метриках: {', '.join(extra)}"),
186
+ name,
187
+ link_,
188
+ lic,
189
  metrics_str,
190
  )
191
  for ds in DATASETS:
 
197
  "error",
198
  f"{ds}: значение должно быть объектом с полями wer и cer",
199
  ),
200
+ name,
201
+ link_,
202
+ lic,
203
  metrics_str,
204
  )
205
  for k in ("wer", "cer"):
 
208
  return (
209
  gr.update(),
210
  _alert("error", f"{ds}: поле {k} должно быть числом"),
211
+ name,
212
+ link_,
213
+ lic,
214
  metrics_str,
215
  )
216
  if not (0 <= float(v) <= 1):
 
220
  "error",
221
  f"{ds}: поле {k} должно быть в диапазоне [0, 1]",
222
  ),
223
+ name,
224
+ link_,
225
+ lic,
226
  metrics_str,
227
  )
228
  payload = json.dumps(
 
234
  },
235
  ensure_ascii=False,
236
  )
237
+ updated_html, status_msg, _ = process_submit(payload)
238
  if updated_html is None:
239
  msg = status_msg.replace("Ошибка:", "").strip()
240
  return (
241
  gr.update(),
242
  _alert("error", f"Не удалось добавить: {msg}"),
243
+ name,
244
+ link_,
245
+ lic,
246
  metrics_str,
247
  )
248
  return (
249
  updated_html,
250
  _alert("success", "✅ Результат добавлен в лидерборд."),
251
  "",
252
+ "",
253
+ "",
254
+ "",
255
  )
256
 
257
  submit_btn.click(
258
  build_json_and_submit,
259
  inputs=[model_name, link, license_field, metrics_json],
260
+ outputs=[
261
+ leaderboard_html,
262
+ output_msg,
263
+ model_name,
264
+ link,
265
+ license_field,
266
+ metrics_json,
267
+ ],
268
  )
269
 
270
  demo.launch()
styles.py CHANGED
@@ -1,5 +1,4 @@
1
  LEADERBOARD_CSS = """
2
- /* ====== Leaderboard ====== */
3
  .leaderboard-wrapper { overflow-x: auto; margin-bottom: 40px; }
4
  .leaderboard-table table { width: 100%; border-collapse: collapse; }
5
  .leaderboard-table th, .leaderboard-table td { text-align: center; padding: 8px; }
@@ -15,12 +14,10 @@ LEADERBOARD_CSS = """
15
  .dark .leaderboard-table th { background-color: #21262d; }
16
  .dark .leaderboard-table a { color: #58a6ff; }
17
 
18
- /* ====== Container & Markdown ====== */
19
  .gradio-container { max-width: 1400px; margin: auto; padding: 20px; }
20
  .markdown-text { color: #24292e; padding: 15px; border-radius: 6px; background-color: #f6f8fa; margin-bottom: 20px; }
21
  .dark .markdown-text { color: #c9d1d9; background-color: #161b22; }
22
 
23
- /* ====== Dataset cards ====== */
24
  .datasets-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 20px; }
25
  .dataset-card { background: #f6f8fa; border-radius: 8px; padding: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); transition: transform .2s ease; }
26
  .dataset-card:hover { transform: translateY(-4px); }
@@ -33,7 +30,6 @@ LEADERBOARD_CSS = """
33
  .dark .dataset-card .full-name { color: #a9c4e2; }
34
  .dark .dataset-card .records { background: #0f2a45; color: #9bd1ff; }
35
 
36
- /* ====== Metrics cards ====== */
37
  .metrics-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; margin-bottom: 16px; }
38
  .metric-card { background: #f6f8fa; border-radius: 12px; padding: 14px; box-shadow: 0 2px 5px rgba(0,0,0,0.04); color:#1f2937; }
39
  .metric-card h3 { margin: 0 0 10px; color:#0b63ce; }
@@ -55,7 +51,6 @@ LEADERBOARD_CSS = """
55
  .chip small { font-size: 12px; opacity: .9; }
56
  .dark .chip { background: #0f172a; border-color: #22304a; color:#e5e7eb; }
57
 
58
- /* ====== Submit form ====== */
59
  .submit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
60
  .form-card { background: #f6f8fa; padding: 15px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
61
  .form-card h3 { margin-top: 0; color: #0366d6; }
@@ -63,10 +58,35 @@ LEADERBOARD_CSS = """
63
  .dark .form-card h3 { color: #58a6ff; }
64
  @media (max-width: 900px) { .submit-grid { grid-template-columns: 1fr; } }
65
 
66
- /* ====== Alerts ====== */
67
  .alert { padding:12px 14px; border-radius:8px; margin-top:10px; font-weight:500; }
68
  .alert.success { background:#e6f7ed; color:#0f5132; border:1px solid #b7ebc6; }
69
  .dark .alert.success { background:#0f2a1d; color:#a6f3c2; border-color:#1f5c3a; }
70
  .alert.error { background:#fdecea; color:#842029; border:1px solid #f5c2c7; }
71
  .dark .alert.error { background:#3a0b0e; color:#f5a3aa; border-color:#7a1a21; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  """
 
1
  LEADERBOARD_CSS = """
 
2
  .leaderboard-wrapper { overflow-x: auto; margin-bottom: 40px; }
3
  .leaderboard-table table { width: 100%; border-collapse: collapse; }
4
  .leaderboard-table th, .leaderboard-table td { text-align: center; padding: 8px; }
 
14
  .dark .leaderboard-table th { background-color: #21262d; }
15
  .dark .leaderboard-table a { color: #58a6ff; }
16
 
 
17
  .gradio-container { max-width: 1400px; margin: auto; padding: 20px; }
18
  .markdown-text { color: #24292e; padding: 15px; border-radius: 6px; background-color: #f6f8fa; margin-bottom: 20px; }
19
  .dark .markdown-text { color: #c9d1d9; background-color: #161b22; }
20
 
 
21
  .datasets-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 20px; }
22
  .dataset-card { background: #f6f8fa; border-radius: 8px; padding: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); transition: transform .2s ease; }
23
  .dataset-card:hover { transform: translateY(-4px); }
 
30
  .dark .dataset-card .full-name { color: #a9c4e2; }
31
  .dark .dataset-card .records { background: #0f2a45; color: #9bd1ff; }
32
 
 
33
  .metrics-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; margin-bottom: 16px; }
34
  .metric-card { background: #f6f8fa; border-radius: 12px; padding: 14px; box-shadow: 0 2px 5px rgba(0,0,0,0.04); color:#1f2937; }
35
  .metric-card h3 { margin: 0 0 10px; color:#0b63ce; }
 
51
  .chip small { font-size: 12px; opacity: .9; }
52
  .dark .chip { background: #0f172a; border-color: #22304a; color:#e5e7eb; }
53
 
 
54
  .submit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
55
  .form-card { background: #f6f8fa; padding: 15px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
56
  .form-card h3 { margin-top: 0; color: #0366d6; }
 
58
  .dark .form-card h3 { color: #58a6ff; }
59
  @media (max-width: 900px) { .submit-grid { grid-template-columns: 1fr; } }
60
 
 
61
  .alert { padding:12px 14px; border-radius:8px; margin-top:10px; font-weight:500; }
62
  .alert.success { background:#e6f7ed; color:#0f5132; border:1px solid #b7ebc6; }
63
  .dark .alert.success { background:#0f2a1d; color:#a6f3c2; border-color:#1f5c3a; }
64
  .alert.error { background:#fdecea; color:#842029; border:1px solid #f5c2c7; }
65
  .dark .alert.error { background:#3a0b0e; color:#f5a3aa; border-color:#7a1a21; }
66
+
67
+ .code-block {
68
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
69
+ font-size: 13px;
70
+ padding: 12px 14px;
71
+ border-radius: 8px;
72
+ overflow-x: auto;
73
+ border: 1px solid #ddd;
74
+ background: #f5f5f5;
75
+ color: #111;
76
+ }
77
+ .code-block.json {
78
+ background: #1e1e1e;
79
+ border-color: #333;
80
+ color: #d4d4d4;
81
+ }
82
+ .dark .code-block.json {
83
+ background: #1e1e1e;
84
+ border-color: #333;
85
+ color: #d4d4d4;
86
+ }
87
+ .code-block.json .string { color: #ce9178; }
88
+ .code-block.json .number { color: #b5cea8; }
89
+ .code-block.json .boolean { color: #569cd6; }
90
+ .code-block.json .null { color: #569cd6; }
91
+ .code-block.json .key { color: #9cdcfe; }
92
  """
utils.py CHANGED
@@ -88,21 +88,7 @@ def load_data():
88
  table_html = df.to_html(
89
  escape=False, index=False, classes="display cell-border compact stripe"
90
  )
91
- scripts = """
92
- <link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
93
- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
94
- <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
95
- <script>
96
- $(document).ready(function(){
97
- $('.leaderboard-table table').DataTable({
98
- pageLength: 25,
99
- order: [[3, 'asc']],
100
- language: { url: '//cdn.datatables.net/plug-ins/1.13.4/i18n/ru.json' }
101
- });
102
- });
103
- </script>
104
- """
105
- return f'<div class="leaderboard-wrapper"><div class="leaderboard-table">{table_html}</div></div>{scripts}'
106
  else:
107
  return (
108
  '<div class="leaderboard-wrapper"><div class="leaderboard-table"><table><thead><tr><th>Ранг</th><th>Модель</th><th>Тип модели</th><th>Средний WER ⬇️</th><th>Средний CER ⬇️</th>'
@@ -259,13 +245,13 @@ def get_submit_html():
259
  <div class="form-card">
260
  <h3>Метрики</h3>
261
  <p>Укажите WER и CER для всех датасетов в формате JSON. Значения — от 0 до 1.</p>
262
- <pre>{
263
- "Russian_LibriSpeech": {"wer": 0.1234, "cer": 0.0567},
264
- "Common_Voice_Corpus_22.0": {"wer": 0.2345, "cer": 0.0789},
265
- "Tone_Webinars": {"wer": 0.3456, "cer": 0.0987},
266
- "Tone_Books": {"wer": 0.4567, "cer": 0.1098},
267
- "Tone_Speak": {"wer": 0.5678, "cer": 0.1209},
268
- "Sova_RuDevices": {"wer": 0.6789, "cer": 0.1310}
269
  }</pre>
270
  </div>
271
  </div>
 
88
  table_html = df.to_html(
89
  escape=False, index=False, classes="display cell-border compact stripe"
90
  )
91
+ return f'<div class="leaderboard-wrapper"><div class="leaderboard-table">{table_html}</div></div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  else:
93
  return (
94
  '<div class="leaderboard-wrapper"><div class="leaderboard-table"><table><thead><tr><th>Ранг</th><th>Модель</th><th>Тип модели</th><th>Средний WER ⬇️</th><th>Средний CER ⬇️</th>'
 
245
  <div class="form-card">
246
  <h3>Метрики</h3>
247
  <p>Укажите WER и CER для всех датасетов в формате JSON. Значения — от 0 до 1.</p>
248
+ <pre class="code-block json">{
249
+ <span class="key">"Russian_LibriSpeech"</span>: { <span class="key">"wer"</span>: <span class="number">0.1234</span>, <span class="key">"cer"</span>: <span class="number">0.0567</span> },
250
+ <span class="key">"Common_Voice_Corpus_22.0"</span>: { <span class="key">"wer"</span>: <span class="number">0.2345</span>, <span class="key">"cer"</span>: <span class="number">0.0789</span> },
251
+ <span class="key">"Tone_Webinars"</span>: { <span class="key">"wer"</span>: <span class="number">0.3456</span>, <span class="key">"cer"</span>: <span class="number">0.0987</span> },
252
+ <span class="key">"Tone_Books"</span>: { <span class="key">"wer"</span>: <span class="number">0.4567</span>, <span class="key">"cer"</span>: <span class="number">0.1098</span> },
253
+ <span class="key">"Tone_Speak"</span>: { <span class="key">"wer"</span>: <span class="number">0.5678</span>, <span class="key">"cer"</span>: <span class="number">0.1209</span> },
254
+ <span class="key">"Sova_RuDevices"</span>: { <span class="key">"wer"</span>: <span class="number">0.6789</span>, <span class="key">"cer"</span>: <span class="number">0.1310</span> }
255
  }</pre>
256
  </div>
257
  </div>