taskdependencygraph 0.1.0__tar.gz → 0.2.0__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.
Files changed (41) hide show
  1. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/dependabot.yml +0 -2
  2. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/codeql-analysis.yml +4 -4
  3. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/coverage.yml +5 -9
  4. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/dev_test.yml +3 -3
  5. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/formatting.yml +5 -7
  6. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/no_byte_order_mark.yml +1 -1
  7. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/packaging_test.yml +2 -2
  8. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/python-publish.yml +4 -4
  9. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/pythonlint.yml +5 -7
  10. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/unittests.yml +3 -3
  11. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/PKG-INFO +21 -17
  12. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/README.md +12 -9
  13. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/pyproject.toml +10 -9
  14. taskdependencygraph-0.2.0/requirements.txt +60 -0
  15. taskdependencygraph-0.2.0/src/_taskdependencygraph_version.py +1 -0
  16. taskdependencygraph-0.2.0/src/taskdependencygraph/__init__.py +59 -0
  17. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/__init__.py +27 -9
  18. taskdependencygraph-0.2.0/src/taskdependencygraph/models/graph_definition_validation.py +50 -0
  19. taskdependencygraph-0.2.0/src/taskdependencygraph/models/mermaid_gantt_config.py +67 -0
  20. taskdependencygraph-0.2.0/src/taskdependencygraph/models/schedule_report.py +47 -0
  21. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/task_node.py +8 -2
  22. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/task_node_as_artificial_endnode.py +0 -1
  23. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/plotting/protocols.py +3 -1
  24. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/task_dependency_graph.py +416 -19
  25. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/tox.ini +0 -2
  26. taskdependencygraph-0.1.0/.pre-commit-config.yaml +0 -24
  27. taskdependencygraph-0.1.0/requirements.txt +0 -54
  28. taskdependencygraph-0.1.0/src/_taskdependencygraph_version.py +0 -1
  29. taskdependencygraph-0.1.0/src/taskdependencygraph/__init__.py +0 -8
  30. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.github/workflows/dependabot_automerge.yml +0 -0
  31. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/.gitignore +0 -0
  32. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/domain-specific-terms.txt +0 -0
  33. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/ids.py +0 -0
  34. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/person.py +0 -0
  35. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/task_dependency_edge.py +0 -0
  36. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/task_dependency_update.py +0 -0
  37. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/task_execution_status.py +0 -0
  38. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/models/task_node_as_artificial_startnode.py +0 -0
  39. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/plotting/__init__.py +0 -0
  40. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/plotting/kroki.py +0 -0
  41. {taskdependencygraph-0.1.0 → taskdependencygraph-0.2.0}/src/taskdependencygraph/py.typed +0 -0
@@ -9,8 +9,6 @@ updates:
9
9
  directory: "/" # Location of package manifests
10
10
  schedule:
11
11
  interval: "weekly"
12
- reviewers:
13
- - "@Hochfrequenz/python-developers-review-team"
14
12
  # Maintain dependencies for GitHub Actions
15
13
  - package-ecosystem: "github-actions"
16
14
  directory: "/"
@@ -38,11 +38,11 @@ jobs:
38
38
 
39
39
  steps:
40
40
  - name: Checkout repository
41
- uses: actions/checkout@v4
41
+ uses: actions/checkout@v6
42
42
 
43
43
  # Initializes the CodeQL tools for scanning.
44
44
  - name: Initialize CodeQL
45
- uses: github/codeql-action/init@v3
45
+ uses: github/codeql-action/init@v4
46
46
  with:
47
47
  languages: ${{ matrix.language }}
48
48
  # If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
53
53
  # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
54
  # If this step fails, then you should remove it and run the build manually (see below)
55
55
  - name: Autobuild
56
- uses: github/codeql-action/autobuild@v3
56
+ uses: github/codeql-action/autobuild@v4
57
57
 
58
58
  # ℹ️ Command-line programs to run using the OS shell.
59
59
  # 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
67
67
  # make release
68
68
 
69
69
  - name: Perform CodeQL Analysis
70
- uses: github/codeql-action/analyze@v3
70
+ uses: github/codeql-action/analyze@v4
@@ -6,17 +6,13 @@ on:
6
6
  pull_request: {}
7
7
  jobs:
8
8
  coverage:
