ipydagflow 0.0.1__tar.gz

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.
@@ -0,0 +1,12 @@
1
+ node_modules
2
+ .venv
3
+ dist
4
+ .DS_Store
5
+
6
+ # Python
7
+ __pycache__
8
+ .ipynb_checkpoints
9
+ .pytest_cache
10
+ .ruff_cache
11
+
12
+ src/ipydagflow/static
@@ -0,0 +1,268 @@
1
+ Metadata-Version: 2.4
2
+ Name: ipydagflow
3
+ Version: 0.0.1
4
+ Requires-Python: >=3.9
5
+ Requires-Dist: anywidget
6
+ Provides-Extra: dev
7
+ Requires-Dist: jupyterlab; extra == 'dev'
8
+ Requires-Dist: pytest-cov; extra == 'dev'
9
+ Requires-Dist: pytest>=7.0; extra == 'dev'
10
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
11
+ Requires-Dist: watchfiles; extra == 'dev'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # ipydagflow
15
+
16
+ Interactive DAG (Directed Acyclic Graph) visualization widgets for Jupyter notebooks using React Flow.
17
+
18
+ ## Features
19
+
20
+ - 🎨 **Beautiful Interactive Visualizations** - Smooth, modern DAG rendering with React Flow
21
+ - 🔧 **Two APIs** - Low-level (DynamicDAG) and high-level (StepDAG) interfaces
22
+ - 🎯 **Automatic Layout** - No need to specify positions - nodes are laid out automatically
23
+ - 🎨 **Customizable Styles** - Full control over colors, borders, and appearance
24
+ - 📊 **Built for Data Pipelines** - Perfect for ETL workflows, ML pipelines, and task dependencies
25
+ - ✅ **Validation** - Automatic cycle detection and DAG validation
26
+ - 🔍 **Interactive** - Click nodes to see details, drag to rearrange
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install ipydagflow
32
+ ```
33
+
34
+ For development:
35
+ ```bash
36
+ git clone https://github.com/yourusername/ipydagflow
37
+ cd ipydagflow
38
+ pip install -e .
39
+ ```
40
+
41
+ ## Quick Start
42
+
43
+ ### High-Level API (StepDAG)
44
+
45
+ Build DAGs programmatically using Step objects:
46
+
47
+ ```python
48
+ from ipydagflow import StepDAG, Step
49
+
50
+ # Create steps
51
+ extract = Step(id="extract", label="Extract Data", step_type="datasource")
52
+ transform = Step(id="transform", label="Transform", step_type="box")
53
+ load = Step(id="load", label="Load to DB", step_type="datasource")
54
+
55
+ # Connect steps
56
+ extract.add_child(transform)
57
+ transform.add_child(load)
58
+
59
+ # Create and display DAG
60
+ dag = StepDAG()
61
+ dag.add_steps(extract, transform, load)
62
+ dag.render() # Returns a widget to display in Jupyter
63
+ ```
64
+
65
+ ### Low-Level API (DynamicDAG)
66
+
67
+ For direct control over nodes and edges:
68
+
69
+ ```python
70
+ from ipydagflow import DynamicDAG
71
+
72
+ nodes = [
73
+ {"id": "1", "type": "datasource", "data": {"label": "Input"}},
74
+ {"id": "2", "type": "box", "data": {"label": "Process"}},
75
+ {"id": "3", "type": "datasource", "data": {"label": "Output"}},
76
+ ]
77
+
78
+ edges = [
79
+ {"id": "e1", "source": "1", "target": "2"},
80
+ {"id": "e2", "source": "2", "target": "3"},
81
+ ]
82
+
83
+ dag = DynamicDAG(nodes=nodes, edges=edges)
84
+ dag # Display in Jupyter
85
+ ```
86
+
87
+ ## Examples
88
+
89
+ ### Complex Pipeline
90
+
91
+ ```python
92
+ from ipydagflow import StepDAG, Step
93
+
94
+ # Build a data processing pipeline
95
+ raw = Step(id="raw", label="Raw Data", step_type="datasource")
96
+ clean = Step(id="clean", label="Clean", step_type="box")
97
+ feature_a = Step(id="feat_a", label="Feature A", step_type="box")
98
+ feature_b = Step(id="feat_b", label="Feature B", step_type="box")
99
+ combine = Step(id="combine", label="Combine", step_type="box")
100
+ output = Step(id="output", label="Output", step_type="datasource")
101
+
102
+ # Connect: raw -> clean -> [feature_a, feature_b] -> combine -> output
103
+ raw.add_child(clean)
104
+ clean.add_children(feature_a, feature_b)
105
+ feature_a.add_child(combine)
106
+ feature_b.add_child(combine)
107
+ combine.add_child(output)
108
+
109
+ # Render
110
+ dag = StepDAG()
111
+ dag.add_steps(raw, clean, feature_a, feature_b, combine, output)
112
+ dag.render()
113
+ ```
114
+
115
+ ### Custom Styling
116
+
117
+ ```python
118
+ custom_styles = {
119
+ "datasource": {
120
+ "background": "rgba(255, 99, 132, 0.8)",
121
+ "color": "white",
122
+ "borderColor": "#ff6384",
123
+ "borderWidth": 3,
124
+ },
125
+ "box": {
126
+ "background": "rgba(54, 162, 235, 0.8)",
127
+ "color": "white",
128
+ "borderColor": "#36a2eb",
129
+ "borderWidth": 2,
130
+ },
131
+ }
132
+
133
+ dag = StepDAG(styles=custom_styles)
134
+ dag.add_steps(step1, step2, step3)
135
+ dag.render()
136
+ ```
137
+
138
+ ### Adding Metadata
139
+
140
+ ```python
141
+ step = Step(
142
+ id="process",
143
+ label="Calculate Metrics",
144
+ step_type="box",
145
+ data={
146
+ "owner": "data-team",
147
+ "schedule": "0 0 * * *",
148
+ "retry": 3,
149
+ "timeout": "1h"
150
+ }
151
+ )
152
+ # Click the node in the widget to see the metadata!
153
+ ```
154
+
155
+ ## API Reference
156
+
157
+ ### Step
158
+
159
+ ```python
160
+ Step(
161
+ id: str, # Unique identifier
162
+ label: str, # Display label
163
+ step_type: str, # Node type: "datasource", "box", or custom
164
+ data: dict # Additional metadata
165
+ )
166
+ ```
167
+
168
+ **Methods:**
169
+ - `add_child(step)` - Add a child step
170
+ - `add_children(*steps)` - Add multiple children
171
+ - `add_parent(step)` - Add a parent step
172
+ - `get_all_descendants()` - Get all descendant steps
173
+ - `get_all_ancestors()` - Get all ancestor steps
174
+
175
+ ### StepDAG
176
+
177
+ ```python
178
+ StepDAG(styles: dict = None)
179
+ ```
180
+
181
+ **Methods:**
182
+ - `add_step(step)` - Add a step
183
+ - `add_steps(*steps)` - Add multiple steps
184
+ - `get_step(step_id)` - Get step by ID
185
+ - `get_all_steps()` - Get all steps
186
+ - `get_root_steps()` - Get steps with no parents
187
+ - `get_leaf_steps()` - Get steps with no children
188
+ - `validate()` - Validate DAG (returns list of errors)
189
+ - `render()` - Create and return DynamicDAG widget
190
+
191
+ ### DynamicDAG
192
+
193
+ ```python
194
+ DynamicDAG(
195
+ nodes: list, # List of node dicts
196
+ edges: list, # List of edge dicts
197
+ styles: dict = None # Custom styling
198
+ )
199
+ ```
200
+
201
+ **Node format:**
202
+ ```python
203
+ {
204
+ "id": "unique_id",
205
+ "type": "datasource", # or "box"
206
+ "data": {"label": "Display Name", ...},
207
+ "position": {"x": 100, "y": 100} # Optional - auto-layout if omitted
208
+ }
209
+ ```
210
+
211
+ **Edge format:**
212
+ ```python
213
+ {
214
+ "id": "edge_id",
215
+ "source": "source_node_id",
216
+ "target": "target_node_id",
217
+ "animated": True # Optional
218
+ }
219
+ ```
220
+
221
+ ## Development
222
+
223
+ ### Setup
224
+
225
+ ```bash
226
+ # Clone the repo
227
+ git clone https://github.com/yourusername/ipydagflow
228
+ cd ipydagflow
229
+
230
+ # Install dependencies
231
+ npm install
232
+ pip install -e .
233
+
234
+ # Build JavaScript
235
+ npm run build
236
+
237
+ # Run in dev mode (auto-rebuild on changes)
238
+ npm run dev
239
+ ```
240
+
241
+ ### Project Structure
242
+
243
+ ```
244
+ ipydagflow/
245
+ ├── src/
246
+ │ └── ipydagflow/
247
+ │ ├── __init__.py # Main exports
248
+ │ ├── widgets/ # Widget classes
249
+ │ │ ├── dynamic_dag.py # Low-level widget
250
+ │ │ └── step_dag.py # High-level builder
251
+ │ ├── models/ # Data models
252
+ │ │ └── step.py # Step class
253
+ │ ├── utils/ # Utilities
254
+ │ │ └── layout.py # Layout algorithms
255
+ │ └── static/ # Built JS/CSS
256
+ ├── ui/ # TypeScript/React source
257
+ │ ├── dynamic_dag.tsx # React Flow component
258
+ │ └── dynamic_dag.css # Styles
259
+ └── examples/ # Example notebooks
260
+ ```
261
+
262
+ ## License
263
+
264
+ MIT
265
+
266
+ ## Contributing
267
+
268
+ Contributions welcome! Please open an issue or PR on GitHub.
@@ -0,0 +1,255 @@
1
+ # ipydagflow
2
+
3
+ Interactive DAG (Directed Acyclic Graph) visualization widgets for Jupyter notebooks using React Flow.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Beautiful Interactive Visualizations** - Smooth, modern DAG rendering with React Flow
8
+ - 🔧 **Two APIs** - Low-level (DynamicDAG) and high-level (StepDAG) interfaces
9
+ - 🎯 **Automatic Layout** - No need to specify positions - nodes are laid out automatically
10
+ - 🎨 **Customizable Styles** - Full control over colors, borders, and appearance
11
+ - 📊 **Built for Data Pipelines** - Perfect for ETL workflows, ML pipelines, and task dependencies
12
+ - ✅ **Validation** - Automatic cycle detection and DAG validation
13
+ - 🔍 **Interactive** - Click nodes to see details, drag to rearrange
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install ipydagflow
19
+ ```
20
+
21
+ For development:
22
+ ```bash
23
+ git clone https://github.com/yourusername/ipydagflow
24
+ cd ipydagflow
25
+ pip install -e .
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ### High-Level API (StepDAG)
31
+
32
+ Build DAGs programmatically using Step objects:
33
+
34
+ ```python
35
+ from ipydagflow import StepDAG, Step
36
+
37
+ # Create steps
38
+ extract = Step(id="extract", label="Extract Data", step_type="datasource")
39
+ transform = Step(id="transform", label="Transform", step_type="box")
40
+ load = Step(id="load", label="Load to DB", step_type="datasource")
41
+
42
+ # Connect steps
43
+ extract.add_child(transform)
44
+ transform.add_child(load)
45
+
46
+ # Create and display DAG
47
+ dag = StepDAG()
48
+ dag.add_steps(extract, transform, load)
49
+ dag.render() # Returns a widget to display in Jupyter
50
+ ```
51
+
52
+ ### Low-Level API (DynamicDAG)
53
+
54
+ For direct control over nodes and edges:
55
+
56
+ ```python
57
+ from ipydagflow import DynamicDAG
58
+
59
+ nodes = [
60
+ {"id": "1", "type": "datasource", "data": {"label": "Input"}},
61
+ {"id": "2", "type": "box", "data": {"label": "Process"}},
62
+ {"id": "3", "type": "datasource", "data": {"label": "Output"}},
63
+ ]
64
+
65
+ edges = [
66
+ {"id": "e1", "source": "1", "target": "2"},
67
+ {"id": "e2", "source": "2", "target": "3"},
68
+ ]
69
+
70
+ dag = DynamicDAG(nodes=nodes, edges=edges)
71
+ dag # Display in Jupyter
72
+ ```
73
+
74
+ ## Examples
75
+
76
+ ### Complex Pipeline
77
+
78
+ ```python
79
+ from ipydagflow import StepDAG, Step
80
+
81
+ # Build a data processing pipeline
82
+ raw = Step(id="raw", label="Raw Data", step_type="datasource")
83
+ clean = Step(id="clean", label="Clean", step_type="box")
84
+ feature_a = Step(id="feat_a", label="Feature A", step_type="box")
85
+ feature_b = Step(id="feat_b", label="Feature B", step_type="box")
86
+ combine = Step(id="combine", label="Combine", step_type="box")
87
+ output = Step(id="output", label="Output", step_type="datasource")
88
+
89
+ # Connect: raw -> clean -> [feature_a, feature_b] -> combine -> output
90
+ raw.add_child(clean)
91
+ clean.add_children(feature_a, feature_b)
92
+ feature_a.add_child(combine)
93
+ feature_b.add_child(combine)
94
+ combine.add_child(output)
95
+
96
+ # Render
97
+ dag = StepDAG()
98
+ dag.add_steps(raw, clean, feature_a, feature_b, combine, output)
99
+ dag.render()
100
+ ```
101
+
102
+ ### Custom Styling
103
+
104
+ ```python
105
+ custom_styles = {
106
+ "datasource": {
107
+ "background": "rgba(255, 99, 132, 0.8)",
108
+ "color": "white",
109
+ "borderColor": "#ff6384",
110
+ "borderWidth": 3,
111
+ },
112
+ "box": {
113
+ "background": "rgba(54, 162, 235, 0.8)",
114
+ "color": "white",
115
+ "borderColor": "#36a2eb",
116
+ "borderWidth": 2,
117
+ },
118
+ }
119
+
120
+ dag = StepDAG(styles=custom_styles)
121
+ dag.add_steps(step1, step2, step3)
122
+ dag.render()
123
+ ```
124
+
125
+ ### Adding Metadata
126
+
127
+ ```python
128
+ step = Step(
129
+ id="process",
130
+ label="Calculate Metrics",
131
+ step_type="box",
132
+ data={
133
+ "owner": "data-team",
134
+ "schedule": "0 0 * * *",
135
+ "retry": 3,
136
+ "timeout": "1h"
137
+ }
138
+ )
139
+ # Click the node in the widget to see the metadata!
140
+ ```
141
+
142
+ ## API Reference
143
+
144
+ ### Step
145
+
146
+ ```python
147
+ Step(
148
+ id: str, # Unique identifier
149
+ label: str, # Display label
150
+ step_type: str, # Node type: "datasource", "box", or custom
151
+ data: dict # Additional metadata
152
+ )
153
+ ```
154
+
155
+ **Methods:**
156
+ - `add_child(step)` - Add a child step
157
+ - `add_children(*steps)` - Add multiple children
158
+ - `add_parent(step)` - Add a parent step
159
+ - `get_all_descendants()` - Get all descendant steps
160
+ - `get_all_ancestors()` - Get all ancestor steps
161
+
162
+ ### StepDAG
163
+
164
+ ```python
165
+ StepDAG(styles: dict = None)
166
+ ```
167
+
168
+ **Methods:**
169
+ - `add_step(step)` - Add a step
170
+ - `add_steps(*steps)` - Add multiple steps
171
+ - `get_step(step_id)` - Get step by ID
172
+ - `get_all_steps()` - Get all steps
173
+ - `get_root_steps()` - Get steps with no parents
174
+ - `get_leaf_steps()` - Get steps with no children
175
+ - `validate()` - Validate DAG (returns list of errors)
176
+ - `render()` - Create and return DynamicDAG widget
177
+
178
+ ### DynamicDAG
179
+
180
+ ```python
181
+ DynamicDAG(
182
+ nodes: list, # List of node dicts
183
+ edges: list, # List of edge dicts
184
+ styles: dict = None # Custom styling
185
+ )
186
+ ```
187
+
188
+ **Node format:**
189
+ ```python
190
+ {
191
+ "id": "unique_id",
192
+ "type": "datasource", # or "box"
193
+ "data": {"label": "Display Name", ...},
194
+ "position": {"x": 100, "y": 100} # Optional - auto-layout if omitted
195
+ }
196
+ ```
197
+
198
+ **Edge format:**
199
+ ```python
200
+ {
201
+ "id": "edge_id",
202
+ "source": "source_node_id",
203
+ "target": "target_node_id",
204
+ "animated": True # Optional
205
+ }
206
+ ```
207
+
208
+ ## Development
209
+
210
+ ### Setup
211
+
212
+ ```bash
213
+ # Clone the repo
214
+ git clone https://github.com/yourusername/ipydagflow
215
+ cd ipydagflow
216
+
217
+ # Install dependencies
218
+ npm install
219
+ pip install -e .
220
+
221
+ # Build JavaScript
222
+ npm run build
223
+
224
+ # Run in dev mode (auto-rebuild on changes)
225
+ npm run dev
226
+ ```
227
+
228
+ ### Project Structure
229
+
230
+ ```
231
+ ipydagflow/
232
+ ├── src/
233
+ │ └── ipydagflow/
234
+ │ ├── __init__.py # Main exports
235
+ │ ├── widgets/ # Widget classes
236
+ │ │ ├── dynamic_dag.py # Low-level widget
237
+ │ │ └── step_dag.py # High-level builder
238
+ │ ├── models/ # Data models
239
+ │ │ └── step.py # Step class
240
+ │ ├── utils/ # Utilities
241
+ │ │ └── layout.py # Layout algorithms
242
+ │ └── static/ # Built JS/CSS
243
+ ├── ui/ # TypeScript/React source
244
+ │ ├── dynamic_dag.tsx # React Flow component
245
+ │ └── dynamic_dag.css # Styles
246
+ └── examples/ # Example notebooks
247
+ ```
248
+
249
+ ## License
250
+
251
+ MIT
252
+
253
+ ## Contributing
254
+
255
+ Contributions welcome! Please open an issue or PR on GitHub.
@@ -0,0 +1,84 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "ipydagflow"
7
+ version = "0.0.1"
8
+ dependencies = ["anywidget"]
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+
12
+ [project.optional-dependencies]
13
+ dev = [
14
+ "ruff>=0.6.0",
15
+ "watchfiles",
16
+ "jupyterlab",
17
+ "pytest>=7.0",
18
+ "pytest-cov",
19
+ ]
20
+
21
+ [tool.uv]
22
+ dev-dependencies = [
23
+ "pytest>=7.0",
24
+ "pytest-cov",
25
+ "ruff>=0.6.0",
26
+ "watchfiles",
27
+ "jupyterlab",
28
+ ]
29
+
30
+ [tool.hatch.envs.default]
31
+ features = ["dev"]
32
+
33
+ [tool.hatch.build]
34
+ only-packages = true
35
+ artifacts = ["src/ipydagflow/static/*"]
36
+
37
+ [tool.hatch.build.hooks.jupyter-builder]
38
+ build-function = "hatch_jupyter_builder.npm_builder"
39
+ ensured-targets = ["src/ipydagflow/static/dynamic_dag.js"]
40
+ skip-if-exists = ["src/ipydagflow/static/dynamic_dag.js"]
41
+ dependencies = ["hatch-jupyter-builder>=0.5.0"]
42
+
43
+ [tool.hatch.build.hooks.jupyter-builder.build-kwargs]
44
+ npm = "npm"
45
+ build_cmd = "build"
46
+
47
+ # ───────────────────────────────────────
48
+ # Ruff configuration
49
+ # ───────────────────────────────────────
50
+
51
+ [tool.ruff]
52
+ target-version = "py39"
53
+ line-length = 88
54
+ src = ["src", "tests"]
55
+ extend-exclude = ["src/ipydagflow/static"]
56
+
57
+ [tool.ruff.lint]
58
+ select = [
59
+ "E", # pycodestyle errors
60
+ "W", # pycodestyle warnings
61
+ "F", # pyflakes
62
+ "I", # isort
63
+ "B", # bugbear
64
+ "C4", # comprehensions
65
+ "UP", # pyupgrade
66
+ "RUF", # ruff-specific rules
67
+ "PL", # pylint (selected rules)
68
+ "SIM", # flake8-simplify
69
+ ]
70
+ ignore = [
71
+ "E501", # line too long → we use Ruff formatter
72
+ "PLR2004",# magic value comparison
73
+ ]
74
+
75
+ [tool.ruff.format]
76
+ quote-style = "double"
77
+ indent-style = "space"
78
+ skip-magic-trailing-comma = false
79
+
80
+ [tool.ruff.lint.per-file-ignores]
81
+ "tests/*" = ["S101"] # allow assert in tests
82
+
83
+ [tool.ruff.lint.mccabe]
84
+ max-complexity = 12
@@ -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"]