Theflame47 commited on
Commit
614a42e
·
verified ·
1 Parent(s): 0eafdf2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -14
app.py CHANGED
@@ -1,6 +1,5 @@
1
- # app.py
2
  import os, time, html, logging, requests, threading
3
- from typing import Any, Dict, List, Optional
4
  from fastapi import FastAPI, HTTPException
5
  from fastapi.responses import HTMLResponse, JSONResponse
6
 
@@ -49,6 +48,46 @@ def _get_with_backoff(url: str) -> Dict[str, Any]:
49
  raise HTTPException(status_code=502, detail=f"Replicate error {r.status_code}: {r.text[:500]}")
50
  raise HTTPException(status_code=429, detail="Too many retries after 429 responses.")
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  def _ensure_page_loaded(target_index: int) -> None:
53
  """
54
  Ensure pages up to target_index (0-based) are loaded into _pages.
@@ -106,19 +145,48 @@ def _render_table(models: List[Dict[str, Any]]) -> str:
106
  tags = m.get("tags") or m.get("categories") or []
107
  tags_html = " ".join(f"<span class='tag'>{html.escape(str(t))}</span>" for t in tags[:8])
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  thumb_html = f"<img src='{html.escape(thumb)}' alt='' />" if thumb else ""
110
  rows.append(
111
  f"<tr>"
112
  f"<td class='thumb'>{thumb_html}</td>"
113
- f"<td class='id'>{mid}</td>"
114
  f"<td class='desc'>{desc}</td>"
115
  f"<td class='vis'>{vis}</td>"
 
 
116
  f"<td class='tags'>{tags_html}</td>"
117
  f"</tr>"
118
  )
119
  return (
120
  "<table class='grid'>"
121
- "<thead><tr><th>Thumbnail</th><th>ID</th><th>Description</th><th>Visibility</th><th>Tags</th></tr></thead>"
 
 
 
 
 
 
 
 
122
  "<tbody>"
123
  + "\n".join(rows) +
124
  "</tbody></table>"
@@ -187,6 +255,8 @@ def _shell(page_index_0: int, html_table: str, *, search_query: str = "") -> str
187
  .id {{ font-weight: 600; white-space: nowrap; }}
188
  .desc {{ color: #333; max-width: 700px; }}
189
  .vis {{ white-space: nowrap; color: #555; }}
 
 
190
  .tags .tag {{ display: inline-block; padding: 2px 8px; margin: 2px; background: #f2f2f7; border: 1px solid #e5e5ea; border-radius: 999px; font-size: 12px; color: #333; }}
191
  .pagination {{ margin-top: 20px; }}
192
  .page {{ display: inline-block; padding: 6px 10px; margin-right: 6px; border: 1px solid #ddd; border-radius: 6px; text-decoration: none; color: #333; }}
@@ -207,16 +277,13 @@ def _shell(page_index_0: int, html_table: str, *, search_query: str = "") -> str
207
  <span class="pill">building: {building}</span>
208
  <span class="pill">complete: {complete}</span>
209
  </div>
210
-
211
  <form class="searchbar" method="GET" action="/search" onsubmit="return onSearchSubmit();" >
212
  <input id="q" name="q" type="text" placeholder="Search models, descriptions, tags" value="{html.escape(search_query)}" {search_disabled} />
213
  <button id="qbtn" type="submit" {search_disabled}>Search</button>
214
  <span id="searchHint" style="color:#666; font-size:12px;">{'' if _BUILD_COMPLETE else 'Search available after catalog completes.'}</span>
215
  </form>
216
-
217
  {html_table}
218
  {_render_pagination(page_num)}
219
-
220
  <script>
221
  // Start building after the first render completes
222
  document.addEventListener("DOMContentLoaded", function() {{
@@ -244,7 +311,6 @@ def _shell(page_index_0: int, html_table: str, *, search_query: str = "") -> str
244
  }};
245
  poll();
246
  }});
247
-
248
  function onSearchSubmit() {{
249
  const q = document.getElementById("q");
250
  return q && !q.disabled;
@@ -313,9 +379,4 @@ def search(q: str = ""):
313
  base_models = _pages[idx]["results"] if _pages else []
314
  if _BUILD_COMPLETE and q.strip():
315
  results = _search_models(q)
316
- content = _render_table(results)
317
- return HTMLResponse(_shell(idx, content, search_query=q))
318
- else:
319
- # Show page 1 with disabled search and a hint
320
- content = _render_table(base_models)
321
- return HTMLResponse(_shell(idx, content, search_query=q))
 
 
1
  import os, time, html, logging, requests, threading
2
+ from typing import Any, Dict, List, Optional, Tuple
3
  from fastapi import FastAPI, HTTPException
4
  from fastapi.responses import HTMLResponse, JSONResponse
5
 
 
48
  raise HTTPException(status_code=502, detail=f"Replicate error {r.status_code}: {r.text[:500]}")
49
  raise HTTPException(status_code=429, detail="Too many retries after 429 responses.")
50
 
51
+ def _license_label_and_url(m: Dict[str, Any]) -> Tuple[str, Optional[str]]:
52
+ """
53
+ Prefer explicit license_url; fall back to a 'license' text field if present.
54
+ Returns (label, url or None). Label is normalized toward SPDX-style.
55
+ """
56
+ url = m.get("license_url")
57
+ txt = (m.get("license") or "").strip() if not url else ""
58
+
59
+ if url:
60
+ # Derive a short, user-friendly label from the URL path.
61
+ try:
62
+ import urllib.parse as up, re
63
+ last = up.urlparse(url).path.rstrip("/").split("/")[-1]
64
+ last = re.sub(r"(?i)\.(html|md)$", "", last)
65
+ # Normalize a few common SPDX forms
66
+ norm = {
67
+ "apache-2.0": "Apache-2.0",
68
+ "apache 2.0": "Apache-2.0",
69
+ "mit": "MIT",
70
+ "gpl-3.0": "GPL-3.0",
71
+ "gpl 3.0": "GPL-3.0",
72
+ "agpl-3.0": "AGPL-3.0",
73
+ "agpl 3.0": "AGPL-3.0",
74
+ "bsd-3-clause": "BSD-3-Clause",
75
+ "bsd 3 clause": "BSD-3-Clause",
76
+ "cc-by-4.0": "CC-BY-4.0",
77
+ "cc by 4.0": "CC-BY-4.0",
78
+ "cc-by-nc-4.0": "CC-BY-NC-4.0",
79
+ "cc by nc 4.0": "CC-BY-NC-4.0",
80
+ }
81
+ key = last.replace("_", "-").lower()
82
+ label = norm.get(key, (last or "License"))
83
+ except Exception:
84
+ label = "License"
85
+ return (label, url)
86
+ elif txt:
87
+ return (txt, None)
88
+ else:
89
+ return ("Unknown", None)
90
+
91
  def _ensure_page_loaded(target_index: int) -> None:
92
  """
