# pxy_de/api.py from __future__ import annotations from pathlib import Path from typing import List, Dict, Any import pandas as pd from django.conf import settings from rest_framework.decorators import api_view from rest_framework.response import Response from .providers.base import get_provider def _rel(path: Path, base_dir: Path) -> str: """ Return a clean relative path like 'data/...' """ try: return str(path.relative_to(settings.BASE_DIR)) except Exception: # Fallback: show relative to provider base_dir try: return str(Path("data") / path.relative_to(base_dir)) except Exception: return str(path) def _probe_csv(path: Path) -> Dict[str, Any]: """ Lightweight readability probe: existence + sample columns (no full read). """ info: Dict[str, Any] = {"exists": path.exists()} if not info["exists"]: return info try: sample = pd.read_csv(path, nrows=5) info["columns"] = list(sample.columns) info["sample_rows"] = int(sample.shape[0]) # up to 5 except Exception as e: info["error"] = f"{type(e).__name__}: {e}" return info @api_view(["GET"]) def de_health(request): """ GET /api/de/health?city=CDMX&business=cafe&indicator=imss_wages_2023 Reports: - provider in use - base_dir used by the provider - required/missing files (population.csv always; others if params passed) - lightweight probes for each checked file (exists, columns, sample_rows) """ provider = get_provider() base_dir: Path = getattr(provider, "base_dir", Path(settings.BASE_DIR) / "data") checks: List[Dict[str, Any]] = [] missing: List[str] = [] city = (request.query_params.get("city") or "").strip() business = (request.query_params.get("business") or "").strip() indicator = (request.query_params.get("indicator") or "").strip() # Always check SAMI population pop_path = base_dir / "sami" / "population.csv" pop_probe = _probe_csv(pop_path) pop_probe["path"] = _rel(pop_path, base_dir) checks.append(pop_probe) if not pop_probe["exists"]: missing.append(pop_probe["path"]) # Optional: indicator for SAMI if indicator: ind_path = base_dir / "sami" / f"{indicator}.csv" ind_probe = _probe_csv(ind_path) ind_probe["path"] = _rel(ind_path, base_dir) checks.append(ind_probe) if not ind_probe["exists"]: missing.append(ind_probe["path"]) # Optional: Sites (competition / DENUE) if city and business: denue_path = base_dir / "denue" / f"{city}_{business}.csv" denue_probe = _probe_csv(denue_path) denue_probe["path"] = _rel(denue_path, base_dir) checks.append(denue_probe) if not denue_probe["exists"]: missing.append(denue_probe["path"]) # Optional: Sites (demand / population grid) if city: grid_path = base_dir / "popgrid" / f"{city}_grid.csv" grid_probe = _probe_csv(grid_path) grid_probe["path"] = _rel(grid_path, base_dir) checks.append(grid_probe) if not grid_probe["exists"]: missing.append(grid_probe["path"]) ok = len(missing) == 0 return Response({ "provider": "csv-data", "base_dir": str(base_dir), "ok": ok, "missing": missing, "files": checks, })