tallyfy 1.0.16__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.
- tallyfy/__init__.py +27 -0
- tallyfy/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/__pycache__/core.cpython-310.pyc +0 -0
- tallyfy/__pycache__/form_fields_management.cpython-310.pyc +0 -0
- tallyfy/__pycache__/models.cpython-310.pyc +0 -0
- tallyfy/__pycache__/task_management.cpython-310.pyc +0 -0
- tallyfy/__pycache__/template_management.cpython-310.pyc +0 -0
- tallyfy/__pycache__/user_management.cpython-310.pyc +0 -0
- tallyfy/core.py +361 -0
- tallyfy/form_fields_management/__init__.py +70 -0
- tallyfy/form_fields_management/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/base.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/crud_operations.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/options_management.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/suggestions.cpython-310.pyc +0 -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 +1464 -0
- tallyfy/organization_management/__init__.py +26 -0
- tallyfy/organization_management/base.py +76 -0
- tallyfy/organization_management/retrieval.py +39 -0
- tallyfy/task_management/__init__.py +81 -0
- tallyfy/task_management/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/base.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/creation.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/retrieval.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/search.cpython-310.pyc +0 -0
- tallyfy/task_management/base.py +125 -0
- tallyfy/task_management/creation.py +221 -0
- tallyfy/task_management/retrieval.py +252 -0
- tallyfy/task_management/search.py +198 -0
- tallyfy/template_management/__init__.py +85 -0
- tallyfy/template_management/analysis.py +1099 -0
- tallyfy/template_management/automation.py +469 -0
- tallyfy/template_management/base.py +56 -0
- tallyfy/template_management/basic_operations.py +479 -0
- tallyfy/template_management/health_assessment.py +793 -0
- tallyfy/user_management/__init__.py +70 -0
- tallyfy/user_management/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/user_management/__pycache__/base.cpython-310.pyc +0 -0
- tallyfy/user_management/__pycache__/invitation.cpython-310.pyc +0 -0
- tallyfy/user_management/__pycache__/retrieval.cpython-310.pyc +0 -0
- tallyfy/user_management/base.py +146 -0
- tallyfy/user_management/invitation.py +286 -0
- tallyfy/user_management/retrieval.py +381 -0
- tallyfy-1.0.16.dist-info/METADATA +742 -0
- tallyfy-1.0.16.dist-info/RECORD +52 -0
- tallyfy-1.0.16.dist-info/WHEEL +5 -0
- tallyfy-1.0.16.dist-info/licenses/LICENSE +21 -0
- tallyfy-1.0.16.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User and guest retrieval operations
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
from .base import UserManagerBase
|
|
7
|
+
from ..models import User, Guest, UsersList, GuestsList, TallyfyError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UserRetrieval(UserManagerBase):
|
|
11
|
+
"""Handles user and guest retrieval operations"""
|
|
12
|
+
|
|
13
|
+
def get_current_user_info(self, org_id: str) -> Optional[User]:
|
|
14
|
+
"""
|
|
15
|
+
Get current user with full profile data.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
org_id: Organization ID
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
A User object with full profile data
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
TallyfyError: If the request fails
|
|
25
|
+
"""
|
|
26
|
+
self._validate_org_id(org_id)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
endpoint = f"organizations/{org_id}/me"
|
|
30
|
+
response_data = self.sdk._make_request('GET', endpoint)
|
|
31
|
+
|
|
32
|
+
user_data = self._extract_data(response_data, default_empty=False)
|
|
33
|
+
if user_data:
|
|
34
|
+
# Handle both single user data and wrapped responses
|
|
35
|
+
if isinstance(user_data, list) and user_data:
|
|
36
|
+
return User.from_dict(user_data[0])
|
|
37
|
+
elif isinstance(user_data, dict):
|
|
38
|
+
return User.from_dict(user_data)
|
|
39
|
+
|
|
40
|
+
self.sdk.logger.warning("Unexpected response format for getting current user")
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
except TallyfyError:
|
|
44
|
+
raise
|
|
45
|
+
except Exception as e:
|
|
46
|
+
self._handle_api_error(e, "get current user info", org_id=org_id)
|
|
47
|
+
|
|
48
|
+
def get_user(self, org_id: str, user_id: int) -> Optional[User]:
|
|
49
|
+
"""
|
|
50
|
+
Get user with full profile data.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
org_id: Organization ID
|
|
54
|
+
user_id: User ID
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A User object with full profile data
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
TallyfyError: If the request fails
|
|
61
|
+
"""
|
|
62
|
+
self._validate_org_id(org_id)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
endpoint = f"organizations/{org_id}/users/{user_id}"
|
|
66
|
+
response_data = self.sdk._make_request('GET', endpoint)
|
|
67
|
+
|
|
68
|
+
user_data = self._extract_data(response_data, default_empty=False)
|
|
69
|
+
if user_data:
|
|
70
|
+
# Handle both single user data and wrapped responses
|
|
71
|
+
if isinstance(user_data, list) and user_data:
|
|
72
|
+
return User.from_dict(user_data[0])
|
|
73
|
+
elif isinstance(user_data, dict):
|
|
74
|
+
return User.from_dict(user_data)
|
|
75
|
+
|
|
76
|
+
self.sdk.logger.warning("Unexpected response format for getting current user")
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
except TallyfyError:
|
|
80
|
+
raise
|
|
81
|
+
except Exception as e:
|
|
82
|
+
self._handle_api_error(e, "get current user info", org_id=org_id)
|
|
83
|
+
|
|
84
|
+
def get_organization_users(self, org_id: str, with_groups: bool = False,
|
|
85
|
+
page: int = 1, per_page: int = 100) -> UsersList:
|
|
86
|
+
"""
|
|
87
|
+
Get all organization members with full profile data.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
org_id: Organization ID
|
|
91
|
+
with_groups: Include user groups data
|
|
92
|
+
page: Page number (default: 1)
|
|
93
|
+
per_page: Number of results per page (default: 100)
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
UsersList object containing users and pagination metadata (total count, etc.)
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
TallyfyError: If the request fails
|
|
100
|
+
"""
|
|
101
|
+
self._validate_org_id(org_id)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
endpoint = f"organizations/{org_id}/users"
|
|
105
|
+
params = self._build_query_params(
|
|
106
|
+
page=page,
|
|
107
|
+
per_page=per_page,
|
|
108
|
+
**({'with': 'groups'} if with_groups else {})
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
response_data = self.sdk._make_request('GET', endpoint, params=params)
|
|
112
|
+
|
|
113
|
+
# Return the structured response with pagination
|
|
114
|
+
return UsersList.from_dict(response_data)
|
|
115
|
+
|
|
116
|
+
except TallyfyError:
|
|
117
|
+
raise
|
|
118
|
+
except Exception as e:
|
|
119
|
+
self._handle_api_error(e, "get organization users", org_id=org_id)
|
|
120
|
+
|
|
121
|
+
def get_organization_users_list(self, org_id: str) -> List[User]:
|
|
122
|
+
"""
|
|
123
|
+
Get all organization members with minimal data for listing.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
org_id: Organization ID
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
List of User objects with minimal data
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
TallyfyError: If the request fails
|
|
133
|
+
"""
|
|
134
|
+
self._validate_org_id(org_id)
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
endpoint = f"organizations/{org_id}/users-list"
|
|
138
|
+
response_data = self.sdk._make_request('GET', endpoint)
|
|
139
|
+
|
|
140
|
+
users_data = self._extract_data(response_data)
|
|
141
|
+
if users_data:
|
|
142
|
+
return [User.from_dict(user_data) for user_data in users_data]
|
|
143
|
+
else:
|
|
144
|
+
self.sdk.logger.warning("Unexpected response format for users list")
|
|
145
|
+
return []
|
|
146
|
+
|
|
147
|
+
except TallyfyError:
|
|
148
|
+
raise
|
|
149
|
+
except Exception as e:
|
|
150
|
+
self._handle_api_error(e, "get organization users list", org_id=org_id)
|
|
151
|
+
|
|
152
|
+
def get_organization_guests(self, org_id: str, with_stats: bool = False,
|
|
153
|
+
page: int = 1, per_page: int = 100) -> GuestsList:
|
|
154
|
+
"""
|
|
155
|
+
Get all guests in an organization with full profile data.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
org_id: Organization ID
|
|
159
|
+
with_stats: Include guest statistics
|
|
160
|
+
page: Page number (default: 1)
|
|
161
|
+
per_page: Number of results per page (default: 100)
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
GuestsList object containing guests and pagination metadata (total count, etc.)
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
TallyfyError: If the request fails
|
|
168
|
+
"""
|
|
169
|
+
self._validate_org_id(org_id)
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
endpoint = f"organizations/{org_id}/guests"
|
|
173
|
+
params = self._build_query_params(
|
|
174
|
+
page=page,
|
|
175
|
+
per_page=per_page,
|
|
176
|
+
**({'with': 'stats'} if with_stats else {})
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
response_data = self.sdk._make_request('GET', endpoint, params=params)
|
|
180
|
+
|
|
181
|
+
# Return the structured response with pagination
|
|
182
|
+
return GuestsList.from_dict(response_data)
|
|
183
|
+
|
|
184
|
+
except TallyfyError:
|
|
185
|
+
raise
|
|
186
|
+
except Exception as e:
|
|
187
|
+
self._handle_api_error(e, "get organization guests", org_id=org_id)
|
|
188
|
+
|
|
189
|
+
def get_organization_guests_list(self, org_id: str) -> List[Guest]:
|
|
190
|
+
"""
|
|
191
|
+
Get organization guests with minimal data.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
org_id: Organization ID
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
List of Guest objects with minimal data
|
|
198
|
+
|
|
199
|
+
Raises:
|
|
200
|
+
TallyfyError: If the request fails
|
|
201
|
+
"""
|
|
202
|
+
self._validate_org_id(org_id)
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
endpoint = f"organizations/{org_id}/guests-list"
|
|
206
|
+
response_data = self.sdk._make_request('GET', endpoint)
|
|
207
|
+
|
|
208
|
+
# Handle different response formats
|
|
209
|
+
guests_data = self._extract_data(response_data)
|
|
210
|
+
if guests_data:
|
|
211
|
+
# Handle both list of guests and single guest responses
|
|
212
|
+
if isinstance(guests_data, list):
|
|
213
|
+
return [Guest.from_dict(guest_data) for guest_data in guests_data]
|
|
214
|
+
else:
|
|
215
|
+
return [Guest.from_dict(guests_data)]
|
|
216
|
+
else:
|
|
217
|
+
self.sdk.logger.warning("Unexpected response format for guests list")
|
|
218
|
+
return []
|
|
219
|
+
|
|
220
|
+
except TallyfyError:
|
|
221
|
+
raise
|
|
222
|
+
except Exception as e:
|
|
223
|
+
self._handle_api_error(e, "get organization guests list", org_id=org_id)
|
|
224
|
+
|
|
225
|
+
def get_all_organization_members(self, org_id: str, include_guests: bool = True,
|
|
226
|
+
with_groups: bool = False, with_stats: bool = False) -> dict:
|
|
227
|
+
"""
|
|
228
|
+
Get all organization members and optionally guests in a single call.
|
|
229
|
+
|
|
230
|
+
This is a convenience method that combines users and guests data.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
org_id: Organization ID
|
|
234
|
+
include_guests: Whether to include guests in the results
|
|
235
|
+
with_groups: Include user groups data
|
|
236
|
+
with_stats: Include guest statistics
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Dictionary with 'users' (UsersList) and optionally 'guests' (GuestsList) keys
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
TallyfyError: If the request fails
|
|
243
|
+
"""
|
|
244
|
+
result = {}
|
|
245
|
+
|
|
246
|
+
# Get users
|
|
247
|
+
result['users'] = self.get_organization_users(org_id, with_groups=with_groups)
|
|
248
|
+
|
|
249
|
+
# Get guests if requested
|
|
250
|
+
if include_guests:
|
|
251
|
+
result['guests'] = self.get_organization_guests(org_id, with_stats=with_stats)
|
|
252
|
+
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
def get_user_by_email(self, org_id: str, email: str) -> Optional[User]:
|
|
256
|
+
"""
|
|
257
|
+
Find a user by email address within an organization.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
org_id: Organization ID
|
|
261
|
+
email: Email address to search for
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
User object if found, None otherwise
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
TallyfyError: If the request fails
|
|
268
|
+
"""
|
|
269
|
+
self._validate_org_id(org_id)
|
|
270
|
+
self._validate_email(email)
|
|
271
|
+
|
|
272
|
+
# Get all users and search for the email
|
|
273
|
+
users_list = self.get_organization_users(org_id)
|
|
274
|
+
|
|
275
|
+
for user in users_list.data:
|
|
276
|
+
if hasattr(user, 'email') and user.email and user.email.lower() == email.lower():
|
|
277
|
+
return user
|
|
278
|
+
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
def get_guest_by_email(self, org_id: str, email: str) -> Optional[Guest]:
|
|
282
|
+
"""
|
|
283
|
+
Find a guest by email address within an organization.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
org_id: Organization ID
|
|
287
|
+
email: Email address to search for
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Guest object if found, None otherwise
|
|
291
|
+
|
|
292
|
+
Raises:
|
|
293
|
+
TallyfyError: If the request fails
|
|
294
|
+
"""
|
|
295
|
+
self._validate_org_id(org_id)
|
|
296
|
+
self._validate_email(email)
|
|
297
|
+
|
|
298
|
+
# Get all guests and search for the email
|
|
299
|
+
guests_list = self.get_organization_guests(org_id)
|
|
300
|
+
|
|
301
|
+
for guest in guests_list.data:
|
|
302
|
+
if hasattr(guest, 'email') and guest.email and guest.email.lower() == email.lower():
|
|
303
|
+
return guest
|
|
304
|
+
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
def search_members_by_name(self, org_id: str, name_query: str, include_guests: bool = True) -> dict:
|
|
308
|
+
"""
|
|
309
|
+
Search for organization members by name (first name, last name, or full name).
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
org_id: Organization ID
|
|
313
|
+
name_query: Name to search for (case-insensitive partial match)
|
|
314
|
+
include_guests: Whether to include guests in the search
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Dictionary with 'users' and optionally 'guests' keys containing matching members
|
|
318
|
+
|
|
319
|
+
Raises:
|
|
320
|
+
TallyfyError: If the request fails
|
|
321
|
+
"""
|
|
322
|
+
self._validate_org_id(org_id)
|
|
323
|
+
|
|
324
|
+
if not name_query or not isinstance(name_query, str):
|
|
325
|
+
raise ValueError("Name query must be a non-empty string")
|
|
326
|
+
|
|
327
|
+
name_query_lower = name_query.lower()
|
|
328
|
+
result = {'users': [], 'guests': []}
|
|
329
|
+
|
|
330
|
+
# Search users
|
|
331
|
+
users_list = self.get_organization_users(org_id)
|
|
332
|
+
for user in users_list.data:
|
|
333
|
+
user_matches = False
|
|
334
|
+
|
|
335
|
+
# Check first name
|
|
336
|
+
if hasattr(user, 'first_name') and user.first_name:
|
|
337
|
+
if name_query_lower in user.first_name.lower():
|
|
338
|
+
user_matches = True
|
|
339
|
+
|
|
340
|
+
# Check last name
|
|
341
|
+
if hasattr(user, 'last_name') and user.last_name:
|
|
342
|
+
if name_query_lower in user.last_name.lower():
|
|
343
|
+
user_matches = True
|
|
344
|
+
|
|
345
|
+
# Check full name
|
|
346
|
+
if hasattr(user, 'first_name') and hasattr(user, 'last_name'):
|
|
347
|
+
if user.first_name and user.last_name:
|
|
348
|
+
full_name = f"{user.first_name} {user.last_name}".lower()
|
|
349
|
+
if name_query_lower in full_name:
|
|
350
|
+
user_matches = True
|
|
351
|
+
|
|
352
|
+
if user_matches:
|
|
353
|
+
result['users'].append(user)
|
|
354
|
+
|
|
355
|
+
# Search guests if requested
|
|
356
|
+
if include_guests:
|
|
357
|
+
guests_list = self.get_organization_guests(org_id)
|
|
358
|
+
for guest in guests_list.data:
|
|
359
|
+
guest_matches = False
|
|
360
|
+
|
|
361
|
+
# Check first name
|
|
362
|
+
if hasattr(guest, 'first_name') and guest.first_name:
|
|
363
|
+
if name_query_lower in guest.first_name.lower():
|
|
364
|
+
guest_matches = True
|
|
365
|
+
|
|
366
|
+
# Check last name
|
|
367
|
+
if hasattr(guest, 'last_name') and guest.last_name:
|
|
368
|
+
if name_query_lower in guest.last_name.lower():
|
|
369
|
+
guest_matches = True
|
|
370
|
+
|
|
371
|
+
# Check full name
|
|
372
|
+
if hasattr(guest, 'first_name') and hasattr(guest, 'last_name'):
|
|
373
|
+
if guest.first_name and guest.last_name:
|
|
374
|
+
full_name = f"{guest.first_name} {guest.last_name}".lower()
|
|
375
|
+
if name_query_lower in full_name:
|
|
376
|
+
guest_matches = True
|
|
377
|
+
|
|
378
|
+
if guest_matches:
|
|
379
|
+
result['guests'].append(guest)
|
|
380
|
+
|
|
381
|
+
return result
|