OSVauco/agents/recommendations_engine.py

87 lines
3.0 KiB
Python

from google.cloud import bigquery
from datetime import datetime, timezone
PROJECT_ID = "propane-will-491900-m5"
DATASET = "costguard_dataset"
BILLING_TABLE = f"{PROJECT_ID}.{DATASET}.gcp_billing_export"
def get_recommendations(budget_nok: float = 500.0) -> dict:
client = bigquery.Client(project=PROJECT_ID)
recommendations = []
# 1. Anomali: tjenester der siste 3d-snitt >= 2x 30d-snitt
anomaly_query = f"""
WITH base AS (
SELECT service.description AS service,
AVG(cost) AS avg_30d
FROM `{BILLING_TABLE}`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY service
),
recent AS (
SELECT service.description AS service,
AVG(cost) AS avg_3d
FROM `{BILLING_TABLE}`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 3 DAY)
GROUP BY service
)
SELECT r.service, r.avg_3d, b.avg_30d,
SAFE_DIVIDE(r.avg_3d, b.avg_30d) AS ratio
FROM recent r JOIN base b ON r.service = b.service
WHERE SAFE_DIVIDE(r.avg_3d, b.avg_30d) >= 2.0
AND b.avg_30d > 0.001
ORDER BY ratio DESC
"""
# 2. Sløsing: tjenester med kostnad men 0 aktivitet siste 7d
waste_query = f"""
WITH active AS (
SELECT DISTINCT service.description AS service
FROM `{BILLING_TABLE}`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
AND cost > 0
),
all_svc AS (
SELECT DISTINCT service.description AS service
FROM `{BILLING_TABLE}`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND cost > 0
)
SELECT a.service FROM all_svc a
LEFT JOIN active b ON a.service = b.service
WHERE b.service IS NULL
"""
try:
# Anomalier
for row in client.query(anomaly_query).result():
ratio = row.ratio or 0
severity = "high" if ratio >= 3 else "medium"
savings = round(float(row.avg_3d - row.avg_30d) * 30 * 10, 2) # rough NOK
recommendations.append({
"type": "anomaly",
"severity": severity,
"service": row.service,
"message": f"Unormal kostnadsøkning på {row.service}{ratio:.1f}x over normalt",
"estimated_savings_nok": max(savings, 0)
})
# Sløsing
for row in client.query(waste_query).result():
recommendations.append({
"type": "waste",
"severity": "low",
"service": row.service,
"message": f"{row.service} har hatt kostnad men ingen aktivitet siste 7 dager — vurder å skru av",
"estimated_savings_nok": 0
})
except Exception as e:
return {"recommendations": [], "total_estimated_savings_nok": 0, "error": str(e)}
total_savings = sum(r["estimated_savings_nok"] for r in recommendations)
return {
"recommendations": recommendations,
"total_estimated_savings_nok": round(total_savings, 2)
}