scriptplan 0.10.1__tar.gz → 0.10.2__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 (276) hide show
  1. {scriptplan-0.10.1 → scriptplan-0.10.2}/CHANGELOG.md +140 -1
  2. {scriptplan-0.10.1 → scriptplan-0.10.2}/MANIFEST.in +1 -0
  3. {scriptplan-0.10.1/scriptplan.egg-info → scriptplan-0.10.2}/PKG-INFO +33 -18
  4. {scriptplan-0.10.1 → scriptplan-0.10.2}/README.md +32 -17
  5. scriptplan-0.10.2/examples/README.md +42 -0
  6. scriptplan-0.10.2/examples/edge_cases/alap_before_declared_start.expected.csv +5 -0
  7. scriptplan-0.10.2/examples/edge_cases/alap_before_declared_start.expected.md +6 -0
  8. scriptplan-0.10.2/examples/edge_cases/alap_before_declared_start.tjp +8 -0
  9. scriptplan-0.10.2/examples/edge_cases/alap_bounded_by_declared_starts.expected.csv +5 -0
  10. scriptplan-0.10.2/examples/edge_cases/alap_bounded_by_declared_starts.expected.md +6 -0
  11. scriptplan-0.10.2/examples/edge_cases/alap_bounded_by_declared_starts.tjp +8 -0
  12. scriptplan-0.10.2/examples/edge_cases/alap_gapduration_before_successor.expected.csv +5 -0
  13. scriptplan-0.10.2/examples/edge_cases/alap_gapduration_before_successor.expected.md +6 -0
  14. scriptplan-0.10.2/examples/edge_cases/alap_gapduration_before_successor.tjp +15 -0
  15. scriptplan-0.10.2/examples/edge_cases/alap_keeps_successor_gaps.expected.csv +7 -0
  16. scriptplan-0.10.2/examples/edge_cases/alap_keeps_successor_gaps.expected.md +8 -0
  17. scriptplan-0.10.2/examples/edge_cases/alap_keeps_successor_gaps.tjp +10 -0
  18. scriptplan-0.10.2/examples/edge_cases/alap_milestones_exact.expected.csv +6 -0
  19. scriptplan-0.10.2/examples/edge_cases/alap_milestones_exact.expected.md +5 -0
  20. scriptplan-0.10.2/examples/edge_cases/alap_milestones_exact.tjp +9 -0
  21. scriptplan-0.10.2/examples/edge_cases/alap_onstart_reading.expected.csv +5 -0
  22. scriptplan-0.10.2/examples/edge_cases/alap_onstart_reading.expected.md +5 -0
  23. scriptplan-0.10.2/examples/edge_cases/alap_onstart_reading.tjp +15 -0
  24. scriptplan-0.10.2/examples/edge_cases/alap_own_end_before_successor.expected.csv +5 -0
  25. scriptplan-0.10.2/examples/edge_cases/alap_own_end_before_successor.expected.md +5 -0
  26. scriptplan-0.10.2/examples/edge_cases/alap_own_end_before_successor.tjp +15 -0
  27. scriptplan-0.10.2/examples/edge_cases/alap_project_end_and_declared_end.expected.csv +4 -0
  28. scriptplan-0.10.2/examples/edge_cases/alap_project_end_and_declared_end.expected.md +5 -0
  29. scriptplan-0.10.2/examples/edge_cases/alap_project_end_and_declared_end.tjp +7 -0
  30. scriptplan-0.10.2/examples/edge_cases/alap_spans_before_mid_slot_successor.expected.csv +5 -0
  31. scriptplan-0.10.2/examples/edge_cases/alap_spans_before_mid_slot_successor.expected.md +5 -0
  32. scriptplan-0.10.2/examples/edge_cases/alap_spans_before_mid_slot_successor.tjp +8 -0
  33. scriptplan-0.10.2/examples/edge_cases/alap_without_anchor_parent.expected.csv +5 -0
  34. scriptplan-0.10.2/examples/edge_cases/alap_without_anchor_parent.expected.md +6 -0
  35. scriptplan-0.10.2/examples/edge_cases/alap_without_anchor_parent.tjp +15 -0
  36. scriptplan-0.10.2/examples/edge_cases/alap_without_anchor_plan.expected.csv +5 -0
  37. scriptplan-0.10.2/examples/edge_cases/alap_without_anchor_plan.expected.md +6 -0
  38. scriptplan-0.10.2/examples/edge_cases/alap_without_anchor_plan.tjp +15 -0
  39. scriptplan-0.10.2/examples/edge_cases/asap_and_alap_share_a_slot.expected.csv +4 -0
  40. scriptplan-0.10.2/examples/edge_cases/asap_and_alap_share_a_slot.expected.md +5 -0
  41. scriptplan-0.10.2/examples/edge_cases/asap_and_alap_share_a_slot.tjp +7 -0
  42. scriptplan-0.10.2/examples/edge_cases/daily_cap_counts_worked_time.expected.csv +4 -0
  43. scriptplan-0.10.2/examples/edge_cases/daily_cap_counts_worked_time.expected.md +6 -0
  44. scriptplan-0.10.2/examples/edge_cases/daily_cap_counts_worked_time.tjp +7 -0
  45. scriptplan-0.10.2/examples/edge_cases/dependency_offset_one_slot.expected.csv +4 -0
  46. scriptplan-0.10.2/examples/edge_cases/dependency_offset_one_slot.expected.md +5 -0
  47. scriptplan-0.10.2/examples/edge_cases/dependency_offset_one_slot.tjp +8 -0
  48. scriptplan-0.10.2/examples/edge_cases/duration_length_after_mid_slot_end.expected.csv +4 -0
  49. scriptplan-0.10.2/examples/edge_cases/duration_length_after_mid_slot_end.expected.md +5 -0
  50. scriptplan-0.10.2/examples/edge_cases/duration_length_after_mid_slot_end.tjp +7 -0
  51. scriptplan-0.10.2/examples/edge_cases/eight_efforts_fill_whole_days.expected.csv +9 -0
  52. scriptplan-0.10.2/examples/edge_cases/eight_efforts_fill_whole_days.expected.md +5 -0
  53. scriptplan-0.10.2/examples/edge_cases/eight_efforts_fill_whole_days.tjp +12 -0
  54. scriptplan-0.10.2/examples/edge_cases/gaplength_on_project_calendar.expected.csv +5 -0
  55. scriptplan-0.10.2/examples/edge_cases/gaplength_on_project_calendar.expected.md +6 -0
  56. scriptplan-0.10.2/examples/edge_cases/gaplength_on_project_calendar.tjp +15 -0
  57. scriptplan-0.10.2/examples/edge_cases/inherited_start_waits_for_dependency.expected.csv +4 -0
  58. scriptplan-0.10.2/examples/edge_cases/inherited_start_waits_for_dependency.expected.md +6 -0
  59. scriptplan-0.10.2/examples/edge_cases/inherited_start_waits_for_dependency.tjp +19 -0
  60. scriptplan-0.10.2/examples/edge_cases/mid_slot_start_and_end.expected.csv +3 -0
  61. scriptplan-0.10.2/examples/edge_cases/mid_slot_start_and_end.expected.md +4 -0
  62. scriptplan-0.10.2/examples/edge_cases/mid_slot_start_and_end.tjp +7 -0
  63. scriptplan-0.10.2/examples/edge_cases/shared_slot_backward.expected.csv +4 -0
  64. scriptplan-0.10.2/examples/edge_cases/shared_slot_backward.expected.md +4 -0
  65. scriptplan-0.10.2/examples/edge_cases/shared_slot_backward.tjp +7 -0
  66. scriptplan-0.10.2/examples/edge_cases/shared_slot_forward_5min.expected.csv +4 -0
  67. scriptplan-0.10.2/examples/edge_cases/shared_slot_forward_5min.expected.md +6 -0
  68. scriptplan-0.10.2/examples/edge_cases/shared_slot_forward_5min.tjp +7 -0
  69. scriptplan-0.10.2/examples/edge_cases/shared_slot_forward_60min.expected.csv +4 -0
  70. scriptplan-0.10.2/examples/edge_cases/shared_slot_forward_60min.expected.md +6 -0
  71. scriptplan-0.10.2/examples/edge_cases/shared_slot_forward_60min.tjp +7 -0
  72. scriptplan-0.10.2/examples/edge_cases/skipped_time_stays_free.expected.csv +5 -0
  73. scriptplan-0.10.2/examples/edge_cases/skipped_time_stays_free.expected.md +6 -0
  74. scriptplan-0.10.2/examples/edge_cases/skipped_time_stays_free.tjp +8 -0
  75. scriptplan-0.10.2/examples/edge_cases/two_people_finish_together.expected.csv +2 -0
  76. scriptplan-0.10.2/examples/edge_cases/two_people_finish_together.expected.md +5 -0
  77. scriptplan-0.10.2/examples/edge_cases/two_people_finish_together.tjp +6 -0
  78. scriptplan-0.10.2/examples/showcase/airport_retrofit.expected.csv +5 -0
  79. scriptplan-0.10.2/examples/showcase/airport_retrofit.expected.md +84 -0
  80. scriptplan-0.10.2/examples/showcase/airport_retrofit.tjp +80 -0
  81. scriptplan-0.10.2/examples/showcase/airport_stress_test.expected.csv +9 -0
  82. scriptplan-0.10.2/examples/showcase/airport_stress_test.expected.md +106 -0
  83. scriptplan-0.10.2/examples/showcase/airport_stress_test.tjp +110 -0
  84. scriptplan-0.10.2/examples/showcase/airport_ultra_math.expected.csv +6 -0
  85. scriptplan-0.10.2/examples/showcase/airport_ultra_math.expected.md +111 -0
  86. scriptplan-0.10.2/examples/showcase/airport_ultra_math.tjp +160 -0
  87. scriptplan-0.10.2/examples/showcase/airport_ultra_math_report.expected.csv +6 -0
  88. scriptplan-0.10.2/examples/showcase/airport_ultra_math_report.expected.md +30 -0
  89. scriptplan-0.10.2/examples/showcase/airport_ultra_math_report.tjp +152 -0
  90. scriptplan-0.10.2/examples/showcase/alap_backward.expected.csv +4 -0
  91. scriptplan-0.10.2/examples/showcase/alap_backward.expected.md +61 -0
  92. scriptplan-0.10.2/examples/showcase/alap_backward.tjp +44 -0
  93. scriptplan-0.10.2/examples/showcase/atomic.expected.csv +2 -0
  94. scriptplan-0.10.2/examples/showcase/atomic.expected.md +49 -0
  95. scriptplan-0.10.2/examples/showcase/atomic.tjp +31 -0
  96. scriptplan-0.10.2/examples/showcase/blackbox.expected.csv +4 -0
  97. scriptplan-0.10.2/examples/showcase/blackbox.expected.md +86 -0
  98. scriptplan-0.10.2/examples/showcase/blackbox.tjp +51 -0
  99. scriptplan-0.10.2/examples/showcase/bottleneck.expected.csv +5 -0
  100. scriptplan-0.10.2/examples/showcase/bottleneck.expected.md +67 -0
  101. scriptplan-0.10.2/examples/showcase/bottleneck.tjp +55 -0
  102. scriptplan-0.10.2/examples/showcase/eclipse.expected.csv +3 -0
  103. scriptplan-0.10.2/examples/showcase/eclipse.expected.md +60 -0
  104. scriptplan-0.10.2/examples/showcase/eclipse.tjp +43 -0
  105. scriptplan-0.10.2/examples/showcase/failover.expected.csv +2 -0
  106. scriptplan-0.10.2/examples/showcase/failover.expected.md +48 -0
  107. scriptplan-0.10.2/examples/showcase/failover.tjp +37 -0
  108. scriptplan-0.10.2/examples/showcase/jit_supply.expected.csv +7 -0
  109. scriptplan-0.10.2/examples/showcase/jit_supply.expected.md +126 -0
  110. scriptplan-0.10.2/examples/showcase/jit_supply.tjp +58 -0
  111. scriptplan-0.10.2/examples/showcase/math_torture.expected.csv +502 -0
  112. scriptplan-0.10.2/examples/showcase/math_torture.expected.md +208 -0
  113. scriptplan-0.10.2/examples/showcase/math_torture.tjp +3028 -0
  114. scriptplan-0.10.2/examples/showcase/paradox.expected.csv +4 -0
  115. scriptplan-0.10.2/examples/showcase/paradox.expected.md +89 -0
  116. scriptplan-0.10.2/examples/showcase/paradox.tjp +48 -0
  117. scriptplan-0.10.2/examples/showcase/priority_clash.expected.csv +4 -0
  118. scriptplan-0.10.2/examples/showcase/priority_clash.expected.md +36 -0
  119. scriptplan-0.10.2/examples/showcase/priority_clash.tjp +36 -0
  120. scriptplan-0.10.2/examples/showcase/quota.expected.csv +5 -0
  121. scriptplan-0.10.2/examples/showcase/quota.expected.md +63 -0
  122. scriptplan-0.10.2/examples/showcase/quota.tjp +49 -0
  123. scriptplan-0.10.2/examples/showcase/simple.expected.csv +5 -0
  124. scriptplan-0.10.2/examples/showcase/simple.expected.md +52 -0
  125. scriptplan-0.10.2/examples/showcase/simple.tjp +50 -0
  126. scriptplan-0.10.2/examples/showcase/synchrony.expected.csv +5 -0
  127. scriptplan-0.10.2/examples/showcase/synchrony.expected.md +54 -0
  128. scriptplan-0.10.2/examples/showcase/synchrony.tjp +70 -0
  129. scriptplan-0.10.2/examples/showcase/thermal.expected.csv +4 -0
  130. scriptplan-0.10.2/examples/showcase/thermal.expected.md +82 -0
  131. scriptplan-0.10.2/examples/showcase/thermal.tjp +43 -0
  132. scriptplan-0.10.2/examples/showcase/throughput.expected.csv +2 -0
  133. scriptplan-0.10.2/examples/showcase/throughput.expected.md +42 -0
  134. scriptplan-0.10.2/examples/showcase/throughput.tjp +37 -0
  135. scriptplan-0.10.2/examples/showcase/time_traveler.expected.csv +4 -0
  136. scriptplan-0.10.2/examples/showcase/time_traveler.expected.md +75 -0
  137. scriptplan-0.10.2/examples/showcase/time_traveler.tjp +63 -0
  138. scriptplan-0.10.2/examples/showcase/timezone_stress.expected.csv +4 -0
  139. scriptplan-0.10.2/examples/showcase/timezone_stress.expected.md +56 -0
  140. scriptplan-0.10.2/examples/showcase/timezone_stress.tjp +65 -0
  141. scriptplan-0.10.2/examples/showcase/tutorial.expected.csv +16 -0
  142. scriptplan-0.10.2/examples/showcase/tutorial.expected.md +448 -0
  143. scriptplan-0.10.2/examples/showcase/tutorial.tjp +494 -0
  144. scriptplan-0.10.2/examples/showcase/union_contract.expected.csv +6 -0
  145. scriptplan-0.10.2/examples/showcase/union_contract.expected.md +44 -0
  146. scriptplan-0.10.2/examples/showcase/union_contract.tjp +53 -0
  147. scriptplan-0.10.2/examples/showcase/workflow_engine.expected.csv +4 -0
  148. scriptplan-0.10.2/examples/showcase/workflow_engine.expected.md +43 -0
  149. scriptplan-0.10.2/examples/showcase/workflow_engine.tjp +66 -0
  150. scriptplan-0.10.2/examples/simple.tjp +26 -0
  151. {scriptplan-0.10.1 → scriptplan-0.10.2}/pyproject.toml +1 -1
  152. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/__init__.py +4 -8
  153. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/cli/main.py +3 -3
  154. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/cli/plan.py +16 -7
  155. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/cli/plan_help.py +5 -5
  156. scriptplan-0.10.2/scriptplan/core/declared_dates.py +47 -0
  157. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/project.py +20 -57
  158. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/resource_reporting.py +32 -28
  159. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/resource_scenario.py +12 -15
  160. scriptplan-0.10.2/scriptplan/core/slot_ledger.py +263 -0
  161. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/task_booking.py +24 -11
  162. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/task_dependencies.py +51 -1
  163. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/task_placement.py +107 -73
  164. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/task_resources.py +36 -10
  165. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/task_scenario.py +42 -15
  166. scriptplan-0.10.2/scriptplan/core/task_spans.py +65 -0
  167. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/units.py +4 -4
  168. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/model_pieces.py +51 -63
  169. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/tjp.lark +3 -3
  170. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/tjp_parser.py +28 -5
  171. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/logical_expression.py +5 -4
  172. scriptplan-0.10.2/scriptplan/scheduler/postcheck.py +211 -0
  173. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/scheduler/preflight.py +32 -1
  174. scriptplan-0.10.2/scriptplan/scheduler/stalls.py +89 -0
  175. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/utils/message_handler.py +37 -21
  176. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/utils/messages.py +2 -2
  177. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/utils/time.py +1 -1
  178. {scriptplan-0.10.1 → scriptplan-0.10.2/scriptplan.egg-info}/PKG-INFO +33 -18
  179. scriptplan-0.10.2/scriptplan.egg-info/SOURCES.txt +271 -0
  180. scriptplan-0.10.2/tests/test_dependency_check.py +67 -0
  181. scriptplan-0.10.2/tests/test_examples.py +117 -0
  182. scriptplan-0.10.2/tests/test_failure_messages.py +49 -0
  183. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_fixture_golden.py +2 -2
  184. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_parser.py +2 -2
  185. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_property_set.py +1 -1
  186. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_scheduling.py +1 -1
  187. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_scheduling_validation.py +21 -21
  188. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_slot_ledger.py +36 -16
  189. scriptplan-0.10.2/tests/test_stated_dates.py +107 -0
  190. scriptplan-0.10.2/tests/test_threads.py +108 -0
  191. scriptplan-0.10.1/scriptplan/core/slot_ledger.py +0 -234
  192. scriptplan-0.10.1/scriptplan/scheduler/postcheck.py +0 -106
  193. scriptplan-0.10.1/scriptplan.egg-info/SOURCES.txt +0 -117
  194. {scriptplan-0.10.1 → scriptplan-0.10.2}/LICENSE +0 -0
  195. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/cli/__init__.py +0 -0
  196. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/cli/plan_files.py +0 -0
  197. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/__init__.py +0 -0
  198. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/account.py +0 -0
  199. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/allocation.py +0 -0
  200. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/allocation_plan.py +0 -0
  201. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/attributes.py +0 -0
  202. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/booking.py +0 -0
  203. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/exceptions.py +0 -0
  204. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/journal.py +0 -0
  205. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/leave.py +0 -0
  206. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/limits.py +0 -0
  207. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/project_attributes.py +0 -0
  208. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/project_calendar.py +0 -0
  209. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/project_tree.py +0 -0
  210. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/property.py +0 -0
  211. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/property_list.py +0 -0
  212. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/property_set.py +0 -0
  213. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/property_tree_node.py +0 -0
  214. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/resolution.py +0 -0
  215. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/resource.py +0 -0
  216. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/resource_availability.py +0 -0
  217. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/scenario.py +0 -0
  218. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/scenario_data.py +0 -0
  219. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/shift.py +0 -0
  220. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/task.py +0 -0
  221. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/timesheet.py +0 -0
  222. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/timezones.py +0 -0
  223. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/core/working_hours.py +0 -0
  224. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/__init__.py +0 -0
  225. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/attribute_apply.py +0 -0
  226. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/macro_processor.py +0 -0
  227. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/report_builder.py +0 -0
  228. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/transformer_project.py +0 -0
  229. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/transformer_report.py +0 -0
  230. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/transformer_resource.py +0 -0
  231. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/parser/transformer_task.py +0 -0
  232. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/py.typed +0 -0
  233. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/__init__.py +0 -0
  234. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/report.py +0 -0
  235. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/report_base.py +0 -0
  236. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/report_context.py +0 -0
  237. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/resource_report.py +0 -0
  238. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/table_cells.py +0 -0
  239. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/table_headers.py +0 -0
  240. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/table_report.py +0 -0
  241. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/task_report.py +0 -0
  242. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/report/text_report.py +0 -0
  243. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/scheduler/__init__.py +0 -0
  244. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/scheduler/scoreboard.py +0 -0
  245. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/utils/__init__.py +0 -0
  246. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/utils/data_cache.py +0 -0
  247. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan/utils/logger.py +0 -0
  248. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan.egg-info/dependency_links.txt +0 -0
  249. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan.egg-info/entry_points.txt +0 -0
  250. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan.egg-info/requires.txt +0 -0
  251. {scriptplan-0.10.1 → scriptplan-0.10.2}/scriptplan.egg-info/top_level.txt +0 -0
  252. {scriptplan-0.10.1 → scriptplan-0.10.2}/setup.cfg +0 -0
  253. {scriptplan-0.10.1 → scriptplan-0.10.2}/setup.py +0 -0
  254. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_account.py +0 -0
  255. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_ansi_color.py +0 -0
  256. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_attribute_base.py +0 -0
  257. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_audit_probes.py +0 -0
  258. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_cli.py +0 -0
  259. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_core_allocation.py +0 -0
  260. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_core_booking.py +0 -0
  261. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_core_project.py +0 -0
  262. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_core_resource.py +0 -0
  263. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_core_scenario.py +0 -0
  264. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_core_task.py +0 -0
  265. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_journal.py +0 -0
  266. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_macro_processor.py +0 -0
  267. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_message_handler.py +0 -0
  268. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_plan_cli.py +0 -0
  269. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_project_isolation.py +0 -0
  270. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_property_list.py +0 -0
  271. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_property_tree_node.py +0 -0
  272. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_report.py +0 -0
  273. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_scoreboard.py +0 -0
  274. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_shift.py +0 -0
  275. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_timesheet.py +0 -0
  276. {scriptplan-0.10.1 → scriptplan-0.10.2}/tests/test_utils_time.py +0 -0
