"""Portfolio operations blueprint: business metrics, health and anomalies. Endpoints --------- ``GET /api/tenants/metrics`` rollup window + health score per tenant ``GET /api/portfolio/anomalies`` cross-tenant anomalies (median + MAD z-score) """ from datetime import date as date_cls from flask import Blueprint, jsonify, request from services import portfolio_service from blueprints.auth_bp import require_manager_auth portfolio_bp = Blueprint("portfolio", __name__, url_prefix="/api") @portfolio_bp.route("/tenants/metrics", methods=["GET"]) @require_manager_auth def tenant_metrics(): days = request.args.get("days", 14, type=int) include_inactive = request.args.get("all", "false").lower() == "true" return jsonify(portfolio_service.get_tenant_metrics( days=days, include_inactive=include_inactive)) @portfolio_bp.route("/portfolio/anomalies", methods=["GET"]) @require_manager_auth def portfolio_anomalies(): target = request.args.get("date") if target: try: date_cls.fromisoformat(target) except ValueError: return jsonify({"error": "date must be YYYY-MM-DD"}), 400 kwargs = { "date": target, "include_inactive": request.args.get("all", "false").lower() == "true", } z = request.args.get("z", type=float) if z is not None: kwargs["z_threshold"] = z min_tenants = request.args.get("min_tenants", type=int) if min_tenants is not None: kwargs["min_tenants"] = min_tenants return jsonify(portfolio_service.get_portfolio_anomalies(**kwargs))