| from __future__ import annotations |
|
|
| import json |
| import os |
| import re |
| import sys |
| import warnings |
| import shutil |
| from pathlib import Path |
|
|
| ROOT_DIR = Path(__file__).resolve().parents[1] |
| if str(ROOT_DIR) not in sys.path: |
| sys.path.insert(0, str(ROOT_DIR)) |
|
|
| from huggingface_hub import HfApi, hf_hub_download |
|
|
| from backend.spaces_index import INDEX_PATH, ORG_NAME, _extract_space_payload, _is_infra_repo, fetch_space_readme |
|
|
|
|
| DATA_DIR = ROOT_DIR / "data" |
| README_DIR = DATA_DIR / "org_readmes" |
| MANIFEST_PATH = DATA_DIR / "org_readmes_manifest.json" |
|
|
| os.environ.setdefault("HF_HUB_DISABLE_SYMLINKS_WARNING", "1") |
| warnings.filterwarnings( |
| "ignore", |
| message="`huggingface_hub` cache-system uses symlinks by default", |
| ) |
|
|
|
|
| def _safe_slug(repo_id: str) -> str: |
| return re.sub(r"[^a-zA-Z0-9._-]+", "__", repo_id.strip()) or "unknown-space" |
|
|
|
|
| def _download_readme(repo_id: str, api: HfApi) -> str: |
| readme_text = fetch_space_readme(repo_id) |
| if readme_text.strip(): |
| return readme_text |
|
|
| try: |
| repo_files = api.list_repo_files(repo_id=repo_id, repo_type="space") |
| except Exception: |
| return "" |
|
|
| for repo_file in repo_files: |
| normalized_name = Path(str(repo_file)).name.lower() |
| if normalized_name != "readme.md": |
| continue |
| try: |
| readme_path = hf_hub_download(repo_id=repo_id, repo_type="space", filename=str(repo_file)) |
| return Path(readme_path).read_text(encoding="utf-8", errors="ignore") |
| except Exception: |
| continue |
| return "" |
|
|
|
|
| def _load_cached_index_targets() -> tuple[list[dict], dict]: |
| if not INDEX_PATH.exists(): |
| return [], {} |
| try: |
| payload = json.loads(INDEX_PATH.read_text(encoding="utf-8")) |
| except Exception: |
| return [], {} |
| if not isinstance(payload, dict): |
| return [], {} |
| spaces = payload.get("spaces", []) |
| stats = payload.get("stats", {}) |
| if not isinstance(spaces, list): |
| return [], {} |
|
|
| targets: list[dict] = [] |
| for item in spaces: |
| if not isinstance(item, dict): |
| continue |
| repo_id = str(item.get("id", "")).strip() |
| if not repo_id: |
| continue |
| targets.append( |
| { |
| "repo_id": repo_id, |
| "title": str(item.get("name", "")).strip(), |
| "url": str(item.get("url", "")).strip(), |
| "likes": int(item.get("likes", 0) or 0), |
| "sdk": str(item.get("sdk", "")).strip(), |
| "tags": list(item.get("tags", []) or []), |
| } |
| ) |
| return targets, stats if isinstance(stats, dict) else {} |
|
|
|
|
| def main() -> None: |
| if README_DIR.exists(): |
| shutil.rmtree(README_DIR) |
| README_DIR.mkdir(parents=True, exist_ok=True) |
| api = HfApi() |
|
|
| manifest: list[dict] = [] |
| readme_fetched = 0 |
| readme_missing = 0 |
| targets, cached_stats = _load_cached_index_targets() |
| using_cached_index = bool(targets) |
|
|
| if using_cached_index: |
| listed = int(cached_stats.get("listed", len(targets)) or len(targets)) |
| private_skipped = int(cached_stats.get("private_skipped", 0) or 0) |
| infra_skipped = int(cached_stats.get("infra_skipped", 0) or 0) |
| else: |
| try: |
| raw_spaces = list(api.list_spaces(author=ORG_NAME, full=True)) |
| except TypeError: |
| raw_spaces = list(api.list_spaces(author=ORG_NAME)) |
|
|
| listed = 0 |
| private_skipped = 0 |
| infra_skipped = 0 |
| targets = [] |
| for raw_space in raw_spaces: |
| listed += 1 |
| payload = _extract_space_payload(raw_space) |
| repo_id = payload["id"] |
| status = str(payload.get("status", "")).strip().lower() |
| if not repo_id: |
| continue |
| if payload.get("private") or payload.get("disabled") or payload.get("gated") or status == "private": |
| private_skipped += 1 |
| continue |
| if _is_infra_repo(repo_id, payload["raw_name"], payload["name"], payload["tags"]): |
| infra_skipped += 1 |
| continue |
| targets.append( |
| { |
| "repo_id": repo_id, |
| "title": payload["name"], |
| "url": payload["url"], |
| "likes": int(payload.get("likes", 0) or 0), |
| "sdk": str(payload.get("sdk", "")).strip(), |
| "tags": list(payload.get("tags", []) or []), |
| } |
| ) |
|
|
| for index, target in enumerate(targets, start=1): |
| repo_id = target["repo_id"] |
| status = "unknown" |
|
|
| readme_text = _download_readme(repo_id, api) |
| saved = False |
| relative_path = "" |
| if readme_text.strip(): |
| folder = README_DIR / _safe_slug(repo_id) |
| folder.mkdir(parents=True, exist_ok=True) |
| readme_path = folder / "README.md" |
| readme_path.write_text(readme_text, encoding="utf-8") |
| relative_path = str(readme_path.relative_to(ROOT_DIR)).replace("\\", "/") |
| saved = True |
| readme_fetched += 1 |
| else: |
| readme_missing += 1 |
|
|
| if index % 50 == 0: |
| print(f"Processed {index}/{len(targets)} Spaces...") |
|
|
| manifest.append( |
| { |
| "repo_id": repo_id, |
| "title": target["title"], |
| "url": target["url"], |
| "status": status or "unknown", |
| "likes": int(target.get("likes", 0) or 0), |
| "sdk": str(target.get("sdk", "")).strip(), |
| "tags": list(target.get("tags", []) or []), |
| "readme_saved": saved, |
| "readme_path": relative_path, |
| } |
| ) |
|
|
| manifest.sort(key=lambda item: (not item["readme_saved"], item["repo_id"].lower())) |
| MANIFEST_PATH.write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8") |
|
|
| saved_count = sum(1 for item in manifest if item["readme_saved"]) |
| missing_repo_ids = [item["repo_id"] for item in manifest if not item["readme_saved"]] |
| if using_cached_index: |
| print(f"Using cached index source: {INDEX_PATH}") |
| print(f"Listed {listed} Spaces from {ORG_NAME}.") |
| print(f"Private skipped: {private_skipped}") |
| print(f"Infra skipped: {infra_skipped}") |
| print(f"README fetched: {readme_fetched}") |
| print(f"README missing: {readme_missing}") |
| print(f"Indexed pool: {len(manifest)}") |
| print(f"Saved {saved_count} README snapshots under {README_DIR}.") |
| if missing_repo_ids: |
| print("Missing README downloads:") |
| for repo_id in missing_repo_ids: |
| print(f"- {repo_id}") |
| print(f"Manifest written to {MANIFEST_PATH}.") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|