tallyfy 1.0.4__py3-none-any.whl → 1.0.5__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.

Files changed (33) hide show
  1. tallyfy/__init__.py +8 -4
  2. tallyfy/core.py +8 -8
  3. tallyfy/form_fields_management/__init__.py +70 -0
  4. tallyfy/form_fields_management/base.py +109 -0
  5. tallyfy/form_fields_management/crud_operations.py +234 -0
  6. tallyfy/form_fields_management/options_management.py +222 -0
  7. tallyfy/form_fields_management/suggestions.py +411 -0
  8. tallyfy/task_management/__init__.py +81 -0
  9. tallyfy/task_management/base.py +125 -0
  10. tallyfy/task_management/creation.py +221 -0
  11. tallyfy/task_management/retrieval.py +211 -0
  12. tallyfy/task_management/search.py +196 -0
  13. tallyfy/template_management/__init__.py +85 -0
  14. tallyfy/template_management/analysis.py +1093 -0
  15. tallyfy/template_management/automation.py +469 -0
  16. tallyfy/template_management/base.py +56 -0
  17. tallyfy/template_management/basic_operations.py +477 -0
  18. tallyfy/template_management/health_assessment.py +763 -0
  19. tallyfy/user_management/__init__.py +69 -0
  20. tallyfy/user_management/base.py +146 -0
  21. tallyfy/user_management/invitation.py +286 -0
  22. tallyfy/user_management/retrieval.py +339 -0
  23. {tallyfy-1.0.4.dist-info → tallyfy-1.0.5.dist-info}/METADATA +120 -56
  24. tallyfy-1.0.5.dist-info/RECORD +28 -0
  25. tallyfy/BUILD.md +0 -5
  26. tallyfy/form_fields_management.py +0 -582
  27. tallyfy/task_management.py +0 -356
  28. tallyfy/template_management.py +0 -2607
  29. tallyfy/user_management.py +0 -235
  30. tallyfy-1.0.4.dist-info/RECORD +0 -13
  31. {tallyfy-1.0.4.dist-info → tallyfy-1.0.5.dist-info}/WHEEL +0 -0
  32. {tallyfy-1.0.4.dist-info → tallyfy-1.0.5.dist-info}/licenses/LICENSE +0 -0
  33. {tallyfy-1.0.4.dist-info → tallyfy-1.0.5.dist-info}/top_level.txt +0 -0
@@ -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