@@ -1,5 +1,144 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.2
4
+
5
+ 0.10.1 shipped two regressions. This release fixes them and the gaps 0.10.1 listed as
6
+ not fixed. It also fixes the defects found while checking every example project
7
+ against answers worked out without the engine, and the thirteen that Trace reported.
8
+ Several plans that 0.10.1 scheduled silently and wrongly are now refused with an error
9
+ that names the task and the date it cannot keep.
10
+
11
+ ### Regressions in 0.10.1
12
+
13
+ - An alap `duration` or `length` task before a successor that starts inside a slot
14
+ ended after that successor started. Before a task starting at 16:20, a 1-hour
15
+ duration ran 16:00-17:00. It now runs 15:20-16:20. 0.10.0 put it at 15:00-16:00.
16
+ - Time a task skipped to honour a mid-slot start or end was lost to every other task.
17
+ After an alap task that had to end at 14:30, a task free from 14:00 ran 15:00-16:00
18
+ instead of 14:30-15:30. Before a task declared to start at 09:30, a task free from
19
+ 09:00 was pushed to 10:00 instead of using 09:00-09:30.
20
+
21
+ Each booking now holds its own interval of the slot, so time a task skips stays free
22
+ and a task that finishes early frees only what follows it.
23
+
24
+ ### Fixed: wrong schedules
25
+
26
+ - A `duration` or `length` task after a task ending mid-slot started at the top of
27
+ that slot, before its predecessor finished. It now starts when the predecessor ends
28
+ and lasts its duration or length to the second.
29
+ - A `duration` waited for working time. After a task ending Friday 17:00 a 2-hour
30
+ duration ran Monday 09:00-11:00, and it now runs Friday 17:00-19:00. An alap 2-hour
31
+ duration that had to end on a Sunday spanned 48 hours, and it now spans 2.
32
+ - A task's finish depended on `timingresolution` when several people shared its last
33
+ slot, because only the first of them worked it. In `tutorial.tjp` the project ended
34
+ at 12:48, 12:00 or 11:44 depending on the slot size alone. All of them now work the
35
+ last slot, and the answer is the same at every resolution.
36
+ - An alap milestone sat a slot before the moment it had to be at. Before a task
37
+ starting at 16:00 it was placed at 15:00. It now sits at 16:00, and one with no
38
+ successor sits at the project end.
39
+ - An alap task did not keep the gap its successor asked for. With `gapduration 24h`
40
+ it ended where the successor began, and now it ends 24 hours earlier.
41
+ - An alap task with an `end` of its own ignored its successors and could end after
42
+ one had started. It now ends by the earlier of the two.
43
+ - An alap task whose successor states its start was moved to the project start, as
44
+ though nothing bounded it. It now ends as late as that start allows.
45
+ - A `start` inherited from a container overrode the task's dependencies. It is now an
46
+ earliest start, and the task waits for what it depends on.
47
+
48
+ ### Fixed: silent wrong schedules are now errors
49
+
50
+ Each of these exits non-zero with a message naming the task and both dates:
51
+
52
+ - a task that would start before a task it depends on ends, including when its own
53
+ `start` says so, with or without a gap, and for milestones;
54
+ - a stated `end` that a task scheduled as soon as possible, or its container, cannot
55
+ meet, which 0.10.1 overran by hours;
56
+ - a stated `start` that an alap task cannot keep;
57
+ - a stated start or end outside the project window. A start after the window used to
58
+ crash with "Error parsing project file: list index out of range", and an end after
59
+ it was clamped while the milestone was dated outside the project.
60
+
61
+ An alap task that cannot fit before its deadline now names that deadline. A task
62
+ whose successor failed is reported as blocked by it. 0.10.1 blamed the project window
63
+ for the first, and moved the second to the start of the project, booked after the
64
+ deadline.
65
+
66
+ ### Fixed: other
67
+
68
+ - A reference that could mean two tasks with the same id bound whichever came first.
69
+ It is now an error that lists both.
70
+ - `getAllocatedTime()` and timesheet figures credited a shared slot to the last task
71
+ booked in it. 40, 13 and 40 minutes in one hour read as 0, 0 and 2 hours, and now
72
+ read as 0.67, 0.22 and 0.67.
73
+ - Each `ProjectFileParser()` compiled the grammar again (about 0.7 s). The grammar is
74
+ now compiled once per process.
75
+ - The `plan` CLI scheduled every project twice and reported any exception raised
76
+ while scheduling as a parse error. It now schedules each project once and reports
77
+ a scheduling exception as a scheduling error.
78
+ - Projects scheduled in parallel threads shared one message list, one error count
79
+ and one switch that turns error collection on. When one thread finished scheduling,
80
+ another thread's error could raise `TjRuntimeError` out of `schedule()` instead of
81
+ making it return False, and an error in one project could fail another. Each thread
82
+ now has its own messages, error count and switch.
83
+ - An `effort` task with no resource allocated was reported as running outside the
84
+ project window because it "needs more time or more resource availability". It is now
85
+ reported as having effort but no resource to do it.
86
+ - `plan report` printed every engine error as "Error: Error: …". It now prints one
87
+ "Error:".
88
+
89
+ ### Documentation
90
+
91
+ - README described `onstart` as one reading for both directions. For a task scheduled
92
+ as soon as possible it is start-to-start. For one scheduled as late as possible it
93
+ means the task must finish before the predecessor starts.
94
+ - README now says that `length` and `gaplength` count the project's working time, how
95
+ an alap task's end is bounded, and that unkept stated dates are errors.
96
+ - README listed `maxgaplength` among the checked constraints. The syntax has never
97
+ accepted it, and the claim is removed.
98
+
99
+ ### Schedules that move
100
+
101
+ A plan whose dates all fall on its slot grid, with no alap milestones and no
102
+ `duration` across non-working time, schedules as it did in 0.10.1. Of the 24 example
103
+ projects, 22 schedule identically:
104
+
105
+ - `tutorial.tjp`: `spec` ends 14:20 instead of 15:00 on 24 January because three
106
+ developers finish it together, and every task after it moves with it. The project
107
+ ends on 18 April at 11:44 instead of 12:48.
108
+ - `jit_supply.tjp`: the alap milestone `connection_setup.set_deps` sits at 2025-07-18
109
+ 08:00, when `pack` starts, instead of 2025-07-17 16:00.
110
+
111
+ ### Internal API
112
+
113
+ `ResourceScenario.reserve()` returns a list of `SlotBooking`, one per free stretch it
114
+ books. The slot ledger is `slotIntervals`, and `slotFrontUsed` and `slotBackUsed` from
115
+ 0.10.1 are gone. Failure handling moved to `scriptplan.scheduler.stalls`, and stated
116
+ dates are tracked in `scriptplan.core.declared_dates`. `slotSecondsUsed` is now
117
+ exactly the sum of `slotTaskUsage` for every slot.
118
+
119
+ ### Verification
120
+
121
+ The golden snapshots record what the engine produced last time. They catch a change
122
+ but not an error, and 0.10.1 passed all of them. `examples/` now holds an answer for every
123
+ task in the 24 showcase projects and in 24 edge cases, each derived by hand from
124
+ the file and the documented semantics without running ScriptPlan, and compared with an
125
+ hour-level reference scheduler wherever it accepts the file. `tests/test_examples.py`
126
+ runs each project through the `plan` CLI and compares every task to the second. Every
127
+ project passes on 0.10.2, and 13 of the edge cases fail on 0.10.1.
128
+
129
+ `audit/evaluations/probe_placement.py` generates 300 plans that mix effort, duration,
130
+ length and milestones in both directions. It checks that every dependency, stated date
131
+ and span holds, that no slot is overfull, and that no task leaves free time it could
132
+ have used. All 300 pass on 0.10.2, against 28 on 0.10.1 and 14
133
+ on 0.10.0.
134
+
135
+ `tests/test_threads.py` schedules five projects three times each in parallel threads
136
+ and compares every date and message with the same project scheduled alone. It also
137
+ forces one thread to leave error collection while another is still inside it. Its
138
+ three tests fail on the 0.10.1 message handler.
139
+
140
+ The full gate runs on Python 3.9 and 3.13.
141
+
3
142
  ## 0.10.1
