"""
ActiveCollab API client.
"""
from __future__ import annotations
import json
import urllib.request
import urllib.error
from typing import Any
from ..utils.config import Config
class ActiveCollabClient:
def __init__(self, config: Config) -> None:
self._config = config
self._base_url = config.activecollab.api_url.rstrip("/")
self._headers = {
"X-Angie-AuthApiToken": config.activecollab.api_token,
"Content-Type": "application/json",
}
def _get(self, endpoint: str) -> Any:
url = f"{self._base_url}{endpoint}"
req = urllib.request.Request(url, headers=self._headers)
with urllib.request.urlopen(req, timeout=30) as response:
return json.loads(response.read().decode("utf-8"))
def list_projects(self) -> list[dict[str, Any]]:
return self._get("/projects")
def get_project(self, project_id: int) -> dict[str, Any]:
return self._get(f"/projects/{project_id}")
def list_tasks(self, project_id: int) -> list[dict[str, Any]]:
response = self._get(f"/projects/{project_id}/tasks")
return response.get("tasks", []) if isinstance(response, dict) else response
def get_task(self, project_id: int, task_id: int) -> dict[str, Any]:
return self._get(f"/projects/{project_id}/tasks/{task_id}")
def get_user_tasks(self) -> list[dict[str, Any]]:
return self._get("/user-tasks")
def get_task_comments(self, task_id: int) -> list[dict[str, Any]]:
return self._get(f"/comments/task/{task_id}")
def get_task_time_records(self, project_id: int, task_id: int) -> list[dict[str, Any]]:
response = self._get(f"/projects/{project_id}/tasks/{task_id}/time-records")
return response.get("time_records", []) if isinstance(response, dict) else response
def get_project_time_records(self, project_id: int) -> list[dict[str, Any]]:
response = self._get(f"/projects/{project_id}/time-records")
return response.get("time_records", []) if isinstance(response, dict) else response
def get_notifications(self) -> list[dict[str, Any]]:
response = self._get("/notifications")
return response.get("notifications", []) if isinstance(response, dict) else response
def get_subtasks(self, project_id: int, task_id: int) -> list[dict[str, Any]]:
return self._get(f"/projects/{project_id}/tasks/{task_id}/subtasks")
def get_labels(self) -> list[dict[str, Any]]:
return self._get("/labels")
def get_task_lists(self, project_id: int) -> list[dict[str, Any]]:
return self._get(f"/projects/{project_id}/task-lists")
def get_users(self) -> list[dict[str, Any]]:
return self._get("/users")
if __name__ == "__main__":
config = Config.load()
client = ActiveCollabClient(config)
print("Testing API connection...")
projects = client.list_projects()
print(f"\nFound {len(projects)} projects:\n")
for project in projects:
print(f" - [{project.get('id')}] {project.get('name')}")