9
- runs-on: ${{ matrix.os }}
10
- strategy:
11
- matrix:
12
- python-version: ["3.13"]
13
- os: [ubuntu-latest]
9
+ runs-on: ubuntu-latest
14
10
  steps:
15
- - uses: actions/checkout@v4
16
- - name: Set up Python ${{ matrix.python-version }}
17
- uses: actions/setup-python@v5
11
+ - uses: actions/checkout@v6
12
+ - name: Set up Python 3.14
13
+ uses: actions/setup-python@v6
18
14
  with:
19
- python-version: ${{ matrix.python-version }}
15
+ python-version: "3.14"
20
16
  - name: Install dependencies
21
17
  run: |
22
18
  python -m pip install --upgrade pip
@@ -11,12 +11,12 @@ jobs:
11
11
  runs-on: ${{ matrix.os }}
12
12
  strategy:
13
13
  matrix:
14
- python-version: ["3.11", "3.12", "3.13"]
14
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
15
15
  os: [ubuntu-latest]
16
16
  steps:
17
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v6
18
18
  - name: Set up Python ${{ matrix.python-version }}
19
- uses: actions/setup-python@v5
19
+ uses: actions/setup-python@v6
20
20
  with:
21
21
  python-version: ${{ matrix.python-version }}
22
22
  - name: Install Dependencies
@@ -6,18 +6,16 @@ on:
6
6
  pull_request: {}
7
7
  jobs:
8
8
  black:
9
- runs-on: ${{ matrix.os }}
9
+ runs-on: ubuntu-latest
10
10
  strategy:
11
11
  matrix:
12
- python-version: ["3.13"]
13
- os: [ubuntu-latest]
14
12
  tool: ["black", "isort"]
15
13
  steps:
16
- - uses: actions/checkout@v4
17
- - name: Set up Python ${{ matrix.python-version }}
18
- uses: actions/setup-python@v5
14
+ - uses: actions/checkout@v6
15
+ - name: Set up Python 3.14
16
+ uses: actions/setup-python@v6
19
17
  with:
20
- python-version: ${{ matrix.python-version }}
18
+ python-version: "3.14"
21
19
  - name: Install dependencies
22
20
  run: |
23
21
  python -m pip install --upgrade pip
@@ -10,6 +10,6 @@ jobs:
10
10
  bom-check:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v4
13
+ - uses: actions/checkout@v6
14
14
  - uses: arma-actions/bom-check@v1
15
15
  name: Check for BOM
@@ -12,9 +12,9 @@ jobs:
12
12
  python-version: ["3.13"]
13
13
  os: [ubuntu-latest]
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
16
16
  - name: Set up Python ${{ matrix.python-version }}
17
- uses: actions/setup-python@v5
17
+ uses: actions/setup-python@v6
18
18
  with:
19
19
  python-version: ${{ matrix.python-version }}
20
20
  - name: Install dependencies
@@ -20,9 +20,9 @@
20
20
  python-version: ["3.13"]
21
21
  os: [ubuntu-latest]
22
22
  steps:
23
- - uses: actions/checkout@v4
23
+ - uses: actions/checkout@v6
24
24
  - name: Set up Python ${{ matrix.python-version }}
25
- uses: actions/setup-python@v5
25
+ uses: actions/setup-python@v6
26
26
  with:
27
27
  python-version: ${{ matrix.python-version }}
28
28
  - name: Install tox
@@ -48,9 +48,9 @@
48
48
  id-token: write
49
49
  needs: tests
50
50
  steps:
51
- - uses: actions/checkout@v4
51
+ - uses: actions/checkout@v6
52
52
  - name: Set up Python ${{ matrix.python-version }}
53
- uses: actions/setup-python@v5
53
+ uses: actions/setup-python@v6
54
54
  with:
55
55
  python-version: ${{ matrix.python-version }}
56
56
  - name: Install dependencies
@@ -7,18 +7,16 @@ on:
7
7
  jobs:
8
8
  pylint:
9
9
  name: Python Code Quality and Lint
10
- runs-on: ${{ matrix.os }}
10
+ runs-on: ubuntu-latest
11
11
  strategy:
12
12
  matrix:
13
- python-version: ["3.13"]
14
- os: [ubuntu-latest]
15
13
  linter-env: ["linting", "type_check", "spell_check"]
