Ballparker Types¶
Define the main types and constants of this package.
To create instances of Task, it’s recommended to use the DSL in ballparker.dsl.
The Task Type¶
-
class
ballparker.types.Task(description, size, subtasks=NOTHING)[source]¶ A task with a description and a size or sub-tasks.
-
size: Union[ballparker.types.TShirtSizes, int, float]¶ The inherent size of this task.
Only specified with a valid value for leaf (non-grouping) tasks.
-
subtasks: List[ballparker.types.Task]¶ A list of sub-tasks for this task.
Only tasks with no inherent size can have a non-empty list of subtasks.
-
as_markdown(format_task=None, size_first=False, tshirt_sizes=False, skip_levels=None, size_levels=<_AllLevelsSentinel.ALL_LEVELS: 0>, max_levels=<_AllLevelsSentinel.ALL_LEVELS: 0>)[source]¶ Get this task (and any sub-tasks) as a Markdown document.
- Parameters
format_task (
Optional[Callable[[Task],str]]) – An optional function taking a task and returning a string.size_first (
bool) – Whether size should come before the description instead of after. Only applied if format_task isNone.tshirt_sizes (
bool) – Whether sizes should be shown in “T-shirt sizes” (seeTShirtSizes), where available, instead of their story-point equivalent.skip_levels (
Optional[int]) – How many levels of the tree to skip. Most common values are 0 (no levels) and 1 (skip the top level, often a “project” grouping).size_levels (
Union[int,_AllLevelsSentinel]) – How many levels of the tree should have their sizes annotated. The sentinel valuesALL_LEVELSmeans “all levels” (default). Other values may be useful when processing output for supplying to customers, for example, to remove the very fine-grained estimates at tree leaves.max_levels (
Union[int,_AllLevelsSentinel]) – The maximum levels of tasks, starting at the root project, that should be traversed and output. Note that if skip_levels is not 0, the number of levels actually output is less (max_levels - skip_levels). The sentinel valuesALL_LEVELSmeans “all levels” (default). Other values may be useful when processing output for supplying to customers, for example, to remove the very fine-grained tasks at tree leaves.
- Return type
- Returns
Multi-line text suitable for processing as Markdown.
-
to_dsl()[source]¶ Get the equivalent ballpark DSL of this task and any subtasks.
>>> Task.make_leaf("desc").to_dsl() "('desc')" >>> Task.make_leaf("desc", TShirtSizes.S).to_dsl() "('desc', S)" >>> Task.make_leaf("desc", 1).to_dsl() "('desc', 1)" >>> Task.make_grouping("desc", subtasks=[]).to_dsl() "grouping('desc')" >>> Task.make_grouping("desc", subtasks=[Task.make_leaf("subtask")]).to_dsl() "grouping('desc',\n ('subtask'))" >>> Task.make_project(subtasks=[]).to_dsl() 'project()' >>> Task.make_project(subtasks=[Task.make_leaf("subtask")]).to_dsl() "project(\n ('subtask'))"
- Return type
-
apply_visitor(visitor, parent=None)[source]¶ Call the visitor with this task and all sub-tasks recursively.
The visitor will get a single positional argument (the task) and a keyword argument parent (which will be None by default at the initial task).
This is a pre-order, depth-first traversal, if you are curious.
See also
ballparker.visitorsBundled standalone visitors.
- Return type
None
-
property
size_string¶ Get the size of this task or its recursive sub-tasks.
Same as
story_points_stringfor groupings. For leaf tasks, this returns the t-shirt size estimate instead of the numerical story points, where available.>>> Task.make_leaf("desc").size_string 'UNKNOWN' >>> Task.make_leaf("desc", TShirtSizes.S).size_string 'S' >>> Task.make_leaf("desc", 1).size_string '1' >>> Task.make_grouping("desc", subtasks=[]).size_string '?' >>> Task.make_grouping("desc", subtasks=[Task.make_leaf("subtask")]).size_string '?' >>> Task.make_project(subtasks=[]).size_string '?' >>> Task.make_project(subtasks=[Task.make_leaf("subtask", TShirtSizes.S)]).size_string '1'
- Return type
-
property
story_points_string¶ Get a string representation of the numerical story points for this task (or its sub-tasks, recursively).
String representation allows returning
"?"if no size was provided, or">"and some value if one or more sub-tasks has unknown size.>>> Task.make_leaf("desc").story_points_string '?' >>> Task.make_leaf("desc", TShirtSizes.S).story_points_string '1' >>> Task.make_leaf("desc", 1).story_points_string '1.0' >>> Task.make_grouping("desc", subtasks=[]).story_points_string '?' >>> Task.make_grouping("desc", subtasks=[Task.make_leaf("subtask")]).story_points_string '?' >>> Task.make_grouping("desc", subtasks=[ ... Task.make_leaf("subtask"), ... Task.make_leaf("subtask 2", TShirtSizes.S)]).story_points_string '> 1' >>> Task.make_project(subtasks=[]).story_points_string '?' >>> Task.make_project(subtasks=[Task.make_leaf("subtask", TShirtSizes.S)]).story_points_string '1'
>>> from ballparker.dsl import * >>> make_task("desc").story_points_string '?' >>> make_task(("desc", S)).story_points_string '1' >>> make_task(("desc", 1)).story_points_string '1.0' >>> grouping("desc").story_points_string '?' >>> grouping("desc", ... ("subtask")).story_points_string '?' >>> grouping("desc", ... ("subtask"), ... ("subtask 2", S)).story_points_string '> 1' >>> project().story_points_string '?' >>> project( ... ("subtask", S)).story_points_string '1'
- Return type
-
property
is_grouping¶ Check if this task is a grouping.
A grouping has no inherent size, and may have subtasks.
>>> Task.make_leaf("desc").is_grouping False >>> Task.make_leaf("desc", TShirtSizes.S).is_grouping False >>> Task.make_grouping("desc", subtasks=[]).is_grouping True >>> Task.make_grouping("desc", subtasks=[Task.make_leaf("subtask")]).is_grouping True >>> Task.make_project(subtasks=[]).is_grouping True >>> Task.make_project(subtasks=[Task.make_leaf("subtask")]).is_grouping True
- Return type
-
property
is_project¶ Return True if this is the
project()-created grouping.>>> Task.make_leaf("desc").is_project False >>> Task.make_leaf("desc", TShirtSizes.S).is_project False >>> Task.make_grouping("desc", subtasks=[]).is_project False >>> Task.make_grouping("desc", subtasks=[Task.make_leaf("subtask")]).is_project False >>> Task.make_project(subtasks=[]).is_project True >>> Task.make_project(subtasks=[Task.make_leaf("subtask")]).is_project True
- Return type
-
property
has_unknowns¶ Check if this task or any sub-tasks are missing a size estimate.
- Return type
-
property
known_story_points¶ Get the number of known story points for this task or its sub-tasks.
Note that this is usually not as useful for display as
story_points_string, since that incorporates the uncertainty of tasks with unknown size.
-
classmethod
make_leaf(description, size=<TShirtSizes.UNKNOWN: None>)[source]¶ Create a leaf (non-grouping) task.
Typically invoked by passing additional arguments to the
grouping()orproject()DSL functions.- Return type
-
Constants¶
-
class
ballparker.types.TShirtSizes(value)[source]¶ So-called “T-shirt size” estimation sizes.
This is a binning/quantization method of estimation: it is intentionally somewhat coarse-grained, to provide fewer choices and thus easier estimation. It is based on US T-shirt sizes: rather than a number, there is just extra-small, small, medium, large, extra-large, etc. Those five basic ones are the ones supported by Ballparker. Rather than directly estimating points (days of work), you estimate the kind of work or size category instead.
Each size is associated with a number of story points as a starting place, though the various ways to make a
Taskdo accept numbers as sizes in addition toTShirtSizesif you want to fine-tune.These are all imported directly by name into
ballparker.dslfor ease of use.-
DONE= 0¶ Use this as the size when you’re tracking an ongoing project with ballparker, and a task is completed.
-
XS= 0.5¶ Smallest task possible - about half a day.
-
S= 1¶ Common task size with some interaction (may have to talk to someone, etc.) - about a day.
-
M= 3¶ Common task size, taking about half a week.
-
L= 5¶ Task size of “half a sprint” (about 1 week).
You will want to split this into finer-grained tasks before finalizing the ballpark.
-
XL= 10¶ Largest task size, an entire sprint (about two weeks).
You will want to split this into finer-grained tasks before finalizing the ballpark.
-
UNKNOWN= None¶ This is the default task size if unspecified.
Adds “uncertainty” to the ballpark (doesn’t add any points to parent tasks, but does turn them into an inequality).
-
GROUPING= -1¶ This is a sentinel value used in groupings, not for manual use.
It indicates this task has no inherent size of its own, but should instead sum up all sub-task sizes.
-
-
ballparker.types.ALL_LEVELS= <_AllLevelsSentinel.ALL_LEVELS: 0>¶ Singleton sentinel value to pass to
Task.as_markdown()as a level number.If this, instead of a number, is passed as size_levels (the default), the size_levels value will be considered to be larger than all levels in the tree, and thus sizes will be output for all task levels.
If this, instead of a number, is passed as max_levels, the max_levels value will be considered to be larger than all levels in the tree, and thus all task levels will be traversed for output.