Spaces:
Running
Running
Commit
·
445a7b6
1
Parent(s):
a22d77d
init
Browse files
app.py
CHANGED
|
@@ -20,7 +20,6 @@ import os
|
|
| 20 |
import re
|
| 21 |
from collections import Counter
|
| 22 |
from dataclasses import asdict, dataclass
|
| 23 |
-
from typing import Dict, List, Optional, Tuple
|
| 24 |
|
| 25 |
import gradio as gr
|
| 26 |
|
|
@@ -39,14 +38,14 @@ class TaskDoc:
|
|
| 39 |
file_path: str
|
| 40 |
module: str
|
| 41 |
abstract: str
|
| 42 |
-
languages:
|
| 43 |
-
tags:
|
| 44 |
-
paper:
|
| 45 |
-
dataset:
|
| 46 |
-
name:
|
| 47 |
|
| 48 |
|
| 49 |
-
def read_file_text(path: str) ->
|
| 50 |
try:
|
| 51 |
with open(path, "r", encoding="utf-8") as f:
|
| 52 |
return f.read()
|
|
@@ -54,7 +53,7 @@ def read_file_text(path: str) -> Optional[str]:
|
|
| 54 |
return None
|
| 55 |
|
| 56 |
|
| 57 |
-
def parse_module_docstring(text: str) ->
|
| 58 |
try:
|
| 59 |
mod = ast.parse(text)
|
| 60 |
return ast.get_docstring(mod)
|
|
@@ -64,11 +63,11 @@ def parse_module_docstring(text: str) -> Optional[str]:
|
|
| 64 |
return m.group(2).strip() if m else None
|
| 65 |
|
| 66 |
|
| 67 |
-
def parse_sections(doc: str) ->
|
| 68 |
# Very simple section parser keyed by lines ending with ':' on their own
|
| 69 |
# Expected keys: name, dataset, abstract, languages, tags, paper
|
| 70 |
-
out:
|
| 71 |
-
current_key:
|
| 72 |
for raw_line in doc.splitlines():
|
| 73 |
line = raw_line.rstrip()
|
| 74 |
if line.endswith(":") and line.strip().lower() in {"name:", "dataset:", "abstract:", "languages:", "tags:", "paper:"}:
|
|
@@ -80,12 +79,12 @@ def parse_sections(doc: str) -> Dict[str, str]:
|
|
| 80 |
return out
|
| 81 |
|
| 82 |
|
| 83 |
-
def split_list_field(value: str) ->
|
| 84 |
if not value:
|
| 85 |
return []
|
| 86 |
# Support comma and newline separated values
|
| 87 |
parts = re.split(r"[\n,]", value)
|
| 88 |
-
cleaned:
|
| 89 |
for p in parts:
|
| 90 |
token = p.strip()
|
| 91 |
if not token:
|
|
@@ -94,8 +93,8 @@ def split_list_field(value: str) -> List[str]:
|
|
| 94 |
return cleaned
|
| 95 |
|
| 96 |
|
| 97 |
-
def discover_task_files() ->
|
| 98 |
-
files:
|
| 99 |
for base in TASK_DIRS:
|
| 100 |
if not os.path.isdir(base):
|
| 101 |
continue
|
|
@@ -111,7 +110,7 @@ def discover_task_files() -> List[str]:
|
|
| 111 |
files.append(os.path.join(dirpath, "main.py"))
|
| 112 |
# Deduplicate while preserving order
|
| 113 |
seen: set = set()
|
| 114 |
-
unique_files:
|
| 115 |
for p in files:
|
| 116 |
if p in seen:
|
| 117 |
continue
|
|
@@ -120,8 +119,8 @@ def discover_task_files() -> List[str]:
|
|
| 120 |
return sorted(unique_files)
|
| 121 |
|
| 122 |
|
| 123 |
-
def index_tasks() ->
|
| 124 |
-
docs:
|
| 125 |
language_counts: Counter = Counter()
|
| 126 |
tag_set: set = set()
|
| 127 |
for path in discover_task_files():
|
|
@@ -151,7 +150,7 @@ def index_tasks() -> Tuple[List[TaskDoc], List[str], List[str]]:
|
|
| 151 |
return docs, languages_sorted, tags_sorted
|
| 152 |
|
| 153 |
|
| 154 |
-
def save_index(path: str, tasks:
|
| 155 |
data = {
|
| 156 |
"tasks": [asdict(t) for t in tasks],
|
| 157 |
"languages": list(langs),
|
|
@@ -162,7 +161,7 @@ def save_index(path: str, tasks: List[TaskDoc], langs: List[str], tags: List[str
|
|
| 162 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 163 |
|
| 164 |
|
| 165 |
-
def load_index(path: str) ->
|
| 166 |
if not os.path.exists(path):
|
| 167 |
return None
|
| 168 |
with open(path, "r", encoding="utf-8") as f:
|
|
@@ -173,7 +172,7 @@ def load_index(path: str) -> Optional[Tuple[List[TaskDoc], List[str], List[str]]
|
|
| 173 |
return tasks, langs, tags
|
| 174 |
|
| 175 |
|
| 176 |
-
def build_and_cache_index() ->
|
| 177 |
tasks, langs, tags = index_tasks()
|
| 178 |
save_index(CACHE_PATH, tasks, langs, tags)
|
| 179 |
return tasks, langs, tags
|
|
@@ -190,11 +189,11 @@ print(f"Loaded {len(ALL_TASKS)} tasks from cache")
|
|
| 190 |
TOP_LANGS = ALL_LANGS[:8] # show more by default
|
| 191 |
|
| 192 |
|
| 193 |
-
def filter_tasks(languages:
|
| 194 |
selected_langs = [lang.lower() for lang in (languages or [])]
|
| 195 |
selected_tags = [t.lower() for t in (tags or [])]
|
| 196 |
search_lc = (search or "").strip().lower()
|
| 197 |
-
out:
|
| 198 |
for td in ALL_TASKS:
|
| 199 |
if selected_langs and not any(lang in td.languages for lang in selected_langs):
|
| 200 |
continue
|
|
@@ -209,9 +208,9 @@ def filter_tasks(languages: List[str], tags: List[str], search: str) -> List[Tas
|
|
| 209 |
return out
|
| 210 |
|
| 211 |
|
| 212 |
-
def render_cards(tasks:
|
| 213 |
# Responsive grid of pretty cards; show all details without clicks
|
| 214 |
-
items:
|
| 215 |
for t in tasks:
|
| 216 |
parts = t.module.replace("\\", "/").split("/")
|
| 217 |
base_no_ext = parts[-1].rsplit(".", 1)[0]
|
|
@@ -313,21 +312,21 @@ def render_cards(tasks: List[TaskDoc]) -> str:
|
|
| 313 |
return style + "<div class=\"cards-grid\">" + "\n".join(items) + "</div>"
|
| 314 |
|
| 315 |
|
| 316 |
-
def on_filter(languages:
|
| 317 |
tasks = filter_tasks(languages, tags, search)
|
| 318 |
return render_cards(tasks)
|
| 319 |
|
| 320 |
|
| 321 |
-
def on_toggle_language_choices(show_all: bool, selected_langs:
|
| 322 |
choices = ALL_LANGS if show_all else TOP_LANGS
|
| 323 |
kept = [lang for lang in (selected_langs or []) if lang in choices]
|
| 324 |
tasks = filter_tasks(kept, tags, search)
|
| 325 |
return gr.update(choices=choices, value=kept), render_cards(tasks)
|
| 326 |
|
| 327 |
|
| 328 |
-
def on_toggle_tags_visibility(show: bool, selected_tags:
|
| 329 |
# Only toggle visibility; preserve current tag selections and keep them active in filtering
|
| 330 |
-
tags_value:
|
| 331 |
tasks = filter_tasks(languages, tags_value, search)
|
| 332 |
# keep selections when showing; when hiding we keep value but component hidden (so filter still uses them)
|
| 333 |
return gr.update(visible=show, value=tags_value), render_cards(tasks)
|
|
|
|
| 20 |
import re
|
| 21 |
from collections import Counter
|
| 22 |
from dataclasses import asdict, dataclass
|
|
|
|
| 23 |
|
| 24 |
import gradio as gr
|
| 25 |
|
|
|
|
| 38 |
file_path: str
|
| 39 |
module: str
|
| 40 |
abstract: str
|
| 41 |
+
languages: list[str]
|
| 42 |
+
tags: list[str]
|
| 43 |
+
paper: str | None
|
| 44 |
+
dataset: str | None
|
| 45 |
+
name: str | None = None
|
| 46 |
|
| 47 |
|
| 48 |
+
def read_file_text(path: str) -> str | None:
|
| 49 |
try:
|
| 50 |
with open(path, "r", encoding="utf-8") as f:
|
| 51 |
return f.read()
|
|
|
|
| 53 |
return None
|
| 54 |
|
| 55 |
|
| 56 |
+
def parse_module_docstring(text: str) -> str | None:
|
| 57 |
try:
|
| 58 |
mod = ast.parse(text)
|
| 59 |
return ast.get_docstring(mod)
|
|
|
|
| 63 |
return m.group(2).strip() if m else None
|
| 64 |
|
| 65 |
|
| 66 |
+
def parse_sections(doc: str) -> dict[str, str]:
|
| 67 |
# Very simple section parser keyed by lines ending with ':' on their own
|
| 68 |
# Expected keys: name, dataset, abstract, languages, tags, paper
|
| 69 |
+
out: dict[str, str] = {"name": "", "dataset": "", "abstract": "", "languages": "", "tags": "", "paper": ""}
|
| 70 |
+
current_key: str | None = None
|
| 71 |
for raw_line in doc.splitlines():
|
| 72 |
line = raw_line.rstrip()
|
| 73 |
if line.endswith(":") and line.strip().lower() in {"name:", "dataset:", "abstract:", "languages:", "tags:", "paper:"}:
|
|
|
|
| 79 |
return out
|
| 80 |
|
| 81 |
|
| 82 |
+
def split_list_field(value: str) -> list[str]:
|
| 83 |
if not value:
|
| 84 |
return []
|
| 85 |
# Support comma and newline separated values
|
| 86 |
parts = re.split(r"[\n,]", value)
|
| 87 |
+
cleaned: list[str] = []
|
| 88 |
for p in parts:
|
| 89 |
token = p.strip()
|
| 90 |
if not token:
|
|
|
|
| 93 |
return cleaned
|
| 94 |
|
| 95 |
|
| 96 |
+
def discover_task_files() -> list[str]:
|
| 97 |
+
files: list[str] = []
|
| 98 |
for base in TASK_DIRS:
|
| 99 |
if not os.path.isdir(base):
|
| 100 |
continue
|
|
|
|
| 110 |
files.append(os.path.join(dirpath, "main.py"))
|
| 111 |
# Deduplicate while preserving order
|
| 112 |
seen: set = set()
|
| 113 |
+
unique_files: list[str] = []
|
| 114 |
for p in files:
|
| 115 |
if p in seen:
|
| 116 |
continue
|
|
|
|
| 119 |
return sorted(unique_files)
|
| 120 |
|
| 121 |
|
| 122 |
+
def index_tasks() -> tuple[list[TaskDoc], list[str], list[str]]:
|
| 123 |
+
docs: list[TaskDoc] = []
|
| 124 |
language_counts: Counter = Counter()
|
| 125 |
tag_set: set = set()
|
| 126 |
for path in discover_task_files():
|
|
|
|
| 150 |
return docs, languages_sorted, tags_sorted
|
| 151 |
|
| 152 |
|
| 153 |
+
def save_index(path: str, tasks: list[TaskDoc], langs: list[str], tags: list[str]) -> None:
|
| 154 |
data = {
|
| 155 |
"tasks": [asdict(t) for t in tasks],
|
| 156 |
"languages": list(langs),
|
|
|
|
| 161 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 162 |
|
| 163 |
|
| 164 |
+
def load_index(path: str) -> tuple[list[TaskDoc], list[str], list[str]] | None:
|
| 165 |
if not os.path.exists(path):
|
| 166 |
return None
|
| 167 |
with open(path, "r", encoding="utf-8") as f:
|
|
|
|
| 172 |
return tasks, langs, tags
|
| 173 |
|
| 174 |
|
| 175 |
+
def build_and_cache_index() -> tuple[list[TaskDoc], list[str], list[str]]:
|
| 176 |
tasks, langs, tags = index_tasks()
|
| 177 |
save_index(CACHE_PATH, tasks, langs, tags)
|
| 178 |
return tasks, langs, tags
|
|
|
|
| 189 |
TOP_LANGS = ALL_LANGS[:8] # show more by default
|
| 190 |
|
| 191 |
|
| 192 |
+
def filter_tasks(languages: list[str], tags: list[str], search: str) -> list[TaskDoc]:
|
| 193 |
selected_langs = [lang.lower() for lang in (languages or [])]
|
| 194 |
selected_tags = [t.lower() for t in (tags or [])]
|
| 195 |
search_lc = (search or "").strip().lower()
|
| 196 |
+
out: list[TaskDoc] = []
|
| 197 |
for td in ALL_TASKS:
|
| 198 |
if selected_langs and not any(lang in td.languages for lang in selected_langs):
|
| 199 |
continue
|
|
|
|
| 208 |
return out
|
| 209 |
|
| 210 |
|
| 211 |
+
def render_cards(tasks: list[TaskDoc]) -> str:
|
| 212 |
# Responsive grid of pretty cards; show all details without clicks
|
| 213 |
+
items: list[str] = []
|
| 214 |
for t in tasks:
|
| 215 |
parts = t.module.replace("\\", "/").split("/")
|
| 216 |
base_no_ext = parts[-1].rsplit(".", 1)[0]
|
|
|
|
| 312 |
return style + "<div class=\"cards-grid\">" + "\n".join(items) + "</div>"
|
| 313 |
|
| 314 |
|
| 315 |
+
def on_filter(languages: list[str], tags: list[str], search: str):
|
| 316 |
tasks = filter_tasks(languages, tags, search)
|
| 317 |
return render_cards(tasks)
|
| 318 |
|
| 319 |
|
| 320 |
+
def on_toggle_language_choices(show_all: bool, selected_langs: list[str], tags: list[str], search: str):
|
| 321 |
choices = ALL_LANGS if show_all else TOP_LANGS
|
| 322 |
kept = [lang for lang in (selected_langs or []) if lang in choices]
|
| 323 |
tasks = filter_tasks(kept, tags, search)
|
| 324 |
return gr.update(choices=choices, value=kept), render_cards(tasks)
|
| 325 |
|
| 326 |
|
| 327 |
+
def on_toggle_tags_visibility(show: bool, selected_tags: list[str], languages: list[str], search: str):
|
| 328 |
# Only toggle visibility; preserve current tag selections and keep them active in filtering
|
| 329 |
+
tags_value: list[str] = selected_tags or []
|
| 330 |
tasks = filter_tasks(languages, tags_value, search)
|
| 331 |
# keep selections when showing; when hiding we keep value but component hidden (so filter still uses them)
|
| 332 |
return gr.update(visible=show, value=tags_value), render_cards(tasks)
|