Metadata-Version: 2.4
Name: tuxfan-planner
Version: 0.2.5
Summary: Simple text-driven project planning system
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: PyYAML>=6.0
Requires-Dist: python-pptx>=1.0
Provides-Extra: docs
Requires-Dist: Sphinx>=7.2; extra == "docs"
Requires-Dist: furo; extra == "docs"

# Project Planner

A small planning system that lets you define project plans and tasks in either YAML or Python.

Repository: `git@re-git.lanl.gov:tpp/planner.git`

A plan can include optional top-level project metadata:

- `portfolio`: portfolio or parent program name.
- `project`: high-level project name.
- `managers`: list of manager names.
- `pocs`: list of point-of-contact names.
- `summary`: high-level plan summary.
- `fiscal_range_begin` and `fiscal_range_end`: optional fiscal year range covered by the plan, for example `27` through `31`.
- `execution`: execution metadata. It can be a mapping with `overview` text and `sections`, where each section has `label`, `description`, and an optional stable `id` for task links. The older flat list of sections is still accepted.

One optional `defaults` entry inside the `tasks` list sets `site`, `bnr`, and
`type` for every task that omits them:

```yaml
tasks:
  - defaults:
      site: LANL
      bnr: DP1518130
      type: labor

  - id: MAINTENANCE
    label: Tool maintenance
    project: Planning System
    description: Maintain the planning tool.
    status: ongoing
    funding: {fy27: 50K}
```

The three values must be indented beneath `defaults`. Only one defaults entry
is allowed, and it applies to all tasks regardless of its position. Explicit
task values override defaults; explicit null or empty values clear a default.
Multipart parents inherit defaults, and part values override parent values.
Without defaults, `site`, `bnr`, and `type` remain optional. This also works in
raw YAML lists and Python `PLAN`/`TASKS` lists. See
[`planner/examples/task-defaults.yaml`](planner/examples/task-defaults.yaml).

Omitted dependencies and tags are empty collections internally. Outputs that
need a value display `None`, including dependency fields in `list-detail` and
`schedule`, SVG dependency text, and an explicitly selected Tags table column.
The concise `list` output continues to omit empty Dependencies lines.

Each task supports these fields:

- `id`: required string task id. Must contain only letters, numbers, or underscores, with no spaces.
- `label`: required string display name for the task.
- `site`: optional site or institution label.
- `bnr`: optional budget and reporting identifier.
- `cost`: optional task cost label, for example `$100K`.
- `type`: optional task type label.
- `tags`: optional comma-separated string or list of task tags. Omitted or null values mean no tags.
- `project`: required string project name.
- `execution_section`: optional ID of a section under `execution.sections`. Linked tasks use that section's label and position for activity headings and task numbers in exports. An unknown ID is rejected.
- `description`: required string describing the task.
- `notes`: optional string with additional task notes.
- `start`: required fiscal period string in `M{month}Q{quarter}FY{year}` format, where `month` is `1` to `3` within the quarter.
- `expected`: required positive whole number of months for the task duration. The end month is computed from `start`, counting the start month as month one.
- `completed`: optional fiscal period string for work that is already complete. A task with `status: complete` and `completed` may omit planning-only fields: `start`, `expected`, `priority`, and `risk`. A complete task without `completed` still requires these fields.
- `milestone`: optional milestone ID. When supplied, it must match an entry in the top-level `milestones` list. Omitted/null values mean no milestone.
- `priority`: required string priority label such as `low`, `medium`, `high`, or `urgent`.
- `status`: required string status. Allowed values are `pending`, `active`, `ongoing`, `blocked`, `unfunded`, and `complete`. Use `unfunded` for work awaiting funding.
- `dependencies`: optional list of task ids. Omitted or null values mean no dependencies. Each supplied dependency must reference another task `id`.
- `risk`: required risk entry or list of risk entries. Each entry has `type`, `level`, and `mitigation`, and may include `description`; `level` must be `low`, `medium`, `high`, or `extreme`.
- `funding`: optional mapping of fiscal year to funding level, for example `fy27: 50K`. A task is treated as unfunded for fiscal years not present in this mapping. For tasks other than `ongoing`, a warning is issued when a funding year falls outside the fiscal years covered by the task's `start` and calculated end month.