4
143
 
5
144
  Tasks that shared a timing slot on one resource could be booked into time that
@@ -106,7 +245,7 @@ engine took the largest contribution in each slot rather than the sum, so the
106
245
  labour of every resource but one was booked, billed and discarded.
107
246
 
108
247
  Effort is now the amount of work to be done, and each allocated resource
109
- contributes its own time towards it, as TaskJuggler defines it. The same task
248
+ contributes its own time towards it, as the .tjp format defines it. The same task
110
249
  with two resources finishes in half the time and costs what one resource's worth
111
250
  of work costs.
112
251
 
@@ -4,3 +4,4 @@ include CHANGELOG.md
4
4
  include pyproject.toml
5
5
  recursive-include scriptplan *.lark
6
6
  recursive-include scriptplan py.typed
7
+ recursive-include examples *.tjp *.csv *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptplan
3
- Version: 0.10.1
3
+ Version: 0.10.2
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>
@@ -49,12 +49,12 @@ Dynamic: license-file
49
49
 
50
50
  # ScriptPlan
51
51
 
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)
52
+ ![Tests](https://img.shields.io/badge/tests-496_passing-success?style=for-the-badge)
53
+ ![Probes](https://img.shields.io/badge/audit_probes-85_passing-success?style=for-the-badge)
54
54
  ![Python](https://img.shields.io/badge/python-3.9%2B-blue?style=for-the-badge)
55
55
  ![Licence](https://img.shields.io/badge/licence-Apache--2.0-lightgrey?style=for-the-badge)
56
56
 
57
- A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management. The syntax is compatible with TaskJuggler (.tjp files).
57
+ A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management. It reads projects written in the .tjp format.
58
58
 
59
59
  ## Installation
60
60
 
@@ -131,6 +131,10 @@ for task in project.tasks:
131
131
  print(f"{task.id}: {start} -> {end}")
132
132
  ```
133
133
 
134
+ Projects can be parsed and scheduled in parallel threads. Messages and the error
135
+ count belong to the thread that produced them, so read
136
+ `get_message_handler_instance().messages` in the thread that called `schedule()`.
137
+
134
138
  ## What is verified
135
139
 
136
140
  Each behaviour below has a probe in `audit/evaluations/` that drives ScriptPlan
@@ -168,12 +172,22 @@ audit/evaluations/run_all.sh
168
172
  **Scheduling**
169
173
 
170
174
  - ASAP and ALAP, including chains that mix the two.
175
+ - A task scheduled as late as possible ends by the earliest of its own `end`, the
176
+ start of each successor (less any gap that successor asks for) and the project
177
+ end. With none of these but a successor scheduled as soon as possible that
178
+ states no start, it is scheduled as soon as possible instead, with a warning.
171
179
  - Contention is resolved by priority, then criticalness, then declaration order.
172
180
  - The declared project period is never rewritten. A task that does not fit is
173
- reported, with its name and the reason.
181
+ reported, with its name and the reason. A date stated outside the period is an
182
+ error.
174
183
  - 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.
184
+ - A stated `start` or `end` that the schedule cannot keep, and a task that would
185
+ start before something it depends on finishes, are errors.
186
+ - `length` and `gaplength` count the project's working time, not a resource's:
187
+ a resource on longer hours does not shorten the gap.
188
+ - `maxgapduration`, `maxstart`, `minstart`, `maxend` and `minend` are checked
189
+ once the schedule is complete, and a violation is an error. (`maxgaplength` is
190
+ not part of the syntax and is refused when parsed.)
177
191
 
178
192
  **Honesty**
179
193
 
@@ -191,9 +205,15 @@ audit/evaluations/run_all.sh
191
205
  rejected at parse time rather than silently producing nothing.
192
206
  - Accounts are parsed but not yet modelled, so `chargeset` and `charge` record an
193
207
  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.
208
+ - A task reference is first looked up from the referring task's scope, one level
209
+ up for each `!`. When that finds nothing but exactly one task in the project has
210
+ that id, the reference binds to it, so `depends !!pack` inside `delivery` finds
211
+ `delivery.pack`. When two tasks could be meant, the file is refused and both are
212
+ named.
213
+ - `onstart` on a dependency means start-to-start for a task scheduled as soon as
214
+ possible: it may start once the predecessor starts. For a task scheduled as
215
+ late as possible it means the task must finish before the predecessor starts,
216
+ which is ScriptPlan's own reading.
197
217
 
198
218
  ## Example Project
199
219
 
@@ -266,17 +286,12 @@ Apache-2.0
266
286
 
267
287
  ## Acknowledgments
268
288
 
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/).
289
+ ScriptPlan reads the .tjp project file format. It contains no code from any other
290
+ scheduler: the parser, the scheduling algorithms and the reports are its own.
276
291
 
277
292
  ## Why ScriptPlan?
278
293
 
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.
294
+ 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. Because it reads the established .tjp syntax, users can adopt ScriptPlan without learning a new format.
280
295
 
281
296
  Yours,
282
297
  Farshid.
@@ -4,12 +4,12 @@
4
4
 
5
5
  # ScriptPlan
6
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)
7
+ ![Tests](https://img.shields.io/badge/tests-496_passing-success?style=for-the-badge)
8
+ ![Probes](https://img.shields.io/badge/audit_probes-85_passing-success?style=for-the-badge)
9
9
  ![Python](https://img.shields.io/badge/python-3.9%2B-blue?style=for-the-badge)
10
10
  ![Licence](https://img.shields.io/badge/licence-Apache--2.0-lightgrey?style=for-the-badge)
11
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).
12
+ A precise project scheduling engine with minute-level accuracy for resource allocation and dependency management. It reads projects written in the .tjp format.
13
13
 
14
14
  ## Installation
15
15
 
@@ -86,6 +86,10 @@ for task in project.tasks:
86
86
  print(f"{task.id}: {start} -> {end}")
87
87
  ```
88
88
 
89
+ Projects can be parsed and scheduled in parallel threads. Messages and the error
90
+ count belong to the thread that produced them, so read
91
+ `get_message_handler_instance().messages` in the thread that called `schedule()`.
92
+
89
93
  ## What is verified
90
94
 
91
95
  Each behaviour below has a probe in `audit/evaluations/` that drives ScriptPlan
@@ -123,12 +127,22 @@ audit/evaluations/run_all.sh
123
127
  **Scheduling**
124
128
 
125
129
  - ASAP and ALAP, including chains that mix the two.
130
+ - A task scheduled as late as possible ends by the earliest of its own `end`, the
131
+ start of each successor (less any gap that successor asks for) and the project
132
+ end. With none of these but a successor scheduled as soon as possible that
133
+ states no start, it is scheduled as soon as possible instead, with a warning.
126
134
  - Contention is resolved by priority, then criticalness, then declaration order.
127
135
  - The declared project period is never rewritten. A task that does not fit is
128
- reported, with its name and the reason.
136
+ reported, with its name and the reason. A date stated outside the period is an
137
+ error.
129
138
  - 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.
139
+ - A stated `start` or `end` that the schedule cannot keep, and a task that would
140
+ start before something it depends on finishes, are errors.
141
+ - `length` and `gaplength` count the project's working time, not a resource's:
142
+ a resource on longer hours does not shorten the gap.
143
+ - `maxgapduration`, `maxstart`, `minstart`, `maxend` and `minend` are checked
144
+ once the schedule is complete, and a violation is an error. (`maxgaplength` is
145
+ not part of the syntax and is refused when parsed.)
132
146
 
133
147
  **Honesty**
134
148
 
@@ -146,9 +160,15 @@ audit/evaluations/run_all.sh
146
160
  rejected at parse time rather than silently producing nothing.
147
161
  - Accounts are parsed but not yet modelled, so `chargeset` and `charge` record an
148
162
  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.
163
+ - A task reference is first looked up from the referring task's scope, one level
164
+ up for each `!`. When that finds nothing but exactly one task in the project has
165
+ that id, the reference binds to it, so `depends !!pack` inside `delivery` finds
166
+ `delivery.pack`. When two tasks could be meant, the file is refused and both are
167
+ named.
168
+ - `onstart` on a dependency means start-to-start for a task scheduled as soon as
169
+ possible: it may start once the predecessor starts. For a task scheduled as
170
+ late as possible it means the task must finish before the predecessor starts,
171
+ which is ScriptPlan's own reading.
152
172
 
153
173
  ## Example Project
154
174
 
@@ -221,17 +241,12 @@ Apache-2.0
221
241
 
222
242
  ## Acknowledgments
223
243
 
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/).
244
+ ScriptPlan reads the .tjp project file format. It contains no code from any other
245
+ scheduler: the parser, the scheduling algorithms and the reports are its own.
231
246
 
232
247
  ## Why ScriptPlan?
233
248
 
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.
249
+ 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. Because it reads the established .tjp syntax, users can adopt ScriptPlan without learning a new format.
235
250
 
236
251
  Yours,
237
252
  Farshid.
@@ -0,0 +1,42 @@
1
+ # Examples
2
+
3
+ Each project here comes with the answer it should produce.
4
+
5
+ - `showcase/` holds the 24 demonstration projects that `SHOWCASE.md` is built from:
6
+ airport upgrades, a 500-task chain on an irregular shift, time zones, quotas,
7
+ limits, mixed forward and backward plans.
8
+ - `edge_cases/` holds one small project for each scheduling pattern that has gone
9
+ wrong in a release, such as several tasks sharing one hour of one person, a
10
+ backward task before a successor that starts mid-hour, or a declared start inside
11
+ a slot.
12
+ - `simple.tjp` is a minimal starting point.
13
+
14
+ ## The answers
15
+
16
+ Next to every project is `<name>.expected.csv`, with the start and end of every task
17
+ in the project's own time zone, to the second, and `<name>.expected.md`, which shows
18
+ how each date was worked out.
19
+
20
+ The answers were derived by hand from the project file and the documented semantics,
21
+ without running ScriptPlan. Where an hour-level reference scheduler accepts the file,
22
+ its result was compared too. Where the two disagree, the reason is recorded: it books
23
+ whole slots, so it can end a task later than ScriptPlan, which places work to the
24
+ minute.
25
+
26
+ In three projects the hand derivation and the engine first disagreed, and the notes
27
+ record how each was settled. In `thermal` and `quota` the project's own header and its
28
+ long-standing test specify the engine's answer. In `jit_supply` the engine moved in
29
+ 0.10.2 and two independent sources confirm the new date.
30
+
31
+ ## Checking them
32
+
33
+ ```bash
34
+ pytest tests/test_examples.py # this checkout
35
+ SCRIPTPLAN_EXAMPLES_PYTHON=/path/to/venv/bin/python \
36
+ pytest tests/test_examples.py # an installed release
37
+ ```
38
+
39
+ Each project runs through the `plan` CLI in a fresh process, and every task is
40
+ compared with its answer. These answers are not the golden snapshots in
41
+ `tests/golden/`. A snapshot records what the engine produced last time and catches a
42
+ change; these files record what it should produce and catch an error.
@@ -0,0 +1,5 @@
1
+ id;start;end
2
+ d;2026-10-19-12:30:00;2026-10-19-14:30:00
3
+ l;2026-10-19-12:30:00;2026-10-19-14:30:00
4
+ e;2026-10-19-14:00:00;2026-10-19-14:30:00
5
+ x;2026-10-19-14:30:00;2026-10-19-15:30:00
@@ -0,0 +1,6 @@
1
+ # alap_before_declared_start
2
+
3
+ x is declared to start at 14:30 and depends on d, l and e, which are alap with no end of their own.
4
+ x's stated start is a fixed moment, so each ends as late as it can before it: d and l 12:30-14:30, and e on
5
+ the desk 14:00-14:30. x then starts at its declared 14:30 and runs to 15:30. 0.10.1 degraded d, l and e
6
+ to asap and put them at the project start (issuedb #14).
@@ -0,0 +1,8 @@
1
+ project p "alap_before_declared_start" 2026-10-19 +2w {
2
+ timingresolution 60min
3
+ }
4
+ resource r0 "Desk" { workinghours mon - fri 9:00 - 17:00 }
5
+ task d "D" { scheduling alap duration 2h }
6
+ task l "L" { scheduling alap length 2h }
7
+ task e "E" { scheduling alap effort 30min allocate r0 }
8
+ task x "X" { depends d, l, e start 2026-10-19-14:30 effort 60min allocate r0 }
@@ -0,0 +1,5 @@
1
+ id;start;end
2
+ x;2026-10-19-14:00:00;2026-10-19-15:00:00
3
+ y;2026-10-19-15:00:00;2026-10-19-16:00:00
4
+ m;2026-10-19-14:00:00;2026-10-19-14:00:00
5
+ n;2026-10-19-14:30:00;2026-10-19-14:30:00
@@ -0,0 +1,6 @@
1
+ # alap_bounded_by_declared_starts
2
+
3
+ x and y are declared to start at 14:00 and 14:30; x holds the desk until 15:00, so y runs 15:00-16:00.
4
+ The alap milestones m and n precede them and are bounded by the starts the file states: m at 14:00, n at
5
+ 14:30. Scheduling twice gives the same answer because the bound is the stated start, not a start a
6
+ previous run wrote back.
@@ -0,0 +1,8 @@
1
+ project p "alap_bounded_by_declared_starts" 2026-10-19 +2w {
2
+ timingresolution 60min
3
+ }
4
+ resource r0 "Desk" { workinghours mon - fri 9:00 - 17:00 }
5
+ task x "X" { start 2026-10-19-14:00 effort 60min allocate r0 }
6
+ task y "Y" { start 2026-10-19-14:30 effort 60min allocate r0 }
7
+ task m "M" { scheduling alap precedes x }
8
+ task n "N" { scheduling alap precedes y }
@@ -0,0 +1,5 @@
1
+ id;start;end
2
+ g;2026-11-04-13:00:00;2026-11-06-17:30:00
3
+ g.a;2026-11-04-13:00:00;2026-11-04-17:00:00
4
+ g.b;2026-11-05-17:00:00;2026-11-06-17:30:00
5
+ g.m;2026-11-06-17:30:00;2026-11-06-17:30:00
@@ -0,0 +1,6 @@
1
+ # alap_gapduration_before_successor
2
+
3
+ Everything is alap and g must end Fri 17:30, so m sits there. b's 480 minutes end at m: Friday allows
4
+ 450 of them (10:00-17:30) under the daily cap and Thursday the last 30 (17:00-17:30). a must end 24 hours
5
+ before b starts, by Wed 17:00, and takes Wed 13:00-17:00. 0.10.1 ignored the gap and ended a where b
6
+ began (trace's defect 1).
@@ -0,0 +1,15 @@
1
+ project d "alap_gapduration_before_successor" 2026-11-02 +4w {
2
+ timingresolution 30min
3
+ scheduling alap
4
+ }
5
+ resource r1 "Analyst" {
6
+ workinghours mon - fri 8:30 - 17:30
7
+ timezone "Europe/London"
8
+ limits { dailymax 450min }
9
+ }
10
+ task g "Group" {
11
+ end 2026-11-06-17:30
12
+ task a "A" { effort 240min allocate r1 }
13
+ task b "B" { effort 480min allocate r1 depends !a { gapduration 1440min } }
14
+ task m "Milestone" { milestone depends !b }
15
+ }
@@ -0,0 +1,7 @@
1
+ id;start;end
2
+ b;2026-10-20-14:00:00;2026-10-20-15:00:00
3
+ a;2026-10-20-13:00:00;2026-10-20-14:00:00
4
+ c;2026-10-20-12:00:00;2026-10-20-13:00:00
5
+ d;2026-10-20-15:00:00;2026-10-20-16:00:00
6
+ e;2026-10-19-15:00:00;2026-10-19-16:00:00
7
+ f;2026-10-20-11:00:00;2026-10-20-12:00:00
@@ -0,0 +1,8 @@
1
+ # alap_keeps_successor_gaps
2
+
3
+ Backward tasks end as late as their successors allow, less the gap each successor asks for.
4
+ b holds the desk Tue 14:00-15:00, so a (alap, precedes b) runs 13:00-14:00. d starts Tue 15:00 and wants
5
+ c finished 2 hours (calendar) before it: c ends 13:00 and runs 12:00-13:00. f starts Tue 11:00 and wants
6
+ 3 working hours after e: Tue 09:00-11:00 is 2 of them and Mon 16:00-17:00 the third, so e ends Mon 16:00
7
+ and runs 15:00-16:00. Before 0.10.2 these alap tasks were moved to the project start, and once they were
8
+ not, the gap was ignored (issuedb #19).
@@ -0,0 +1,10 @@
1
+ project p "alap_keeps_successor_gaps" 2026-10-19 +2w {
2
+ timingresolution 60min
3
+ }
4
+ resource r0 "Desk" { workinghours mon - fri 9:00 - 17:00 }
5
+ task b "B" { start 2026-10-20-14:00 effort 1h allocate r0 }
6
+ task a "A" { scheduling alap effort 1h allocate r0 precedes b }
7
+ task c "C" { scheduling alap duration 1h }
8
+ task d "D" { start 2026-10-20-15:00 depends c { gapduration 2h } effort 1h allocate r0 }
9
+ task e "E" { scheduling alap duration 1h }
10
+ task f "F" { start 2026-10-20-11:00 depends e { gaplength 3h } duration 1h }
@@ -0,0 +1,6 @@
1
+ id;start;end
2
+ t;2026-10-19-16:00:00;2026-10-19-17:00:00
3
+ m;2026-10-19-16:00:00;2026-10-19-16:00:00
4
+ d;2026-10-19-14:00:00;2026-10-19-16:00:00
5
+ t2;2026-10-19-15:30:00;2026-10-19-16:00:00
6
+ m2;2026-10-19-15:30:00;2026-10-19-15:30:00
@@ -0,0 +1,5 @@
1
+ # alap_milestones_exact
2
+
3
+ t holds the desk 16:00-17:00, so t2 (end 16:30) must run before it: 15:30-16:00. An alap milestone
4
+ sits at the latest moment its successor allows: m at t's start 16:00, m2 at t2's start 15:30. d ends at m,
5
+ 16:00, and lasts 2 hours: 14:00. 0.10.1 put m at 15:00 and m2 at 15:00 (issuedb #14).
@@ -0,0 +1,9 @@
1
+ project p "alap_milestones_exact" 2026-10-19 +2w {
2
+ timingresolution 60min
3
+ }
4
+ resource r0 "Desk" { workinghours mon - fri 9:00 - 17:00 }
5
+ task t "T" { scheduling alap effort 60min allocate r0 end 2026-10-19-17:00 }
6
+ task m "M" { scheduling alap precedes t }
7
+ task d "D" { scheduling alap duration 2h precedes m }
8
+ task t2 "T2" { scheduling alap effort 30min allocate r0 end 2026-10-19-16:30 }
9
+ task m2 "M2" { scheduling alap precedes t2 }
@@ -0,0 +1,5 @@
1
+ id;start;end
2
+ g;2026-11-05-13:00:00;2026-11-06-17:30:00
3
+ g.a;2026-11-06-13:30:00;2026-11-06-17:30:00
4
+ g.b;2026-11-05-13:00:00;2026-11-06-13:30:00
5
+ g.m;2026-11-06-17:30:00;2026-11-06-17:30:00
@@ -0,0 +1,5 @@
1
+ # alap_onstart_reading
2
+
3
+ For a task scheduled as late as possible, `onstart` reads as: it must finish before the predecessor
4
+ starts (README). a is bounded only by g's end: Fri 13:30-17:30. b must end by a's start, Fri 13:30:
5
+ Friday has 210 minutes left under the cap (10:00-13:30) and Thursday gives the other 270 (13:00-17:30).
@@ -0,0 +1,15 @@
1
+ project d "alap_onstart_reading" 2026-11-02 +4w {
2
+ timingresolution 30min
3
+ scheduling alap
4
+ }
5
+ resource r1 "Analyst" {
6
+ workinghours mon - fri 8:30 - 17:30
7
+ timezone "Europe/London"
8
+ limits { dailymax 450min }
9
+ }
10
+ task g "Group" {
11
+ end 2026-11-06-17:30
12
+ task a "A" { effort 240min allocate r1 }
13
+ task b "B" { effort 480min allocate r1 depends !a { onstart } }
14
+ task m "Milestone" { milestone depends !b }
15
+ }
@@ -0,0 +1,5 @@
1
+ id;start;end
2
+ g;2026-11-05-13:00:00;2026-11-06-17:30:00
3
+ g.a;2026-11-05-13:00:00;2026-11-05-17:00:00
4
+ g.b;2026-11-05-17:00:00;2026-11-06-17:30:00
5
+ g.m;2026-11-06-17:30:00;2026-11-06-17:30:00
@@ -0,0 +1,5 @@
1
+ # alap_own_end_before_successor
2
+
3
+ b runs Thu 17:00-Fri 17:30 as above. a has its own end, Fri 12:00, and must also end before b starts,
4
+ Thu 17:00; the earlier wins. Thursday has 420 minutes left under the cap once b's 30 are booked, so a runs
5
+ 13:00-17:00. 0.10.1 ended a at its own end, after b had started (trace's defect 9).
@@ -0,0 +1,15 @@
1
+ project d "alap_own_end_before_successor" 2026-11-02 +4w {
2
+ timingresolution 30min
3
+ scheduling alap
4
+ }
5
+ resource r1 "Analyst" {
6
+ workinghours mon - fri 8:30 - 17:30
7
+ timezone "Europe/London"
8
+ limits { dailymax 450min }
9
+ }
10
+ task g "Group" {
11
+ end 2026-11-06-17:30
12
+ task a "A" { effort 240min allocate r1 end 2026-11-06-12:00 }
13
+ task b "B" { effort 480min allocate r1 depends !a }
14
+ task m "Milestone" { milestone depends !b }
15
+ }
@@ -0,0 +1,4 @@
1
+ id;start;end
2
+ m;2026-11-02-00:00:00;2026-11-02-00:00:00
3
+ mm;2026-10-21-11:30:00;2026-10-21-11:30:00
4
+ d;2026-10-21-09:30:00;2026-10-21-11:30:00
@@ -0,0 +1,5 @@
1
+ # alap_project_end_and_declared_end
2
+
3
+ The project runs 2026-10-19 +2w, so it ends 2026-11-02 00:00. An alap milestone with no successor and
4
+ no end of its own sits at the project end. One declared to end at 11:30 sits at 11:30. An alap duration
5
+ of 2 hours declared to end at 11:30 runs 09:30-11:30 (0.10.0: 09:00-11:30; 0.10.1: 10:00-11:30).
@@ -0,0 +1,7 @@
1
+ project p "alap_project_end_and_declared_end" 2026-10-19 +2w {
2
+ timingresolution 60min
3
+ }
4
+ resource r0 "Desk" { workinghours mon - fri 9:00 - 17:00 }
5
+ task m "M" { scheduling alap }
6
+ task mm "MM" { scheduling alap end 2026-10-21-11:30 }
7
+ task d "D" { scheduling alap duration 2h end 2026-10-21-11:30 }
@@ -0,0 +1,5 @@
1
+ id;start;end
2
+ t4;2026-10-19-16:20:00;2026-10-19-17:00:00
3
+ d4;2026-10-19-15:20:00;2026-10-19-16:20:00
4
+ l4;2026-10-19-15:20:00;2026-10-19-16:20:00
5
+ m4;2026-10-19-16:20:00;2026-10-19-16:20:00
@@ -0,0 +1,5 @@
1
+ # alap_spans_before_mid_slot_successor
2
+
3
+ t4 (alap, 40 min, end 17:00) runs 16:20-17:00. Everything that precedes it must end by 16:20:
4
+ the 1-hour duration and length tasks run 15:20-16:20, and the milestone sits at 16:20.
5
+ 0.10.1 placed d4 and l4 at 16:00-17:00, ending after t4 began: a regression (issuedb #13).