scriptplan 0.9.1__tar.gz → 0.10.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 (132) hide show
  1. scriptplan-0.10.0/CHANGELOG.md +118 -0
  2. {scriptplan-0.9.1 → scriptplan-0.10.0}/MANIFEST.in +1 -0
  3. {scriptplan-0.9.1/scriptplan.egg-info → scriptplan-0.10.0}/PKG-INFO +72 -35
  4. scriptplan-0.10.0/README.md +237 -0
  5. {scriptplan-0.9.1 → scriptplan-0.10.0}/pyproject.toml +9 -2
  6. scriptplan-0.10.0/scriptplan/__init__.py +29 -0
  7. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/cli/__init__.py +1 -1
  8. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/cli/main.py +72 -121
  9. scriptplan-0.10.0/scriptplan/cli/plan.py +380 -0
  10. scriptplan-0.10.0/scriptplan/cli/plan_files.py +195 -0
  11. scriptplan-0.10.0/scriptplan/cli/plan_help.py +194 -0
  12. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/account.py +21 -32
  13. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/allocation.py +3 -3
  14. scriptplan-0.10.0/scriptplan/core/allocation_plan.py +171 -0
  15. scriptplan-0.10.0/scriptplan/core/attributes.py +464 -0
  16. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/booking.py +2 -7
  17. scriptplan-0.10.0/scriptplan/core/exceptions.py +319 -0
  18. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/journal.py +61 -40
  19. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/leave.py +7 -7
  20. scriptplan-0.10.0/scriptplan/core/limits.py +335 -0
  21. scriptplan-0.10.0/scriptplan/core/project.py +519 -0
  22. scriptplan-0.10.0/scriptplan/core/project_attributes.py +212 -0
  23. scriptplan-0.10.0/scriptplan/core/project_calendar.py +126 -0
  24. scriptplan-0.10.0/scriptplan/core/project_tree.py +256 -0
  25. scriptplan-0.10.0/scriptplan/core/property.py +100 -0
  26. scriptplan-0.10.0/scriptplan/core/property_list.py +358 -0
  27. scriptplan-0.10.0/scriptplan/core/property_set.py +338 -0
  28. scriptplan-0.10.0/scriptplan/core/property_tree_node.py +334 -0
  29. scriptplan-0.10.0/scriptplan/core/resolution.py +95 -0
  30. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/resource.py +11 -18
  31. scriptplan-0.10.0/scriptplan/core/resource_availability.py +187 -0
  32. scriptplan-0.10.0/scriptplan/core/resource_reporting.py +311 -0
  33. scriptplan-0.10.0/scriptplan/core/resource_scenario.py +436 -0
  34. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/scenario.py +1 -1
  35. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/scenario_data.py +9 -13
  36. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/shift.py +6 -12
  37. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/task.py +5 -11
  38. scriptplan-0.10.0/scriptplan/core/task_booking.py +320 -0
  39. scriptplan-0.10.0/scriptplan/core/task_dependencies.py +267 -0
  40. scriptplan-0.10.0/scriptplan/core/task_placement.py +415 -0
  41. scriptplan-0.10.0/scriptplan/core/task_resources.py +335 -0
  42. scriptplan-0.10.0/scriptplan/core/task_scenario.py +424 -0
  43. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/timesheet.py +91 -85
  44. scriptplan-0.10.0/scriptplan/core/timezones.py +59 -0
  45. scriptplan-0.10.0/scriptplan/core/units.py +190 -0
  46. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/working_hours.py +86 -58
  47. scriptplan-0.10.0/scriptplan/parser/attribute_apply.py +302 -0
  48. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/parser/macro_processor.py +34 -33
  49. scriptplan-0.10.0/scriptplan/parser/model_pieces.py +227 -0
  50. scriptplan-0.10.0/scriptplan/parser/report_builder.py +223 -0
  51. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/parser/tjp.lark +29 -6
  52. scriptplan-0.10.0/scriptplan/parser/tjp_parser.py +416 -0
  53. scriptplan-0.10.0/scriptplan/parser/transformer_project.py +193 -0
  54. scriptplan-0.10.0/scriptplan/parser/transformer_report.py +285 -0
  55. scriptplan-0.10.0/scriptplan/parser/transformer_resource.py +224 -0
  56. scriptplan-0.10.0/scriptplan/parser/transformer_task.py +339 -0
  57. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/__init__.py +17 -17
  58. scriptplan-0.10.0/scriptplan/report/logical_expression.py +392 -0
  59. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/report.py +102 -60
  60. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/report_base.py +88 -87
  61. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/report_context.py +26 -25
  62. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/resource_report.py +39 -52
  63. scriptplan-0.10.0/scriptplan/report/table_cells.py +165 -0
  64. scriptplan-0.10.0/scriptplan/report/table_headers.py +69 -0
  65. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/table_report.py +124 -257
  66. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/task_report.py +52 -64
  67. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/report/text_report.py +34 -34
  68. scriptplan-0.10.0/scriptplan/scheduler/postcheck.py +106 -0
  69. scriptplan-0.10.0/scriptplan/scheduler/preflight.py +121 -0
  70. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/scheduler/scoreboard.py +2 -11
  71. scriptplan-0.10.0/scriptplan/utils/data_cache.py +9 -0
  72. scriptplan-0.10.0/scriptplan/utils/logger.py +39 -0
  73. scriptplan-0.10.0/scriptplan/utils/message_handler.py +461 -0
  74. scriptplan-0.10.0/scriptplan/utils/messages.py +192 -0
  75. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/utils/time.py +30 -28
  76. {scriptplan-0.9.1 → scriptplan-0.10.0/scriptplan.egg-info}/PKG-INFO +72 -35
  77. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan.egg-info/SOURCES.txt +40 -3
  78. scriptplan-0.10.0/setup.py +17 -0
  79. scriptplan-0.10.0/tests/test_ansi_color.py +24 -0
  80. scriptplan-0.10.0/tests/test_audit_probes.py +78 -0
  81. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_core_resource.py +1 -1
  82. scriptplan-0.10.0/tests/test_fixture_golden.py +63 -0
  83. scriptplan-0.10.0/tests/test_plan_cli.py +139 -0
  84. scriptplan-0.10.0/tests/test_project_isolation.py +89 -0
  85. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_property_set.py +4 -2
  86. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_report.py +2 -2
  87. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_scheduling.py +3 -3
  88. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_scheduling_validation.py +130 -210
  89. scriptplan-0.9.1/README.md +0 -201
  90. scriptplan-0.9.1/scriptplan/__init__.py +0 -22
  91. scriptplan-0.9.1/scriptplan/cli/plan.py +0 -678
  92. scriptplan-0.9.1/scriptplan/core/limits.py +0 -376
  93. scriptplan-0.9.1/scriptplan/core/project.py +0 -936
  94. scriptplan-0.9.1/scriptplan/core/property.py +0 -1292
  95. scriptplan-0.9.1/scriptplan/core/resource_scenario.py +0 -709
  96. scriptplan-0.9.1/scriptplan/core/task_scenario.py +0 -1551
  97. scriptplan-0.9.1/scriptplan/parser/tjp_parser.py +0 -1903
  98. scriptplan-0.9.1/scriptplan/scheduler/batch_processor.py +0 -260
  99. scriptplan-0.9.1/scriptplan/utils/data_cache.py +0 -50
  100. scriptplan-0.9.1/scriptplan/utils/logger.py +0 -245
  101. scriptplan-0.9.1/scriptplan/utils/message_handler.py +0 -535
  102. scriptplan-0.9.1/tests/test_batch_processor.py +0 -118
  103. scriptplan-0.9.1/tests/test_logger.py +0 -148
  104. {scriptplan-0.9.1 → scriptplan-0.10.0}/LICENSE +0 -0
  105. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/core/__init__.py +0 -0
  106. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/parser/__init__.py +0 -0
  107. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/py.typed +0 -0
  108. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/scheduler/__init__.py +0 -0
  109. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan/utils/__init__.py +0 -0
  110. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan.egg-info/dependency_links.txt +0 -0
  111. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan.egg-info/entry_points.txt +0 -0
  112. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan.egg-info/requires.txt +0 -0
  113. {scriptplan-0.9.1 → scriptplan-0.10.0}/scriptplan.egg-info/top_level.txt +0 -0
  114. {scriptplan-0.9.1 → scriptplan-0.10.0}/setup.cfg +0 -0
  115. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_account.py +0 -0
  116. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_attribute_base.py +0 -0
  117. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_cli.py +0 -0
  118. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_core_allocation.py +0 -0
  119. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_core_booking.py +0 -0
  120. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_core_project.py +0 -0
  121. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_core_scenario.py +0 -0
  122. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_core_task.py +0 -0
  123. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_journal.py +0 -0
  124. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_macro_processor.py +0 -0
  125. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_message_handler.py +0 -0
  126. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_parser.py +0 -0
  127. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_property_list.py +0 -0
  128. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_property_tree_node.py +0 -0
  129. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_scoreboard.py +0 -0
  130. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_shift.py +0 -0
  131. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_timesheet.py +0 -0
  132. {scriptplan-0.9.1 → scriptplan-0.10.0}/tests/test_utils_time.py +0 -0
