saneowl commited on
Commit
6f8d5b6
Β·
verified Β·
1 Parent(s): d924da3

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +475 -0
app.py ADDED
@@ -0,0 +1,475 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HF Dataset Explorer β€” view ANY Hugging Face dataset, even when the
3
+ official dataset-viewer is unavailable / stuck / not-yet-built.
4
+
5
+ Run: python app.py (add --share for a public link)
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import json
12
+ import os
13
+ import tempfile
14
+ import traceback
15
+
16
+ import gradio as gr
17
+ import pandas as pd
18
+
19
+ import backend as B
20
+
21
+ STATE_DEFAULT = {"dataset": "", "config": "", "split": "", "df": None,
22
+ "offset": 0, "total": None, "token": ""}
23
+
24
+ EXAMPLES = [
25
+ "stanfordnlp/imdb",
26
+ "openai/gsm8k",
27
+ "cais/mmlu",
28
+ "HuggingFaceFW/fineweb",
29
+ "lmms-lab/COCO-Caption",
30
+ ]
31
+
32
+
33
+ # --------------------------------------------------------------------------- #
34
+ # tab 1: connect
35
+ # --------------------------------------------------------------------------- #
36
+ def connect(raw_id, token):
37
+ try:
38
+ ds = B.normalize_id(raw_id)
39
+ except B.LoadError as e:
40
+ return (gr.update(), gr.update(), f"### ❌ {e}", "", gr.update(choices=[]), {})
41
+
42
+ lines = [f"## `{ds}`", ""]
43
+ try:
44
+ info = B.repo_info(ds, token)
45
+ lines += [
46
+ f"- **Downloads (30d):** {info.get('downloads', '?'):,} Β· **Likes:** {info.get('likes', '?')}",
47
+ f"- **Modified:** {str(info.get('lastModified', ''))[:19]}",
48
+ f"- **Tags:** {', '.join(info.get('tags', [])[:14]) or 'β€”'}",
49
+ f"- **Gated:** {info.get('gated', False)} Β· **Private:** {info.get('private', False)}",
50
+ ]
51
+ except Exception as e: # noqa: BLE001
52
+ lines.append(f"> ⚠️ repo metadata unavailable: {e}")
53
+
54
+ ok, detail = B.viewer_status(ds, token)
55
+ lines += ["",
56
+ f"- **Official viewer:** {'βœ… ready' if ok else 'β›” NOT ready'} β€” `{detail}`",
57
+ f" {'' if ok else 'β†’ no problem, this app falls back to parquet / streaming / raw files.'}"]
58
+
59
+ try:
60
+ mapping = B.discover_splits(ds, token)
61
+ except Exception as e: # noqa: BLE001
62
+ mapping = {"default": ["train"]}
63
+ lines.append(f"> ⚠️ split discovery fell back to guesses: {e}")
64
+
65
+ configs = list(mapping)
66
+ first_cfg = configs[0]
67
+ splits = mapping[first_cfg]
68
+ lines.append(f"- **Configs:** {len(configs)} Β· **Splits in `{first_cfg}`:** {', '.join(splits)}")
69
+
70
+ # file tree
71
+ try:
72
+ files = B.list_files(ds, token)
73
+ tab = pd.DataFrame([{"path": f["path"],
74
+ "size": f.get("size") or (f.get("lfs") or {}).get("size") or 0}
75
+ for f in files if f.get("type") == "file"])
76
+ if not tab.empty:
77
+ tab["size"] = tab["size"].map(lambda n: f"{n/1e6:,.2f} MB" if n else "β€”")
78
+ tabular = [f["path"] for f in files
79
+ if f.get("type") == "file" and f["path"].lower().endswith(B.TABULAR_EXT)]
80
+ except Exception:
81
+ tab, tabular = pd.DataFrame(), []
82
+
83
+ return (gr.update(choices=configs, value=first_cfg),
84
+ gr.update(choices=splits, value=splits[0] if splits else None),
85
+ "\n".join(lines),
86
+ B.readme(ds, token),
87
+ gr.update(choices=tabular[:500], value=tabular[0] if tabular else None),
88
+ {"dataset": ds, "mapping": mapping, "files": tab.to_dict("records")})
89
+
90
+
91
+ def on_config_change(cfg, meta):
92
+ splits = (meta or {}).get("mapping", {}).get(cfg, ["train"])
93
+ return gr.update(choices=splits, value=splits[0] if splits else None)
94
+
95
+
96
+ def file_table(meta):
97
+ recs = (meta or {}).get("files", [])
98
+ return pd.DataFrame(recs) if recs else pd.DataFrame({"path": [], "size": []})
99
+
100
+
101
+ # --------------------------------------------------------------------------- #
102
+ # tab 2: rows
103
+ # --------------------------------------------------------------------------- #
104
+ def _render(res: B.LoadResult, state, maxlen, cols_pick=None):
105
+ df = res.df
106
+ state = dict(state or {})
107
+ state.update(df=df, offset=res.offset, total=res.total_rows,
108
+ strategy=res.strategy)
109
+
110
+ media = B.detect_media_columns(df)
111
+ total = f"{res.total_rows:,}" if res.total_rows else "unknown"
112
+ banner = (f"**Strategy:** `{res.strategy}` Β· **rows {res.offset:,}–{res.offset+len(df)-1:,}** "
113
+ f"of {total} Β· **{len(df.columns)} columns**"
114
+ + (f"\n\n_{' Β· '.join(res.notes)}_" if res.notes else "")
115
+ + (f"\n\nπŸ–ΌοΈ image cols: `{media['image']}`" if media["image"] else "")
116
+ + (f" πŸ”Š audio cols: `{media['audio']}`" if media["audio"] else ""))
117
+
118
+ keep = [c for c in (cols_pick or []) if c in df.columns]
119
+ show = df[keep] if keep else df
120
+ return (B.display_frame(show, maxlen), banner, state,
121
+ gr.update(choices=[str(c) for c in df.columns],
122
+ value=[str(c) for c in df.columns]),
123
+ gr.update(maximum=max(1, len(df) - 1), value=0))
124
+
125
+
126
+ def load_rows(dataset_meta, cfg, split, offset, limit, token, strategy, maxlen, state):
127
+ ds = (dataset_meta or {}).get("dataset", "")
128
+ if not ds:
129
+ return (pd.DataFrame(), "### ❌ Connect to a dataset first (tab 1).", state,
130
+ gr.update(), gr.update())
131
+ try:
132
+ res = B.load_rows(ds, cfg, split, int(offset), int(limit), token, strategy)
133
+ except Exception as e: # noqa: BLE001
134
+ return (pd.DataFrame(), f"### ❌ Could not load rows\n```\n{e}\n```", state,
135
+ gr.update(), gr.update())
136
+ state = dict(state or {}); state["dataset"] = ds
137
+ return _render(res, state, int(maxlen))
138
+
139
+
140
+ def page(delta, dataset_meta, cfg, split, offset, limit, token, strategy, maxlen, state):
141
+ new_off = max(0, int(offset) + delta * int(limit))
142
+ out = load_rows(dataset_meta, cfg, split, new_off, limit, token, strategy, maxlen, state)
143
+ return (*out, new_off)
144
+
145
+
146
+ def _state_df(state) -> pd.DataFrame:
147
+ df = (state or {}).get("df")
148
+ return df if isinstance(df, pd.DataFrame) else pd.DataFrame()
149
+
150
+
151
+ def apply_filter(query, regex_col, regex, state, maxlen):
152
+ df = _state_df(state)
153
+ if df.empty:
154
+ return pd.DataFrame(), "No data loaded."
155
+ note = []
156
+ try:
157
+ if query.strip():
158
+ df = df.query(query, engine="python")
159
+ note.append(f"query `{query}`")
160
+ if regex.strip() and regex_col:
161
+ df = df[df[regex_col].astype(str).str.contains(regex, case=False, regex=True, na=False)]
162
+ note.append(f"regex `{regex}` on `{regex_col}`")
163
+ except Exception as e: # noqa: BLE001
164
+ return pd.DataFrame(), f"❌ filter error: `{e}`"
165
+ return B.display_frame(df, int(maxlen)), f"**{len(df):,} matching rows** ({' + '.join(note) or 'no filter'})"
166
+
167
+
168
+ def pick_columns(cols, state, maxlen):
169
+ df = _state_df(state)
170
+ if df.empty:
171
+ return pd.DataFrame()
172
+ keep = [c for c in df.columns if str(c) in set(cols or [])]
173
+ return B.display_frame(df[keep] if keep else df, int(maxlen))
174
+
175
+
176
+ # --------------------------------------------------------------------------- #
177
+ # tab 3: record inspector
178
+ # --------------------------------------------------------------------------- #
179
+ def inspect(idx, state):
180
+ df = _state_df(state)
181
+ if df.empty:
182
+ return {}, "No data loaded.", None, None
183
+ i = max(0, min(int(idx), len(df) - 1))
184
+ row = df.iloc[i]
185
+ payload = {str(k): B.json_safe(v) for k, v in row.items()}
186
+
187
+ md = [f"### Row {i} (absolute #{(state or {}).get('offset', 0) + i})"]
188
+ for k, v in row.items():
189
+ s = B.stringify(v, 4000)
190
+ md.append(f"**{k}**\n\n```\n{s}\n```")
191
+
192
+ img = aud = None
193
+ media = B.detect_media_columns(df)
194
+ if media["image"]:
195
+ img = B.media_to_display(row[media["image"][0]])
196
+ if media["audio"]:
197
+ aud = B.media_to_display(row[media["audio"][0]])
198
+ return payload, "\n\n".join(md), img, aud
199
+
200
+
201
+ # --------------------------------------------------------------------------- #
202
+ # tab 4: stats
203
+ # --------------------------------------------------------------------------- #
204
+ def profile(state):
205
+ df = _state_df(state)
206
+ if df.empty:
207
+ return pd.DataFrame(), pd.DataFrame(), "No data loaded."
208
+ rows = []
209
+ for c in df.columns:
210
+ s = df[c]
211
+ flat = s.map(lambda v: B.stringify(v, 10_000))
212
+ rows.append({
213
+ "column": str(c),
214
+ "dtype": str(s.dtype),
215
+ "nulls": int(s.isna().sum()),
216
+ "null %": round(100 * s.isna().mean(), 2),
217
+ "unique": int(flat.nunique()),
218
+ "mean len": round(flat.str.len().mean(), 1),
219
+ "max len": int(flat.str.len().max()),
220
+ "example": B.stringify(s.dropna().iloc[0], 80) if s.notna().any() else "",
221
+ })
222
+ schema = pd.DataFrame(rows)
223
+
224
+ num = df.select_dtypes("number")
225
+ desc = num.describe().T.reset_index().rename(columns={"index": "column"}) if not num.empty \
226
+ else pd.DataFrame({"info": ["no numeric columns in this page"]})
227
+
228
+ # label-ish distribution
229
+ md = ["### Value distributions (low-cardinality columns)"]
230
+ for c in df.columns:
231
+ flat = df[c].map(lambda v: B.stringify(v, 60))
232
+ if 1 < flat.nunique() <= 25:
233
+ vc = flat.value_counts()
234
+ md.append(f"**{c}**")
235
+ md += [f"- `{k}` β€” {v} ({100*v/len(flat):.1f}%)" for k, v in vc.items()]
236
+ if len(md) == 1:
237
+ md.append("_none found on this page._")
238
+ return schema, desc, "\n".join(md)
239
+
240
+
241
+ # --------------------------------------------------------------------------- #
242
+ # tab 5: media gallery
243
+ # --------------------------------------------------------------------------- #
244
+ def gallery(state, col, n):
245
+ df = _state_df(state)
246
+ if df.empty:
247
+ return [], "No data loaded."
248
+ media = B.detect_media_columns(df)
249
+ col = col or (media["image"][0] if media["image"] else None)
250
+ if not col or col not in df.columns:
251
+ return [], "No image-like column detected on this page."
252
+ items = []
253
+ for i, v in df[col].head(int(n)).items():
254
+ d = B.media_to_display(v)
255
+ if d is not None:
256
+ items.append((d, f"#{i}"))
257
+ return items, f"{len(items)} image(s) from `{col}`"
258
+
259
+
260
+ def media_choices(state):
261
+ df = _state_df(state)
262
+ if df.empty:
263
+ return gr.update(choices=[])
264
+ m = B.detect_media_columns(df)
265
+ return gr.update(choices=m["image"] + m["audio"],
266
+ value=(m["image"] or m["audio"] or [None])[0])
267
+
268
+
269
+ # --------------------------------------------------------------------------- #
270
+ # tab 6: raw files
271
+ # --------------------------------------------------------------------------- #
272
+ def read_raw(dataset_meta, path, offset, limit, token, maxlen):
273
+ ds = (dataset_meta or {}).get("dataset", "")
274
+ if not ds or not path:
275
+ return pd.DataFrame(), "Pick a file first."
276
+ try:
277
+ res = B.read_raw_file(ds, path, token, int(limit), int(offset))
278
+ except Exception as e: # noqa: BLE001
279
+ return pd.DataFrame(), f"❌ {e}"
280
+ return (B.display_frame(res.df, int(maxlen)),
281
+ f"**{res.strategy}** Β· {len(res.df)} rows"
282
+ + (f" of {res.total_rows:,}" if res.total_rows else ""))
283
+
284
+
285
+ # --------------------------------------------------------------------------- #
286
+ # tab 7: export
287
+ # --------------------------------------------------------------------------- #
288
+ def export(state, fmt):
289
+ df = _state_df(state)
290
+ if df.empty:
291
+ return None, "Nothing to export."
292
+ name = f"{(state or {}).get('dataset','hf')}".replace("/", "__") or "hf_export"
293
+ d = tempfile.mkdtemp()
294
+ path = os.path.join(d, f"{name}.{ {'CSV':'csv','JSON Lines':'jsonl','Parquet':'parquet'}[fmt] }")
295
+ try:
296
+ if fmt == "CSV":
297
+ B.display_frame(df, 10**6).to_csv(path, index=False)
298
+ elif fmt == "JSON Lines":
299
+ with open(path, "w", encoding="utf-8") as fh:
300
+ for _, r in df.iterrows():
301
+ fh.write(json.dumps({str(k): B.json_safe(v) for k, v in r.items()},
302
+ ensure_ascii=False) + "\n")
303
+ else:
304
+ df.to_parquet(path, index=False)
305
+ except Exception as e: # noqa: BLE001
306
+ return None, f"❌ {e}"
307
+ return path, f"βœ… exported {len(df):,} rows"
308
+
309
+
310
+ def code_snippet(meta, cfg, split, strategy):
311
+ ds = (meta or {}).get("dataset", "your/dataset")
312
+ return f'''```python
313
+ # Reproduce this page in your own code
314
+ from datasets import load_dataset
315
+
316
+ # 1) streaming β€” works even when the Hub viewer is not ready
317
+ ds = load_dataset("{ds}", {json.dumps(cfg) if cfg else "None"}, split="{split}", streaming=True)
318
+ for i, ex in zip(range(5), ds):
319
+ print(ex)
320
+
321
+ # 2) direct parquet (random access, no dataset script executed)
322
+ import pandas as pd
323
+ df = pd.read_parquet(
324
+ "hf://datasets/{ds}@refs/convert/parquet/{cfg or 'default'}/{split}/0000.parquet"
325
+ )
326
+ ```'''
327
+
328
+
329
+ # --------------------------------------------------------------------------- #
330
+ # UI
331
+ # --------------------------------------------------------------------------- #
332
+ CSS = """
333
+ .small-note {font-size: 0.85rem; opacity: .75}
334
+ footer {display:none !important}
335
+ """
336
+
337
+ with gr.Blocks(title="HF Dataset Explorer") as demo:
338
+ meta = gr.State({})
339
+ state = gr.State(dict(STATE_DEFAULT))
340
+
341
+ gr.Markdown(
342
+ "# πŸ€— HF Dataset Explorer\n"
343
+ "View **any** Hugging Face dataset β€” even when the official dataset viewer "
344
+ "is not ready. Falls back automatically: `viewer API β†’ parquet β†’ streaming β†’ raw files`."
345
+ )
346
+
347
+ with gr.Row():
348
+ dataset_id = gr.Textbox(label="Dataset id or URL", value="stanfordnlp/imdb",
349
+ placeholder="user/name, or paste a huggingface.co URL", scale=4)
350
+ token = gr.Textbox(label="HF token (optional, for private/gated)",
351
+ type="password", scale=2)
352
+ connect_btn = gr.Button("Connect", variant="primary", scale=1)
353
+ gr.Examples(EXAMPLES, inputs=dataset_id, label="Try")
354
+
355
+ with gr.Row():
356
+ config = gr.Dropdown(label="Config", choices=[], interactive=True, allow_custom_value=True)
357
+ split = gr.Dropdown(label="Split", choices=[], interactive=True, allow_custom_value=True)
358
+ strategy = gr.Dropdown(label="Loader", value="Auto (try everything)",
359
+ choices=["Auto (try everything)", *B.STRATEGIES])
360
+ limit = gr.Slider(5, 500, 50, step=5, label="Rows per page")
361
+ offset = gr.Number(0, label="Offset", precision=0)
362
+ maxlen = gr.Slider(50, 2000, 300, step=50, label="Cell truncation (chars)")
363
+
364
+ info_md = gr.Markdown()
365
+
366
+ with gr.Tabs():
367
+ with gr.Tab("πŸ“‹ Rows"):
368
+ with gr.Row():
369
+ load_btn = gr.Button("Load rows", variant="primary")
370
+ prev_btn = gr.Button("β—€ Prev page")
371
+ next_btn = gr.Button("Next page β–Ά")
372
+ banner = gr.Markdown()
373
+ cols = gr.Dropdown(label="Visible columns", multiselect=True, choices=[], interactive=True)
374
+ table = gr.Dataframe(wrap=True, interactive=False, max_height=650)
375
+
376
+ with gr.Tab("πŸ”Ž Filter & search"):
377
+ gr.Markdown("Filters run on the **currently loaded page** (client-side, instant).")
378
+ with gr.Row():
379
+ query = gr.Textbox(label="pandas query", placeholder="label == 1 and len(text) > 500")
380
+ regex_col = gr.Dropdown(label="Regex column", choices=[], allow_custom_value=True)
381
+ regex = gr.Textbox(label="Regex / substring", placeholder="great|terrible")
382
+ filter_btn = gr.Button("Apply filter", variant="primary")
383
+ filter_note = gr.Markdown()
384
+ ftable = gr.Dataframe(wrap=True, interactive=False, max_height=600)
385
+
386
+ with gr.Tab("πŸ”¬ Record inspector"):
387
+ row_idx = gr.Slider(0, 1, 0, step=1, label="Row on this page")
388
+ with gr.Row():
389
+ with gr.Column(scale=3):
390
+ rec_md = gr.Markdown()
391
+ with gr.Column(scale=2):
392
+ rec_json = gr.JSON(label="Raw record")
393
+ rec_img = gr.Image(label="Image field", height=260)
394
+ rec_aud = gr.Audio(label="Audio field")
395
+
396
+ with gr.Tab("πŸ“Š Stats"):
397
+ stats_btn = gr.Button("Profile this page", variant="primary")
398
+ schema_tbl = gr.Dataframe(label="Schema & quality", wrap=True, interactive=False)
399
+ num_tbl = gr.Dataframe(label="Numeric describe()", interactive=False)
400
+ dist_md = gr.Markdown()
401
+
402
+ with gr.Tab("πŸ–ΌοΈ Media"):
403
+ with gr.Row():
404
+ media_col = gr.Dropdown(label="Media column", choices=[], allow_custom_value=True)
405
+ n_media = gr.Slider(1, 100, 24, step=1, label="How many")
406
+ media_btn = gr.Button("Render", variant="primary")
407
+ media_note = gr.Markdown()
408
+ gal = gr.Gallery(columns=6, height=520, object_fit="contain")
409
+
410
+ with gr.Tab("πŸ—‚οΈ Repo files"):
411
+ gr.Markdown("Bypass everything and read a file straight out of the repo.")
412
+ files_tbl = gr.Dataframe(interactive=False, max_height=280)
413
+ with gr.Row():
414
+ file_pick = gr.Dropdown(label="Tabular file", choices=[], allow_custom_value=True, scale=3)
415
+ raw_off = gr.Number(0, label="Offset", precision=0)
416
+ raw_lim = gr.Slider(5, 500, 50, step=5, label="Rows")
417
+ raw_btn = gr.Button("Read file", variant="primary")
418
+ raw_note = gr.Markdown()
419
+ raw_tbl = gr.Dataframe(wrap=True, interactive=False, max_height=520)
420
+
421
+ with gr.Tab("πŸ“„ README"):
422
+ readme_md = gr.Markdown()
423
+
424
+ with gr.Tab("⬇️ Export & code"):
425
+ with gr.Row():
426
+ fmt = gr.Radio(["CSV", "JSON Lines", "Parquet"], value="CSV", label="Format")
427
+ exp_btn = gr.Button("Export current page", variant="primary")
428
+ exp_note = gr.Markdown()
429
+ exp_file = gr.File(label="Download")
430
+ snippet = gr.Markdown()
431
+
432
+ # ---------------- wiring ---------------- #
433
+ connect_btn.click(connect, [dataset_id, token],
434
+ [config, split, info_md, readme_md, file_pick, meta]) \
435
+ .then(file_table, meta, files_tbl) \
436
+ .then(code_snippet, [meta, config, split, strategy], snippet)
437
+
438
+ dataset_id.submit(connect, [dataset_id, token],
439
+ [config, split, info_md, readme_md, file_pick, meta]) \
440
+ .then(file_table, meta, files_tbl)
441
+
442
+ config.change(on_config_change, [config, meta], split)
443
+
444
+ load_inputs = [meta, config, split, offset, limit, token, strategy, maxlen, state]
445
+ load_btn.click(load_rows, load_inputs, [table, banner, state, cols, row_idx]) \
446
+ .then(lambda s: gr.update(choices=[str(c) for c in _state_df(s).columns]), state, regex_col) \
447
+ .then(media_choices, state, media_col) \
448
+ .then(code_snippet, [meta, config, split, strategy], snippet)
449
+
450
+ next_btn.click(lambda *a: page(1, *a), load_inputs,
451
+ [table, banner, state, cols, row_idx, offset])
452
+ prev_btn.click(lambda *a: page(-1, *a), load_inputs,
453
+ [table, banner, state, cols, row_idx, offset])
454
+
455
+ cols.change(pick_columns, [cols, state, maxlen], table)
456
+ filter_btn.click(apply_filter, [query, regex_col, regex, state, maxlen], [ftable, filter_note])
457
+ row_idx.change(inspect, [row_idx, state], [rec_json, rec_md, rec_img, rec_aud])
458
+ stats_btn.click(profile, state, [schema_tbl, num_tbl, dist_md])
459
+ media_btn.click(gallery, [state, media_col, n_media], [gal, media_note])
460
+ raw_btn.click(read_raw, [meta, file_pick, raw_off, raw_lim, token, maxlen], [raw_tbl, raw_note])
461
+ exp_btn.click(export, [state, fmt], [exp_file, exp_note])
462
+
463
+
464
+ if __name__ == "__main__":
465
+ p = argparse.ArgumentParser()
466
+ p.add_argument("--share", action="store_true")
467
+ p.add_argument("--port", type=int, default=7860)
468
+ a = p.parse_args()
469
+ kwargs = dict(server_name="0.0.0.0", server_port=a.port,
470
+ share=a.share, show_error=True)
471
+ try: # gradio >= 6 moved theme/css to launch()
472
+ demo.queue(default_concurrency_limit=4).launch(
473
+ theme=gr.themes.Soft(), css=CSS, **kwargs)
474
+ except TypeError:
475
+ demo.queue(default_concurrency_limit=4).launch(**kwargs)