The first test module. Reports team staffing capacity from Objective issues + a Projects v2 board. FTE is summed per person per Program Increment (PI); > 1.0 = over-allocated. A duration-weighted FTE adjusts for objectives that cover only part of a PI.
- Issue body holds the
## LOE/FTEtable —| Person | Role | FTE | Notes |. Roles are a fixed set (PM, Frontend, Backend, Geospatial, Data Curator, Comms, ML, Designer, Jupyterhub); any other role or non-numeric FTE is skipped + warned. - Board fields hold everything else, per objective: Program Increment, Start/End dates, and Project / Initiative / Team. The generator joins body + board by issue number.
| Field | Type | Notes |
|---|---|---|
| Program Increment | single-select | PI 26.4, PI 27.2 — add more to test PI changes. Windows come from PI_WINDOWS in the generator; the parser also handles a real iteration PI field. |
| Start Date / End Date | date | the objective's own window inside the PI |
| Project / Initiative / Team | single-select | grouping fields; Initiative drives the dashboard's Capacity Matrix grouping |
generate_sample_issues.py (→ sample_issues.json) → create_issues.py (real issues) →
setup_project.py (add to board + PI/Start/End) → setup_board_grouping.py (Project/Initiative/Team).
Tracking files (created_issues.json, project_items.json) stay in seed/. Board = the personal
project kyle-lesinger/projects/2 (isolated from the org project list).
.github/scripts/generate_fte_report.py (stdlib) accepts --project-json (board JSON),
--issues-json (offline sample), or --from-dir; --pi "PI 26.4" filters. It emits
PI-slugged fte_allocations_<slug>.csv (with project,initiative,team), fte_by_person_<slug>.csv,
fte_by_role_<slug>.csv, and fte_summary_<slug>.md (slug = pi-26.4, or all-pis when unfiltered),
plus a fixed fte_manifest.json. The workflow fte-report.yml runs it against the board and publishes to
fte-report/<pi> (+ artifact + run summary). Adding a board field ⇒ no code change: the parser
reads any field generically via _item_field(it, "<name>").
Σ allocation FTE == Σ by_person == Σ by_role, per PI, for both raw and weighted (weighted is rounded per-allocation, so it reconciles to the cent). Sample assignment of PI / Initiative / Project / Team is deterministic (seeded + index-based) so reports diff cleanly across runs.
PROJECT_TOKEN_FOR_BOARD_READS(repo secret) = classic PATrepo+read:org+project. The defaultGITHUB_TOKENcannot read Projects v2.unknown owner typefromgh project⇒ missingread:org.- GraphQL rate limit is 5,000/hr — a full reseed makes 150+ board mutations; space them out or you'll get throttled (the Action's board read will then fail until the hour resets).
- Dashboard reads the public
fte-report/all-pisbranch at runtime (fte-dashboard/src/data.tsRAW_BASE), with the bundledpublic/data/snapshot as fallback.
Full dashboard internals: source repo Disasters-Learning-Portal/disasters-aws-conversion, PR #74.