16
14
  steps:
17
- - uses: actions/checkout@v4
18
- - name: Set up Python ${{ matrix.python-version }}
19
- uses: actions/setup-python@v5
15
+ - uses: actions/checkout@v6
16
+ - name: Set up Python 3.14
17
+ uses: actions/setup-python@v6
20
18
  with:
21
- python-version: ${{ matrix.python-version }}
19
+ python-version: "3.14"
22
20
  - name: Install Dependencies
23
21
  run: |
24
22
  python -m pip install --upgrade pip
@@ -9,12 +9,12 @@ jobs:
9
9
  runs-on: ${{ matrix.os }}
10
10
  strategy:
11
11
  matrix:
12
- python-version: ["3.11", "3.12", "3.13"]
12
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
13
13
  os: [ubuntu-latest]
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
16
16
  - name: Set up Python ${{ matrix.python-version }}
17
- uses: actions/setup-python@v5
17
+ uses: actions/setup-python@v6
18
18
  with:
19
19
  python-version: ${{ matrix.python-version }}
20
20
  - name: Install Dependencies
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskdependencygraph
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Wrapper around a NetworkX Digraph to model and visualize tasks/todos, their duration and interdependencies
5
5
  Project-URL: Changelog, https://github.com/Hochfrequenz/task-dependency-graph/releases
6
6
  Project-URL: Homepage, https://github.com/Hochfrequenz/task-dependency-graph
@@ -17,31 +17,32 @@ Classifier: Programming Language :: Python :: 3 :: Only
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
20
21
  Requires-Python: >=3.11
21
22
  Requires-Dist: aiohttp
22
23
  Requires-Dist: networkx
23
24
  Requires-Dist: pydantic-settings
24
25
  Requires-Dist: pydantic[email]>=2
25
26
  Provides-Extra: coverage
26
- Requires-Dist: coverage==7.6.12; extra == 'coverage'
27
+ Requires-Dist: coverage==7.9.1; extra == 'coverage'
27
28
  Provides-Extra: dev
28
29
  Requires-Dist: pip-tools; extra == 'dev'
29
30
  Provides-Extra: formatting
30
- Requires-Dist: black==25.1.0; extra == 'formatting'
31
- Requires-Dist: isort==6.0.1; extra == 'formatting'
31
+ Requires-Dist: black==26.5.1; extra == 'formatting'
32
+ Requires-Dist: isort==8.0.1; extra == 'formatting'
32
33
  Provides-Extra: linting
33
- Requires-Dist: pylint==3.3.4; extra == 'linting'
34
+ Requires-Dist: pylint==4.0.5; extra == 'linting'
34
35
  Provides-Extra: packaging
35
36
  Requires-Dist: build==1.2.2.post1; extra == 'packaging'
36
37
  Requires-Dist: twine==6.1.0; extra == 'packaging'
37
38
  Provides-Extra: spell-check
38
39
  Requires-Dist: codespell==2.4.1; extra == 'spell-check'
39
40
  Provides-Extra: tests
40
- Requires-Dist: pytest-asyncio==0.21.2; extra == 'tests'
41
- Requires-Dist: pytest==8.3.5; extra == 'tests'
41
+ Requires-Dist: pytest-asyncio==1.1.0; extra == 'tests'
42
+ Requires-Dist: pytest==8.4.2; extra == 'tests'
42
43
  Requires-Dist: testcontainers; extra == 'tests'
43
44
  Provides-Extra: type-check
44
- Requires-Dist: mypy==1.15.0; extra == 'type-check'
45
+ Requires-Dist: mypy==2.1.0; extra == 'type-check'
45
46
  Requires-Dist: types-docker; extra == 'type-check'
46
47
  Description-Content-Type: text/markdown
47
48
 
@@ -87,9 +88,7 @@ my_task = TaskNode(
87
88
  name="Shop groceries", # human readable description
88
89
  external_id="some unique string", # technical ID for those who don't like uuids ;)
89
90
  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.)
91
+ # You may also add an assignee, phase, tags, earliest_starttime, is_milestone, or execution_status
93
92
  )
94
93
  ```
95
94
 
@@ -147,12 +146,14 @@ tdg = TaskDependencyGraph(
147
146
  )
148
147
  ```
