- Fix Python formatting (sync no longer strips blank lines from .py files) - Remove test_community_build.py (tests private sync module) - Fix ruff warnings in test files - Add CODE_OF_CONDUCT.md - Add .github/PULL_REQUEST_TEMPLATE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
343 lines
12 KiB
Python
343 lines
12 KiB
Python
"""
|
|
Dashboards Handler - Dashboards & Panels
|
|
|
|
Phase J.4: 8 tools
|
|
- Dashboards: list, get, create, update, delete (5)
|
|
- Panels: list, create, delete (3)
|
|
"""
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from plugins.directus.client import DirectusClient
|
|
|
|
|
|
def get_tool_specifications() -> list[dict[str, Any]]:
|
|
"""Return tool specifications for ToolGenerator (8 tools)"""
|
|
return [
|
|
# =====================
|
|
# DASHBOARDS (5)
|
|
# =====================
|
|
{
|
|
"name": "list_dashboards",
|
|
"method_name": "list_dashboards",
|
|
"description": "List all insights dashboards.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"filter": {
|
|
"anyOf": [{"type": "object"}, {"type": "null"}],
|
|
"description": "Filter object",
|
|
},
|
|
"sort": {
|
|
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
|
"description": "Sort fields",
|
|
},
|
|
"limit": {
|
|
"type": "integer",
|
|
"description": "Maximum dashboards to return",
|
|
"default": 100,
|
|
},
|
|
},
|
|
"required": [],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "get_dashboard",
|
|
"method_name": "get_dashboard",
|
|
"description": "Get dashboard details by ID.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {"id": {"type": "string", "description": "Dashboard UUID"}},
|
|
"required": ["id"],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "create_dashboard",
|
|
"method_name": "create_dashboard",
|
|
"description": "Create a new insights dashboard.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {"type": "string", "description": "Dashboard name"},
|
|
"icon": {
|
|
"type": "string",
|
|
"description": "Material icon",
|
|
"default": "dashboard",
|
|
},
|
|
"note": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Dashboard description",
|
|
},
|
|
"color": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Accent color",
|
|
},
|
|
},
|
|
"required": ["name"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
{
|
|
"name": "update_dashboard",
|
|
"method_name": "update_dashboard",
|
|
"description": "Update dashboard settings.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {"type": "string", "description": "Dashboard UUID"},
|
|
"data": {
|
|
"type": "object",
|
|
"description": "Fields to update (name, icon, note, color)",
|
|
},
|
|
},
|
|
"required": ["id", "data"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
{
|
|
"name": "delete_dashboard",
|
|
"method_name": "delete_dashboard",
|
|
"description": "Delete a dashboard and its panels.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {"id": {"type": "string", "description": "Dashboard UUID to delete"}},
|
|
"required": ["id"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
# =====================
|
|
# PANELS (3)
|
|
# =====================
|
|
{
|
|
"name": "list_panels",
|
|
"method_name": "list_panels",
|
|
"description": "List all panels, optionally filtered by dashboard.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"filter": {
|
|
"anyOf": [{"type": "object"}, {"type": "null"}],
|
|
"description": 'Filter object (e.g., {"dashboard": {"_eq": "dashboard-uuid"}})',
|
|
},
|
|
"limit": {
|
|
"type": "integer",
|
|
"description": "Maximum panels to return",
|
|
"default": 100,
|
|
},
|
|
},
|
|
"required": [],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "create_panel",
|
|
"method_name": "create_panel",
|
|
"description": "Create a new panel in a dashboard.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"dashboard": {"type": "string", "description": "Dashboard UUID"},
|
|
"type": {
|
|
"type": "string",
|
|
"description": "Panel type (label, list, metric, time-series, etc.)",
|
|
},
|
|
"name": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Panel name",
|
|
},
|
|
"icon": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Material icon",
|
|
},
|
|
"color": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Accent color",
|
|
},
|
|
"note": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Panel description",
|
|
},
|
|
"width": {
|
|
"type": "integer",
|
|
"description": "Panel width (1-12 grid units)",
|
|
"default": 12,
|
|
},
|
|
"height": {
|
|
"type": "integer",
|
|
"description": "Panel height (grid units)",
|
|
"default": 6,
|
|
},
|
|
"position_x": {
|
|
"type": "integer",
|
|
"description": "X position in dashboard",
|
|
"default": 0,
|
|
},
|
|
"position_y": {
|
|
"type": "integer",
|
|
"description": "Y position in dashboard",
|
|
"default": 0,
|
|
},
|
|
"options": {
|
|
"anyOf": [{"type": "object"}, {"type": "null"}],
|
|
"description": "Panel-specific options",
|
|
},
|
|
},
|
|
"required": ["dashboard", "type"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
{
|
|
"name": "delete_panel",
|
|
"method_name": "delete_panel",
|
|
"description": "Delete a panel from a dashboard.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {"id": {"type": "string", "description": "Panel UUID to delete"}},
|
|
"required": ["id"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
]
|
|
|
|
|
|
# =====================
|
|
# HANDLER FUNCTIONS
|
|
# =====================
|
|
|
|
|
|
async def list_dashboards(
|
|
client: DirectusClient,
|
|
filter: dict | None = None,
|
|
sort: list[str] | None = None,
|
|
limit: int = 100,
|
|
) -> str:
|
|
"""List dashboards."""
|
|
try:
|
|
result = await client.list_dashboards(filter=filter, sort=sort, limit=limit)
|
|
dashboards = result.get("data", [])
|
|
return json.dumps(
|
|
{"success": True, "total": len(dashboards), "dashboards": dashboards},
|
|
indent=2,
|
|
ensure_ascii=False,
|
|
)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
|
|
async def get_dashboard(client: DirectusClient, id: str) -> str:
|
|
"""Get dashboard by ID."""
|
|
try:
|
|
result = await client.get_dashboard(id)
|
|
return json.dumps(
|
|
{"success": True, "dashboard": result.get("data")}, indent=2, ensure_ascii=False
|
|
)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
|
|
async def create_dashboard(
|
|
client: DirectusClient,
|
|
name: str,
|
|
icon: str = "dashboard",
|
|
note: str | None = None,
|
|
color: str | None = None,
|
|
) -> str:
|
|
"""Create a new dashboard."""
|
|
try:
|
|
result = await client.create_dashboard(name=name, icon=icon, note=note, color=color)
|
|
return json.dumps(
|
|
{
|
|
"success": True,
|
|
"message": f"Dashboard '{name}' created",
|
|
"dashboard": result.get("data"),
|
|
},
|
|
indent=2,
|
|
ensure_ascii=False,
|
|
)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
|
|
async def update_dashboard(client: DirectusClient, id: str, data: dict[str, Any]) -> str:
|
|
"""Update dashboard."""
|
|
try:
|
|
result = await client.update_dashboard(id, data)
|
|
return json.dumps(
|
|
{"success": True, "message": "Dashboard updated", "dashboard": result.get("data")},
|
|
indent=2,
|
|
ensure_ascii=False,
|
|
)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
|
|
async def delete_dashboard(client: DirectusClient, id: str) -> str:
|
|
"""Delete a dashboard."""
|
|
try:
|
|
await client.delete_dashboard(id)
|
|
return json.dumps({"success": True, "message": f"Dashboard {id} deleted"}, indent=2)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
|
|
async def list_panels(client: DirectusClient, filter: dict | None = None, limit: int = 100) -> str:
|
|
"""List panels."""
|
|
try:
|
|
result = await client.list_panels(filter=filter, limit=limit)
|
|
panels = result.get("data", [])
|
|
return json.dumps(
|
|
{"success": True, "total": len(panels), "panels": panels}, indent=2, ensure_ascii=False
|
|
)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
|
|
async def create_panel(
|
|
client: DirectusClient,
|
|
dashboard: str,
|
|
type: str,
|
|
name: str | None = None,
|
|
icon: str | None = None,
|
|
color: str | None = None,
|
|
note: str | None = None,
|
|
width: int = 12,
|
|
height: int = 6,
|
|
position_x: int = 0,
|
|
position_y: int = 0,
|
|
options: dict | None = None,
|
|
) -> str:
|
|
"""Create a new panel."""
|
|
try:
|
|
result = await client.create_panel(
|
|
dashboard=dashboard,
|
|
type=type,
|
|
name=name,
|
|
icon=icon,
|
|
color=color,
|
|
note=note,
|
|
width=width,
|
|
height=height,
|
|
position_x=position_x,
|
|
position_y=position_y,
|
|
options=options,
|
|
)
|
|
return json.dumps(
|
|
{"success": True, "message": f"Panel '{type}' created", "panel": result.get("data")},
|
|
indent=2,
|
|
ensure_ascii=False,
|
|
)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
|
|
async def delete_panel(client: DirectusClient, id: str) -> str:
|
|
"""Delete a panel."""
|
|
try:
|
|
await client.delete_panel(id)
|
|
return json.dumps({"success": True, "message": f"Panel {id} deleted"}, indent=2)
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|