For `status: ongoing`, `start`, `expected`, `priority`, and
`risk` are optional (dependencies and tags are optional for every task). Scheduling uses every fiscal year with
positive numeric funding for its full twelve months, even when start/duration
fields are supplied. Supplied optional fields are still validated. Omitted
priority defaults to `low`; dependencies and risks
default to empty lists.

```yaml
id: Maintenance
label: Tool maintenance
project: Planning System
description: Maintain the planning tool.
status: ongoing
funding:
  fy27: 50K
  fy28: 0K
  fy29: 50K
```

This task spans all of FY27 and FY29, with no bar in FY28. Ongoing work has one
Gantt bar per funded year; gaps are preserved. Its duration is twelve months per
funded year. Without positive funding, it remains in lists and schedules but
has no Gantt bar. Ongoing tasks without funded years sort after dated tasks.

A task can also be a multi-part task by using `parts` instead of the per-task
schedule fields on the parent. Parent fields such as `project`, `execution_section`, `site`, `bnr`, `notes`,
`type` and `tags` are inherited by each part, while each part
provides its own `id`, `label`, `description`, `start`, `expected`,
`priority`, `status`, `dependencies`, and
`risk`. Funding can be specified on the parent or on individual parts. Part ids are expanded as `{parent_id}_{part_id}`. Dependencies inside a
multi-part task can reference sibling part ids, and dependencies from other
tasks can reference the parent id to depend on all expanded parts. A parent-level budget contributes once to fiscal-year totals whenever any of
its parts is included. Without a parent budget, totals sum the included parts
only. Parts inherit parent funding unless they specify their own; explicit part
funding controls their displayed amounts and ongoing time spans, while totals
use the parent budget when present.
In exports, parts in the same activity stay together at the position of their
first part, and keep their order relative to one another.

Task dates now use `start` plus `expected`. For example:

```yaml
start: M3Q4FY26
expected: 2
```

This task spans `M3Q4FY26` through `M1Q1FY27`. A duration of `1` stays in the
start month. For existing task files, rename `expected duration` or
`expected_duration` to `expected` and remove `deadline`. Compact completed tasks
may omit both scheduling fields; they use `completed` as the start and a
one-month duration.

All Word, plan SVG, and Gantt SVG/PowerPoint exports include centered, bold
`CUI//SP-BDGT` headers and footers. Word repeats them on every page; PowerPoint
includes them on every slide.

## Quick start

Run from this directory:

```bash
planner validate planner/examples/tasks.yaml
planner list planner/examples/tasks.yaml
planner list-detail planner/examples/tasks.yaml
planner summary planner/examples/tasks.yaml
planner schedule planner/examples/tasks.yaml
planner export planner/examples/tasks.yaml /tmp/plan.docx
planner export planner/examples/tasks.yaml /tmp/plan.svg
planner export-gantt planner/examples/tasks.yaml /tmp/gantt.svg
planner export-gantt planner/examples/tasks.yaml /tmp/gantt.pptx
planner export-gantt planner/examples/tasks.yaml /tmp/fy26-fy27.svg --from FY26 --through FY27
planner export-gantt planner/examples/tasks.yaml /tmp/partial.pptx --from M1Q1FY26 --through M3Q2FY27
```

If you set `TUXFAN_PLANNER_DATAFILE`, the CLI uses that file by default:

```bash
export TUXFAN_PLANNER_DATAFILE=planner/examples/tasks.yaml
planner validate
planner list
planner list-detail
planner export /tmp/plan.docx
planner export /tmp/plan.svg
planner export-gantt /tmp/gantt.pptx
```