149
148
 
150
- Now you can
149
+ Once you have a graph, you can:
151
150
 
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.
151
+ * **Validate the definition** before construction — `TaskDependencyGraph.validate_definition(task_list, dependency_list)` checks for duplicate task/dependency IDs, duplicate external IDs, missing edge endpoints, invalid milestone durations, duplicate edge pairs, and cycles, returning a `GraphDefinitionValidationResult`.
152
+ * **Get the full schedule** `tdg.create_schedule_report()` returns a `ScheduleReport` with planned start/finish, critical-path flag, and total slack for every task.
153
+ * **Inspect the critical path** — `tdg.get_critical_path_tasks()` returns the ordered list of `TaskNode` objects on the critical path.
154
+ * **Calculate total slack** `tdg.calculate_total_slack_of_task(task_id)` returns how much a task can slip without affecting the deadline.
155
+ * **Query finish times** — `tdg.calculate_planned_finish_time_of_task(task_id)` and `tdg.calculate_planned_finish_time_of_graph()`.
156
+ * Assign persons to tasks and check if any person has more than one task assigned at a time.
156
157
 
157
158
  Find a complete working example in [the demo unittest](unittests/test_demonstration.py).
158
159
  This demo test is also the basis for the following visualization examples.
@@ -166,7 +167,7 @@ docker container:
166
167
  # docker-compose.yaml
167
168
  services:
168
169
  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
+ image: yuzutech/kroki:0.30.1
170
171
  depends_on:
171
172
  - mermaid
172
173
  environment:
@@ -213,6 +214,9 @@ Shopping' done before starting with the next step, even though there's no "real"
213
214
  buying the strawberries.)
214
215
  The Gantt chart is useful to get an overview of your project and to identify which tasks are crucial.
215
216
 
217
+ The Gantt output is customizable via `MermaidGanttConfig` — you can set the title, axis format, tick interval, and
218
+ group tasks into sections by their `phase` field.
219
+
216
220
  ### Raw Graph ("dot" engine)
217
221
 
218
222
  ![](unittests/baking_a_cake_dot.svg)
@@ -40,9 +40,7 @@ my_task = TaskNode(
40
40
  name="Shop groceries", # human readable description
41
41
  external_id="some unique string", # technical ID for those who don't like uuids ;)
42
42
  planned_duration=timedelta(minutes=15) # how long it probably takes
43
- # You may also add an assignees or an earliest_possible_start
44
- # (The latter is useful, when e.g. the supermarket opens at 7am and you cannot shop groceries before,
45
- # even if you were awake and have nothing else todo.)
43
+ # You may also add an assignee, phase, tags, earliest_starttime, is_milestone, or execution_status
46
44
  )
47
45
  ```
48
46
 
@@ -100,12 +98,14 @@ tdg = TaskDependencyGraph(
100
98
  )
101
99
  ```
102
100
 
103
- Now you can
101
+ Once you have a graph, you can:
104
102
 
105
- * calculate at which time which task is scheduled to start depending on which predecessors it has,
106
- * find out which tasks are 'critical' in sense that if they're delayed, then the finishing time of the last node is also
107
- delayed,
108
- * assign persons to tasks and check if any person has more than one task assigned at a time.
103
+ * **Validate the definition** before construction — `TaskDependencyGraph.validate_definition(task_list, dependency_list)` checks for duplicate task/dependency IDs, duplicate external IDs, missing edge endpoints, invalid milestone durations, duplicate edge pairs, and cycles, returning a `GraphDefinitionValidationResult`.
104
+ * **Get the full schedule** `tdg.create_schedule_report()` returns a `ScheduleReport` with planned start/finish, critical-path flag, and total slack for every task.
105
+ * **Inspect the critical path** — `tdg.get_critical_path_tasks()` returns the ordered list of `TaskNode` objects on the critical path.
106
+ * **Calculate total slack** `tdg.calculate_total_slack_of_task(task_id)` returns how much a task can slip without affecting the deadline.
107
+ * **Query finish times** — `tdg.calculate_planned_finish_time_of_task(task_id)` and `tdg.calculate_planned_finish_time_of_graph()`.
108
+ * Assign persons to tasks and check if any person has more than one task assigned at a time.
109
109
 
