13. Dashboard Updates

Chapter 13 of 18 · 25 min

Static dashboards quickly become stale. Implement automated refresh mechanisms that update visualizations when underlying data changes or when AI detects significant trends.

Webhook-Based Updates

Grafana and similar tools support webhooks for dashboard updates.

# dashboard_updater.py
import json
import requests
from datetime import datetime
from pathlib import Path
import ollama

class DashboardUpdater:
    def __init__(self, config_path: str = "dashboard_config.json"):
        self.config = self._load_config(config_path)
        self.update_log = Path("update_log.jsonl")
    
    def _load_config(self, path: str) -> dict:
        with open(path) as f:
            return json.load(f)
    
    def check_for_updates(self) -> list[dict]:
        """Check all configured dashboards for required updates."""
        updates_needed = []
        
        for dashboard in self.config["dashboards"]:
            if self._needs_update(dashboard):
                updates_needed.append(self._prepare_update(dashboard))
        
        return updates_needed
    
    def _needs_update(self, dashboard: dict) -> bool:
        """Determine if dashboard requires refresh."""
        last_update = dashboard.get("last_update")
        refresh_interval = dashboard.get("refresh_minutes", 60)
        
        if not last_update:
            return True
        
        last = datetime.fromisoformat(last_update)
        age_minutes = (datetime.now() - last).total_seconds() / 60
        
        return age_minutes >= refresh_interval
    
    def _prepare_update(self, dashboard: dict) -> dict:
        """Generate new data for dashboard."""
        dashboard_type = dashboard["type"]
        
        if dashboard_type == "anomaly_detection":
            return self._prepare_anomaly_dashboard(dashboard)
        elif dashboard_type == "trend_analysis":
            return self._prepare_trend_dashboard(dashboard)
        elif dashboard_type == "forecast":
            return self._prepare_forecast_dashboard(dashboard)
        else:
            return {"error": f"Unknown dashboard type: {dashboard_type}"}
    
    def _prepare_anomaly_dashboard(self, dashboard: dict) -> dict:
        """Update anomaly detection dashboard."""
        # Fetch latest metrics
        metrics = self._fetch_metrics(dashboard["metrics"])
        
        # Run AI analysis
        prompt = f"""Analyze these metrics for anomalies:
{json.dumps(metrics, indent=2)}

Return JSON with:
- "anomalies": list of detected anomalies
- "severity": "low" | "medium" | "high"
- "recommendations": list of suggested actions
- "chart_data": object for visualization update
"""
        
        response = ollama.chat(
            model=self.config.get("model", "llama3"),
            messages=[{"role": "user", "content": prompt}],
            options={"temperature": 0.3}
        )
        
        try:
            analysis = json.loads(response["message"]["content"])
            return {
                "dashboard_id": dashboard["id"],
                "panel_updates": analysis.get("chart_data", {}),
                "alerts": analysis.get("anomalies", []),
                "severity": analysis.get("severity", "low"),
                "timestamp": datetime.now().isoformat()
            }
        except json.JSONDecodeError:
            return {"dashboard_id": dashboard["id"], "error": "Parse failed"}
    
    def _fetch_metrics(self, metric_names: list[str]) -> dict:
        """Fetch current values for specified metrics."""
        # Implementation depends on metrics source
        return {name: self._fetch_single_metric(name) for name in metric_names}
    
    def _fetch_single_metric(self, metric_name: str) -> dict:
        """Fetch a single metric value."""
        # Placeholder - implement based on your metrics system
        return {"value": 0, "timestamp": datetime.now().isoformat()}
    
    def _prepare_trend_dashboard(self, dashboard: dict) -> dict:
        """Update trend analysis dashboard."""
        historical_data = self._fetch_historical(dashboard["time_range"])
        
        prompt = f"""Analyze these historical trends and provide forecast:
{json.dumps(historical_data)}

Return JSON with:
- "trend_direction": "up" | "down" | "stable"
- "growth_rate": percentage
- "forecast": array of predicted values for next period
- "insights": key observations
"""
        
        response = ollama.chat(
            model=self.config.get("model", "llama3"),
            messages=[{"role": "user", "content": prompt}]
        )
        
        try:
            analysis = json.loads(response["message"]["content"])
            return {
                "dashboard_id": dashboard["id"],
                "trend_data": analysis,
                "timestamp": datetime.now().isoformat()
            }
        except json.JSONDecodeError:
            return {"dashboard_id": dashboard["id"], "error": "Parse failed"}
    
    def _prepare_forecast_dashboard(self, dashboard: dict) -> dict:
        """Update forecast dashboard."""
        features = self._prepare_forecast_features(dashboard)
        
        prompt = f"""Generate forecast based on these features:
{json.dumps(features)}

Return JSON with:
- "predictions": list of predicted values with confidence intervals
- "model_used": description of forecasting approach
- "accuracy_estimate": percentage
"""
        
        response = ollama.chat(
            model=self.config.get("model", "llama3"),
            messages=[{"role": "user", "content": prompt}]
        )
        
        try:
            forecast = json.loads(response["message"]["content"])
            return {
                "dashboard_id": dashboard["id"],
                "forecast": forecast,
                "timestamp": datetime.now().isoformat()
            }
        except json.JSONDecodeError:
            return {"dashboard_id": dashboard["id"], "error": "Parse failed"}
    
    def push_updates(self, updates: list[dict]):
        """Push dashboard updates to visualization platform."""
        for update in updates:
            if "error" in update:
                continue
            
            dashboard = self._find_dashboard(update["dashboard_id"])
            if not dashboard:
                continue
            
            self._update_grafana(dashboard, update) if dashboard["platform"] == "grafana" else None
            self._update_grafana(dashboard, update) if dashboard["platform"] == "grafana" else None
    
    def _update_grafana(self, dashboard: dict, update: dict):
        """Update Grafana panel with new data."""
        panel_url = dashboard["panel_url"]
        
        # Update specific panel via Grafana API
        headers = {"Authorization": f"Bearer {dashboard['api_key']}"}
        
        payload = self._format_grafana_payload(update)
        response = requests.patch(panel_url, json=payload, headers=headers)
        
        self._log_update(dashboard["id"], response.status_code)
    
    def _log_update(self, dashboard_id: str, status_code: int):
        """Record update attempt."""
        with open(self.update_log, "a") as f:
            f.write(json.dumps({
                "dashboard_id": dashboard_id,
                "status_code": status_code,
                "timestamp": datetime.now().isoformat()
            }) + "\n")

