Files
POPS/app/vikunja.py
Martin Hohenberg 5dfa700916 Tag all Vikunja tasks with #POPS label; fix Vikunja URL
- On create_task(), look up the 'POPS' label (create it if missing) and
  attach it to every task. Label ID is cached for the process lifetime.
- Corrected VIKUNJA_URL typo (tasks → task)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 07:58:20 +02:00

115 lines
3.0 KiB
Python

"""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