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>
This commit is contained in:
1 parent
dd864e0b4c
commit
bfd72825b4
3 files changed
+317
-9
No files matched your search
@@ -0,0 +1,88 @@
|
||||
#!/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())
|
||||
Reference in new issue
Block a user