"""Vikunja task API client.""" import logging import os import httpx logger = logging.getLogger("pops") _pops_label_id: int | None = None def _base() -> str: return os.environ.get("VIKUNJA_URL", "").rstrip("/") def _headers() -> dict: return { "Authorization": f"Bearer {os.environ.get('VIKUNJA_API_TOKEN', '')}", "Content-Type": "application/json", } def _project_id() -> int: return int(os.environ.get("VIKUNJA_PROJECT_ID", 0)) def _get_or_create_pops_label() -> int | None: """Return the ID of the 'POPS' label, creating it if it doesn't exist.""" global _pops_label_id if _pops_label_id is not None: return _pops_label_id try: r = httpx.get(f"{_base()}/api/v1/labels", headers=_headers(), timeout=5) r.raise_for_status() for label in r.json() or []: if label.get("title") == "POPS": _pops_label_id = label["id"] return _pops_label_id # Not found — create it r = httpx.put( f"{_base()}/api/v1/labels", headers=_headers(), json={"title": "POPS"}, timeout=5, ) r.raise_for_status() _pops_label_id = r.json()["id"] return _pops_label_id except Exception: logger.warning("Vikunja: failed to get or create POPS label") return None def get_tasks() -> list[dict]: try: r = httpx.get( f"{_base()}/api/v1/projects/{_project_id()}/tasks", headers=_headers(), params={"sort_by": "created", "order_by": "desc"}, timeout=5, ) r.raise_for_status() return r.json() or [] except Exception: logger.warning("Vikunja: failed to fetch tasks") return [] def create_task(title: str) -> dict | None: label_id = _get_or_create_pops_label() payload: dict = {"title": title} if label_id: payload["labels"] = [{"id": label_id}] try: r = httpx.put( f"{_base()}/api/v1/projects/{_project_id()}/tasks", headers=_headers(), json=payload, timeout=5, ) r.raise_for_status() return r.json() except Exception: logger.warning("Vikunja: failed to create task %r", title) return None def update_task(task_id: int, **fields) -> bool: try: r = httpx.post( f"{_base()}/api/v1/tasks/{task_id}", headers=_headers(), json=fields, timeout=5, ) r.raise_for_status() return True except Exception: logger.warning("Vikunja: failed to update task %s", task_id) return False def delete_task(task_id: int) -> bool: try: r = httpx.delete( f"{_base()}/api/v1/tasks/{task_id}", headers=_headers(), timeout=5, ) r.raise_for_status() return True except Exception: logger.warning("Vikunja: failed to delete task %s", task_id) return False