110
110
  Find a complete working example in [the demo unittest](unittests/test_demonstration.py).
111
111
  This demo test is also the basis for the following visualization examples.
@@ -119,7 +119,7 @@ docker container:
119
119
  # docker-compose.yaml
120
120
  services:
121
121
  kroki: # see https://docs.kroki.io/kroki/setup/use-docker-or-podman/#_run_multiple_kroki_containers_together
122
- image: yuzutech/kroki:0.24.1
122
+ image: yuzutech/kroki:0.30.1
123
123
  depends_on:
124
124
  - mermaid
125
125
  environment:
@@ -166,6 +166,9 @@ Shopping' done before starting with the next step, even though there's no "real"
166
166
  buying the strawberries.)
167
167
  The Gantt chart is useful to get an overview of your project and to identify which tasks are crucial.
168
168
 
169
+ The Gantt output is customizable via `MermaidGanttConfig` — you can set the title, axis format, tick interval, and
170
+ group tasks into sections by their `phase` field.
171
+
169
172
  ### Raw Graph ("dot" engine)
170
173
 
171
174
  ![](unittests/baking_a_cake_dot.svg)
@@ -16,6 +16,7 @@ classifiers = [
16
16
  "Programming Language :: Python :: 3.11",
17
17
  "Programming Language :: Python :: 3.12",
18
18
  "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
19
20
  ]
