taskdog-client 0.18.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. taskdog_client-0.18.1/PKG-INFO +160 -0
  2. taskdog_client-0.18.1/README.md +133 -0
  3. taskdog_client-0.18.1/pyproject.toml +64 -0
  4. taskdog_client-0.18.1/setup.cfg +4 -0
  5. taskdog_client-0.18.1/src/taskdog_client/__init__.py +31 -0
  6. taskdog_client-0.18.1/src/taskdog_client/analytics_client.py +97 -0
  7. taskdog_client-0.18.1/src/taskdog_client/audit_client.py +120 -0
  8. taskdog_client-0.18.1/src/taskdog_client/base_client.py +205 -0
  9. taskdog_client-0.18.1/src/taskdog_client/bulk_client.py +90 -0
  10. taskdog_client-0.18.1/src/taskdog_client/converters/__init__.py +31 -0
  11. taskdog_client-0.18.1/src/taskdog_client/converters/datetime_utils.py +160 -0
  12. taskdog_client-0.18.1/src/taskdog_client/converters/exceptions.py +33 -0
  13. taskdog_client-0.18.1/src/taskdog_client/converters/gantt_converters.py +158 -0
  14. taskdog_client-0.18.1/src/taskdog_client/converters/optimization_converters.py +139 -0
  15. taskdog_client-0.18.1/src/taskdog_client/converters/statistics_converters.py +215 -0
  16. taskdog_client-0.18.1/src/taskdog_client/converters/tag_converters.py +25 -0
  17. taskdog_client-0.18.1/src/taskdog_client/converters/task_converters.py +203 -0
  18. taskdog_client-0.18.1/src/taskdog_client/lifecycle_client.py +148 -0
  19. taskdog_client-0.18.1/src/taskdog_client/notes_client.py +61 -0
  20. taskdog_client-0.18.1/src/taskdog_client/py.typed +0 -0
  21. taskdog_client-0.18.1/src/taskdog_client/query_client.py +211 -0
  22. taskdog_client-0.18.1/src/taskdog_client/relationship_client.py +102 -0
  23. taskdog_client-0.18.1/src/taskdog_client/task_client.py +217 -0
  24. taskdog_client-0.18.1/src/taskdog_client/taskdog_api_client.py +443 -0
  25. taskdog_client-0.18.1/src/taskdog_client/websocket/__init__.py +8 -0
  26. taskdog_client-0.18.1/src/taskdog_client/websocket/websocket_client.py +203 -0
  27. taskdog_client-0.18.1/src/taskdog_client.egg-info/PKG-INFO +160 -0
  28. taskdog_client-0.18.1/src/taskdog_client.egg-info/SOURCES.txt +40 -0
  29. taskdog_client-0.18.1/src/taskdog_client.egg-info/dependency_links.txt +1 -0
  30. taskdog_client-0.18.1/src/taskdog_client.egg-info/requires.txt +10 -0
  31. taskdog_client-0.18.1/src/taskdog_client.egg-info/top_level.txt +1 -0
  32. taskdog_client-0.18.1/tests/test_analytics_client.py +83 -0
  33. taskdog_client-0.18.1/tests/test_audit_client.py +328 -0
  34. taskdog_client-0.18.1/tests/test_base_client.py +176 -0
  35. taskdog_client-0.18.1/tests/test_bulk_client.py +171 -0
  36. taskdog_client-0.18.1/tests/test_converters.py +610 -0
  37. taskdog_client-0.18.1/tests/test_lifecycle_client.py +53 -0
  38. taskdog_client-0.18.1/tests/test_notes_client.py +62 -0
  39. taskdog_client-0.18.1/tests/test_query_client.py +132 -0
  40. taskdog_client-0.18.1/tests/test_relationship_client.py +92 -0
  41. taskdog_client-0.18.1/tests/test_task_client.py +152 -0
  42. taskdog_client-0.18.1/tests/test_taskdog_api_client.py +319 -0
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: taskdog-client
3
+ Version: 0.18.1
4
+ Summary: HTTP API client for Taskdog server
5
+ Author: Kohei Wada
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Kohei-Wada/taskdog
8
+ Project-URL: Repository, https://github.com/Kohei-Wada/taskdog
9
+ Project-URL: Bug Tracker, https://github.com/Kohei-Wada/taskdog/issues
10
+ Keywords: task,management,api-client
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: taskdog-core==0.18.1
19
+ Requires-Dist: httpx>=0.27.0
20
+ Requires-Dist: websockets>=14.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
23
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
24
+ Requires-Dist: pytest-asyncio>=1.0.0; extra == "dev"
25
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
26
+ Requires-Dist: ruff>=0.7.0; extra == "dev"
27
+
28
+ # taskdog-client
29
+
30
+ HTTP API client library for Taskdog server.
31
+
32
+ ## Overview
33
+
34
+ This package provides a type-safe HTTP client for communicating with the Taskdog API server. It handles authentication, error mapping, and response conversion to domain DTOs.
35
+
36
+ **Use cases:**
37
+
38
+ - Building custom CLI tools
39
+ - Integrating Taskdog with other systems
40
+ - Creating automated workflows
41
+ - Testing API endpoints
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install taskdog-client
47
+ ```
48
+
49
+ For development:
50
+
51
+ ```bash
52
+ pip install -e ".[dev]"
53
+ ```
54
+
55
+ ## Client Classes
56
+
57
+ | Client | Purpose |
58
+ |--------|---------|
59
+ | `TaskClient` | Task CRUD operations (create, update, delete, archive) |
60
+ | `LifecycleClient` | Status changes (start, complete, pause, cancel, reopen) |
61
+ | `RelationshipClient` | Dependencies and tags |
62
+ | `QueryClient` | List tasks, get task details |
63
+ | `AnalyticsClient` | Statistics and Gantt data |
64
+ | `NotesClient` | Task notes (markdown) |
65
+ | `AuditClient` | Audit log access |
66
+ | `BaseApiClient` | Base class with common HTTP logic |
67
+
68
+ ## Usage Example
69
+
70
+ ```python
71
+ from taskdog_client import TaskClient, LifecycleClient, QueryClient
72
+
73
+ # Create clients
74
+ base_url = "http://127.0.0.1:8000"
75
+ api_key = "your-api-key" # Optional, for authenticated servers
76
+
77
+ task_client = TaskClient(base_url, api_key)
78
+ lifecycle_client = LifecycleClient(base_url, api_key)
79
+ query_client = QueryClient(base_url, api_key)
80
+
81
+ # Create a task
82
+ task = task_client.create_task(
83
+ name="My Task",
84
+ priority=100,
85
+ estimated_duration=8.0,
86
+ tags=["backend"]
87
+ )
88
+ print(f"Created task: {task.id}")
89
+
90
+ # List tasks
91
+ tasks = query_client.list_tasks(status="pending")
92
+ for t in tasks:
93
+ print(f"- {t.name} (ID: {t.id})")
94
+
95
+ # Start a task
96
+ lifecycle_client.start_task(task.id)
97
+
98
+ # Complete a task
99
+ lifecycle_client.complete_task(task.id)
100
+ ```
101
+
102
+ ## Error Handling
103
+
104
+ Clients raise exceptions from `taskdog_core.domain.exceptions`:
105
+
106
+ ```python
107
+ from taskdog_core.domain.exceptions import TaskNotFoundException, TaskValidationError
108
+
109
+ try:
110
+ task_client.get_task(999)
111
+ except TaskNotFoundException:
112
+ print("Task not found")
113
+ except TaskValidationError as e:
114
+ print(f"Validation error: {e}")
115
+ ```
116
+
117
+ ## Configuration
118
+
119
+ Clients accept optional configuration:
120
+
121
+ ```python
122
+ from taskdog_client import TaskClient
123
+
124
+ # Basic usage (no auth)
125
+ client = TaskClient("http://127.0.0.1:8000")
126
+
127
+ # With API key authentication
128
+ client = TaskClient(
129
+ base_url="http://127.0.0.1:8000",
130
+ api_key="your-secret-key"
131
+ )
132
+
133
+ # Custom timeout (in seconds)
134
+ client = TaskClient(
135
+ base_url="http://127.0.0.1:8000",
136
+ timeout=30.0
137
+ )
138
+ ```
139
+
140
+ ## Dependencies
141
+
142
+ - `taskdog-core`: Domain DTOs and exceptions
143
+ - `httpx`: HTTP client library
144
+
145
+ ## Related Packages
146
+
147
+ - [taskdog-core](../taskdog-core/): Domain DTOs used by this package
148
+ - [taskdog-server](../taskdog-server/): API server this client connects to
149
+ - [taskdog-ui](../taskdog-ui/): CLI/TUI that uses this client
150
+ - [taskdog-mcp](../taskdog-mcp/): MCP server that uses this client
151
+
152
+ ## Testing
153
+
154
+ ```bash
155
+ pytest tests/
156
+ ```
157
+
158
+ ## License
159
+
160
+ MIT
@@ -0,0 +1,133 @@
1
+ # taskdog-client
2
+
3
+ HTTP API client library for Taskdog server.
4
+
5
+ ## Overview
6
+
7
+ This package provides a type-safe HTTP client for communicating with the Taskdog API server. It handles authentication, error mapping, and response conversion to domain DTOs.
8
+
9
+ **Use cases:**
10
+
11
+ - Building custom CLI tools
12
+ - Integrating Taskdog with other systems
13
+ - Creating automated workflows
14
+ - Testing API endpoints
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install taskdog-client
20
+ ```
21
+
22
+ For development:
23
+
24
+ ```bash
25
+ pip install -e ".[dev]"
26
+ ```
27
+
28
+ ## Client Classes
29
+
30
+ | Client | Purpose |
31
+ |--------|---------|
32
+ | `TaskClient` | Task CRUD operations (create, update, delete, archive) |
33
+ | `LifecycleClient` | Status changes (start, complete, pause, cancel, reopen) |
34
+ | `RelationshipClient` | Dependencies and tags |
35
+ | `QueryClient` | List tasks, get task details |
36
+ | `AnalyticsClient` | Statistics and Gantt data |
37
+ | `NotesClient` | Task notes (markdown) |
38
+ | `AuditClient` | Audit log access |
39
+ | `BaseApiClient` | Base class with common HTTP logic |
40
+
41
+ ## Usage Example
42
+
43
+ ```python
44
+ from taskdog_client import TaskClient, LifecycleClient, QueryClient
45
+
46
+ # Create clients
47
+ base_url = "http://127.0.0.1:8000"
48
+ api_key = "your-api-key" # Optional, for authenticated servers
49
+
50
+ task_client = TaskClient(base_url, api_key)
51
+ lifecycle_client = LifecycleClient(base_url, api_key)
52
+ query_client = QueryClient(base_url, api_key)
53
+
54
+ # Create a task
55
+ task = task_client.create_task(
56
+ name="My Task",
57
+ priority=100,
58
+ estimated_duration=8.0,
59
+ tags=["backend"]
60
+ )
61
+ print(f"Created task: {task.id}")
62
+
63
+ # List tasks
64
+ tasks = query_client.list_tasks(status="pending")
65
+ for t in tasks:
66
+ print(f"- {t.name} (ID: {t.id})")
67
+
68
+ # Start a task
69
+ lifecycle_client.start_task(task.id)
70
+
71
+ # Complete a task
72
+ lifecycle_client.complete_task(task.id)
73
+ ```
74
+
75
+ ## Error Handling
76
+
77
+ Clients raise exceptions from `taskdog_core.domain.exceptions`:
78
+
79
+ ```python
80
+ from taskdog_core.domain.exceptions import TaskNotFoundException, TaskValidationError
81
+
82
+ try:
83
+ task_client.get_task(999)
84
+ except TaskNotFoundException:
85
+ print("Task not found")
86
+ except TaskValidationError as e:
87
+ print(f"Validation error: {e}")
88
+ ```
89
+
90
+ ## Configuration
91
+
92
+ Clients accept optional configuration:
93
+
94
+ ```python
95
+ from taskdog_client import TaskClient
96
+
97
+ # Basic usage (no auth)
98
+ client = TaskClient("http://127.0.0.1:8000")
99
+
100
+ # With API key authentication
101
+ client = TaskClient(
102
+ base_url="http://127.0.0.1:8000",
103
+ api_key="your-secret-key"
104
+ )
105
+
106
+ # Custom timeout (in seconds)
107
+ client = TaskClient(
108
+ base_url="http://127.0.0.1:8000",
109
+ timeout=30.0
110
+ )
111
+ ```
112
+
113
+ ## Dependencies
114
+
115
+ - `taskdog-core`: Domain DTOs and exceptions
116
+ - `httpx`: HTTP client library
117
+
118
+ ## Related Packages
119
+
120
+ - [taskdog-core](../taskdog-core/): Domain DTOs used by this package
121
+ - [taskdog-server](../taskdog-server/): API server this client connects to
122
+ - [taskdog-ui](../taskdog-ui/): CLI/TUI that uses this client
123
+ - [taskdog-mcp](../taskdog-mcp/): MCP server that uses this client
124
+
125
+ ## Testing
126
+
127
+ ```bash
128
+ pytest tests/
129
+ ```
130
+
131
+ ## License
132
+
133
+ MIT
@@ -0,0 +1,64 @@
1
+ [project]
2
+ name = "taskdog-client"
3
+ version = "0.18.1"
4
+ description = "HTTP API client for Taskdog server"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ authors = [
8
+ { name = "Kohei Wada" }
9
+ ]
10
+ license = { text = "MIT" }
11
+ keywords = ["task", "management", "api-client"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ ]
19
+
20
+ dependencies = [
21
+ "taskdog-core==0.18.1",
22
+ "httpx>=0.27.0",
23
+ "websockets>=14.0",
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ dev = [
28
+ "pytest>=7.0.0",
29
+ "pytest-cov>=4.0.0",
30
+ "pytest-asyncio>=1.0.0",
31
+ "mypy>=1.0.0",
32
+ "ruff>=0.7.0",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/Kohei-Wada/taskdog"
37
+ Repository = "https://github.com/Kohei-Wada/taskdog"
38
+ "Bug Tracker" = "https://github.com/Kohei-Wada/taskdog/issues"
39
+
40
+ [build-system]
41
+ requires = ["setuptools>=68.0.0", "wheel"]
42
+ build-backend = "setuptools.build_meta"
43
+
44
+ [tool.setuptools]
45
+ packages = [
46
+ "taskdog_client",
47
+ "taskdog_client.converters",
48
+ "taskdog_client.websocket",
49
+ ]
50
+ package-dir = { "" = "src" }
51
+
52
+ [tool.setuptools.package-data]
53
+ taskdog_client = ["py.typed"]
54
+
55
+ [tool.mypy]
56
+ python_version = "3.13"
57
+ strict = true
58
+ warn_unused_ignores = false
59
+
60
+ [tool.pytest.ini_options]
61
+ testpaths = ["tests"]
62
+ python_files = ["test_*.py"]
63
+ python_classes = ["Test*"]
64
+ python_functions = ["test_*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ """HTTP and WebSocket client for Taskdog server.
2
+
3
+ This package provides type-safe HTTP and WebSocket clients for communicating with
4
+ the Taskdog API server. It handles authentication, error mapping,
5
+ and response conversion to domain DTOs.
6
+ """
7
+
8
+ from taskdog_client.analytics_client import AnalyticsClient
9
+ from taskdog_client.audit_client import AuditClient
10
+ from taskdog_client.base_client import BaseApiClient
11
+ from taskdog_client.lifecycle_client import LifecycleClient
12
+ from taskdog_client.notes_client import NotesClient
13
+ from taskdog_client.query_client import QueryClient
14
+ from taskdog_client.relationship_client import RelationshipClient
15
+ from taskdog_client.task_client import TaskClient
16
+ from taskdog_client.taskdog_api_client import TaskdogApiClient
17
+ from taskdog_client.websocket import ConnectionState, WebSocketClient
18
+
19
+ __all__ = [
20
+ "AnalyticsClient",
21
+ "AuditClient",
22
+ "BaseApiClient",
23
+ "ConnectionState",
24
+ "LifecycleClient",
25
+ "NotesClient",
26
+ "QueryClient",
27
+ "RelationshipClient",
28
+ "TaskClient",
29
+ "TaskdogApiClient",
30
+ "WebSocketClient",
31
+ ]
@@ -0,0 +1,97 @@
1
+ """Analytics and optimization client."""
2
+
3
+ from datetime import datetime
4
+
5
+ from taskdog_client.base_client import BaseApiClient
6
+ from taskdog_client.converters import (
7
+ convert_to_optimization_output,
8
+ convert_to_statistics_output,
9
+ )
10
+ from taskdog_core.application.dto.optimization_output import OptimizationOutput
11
+ from taskdog_core.application.dto.statistics_output import StatisticsOutput
12
+
13
+
14
+ class AnalyticsClient:
15
+ """Client for analytics and schedule optimization.
16
+
17
+ Operations:
18
+ - Calculate statistics
19
+ - Optimize schedules
20
+ - Get algorithm metadata
21
+ """
22
+
23
+ def __init__(self, base_client: BaseApiClient):
24
+ """Initialize analytics client.
25
+
26
+ Args:
27
+ base_client: Base API client for HTTP operations
28
+ """
29
+ self._base = base_client
30
+
31
+ def calculate_statistics(self, period: str = "all") -> StatisticsOutput:
32
+ """Calculate task statistics.
33
+
34
+ Args:
35
+ period: Time period (all, 7d, 30d)
36
+
37
+ Returns:
38
+ StatisticsOutput with statistics data
39
+
40
+ Raises:
41
+ TaskValidationError: If period is invalid
42
+ """
43
+ data = self._base._request_json("get", f"/api/v1/statistics?period={period}")
44
+ return convert_to_statistics_output(data)
45
+
46
+ def optimize_schedule(
47
+ self,
48
+ algorithm: str,
49
+ start_date: datetime | None,
50
+ max_hours_per_day: float,
51
+ force_override: bool = True,
52
+ task_ids: list[int] | None = None,
53
+ include_all_days: bool = False,
54
+ ) -> OptimizationOutput:
55
+ """Optimize task schedules.
56
+
57
+ Args:
58
+ algorithm: Algorithm name (required)
59
+ start_date: Optimization start date (None = server current time)
60
+ max_hours_per_day: Maximum hours per day (required)
61
+ force_override: Force override existing schedules
62
+ task_ids: Specific task IDs to optimize (None means all schedulable tasks)
63
+ include_all_days: If True, schedule tasks on weekends and holidays too (default: False)
64
+
65
+ Returns:
66
+ OptimizationOutput with optimization results
67
+
68
+ Raises:
69
+ TaskValidationError: If validation fails
70
+ TaskNotFoundException: If any specified task_id does not exist
71
+ NoSchedulableTasksError: If no tasks can be scheduled
72
+ """
73
+ payload: dict[str, str | float | bool | list[int] | None] = {
74
+ "algorithm": algorithm,
75
+ "start_date": start_date.isoformat() if start_date else None,
76
+ "max_hours_per_day": max_hours_per_day,
77
+ "force_override": force_override,
78
+ "include_all_days": include_all_days,
79
+ }
80
+
81
+ # Only include task_ids if it's not None
82
+ if task_ids is not None:
83
+ payload["task_ids"] = task_ids
84
+
85
+ data = self._base._request_json("post", "/api/v1/optimize", json=payload)
86
+ return convert_to_optimization_output(data)
87
+
88
+ def get_algorithm_metadata(self) -> list[tuple[str, str, str]]:
89
+ """Get available optimization algorithms.
90
+
91
+ Returns:
92
+ List of (name, display_name, description) tuples
93
+ """
94
+ data = self._base._request_json("get", "/api/v1/algorithms")
95
+ return [
96
+ (algo["name"], algo["display_name"], algo["description"]) for algo in data
97
+ ]
@@ -0,0 +1,120 @@
1
+ """Audit log client."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from taskdog_client.base_client import BaseApiClient
7
+ from taskdog_core.application.dto.audit_log_dto import (
8
+ AuditLogListOutput,
9
+ AuditLogOutput,
10
+ )
11
+
12
+
13
+ class AuditClient:
14
+ """Client for audit log operations.
15
+
16
+ Operations:
17
+ - List audit logs with filtering
18
+ - Get single audit log by ID
19
+ """
20
+
21
+ def __init__(self, base_client: BaseApiClient):
22
+ """Initialize audit client.
23
+
24
+ Args:
25
+ base_client: Base API client for HTTP operations
26
+ """
27
+ self._base = base_client
28
+
29
+ def list_audit_logs(
30
+ self,
31
+ client_filter: str | None = None,
32
+ operation: str | None = None,
33
+ resource_type: str | None = None,
34
+ resource_id: int | None = None,
35
+ success: bool | None = None,
36
+ start_date: datetime | None = None,
37
+ end_date: datetime | None = None,
38
+ limit: int = 100,
39
+ offset: int = 0,
40
+ ) -> AuditLogListOutput:
41
+ """List audit logs with optional filtering.
42
+
43
+ Args:
44
+ client_filter: Filter by client name
45
+ operation: Filter by operation type
46
+ resource_type: Filter by resource type
47
+ resource_id: Filter by resource ID
48
+ success: Filter by success status
49
+ start_date: Filter logs after this datetime
50
+ end_date: Filter logs before this datetime
51
+ limit: Maximum number of logs to return
52
+ offset: Number of logs to skip for pagination
53
+
54
+ Returns:
55
+ AuditLogListOutput with logs and pagination info
56
+ """
57
+ params: dict[str, str | int] = {"limit": limit, "offset": offset}
58
+
59
+ if client_filter is not None:
60
+ params["client"] = client_filter
61
+ if operation is not None:
62
+ params["operation"] = operation
63
+ if resource_type is not None:
64
+ params["resource_type"] = resource_type
65
+ if resource_id is not None:
66
+ params["resource_id"] = resource_id
67
+ if success is not None:
68
+ params["success"] = str(success).lower()
69
+ if start_date is not None:
70
+ params["start_date"] = start_date.isoformat()
71
+ if end_date is not None:
72
+ params["end_date"] = end_date.isoformat()
73
+
74
+ data = self._base._request_json("get", "/api/v1/audit-logs", params=params)
75
+ return self._convert_to_list_output(data)
76
+
77
+ def get_audit_log(self, log_id: int) -> AuditLogOutput:
78
+ """Get a single audit log entry by ID.
79
+
80
+ Args:
81
+ log_id: ID of the audit log entry to retrieve
82
+
83
+ Returns:
84
+ AuditLogOutput with the audit log details
85
+ """
86
+ data = self._base._request_json("get", f"/api/v1/audit-logs/{log_id}")
87
+ return self._convert_to_output(data)
88
+
89
+ def _convert_to_output(self, data: dict[str, Any]) -> AuditLogOutput:
90
+ """Convert API response to AuditLogOutput DTO."""
91
+ # Parse timestamp with error handling for malformed data
92
+ try:
93
+ timestamp = datetime.fromisoformat(data["timestamp"])
94
+ except (ValueError, KeyError):
95
+ # Fallback to current time if timestamp is invalid or missing
96
+ timestamp = datetime.now()
97
+
98
+ return AuditLogOutput(
99
+ id=data["id"],
100
+ timestamp=timestamp,
101
+ client_name=data.get("client_name"),
102
+ operation=data["operation"],
103
+ resource_type=data["resource_type"],
104
+ resource_id=data.get("resource_id"),
105
+ resource_name=data.get("resource_name"),
106
+ old_values=data.get("old_values"),
107
+ new_values=data.get("new_values"),
108
+ success=data["success"],
109
+ error_message=data.get("error_message"),
110
+ )
111
+
112
+ def _convert_to_list_output(self, data: dict[str, Any]) -> AuditLogListOutput:
113
+ """Convert API response to AuditLogListOutput DTO."""
114
+ logs = [self._convert_to_output(log) for log in data["logs"]]
115
+ return AuditLogListOutput(
116
+ logs=logs,
117
+ total_count=data["total_count"],
118
+ limit=data["limit"],
119
+ offset=data["offset"],
120
+ )