tallyfy 1.0.4__py3-none-any.whl → 1.0.6__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.
Potentially problematic release.
This version of tallyfy might be problematic. Click here for more details.
- tallyfy/__init__.py +8 -4
- tallyfy/core.py +11 -8
- tallyfy/form_fields_management/__init__.py +70 -0
- tallyfy/form_fields_management/base.py +109 -0
- tallyfy/form_fields_management/crud_operations.py +234 -0
- tallyfy/form_fields_management/options_management.py +222 -0
- tallyfy/form_fields_management/suggestions.py +411 -0
- tallyfy/models.py +3 -1
- tallyfy/task_management/__init__.py +81 -0
- tallyfy/task_management/base.py +125 -0
- tallyfy/task_management/creation.py +221 -0
- tallyfy/task_management/retrieval.py +211 -0
- tallyfy/task_management/search.py +196 -0
- tallyfy/template_management/__init__.py +85 -0
- tallyfy/template_management/analysis.py +1093 -0
- tallyfy/template_management/automation.py +469 -0
- tallyfy/template_management/base.py +56 -0
- tallyfy/template_management/basic_operations.py +477 -0
- tallyfy/template_management/health_assessment.py +763 -0
- tallyfy/user_management/__init__.py +69 -0
- tallyfy/user_management/base.py +146 -0
- tallyfy/user_management/invitation.py +286 -0
- tallyfy/user_management/retrieval.py +339 -0
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/METADATA +120 -56
- tallyfy-1.0.6.dist-info/RECORD +28 -0
- tallyfy/BUILD.md +0 -5
- tallyfy/form_fields_management.py +0 -582
- tallyfy/task_management.py +0 -356
- tallyfy/template_management.py +0 -2607
- tallyfy/user_management.py +0 -235
- tallyfy-1.0.4.dist-info/RECORD +0 -13
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/WHEEL +0 -0
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/licenses/LICENSE +0 -0
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/top_level.txt +0 -0
tallyfy/task_management.py
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Task and process management functionality for Tallyfy SDK
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import List, Optional
|
|
6
|
-
from .models import Task, Run, SearchResult, TaskOwners, TallyfyError
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TaskManagement:
|
|
10
|
-
"""Handles task and process management operations"""
|
|
11
|
-
|
|
12
|
-
def __init__(self, sdk):
|
|
13
|
-
self.sdk = sdk
|
|
14
|
-
|
|
15
|
-
def get_my_tasks(self, org_id: str) -> List[Task]:
|
|
16
|
-
"""
|
|
17
|
-
Get all tasks assigned to the current user in the organization.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
org_id: Organization ID
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
List of Task objects assigned to the current user
|
|
24
|
-
|
|
25
|
-
Raises:
|
|
26
|
-
TallyfyError: If the request fails
|
|
27
|
-
"""
|
|
28
|
-
try:
|
|
29
|
-
endpoint = f"organizations/{org_id}/me/tasks"
|
|
30
|
-
response_data = self.sdk._make_request('GET', endpoint)
|
|
31
|
-
|
|
32
|
-
if isinstance(response_data, dict) and 'data' in response_data:
|
|
33
|
-
tasks_data = response_data['data']
|
|
34
|
-
return [Task.from_dict(task_data) for task_data in tasks_data]
|
|
35
|
-
else:
|
|
36
|
-
if isinstance(response_data, list):
|
|
37
|
-
return [Task.from_dict(task_data) for task_data in response_data]
|
|
38
|
-
else:
|
|
39
|
-
self.sdk.logger.warning("Unexpected response format for tasks")
|
|
40
|
-
return []
|
|
41
|
-
|
|
42
|
-
except TallyfyError as e:
|
|
43
|
-
self.sdk.logger.error(f"Failed to get my tasks for org {org_id}: {e}")
|
|
44
|
-
raise
|
|
45
|
-
|
|
46
|
-
def get_user_tasks(self, org_id: str, user_id: int) -> List[Task]:
|
|
47
|
-
"""
|
|
48
|
-
Get all tasks assigned to the given user in the organization.
|
|
49
|
-
|
|
50
|
-
Args:
|
|
51
|
-
org_id: Organization ID
|
|
52
|
-
user_id: User ID
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
List of Task objects assigned to the given user ID
|
|
56
|
-
|
|
57
|
-
Raises:
|
|
58
|
-
TallyfyError: If the request fails
|
|
59
|
-
"""
|
|
60
|
-
try:
|
|
61
|
-
endpoint = f"organizations/{org_id}/users/{user_id}/tasks?per_page=100&sort_by=newest&status=all&with=run,threads_count,step,tags,folders,member_watchers.watcher"
|
|
62
|
-
response_data = self.sdk._make_request('GET', endpoint)
|
|
63
|
-
|
|
64
|
-
if isinstance(response_data, dict) and 'data' in response_data:
|
|
65
|
-
tasks_data = response_data['data']
|
|
66
|
-
return [Task.from_dict(task_data) for task_data in tasks_data]
|
|
67
|
-
else:
|
|
68
|
-
if isinstance(response_data, list):
|
|
69
|
-
return [Task.from_dict(task_data) for task_data in response_data]
|
|
70
|
-
else:
|
|
71
|
-
self.sdk.logger.warning("Unexpected response format for tasks")
|
|
72
|
-
return []
|
|
73
|
-
|
|
74
|
-
except TallyfyError as e:
|
|
75
|
-
self.sdk.logger.error(f"Failed to get user tasks for org {org_id}: {e}")
|
|
76
|
-
raise
|
|
77
|
-
|
|
78
|
-
def search_processes_by_name(self, org_id: str, process_name: str) -> str:
|
|
79
|
-
"""
|
|
80
|
-
Search for processes by name using the search endpoint.
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
org_id: Organization ID
|
|
84
|
-
process_name: Name or partial name of the process to search for
|
|
85
|
-
|
|
86
|
-
Returns:
|
|
87
|
-
Process ID of the found process
|
|
88
|
-
|
|
89
|
-
Raises:
|
|
90
|
-
TallyfyError: If no process found, multiple matches, or search fails
|
|
91
|
-
"""
|
|
92
|
-
try:
|
|
93
|
-
search_endpoint = f"organizations/{org_id}/search"
|
|
94
|
-
search_params = {
|
|
95
|
-
'on': 'process',
|
|
96
|
-
'per_page': '20',
|
|
97
|
-
'search': process_name
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
search_response = self.sdk._make_request('GET', search_endpoint, params=search_params)
|
|
101
|
-
|
|
102
|
-
if isinstance(search_response, dict) and 'process' in search_response:
|
|
103
|
-
process_data = search_response['process']
|
|
104
|
-
if 'data' in process_data and process_data['data']:
|
|
105
|
-
processes = process_data['data']
|
|
106
|
-
|
|
107
|
-
# First try exact match (case-insensitive)
|
|
108
|
-
exact_matches = [p for p in processes if p['name'].lower() == process_name.lower()]
|
|
109
|
-
if exact_matches:
|
|
110
|
-
return exact_matches[0]['id']
|
|
111
|
-
elif len(processes) == 1:
|
|
112
|
-
# Single search result, use it
|
|
113
|
-
return processes[0]['id']
|
|
114
|
-
else:
|
|
115
|
-
# Multiple matches found, provide helpful error with options
|
|
116
|
-
match_names = [f"'{p['name']}'" for p in processes[:5]] # Show max 5
|
|
117
|
-
raise TallyfyError(f"Multiple processes found matching '{process_name}': {', '.join(match_names)}. Please be more specific.")
|
|
118
|
-
else:
|
|
119
|
-
raise TallyfyError(f"No process found matching name: {process_name}")
|
|
120
|
-
else:
|
|
121
|
-
raise TallyfyError(f"Search failed for process name: {process_name}")
|
|
122
|
-
|
|
123
|
-
except TallyfyError as e:
|
|
124
|
-
self.sdk.logger.error(f"Failed to search for process '{process_name}': {e}")
|
|
125
|
-
raise
|
|
126
|
-
|
|
127
|
-
def get_tasks_for_process(self, org_id: str, process_id: Optional[str] = None, process_name: Optional[str] = None) -> List[Task]:
|
|
128
|
-
"""
|
|
129
|
-
Get all tasks for a given process (run).
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
org_id: Organization ID
|
|
133
|
-
process_id: Process (run) ID to get tasks for
|
|
134
|
-
process_name: Process (run) name to get tasks for (alternative to process_id)
|
|
135
|
-
|
|
136
|
-
Returns:
|
|
137
|
-
List of Task objects for the specified process
|
|
138
|
-
|
|
139
|
-
Raises:
|
|
140
|
-
TallyfyError: If the request fails
|
|
141
|
-
ValueError: If neither process_id nor process_name is provided
|
|
142
|
-
"""
|
|
143
|
-
if not process_id and not process_name:
|
|
144
|
-
raise ValueError("Either process_id or process_name must be provided")
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
# If process_name is provided but not process_id, search for the process first
|
|
148
|
-
if process_name and not process_id:
|
|
149
|
-
process_id = self.search_processes_by_name(org_id, process_name)
|
|
150
|
-
|
|
151
|
-
endpoint = f"organizations/{org_id}/runs/{process_id}/tasks"
|
|
152
|
-
response_data = self.sdk._make_request('GET', endpoint)
|
|
153
|
-
|
|
154
|
-
if isinstance(response_data, dict) and 'data' in response_data:
|
|
155
|
-
tasks_data = response_data['data']
|
|
156
|
-
return [Task.from_dict(task_data) for task_data in tasks_data]
|
|
157
|
-
else:
|
|
158
|
-
if isinstance(response_data, list):
|
|
159
|
-
return [Task.from_dict(task_data) for task_data in response_data]
|
|
160
|
-
else:
|
|
161
|
-
self.sdk.logger.warning("Unexpected response format for process tasks")
|
|
162
|
-
return []
|
|
163
|
-
|
|
164
|
-
except TallyfyError as e:
|
|
165
|
-
self.sdk.logger.error(f"Failed to get tasks for process {process_id or process_name}: {e}")
|
|
166
|
-
raise
|
|
167
|
-
|
|
168
|
-
def get_organization_runs(self, org_id: str, with_data: Optional[str] = None,
|
|
169
|
-
form_fields_values: Optional[bool] = None,
|
|
170
|
-
owners: Optional[str] = None, task_status: Optional[str] = None,
|
|
171
|
-
groups: Optional[str] = None, status: Optional[str] = None,
|
|
172
|
-
folder: Optional[str] = None, checklist_id: Optional[str] = None,
|
|
173
|
-
starred: Optional[bool] = None, run_type: Optional[str] = None,
|
|
174
|
-
tag: Optional[str] = None) -> List[Run]:
|
|
175
|
-
"""
|
|
176
|
-
Get all processes (runs) in the organization.
|
|
177
|
-
|
|
178
|
-
Args:
|
|
179
|
-
org_id: Organization ID
|
|
180
|
-
with_data: Comma-separated data to include (e.g., 'checklist,tasks,assets,tags')
|
|
181
|
-
form_fields_values: Include form field values
|
|
182
|
-
owners: Filter by specific member IDs
|
|
183
|
-
task_status: Filter by task status ('all', 'in-progress', 'completed')
|
|
184
|
-
groups: Filter by group IDs
|
|
185
|
-
status: Filter by process status ('active', 'problem', 'delayed', 'complete', 'archived')
|
|
186
|
-
folder: Filter by folder ID
|
|
187
|
-
checklist_id: Filter by template ID
|
|
188
|
-
starred: Filter by starred status
|
|
189
|
-
run_type: Filter by type ('procedure', 'form', 'document')
|
|
190
|
-
tag: Filter by tag ID
|
|
191
|
-
|
|
192
|
-
Returns:
|
|
193
|
-
List of Run objects
|
|
194
|
-
|
|
195
|
-
Raises:
|
|
196
|
-
TallyfyError: If the request fails
|
|
197
|
-
"""
|
|
198
|
-
try:
|
|
199
|
-
endpoint = f"organizations/{org_id}/runs"
|
|
200
|
-
params = {}
|
|
201
|
-
|
|
202
|
-
if with_data:
|
|
203
|
-
params['with'] = with_data
|
|
204
|
-
if form_fields_values is not None:
|
|
205
|
-
params['form_fields_values'] = 'true' if form_fields_values else 'false'
|
|
206
|
-
if owners:
|
|
207
|
-
params['owners'] = owners
|
|
208
|
-
if task_status:
|
|
209
|
-
params['task_status'] = task_status
|
|
210
|
-
if groups:
|
|
211
|
-
params['groups'] = groups
|
|
212
|
-
if status:
|
|
213
|
-
params['status'] = status
|
|
214
|
-
if folder:
|
|
215
|
-
params['folder'] = folder
|
|
216
|
-
if checklist_id:
|
|
217
|
-
params['checklist_id'] = checklist_id
|
|
218
|
-
if starred is not None:
|
|
219
|
-
params['starred'] = starred
|
|
220
|
-
if run_type:
|
|
221
|
-
params['type'] = run_type
|
|
222
|
-
if tag:
|
|
223
|
-
params['tag'] = tag
|
|
224
|
-
|
|
225
|
-
response_data = self.sdk._make_request('GET', endpoint, params=params)
|
|
226
|
-
|
|
227
|
-
if isinstance(response_data, dict) and 'data' in response_data:
|
|
228
|
-
runs_data = response_data['data']
|
|
229
|
-
return [Run.from_dict(run_data) for run_data in runs_data]
|
|
230
|
-
else:
|
|
231
|
-
if isinstance(response_data, list):
|
|
232
|
-
return [Run.from_dict(run_data) for run_data in response_data]
|
|
233
|
-
else:
|
|
234
|
-
self.sdk.logger.warning("Unexpected response format for runs")
|
|
235
|
-
return []
|
|
236
|
-
|
|
237
|
-
except TallyfyError as e:
|
|
238
|
-
self.sdk.logger.error(f"Failed to get organization runs for org {org_id}: {e}")
|
|
239
|
-
raise
|
|
240
|
-
|
|
241
|
-
def search(self, org_id: str, search_query: str, search_type: str = "process", per_page: int = 20) -> List[SearchResult]:
|
|
242
|
-
"""
|
|
243
|
-
Search for processes, templates, or tasks in the organization.
|
|
244
|
-
|
|
245
|
-
Args:
|
|
246
|
-
org_id: Organization ID
|
|
247
|
-
search_query: Text to search for
|
|
248
|
-
search_type: Type of search - 'process', 'blueprint', or 'task' (default: 'process'). blueprint equals template
|
|
249
|
-
per_page: Number of results per page (default: 20)
|
|
250
|
-
|
|
251
|
-
Returns:
|
|
252
|
-
List of SearchResult objects
|
|
253
|
-
|
|
254
|
-
Raises:
|
|
255
|
-
TallyfyError: If the request fails
|
|
256
|
-
ValueError: If search_type is not valid
|
|
257
|
-
"""
|
|
258
|
-
# Validate search type
|
|
259
|
-
valid_types = ["process", "blueprint", "task"]
|
|
260
|
-
if search_type not in valid_types:
|
|
261
|
-
raise ValueError(f"Search type must be one of: {', '.join(valid_types)}")
|
|
262
|
-
|
|
263
|
-
try:
|
|
264
|
-
endpoint = f"organizations/{org_id}/search"
|
|
265
|
-
params = {
|
|
266
|
-
'on': search_type,
|
|
267
|
-
'per_page': str(per_page),
|
|
268
|
-
'search': search_query
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
response_data = self.sdk._make_request('GET', endpoint, params=params)
|
|
272
|
-
|
|
273
|
-
if isinstance(response_data, dict) and search_type in response_data:
|
|
274
|
-
search_data = response_data[search_type]
|
|
275
|
-
if 'data' in search_data and search_data['data']:
|
|
276
|
-
results_data = search_data['data']
|
|
277
|
-
return [SearchResult.from_dict(result_data, search_type) for result_data in results_data]
|
|
278
|
-
else:
|
|
279
|
-
self.sdk.logger.info(f"No {search_type} results found for query: {search_query}")
|
|
280
|
-
return []
|
|
281
|
-
else:
|
|
282
|
-
self.sdk.logger.warning(f"Unexpected response format for {search_type} search")
|
|
283
|
-
return []
|
|
284
|
-
|
|
285
|
-
except TallyfyError as e:
|
|
286
|
-
self.sdk.logger.error(f"Failed to search {search_type} for query '{search_query}': {e}")
|
|
287
|
-
raise
|
|
288
|
-
|
|
289
|
-
def create_task(self, org_id: str, title: str, deadline: str,
|
|
290
|
-
owners: TaskOwners, description: Optional[str] = None,
|
|
291
|
-
max_assignable: Optional[int] = None, prevent_guest_comment: Optional[bool] = None) -> Optional[Task]:
|
|
292
|
-
"""
|
|
293
|
-
Create a standalone task in the organization.
|
|
294
|
-
|
|
295
|
-
Args:
|
|
296
|
-
org_id: Organization ID
|
|
297
|
-
title: Task name (required)
|
|
298
|
-
deadline: Task deadline in "YYYY-mm-dd HH:ii:ss" format
|
|
299
|
-
owners: TaskOwners object with users, guests, and groups
|
|
300
|
-
description: Task description (optional)
|
|
301
|
-
max_assignable: Maximum number of assignees (optional)
|
|
302
|
-
prevent_guest_comment: Prevent guests from commenting (optional)
|
|
303
|
-
|
|
304
|
-
Returns:
|
|
305
|
-
Task object for the created task
|
|
306
|
-
|
|
307
|
-
Raises:
|
|
308
|
-
TallyfyError: If the request fails
|
|
309
|
-
ValueError: If required parameters are missing
|
|
310
|
-
"""
|
|
311
|
-
if not title:
|
|
312
|
-
raise ValueError("Task title is required")
|
|
313
|
-
|
|
314
|
-
# Validate that at least one assignee is provided
|
|
315
|
-
if not owners or (not owners.users and not owners.guests and not owners.groups):
|
|
316
|
-
raise ValueError("At least one assignee is required (users, guests, or groups)")
|
|
317
|
-
|
|
318
|
-
try:
|
|
319
|
-
endpoint = f"organizations/{org_id}/tasks"
|
|
320
|
-
|
|
321
|
-
task_data = {
|
|
322
|
-
"title": title
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if description:
|
|
326
|
-
task_data["description"] = description
|
|
327
|
-
if owners:
|
|
328
|
-
task_data["owners"] = {
|
|
329
|
-
"users": owners.users,
|
|
330
|
-
"guests": owners.guests,
|
|
331
|
-
"groups": owners.groups
|
|
332
|
-
}
|
|
333
|
-
if deadline:
|
|
334
|
-
task_data["deadline"] = deadline
|
|
335
|
-
if max_assignable is not None:
|
|
336
|
-
task_data["max_assignable"] = max_assignable
|
|
337
|
-
if prevent_guest_comment is not None:
|
|
338
|
-
task_data["prevent_guest_comment"] = prevent_guest_comment
|
|
339
|
-
|
|
340
|
-
task_data["task_type"] = "task"
|
|
341
|
-
task_data["separate_task_for_each_assignee"] = True
|
|
342
|
-
task_data["status"]= "not-started"
|
|
343
|
-
task_data["everyone_must_complete"] = False
|
|
344
|
-
task_data["is_soft_start_date"]= True
|
|
345
|
-
response_data = self.sdk._make_request('POST', endpoint, data=task_data)
|
|
346
|
-
|
|
347
|
-
if isinstance(response_data, dict) and 'data' in response_data:
|
|
348
|
-
task_data = response_data['data']
|
|
349
|
-
return Task.from_dict(task_data)
|
|
350
|
-
else:
|
|
351
|
-
self.sdk.logger.warning("Unexpected response format for task creation")
|
|
352
|
-
return None
|
|
353
|
-
|
|
354
|
-
except TallyfyError as e:
|
|
355
|
-
self.sdk.logger.error(f"Failed to create task in organization {org_id}: {e}")
|
|
356
|
-
raise
|