This commit is contained in:
dingfeng.wong
2025-07-10 00:21:43 +08:00
parent cfd4e08e4e
commit 6461b7e906
2 changed files with 136 additions and 0 deletions
+4
View File
@@ -1,6 +1,7 @@
import typer
from rich.console import Console
from .wrap_cli import wrap_app
from .grafana_cli import grafana_app
app = typer.Typer()
console = Console()
@@ -8,5 +9,8 @@ console = Console()
# Add the wrap subcommand to the main app
app.add_typer(wrap_app, name="wrap")
# Add the grafana subcommand to the main app
app.add_typer(grafana_app, name="grafana")
if __name__ == "__main__":
app()
+132
View File
@@ -0,0 +1,132 @@
import typer
from rich.console import Console
import json
import pyperclip
from typing import Dict, Any, List, Optional
from pathlib import Path
console = Console()
# Create the main grafana subcommand
grafana_app = typer.Typer()
def safe_get(data: Dict[str, Any], key: str, default: Any = None) -> Any:
"""Safely get a value from a dictionary."""
return data.get(key, default)
def mutate_panel(panel: Dict[str, Any]) -> None:
"""Mutate a single panel by setting repeat and repeatDirection fields."""
# Check if type is "row" and remove repeat key if it exists
if safe_get(panel, 'type') == 'row':
if 'repeat' in panel:
del panel['repeat']
else:
# For non-row panels, set repeat and repeatDirection
panel['repeat'] = 'instance'
panel['repeatDirection'] = 'h'
panel['maxPerRow'] = 12
def mutate_panels_recursive(panels: List[Dict[str, Any]]) -> None:
"""Recursively mutate panels, handling nested panels."""
for panel in panels:
if not isinstance(panel, dict):
continue
# Mutate current panel
mutate_panel(panel)
# Check for nested panels
nested_panels = safe_get(panel, 'panels', [])
if nested_panels and isinstance(nested_panels, list):
mutate_panels_recursive(nested_panels)
def mutate_grafana_json(data: Dict[str, Any]) -> Dict[str, Any]:
"""
Mutate Grafana JSON by setting repeat and repeatDirection fields.
Args:
data: The Grafana dashboard JSON data
Returns:
The mutated JSON data
"""
# Make a copy to avoid mutating the original
mutated_data = json.loads(json.dumps(data))
# Get panels safely
panels = safe_get(mutated_data, 'panels', [])
if panels and isinstance(panels, list):
mutate_panels_recursive(panels)
return mutated_data
@grafana_app.command("mutate", short_help="Mutate Grafana JSON to set repeat fields.")
def mutate_dashboard(
input_file: Optional[Path] = typer.Option(
None, "--input-file", "-i", help="Input JSON file path. If not provided, reads from clipboard."
),
output_file: Optional[Path] = typer.Option(
None, "--output-file", "-o", help="Output JSON file path. If not provided, outputs to clipboard."
)
):
"""
Mutate Grafana JSON by setting repeat="instance" and repeatDirection="h".
This command processes Grafana dashboard JSON and modifies panels to:
- Set repeat field to "instance"
- Set repeatDirection field to "h"
- Set maxPerRow to 12
- Remove repeat field from row-type panels
"""
console.print("[blue]Processing Grafana dashboard JSON...[/blue]")
# Read input
try:
if input_file:
console.print(f"[blue]Reading from file: {input_file}[/blue]")
if not input_file.exists():
console.print(f"[red]Error: Input file '{input_file}' not found[/red]")
raise typer.Exit(code=1)
json_content = input_file.read_text(encoding='utf-8')
else:
console.print("[blue]Reading from clipboard...[/blue]")
json_content = pyperclip.paste()
if not json_content.strip():
console.print("[red]Error: Clipboard is empty or contains no text.[/red]")
raise typer.Exit(code=1)
except Exception as e:
console.print(f"[red]Error reading input: {e}[/red]")
raise typer.Exit(code=1)
# Parse JSON
try:
data = json.loads(json_content)
except json.JSONDecodeError as e:
console.print(f"[red]Error parsing JSON: {e}[/red]")
raise typer.Exit(code=1)
# Mutate the data
try:
mutated_data = mutate_grafana_json(data)
console.print("[green]Successfully mutated Grafana JSON![/green]")
except Exception as e:
console.print(f"[red]Error mutating JSON: {e}[/red]")
raise typer.Exit(code=1)
# Output result
try:
output_json = json.dumps(mutated_data, indent=2, ensure_ascii=False)
if output_file:
console.print(f"[blue]Writing to file: {output_file}[/blue]")
output_file.write_text(output_json, encoding='utf-8')
console.print(f"[green]Output written to {output_file}[/green]")
else:
console.print("[blue]Copying to clipboard...[/blue]")
pyperclip.copy(output_json)
console.print("[green]Output copied to clipboard![/green]")
except Exception as e:
console.print(f"[red]Error writing output: {e}[/red]")
raise typer.Exit(code=1)