"""Query ClickUp 'to do' tasks tagged 'feb26' in OPT/LINKS/Content categories.""" from __future__ import annotations import os import sys from datetime import datetime, timezone from pathlib import Path _root = Path(__file__).resolve().parent.parent sys.path.insert(0, str(_root)) from dotenv import load_dotenv load_dotenv(_root / ".env") from cheddahbot.clickup import ClickUpClient API_TOKEN = os.environ.get("CLICKUP_API_TOKEN", "") SPACE_ID = os.environ.get("CLICKUP_SPACE_ID", "") if not API_TOKEN: sys.exit("ERROR: CLICKUP_API_TOKEN env var is required") if not SPACE_ID: sys.exit("ERROR: CLICKUP_SPACE_ID env var is required") # Work Category values to include (case-insensitive partial match) CATEGORY_FILTERS = ["opt", "link", "content"] TAG_FILTER = "feb26" def ms_to_date(ms_str: str) -> str: """Convert Unix-ms timestamp string to YYYY-MM-DD.""" if not ms_str: return "—" try: ts = int(ms_str) / 1000 return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d") except (ValueError, OSError): return ms_str def main() -> None: client = ClickUpClient(api_token=API_TOKEN, task_type_field_name="Work Category") print(f"Fetching 'to do' tasks from space {SPACE_ID} ...") tasks = client.get_tasks_from_overall_lists(SPACE_ID, statuses=["to do"]) print(f"Total 'to do' tasks: {len(tasks)}") # Filter by feb26 tag tagged = [t for t in tasks if TAG_FILTER in [tag.lower() for tag in t.tags]] print(f"Tasks with '{TAG_FILTER}' tag: {len(tagged)}") # Show all Work Category values for debugging categories = set() for t in tagged: wc = t.custom_fields.get("Work Category", "") or "" categories.add(wc) print(f"Work Categories found: {categories}") # Filter by OPT/LINKS/Content categories filtered = [] for t in tagged: wc = str(t.custom_fields.get("Work Category", "") or "").lower() if any(cat in wc for cat in CATEGORY_FILTERS): filtered.append(t) print(f"After category filter (OPT/LINKS/Content): {len(filtered)}") # Sort by due date (oldest first), tasks with no due date go last def sort_key(t): if t.due_date: try: return (0, int(t.due_date)) except ValueError: return (1, 0) return (2, 0) filtered.sort(key=sort_key) # Top 10 top10 = filtered[:10] # Print table print(f"\n{'#':>3} | {'ID':>11} | {'Keyword/Name':<45} | {'Due':>10} | {'Customer':<20} | Tags") print("-" * 120) for i, t in enumerate(top10, 1): customer = t.custom_fields.get("Customer", "") or "—" due = ms_to_date(t.due_date) wc = t.custom_fields.get("Work Category", "") or "" tags_str = ", ".join(t.tags) name_display = t.name[:45] if len(t.name) > 45 else t.name print(f"{i:>3} | {t.id:>11} | {name_display:<45} | {due:>10} | {customer:<20} | {tags_str}") if not top10: print(" (no matching tasks found)") print(f"\n--- {len(filtered)} total matching tasks, showing top {len(top10)} (oldest first) ---") if __name__ == "__main__": main()