@@ -0,0 +1,118 @@
1
+ # Changelog
2
+
3
+ ## 0.10.0
4
+
5
+ A readiness audit of 0.9.2 turned every documented claim into a probe that
6
+ drives ScriptPlan through its own API or CLI. 32 of those probes failed. This
7
+ release is what it took to make them pass, and it changes scheduling results.
8
+
9
+ ### Breaking: `effort` is person-time
10
+
11
+ `effort 16h allocate a, b` used to occupy both resources for sixteen hours and
12
+ credit sixteen hours of work. Two resources booked thirty-two hours, four booked
13
+ sixty-four, and the completion date never moved however many were assigned. The
14
+ engine took the largest contribution in each slot rather than the sum, so the
15
+ labour of every resource but one was booked, billed and discarded.
16
+
17
+ Effort is now the amount of work to be done, and each allocated resource
18
+ contributes its own time towards it, as TaskJuggler defines it. The same task
19
+ with two resources finishes in half the time and costs what one resource's worth
20
+ of work costs.
21
+
22
+ **Upgrading.** A task whose allocations were meant to work side by side is
23
+ written with `mandatory` and its effort in person-hours:
24
+
25
+ ```
26
+ # before 0.10.0: four hours of a pair working together
27
+ task deploy "Deploy" { effort 4h allocate dev, qa }
28
+
29
+ # 0.10.0 and after
30
+ task deploy "Deploy" { effort 8h allocate dev { mandatory }, qa { mandatory } }
31
+ ```
32
+
33
+ Tasks with a single allocation are unaffected. Of the 24 example projects, 21
34
+ schedule identically to 0.9.2; the three that differ are those that allocate
35
+ more than one resource to a task.
36
+
37
+ ### Breaking: failures are reported as failures
38
+
39
+ `Project.schedule()` always returned `True`, and the CLI exited 0 with empty
40
+ start and end cells. A circular dependency, an effort task with no allocation,
41
+ or a task that did not fit the project window produced a warning nobody saw.
42
+
43
+ Scheduling now returns the real result and names each failing task and why; the
44
+ CLIs exit non-zero. A dependency cycle is rejected before scheduling, naming the
45
+ cycle. An attribute or reference the parser cannot place is an error rather than
46
+ a silent drop, which is how `duration`, `length`, `complete` and `note` reached
47
+ the model for the first time.
48
+
49
+ ### Breaking: the declared project period is honoured
50
+
51
+ The project end was grown by a heuristic before scheduling. It is now taken as
52
+ written, and a task that does not fit is reported. Projects that relied on the
53
+ extension need their period stated honestly.
54
+
55
+ ### Breaking: unimplemented report formats are rejected
56
+
57
+ `ical`, `tjp`, `niku`, `ctags` and MS Project XML were accepted, wrote no file,
58
+ and left the CLI blaming a scheduling problem that did not exist. They now raise
59
+ at parse time. Use `json` or `csv`.
60
+
61
+ ### Fixed
62
+
63
+ - Durations follow one table: `m` is months and `min` is minutes everywhere;
64
+ `duration` and `gapduration` are calendar time, `effort`, `length` and
65
+ `gaplength` working time derived from `dailyworkinghours` and
66
+ `yearlyworkingdays`. `gapduration 2d` was 16 hours; `gaplength 30min` was no
67
+ gap at all; `limits { dailymax 30min }` was a 4800-hour cap.
68
+ - Limits count seconds in the resource's own calendar and admit part of a slot,
69
+ so `dailymax 6.4h` is exact. A resource in Los Angeles capped at four hours a
70
+ day was working five in one of its own days. `dailymin`, `weeklymin`,
71
+ `monthlymax`, `monthlymin`, `maximum` and `minimum` reach the grammar, as do
72
+ `maxend`, `maxstart`, `minend` and `minstart`, and violations are reported.
73
+ - Dates are read in the project's timezone and scheduled in UTC; reports convert
74
+ back. The default working day was compared against the hour of a UTC slot,
75
+ putting a New York project five hours out.
76
+ - Slot size is derived from the times a project uses, so `start 09:30` and a
77
+ shift of `08:13 - 11:59` are exact without setting `timingresolution`. That
78
+ shift was credited with seven hours a day instead of eight and a half.
79
+ - A working-hours interval crossing midnight applies to the following day.
80
+ `fri 22:00 - 06:00` marked Friday morning instead of Saturday.
81
+ - Chains mixing ASAP and ALAP schedule instead of deadlocking.
82
+ - Scheduling twice gives the same dates and the same bookings. The second run
83
+ kept the dates and discarded every booking.
84
+ - Reports honour their own `sorttasks`, `hidetask`, column options and formats,
85
+ and `plan report` renders the reports a project declares rather than
86
+ substituting a three-column one. Descending sort was a no-op, and any filter
87
+ hid every row.
88
+ - `report_id` is a SHA-256 of the rendered result. It hashed the input file, so
89
+ a correct and a broken schedule of the same input shared an id.
90
+ - `mandatory`, `persistent` and per-resource allocation options work; they
91
+ parsed to nothing.
92
+
93
+ ### Removed
94
+
95
+ - The Cython extensions. Under `cdivision` the compiled path computed
96
+ `(0 - 1) % 7` as `-1`, so a resource with a Sunday night shift lost its Monday
97
+ morning in every published wheel and kept it in every source checkout: the
98
+ same file, two schedules, and no test covering the compiled one. The wheel is
99
+ now pure Python and cannot diverge from its source.
100
+ - `scheduler.batch_processor`, `utils.data_cache.DataCache` and `utils.Log`,
101
+ none of which were reachable.
102
+
103
+ ### Performance
104
+
105
+ 500 chained tasks at minute resolution schedule in 3.3s, from 14.4s. A year at
106
+ minute resolution with ten resources takes about 13s, from 58.7s.
107
+
108
+ ### Verification
109
+
110
+ `audit/evaluations/` holds 74 probes across 12 files, each named after the
111
+ behaviour it would falsify and each driving ScriptPlan through its own
112
+ interface. `scripts/ci.sh` runs lint, types, a file-size cap, 411 tests, a
113
+ showcase currency check and the probes. The probes also pass against the built
114
+ wheel installed in a clean virtualenv, so what is verified is what ships.
115
+
116
+ ## 0.9.2 and earlier
117
+
118
+ See the git history.
@@ -1,5 +1,6 @@
1
1
  include LICENSE
