"""Query ClickUp 'to do' tasks tagged feb26 in OPT/LINKS/Content categories.""" import sys from datetime import datetime, timezone from pathlib import Path sys.stdout.reconfigure(line_buffering=True) sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from cheddahbot.config import load_config from cheddahbot.clickup import ClickUpClient CATEGORY_PREFIXES = ("opt", "link", "content", "ai content") TAG_FILTER = "feb26" def ms_to_date(ms_str: str) -> str: if not ms_str: return "—" try: ts = int(ms_str) / 1000 return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%m/%d") except (ValueError, OSError): return "—" def main(): cfg = load_config() if not cfg.clickup.api_token or not cfg.clickup.space_id: print("ERROR: CLICKUP_API_TOKEN or CLICKUP_SPACE_ID not set.") return client = ClickUpClient( api_token=cfg.clickup.api_token, workspace_id=cfg.clickup.workspace_id, task_type_field_name=cfg.clickup.task_type_field_name, ) try: # Fetch all 'to do' tasks across the space tasks = client.get_tasks_from_space(cfg.clickup.space_id, statuses=["to do"]) # Filter by feb26 tag tagged = [t for t in tasks if TAG_FILTER in [tag.lower() for tag in t.tags]] if not tagged: all_tags = set() for t in tasks: all_tags.update(t.tags) print(f"No tasks with tag '{TAG_FILTER}'. Tags seen: {sorted(all_tags)}") print(f"Total 'to do' tasks found: {len(tasks)}") return # Filter to OPT/LINKS/Content categories (by task name, Work Category, or list name) def is_target_category(t): name_lower = t.name.lower().strip() wc = (t.custom_fields.get("Work Category") or "").lower() ln = (t.list_name or "").lower() for prefix in CATEGORY_PREFIXES: if name_lower.startswith(prefix) or prefix in wc or prefix in ln: return True return False filtered = [t for t in tagged if is_target_category(t)] skipped = [t for t in tagged if not is_target_category(t)] # Sort by due date (oldest first), tasks with no due date go last filtered.sort(key=lambda t: int(t.due_date) if t.due_date else float("inf")) top = filtered[:10] # Build table print(f"feb26-tagged 'to do' tasks — OPT / LINKS / Content (top 10, oldest first)") print(f"\n{'#':>2} | {'ID':<11} | {'Keyword/Name':<50} | {'Due':<6} | {'Customer':<25} | Tags") print("-" * 120) for i, t in enumerate(top, 1): customer = t.custom_fields.get("Customer", "") or "—" due = ms_to_date(t.due_date) tags = ", ".join(t.tags) name = t.name[:50] print(f"{i:>2} | {t.id:<11} | {name:<50} | {due:<6} | {customer:<25} | {tags}") print(f"\nShowing {len(top)} of {len(filtered)} OPT/LINKS/Content tasks ({len(tagged)} total feb26-tagged).") if skipped: print(f"\nSkipped {len(skipped)} non-OPT/LINKS/Content tasks:") for t in skipped: print(f" - {t.name} ({t.id})") finally: client.close() if __name__ == "__main__": main()