107 lines
3.3 KiB
Python
107 lines
3.3 KiB
Python
# 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,
|
|
})
|