88 lines
2.7 KiB
Python
88 lines
2.7 KiB
Python
"""Query ClickUp 'to do' tasks tagged feb26 in OPT/LINKS/Content categories."""
|
|
|
|
import os
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))
|
|
|
|
from cheddahbot.clickup import ClickUpClient
|
|
|
|
TOKEN = os.getenv("CLICKUP_API_TOKEN", "")
|
|
SPACE_ID = os.getenv("CLICKUP_SPACE_ID", "")
|
|
|
|
if not TOKEN or not SPACE_ID:
|
|
print("ERROR: CLICKUP_API_TOKEN and CLICKUP_SPACE_ID must be set in .env")
|
|
sys.exit(1)
|
|
|
|
CATEGORIES = {"On Page Optimization", "Content Creation", "Link Building"}
|
|
TAG_FILTER = "feb26"
|
|
|
|
client = ClickUpClient(api_token=TOKEN, workspace_id="", task_type_field_name="Work Category")
|
|
|
|
print(f"Querying ClickUp space {SPACE_ID} for 'to do' tasks...")
|
|
tasks = client.get_tasks_from_space(SPACE_ID, statuses=["to do"])
|
|
client.close()
|
|
|
|
print(f"Total 'to do' tasks found: {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)}")
|
|
|
|
# Filter by Work Category (OPT / LINKS / Content)
|
|
filtered = []
|
|
for t in tagged:
|
|
cat = (t.custom_fields.get("Work Category") or t.task_type or "").strip()
|
|
if cat in CATEGORIES:
|
|
filtered.append(t)
|
|
|
|
if not filtered and tagged:
|
|
# Show what categories exist so we can refine
|
|
cats_found = set()
|
|
for t in tagged:
|
|
cats_found.add(t.custom_fields.get("Work Category") or t.task_type or "(none)")
|
|
print(f"\nNo tasks matched categories {CATEGORIES}.")
|
|
print(f"Work Categories found on feb26-tagged tasks: {cats_found}")
|
|
print("\nShowing ALL feb26-tagged tasks instead:\n")
|
|
filtered = tagged
|
|
|
|
# Sort by due date (oldest first), tasks without due date go last
|
|
def sort_key(t):
|
|
if t.due_date:
|
|
return int(t.due_date)
|
|
return float("inf")
|
|
|
|
filtered.sort(key=sort_key)
|
|
|
|
# Take top 10
|
|
top = filtered[:10]
|
|
|
|
# Format table
|
|
def fmt_due(raw_due: str) -> str:
|
|
if not raw_due:
|
|
return "—"
|
|
try:
|
|
ts = int(raw_due) / 1000
|
|
return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%m/%d")
|
|
except (ValueError, OSError):
|
|
return raw_due
|
|
|
|
def fmt_customer(t) -> str:
|
|
return t.custom_fields.get("Customer", "") or "—"
|
|
|
|
print(f"\n{'#':<3} | {'ID':<12} | {'Keyword/Name':<45} | {'Cat':<15} | {'Due':<6} | {'Customer':<20} | Tags")
|
|
print("-" * 120)
|
|
|
|
for i, t in enumerate(top, 1):
|
|
tags_str = ", ".join(t.tags)
|
|
name = t.name[:45]
|
|
cat = t.custom_fields.get("Work Category") or t.task_type or "—"
|
|
print(f"{i:<3} | {t.id:<12} | {name:<45} | {cat:<15} | {fmt_due(t.due_date):<6} | {fmt_customer(t):<20} | {tags_str}")
|
|
|
|
print(f"\nTotal shown: {len(top)} of {len(filtered)} matching tasks")
|