Domain-Specific Language

Provides the names for the domain-specific language of ballparks.

The key feature of Ballparker is an embedded domain-specific-language (DSL) for setting up trees of tasks and their associated estimates for ballparks, and a task model for their manipulation. Your ballpark’s “source file” is just a Python file, typically defining a single estimate and doing some manipulation and output when called as __main__.

An “estimate” is just a recursive structure of ballparker.types.Task objects. Some tasks are “groupings” - they have no intrinsic size, and they may have subtasks. These are created with grouping() calls, or with a project() call, used as the “top-level” grouping. Both of those calls take any number of additional arguments that are or can be converted into tasks: other ballparker.types.Task instances (like a nested grouping), strings (which become non-grouping tasks with UNKNOWN size), or (string, tshirt-size-constant or float size) tuples which become non-grouping tasks with the indicated size. (See make_task() for details.)

Use via the following is recommended for easier-to-read ballparks:

from ballparker.dsl import *

or, especially if you have an over-eager linter, you can be more explicit:

from ballparker.dsl import project, grouping, XS, S, M, L, XL, DONE, process_estimate

See also

Introduction to Ballparker

Details on the ballpark process using this DSL, including a template for starting a ballpark script

ballparker/tests/data.py

Test data, which includes several anonymized real-world ballparks.

Groupings

ballparker.dsl.project(*args)[source]

Make a top-level “project” grouping - the root task of an estimate.

Parameters

*args – See below.

Each argument is converted/coerced into a Task using make_task() and included in the list of subtasks.

A project should start with a line like ESTIMATE = project(, followed by as many tasks (including groupings) as desired, with all remaining closing parentheses ) on the last task line, as needed to match the opening parentheses.

ballparker.dsl.grouping(description, *args)[source]

Make a “grouping” task with description and sub-tasks, but no inherent size.

Parameters
  • description (str) – An string description of this grouping

  • *args – See below.

Each additional argument is converted/coerced into a Task using make_task() and included in the list of subtasks.

Task groupings should start with a grouping("group desc", line, followed by as many tasks (including groupings) as desired, with all remaining closing parentheses ) on the last task line, as needed to match the opening parentheses.

Creating leaf tasks

ballparker.dsl.make_task(a)[source]

Coerce an argument into a Task.

This is mainly for use by grouping() and project(), not for direct use in your ballparks. However, since those two functions both process their arguments using this, it is documented separately. You can consider each argument to those functions (aside from the description argument to grouping()) to be implicitly wrapped in a make_task() call.

  • If the argument is a string or single-element tuple, it becomes a leaf task with the string as a description and with unknown (UNKNOWN) size.

  • If the argument is a two-element tuple, it’s treated as a (description, size) pair and converted to a Task with a string description and specified size (either a TShirtSizes or a number).

  • If the argument already is a Task (e.g., using grouping()), no conversion is required, and the argument is returned unchanged.

So, lines with “leaf” tasks can simply be a string or a 2-tuple of string and size (number or t-shirt size).

T-shirt size constants

The “t-shirt size” constants referred to above are the elements of ballparker.types.TShirtSizes, which are imported into the ballparker.dsl namespace as follows:

ballparker.dsl.XS = <TShirtSizes.XS: 0.5>

Smallest task possible - about half a day.

ballparker.dsl.S = <TShirtSizes.S: 1>

Common task size with some interaction (may have to talk to someone, etc.) - about a day.

ballparker.dsl.M = <TShirtSizes.M: 3>

Common task size, taking about half a week.

ballparker.dsl.L = <TShirtSizes.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.

ballparker.dsl.XL = <TShirtSizes.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.

ballparker.dsl.DONE = <TShirtSizes.DONE: 0>

Use this as the size when you’re tracking an ongoing project with ballparker, and a task is completed.

ballparker.dsl.UNKNOWN = <TShirtSizes.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).

Output

In addition to as_markdown() and other properties and methods of Task, a general-purpose function is provided in the DSL module that automates some common uses.

ballparker.dsl.process_estimate(estimate, fn='output.md', *args, **kwargs)[source]

Output an estimate in Markdown format to screen and a file.

Parameters
  • estimate (Task) – An estimate (task tree).

  • fn (str) – Filename to write Markdown-formatted estimate to.

  • *args – Any dditional positional arguments are passed to as_markdown().

  • **kwargs – Any additional keyword arguments are passed to as_markdown().