jsonantt 2026.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.
- jsonantt-2026.1/PKG-INFO +166 -0
- jsonantt-2026.1/README.md +148 -0
- jsonantt-2026.1/jsonantt/__init__.py +18 -0
- jsonantt-2026.1/jsonantt/cli.py +49 -0
- jsonantt-2026.1/jsonantt/models.py +129 -0
- jsonantt-2026.1/jsonantt/parser.py +266 -0
- jsonantt-2026.1/jsonantt/renderer.py +552 -0
- jsonantt-2026.1/jsonantt.egg-info/PKG-INFO +166 -0
- jsonantt-2026.1/jsonantt.egg-info/SOURCES.txt +15 -0
- jsonantt-2026.1/jsonantt.egg-info/dependency_links.txt +1 -0
- jsonantt-2026.1/jsonantt.egg-info/entry_points.txt +2 -0
- jsonantt-2026.1/jsonantt.egg-info/requires.txt +1 -0
- jsonantt-2026.1/jsonantt.egg-info/top_level.txt +1 -0
- jsonantt-2026.1/pyproject.toml +32 -0
- jsonantt-2026.1/setup.cfg +4 -0
- jsonantt-2026.1/tests/test_parser.py +231 -0
- jsonantt-2026.1/tests/test_renderer.py +241 -0
jsonantt-2026.1/PKG-INFO
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jsonantt
|
|
3
|
+
Version: 2026.1
|
|
4
|
+
Summary: Generate Gantt charts from JSON descriptions using matplotlib
|
|
5
|
+
Keywords: gantt,chart,matplotlib,project-management,visualization
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: matplotlib>=3.5
|
|
18
|
+
|
|
19
|
+
# jsonantt
|
|
20
|
+
|
|
21
|
+
**jsonantt** generates beautiful Gantt chart images from a simple JSON description.
|
|
22
|
+
Charts are rendered with [matplotlib](https://matplotlib.org/) so they can be saved as `.png`, `.pdf`, `.svg`, and more.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Infinitely nestable tasks** — define sub-tasks, sub-sub-tasks, etc.
|
|
29
|
+
- **Auto date computation** — parent task start/end are derived automatically from children when not specified.
|
|
30
|
+
- **Milestone markers** — easy `"milestone": true` flag renders a distinctive diamond.
|
|
31
|
+
- **Fully colourable** — set colours per-task; children inherit their parent's colour.
|
|
32
|
+
- **Clean, indented y-axis labels** — task names are left-aligned with proper indentation per depth level.
|
|
33
|
+
- **PNG / PDF / SVG output** — whatever matplotlib supports.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install jsonantt
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or, directly from source:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/briday1/jsonantt.git
|
|
47
|
+
cd jsonantt
|
|
48
|
+
pip install -e .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
### 1. Create a JSON description
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"title": "My Project",
|
|
60
|
+
"dateformat": "%Y-%m-%d",
|
|
61
|
+
"tasks": [
|
|
62
|
+
{
|
|
63
|
+
"name": "Phase 1 – Planning",
|
|
64
|
+
"children": [
|
|
65
|
+
{ "name": "Requirements", "start": "2024-01-08", "end": "2024-01-19" },
|
|
66
|
+
{ "name": "Architecture", "start": "2024-01-15", "end": "2024-01-31" }
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{ "name": "Planning done", "milestone": true, "date": "2024-01-31" },
|
|
70
|
+
{
|
|
71
|
+
"name": "Phase 2 – Build",
|
|
72
|
+
"color": "#70AD47",
|
|
73
|
+
"children": [
|
|
74
|
+
{ "name": "Backend", "start": "2024-02-01", "end": "2024-03-01" },
|
|
75
|
+
{ "name": "Frontend", "start": "2024-02-12", "end": "2024-03-08" }
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{ "name": "Launch", "milestone": true, "date": "2024-04-01", "color": "#FF5757" }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. Generate the chart
|
|
84
|
+
|
|
85
|
+
**CLI:**
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
jsonantt project.json project.png
|
|
89
|
+
jsonantt project.json project.pdf # vector PDF
|
|
90
|
+
jsonantt project.json project.svg # scalable SVG
|
|
91
|
+
jsonantt --dpi 300 project.json project.png # high-resolution PNG
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Python API:**
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from jsonantt import load_chart, render_chart
|
|
98
|
+
|
|
99
|
+
config = load_chart("project.json")
|
|
100
|
+
render_chart(config, "project.png", dpi=150)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## JSON reference
|
|
106
|
+
|
|
107
|
+
| Field | Type | Description |
|
|
108
|
+
|-------|------|-------------|
|
|
109
|
+
| `title` | string | Optional chart title shown at the top |
|
|
110
|
+
| `dateformat` | string | `strptime` format string (default: `"%Y-%m-%d"`) |
|
|
111
|
+
| `start` | date string | Optional chart x-axis start date (overrides task dates) |
|
|
112
|
+
| `end` | date string | Optional chart x-axis end date |
|
|
113
|
+
| `style` | object | Visual style overrides (see below) |
|
|
114
|
+
| `tasks` | array | Top-level list of task objects |
|
|
115
|
+
|
|
116
|
+
### Task object
|
|
117
|
+
|
|
118
|
+
| Field | Type | Description |
|
|
119
|
+
|-------|------|-------------|
|
|
120
|
+
| `name` | string | **Required.** Task label |
|
|
121
|
+
| `start` | date string | Bar start date |
|
|
122
|
+
| `end` | date string | Bar end date |
|
|
123
|
+
| `color` | CSS hex string | Bar/milestone colour (e.g. `"#4472C4"`) |
|
|
124
|
+
| `milestone` | boolean | Render as a diamond milestone instead of a bar |
|
|
125
|
+
| `date` | date string | Milestone date (used when `milestone: true`) |
|
|
126
|
+
| `children` | array | Nested sub-tasks (infinitely nestable) |
|
|
127
|
+
|
|
128
|
+
> **Auto date computation:** When a task has `children` but no explicit `start`/`end`, the dates are computed automatically as the earliest child start and latest child end, recursively.
|
|
129
|
+
|
|
130
|
+
### Style object
|
|
131
|
+
|
|
132
|
+
| Field | Default | Description |
|
|
133
|
+
|-------|---------|-------------|
|
|
134
|
+
| `width` | `14` | Figure width in inches |
|
|
135
|
+
| `row_height` | `0.45` | Height of each task row in inches |
|
|
136
|
+
| `bar_height` | `0.5` | Bar height as a fraction of `row_height` |
|
|
137
|
+
| `font_size` | `9` | Base font size in points |
|
|
138
|
+
| `indent_size` | `3` | Spaces added per depth level in labels |
|
|
139
|
+
| `label_fraction` | `0.28` | Fraction of figure width used for labels |
|
|
140
|
+
| `colors` | palette | Array of default hex colours cycled per top-level task |
|
|
141
|
+
| `background` | `"#FFFFFF"` | Figure background colour |
|
|
142
|
+
| `grid_color` | `"#E0E0E0"` | Vertical gridline colour |
|
|
143
|
+
| `row_band_color` | `"#F5F5F5"` | Alternating row band colour |
|
|
144
|
+
| `milestone_color` | `"#E65100"` | Default milestone colour |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Examples
|
|
149
|
+
|
|
150
|
+
See the [`examples/`](examples/) folder for ready-to-run JSON files:
|
|
151
|
+
|
|
152
|
+
- [`examples/simple.json`](examples/simple.json) — a three-phase project with milestones
|
|
153
|
+
- [`examples/complex.json`](examples/complex.json) — a multi-quarter roadmap with deep nesting and custom colours
|
|
154
|
+
|
|
155
|
+
Generate them locally:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
jsonantt examples/simple.json simple.png
|
|
159
|
+
jsonantt examples/complex.json complex.png
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# jsonantt
|
|
2
|
+
|
|
3
|
+
**jsonantt** generates beautiful Gantt chart images from a simple JSON description.
|
|
4
|
+
Charts are rendered with [matplotlib](https://matplotlib.org/) so they can be saved as `.png`, `.pdf`, `.svg`, and more.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Infinitely nestable tasks** — define sub-tasks, sub-sub-tasks, etc.
|
|
11
|
+
- **Auto date computation** — parent task start/end are derived automatically from children when not specified.
|
|
12
|
+
- **Milestone markers** — easy `"milestone": true` flag renders a distinctive diamond.
|
|
13
|
+
- **Fully colourable** — set colours per-task; children inherit their parent's colour.
|
|
14
|
+
- **Clean, indented y-axis labels** — task names are left-aligned with proper indentation per depth level.
|
|
15
|
+
- **PNG / PDF / SVG output** — whatever matplotlib supports.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install jsonantt
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or, directly from source:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone https://github.com/briday1/jsonantt.git
|
|
29
|
+
cd jsonantt
|
|
30
|
+
pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
### 1. Create a JSON description
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"title": "My Project",
|
|
42
|
+
"dateformat": "%Y-%m-%d",
|
|
43
|
+
"tasks": [
|
|
44
|
+
{
|
|
45
|
+
"name": "Phase 1 – Planning",
|
|
46
|
+
"children": [
|
|
47
|
+
{ "name": "Requirements", "start": "2024-01-08", "end": "2024-01-19" },
|
|
48
|
+
{ "name": "Architecture", "start": "2024-01-15", "end": "2024-01-31" }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{ "name": "Planning done", "milestone": true, "date": "2024-01-31" },
|
|
52
|
+
{
|
|
53
|
+
"name": "Phase 2 – Build",
|
|
54
|
+
"color": "#70AD47",
|
|
55
|
+
"children": [
|
|
56
|
+
{ "name": "Backend", "start": "2024-02-01", "end": "2024-03-01" },
|
|
57
|
+
{ "name": "Frontend", "start": "2024-02-12", "end": "2024-03-08" }
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
{ "name": "Launch", "milestone": true, "date": "2024-04-01", "color": "#FF5757" }
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Generate the chart
|
|
66
|
+
|
|
67
|
+
**CLI:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
jsonantt project.json project.png
|
|
71
|
+
jsonantt project.json project.pdf # vector PDF
|
|
72
|
+
jsonantt project.json project.svg # scalable SVG
|
|
73
|
+
jsonantt --dpi 300 project.json project.png # high-resolution PNG
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Python API:**
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from jsonantt import load_chart, render_chart
|
|
80
|
+
|
|
81
|
+
config = load_chart("project.json")
|
|
82
|
+
render_chart(config, "project.png", dpi=150)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## JSON reference
|
|
88
|
+
|
|
89
|
+
| Field | Type | Description |
|
|
90
|
+
|-------|------|-------------|
|
|
91
|
+
| `title` | string | Optional chart title shown at the top |
|
|
92
|
+
| `dateformat` | string | `strptime` format string (default: `"%Y-%m-%d"`) |
|
|
93
|
+
| `start` | date string | Optional chart x-axis start date (overrides task dates) |
|
|
94
|
+
| `end` | date string | Optional chart x-axis end date |
|
|
95
|
+
| `style` | object | Visual style overrides (see below) |
|
|
96
|
+
| `tasks` | array | Top-level list of task objects |
|
|
97
|
+
|
|
98
|
+
### Task object
|
|
99
|
+
|
|
100
|
+
| Field | Type | Description |
|
|
101
|
+
|-------|------|-------------|
|
|
102
|
+
| `name` | string | **Required.** Task label |
|
|
103
|
+
| `start` | date string | Bar start date |
|
|
104
|
+
| `end` | date string | Bar end date |
|
|
105
|
+
| `color` | CSS hex string | Bar/milestone colour (e.g. `"#4472C4"`) |
|
|
106
|
+
| `milestone` | boolean | Render as a diamond milestone instead of a bar |
|
|
107
|
+
| `date` | date string | Milestone date (used when `milestone: true`) |
|
|
108
|
+
| `children` | array | Nested sub-tasks (infinitely nestable) |
|
|
109
|
+
|
|
110
|
+
> **Auto date computation:** When a task has `children` but no explicit `start`/`end`, the dates are computed automatically as the earliest child start and latest child end, recursively.
|
|
111
|
+
|
|
112
|
+
### Style object
|
|
113
|
+
|
|
114
|
+
| Field | Default | Description |
|
|
115
|
+
|-------|---------|-------------|
|
|
116
|
+
| `width` | `14` | Figure width in inches |
|
|
117
|
+
| `row_height` | `0.45` | Height of each task row in inches |
|
|
118
|
+
| `bar_height` | `0.5` | Bar height as a fraction of `row_height` |
|
|
119
|
+
| `font_size` | `9` | Base font size in points |
|
|
120
|
+
| `indent_size` | `3` | Spaces added per depth level in labels |
|
|
121
|
+
| `label_fraction` | `0.28` | Fraction of figure width used for labels |
|
|
122
|
+
| `colors` | palette | Array of default hex colours cycled per top-level task |
|
|
123
|
+
| `background` | `"#FFFFFF"` | Figure background colour |
|
|
124
|
+
| `grid_color` | `"#E0E0E0"` | Vertical gridline colour |
|
|
125
|
+
| `row_band_color` | `"#F5F5F5"` | Alternating row band colour |
|
|
126
|
+
| `milestone_color` | `"#E65100"` | Default milestone colour |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Examples
|
|
131
|
+
|
|
132
|
+
See the [`examples/`](examples/) folder for ready-to-run JSON files:
|
|
133
|
+
|
|
134
|
+
- [`examples/simple.json`](examples/simple.json) — a three-phase project with milestones
|
|
135
|
+
- [`examples/complex.json`](examples/complex.json) — a multi-quarter roadmap with deep nesting and custom colours
|
|
136
|
+
|
|
137
|
+
Generate them locally:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
jsonantt examples/simple.json simple.png
|
|
141
|
+
jsonantt examples/complex.json complex.png
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""jsonantt – Gantt chart generation from JSON descriptions.
|
|
2
|
+
|
|
3
|
+
Quick-start example::
|
|
4
|
+
|
|
5
|
+
from jsonantt import load_chart, render_chart
|
|
6
|
+
|
|
7
|
+
config = load_chart("project.json")
|
|
8
|
+
render_chart(config, "project.png")
|
|
9
|
+
|
|
10
|
+
Or from the command line::
|
|
11
|
+
|
|
12
|
+
jsonantt project.json project.png
|
|
13
|
+
"""
|
|
14
|
+
from .parser import load_chart, parse_chart
|
|
15
|
+
from .renderer import render_chart
|
|
16
|
+
|
|
17
|
+
__all__ = ["load_chart", "parse_chart", "render_chart"]
|
|
18
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Command-line interface for jsonantt."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from .parser import load_chart
|
|
8
|
+
from .renderer import render_chart
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main(argv=None) -> int:
|
|
12
|
+
parser = argparse.ArgumentParser(
|
|
13
|
+
prog="jsonantt",
|
|
14
|
+
description=(
|
|
15
|
+
"Generate a Gantt chart image from a JSON description.\n\n"
|
|
16
|
+
"Example:\n jsonantt project.json chart.png\n jsonantt project.json chart.pdf"
|
|
17
|
+
),
|
|
18
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument("input", help="Path to the JSON chart description file")
|
|
21
|
+
parser.add_argument("output", help="Output image path (e.g. chart.png, chart.pdf)")
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--dpi", type=int, default=150,
|
|
24
|
+
help="Image resolution in DPI (default: 150, raster formats only)",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
args = parser.parse_args(argv)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
config = load_chart(args.input)
|
|
31
|
+
except FileNotFoundError:
|
|
32
|
+
print(f"error: input file not found: {args.input}", file=sys.stderr)
|
|
33
|
+
return 1
|
|
34
|
+
except Exception as exc: # noqa: BLE001
|
|
35
|
+
print(f"error: failed to parse {args.input}: {exc}", file=sys.stderr)
|
|
36
|
+
return 1
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
render_chart(config, args.output, dpi=args.dpi)
|
|
40
|
+
except Exception as exc: # noqa: BLE001
|
|
41
|
+
print(f"error: failed to render chart: {exc}", file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
print(f"Chart saved to {args.output}")
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == "__main__":
|
|
49
|
+
sys.exit(main())
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Data models for jsonantt."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import date
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
# Default colour palette (can be overridden in JSON "style" block)
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
DEFAULT_PALETTE: List[str] = [
|
|
13
|
+
"#4472C4", # steel blue
|
|
14
|
+
"#ED7D31", # orange
|
|
15
|
+
"#70AD47", # green
|
|
16
|
+
"#FF5757", # coral red
|
|
17
|
+
"#9DC3E6", # sky blue
|
|
18
|
+
"#FFC000", # amber
|
|
19
|
+
"#7030A0", # purple
|
|
20
|
+
"#00B0F0", # cyan
|
|
21
|
+
"#FF0066", # hot pink
|
|
22
|
+
"#00B050", # emerald
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Arrow:
|
|
28
|
+
"""A dependency arrow connecting the end of one task to the start of another."""
|
|
29
|
+
|
|
30
|
+
from_id: str
|
|
31
|
+
to_id: str
|
|
32
|
+
color: str = "#888888"
|
|
33
|
+
label: Optional[str] = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class Task:
|
|
38
|
+
"""A single task (or milestone) in the Gantt chart.
|
|
39
|
+
|
|
40
|
+
Date resolution modes
|
|
41
|
+
---------------------
|
|
42
|
+
1. ``start`` + ``end`` – explicit dates (original behaviour)
|
|
43
|
+
2. ``start`` + ``duration`` – ``end`` computed from ``start`` + duration
|
|
44
|
+
3. ``not_before`` + ``duration`` – ``start`` set to the ``effective_end``
|
|
45
|
+
of the task whose ``id`` matches ``not_before``, then ``end`` computed.
|
|
46
|
+
|
|
47
|
+
When absent the values are derived recursively from *children* via
|
|
48
|
+
:pymeth:`effective_start` / :pymeth:`effective_end`.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
name: str
|
|
52
|
+
id: Optional[str] = None # unique identifier for cross-referencing
|
|
53
|
+
start: Optional[date] = None
|
|
54
|
+
end: Optional[date] = None
|
|
55
|
+
color: Optional[str] = None
|
|
56
|
+
milestone: bool = False
|
|
57
|
+
milestone_date: Optional[date] = None
|
|
58
|
+
children: List["Task"] = field(default_factory=list)
|
|
59
|
+
# -- deferred-resolution fields (set by parser, consumed during resolve) --
|
|
60
|
+
not_before: Optional[str] = None # id of task whose end becomes this start
|
|
61
|
+
duration_spec: Optional[str] = None # raw duration string e.g. "3m", "14d"
|
|
62
|
+
marker_size: Optional[float] = None # override milestone diamond size (pts)
|
|
63
|
+
bold: bool = False # render label in bold
|
|
64
|
+
|
|
65
|
+
# ------------------------------------------------------------------ #
|
|
66
|
+
# Computed properties #
|
|
67
|
+
# ------------------------------------------------------------------ #
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def effective_start(self) -> Optional[date]:
|
|
71
|
+
"""Earliest start date, resolving through children if needed."""
|
|
72
|
+
if self.milestone:
|
|
73
|
+
return self.milestone_date or self.start
|
|
74
|
+
if self.start is not None:
|
|
75
|
+
return self.start
|
|
76
|
+
starts = [c.effective_start for c in self.children if c.effective_start is not None]
|
|
77
|
+
return min(starts) if starts else None
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def effective_end(self) -> Optional[date]:
|
|
81
|
+
"""Latest end date, resolving through children if needed."""
|
|
82
|
+
if self.milestone:
|
|
83
|
+
return self.milestone_date or self.start
|
|
84
|
+
if self.end is not None:
|
|
85
|
+
return self.end
|
|
86
|
+
ends = [c.effective_end for c in self.children if c.effective_end is not None]
|
|
87
|
+
return max(ends) if ends else None
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def is_parent(self) -> bool:
|
|
91
|
+
"""True when this task has sub-tasks."""
|
|
92
|
+
return bool(self.children)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class Style:
|
|
97
|
+
"""Visual style configuration for the chart."""
|
|
98
|
+
|
|
99
|
+
width: float = 14.0 # figure width in inches
|
|
100
|
+
row_height: float = 0.3 # height of each row in inches
|
|
101
|
+
bar_height: float = 0.5 # bar height as fraction of row_height
|
|
102
|
+
font_size: float = 12.0 # base font size in pts
|
|
103
|
+
indent_size: int = 3 # spaces added per depth level
|
|
104
|
+
label_fraction: float = 0.28 # fraction of figure width used for labels
|
|
105
|
+
colors: List[str] = field(default_factory=lambda: list(DEFAULT_PALETTE))
|
|
106
|
+
background: str = "#FFFFFF" # figure background colour
|
|
107
|
+
grid_color: str = "#E0E0E0" # vertical gridline colour
|
|
108
|
+
row_band_color: str = "#F5F5F5" # alternating row band colour
|
|
109
|
+
milestone_color: str = "#E65100" # default milestone colour
|
|
110
|
+
milestone_size: float = 14.0 # default milestone diamond size (pts)
|
|
111
|
+
major_tick: Optional[str] = None # e.g. "year", "quarter", "month", "week"
|
|
112
|
+
minor_tick: Optional[str] = None # e.g. "quarter", "month", "week", "day"
|
|
113
|
+
major_grid_width: float = 2.0 # major gridline linewidth
|
|
114
|
+
minor_grid_width: float = 1.5 # minor gridline linewidth
|
|
115
|
+
bold_tasks: bool = True # auto-bold top-level (depth 0) tasks
|
|
116
|
+
tick_position: str = "top" # x-axis label position: "top", "bottom", or "both"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class ChartConfig:
|
|
121
|
+
"""Top-level chart configuration parsed from JSON."""
|
|
122
|
+
|
|
123
|
+
tasks: List[Task] = field(default_factory=list)
|
|
124
|
+
title: str = ""
|
|
125
|
+
date_format: str = "%Y-%m-%d"
|
|
126
|
+
start: Optional[date] = None # force chart x-axis start
|
|
127
|
+
end: Optional[date] = None # force chart x-axis end
|
|
128
|
+
style: Style = field(default_factory=Style)
|
|
129
|
+
arrows: List[Arrow] = field(default_factory=list)
|