2
2
  include README.md
3
+ include CHANGELOG.md
3
4
  include pyproject.toml
4
5
  recursive-include scriptplan *.lark
5
6
  recursive-include scriptplan py.typed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptplan
3
- Version: 0.9.1
3
+ Version: 0.10.0
4
4
  Summary: A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management.
5
5
  Author-email: Farshid Ashouri <farsheed.ashouri@gmail.com>
6
6
  Maintainer-email: Farshid Ashouri <farsheed.ashouri@gmail.com>
@@ -9,6 +9,7 @@ Project-URL: Homepage, https://github.com/rodmena-limited/scriptplan
9
9
  Project-URL: Documentation, https://github.com/rodmena-limited/scriptplan#readme
10
10
  Project-URL: Bug Tracker, https://github.com/rodmena-limited/scriptplan/issues
11
11
  Project-URL: Source, https://github.com/rodmena-limited/scriptplan
12
+ Project-URL: Changelog, https://github.com/rodmena-limited/scriptplan/blob/main/CHANGELOG.md
12
13
  Keywords: scheduling,project-management,resource-allocation,gantt,critical-path
13
14
  Classifier: Development Status :: 4 - Beta
14
15
  Classifier: Intended Audience :: Developers
@@ -48,11 +49,10 @@ Dynamic: license-file
48
49
 
