pyvikunja 0.16__py3-none-any.whl → 0.18__py3-none-any.whl
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.
- pyvikunja/api.py +68 -30
- pyvikunja/models/project.py +2 -2
- {pyvikunja-0.16.dist-info → pyvikunja-0.18.dist-info}/METADATA +3 -2
- {pyvikunja-0.16.dist-info → pyvikunja-0.18.dist-info}/RECORD +7 -7
- {pyvikunja-0.16.dist-info → pyvikunja-0.18.dist-info}/WHEEL +1 -1
- {pyvikunja-0.16.dist-info → pyvikunja-0.18.dist-info/licenses}/LICENSE +0 -0
- {pyvikunja-0.16.dist-info → pyvikunja-0.18.dist-info}/top_level.txt +0 -0
pyvikunja/api.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
from functools import cached_property
|
2
3
|
from typing import List, Dict, Any, Optional
|
3
4
|
from urllib.parse import urlparse, urlunparse
|
4
5
|
|
@@ -29,7 +30,11 @@ class VikunjaAPI:
|
|
29
30
|
self.api_base_url = self._normalize_api_base_url(self.host)
|
30
31
|
self.headers = {"Authorization": f"Bearer {token}"}
|
31
32
|
self.strict_ssl = strict_ssl
|
32
|
-
|
33
|
+
|
34
|
+
@cached_property
|
35
|
+
def client(self) -> httpx.AsyncClient:
|
36
|
+
"""Lazily instantiate the HTTP client when first accessed."""
|
37
|
+
return httpx.AsyncClient(verify=self.strict_ssl)
|
33
38
|
|
34
39
|
@property
|
35
40
|
def web_ui_link(self):
|
@@ -59,14 +64,18 @@ class VikunjaAPI:
|
|
59
64
|
return f"{host}/api/v1"
|
60
65
|
return host
|
61
66
|
|
62
|
-
|
63
67
|
async def _request(self, method: str, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None) -> \
|
64
|
-
Optional[Any]:
|
68
|
+
Optional[Dict[str, Any]]:
|
65
69
|
url = f"{self.api_base_url}{endpoint}"
|
66
70
|
try:
|
67
71
|
response = await self.client.request(method, url, headers=self.headers, params=params, json=data)
|
68
72
|
response.raise_for_status()
|
69
|
-
|
73
|
+
|
74
|
+
# Return JSON data and headers
|
75
|
+
return {
|
76
|
+
"data": response.json(),
|
77
|
+
"headers": response.headers
|
78
|
+
}
|
70
79
|
except httpx.HTTPStatusError as e:
|
71
80
|
logger.error(f"HTTP error occurred: {e.response.status_code} | {e.response.text} | URL: {url}")
|
72
81
|
except httpx.RequestError as e:
|
@@ -91,76 +100,105 @@ class VikunjaAPI:
|
|
91
100
|
except httpx.HTTPError as e:
|
92
101
|
raise e
|
93
102
|
|
103
|
+
async def get_paginated_data(self, endpoint: str) -> List[Dict[str, Any]]:
|
104
|
+
all_data = []
|
105
|
+
page = 1
|
106
|
+
per_page = 20
|
107
|
+
|
108
|
+
while True:
|
109
|
+
response = await self._request("GET", endpoint, params={"page": page, "per_page": per_page})
|
110
|
+
if not response:
|
111
|
+
break
|
112
|
+
|
113
|
+
all_data.extend(response["data"])
|
114
|
+
total_pages = int(response["headers"].get("x-pagination-total-pages", 1))
|
115
|
+
if page >= total_pages:
|
116
|
+
break
|
117
|
+
|
118
|
+
page += 1
|
119
|
+
|
120
|
+
return all_data
|
121
|
+
|
94
122
|
# Projects
|
95
|
-
async def get_projects(self
|
96
|
-
|
97
|
-
return [Project(self,
|
123
|
+
async def get_projects(self) -> List[Project]:
|
124
|
+
data = await self.get_paginated_data("/projects")
|
125
|
+
return [Project(self, project) for project in data]
|
98
126
|
|
99
127
|
async def get_project(self, project_id: int) -> Optional[Project]:
|
100
128
|
response = await self._request("GET", f"/projects/{project_id}")
|
101
|
-
return Project(self, response)
|
129
|
+
return Project(self, response['data'])
|
102
130
|
|
103
131
|
async def create_project(self, project: Dict) -> Optional[Dict]:
|
104
|
-
|
132
|
+
result = await self._request("PUT", "/projects", data=project)
|
133
|
+
return result['data']
|
105
134
|
|
106
135
|
async def update_project(self, project_id: int, project: Dict) -> Optional[Dict]:
|
107
|
-
|
136
|
+
result = await self._request("POST", f"/projects/{project_id}", data=project)
|
137
|
+
return result['data']
|
108
138
|
|
109
139
|
async def delete_project(self, project_id: int) -> Optional[Dict]:
|
110
|
-
|
140
|
+
result = await self._request("DELETE", f"/projects/{project_id}")
|
141
|
+
return result['data']
|
111
142
|
|
112
143
|
# Tasks
|
113
|
-
async def get_tasks(self, project_id: int
|
114
|
-
response = await self.
|
115
|
-
params={"page": page, "per_page": per_page})
|
144
|
+
async def get_tasks(self, project_id: int) -> List[Task]:
|
145
|
+
response = await self.get_paginated_data(f"/projects/{project_id}/tasks")
|
116
146
|
return [Task(self, task_data) for task_data in response or []]
|
117
147
|
|
118
148
|
async def get_task(self, task_id: int) -> Task:
|
119
149
|
data = await self._request("GET", f"/tasks/{task_id}")
|
120
|
-
return Task(self, data)
|
150
|
+
return Task(self, data['data'])
|
121
151
|
|
122
152
|
async def create_task(self, project_id: int, task: Dict) -> Optional[Dict]:
|
123
|
-
|
153
|
+
result = await self._request("PUT", f"/projects/{project_id}/tasks", data=task)
|
154
|
+
return result['data']
|
124
155
|
|
125
156
|
async def update_task(self, task_id: int, task: Dict) -> Optional[Dict]:
|
126
|
-
|
157
|
+
result = await self._request("POST", f"/tasks/{task_id}", data=task)
|
158
|
+
return result['data']
|
127
159
|
|
128
160
|
async def delete_task(self, task_id: int) -> Optional[Dict]:
|
129
|
-
|
161
|
+
result = await self._request("DELETE", f"/tasks/{task_id}")
|
162
|
+
return result['data']
|
130
163
|
|
131
164
|
# Labels
|
132
|
-
async def get_labels(self
|
133
|
-
response = await self.
|
165
|
+
async def get_labels(self) -> List[Label]:
|
166
|
+
response = await self.get_paginated_data("/labels")
|
134
167
|
return [Label(label_data) for label_data in response or []]
|
135
168
|
|
136
169
|
async def get_label(self, label_id: int) -> Optional[Dict]:
|
137
|
-
|
170
|
+
result = await self._request("GET", f"/labels/{label_id}")
|
171
|
+
return result['data']
|
138
172
|
|
139
173
|
async def create_label(self, label: Dict) -> Optional[Dict]:
|
140
|
-
|
174
|
+
result = await self._request("PUT", "/labels", data=label)
|
175
|
+
return result['data']
|
141
176
|
|
142
177
|
async def update_label(self, label_id: int, label: Dict) -> Optional[Dict]:
|
143
|
-
|
178
|
+
result = await self._request("PUT", f"/labels/{label_id}", data=label)
|
179
|
+
return result['data']
|
144
180
|
|
145
181
|
async def delete_label(self, label_id: int) -> Optional[Dict]:
|
146
|
-
|
182
|
+
result = await self._request("DELETE", f"/labels/{label_id}")
|
183
|
+
return result['data']
|
147
184
|
|
148
185
|
# Teams
|
149
|
-
async def get_teams(self
|
150
|
-
response = await self.
|
186
|
+
async def get_teams(self) -> List[Team]:
|
187
|
+
response = await self.get_paginated_data("/teams")
|
151
188
|
return [Team(self, team_data) for team_data in response or []]
|
152
189
|
|
153
190
|
async def get_team(self, team_id: int) -> Optional[Team]:
|
154
191
|
response = await self._request("GET", f"/teams/{team_id}")
|
155
|
-
return Team(self, response)
|
192
|
+
return Team(self, response['data'])
|
156
193
|
|
157
194
|
async def create_team(self, team: Dict) -> Optional[Team]:
|
158
195
|
response = await self._request("PUT", "/teams", data=team)
|
159
|
-
return Team(self, response)
|
196
|
+
return Team(self, response['data'])
|
160
197
|
|
161
198
|
async def update_team(self, team_id: int, team: Dict) -> Optional[Team]:
|
162
199
|
response = await self._request("POST", f"/teams/{team_id}", data=team)
|
163
|
-
return Team(self, response)
|
200
|
+
return Team(self, response['data'])
|
164
201
|
|
165
202
|
async def delete_team(self, team_id: int) -> Optional[Team]:
|
166
|
-
|
203
|
+
result = await self._request("DELETE", f"/teams/{team_id}")
|
204
|
+
return result['data']
|
pyvikunja/models/project.py
CHANGED
@@ -15,8 +15,8 @@ class Project(BaseModel):
|
|
15
15
|
self.hex_color: Optional[str] = data.get('hex_color')
|
16
16
|
self.owner: 'User' = User(data.get('owner') or {})
|
17
17
|
|
18
|
-
async def get_tasks(self
|
19
|
-
return await self.api.get_tasks(self.id
|
18
|
+
async def get_tasks(self) -> List['Task']:
|
19
|
+
return await self.api.get_tasks(self.id)
|
20
20
|
|
21
21
|
async def create_task(self, task: Dict) -> 'Task':
|
22
22
|
from pyvikunja.models.task import Task
|
@@ -1,8 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: pyvikunja
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.18
|
4
4
|
Summary: A Python wrapper for Vikunja API
|
5
5
|
Author: Joseph Shufflebotham
|
6
6
|
License: AGPL
|
7
7
|
License-File: LICENSE
|
8
8
|
Requires-Dist: httpx==0.28.1
|
9
|
+
Dynamic: license-file
|
@@ -1,15 +1,15 @@
|
|
1
1
|
pyvikunja/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
pyvikunja/api.py,sha256=
|
2
|
+
pyvikunja/api.py,sha256=30sosl6wNWT6nTa7swLFSv9q9G9AlQ6Rq_FGDH7mLBs,7887
|
3
3
|
pyvikunja/models/label.py,sha256=cOOuVYDVDMmK96Ev22EDgWwXljkAonQNL4XsTMay4K4,467
|
4
4
|
pyvikunja/models/models.py,sha256=VmnBKs_DhcF1CNE3b2MhH4harwZo1697cMhTXetLGWk,822
|
5
|
-
pyvikunja/models/project.py,sha256=
|
5
|
+
pyvikunja/models/project.py,sha256=wFtnyuLTteDUQrUWeImR7DXTI0XYhCSiQGlmwaIzdwM,1128
|
6
6
|
pyvikunja/models/task.py,sha256=8KUoN0SuTRkJq_cHdsuIeQHUiNgOiWCLcWFVN9NJH5c,7806
|
7
7
|
pyvikunja/models/team.py,sha256=0Z3828Cm3nNuNr1z2on63fLYAVW325_SQKi5RmqXf-I,559
|
8
8
|
pyvikunja/models/user.py,sha256=36duFNyGXKzlwqO0d6FA-C1KTCci2sIMU1IBIF-N_LM,310
|
9
9
|
pyvikunja/models/enum/repeat_mode.py,sha256=xkDPfYeRz342ovqro1UVBAoegpPgpZQYTnyo2sf6R7I,112
|
10
10
|
pyvikunja/models/enum/task_priority.py,sha256=aVz1HEofIqwUkXG0vYm58n_vLNbgqtbvHuQ5K-YQqA0,119
|
11
|
-
pyvikunja-0.
|
12
|
-
pyvikunja-0.
|
13
|
-
pyvikunja-0.
|
14
|
-
pyvikunja-0.
|
15
|
-
pyvikunja-0.
|
11
|
+
pyvikunja-0.18.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
12
|
+
pyvikunja-0.18.dist-info/METADATA,sha256=SvUsJ9e0YkFFDAcrVBM37n9ivnciqJdOjTQo5mtWy3s,210
|
13
|
+
pyvikunja-0.18.dist-info/WHEEL,sha256=L0N565qmK-3nM2eBoMNFszYJ_MTx03_tQ0CQu1bHLYo,91
|
14
|
+
pyvikunja-0.18.dist-info/top_level.txt,sha256=WVV9zgxUBuWOkUY1t_U7zI0paWWTVelKYB4vjsiKsks,10
|
15
|
+
pyvikunja-0.18.dist-info/RECORD,,
|
File without changes
|
File without changes
|