import argparse import json from pathlib import Path VALID_ICONS = { "detector-smoke", "detector-heat", "module-input", "siren", "panel-nfs320", } def parse_args(): parser = argparse.ArgumentParser(description="Gera bundle versionavel de planta baixa para o Grafana") parser.add_argument("spec", help="Arquivo JSON com o mapa da planta") parser.add_argument( "--output", help="Arquivo de saida. Default: mesmo nome com sufixo .bundle.json", ) return parser.parse_args() def validate_item(item: dict, seen_ids: set[str]) -> dict: item_id = str(item.get("id", "")).strip() if not item_id: raise ValueError("Cada item precisa de id.") if item_id in seen_ids: raise ValueError(f"Item duplicado: {item_id}") seen_ids.add(item_id) icon = str(item.get("icon", "")).strip() if icon not in VALID_ICONS: raise ValueError(f"Icone invalido para item {item_id}: {icon}") label = str(item.get("label", "")).strip() item_key = str(item.get("item_key", "")).strip() if not label: raise ValueError(f"Label obrigatorio para item {item_id}.") if not item_key: raise ValueError(f"item_key obrigatorio para item {item_id}.") for field in ("x", "y", "width", "height"): value = item.get(field) if not isinstance(value, int) or value < 0: raise ValueError(f"Campo {field} invalido para item {item_id}.") rotation = item.get("rotation", 0) if not isinstance(rotation, int): raise ValueError(f"rotation invalido para item {item_id}.") return { "id": item_id, "label": label, "item_key": item_key, "icon": icon, "x": item["x"], "y": item["y"], "width": item["width"], "height": item["height"], "rotation": rotation, } def build_bundle(spec: dict) -> dict: title = str(spec.get("title", "")).strip() dashboard_uid = str(spec.get("grafana_dashboard_uid", "")).strip() background_image_url = str(spec.get("background_image_url", "")).strip() vfire_base_url = str(spec.get("vfire_base_url", "")).rstrip("/") zabbix_host = str(spec.get("zabbix_host", "")).strip() if not title: raise ValueError("title obrigatorio.") if not dashboard_uid: raise ValueError("grafana_dashboard_uid obrigatorio.") if not background_image_url: raise ValueError("background_image_url obrigatorio.") if not vfire_base_url: raise ValueError("vfire_base_url obrigatorio.") if not zabbix_host: raise ValueError("zabbix_host obrigatorio.") raw_items = spec.get("items") if not isinstance(raw_items, list) or not raw_items: raise ValueError("items deve conter ao menos um elemento.") seen_ids: set[str] = set() items = [validate_item(item, seen_ids) for item in raw_items] for item in items: item["icon_url"] = f"{vfire_base_url}/static/grafana-icons/{item['icon']}.svg" item["zabbix_ref"] = { "host": zabbix_host, "item_key": item["item_key"], } return { "title": title, "grafana_dashboard_uid": dashboard_uid, "background_image_url": background_image_url, "vfire_base_url": vfire_base_url, "zabbix_host": zabbix_host, "items": items, } def main(): args = parse_args() spec_path = Path(args.spec) output_path = Path(args.output) if args.output else spec_path.with_suffix(".bundle.json") spec = json.loads(spec_path.read_text(encoding="utf-8")) bundle = build_bundle(spec) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(json.dumps(bundle, indent=2, ensure_ascii=False), encoding="utf-8") print(output_path) if __name__ == "__main__": main()