ipydagflow 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ipydagflow/__init__.py ADDED
@@ -0,0 +1,47 @@
1
+ """
2
+ ipydagflow - Interactive DAG visualization widgets for Jupyter.
3
+
4
+ This library provides tools for creating and visualizing directed acyclic graphs (DAGs)
5
+ in Jupyter notebooks using React Flow.
6
+
7
+ Main classes:
8
+ - DynamicDAG: Low-level widget for rendering DAGs from nodes/edges
9
+ - StepDAG: High-level DAG builder using Step objects
10
+ - Step: Represents a single step/node in a workflow
11
+
12
+ Example (Low-level):
13
+ >>> from ipydagflow import DynamicDAG
14
+ >>> nodes = [
15
+ ... {"id": "1", "type": "datasource", "data": {"label": "Input"}},
16
+ ... {"id": "2", "type": "box", "data": {"label": "Process"}},
17
+ ... ]
18
+ >>> edges = [{"id": "e1", "source": "1", "target": "2"}]
19
+ >>> DynamicDAG(nodes=nodes, edges=edges)
20
+
21
+ Example (High-level):
22
+ >>> from ipydagflow import StepDAG, Step
23
+ >>> extract = Step(id="extract", label="Extract", step_type="datasource")
24
+ >>> transform = Step(id="transform", label="Transform", step_type="box")
25
+ >>> extract.add_child(transform)
26
+ >>> dag = StepDAG()
27
+ >>> dag.add_steps(extract, transform)
28
+ >>> dag.render()
29
+ """
30
+
31
+ import importlib.metadata
32
+
33
+ try:
34
+ __version__ = importlib.metadata.version("ipydagflow")
35
+ except importlib.metadata.PackageNotFoundError:
36
+ __version__ = "unknown"
37
+
38
+ # Main exports
39
+ from .models import Step
40
+ from .widgets import DynamicDAG, StepDAG
41
+
42
+ __all__ = [
43
+ "DynamicDAG",
44
+ "Step",
45
+ "StepDAG",
46
+ "__version__",
47
+ ]
@@ -0,0 +1,5 @@
1
+ """Data models for ipydagflow."""
2
+
3
+ from .step import Step
4
+
5
+ __all__ = ["Step"]
@@ -0,0 +1,115 @@
1
+ """Step class for building DAGs programmatically."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, List, Set
5
+
6
+
7
+ @dataclass
8
+ class Step:
9
+ """
10
+ Represents a single step/node in a DAG workflow.
11
+
12
+ Attributes:
13
+ id: Unique identifier for the step
14
+ label: Display label for the step
15
+ step_type: Type of step (datasource, box, etc.)
16
+ data: Additional metadata for the step
17
+ children: List of child steps
18
+ parents: List of parent steps
19
+
20
+ Example:
21
+ >>> step1 = Step(id="extract", label="Extract Data", step_type="datasource")
22
+ >>> step2 = Step(id="transform", label="Transform", step_type="box")
23
+ >>> step1.add_child(step2)
24
+ """
25
+
26
+ id: str
27
+ label: str
28
+ step_type: str = "box"
29
+ data: Dict[str, Any] = field(default_factory=dict)
30
+ children: List["Step"] = field(default_factory=list, repr=False)
31
+ parents: List["Step"] = field(default_factory=list, repr=False)
32
+
33
+ def add_child(self, child: "Step") -> "Step":
34
+ """
35
+ Add a child step.
36
+
37
+ Args:
38
+ child: The child step to add
39
+
40
+ Returns:
41
+ The child step (for chaining)
42
+ """
43
+ if child not in self.children:
44
+ self.children.append(child)
45
+ if self not in child.parents:
46
+ child.parents.append(self)
47
+ return child
48
+
49
+ def add_children(self, *children: "Step") -> List["Step"]:
50
+ """
51
+ Add multiple child steps.
52
+
53
+ Args:
54
+ *children: Variable number of child steps
55
+
56
+ Returns:
57
+ List of child steps
58
+ """
59
+ return [self.add_child(child) for child in children]
60
+
61
+ def add_parent(self, parent: "Step") -> "Step":
62
+ """
63
+ Add a parent step.
64
+
65
+ Args:
66
+ parent: The parent step to add
67
+
68
+ Returns:
69
+ The parent step (for chaining)
70
+ """
71
+ if parent not in self.parents:
72
+ self.parents.append(parent)
73
+ if self not in parent.children:
74
+ parent.children.append(self)
75
+ return parent
76
+
77
+ def get_all_descendants(self) -> Set["Step"]:
78
+ """Get all descendant steps (children, grandchildren, etc.)."""
79
+ descendants = set()
80
+ to_visit = list(self.children)
81
+
82
+ while to_visit:
83
+ step = to_visit.pop()
84
+ if step not in descendants:
85
+ descendants.add(step)
86
+ to_visit.extend(step.children)
87
+
88
+ return descendants
89
+
90
+ def get_all_ancestors(self) -> Set["Step"]:
91
+ """Get all ancestor steps (parents, grandparents, etc.)."""
92
+ ancestors = set()
93
+ to_visit = list(self.parents)
94
+
95
+ while to_visit:
96
+ step = to_visit.pop()
97
+ if step not in ancestors:
98
+ ancestors.add(step)
99
+ to_visit.extend(step.parents)
100
+
101
+ return ancestors
102
+
103
+ def __hash__(self) -> int:
104
+ """Make Step hashable based on id."""
105
+ return hash(self.id)
106
+
107
+ def __eq__(self, other: Any) -> bool:
108
+ """Check equality based on id."""
109
+ if not isinstance(other, Step):
110
+ return False
111
+ return self.id == other.id
112
+
113
+ def __repr__(self) -> str:
114
+ """String representation."""
115
+ return f"Step(id='{self.id}', label='{self.label}', type='{self.step_type}')"
@@ -0,0 +1 @@
1
+ .react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgb(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgb(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color, var(--xy-background-color-props, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}.react-flow__node{font-size:14px;cursor:pointer}.react-flow__node:hover{filter:brightness(1.05)}.custom-node{padding:16px 20px;border:2px solid #2c3e50;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;transition:all .2s ease;min-width:140px;text-align:center}.custom-node:hover{transform:translateY(-2px);box-shadow:0 4px 12px #0003}.custom-node.selected{border-color:#3498db;box-shadow:0 0 0 3px #3498db4d}.datasource-node{border-radius:50px 50px 12px 12px}.box-node{border-radius:8px}.node-content{display:flex;flex-direction:column;align-items:center;gap:4px}.node-label{font-weight:600;font-size:13px;line-height:1.3}.custom-node .react-flow__handle{width:10px;height:10px;background:#fff;border:2px solid #2c3e50}.custom-node .react-flow__handle:hover{width:12px;height:12px}.react-flow__edge-path{stroke-width:2}.react-flow__handle{width:8px;height:8px;border-radius:50%}.node-popup{position:absolute;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000}.popup-content{background:#fff;border-radius:8px;padding:20px;max-width:500px;min-width:300px;box-shadow:0 4px 20px #0000004d;position:relative}.close-btn{position:absolute;top:10px;right:10px;background:none;border:none;font-size:24px;cursor:pointer;color:#666;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:4px}.close-btn:hover{background:#f0f0f0;color:#333}.popup-content h3{margin-top:0;margin-bottom:15px;color:#2c3e50;font-size:18px;font-weight:700;border-bottom:2px solid #3498db;padding-bottom:10px}.popup-body p{margin:8px 0;color:#555;font-size:14px}.popup-body strong{color:#333;font-weight:600}.popup-body pre{background:#f5f5f5;color:#2c3e50;padding:10px;border-radius:4px;overflow-x:auto;font-size:12px;margin-top:8px}