93
  Ensure pages up to target_index (0-based) are loaded into _pages.
 
145
  tags = m.get("tags") or m.get("categories") or []
146
  tags_html = " ".join(f"<span class='tag'>{html.escape(str(t))}</span>" for t in tags[:8])
147
 
148
+ # License
149
+ lic_label, lic_url = _license_label_and_url(m)
150
+ lic_label_h = html.escape(lic_label)
151
+ lic_html = f"<a href='{html.escape(lic_url)}' target='_blank' rel='noopener'>{lic_label_h}</a>" if lic_url else lic_label_h
152
+
153
+ # External sources
154
+ src_links = []
155
+ if m.get("github_url"):
156
+ src_links.append(f"<a href='{html.escape(m['github_url'])}' target='_blank' rel='noopener'>GitHub</a>")
157
+ if m.get("paper_url"):
158
+ src_links.append(f"<a href='{html.escape(m['paper_url'])}' target='_blank' rel='noopener'>Paper</a>")
159
+ if lic_url:
160
+ src_links.append(f"<a href='{html.escape(lic_url)}' target='_blank' rel='noopener'>License</a>")
161
+ sources_html = " | ".join(src_links) if src_links else ""
162
+
163
+ # Run count
164
+ run_ct = m.get("run_count")
165
+ run_html = f"<span class='pill'>{run_ct:,} runs</span>" if isinstance(run_ct, int) else ""
166
+
167
  thumb_html = f"<img src='{html.escape(thumb)}' alt='' />" if thumb else ""
168
  rows.append(
169
  f"<tr>"
170
  f"<td class='thumb'>{thumb_html}</td>"
171
+ f"<td class='id'>{mid}<br>{run_html}</td>"
172
  f"<td class='desc'>{desc}</td>"
173
  f"<td class='vis'>{vis}</td>"
174
+ f"<td class='lic'>{lic_html}</td>"
175
+ f"<td class='src'>{sources_html}</td>"
176
  f"<td class='tags'>{tags_html}</td>"
177
  f"</tr>"
178
  )
179
  return (
180
  "<table class='grid'>"
181
+ "<thead><tr>"
182
+ "<th>Thumbnail</th>"
183
+ "<th>ID / Runs</th>"
184
+ "<th>Description</th>"
185
+ "<th>Visibility</th>"
186
+ "<th>License</th>"
187
+ "<th>Sources</th>"
188
+ "<th>Tags</th>"
189
+ "</tr></thead>"
190
  "<tbody>"
191
  + "\n".join(rows) +
192
  "</tbody></table>"
 
255
  .id {{ font-weight: 600; white-space: nowrap; }}
256
  .desc {{ color: #333; max-width: 700px; }}
257
  .vis {{ white-space: nowrap; color: #555; }}
258
+ .lic {{ white-space: nowrap; }}
259
+ .src {{ font-size: 13px; color: #0366d6; }}
260
  .tags .tag {{ display: inline-block; padding: 2px 8px; margin: 2px; background: #f2f2f7; border: 1px solid #e5e5ea; border-radius: 999px; font-size: 12px; color: #333; }}
261
  .pagination {{ margin-top: 20px; }}
262
  .page {{ display: inline-block; padding: 6px 10px; margin-right: 6px; border: 1px solid #ddd; border-radius: 6px; text-decoration: none; color: #333; }}
 
277
  <span class="pill">building: {building}</span>
278
  <span class="pill">complete: {complete}</span>
279
  </div>
 
280
  <form class="searchbar" method="GET" action="/search" onsubmit="return onSearchSubmit();" >
281
  <input id="q" name="q" type="text" placeholder="Search models, descriptions, tags" value="{html.escape(search_query)}" {search_disabled} />
282
  <button id="qbtn" type="submit" {search_disabled}>Search</button>
283
  <span id="searchHint" style="color:#666; font-size:12px;">{'' if _BUILD_COMPLETE else 'Search available after catalog completes.'}</span>
284
  </form>
 
285
  {html_table}
286
  {_render_pagination(page_num)}
 
287
  <script>
288
  // Start building after the first render completes
289
  document.addEventListener("DOMContentLoaded", function() {{
 
311
  }};
312
  poll();
313
  }});
 
314
  function onSearchSubmit() {{
315
  const q = document.getElementById("q");
316
  return q && !q.disabled;
 
379
  base_models = _pages[idx]["results"] if _pages else []
380
  if _BUILD_COMPLETE and q.strip():
381
  results = _search_models(q)
382
+ content = _render_table(results