akazemian commited on
Commit
7aabe91
·
verified ·
1 Parent(s): eab2015

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. .gradio/certificate.pem +31 -0
  2. README.md +2 -8
  3. app.py +366 -0
  4. library.csv +0 -0
  5. requirements.py +2 -0
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
README.md CHANGED
@@ -1,12 +1,6 @@
1
  ---
2
- title: Audio Library
3
- emoji: 📈
4
- colorFrom: gray
5
- colorTo: purple
6
  sdk: gradio
7
  sdk_version: 5.46.0
8
- app_file: app.py
9
- pinned: false
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: audio-library
3
+ app_file: app.py
 
 
4
  sdk: gradio
5
  sdk_version: 5.46.0
 
 
6
  ---
 
 
app.py ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, uuid, datetime, traceback
2
+ from pathlib import Path
3
+ import html as _py_html
4
+ import pandas as pd
5
+ import gradio as gr
6
+
7
+ # ----------- FIXED PATHS -----------
8
+ REPORTS_ROOT = Path("/data/atlask/Model-Preds-Html/AudioSet-Audio") # /{model_name}/*.html
9
+ FIXED_MANIFEST = Path("/data/atlask/BAU-Quant/manifest_val.csv")
10
+ # -----------------------------------
11
+
12
+ DB_PATH = "library.csv"
13
+ ALLOWED_EXTS = {".html"}
14
+
15
+ # Columns in DB
16
+ EXTRA_COLS = ["model_name", "category", "dataset"]
17
+ BASE_COLS = ["id","filename","path","tags","keywords","notes","uploaded_at"]
18
+ ALL_DB_COLS = BASE_COLS + EXTRA_COLS
19
+
20
+ # Columns shown in the table (order)
21
+ TABLE_COLS = ["id","filename","model_name","category","dataset",
22
+ "tags","keywords","notes","uploaded_at"]
23
+
24
+ # ---------- DB helpers ----------
25
+ def _load_db() -> pd.DataFrame:
26
+ if os.path.exists(DB_PATH):
27
+ df = pd.read_csv(DB_PATH)
28
+ # migrate: ensure all required columns exist
29
+ for c in ALL_DB_COLS:
30
+ if c not in df.columns:
31
+ df[c] = ""
32
+ # normalize text-ish fields
33
+ for c in ["tags","keywords","notes","model_name","category","dataset"]:
34
+ df[c] = df[c].fillna("").astype(str)
35
+ # keep only our known columns in stable order
36
+ return df[ALL_DB_COLS]
37
+ return pd.DataFrame(columns=ALL_DB_COLS)
38
+
39
+ def _save_db(df: pd.DataFrame):
40
+ df.to_csv(DB_PATH, index=False)
41
+
42
+ # ---------- Table normalizer ----------
43
+ def _df_from_table_value(table_value):
44
+ cols = TABLE_COLS
45
+ if isinstance(table_value, pd.DataFrame):
46
+ for c in cols:
47
+ if c not in table_value.columns:
48
+ table_value[c] = ""
49
+ return table_value[cols]
50
+ if isinstance(table_value, list):
51
+ if not table_value:
52
+ return pd.DataFrame(columns=cols)
53
+ first = table_value[0]
54
+ if isinstance(first, dict):
55
+ df = pd.DataFrame(table_value)
56
+ for c in cols:
57
+ if c not in df.columns:
58
+ df[c] = ""
59
+ return df[cols]
60
+ else:
61
+ return pd.DataFrame(table_value, columns=cols)
62
+ return pd.DataFrame(columns=cols)
63
+
64
+ # ---------- Manifest helpers ----------
65
+ def _stem_for_match(p: Path) -> str:
66
+ stem = p.stem
67
+ if "chunk" in stem:
68
+ stem = stem.split("_chunk")[0]
69
+ return stem
70
+
71
+ def _load_manifest():
72
+ if not FIXED_MANIFEST.exists():
73
+ return None
74
+ mf = pd.read_csv(FIXED_MANIFEST)
75
+ if "file_name" not in mf.columns:
76
+ return None
77
+ mf = mf.copy()
78
+ def mk_from_str(s: str):
79
+ st = Path(str(s)).stem
80
+ return st.split("_chunk")[0] if "chunk" in st else st
81
+ mf["__match_key"] = mf["file_name"].astype(str).apply(mk_from_str)
82
+ return mf
83
+
84
+ # ---------- Sync by model ----------
85
+ def sync_model(model_name: str):
86
+ f"""
87
+ Index all .html reports under {REPORTS_ROOT}/{model_name}.
88
+ Adds NEW files to DB (by exact path), sets model_name,
89
+ and fills category/dataset from the fixed manifest if present.
90
+ """
91
+ model_name = (model_name or "").strip()
92
+ if not model_name:
93
+ return gr.Info("Please enter a model name."), None, None, None, None
94
+
95
+ folder = REPORTS_ROOT / model_name
96
+ if not folder.exists():
97
+ return gr.Info(f"Folder not found: {folder}"), None, None, None, None
98
+
99
+ df = _load_db()
100
+ manifest = _load_manifest()
101
+ now = datetime.datetime.now().isoformat(timespec="seconds")
102
+ new_rows = []
103
+
104
+ for p in sorted(folder.glob("*.html")):
105
+ if p.suffix.lower() not in ALLOWED_EXTS:
106
+ continue
107
+
108
+ # if already indexed, optionally backfill model_name and skip creating a new row
109
+ existing = df["path"] == str(p)
110
+ if existing.any():
111
+ idxs = df.index[existing]
112
+ for i in idxs:
113
+ if (df.at[i, "model_name"] or "") != model_name:
114
+ df.at[i, "model_name"] = model_name
115
+ continue
116
+
117
+ category, dataset = "", ""
118
+ if manifest is not None:
119
+ mk = _stem_for_match(p)
120
+ hit = manifest[manifest["__match_key"].str.contains(mk, na=False)]
121
+ if not hit.empty:
122
+ if "audio_category" in hit.columns:
123
+ category = str(hit.iloc[0]["audio_category"])
124
+ if "dataset" in hit.columns:
125
+ dataset = str(hit.iloc[0]["dataset"])
126
+
127
+ uid = uuid.uuid4().hex[:8]
128
+ new_rows.append({
129
+ "id": uid,
130
+ "filename": p.name,
131
+ "path": str(p), # keep absolute path; no copying
132
+ "tags": "",
133
+ "keywords": "",
134
+ "notes": "",
135
+ "uploaded_at": now,
136
+ "model_name": model_name,
137
+ "category": category,
138
+ "dataset": dataset
139
+ })
140
+
141
+ if new_rows:
142
+ df = pd.concat([df, pd.DataFrame(new_rows)], ignore_index=True)
143
+
144
+ _save_db(df)
145
+ # show refreshed view scoped to this model
146
+ return refresh_view("", [], "", "", model_name)
147
+
148
+ # ---------- Search / filters ----------
149
+ def refresh_view(query, tag_filters, category_filter, dataset_filter, model_filter):
150
+ df = _load_db()
151
+
152
+ # tag vocabulary
153
+ all_tags = sorted({t.strip()
154
+ for s in df["tags"].dropna().astype(str).tolist()
155
+ for t in s.split(",") if t.strip()})
156
+ all_cats = sorted([c for c in df["category"].dropna().astype(str).unique() if c])
157
+ all_sets = sorted([c for c in df["dataset"].dropna().astype(str).unique() if c])
158
+ all_models = sorted([c for c in df["model_name"].dropna().astype(str).unique() if c])
159
+
160
+ # free-text query across filename/tags/keywords/notes/category/dataset/model
161
+ if query:
162
+ q = query.lower()
163
+ mask = (
164
+ df["filename"].str.lower().str.contains(q, na=False) |
165
+ df["tags"].str.lower().str.contains(q, na=False) |
166
+ df["keywords"].str.lower().str.contains(q, na=False) |
167
+ df["notes"].str.lower().str.contains(q, na=False) |
168
+ df["category"].str.lower().str.contains(q, na=False) |
169
+ df["dataset"].str.lower().str.contains(q, na=False) |
170
+ df["model_name"].str.lower().str.contains(q, na=False)
171
+ )
172
+ df = df[mask]
173
+
174
+ # tag filters (AND semantics)
175
+ for t in (tag_filters or []):
176
+ df = df[df["tags"].astype(str).apply(
177
+ lambda s: t in [x.strip() for x in s.split(",") if x.strip()])]
178
+
179
+ # dropdown filters (exact match)
180
+ if category_filter:
181
+ df = df[df["category"] == category_filter]
182
+ if dataset_filter:
183
+ df = df[df["dataset"] == dataset_filter]
184
+ if model_filter:
185
+ df = df[df["model_name"] == model_filter]
186
+
187
+ df = df.sort_values("uploaded_at", ascending=False).reset_index(drop=True)
188
+ view = df[TABLE_COLS].copy()
189
+ count_text = f"**Showing {len(view)} file(s)**"
190
+
191
+ return (
192
+ view,
193
+ gr.update(choices=all_tags),
194
+ gr.update(choices=[""] + all_cats, value=category_filter or ""),
195
+ gr.update(choices=[""] + all_sets, value=dataset_filter or ""),
196
+ gr.update(choices=[""] + all_models, value=model_filter or ""),
197
+ count_text
198
+ )
199
+
200
+ # ---------- Preview ----------
201
+ def _iframe_from_html_string(raw_html: str, height_px: int = 720) -> str:
202
+ srcdoc = raw_html.replace("&", "&").replace('"', """)
203
+ return f'<iframe style="width:100%;height:{height_px}px;border:1px solid #ddd;border-radius:8px;" srcdoc="{srcdoc}"></iframe>'
204
+
205
+ def select_row(evt: gr.SelectData, table_value):
206
+ try:
207
+ view = _df_from_table_value(table_value)
208
+ if view.empty:
209
+ return "<em>No rows.</em>", ""
210
+ # resolve row
211
+ row_idx = None
212
+ ix = getattr(evt, "index", None)
213
+ if isinstance(ix, int):
214
+ row_idx = ix
215
+ elif isinstance(ix, (list, tuple)) and ix and isinstance(ix[0], int):
216
+ row_idx = ix[0]
217
+ if row_idx is None:
218
+ val = getattr(evt, "value", None)
219
+ if isinstance(val, dict) and "id" in val:
220
+ hits = view.index[view["id"] == val["id"]].tolist()
221
+ if hits: row_idx = hits[0]
222
+ elif isinstance(val, list) and len(val) >= 1:
223
+ hits = view.index[view["id"] == val[0]].tolist()
224
+ if hits: row_idx = hits[0]
225
+ if row_idx is None or not (0 <= row_idx < len(view)):
226
+ return "<em>Invalid selection.</em>", ""
227
+
228
+ row = view.iloc[row_idx]
229
+ sel_id = row["id"]
230
+
231
+ db = _load_db()
232
+ rec = db[db["id"] == sel_id]
233
+ if rec.empty:
234
+ return "<em>Could not find file for this row.</em>", ""
235
+
236
+ path = rec["path"].values[0]
237
+ if not os.path.exists(path):
238
+ return f"<em>File not found:</em> <code>{_py_html.escape(path)}</code>", f"📄 {row['filename']}"
239
+
240
+ with open(path, "r", encoding="utf-8") as f:
241
+ raw_html = f.read()
242
+
243
+ iframe = _iframe_from_html_string(raw_html, height_px=720)
244
+ return iframe, f"📄 {row['filename']}"
245
+ except Exception as e:
246
+ traceback.print_exc()
247
+ return f"<pre>Failed to render (see terminal):\n{_py_html.escape(str(e))}</pre>", ""
248
+
249
+ # ---------- Save edits ----------
250
+ def save_edits(edited_table):
251
+ if edited_table is None or not len(edited_table):
252
+ return gr.Info("Nothing to save.")
253
+ df_db = _load_db()
254
+ editable_cols = ["model_name","category","dataset","tags","keywords","notes"]
255
+ for c in editable_cols:
256
+ edited_table[c] = edited_table[c].fillna("").astype(str)
257
+ for _, row in edited_table.iterrows():
258
+ i = df_db.index[df_db["id"] == row["id"]]
259
+ if len(i):
260
+ for c in editable_cols:
261
+ df_db.at[i[0], c] = row[c]
262
+ _save_db(df_db)
263
+ # return refreshed table only
264
+ return refresh_view("", [], "", "", "")[0]
265
+
266
+ # -------------------- UI --------------------
267
+ # CSS that targets only the three buttons via elem_id
268
+ custom_css = """
269
+ /* scope styles to only these 3 components */
270
+ #sync-btn button,
271
+ #refresh-btn button,
272
+ #save-btn button,
273
+ #sync-btn .gr-button,
274
+ #refresh-btn .gr-button,
275
+ #save-btn .gr-button,
276
+ #sync-btn [role="button"],
277
+ #refresh-btn [role="button"],
278
+ #save-btn [role="button"] {
279
+ background: #f97316 !important; /* orange-500 */
280
+ border-color: #f97316 !important;
281
+ color: #fff !important;
282
+ }
283
+
284
+ /* hover/active */
285
+ #sync-btn button:hover,
286
+ #refresh-btn button:hover,
287
+ #save-btn button:hover,
288
+ #sync-btn .gr-button:hover,
289
+ #refresh-btn .gr-button:hover,
290
+ #save-btn .gr-button:hover,
291
+ #sync-btn [role="button"]:hover,
292
+ #refresh-btn [role="button"]:hover,
293
+ #save-btn [role="button"]:hover {
294
+ background: #ea580c !important; /* orange-600 */
295
+ border-color: #ea580c !important;
296
+ }
297
+
298
+ /* (optional) also set CSS vars in case theme uses them */
299
+ #sync-btn, #refresh-btn, #save-btn {
300
+ --button-primary-background-fill: #f97316;
301
+ --button-primary-background-fill-hover: #ea580c;
302
+ --button-text-color: #fff;
303
+ }
304
+ """
305
+
306
+
307
+ with gr.Blocks(title="Audio HTML Library", css=custom_css) as demo:
308
+ gr.Markdown("## 🎧 Audio Reconstruction Reports — sync • search • view")
309
+
310
+ with gr.Row():
311
+ with gr.Column(scale=1):
312
+ # Choose model & sync
313
+ gr.Markdown(f"**Model folder:** `{REPORTS_ROOT}/model_name`")
314
+ model_in = gr.Textbox(label="Model name", placeholder="e.g., WavCochV8192")
315
+ sync_btn = gr.Button("Sync this model", elem_id="sync-btn") # ⬅️ give id
316
+
317
+ # Search & filters
318
+ gr.Markdown("---\n**Search & filter**")
319
+ query = gr.Textbox(label="Keyword search (filename/tags/notes/category/dataset/model)", placeholder="type to search…")
320
+ tag_filter = gr.CheckboxGroup(choices=[], label="Filter by tags (AND)")
321
+ category_filter = gr.Dropdown(choices=[], label="Category")
322
+ dataset_filter = gr.Dropdown(choices=[], label="Dataset")
323
+ model_filter = gr.Dropdown(choices=[], label="Model")
324
+ refresh_btn = gr.Button("Refresh", elem_id="refresh-btn") # ⬅️ give id
325
+
326
+ with gr.Column(scale=2):
327
+ # Count of current view
328
+ count_md = gr.Markdown("**Showing 0 file(s)**")
329
+ gr.Markdown("**Library** (click a row to preview; edit cells and Save)")
330
+ table = gr.Dataframe(
331
+ headers=TABLE_COLS,
332
+ datatype=["str"] * len(TABLE_COLS),
333
+ interactive=True,
334
+ wrap=True,
335
+ row_count=(0, "dynamic"),
336
+ col_count=(len(TABLE_COLS), "fixed")
337
+ )
338
+ with gr.Row():
339
+ save_btn = gr.Button("Save Edits", elem_id="save-btn") # ⬅️ give id
340
+ preview_label = gr.Markdown("")
341
+ preview_html = gr.HTML("")
342
+
343
+ # wiring: sync
344
+ sync_btn.click(sync_model, [model_in],
345
+ [table, tag_filter, category_filter, dataset_filter, model_filter, count_md])
346
+
347
+ # wiring: refresh + live filters
348
+ refresh_btn.click(refresh_view,
349
+ [query, tag_filter, category_filter, dataset_filter, model_filter],
350
+ [table, tag_filter, category_filter, dataset_filter, model_filter, count_md])
351
+
352
+ for comp in (query, tag_filter, category_filter, dataset_filter, model_filter):
353
+ comp.change(refresh_view,
354
+ [query, tag_filter, category_filter, dataset_filter, model_filter],
355
+ [table, tag_filter, category_filter, dataset_filter, model_filter, count_md])
356
+
357
+ table.select(select_row, [table], [preview_html, preview_label])
358
+ save_btn.click(save_edits, [table], [table])
359
+
360
+ # initial load
361
+ demo.load(refresh_view,
362
+ [query, tag_filter, category_filter, dataset_filter, model_filter],
363
+ [table, tag_filter, category_filter, dataset_filter, model_filter, count_md])
364
+
365
+ if __name__ == "__main__":
366
+ demo.launch(share=True) # auth is optional but recommended
library.csv ADDED
The diff for this file is too large to render. See raw diff
 
requirements.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio
2
+ pandas