Files
MISF-Planner-Download/scripts/probe_assignments.py
T
VasanthandClaude Opus 5 bfd72825b4 Skip Not-Started tasks that are already assigned to someone
A task carrying an "Assigned to" avatar in the Planner grid has been picked
up by a person, so its attachments must not be downloaded and its status must
not be moved to In Progress. Until now the only filter was percentComplete
== 0, so an assigned task with an attachment was downloaded like any other —
on the Balboa Nursing and Rehab plan that was one of the two "Facesheets
Needed (GG)" tasks assigned to Dr. Mark Lukasik and Adina Smets.

- _not_started_tasks() now selects `assignments` as well.
- _task_assignee_ids()/_resolve_user_names(): the assignee check runs before
  the task-details call, so an assigned task costs no extra Graph request;
  display names are resolved once per user and cached for the run, falling
  back to the raw ID when the lookup fails.
- Summary workbook gains a "Tasks Skipped (Assigned)" column and a second
  "Tasks" sheet with one row per task — facility, task name, assigned to,
  attachment count, Downloaded Yes/No and the reason — so a skip can be
  explained without reading the log.
- New "All assigned — skipped" facility status, distinct from "No attachments".

scripts/probe_assignments.py is the read-only probe used to confirm the rule
against the live plan before changing behaviour.

Verified on Balboa Nursing and Rehab: 4 Not Started -> 8.24.26 and 8.21.26
downloaded (unassigned), both Facesheets Needed (GG) skipped. 110 tests pass
(tests/test_uipath_pilot.py and tests/test_config.py fail on this checkout for
pre-existing, unrelated reasons).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 18:46:00 +05:30

89 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""
Read-only probe: for one plan, list every Not-Started task with its assignee
count and attachment count, so the "skip assigned tasks" rule can be validated
against what the Planner grid shows before any behaviour is changed.
Downloads nothing and never PATCHes a task — safe against production Planner.
Run: .venv\\Scripts\\python scripts\\probe_assignments.py --facility "Balboa"
"""
from __future__ import annotations
import argparse
import logging
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / "src"
for p in (str(ROOT), str(SRC)):
if p not in sys.path:
sys.path.insert(0, p)
from config import read_app_config # noqa: E402
from onedrive_planner_http import ( # noqa: E402
_build_client,
_discover_all_plans,
_get_paged,
_login_with_retry,
_match_plan,
_task_file_urls,
)
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--facility", required=True, help="facility / plan name fragment")
ap.add_argument("--headed", action="store_true")
args = ap.parse_args()
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)-7s %(message)s",
stream=sys.stdout,
)
cfg = read_app_config()
token, _ = _login_with_retry(cfg, headless=not args.headed)
client = _build_client(cfg, token)
plans = _discover_all_plans(client, cfg)
plan = _match_plan(plans, args.facility)
if not plan:
print(f"no plan matched {args.facility!r}")
return 1
print(f"\nplan: {plan.get('title')!r}\n")
tasks = _get_paged(
client,
f"planner/plans/{plan['id']}/tasks",
cfg,
**{"$select": "id,title,percentComplete,assignments,orderHint"},
)
print(f"{'%':>4} {'assignees':>9} {'files':>5} title")
print("-" * 78)
would_download = would_skip = 0
for t in tasks:
pct = t.get("percentComplete") or 0
if pct != 0:
continue
assignees = len(t.get("assignments") or {})
n_files = len(_task_file_urls(client, t["id"], cfg))
print(f"{pct:>4} {assignees:>9} {n_files:>5} {(t.get('title') or '')[:50]}")
if n_files:
if assignees:
would_skip += 1
else:
would_download += 1
print("-" * 78)
print(f"With the 'skip assigned' rule: {would_download} would download, "
f"{would_skip} would skip (attachment-bearing tasks only)")
return 0
if __name__ == "__main__":
raise SystemExit(main())