20
21
  dependencies = [
21
22
  "pydantic[email]>=2",
@@ -27,27 +28,27 @@ dynamic = ["readme", "version"]
27
28
 
28
29
  [project.optional-dependencies]
29
30
  tests = [
30
- "pytest==8.3.5",
31
- "pytest-asyncio==0.21.2",
31
+ "pytest==8.4.2",
32
+ "pytest-asyncio==1.1.0",
32
33
  "testcontainers",
33
34
  # "syrupy==4.8.2"
34
35
  ]
35
36
  linting = [
36
- "pylint==3.3.4"
37
+ "pylint==4.0.5"
37
38
  ]
38
39
  type_check = [
39
- "mypy==1.15.0",
40
+ "mypy==2.1.0",
40
41
  "types-docker"
41
42
  ]
42
43
  spell_check = [
43
44
  "codespell==2.4.1"
44
45
  ]
45
46
  coverage = [
46
- "coverage==7.6.12"
47
+ "coverage==7.9.1"
47
48
  ]
48
49
  formatting = [
49
- "black==25.1.0",
50
- "isort==6.0.1"
50
+ "black==26.5.1",
51
+ "isort==8.0.1"
51
52
  ]
52
53
  packaging = [
53
54
  "build==1.2.2.post1",
@@ -64,7 +65,7 @@ Homepage = "https://github.com/Hochfrequenz/task-dependency-graph"
64
65
 
65
66
  [tool.black]
66
67
  line-length = 120
67
- target_version = ["py311", "py312", "py313"]
68
+ target_version = ["py311", "py312", "py313", "py314"]
68
69
 
69
70
  [tool.isort]
70
71
  line_length = 120
@@ -109,4 +110,4 @@ sources = ["src"]
109
110
 
110
111
  [tool.pytest.ini_options]
111
112
  asyncio_mode = "auto"
112
- # markers = ["snapshot: mark a test as a snapshot test"]
113
+ # markers = ["snapshot: mark a test as a snapshot test"]
@@ -0,0 +1,60 @@
1
+ #
2
+ # This file is autogenerated by pip-compile with Python 3.12
3
+ # by the following command:
4
+ #
5
+ # pip-compile pyproject.toml
6
+ #
7
+ aiohappyeyeballs==2.6.2
8
+ # via aiohttp
9
+ aiohttp==3.13.5
10
+ # via taskdependencygraph (pyproject.toml)
11
+ aiosignal==1.4.0
12
+ # via aiohttp
13
+ annotated-types==0.7.0
14
+ # via pydantic
15
+ attrs==26.1.0
16
+ # via aiohttp
17
+ dnspython==2.8.0
18
+ # via email-validator
19
+ email-validator==2.3.0
20
+ # via pydantic
21
+ frozenlist==1.8.0
22
+ # via
23
+ # aiohttp
24
+ # aiosignal
25
+ idna==3.16
26
+ # via
27
+ # email-validator
28
+ # yarl
29
+ multidict==6.7.1
30
+ # via
31
+ # aiohttp
32
+ # yarl
33
+ networkx==3.6.1
34
+ # via taskdependencygraph (pyproject.toml)
35
+ propcache==0.5.2
36
+ # via
37
+ # aiohttp
38
+ # yarl
39
+ pydantic[email]==2.13.4
40
+ # via
41
+ # pydantic-settings
42
+ # taskdependencygraph (pyproject.toml)
43
+ pydantic-core==2.46.4
44
+ # via pydantic
45
+ pydantic-settings==2.14.1
46
+ # via taskdependencygraph (pyproject.toml)
47
+ python-dotenv==1.2.2
48
+ # via pydantic-settings
49
+ typing-extensions==4.15.0
50
+ # via
51
+ # aiosignal
52
+ # pydantic
53
+ # pydantic-core
54
+ # typing-inspection
55
+ typing-inspection==0.4.2
56
+ # via
57
+ # pydantic
58
+ # pydantic-settings
59
+ yarl==1.24.2
60
+ # via aiohttp
@@ -0,0 +1 @@
1
+ version = "0.2.0"
@@ -0,0 +1,59 @@
1
+ """
2
+ taskdependencygraph is a library to model tasks and dependencies between tasks in a networkx DiGraph
3
+ and give estimates when which task will be done
4
+ """
5
+
6
+ # pylint: disable=duplicate-code
7
+ # The __all__ list here intentionally mirrors models/__init__.py — re-exporting is the purpose of this file.
8
+
9
+ from .models import (
10
+ ID_OF_ARTIFICIAL_ENDNODE,
11
+ ID_OF_ARTIFICIAL_STARTNODE,
12
+ AddEdgeToGraphPreviewResponse,
13
+ AddNodeToGraphPreviewResponse,
14
+ GraphDefinitionValidationFinding,
15
+ GraphDefinitionValidationResult,
16
+ MermaidGanttConfig,
17
+ Person,
18
+ PersonId,
19
+ RunGroupId,
20
+ RunGroupPersonRelationId,
21
+ RunId,
22
+ ScheduleEntry,
23
+ ScheduleReport,
24
+ TaskDependencyEdge,
25
+ TaskDependencyId,
26
+ TaskExecutionStatus,
27
+ TaskId,
28
+ TaskNode,
29
+ ValidationCode,
30
+ task_node_as_artificial_endnode,
31
+ task_node_as_artificial_startnode,
32
+ )
33
+ from .task_dependency_graph import TaskDependencyGraph
34
+
35
+ __all__ = [
36
+ "AddEdgeToGraphPreviewResponse",
37
+ "AddNodeToGraphPreviewResponse",
38
+ "GraphDefinitionValidationFinding",
39
+ "GraphDefinitionValidationResult",
40
+ "ID_OF_ARTIFICIAL_ENDNODE",
41
+ "ID_OF_ARTIFICIAL_STARTNODE",
42
+ "MermaidGanttConfig",
43
+ "Person",
44
+ "PersonId",
45
+ "RunGroupId",
46
+ "RunGroupPersonRelationId",
47
+ "RunId",
48
+ "ScheduleEntry",
49
+ "ScheduleReport",
50
+ "TaskDependencyEdge",
51
+ "TaskDependencyId",
52
+ "TaskDependencyGraph",
53
+ "TaskExecutionStatus",
54
+ "TaskId",
55
+ "TaskNode",
56
+ "ValidationCode",
57
+ "task_node_as_artificial_endnode",
58
+ "task_node_as_artificial_startnode",
59
+ ]
@@ -1,24 +1,42 @@
1
1
  """models are python objects which we use to model tasks, dependencies and the graph they form"""
2
2
 
3
+ from .graph_definition_validation import (
4
+ GraphDefinitionValidationFinding,
5
+ GraphDefinitionValidationResult,
6
+ ValidationCode,
7
+ )
3
8
  from .ids import PersonId, RunGroupId, RunGroupPersonRelationId, RunId, TaskDependencyId, TaskId
9
+ from .mermaid_gantt_config import MermaidGanttConfig
4
10
  from .person import Person
11
+ from .schedule_report import ScheduleEntry, ScheduleReport
5
12
  from .task_dependency_edge import TaskDependencyEdge
13
+ from .task_dependency_update import AddEdgeToGraphPreviewResponse, AddNodeToGraphPreviewResponse
6
14
  from .task_execution_status import TaskExecutionStatus
7
15
  from .task_node import TaskNode
8
- from .task_node_as_artificial_endnode import ID_OF_ARTIFICIAL_ENDNODE
9
- from .task_node_as_artificial_startnode import ID_OF_ARTIFICIAL_STARTNODE
16
+ from .task_node_as_artificial_endnode import ID_OF_ARTIFICIAL_ENDNODE, task_node_as_artificial_endnode
17
+ from .task_node_as_artificial_startnode import ID_OF_ARTIFICIAL_STARTNODE, task_node_as_artificial_startnode
10
18
 
11
19
  __all__ = [
20
+ "AddEdgeToGraphPreviewResponse",
21
+ "AddNodeToGraphPreviewResponse",
22
+ "GraphDefinitionValidationFinding",
23
+ "GraphDefinitionValidationResult",
24
+ "ID_OF_ARTIFICIAL_ENDNODE",
25
+ "ID_OF_ARTIFICIAL_STARTNODE",
26
+ "MermaidGanttConfig",
12
27
  "Person",
13
- "RunId",
28
+ "PersonId",
14
29
  "RunGroupId",
15
30
  "RunGroupPersonRelationId",
16
- "TaskId",
17
- "TaskDependencyId",
18
- "PersonId",
19
- "TaskNode",
31
+ "RunId",
32
+ "ScheduleEntry",
33
+ "ScheduleReport",
20
34
  "TaskDependencyEdge",
35
+ "TaskDependencyId",
21
36
  "TaskExecutionStatus",
22
- "ID_OF_ARTIFICIAL_ENDNODE",
23
- "ID_OF_ARTIFICIAL_STARTNODE",
37
+ "TaskId",
38
+ "TaskNode",
39
+ "ValidationCode",
40
+ "task_node_as_artificial_endnode",
41
+ "task_node_as_artificial_startnode",
24
42
  ]
@@ -0,0 +1,50 @@
1
+ """
2
+ Graph definition validation models.
3
+ """
4
+
5
+ from enum import Enum
6
+
7
+ from pydantic import BaseModel, ConfigDict
8
+
9
+ from taskdependencygraph.models.ids import TaskDependencyId, TaskId
10
+
11
+
12
+ class ValidationCode(str, Enum):
13
+ """Stable codes identifying the kind of graph definition problem found."""
14
+
15
+ DUPLICATE_TASK_ID = "DUPLICATE_TASK_ID"
16
+ DUPLICATE_EXTERNAL_ID = "DUPLICATE_EXTERNAL_ID"
17
+ MISSING_EDGE_ENDPOINT = "MISSING_EDGE_ENDPOINT"
18
+ DUPLICATE_DEPENDENCY_ID = "DUPLICATE_DEPENDENCY_ID"
19
+ DUPLICATE_EDGE_PAIR = "DUPLICATE_EDGE_PAIR"
20
+ CYCLE = "CYCLE"
21
+ INVALID_MILESTONE_DURATION = "INVALID_MILESTONE_DURATION"
22
+
23
+
24
+ class GraphDefinitionValidationFinding(BaseModel):
25
+ """A single validation problem found in a graph definition."""
26
+
27
+ model_config = ConfigDict(frozen=True)
28
+
29
+ code: ValidationCode
30
+ message: str
31
+ task_id: TaskId | None = None
32
+ """The task ID involved in this finding.
33
+ For MISSING_EDGE_ENDPOINT this may be an ID that does not exist in the task list."""
34
+ dependency_id: TaskDependencyId | None = None
35
+
36
+
37
+ class GraphDefinitionValidationResult(BaseModel):
38
+ """The aggregated result of validating a raw task/dependency list before graph construction."""
39
+
40
+ model_config = ConfigDict(frozen=True)
41
+
42
+ is_valid: bool
43
+ findings: tuple[GraphDefinitionValidationFinding, ...]
44
+
45
+
46
+ __all__ = [
47
+ "GraphDefinitionValidationFinding",
48
+ "GraphDefinitionValidationResult",
49
+ "ValidationCode",
50
+ ]