If you set `TUXFAN_PLANNER_EXPORT_OPTIONS`, the Word and SVG plan exports use that file by default:

```bash
export TUXFAN_PLANNER_EXPORT_OPTIONS=planner/examples/export-options.yaml
planner export planner/examples/tasks.yaml /tmp/plan.docx
```

You can also install it locally:

```bash
python3 -m pip install -e .
planner list planner/examples/tasks.yaml
```

The legacy `tuxfan-planner` command remains available as an alias.
Run `planner --help` to see the current package version and the current values
of both planner environment variables alongside the command help. Ill-formed commands print an error and
the relevant help text to stderr, then exit with status 2.

## Shell completion

The CLI can print completion scripts for `bash`, `zsh`, and `fish`:

```bash
planner completion bash
planner completion zsh
planner completion fish
```

For a one-session bash setup:

```bash
source <(planner completion bash)
```

The generated completion supports the basic planner subcommands, the
`tuxfan-planner` alias, file path completion for task and output arguments, and
`--export-options` for export commands. It also completes `list`/`list-detail --only` and the `list --color`
values.

## Documentation

Sphinx documentation lives in `docs/`. See the
[developer setup and checks](DEVELOPER.md) for the current dependency and HTML
build commands.

The repository's `Documentation` workflow is defined in
`.github/workflows/docs.yml`. It builds the Sphinx output and publishes it to
the `gh-pages` branch.

## Updating the virtual environment

