package visualization import ( "fmt" "strings" ) // TaskNode represents a task in the DAG type TaskNode struct { ID string Status string // pending, running, completed, failed Duration float64 Critical bool } // DAGRenderer renders workflow dependency graphs type DAGRenderer struct { nodes map[string]*TaskNode edges map[string][]string } // NewDAGRenderer creates a new DAG renderer func NewDAGRenderer() *DAGRenderer { return &DAGRenderer{ nodes: make(map[string]*TaskNode), edges: make(map[string][]string), } } // AddNode adds a task node func (dr *DAGRenderer) AddNode(id, status string, duration float64) { dr.nodes[id] = &TaskNode{ ID: id, Status: status, Duration: duration, } } // AddEdge adds a dependency edge func (dr *DAGRenderer) AddEdge(from, to string) error { if _, exists := dr.nodes[from]; !exists { return fmt.Errorf("source node not found: %s", from) } if _, exists := dr.nodes[to]; !exists { return fmt.Errorf("target node not found: %s", to) } dr.edges[from] = append(dr.edges[from], to) return nil } // MarkCriticalPath marks nodes on the critical path func (dr *DAGRenderer) MarkCriticalPath(nodes []string) error { for _, nodeID := range nodes { if node, exists := dr.nodes[nodeID]; exists { node.Critical = true } else { return fmt.Errorf("node not found: %s", nodeID) } } return nil } // RenderDOT generates DOT format for Graphviz func (dr *DAGRenderer) RenderDOT() string { var buf strings.Builder buf.WriteString("digraph WorkflowDAG {\n") buf.WriteString(" rankdir=LR;\n") buf.WriteString(" node [shape=box];\n\n") // Render nodes for _, node := range dr.nodes { color := "lightgray" if node.Critical { color = "red" } else if node.Status == "completed" { color = "lightgreen" } else if node.Status == "failed" { color = "lightcoral" } else if node.Status == "running" { color = "lightyellow" } label := fmt.Sprintf("%s\\n%.0fms", node.ID, node.Duration) buf.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\", fillcolor=%s, style=filled];\n", node.ID, label, color)) } buf.WriteString("\n") // Render edges for from, tos := range dr.edges { for _, to := range tos { buf.WriteString(fmt.Sprintf(" \"%s\" -> \"%s\";\n", from, to)) } } buf.WriteString("}\n") return buf.String() } // RenderHTML generates a simple HTML visualization func (dr *DAGRenderer) RenderHTML() string { var buf strings.Builder buf.WriteString("
\n") buf.WriteString("| Task ID | Status | Duration (ms) | Critical Path |
|---|---|---|---|
| %s | %s | %.0f | %s |