49
50
  # ScriptPlan
50
51
 
51
- ![Logic Certified](https://img.shields.io/badge/Logic-Certified_Airport--Grade-success?style=for-the-badge&logo=github)
52
- ![Tests Passed](https://img.shields.io/badge/Stress_Tests-24%2F24_Passed-success?style=for-the-badge)
53
- ![Temporal Physics](https://img.shields.io/badge/Temporal_Physics-Verified-blue?style=for-the-badge)
54
- ![Scheduling](https://img.shields.io/badge/Scheduling-ALAP_%7C_JIT_%7C_Priority-blueviolet?style=for-the-badge)
55
- ![Constraints](https://img.shields.io/badge/Constraints-Hard_Limits_%7C_Quotas-orange?style=for-the-badge)
52
+ ![Tests](https://img.shields.io/badge/tests-411_passing-success?style=for-the-badge)
53
+ ![Probes](https://img.shields.io/badge/audit_probes-67_passing-success?style=for-the-badge)
54
+ ![Python](https://img.shields.io/badge/python-3.9%2B-blue?style=for-the-badge)
55
+ ![Licence](https://img.shields.io/badge/licence-Apache--2.0-lightgrey?style=for-the-badge)
56
56
 
57
57
  A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management. The syntax is compatible with TaskJuggler (.tjp files).
58
58
 
@@ -109,11 +109,11 @@ done
109
109
  **Features**:
110
110
  - Output to stdout (Unix philosophy)
111
111
  - Messages to stderr
112
- - SHA256 report_id (content-based hash)
112
+ - SHA256 `report_id` over the rendered result, so it changes when the schedule does
113
113
  - Lowercase column names
114
114
  - No HTML metadata
115
115
  - No file pollution (uses temp directories)
116
- - Safe for concurrent execution (100+ instances)
116
+ - Separate processes do not collide: temporary files and directories are unique per run
117
117
 
118
118
  ### Python API
119
119
 
@@ -131,39 +131,69 @@ for task in project.tasks:
131
131
  print(f"{task.id}: {start} -> {end}")
132
132
  ```
133
133
 
134
- ## System Certified
134
+ ## What is verified
135
135
 
136
- **Certification Level**: Airport-Grade / Mission Critical
136
+ Each behaviour below has a probe in `audit/evaluations/` that drives ScriptPlan
137
+ through its own API or CLI and asserts the result. They run as part of the test
138
+ suite, and can be run on their own against an installed release:
137
139
 
138
- **Capabilities Verified**:
140
+ ```bash
141
+ audit/evaluations/run_all.sh
142
+ ```
143
+
144
+ **Time**
145
+
146
+ - Dates are read in the project's timezone and scheduled in UTC; reports convert back.
147
+ - Resource shifts follow their own timezone across daylight-saving changes in both directions.
148
+ - A working-hours interval that crosses midnight applies to the following day.
149
+ - Slot size is derived from the times a project uses, so `start 09:30` is honoured
150
+ exactly and a shift of `08:13 - 11:59, 13:07 - 17:47` yields its true 8h26m a day.
151
+ `timingresolution` overrides this.
139
152
 
140
- - **Temporal Physics**: Floating point precision, Timezones, DST awareness, Date Line crossing.
153
+ **Units**
141
154
 
142
- - **Resource Constraints**: Daily/Weekly limits, Hierarchical quotas, Shift intersections.
155
+ - Calendar time (`duration`, `gapduration`) and working time (`effort`, `length`,
156
+ `gaplength`) are distinguished, with working time derived from
157
+ `dailyworkinghours` and `yearlyworkingdays`. `m` means months; `min` means minutes.
143
158
 
144
- - **Advanced Scheduling**: ALAP (Backward pass), Priority preemption, Smart Resource Selection (Failover).
159
+ **Resources**
145
160
 
146
- - **Workflow Logic**: Atomicity (Contiguous), Perishability (Max Gap), Zero-Buffer Synchronization.
161
+ - `effort` is person-time: two resources available in a slot contribute two
162
+ slots of it. `mandatory` requires a resource for the task to progress at all;
163
+ `persistent` keeps the one first chosen; `alternative` falls back.
164
+ - `dailymax`, `weeklymax`, `monthlymax`, `maximum` and their `min` counterparts
165
+ block at the cap **and** release in the next period. Periods are counted in the
166
+ resource's own calendar, and a cap smaller than one slot is honoured exactly.
147
167
 
148
- ## Features
168
+ **Scheduling**
149
169
 
150
- - Minute-level scheduling precision
151
- - ASAP and ALAP scheduling modes
152
- - Resource allocation with contention handling
153
- - Working hours and shift definitions
154
- - Dependency chains with gap constraints
155
- - Leap year and timezone handling
170
+ - ASAP and ALAP, including chains that mix the two.
171
+ - Contention is resolved by priority, then criticalness, then declaration order.
172
+ - The declared project period is never rewritten. A task that does not fit is
173
+ reported, with its name and the reason.
174
+ - Scheduling the same project twice produces the same dates and the same bookings.
175
+ - `maxgapduration`, `maxgaplength`, `maxstart`, `minstart`, `maxend` and `minend`
176
+ are checked once the schedule is complete, and a violation is an error.
156
177
 
157
- ## Accuracy
178
+ **Honesty**
158
179
 
159
- ScriptPlan uses integer arithmetic for all time calculations, avoiding floating-point drift. The scheduler correctly handles:
180
+ - A project that cannot be scheduled exits non-zero and says why. A dependency
181
+ cycle is rejected before scheduling, naming the cycle.
182
+ - An attribute or reference the parser cannot place is an error, never a silent
183
+ drop.
184
+ - `report_id` is a SHA-256 of the rendered result, so it differs when the
185
+ schedule differs.
160
186
 
161
- - Non-standard shift boundaries (e.g., 08:13 - 11:59, 13:07 - 17:47)
162
- - Prime-number effort durations with prime-number gaps
163
- - Resource contention across multi-day tasks
164
- - Calendar gaps (weekends, holidays) vs working time gaps
187
+ ## Known limits
165
188
 
166
- Example: A chain of 500 tasks, each with 73-minute effort and 29-minute gaps, scheduled across leap year boundaries with non-standard shifts, produces exact minute-aligned results.
189
+ - `include`, `supplement`, `leaveallowance` and `statussheet` are not implemented.
190
+ - Output formats are JSON and CSV. `ical`, `tjp`, `niku` and MS Project XML are
191
+ rejected at parse time rather than silently producing nothing.
192
+ - Accounts are parsed but not yet modelled, so `chargeset` and `charge` record an
193
+ amount without posting it anywhere.
194
+ - `onstart` dependencies are read as "the dependent task must finish before the
195
+ predecessor starts", which is ScriptPlan's own reading rather than
196
+ TaskJuggler's start-to-start one.
167
197
 
168
198
  ## Example Project
169
199
 
@@ -232,14 +262,21 @@ delivery.assemble_b: 2025-07-16-08:00 -> 2025-07-17-16:00
232
262
 
233
263
  ## License
234
264
 
235
- Apache 2.0
265
+ Apache-2.0
266
+
267
+ ## Acknowledgments
268
+
269
+ ScriptPlan references [TaskJuggler](https://taskjuggler.org/) solely for **file format compatibility**. We have not used, modified, or copied any TaskJuggler source code. ScriptPlan is an independent, clean-room implementation.
270
+
271
+ - The `.tjp` file format is documented publicly and widely used in the project management community
272
+ - All scheduling algorithms, parser, and report generation in ScriptPlan are original implementations
273
+ - TaskJuggler is mentioned only to indicate that ScriptPlan can read the same project file format
274
+
275
+ If you're looking for the original TaskJuggler with its full feature set including interactive HTML reports and GUI tools, please visit [taskjuggler.org](https://taskjuggler.org/).
236
276
 
277
+ ## Why ScriptPlan?
237
278
 
238
- ## Why?
239
- This scheduler is part of Highway Workflow Egnine's worker capacity management system.
240
- I decided to open-source it for the community to benefit from its precise scheduling capabilities.
241
- Thanks to TaskJuggler's established syntax, users can easily adopt ScriptPlan without learning a new format.
242
- Credits to TaskJuggler for the original project definition language and concepts.
279
+ This scheduler is part of Highway Workflow Engine's worker capacity management system. I decided to open-source it for the community to benefit from its precise scheduling capabilities. Thanks to TaskJuggler's established syntax, users can easily adopt ScriptPlan without learning a new format.
243
280
 
244
281
  Yours,
245
282
  Farshid.
@@ -0,0 +1,237 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/rodmena-limited/scriptplan/main/icons/logo.svg" alt="ScriptPlan" width="400"/>
3
+ </p>
4
+
5
+ # ScriptPlan
6
+
7
+ ![Tests](https://img.shields.io/badge/tests-411_passing-success?style=for-the-badge)
8
+ ![Probes](https://img.shields.io/badge/audit_probes-67_passing-success?style=for-the-badge)
9
+ ![Python](https://img.shields.io/badge/python-3.9%2B-blue?style=for-the-badge)
10
+ ![Licence](https://img.shields.io/badge/licence-Apache--2.0-lightgrey?style=for-the-badge)
11
+
12
+ A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management. The syntax is compatible with TaskJuggler (.tjp files).
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install scriptplan
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### Report Generation (Unix-style)
23
+
24
+ ```bash
25
+ # Generate JSON report to stdout
26
+ plan report project.tjp
27
+
28
+ # Generate CSV report
29
+ plan report --csv project.tjp
30
+
31
+ # Save to file
32
+ plan report project.tjp > output.json
33
+ plan report --csv project.tjp > output.csv
34
+
35
+ # Read from stdin
36
+ cat project.tjp | plan report
37
+ plan report - < project.tjp
38
+
39
+ # Pipe to other tools
40
+ plan report project.tjp | jq '.data[0]'
41
+ plan report --csv project.tjp | csvkit
42
+
43
+ # Process multiple files
44
+ for f in projects/*.tjp; do
45
+ plan report "$f" | jq -r '.report_id'
46
+ done
47
+ ```
48
+
49
+ **Output Format** (JSON):
50
+ ```json
51
+ {
52
+ "data": [
53
+ {
54
+ "id": "project.task1",
55
+ "start": "2024-01-01-09:00",
56
+ "end": "2024-01-05-17:00"
57
+ }
58
+ ],
59
+ "columns": ["id", "start", "end"],
60
+ "report_id": "ea3f901dd6426dfa58288d945819c75485fd9ff1875db59def350f219e2d62ca"
61
+ }
62
+ ```
63
+
64
+ **Features**:
65
+ - Output to stdout (Unix philosophy)
66
+ - Messages to stderr
67
+ - SHA256 `report_id` over the rendered result, so it changes when the schedule does
68
+ - Lowercase column names
69
+ - No HTML metadata
70
+ - No file pollution (uses temp directories)
71
+ - Separate processes do not collide: temporary files and directories are unique per run
72
+
73
+ ### Python API
74
+
75
+ ```python
76
+ from scriptplan.parser.tjp_parser import ProjectFileParser
77
+
78
+ parser = ProjectFileParser()
79
+ project = parser.parse(open('project.tjp').read())
80
+
81
+ # Access scheduled tasks
82
+ for task in project.tasks:
83
+ if task.leaf():
84
+ start = task.get('start', 0)
85
+ end = task.get('end', 0)
86
+ print(f"{task.id}: {start} -> {end}")
87
+ ```
88
+
89
+ ## What is verified
90
+
91
+ Each behaviour below has a probe in `audit/evaluations/` that drives ScriptPlan
92
+ through its own API or CLI and asserts the result. They run as part of the test
93
+ suite, and can be run on their own against an installed release:
94
+
95
+ ```bash
96
+ audit/evaluations/run_all.sh
97
+ ```
98
+
99
+ **Time**
100
+
101
+ - Dates are read in the project's timezone and scheduled in UTC; reports convert back.
102
+ - Resource shifts follow their own timezone across daylight-saving changes in both directions.
103
+ - A working-hours interval that crosses midnight applies to the following day.
104
+ - Slot size is derived from the times a project uses, so `start 09:30` is honoured
105
+ exactly and a shift of `08:13 - 11:59, 13:07 - 17:47` yields its true 8h26m a day.
106
+ `timingresolution` overrides this.
107
+
108
+ **Units**
109
+
110
+ - Calendar time (`duration`, `gapduration`) and working time (`effort`, `length`,
111
+ `gaplength`) are distinguished, with working time derived from
112
+ `dailyworkinghours` and `yearlyworkingdays`. `m` means months; `min` means minutes.
113
+
114
+ **Resources**
115
+
116
+ - `effort` is person-time: two resources available in a slot contribute two
117
+ slots of it. `mandatory` requires a resource for the task to progress at all;
118
+ `persistent` keeps the one first chosen; `alternative` falls back.
119
+ - `dailymax`, `weeklymax`, `monthlymax`, `maximum` and their `min` counterparts
120
+ block at the cap **and** release in the next period. Periods are counted in the
121
+ resource's own calendar, and a cap smaller than one slot is honoured exactly.
122
+
123
+ **Scheduling**
124
+
125
+ - ASAP and ALAP, including chains that mix the two.
126
+ - Contention is resolved by priority, then criticalness, then declaration order.
127
+ - The declared project period is never rewritten. A task that does not fit is
128
+ reported, with its name and the reason.
129
+ - Scheduling the same project twice produces the same dates and the same bookings.
130
+ - `maxgapduration`, `maxgaplength`, `maxstart`, `minstart`, `maxend` and `minend`
131
+ are checked once the schedule is complete, and a violation is an error.
132
+
133
+ **Honesty**
134
+
135
+ - A project that cannot be scheduled exits non-zero and says why. A dependency
136
+ cycle is rejected before scheduling, naming the cycle.
137
+ - An attribute or reference the parser cannot place is an error, never a silent
138
+ drop.
139
+ - `report_id` is a SHA-256 of the rendered result, so it differs when the
140
+ schedule differs.
141
+
142
+ ## Known limits
143
+
144
+ - `include`, `supplement`, `leaveallowance` and `statussheet` are not implemented.
145
+ - Output formats are JSON and CSV. `ical`, `tjp`, `niku` and MS Project XML are
146
+ rejected at parse time rather than silently producing nothing.
147
+ - Accounts are parsed but not yet modelled, so `chargeset` and `charge` record an
148
+ amount without posting it anywhere.
149
+ - `onstart` dependencies are read as "the dependent task must finish before the
150
+ predecessor starts", which is ScriptPlan's own reading rather than
151
+ TaskJuggler's start-to-start one.
152
+
153
+ ## Example Project
154
+
155
+ ```
156
+ project "Manufacturing" 2025-07-01 +1m {
157
+ timezone "Etc/UTC"
158
+ timeformat "%Y-%m-%d %H:%M"
159
+ scheduling alap
160
+ }
161
+
162
+ shift factory_hours "Factory Hours" {
163
+ workinghours mon - fri 08:00 - 16:00
164
+ }
165
+
166
+ resource machine "Press" {
167
+ workinghours factory_hours
168
+ }
169
+
170
+ task delivery "Product Launch" {
171
+ end 2025-07-18-16:00
172
+
173
+ task pack "Packaging" {
174
+ effort 8h
175
+ allocate machine
176
+ }
177
+
178
+ task assemble_b "Body Assembly" {
179
+ effort 16h
180
+ allocate machine
181
+ depends !!pack { onstart }
182
+ }
183
+
184
+ task assemble_a "Engine Assembly" {
185
+ effort 16h
186
+ allocate machine
187
+ }
188
+ }
189
+
190
+ task connection_setup "Logic" {
191
+ task set_deps "Apply" {
192
+ depends !delivery.assemble_a, !delivery.assemble_b
193
+ precedes !delivery.pack
194
+ }
195
+ }
196
+
197
+ taskreport output "output" {
198
+ formats csv
199
+ columns id, start, end
200
+ timeformat "%Y-%m-%d-%H:%M"
201
+ }
202
+ ```
203
+
204
+ This project schedules backward from the delivery deadline (ALAP mode). The scheduler:
205
+
206
+ 1. Anchors packaging to end at 16:00 on July 18
207
+ 2. Schedules both assembly tasks to complete before packaging starts
208
+ 3. Resolves resource contention (single machine) by sequencing assemblies back-to-back
209
+ 4. Respects weekend boundaries (Mon-Fri working hours)
210
+
211
+ Result:
212
+ ```
213
+ delivery.pack: 2025-07-18-08:00 -> 2025-07-18-16:00
214
+ delivery.assemble_a: 2025-07-14-08:00 -> 2025-07-15-16:00
215
+ delivery.assemble_b: 2025-07-16-08:00 -> 2025-07-17-16:00
216
+ ```
217
+
218
+ ## License
219
+
220
+ Apache-2.0
221
+
222
+ ## Acknowledgments
223
+
224
+ ScriptPlan references [TaskJuggler](https://taskjuggler.org/) solely for **file format compatibility**. We have not used, modified, or copied any TaskJuggler source code. ScriptPlan is an independent, clean-room implementation.
225
+
226
+ - The `.tjp` file format is documented publicly and widely used in the project management community
227
+ - All scheduling algorithms, parser, and report generation in ScriptPlan are original implementations
228
+ - TaskJuggler is mentioned only to indicate that ScriptPlan can read the same project file format
229
+
230
+ If you're looking for the original TaskJuggler with its full feature set including interactive HTML reports and GUI tools, please visit [taskjuggler.org](https://taskjuggler.org/).
231
+
232
+ ## Why ScriptPlan?
233
+
234
+ This scheduler is part of Highway Workflow Engine's worker capacity management system. I decided to open-source it for the community to benefit from its precise scheduling capabilities. Thanks to TaskJuggler's established syntax, users can easily adopt ScriptPlan without learning a new format.
235
+
236
+ Yours,
237
+ Farshid.
@@ -4,7 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "scriptplan"
7
- version = "0.9.1"
7
+ # Single source of truth: scriptplan/__init__.py
8
+ dynamic = ["version"]
8
9
  description = "A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management."
9
10
  authors = [
10
11
  { name = "Farshid Ashouri", email = "farsheed.ashouri@gmail.com" },
@@ -61,10 +62,14 @@ plan = "scriptplan.cli.plan:main"
61
62
  "Documentation" = "https://github.com/rodmena-limited/scriptplan#readme"
62
63
  "Bug Tracker" = "https://github.com/rodmena-limited/scriptplan/issues"
63
64
  "Source" = "https://github.com/rodmena-limited/scriptplan"
65
+ "Changelog" = "https://github.com/rodmena-limited/scriptplan/blob/main/CHANGELOG.md"
64
66
 
65
67
  [tool.setuptools]
66
68
  include-package-data = true
67
69
 
70
+ [tool.setuptools.dynamic]
71
+ version = { attr = "scriptplan.__version__" }
72
+
68
73
  [tool.setuptools.packages.find]
69
74
  where = ["."]
70
75
  include = ["scriptplan*"]
@@ -77,7 +82,9 @@ testpaths = ["tests"]
77
82
  python_files = ["test_*.py"]
78
83
  python_classes = ["Test*"]
79
84
  python_functions = ["test_*"]
80
- addopts = "-v --tb=short -n auto"
85
+ # No -n here: pytest-xdist is an optional extra, and requiring it means
86
+ # the suite cannot start without it. Pass -n yourself to parallelise.
87
+ addopts = "--tb=short"
81
88
  filterwarnings = [
82
89
  "ignore::DeprecationWarning",
83
90
  "ignore::PendingDeprecationWarning",
@@ -0,0 +1,29 @@
1
+ """
2
+ ScriptPlan - A Python implementation inspired by TaskJuggler.
3
+
4
+ This package provides project scheduling and resource management capabilities
5
+ compatible with the TaskJuggler Project Definition Language (.tjp files).
6
+
7
+ ScriptPlan owes its existence to TaskJuggler (https://taskjuggler.org/),
8
+ the pioneering open-source project management tool created by Chris Schlaeger.
9
+ The TJP file format and scheduling concepts originate from TaskJuggler.
10
+ """
11
+
12
+ # The single source of truth for the version; pyproject reads it from here.
13
+ # A semantic change: effort is now person-time, failures are reported as
14
+ # failures, and the compiled extensions are gone.
15
+ __version__ = "0.10.0"
16
+ __author__ = "ScriptPlan Team"
17
+
18
+ from scriptplan.core.project import Project
19
+ from scriptplan.core.resource import Resource
20
+ from scriptplan.core.scenario import Scenario
21
+ from scriptplan.core.task import Task
22
+
23
+ __all__ = [
24
+ "Project",
25
+ "Resource",
26
+ "Scenario",
27
+ "Task",
28
+ "__version__",
29
+ ]
@@ -4,4 +4,4 @@ Command-line interface for ScriptPlan.
4
4
 
5
5
  from scriptplan.cli.main import ScriptPlan, create_parser, main
6
6
 
7
- __all__ = ['ScriptPlan', 'create_parser', 'main']
7
+ __all__ = ["ScriptPlan", "create_parser", "main"]