Follow the [CMake workflow](DEVELOPER.md#cmake-workflow) or the
[direct dependency commands](DEVELOPER.md#install-or-upgrade-dependencies) in
the developer guide to update the root `.venv` and check the result.

Input-file warnings and errors show `filename:line` when available. Funding
warnings identify the funding entry; task validation errors identify the task
block. Line numbers also cover literal Python inputs and YAML/Python syntax
errors. Computed Python task entries may only have a filename.

## Filter list output

Run `planner list --help` or `planner list-detail --help` for built-in filtering
examples, or see the [CLI reference](docs/cli.rst) for detailed matching and
funding rules.

Use `--only` with `list` or `list-detail` to select tasks by activity, project,
execution-section ID, tag, or milestone ID/label:

```bash
planner list tasks.yaml --only tags="Tag1, Tag2"
planner list-detail tasks.yaml --only milestones="Ristra Deliverables"
planner list tasks.yaml --only projects=FleCSI --only tags=gpu
planner list tasks.yaml --only "ristra, gpu"
```

A `target=` prefix limits matching to that field. Supported targets are
`activities`, `projects`, `sections`, `tags`, and `milestones`; singular names
also work. Activities match an activity name, section ID, or generated label
such as `Activity B`. Milestones match IDs or display labels. Targeted values
match complete field values, so `tags=gpu` does not match `gpu-kernels`.

Without a target, bare text matches **substrings** across all supported fields:
`--only "ristra"` can match a milestone ID, its label, a project, or a tag that
contains `ristra`. Matching ignores case and trims whitespace. Comma-separated
values within one flag are alternatives; **repeated `--only` flags must all
match**. For example, `--only projects=FleCSI --only tags=gpu` requires both the
project and the tag. Quote values containing spaces.

Only matching tasks and their associated activities are listed. Dependencies
are shown as references but do not pull other tasks into the view. Activity
letters, task numbers, and dependency numbers stay consistent with the full
plan. No matches produces a message and zero totals for configured years.
Unknown targets and empty value lists are usage errors.

Fiscal-year totals use the matching tasks. If a multipart parent has `funding`,
its full budget contributes once when any part matches, even when several parts
match or the parts specify their own funding. If the parent has no funding,
only matching parts' amounts contribute. `list-detail` also limits its activity
metadata and funding totals to the selection. Filtering does not change exports.

## Milestones

Define milestones at the top level with `id`, `label`, `description`, and a
fiscal `deadline`. `deliverables` is a list of non-empty text items and defaults
to an empty list. IDs must be unique and use letters, numbers, and underscores.
Tasks can omit `milestone`; a supplied ID must be defined, including links
inherited by multipart tasks. Legacy milestone names must be replaced by IDs
and given definitions; task files without milestone references need no section.
Milestone deadlines do not change task durations or dependency scheduling.

CLI metadata and Word output include milestone definitions; plan SVG headers
summarize milestone labels and deadlines.

## YAML format

Top-level can be either a list of tasks or a mapping with a `tasks` key. The mapping form can also include plan-level metadata such as `portfolio`, `project`, `managers`, `pocs`, `summary`, `fiscal_range_begin`, `fiscal_range_end`, and `execution`.

```yaml
portfolio: Advanced Simulation and Computing
project: Task-Parallel Project
managers:
  - Alice Example
pocs:
  - Casey Example, PI
summary: >
  High-level planning context for the task collection.
fiscal_range_begin: 27
fiscal_range_end: 29
execution:
  overview: >
    Overall execution context for the plan.
  sections:
    - id: deliverable_a
      label: Deliverable A
      description: >
        Narrative execution detail for this deliverable.

milestones:
  - id: Design
    label: Design
    description: Complete the architecture draft.
    deadline: M2Q3FY26
    deliverables: [Architecture document]
  - id: Implementation
    label: Implementation
    description: Deliver the task parser.
    deadline: M2Q3FY26
    deliverables: [Parser]
  - id: Phase_A
    label: Phase A
    description: Complete the first phase.
    deadline: M3Q2FY27
    deliverables: []
  - id: Phase_B
    label: Phase B
    description: Complete the second phase.
    deadline: M3Q4FY28
    deliverables: []

tasks:
  - id: DraftArchitecture
    label: Draft architecture
    bnr: DP1518130
    cost: $100K
    site: LANL
    type: labor
    tags:
      - architecture
    project: Planning System
    execution_section: deliverable_a
    description: Produce the first architecture draft.
    start: M1Q3FY26
    expected: 2
    milestone: Design
    priority: high
    status: complete
    dependencies: []
    risk:
      - type: scope
        level: medium
        description: Integration requirements may change during implementation.
        mitigation: Review the architecture with stakeholders before implementation starts.
    funding:
      fy27: 50K
      fy29: 100K

  - id: BuildParser
    label: Build parser
    project: Planning System
    description: Load task definitions from YAML and Python files.
    start: M2Q3FY26
    expected: 1
    milestone: Implementation
    priority: high
    status: pending
    dependencies:
      - DraftArchitecture
    risk:
      - type: integration
        level: medium
        mitigation: Add schema validation tests for both YAML and Python task sources.

  - id: MultiYearDelivery
    label: Multi-year delivery
    site: LANL
    project: Planning System
    parts:
      - id: A
        label: First phase
        funding: {fy27: 50K}
        description: Complete the first phase.
        start: M1Q1FY27
        expected: 6
        milestone: Phase_A
        priority: high
        status: active
        dependencies: []
        risk:
          - type: schedule
            level: low
            mitigation: Review progress monthly.
      - id: B
        label: Second phase
        funding: {fy28: 100K}
        description: Complete the second phase.
        start: M1Q3FY27
        expected: 18
        milestone: Phase_B
        priority: medium
        status: pending
        dependencies:
          - A
        risk:
          - type: technical
            level: medium
            mitigation: Keep integration tests current.
```

## Python format

Define `PLAN` or `plan` in a `.py` file for metadata plus tasks. The legacy `TASKS` or `tasks` list still works.

```python
PLAN = {
    "portfolio": "Advanced Simulation and Computing",
    "project": "Task-Parallel Project",
    "managers": ["Alice Example"],
    "pocs": ["Casey Example, PI"],
    "summary": "High-level planning context for the task collection.",
    "fiscal_range_begin": 27,
    "fiscal_range_end": 29,
    "execution": {
        "overview": "Overall execution context for the plan.",
        "sections": [
            {
                "id": "deliverable_a",
                "label": "Deliverable A",
                "description": "Narrative execution detail for this deliverable.",
            }
        ],
    },
    "milestones": [
        {
            "id": "Design",
            "label": "Design",
            "description": "Complete the architecture draft.",
            "deadline": "M2Q3FY26",
            "deliverables": ["Architecture document"],
        }
    ],
    "tasks": [
        {
            "id": "DraftArchitecture",
            "label": "Draft architecture",
            "bnr": "DP1518130",
            "cost": "$100K",
            "site": "LANL",
            "type": "labor",
            "tags": ["architecture"],
            "project": "Planning System",
            "execution_section": "deliverable_a",
            "description": "Produce the first architecture draft.",
            "start": "M1Q3FY26",
            "expected": 2,
            "milestone": "Design",
            "priority": "high",
            "status": "complete",
            "dependencies": [],
            "risk": [
                {
                    "type": "scope",
                    "level": "medium",
                    "mitigation": "Review the architecture with stakeholders before implementation starts.",
                }
            ],
            "funding": {"fy27": "50K", "fy29": "100K"},
        }
    ],
}
```

## Status values

Use one of:

- `pending`
- `active`
- `ongoing`
- `blocked`
- `unfunded`
- `complete`

## Fiscal period values

`start` and `completed` must use `M{month}Q{quarter}FY{year}`:

- `month` is `1` to `3` within the quarter
- `quarter` is `1` to `4`
- `year` is a 2-digit fiscal year, for example `FY26`

Fiscal years begin on October 1:

- `Q1` = October through December
- `Q2` = January through March
- `Q3` = April through June
- `Q4` = July through September

Examples:

- `M1Q1FY26` = October 2025
- `M2Q3FY26` = May 2026
- `M3Q4FY26` = September 2026

## Commands

- `validate`: checks required fields, invalid status values, invalid fiscal periods or non-positive/non-integer expected durations, invalid ids, duplicate ids, missing dependencies, and dependency cycles
- `list`: prints tasks in Word activity and task-number order with each task's description, numbered dependencies when present, status, and a single funding line for identified fiscal years (for example, `Funding: $500K (FY27), $250K (FY28)`). For configured fiscal years, a `Total Funding:` line at the bottom shows every year in thousands of dollars, including `$0K` years. Linked activity headings also show their execution section descriptions. Activity and task descriptions wrap at 80 characters. A gray block line separates tasks within an activity. Darker gray double rules align with activity headings before each activity and between its description and first task. It uses color when stdout is a compatible terminal, with dollar amounts in light green, statuses by state, and the entire entry for an `unfunded` task in gray; `--color always` or `--color never` overrides automatic detection. `NO_COLOR` disables automatic color. Accepts a task file or uses `TUXFAN_PLANNER_DATAFILE`.
- `list-detail`: prints the full task details in calculated end-month order, including the start and end fiscal periods and duration in months.
- `summary`: groups tasks by project and milestone and prints funding totals when task funding is present
- `schedule`: prints tasks in dependency order and marks them as `READY`, `BLOCKED`, `ACTIVE`, `ONGOING`, or `COMPLETE`
- `export`: writes a Word plan for a `.docx` output path or a graphical plan for `.svg`. Add `--include-notes` or set `include_notes: true` in export options to show an italic `Notes:` line after each task description. Use `--export-options PATH` or `TUXFAN_PLANNER_EXPORT_OPTIONS` to control optional task attributes in both formats. The Word document includes a schedule and a detailed task table, starting on a new page with its section headings. When fiscal years are configured, the table includes one column per year with each task's funding level or a blank cell, plus a final total row. The SVG uses the same activity letters and task numbers as Word, and shows fiscal-year funding totals, completion years, and numbered dependencies. Its fill color reflects status, border color reflects risk, and an accent bar reflects priority. Both formats keep multipart task parts adjacent. Custom column labels and alignment apply only to Word.
- `export-gantt`: writes a fiscal-month Gantt chart as `.svg` or `.pptx`, selected by the output file extension. For ordinary tasks, bars span `start` through the end month calculated from `expected`. Ongoing tasks instead have separate full-year bars for each year with positive funding. By default, the axis covers the configured fiscal year range exactly; tasks outside it are omitted and overlapping bars are clipped. Without configured fiscal years, it covers task and completion dates. Optional inclusive `--from` and `--through` bounds override the corresponding axis endpoints and accept a fiscal year such as `FY26` or an exact period such as `M1Q1FY26`; a fiscal year expands to its first or last month as appropriate. Rows follow execution activity order; each task row keeps its number and label on one line when they fit, otherwise wraps the label at a word boundary within the two-line cell. Arrows leave the dependency bar's right edge and enter the dependent bar's left edge, routing around overlaps without bending farther right than either bar requires; they connect bars within each activity, and PowerPoint draws an arrow when both bars appear on the same slide. Status colors and completion markers identify progress. The SVG is a vector image; the PowerPoint file uses editable native shapes and splits activities into slides of up to ten tasks, with additional slides for timelines longer than five fiscal years. Copy the desired slide into an existing presentation. Gantt exports do not use task table export options.

## Export options

Export options files can be YAML, YML, or Python. The supported settings are `task_table_attributes`, `task_table_columns`, and `include_notes`. The first two control the optional task attribute columns included in the Word export summary table and the attributes shown on SVG task cards. Custom labels and alignment apply only to Word. The `.docx` table always starts with an unlabeled column containing generated task numbers; it does not include a Project column. An `Activity A`, `Activity B`, etc. row precedes each activity's tasks, which appear in numeric task order. When tasks link to execution sections, the activity row includes the section label and its letter follows section order (for example, `Activity B: Specialization Work` with `Task B.1`). Unlinked tasks follow in project-based groups; plans without links keep their existing project-based grouping. Each activity label occupies one left-aligned cell spanning the table. The column header repeats only when the table continues onto another page; Activity rows appear at their normal group transitions. All table cells center their content vertically. Fiscal-year columns are added automatically when plan fiscal years are configured, with a final `Total Funding` row that sums each year's part-level amounts. For completed tasks, those columns become one green-filled cell labeled with the completion year (for example, `Completed FY26`) when a `completed` period is available, or `Completed` otherwise; the other cells remain black on white. The other task metadata columns and per-task attribute narrative are limited to the attributes listed here.

Set `include_notes: true` in an export options file, or pass `--include-notes` to `export`, to show italic notes, including the `Notes:` label, after task descriptions. Notes are hidden by default. Select `notes` as a table attribute when you also want a Notes column in the Word summary table.

In the Word risk mitigation section, the bold risk label is green for `low`, yellow for `medium`, orange for `high`, and deep red for `extreme`. Mitigation text remains black.

Top-level Word section labels, including `Tasks:`, are bold and underlined.

Supported attribute names:

- `bnr`
- `cost`
- `notes`
- `site`
- `type`
- `tags`

Simple YAML:

```yaml
task_table_attributes:
  - site
  - bnr
  - tags
```

Shorthand YAML with custom labels:

```yaml
task_table_attributes:
  - bnr: BNR
  - tags: Keywords
```

Recommended explicit YAML when you need labels or alignment:

```yaml
task_table_columns:
  - attribute: bnr
    label: BNR
  - attribute: tags
    label: Keywords
```

Supported column alignment values:

- `left`
- `center`
- `right`

`alignment` is optional and defaults to `center`.

Example CLI usage:

```bash
planner export planner/examples/tasks.yaml /tmp/plan.docx --export-options planner/examples/export-options.yaml
```