Automated Refresh Schedule

# Run as a continuous service
if __name__ == "__main__":
    import time
    
    updater = DashboardUpdater()
    
    while True:
        try:
            updates = updater.check_for_updates()
            if updates:
                updater.push_updates(updates)
                print(f"Pushed {len(updates)} dashboard updates")
        except Exception as e:
            print(f"Update cycle failed: {e}")
        
        time.sleep(60)  # Check every minute
// dashboard_config.json
{
  "model": "llama3",
  "dashboards": [
    {
      "id": "sales_anomalies",
      "type": "anomaly_detection",
      "metrics": ["revenue", "orders", "conversion_rate"],
      "refresh_minutes": 15,
      "platform": "grafana",
      "panel_url": "https://grafana.example.com/api/panels/123",
      "api_key": "your-api-key",
      "last_update": null
    },
    {
      "id": "inventory_trends",
      "type": "trend_analysis",
      "time_range": "7d",
      "refresh_minutes": 60,
      "platform": "grafana",
      "panel_url": "https://grafana.example.com/api/panels/456",
      "api_key": "your-api-key"
    },
    {
      "id": "demand_forecast",
      "type": "forecast",
      "time_range": "30d",
      "refresh_minutes": 240,
      "platform": "grafana",
      "panel_url": "https://grafana.example.com/api/panels/789",
      "api_key": "your-api-key"
    }
  ]
}
EXERCISE

Build a dashboard updater that monitors your system's error rate and automatically annotates Grafana panels with AI-generated explanations when error rate exceeds 5%.