Download scripts/validate_runtime.py from ArchitSharma/Movie-Recommendation-System: direct link, hf CLI and curl.
- Browser
- Download file 4.36 kB
-
https://huggingface.co/spaces/ArchitSharma/Movie-Recommendation-System/resolve/main/scripts/validate_runtime.py
- Command line
-
hf download hf://spaces/ArchitSharma/Movie-Recommendation-System/scripts/validate_runtime.py
-
curl -L -o validate_runtime.py https://huggingface.co/spaces/ArchitSharma/Movie-Recommendation-System/resolve/main/scripts/validate_runtime.py
4.36 kB
| from __future__ import annotations | |
| import argparse | |
| import csv | |
| from pathlib import Path | |
| import re | |
| import sys | |
| import numpy as np | |
| ROOT_DIR = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT_DIR)) | |
| from app.main import api_recommendations, catalog, health | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description="Validate runtime artifacts and app-level recommendation readiness.") | |
| parser.add_argument("--catalog-path", type=Path, default=Path("data/processed/movie_catalog.csv")) | |
| parser.add_argument("--neighbors-path", type=Path, default=Path("data/processed/item_neighbors.csv")) | |
| parser.add_argument("--semantic-path", type=Path, default=Path("data/processed/semantic_index.npz")) | |
| parser.add_argument("--static-dir", type=Path, default=Path("static")) | |
| parser.add_argument("--query", default="Avatar") | |
| parser.add_argument("--min-movies", type=int, default=1000) | |
| parser.add_argument( | |
| "--require-learned-embeddings", | |
| action="store_true", | |
| help="Fail unless the semantic artifact was built with the SentenceTransformer backend.", | |
| ) | |
| args = parser.parse_args() | |
| failures: list[str] = [] | |
| catalog_rows = count_csv_rows(args.catalog_path) | |
| neighbor_rows = count_csv_rows(args.neighbors_path) | |
| semantic_rows = count_semantic_rows(args.semantic_path) | |
| static_failures = validate_static_assets(args.static_dir) | |
| health_payload = health() | |
| catalog_payload = catalog() | |
| recommendations = api_recommendations(args.query, 5).recommendations | |
| if catalog_rows < args.min_movies: | |
| failures.append(f"catalog has only {catalog_rows} rows") | |
| if neighbor_rows == 0: | |
| failures.append("item neighbor artifact is empty") | |
| if semantic_rows != catalog_rows: | |
| failures.append(f"semantic index rows ({semantic_rows}) do not match catalog rows ({catalog_rows})") | |
| if not health_payload.get("tmdb_enabled"): | |
| failures.append("TMDb is not enabled; check TMDB_API_KEY") | |
| if not health_payload.get("item_cf_enabled"): | |
| failures.append("item-CF artifact is not enabled") | |
| if not health_payload.get("semantic_enabled"): | |
| failures.append("semantic artifact is not enabled") | |
| if args.require_learned_embeddings and health_payload.get("semantic_backend") != "sentence-transformer": | |
| failures.append( | |
| "semantic artifact is not a SentenceTransformer index; rebuild it before deployment" | |
| ) | |
| if not recommendations: | |
| failures.append(f"no recommendations returned for {args.query!r}") | |
| if any(normalize_title(item.title) == normalize_title(args.query) for item in recommendations): | |
| failures.append(f"self-recommendation returned for {args.query!r}") | |
| failures.extend(static_failures) | |
| print("Runtime validation") | |
| print(f"- catalog rows: {catalog_rows}") | |
| print(f"- item neighbor rows: {neighbor_rows}") | |
| print(f"- semantic rows: {semantic_rows}") | |
| print(f"- static assets: {args.static_dir}") | |
| print(f"- health: {health_payload}") | |
| print(f"- catalog: {catalog_payload}") | |
| print(f"- sample recommendations: {[item.title for item in recommendations]}") | |
| if failures: | |
| print("\nFailures:") | |
| for failure in failures: | |
| print(f"- {failure}") | |
| sys.exit(1) | |
| print("\nValidation passed.") | |
| def count_csv_rows(path: Path) -> int: | |
| if not path.exists(): | |
| return 0 | |
| with path.open(newline="", encoding="utf-8") as handle: | |
| return sum(1 for _ in csv.DictReader(handle)) | |
| def count_semantic_rows(path: Path) -> int: | |
| if not path.exists(): | |
| return 0 | |
| data = np.load(path, allow_pickle=False) | |
| _ = data["titles"] | |
| return int(data["embeddings"].shape[0]) | |
| def validate_static_assets(static_dir: Path) -> list[str]: | |
| failures = [] | |
| for filename in ["style.css", "recommend.js", "autocomplete.js", "image.jpg", "loader.gif", "default.jpg"]: | |
| path = static_dir / filename | |
| if not path.exists(): | |
| failures.append(f"missing static asset: {path}") | |
| elif path.stat().st_size == 0: | |
| failures.append(f"empty static asset: {path}") | |
| return failures | |
| def normalize_title(value: str) -> str: | |
| value = re.sub(r"\(\d{4}\)\s*$", "", value) | |
| return " ".join(re.findall(r"[a-z0-9]+", value.lower())) | |
| if __name__ == "__main__": | |
| main() | |