taskdependencygraph 0.1.0__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.
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: taskdependencygraph
3
+ Version: 0.1.0
4
+ Summary: Wrapper around a NetworkX Digraph to model and visualize tasks/todos, their duration and interdependencies
5
+ Project-URL: Changelog, https://github.com/Hochfrequenz/task-dependency-graph/releases
6
+ Project-URL: Homepage, https://github.com/Hochfrequenz/task-dependency-graph
7
+ Author-email: Hochfrequenz Unternehmensberatung GmbH <info+github@hochfrequenz.de>
8
+ License: MIT
9
+ Keywords: digraph,graph,kroki,networkx,task,task dependency
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: aiohttp
22
+ Requires-Dist: networkx
23
+ Requires-Dist: pydantic-settings
24
+ Requires-Dist: pydantic[email]>=2
25
+ Provides-Extra: coverage
26
+ Requires-Dist: coverage==7.6.12; extra == 'coverage'
27
+ Provides-Extra: dev
28
+ Requires-Dist: pip-tools; extra == 'dev'
29
+ Provides-Extra: formatting
30
+ Requires-Dist: black==25.1.0; extra == 'formatting'
31
+ Requires-Dist: isort==6.0.1; extra == 'formatting'
32
+ Provides-Extra: linting
33
+ Requires-Dist: pylint==3.3.4; extra == 'linting'
34
+ Provides-Extra: packaging
35
+ Requires-Dist: build==1.2.2.post1; extra == 'packaging'
36
+ Requires-Dist: twine==6.1.0; extra == 'packaging'
37
+ Provides-Extra: spell-check
38
+ Requires-Dist: codespell==2.4.1; extra == 'spell-check'
39
+ Provides-Extra: tests
40
+ Requires-Dist: pytest-asyncio==0.21.2; extra == 'tests'
41
+ Requires-Dist: pytest==8.3.5; extra == 'tests'
42
+ Requires-Dist: testcontainers; extra == 'tests'
43
+ Provides-Extra: type-check
44
+ Requires-Dist: mypy==1.15.0; extra == 'type-check'
45
+ Requires-Dist: types-docker; extra == 'type-check'
46
+ Description-Content-Type: text/markdown
47
+
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
49
+ ![Python Versions (officially) supported](https://img.shields.io/pypi/pyversions/taskdependencygraph.svg)
50
+ ![Pypi status badge](https://img.shields.io/pypi/v/taskdependencygraph)
51
+
52
+ # Task Dependency Graph
53
+
54
+ Task Dependency Graph is a Python package that allows to model tasks and dependencies between tasks as a directed graph.
55
+ It also supports visualizing the graph with [dot](https://graphviz.org/docs/layouts/dot/) for a graph-like view
56
+ or [mermaid](https://mermaid.js.org/) for Gantt charts.
57
+
58
+ The package is built on [networkx](https://networkx.org/) and under the hood the task dependency graph is just a
59
+ networkx [DiGraph](https://networkx.org/documentation/stable/reference/classes/digraph.html).
60
+ For visualization, it uses GraphViz via [kroki](https://kroki.io/) (in a Docker container).
61
+
62
+ ## Example / How to use
63
+
64
+ Install the package from [PyPI](https://pypi.org/project/taskdependencygraph/)
65
+
66
+ ```bash
67
+ pip install taskdependencygraph
68
+ ```
69
+
70
+ Imagine the following scenario:
71
+
72
+ You and your partner are invited to a birthday party.
73
+ You promised to bring a cake.
74
+ Baking the cake and the recipe can be divided into atomic tasks, all of which have a duration.
75
+ Those are the **nodes** of the task dependency graph (TDG).
76
+
77
+ Tasks are created like this:
78
+
79
+ ```python
80
+ import uuid
81
+ from datetime import timedelta
82
+
83
+ from taskdependencygraph.models import TaskNode
84
+
85
+ my_task = TaskNode(
86
+ id=uuid.uuid4(), # boilerplate only, but you need the ID to find nodes in your graph later on
87
+ name="Shop groceries", # human readable description
88
+ external_id="some unique string", # technical ID for those who don't like uuids ;)
89
+ planned_duration=timedelta(minutes=15) # how long it probably takes
90
+ # You may also add an assignees or an earliest_possible_start
91
+ # (The latter is useful, when e.g. the supermarket opens at 7am and you cannot shop groceries before,
92
+ # even if you were awake and have nothing else todo.)
93
+ )
94
+ ```
95
+
96
+ The tasks depend on each other:
97
+ You cannot prepare the cake without buying the ingredients first.
98
+ You cannot decorate the cake before you've made it.
99
+ Which task has which mandatory predecessor tasks is defined in task dependencies.
100
+ Those are the **edges** of our task dependency graph.
101
+
102
+ Task dependencies are created like this:
103
+
104
+ ```python
105
+ import uuid
106
+
107
+ from taskdependencygraph.models import TaskDependencyEdge, TaskNode
108
+
109
+ shopping_groceries = TaskNode(...)
110
+ mixing_flour_and_sugar = TaskNode(...)
111
+ baking_in_the_oven = TaskNode(...)
112
+
113
+ buy_ingredients_before_mixing_them = TaskDependencyEdge(
114
+ id=uuid.uuid4(), # boilerplate
115
+ predecessor_task=shopping_groceries.id,
116
+ successor_task=mixing_flour_and_sugar.id
117
+ )
118
+ mix_ingredients_before_baking_the_cake = TaskDependencyEdge(
119
+ id=uuid.uuid4(), # boilerplate
120
+ predecessor_task=mixing_flour_and_sugar.id,
121
+ successor_task=baking_in_the_oven.id
122
+ )
123
+ ```
124
+
125
+ The graph is made out of tasks (nodes), task dependencies (edges) and a start datetime.
126
+
127
+ ```python
128
+ from datetime import datetime, UTC
129
+
130
+ from taskdependencygraph import TaskDependencyGraph
131
+ from taskdependencygraph.models import TaskNode, TaskDependencyEdge
132
+
133
+ # nodes
134
+ shopping_groceries = TaskNode(...)
135
+ mixing_flour_and_sugar = TaskNode(...)
136
+ baking_in_the_oven = TaskNode(...)
137
+
138
+ # edges
139
+ buy_ingredients_before_mixing_them = TaskDependencyEdge(...)
140
+ mix_ingredients_before_baking_the_cake = TaskDependencyEdge(...)
141
+
142
+ # graph
143
+ tdg = TaskDependencyGraph(
144
+ task_list=[shopping_groceries, mixing_flour_and_sugar, baking_in_the_oven],
145
+ dependency_list=[buy_ingredients_before_mixing_them, mix_ingredients_before_baking_the_cake],
146
+ starting_time_of_run=datetime(2025, 1, 1, 12, 0, 0, tzinfo=UTC)
147
+ )
148
+ ```
149
+
150
+ Now you can
151
+
152
+ * calculate at which time which task is scheduled to start depending on which predecessors it has,
153
+ * find out which tasks are 'critical' in sense that if they're delayed, then the finishing time of the last node is also
154
+ delayed,
155
+ * assign persons to tasks and check if any person has more than one task assigned at a time.
156
+
157
+ Find a complete working example in [the demo unittest](unittests/test_demonstration.py).
158
+ This demo test is also the basis for the following visualization examples.
159
+
160
+ ## Visualization with Kroki
161
+
162
+ You can visualize the dependencies either as rather simple technical graph or as Gantt chart, when you start kroki in a
163
+ docker container:
164
+
165
+ ```yaml
166
+ # docker-compose.yaml
167
+ services:
168
+ kroki: # see https://docs.kroki.io/kroki/setup/use-docker-or-podman/#_run_multiple_kroki_containers_together
169
+ image: yuzutech/kroki:0.24.1
170
+ depends_on:
171
+ - mermaid
172
+ environment:
173
+ - KROKI_MERMAID_HOST=mermaid
174
+ ports:
175
+ - "8123:8000"
176
+ mermaid:
177
+ image: yuzutech/kroki-mermaid
178
+ # run
179
+ # docker-compose up -d
180
+ # and kroki is ready at localhost:8123
181
+ ```
182
+
183
+ ```python
184
+ import asyncio
185
+
186
+ from taskdependencygraph import TaskDependencyGraph
187
+ from taskdependencygraph.plotting import KrokiClient, KrokiConfig
188
+
189
+
190
+ async def plot_a_graph() -> None:
191
+ tdg = TaskDependencyGraph(...) # with all nodes and edges and stuff
192
+
193
+ config = KrokiConfig(host="http://localhost:8123") # w/o docker, you may also use kroki.io, but it's rate limited
194
+ kroki_client = KrokiClient(config=config)
195
+
196
+ await kroki_client.plot_as_svg(tdg, mode="gantt") # or mode="dot"
197
+
198
+
199
+ if __name__ == '__main__':
200
+ loop = asyncio.get_event_loop()
201
+ loop.run_until_complete(plot_a_graph())
202
+ ```
203
+
204
+ The result may look like this:
205
+
206
+ ### Gantt Chart (Mermaid)
207
+
208
+ ![A Gantt chart](unittests/baking_a_cake_gantt.svg)
209
+
210
+ The tasks marked in red mark the critical path on which delays affect the finishing time.
211
+ The 🔶 are milestones which mark important moments in your project (often you want to have a group of tasks like '
212
+ Shopping' done before starting with the next step, even though there's no "real" dependency between e.g. Cake Base and
213
+ buying the strawberries.)
214
+ The Gantt chart is useful to get an overview of your project and to identify which tasks are crucial.
215
+
216
+ ### Raw Graph ("dot" engine)
217
+
218
+ ![](unittests/baking_a_cake_dot.svg)
219
+
220
+ The raw graph helps you to understand the tasks and dependencies setup in a not so shiny but verbose fashion.
221
+
222
+ ## Storing the Graph in a Database
223
+ You can store the nodes and edges on a database.
224
+ We suggest to just use two tables: One for the edges, one for the nodes.
225
+ You can even add trigger-based [DB constraints to prevent loops in the graph](https://gist.github.com/hf-kklein/49f6d05bd29ca850e33f5ccff3e66469) which are faster than you might guess, even for hundreds of tasks.
226
+
227
+ ## Maintainers/ Further Development / Professional Support
228
+ This library was built for and then cut out of a mainly internal project by [@hf-crings](https://github.com/hf-crings), [@OLILHR](https://github.com/OLILHR), [@hf-sheese](https://github.com/hf-sheese) and [@hf-kklein](https://github.com/hf-kklein), but we decided to publish it, because it might be useful to someone.
229
+ This is why some things are hardcoded here and there and why some features might seem unintuitive at first glance.
230
+
231
+ We at Hochfrequenz also built a SQLAlchemy+FastAPI+htmx web application around this library in which you can plan and schedule time-critical tasks and projects in the browser.
232
+ It's ready to use, but not pretty enough to publish it yet ;)
233
+ Just ping us if interested.
@@ -0,0 +1,19 @@
1
+ _taskdependencygraph_version.py,sha256=aOHawL1zuHMfBWKXqwUkXcW96oXLNCY-CXdHDqkz4g4,18
2
+ taskdependencygraph/__init__.py,sha256=BPP3kiAUt-k5HhjHH0vVWB8nBxi2Xy2rMx3ihQxBOVU,248
3
+ taskdependencygraph/py.typed,sha256=ZvYa-_QF7Xg7F6Dow8m1zHvSP6kNHuhh3BEBVxjAdUQ,168
4
+ taskdependencygraph/task_dependency_graph.py,sha256=CEsTM1j3366UT-w1QwuflDKmncn4lEznvTfIgg561Gg,29606
5
+ taskdependencygraph/models/__init__.py,sha256=Dho2VKW4qrrzEzcqoOx_dZzY3dyRlNHJhqdEvH5CoQ0,788
6
+ taskdependencygraph/models/ids.py,sha256=3cHAujoJhHzg6NZIdwtehGPf_w506rsr74ynPtTTb4o,898
7
+ taskdependencygraph/models/person.py,sha256=xYYfOdRfVHGTPjq7RLujspFywDhcMkbrmBPSAghLPtM,629
8
+ taskdependencygraph/models/task_dependency_edge.py,sha256=nJTEtPiuEYs6T-aQ_wRs7IQ3QfkzaFoDx_1g3b83_Ps,1297
9
+ taskdependencygraph/models/task_dependency_update.py,sha256=_amKiccJR7K1fkrXehKXNGLtvyFng0wjXnVet7jI2UA,2164
10
+ taskdependencygraph/models/task_execution_status.py,sha256=oENBNxwPmr8TKJE-LmDknYpAdCjlNJd0RE0hwRbCfp0,1113
11
+ taskdependencygraph/models/task_node.py,sha256=S_30Ptq-xR1UcvRrc5oUscqBey4KIo79NWV47ayE8eo,5358
12
+ taskdependencygraph/models/task_node_as_artificial_endnode.py,sha256=oztA175PxdTHry-i7Oq5uv3p7exNeAu5UpolSut3BLY,933
13
+ taskdependencygraph/models/task_node_as_artificial_startnode.py,sha256=zR9OsM5VyCXxZ-3XadJZs0B_hf_cejYIaFBBc9Waz38,984
14
+ taskdependencygraph/plotting/__init__.py,sha256=KhefeR-FCRw-8swt9sds3uW6oik2AjgdYusKhxB30Ps,613
15
+ taskdependencygraph/plotting/kroki.py,sha256=2EvAL2LZw0nQPg7M8uGstfu0CeqFTjgIRs7Hx7U9ezA,5988
16
+ taskdependencygraph/plotting/protocols.py,sha256=lXRF2MZJQLoOYEkROfK7S-eb69qpwiR2kP4Hf2t86Qo,1015
17
+ taskdependencygraph-0.1.0.dist-info/METADATA,sha256=eZ7oZWQ37P9DgXkgdKJ6ROGIgvdJyYI0FQFJ1k8YQsM,9288
18
+ taskdependencygraph-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ taskdependencygraph-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any