pyvikunja 0.17__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 +62 -29
- pyvikunja/models/project.py +2 -2
- {pyvikunja-0.17.dist-info → pyvikunja-0.18.dist-info}/METADATA +3 -2
- {pyvikunja-0.17.dist-info → pyvikunja-0.18.dist-info}/RECORD +7 -7
- {pyvikunja-0.17.dist-info → pyvikunja-0.18.dist-info}/WHEEL +1 -1
- {pyvikunja-0.17.dist-info → pyvikunja-0.18.dist-info/licenses}/LICENSE +0 -0
- {pyvikunja-0.17.dist-info → pyvikunja-0.18.dist-info}/top_level.txt +0 -0
pyvikunja/api.py
CHANGED
@@ -64,14 +64,18 @@ class VikunjaAPI:
|
|
64
64
|
return f"{host}/api/v1"
|
65
65
|
return host
|
66
66
|
|
67
|
-
|
68
67
|
async def _request(self, method: str, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None) -> \
|
69
|
-
Optional[Any]:
|
68
|
+
Optional[Dict[str, Any]]:
|
70
69
|
url = f"{self.api_base_url}{endpoint}"
|
71
70
|
try:
|
72
71
|
response = await self.client.request(method, url, headers=self.headers, params=params, json=data)
|
73
72
|
response.raise_for_status()
|
74
|
-
|
73
|
+
|
74
|
+
# Return JSON data and headers
|
75
|
+
return {
|
76
|
+
"data": response.json(),
|
77
|
+
"headers": response.headers
|
78
|
+
}
|
75
79
|
except httpx.HTTPStatusError as e:
|
76
80
|
logger.error(f"HTTP error occurred: {e.response.status_code} | {e.response.text} | URL: {url}")
|
77
81
|
except httpx.RequestError as e:
|
@@ -96,76 +100,105 @@ class VikunjaAPI:
|
|
96
100
|
except httpx.HTTPError as e:
|
97
101
|
raise e
|
98
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
|
+
|
99
122
|
# Projects
|
100
|
-
async def get_projects(self
|
101
|
-
|
102
|
-
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]
|
103
126
|
|
104
127
|
async def get_project(self, project_id: int) -> Optional[Project]:
|
105
128
|
response = await self._request("GET", f"/projects/{project_id}")
|
106
|
-
return Project(self, response)
|
129
|
+
return Project(self, response['data'])
|
107
130
|
|
108
131
|
async def create_project(self, project: Dict) -> Optional[Dict]:
|
109
|
-
|
132
|
+
result = await self._request("PUT", "/projects", data=project)
|
133
|
+
return result['data']
|
110
134
|
|
111
135
|
async def update_project(self, project_id: int, project: Dict) -> Optional[Dict]:
|
112
|
-
|
136
|
+
result = await self._request("POST", f"/projects/{project_id}", data=project)
|
137
|
+
return result['data']
|
113
138
|
|
114
139
|
async def delete_project(self, project_id: int) -> Optional[Dict]:
|
115
|
-
|
140
|
+
result = await self._request("DELETE", f"/projects/{project_id}")
|
141
|
+
return result['data']
|
116
142
|
|
117
143
|
# Tasks
|
118
|
-
async def get_tasks(self, project_id: int
|
119
|
-
response = await self.
|
120
|
-
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")
|
121
146
|
return [Task(self, task_data) for task_data in response or []]
|
122
147
|
|
123
148
|
async def get_task(self, task_id: int) -> Task:
|
124
149
|
data = await self._request("GET", f"/tasks/{task_id}")
|
125
|
-
return Task(self, data)
|
150
|
+
return Task(self, data['data'])
|
126
151
|
|
127
152
|
async def create_task(self, project_id: int, task: Dict) -> Optional[Dict]:
|
128
|
-
|
153
|
+
result = await self._request("PUT", f"/projects/{project_id}/tasks", data=task)
|
154
|
+
return result['data']
|
129
155
|
|
130
156
|
async def update_task(self, task_id: int, task: Dict) -> Optional[Dict]:
|
131
|
-
|
157
|
+
result = await self._request("POST", f"/tasks/{task_id}", data=task)
|
158
|
+
return result['data']
|
132
159
|
|
133
160
|
async def delete_task(self, task_id: int) -> Optional[Dict]:
|
134
|
-
|
161
|
+
result = await self._request("DELETE", f"/tasks/{task_id}")
|
162
|
+
return result['data']
|
135
163
|
|
136
164
|
# Labels
|
137
|
-
async def get_labels(self
|
138
|
-
response = await self.
|
165
|
+
async def get_labels(self) -> List[Label]:
|
166
|
+
response = await self.get_paginated_data("/labels")
|
139
167
|
return [Label(label_data) for label_data in response or []]
|
140
168
|
|
141
169
|
async def get_label(self, label_id: int) -> Optional[Dict]:
|
142
|
-
|
170
|
+
result = await self._request("GET", f"/labels/{label_id}")
|
171
|
+
return result['data']
|
143
172
|
|
144
173
|
async def create_label(self, label: Dict) -> Optional[Dict]:
|
145
|
-
|
174
|
+
result = await self._request("PUT", "/labels", data=label)
|
175
|
+
return result['data']
|
146
176
|
|
147
177
|
async def update_label(self, label_id: int, label: Dict) -> Optional[Dict]:
|
148
|
-
|
178
|
+
result = await self._request("PUT", f"/labels/{label_id}", data=label)
|
179
|
+
return result['data']
|
149
180
|
|
150
181
|
async def delete_label(self, label_id: int) -> Optional[Dict]:
|
151
|
-
|
182
|
+
result = await self._request("DELETE", f"/labels/{label_id}")
|
183
|
+
return result['data']
|
152
184
|
|
153
185
|
# Teams
|
154
|
-
async def get_teams(self
|
155
|
-
response = await self.
|
186
|
+
async def get_teams(self) -> List[Team]:
|
187
|
+
response = await self.get_paginated_data("/teams")
|
156
188
|
return [Team(self, team_data) for team_data in response or []]
|
157
189
|
|
158
190
|
async def get_team(self, team_id: int) -> Optional[Team]:
|
159
191
|
response = await self._request("GET", f"/teams/{team_id}")
|
160
|
-
return Team(self, response)
|
192
|
+
return Team(self, response['data'])
|
161
193
|
|
162
194
|
async def create_team(self, team: Dict) -> Optional[Team]:
|
163
195
|
response = await self._request("PUT", "/teams", data=team)
|
164
|
-
return Team(self, response)
|
196
|
+
return Team(self, response['data'])
|
165
197
|
|
166
198
|
async def update_team(self, team_id: int, team: Dict) -> Optional[Team]:
|
167
199
|
response = await self._request("POST", f"/teams/{team_id}", data=team)
|
168
|
-
return Team(self, response)
|
200
|
+
return Team(self, response['data'])
|
169
201
|
|
170
202
|
async def delete_team(self, team_id: int) -> Optional[Team]:
|
171
|
-
|
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
|