tallyfy 1.0.0__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/README.md +636 -0
- tallyfy/__init__.py +20 -0
- tallyfy/core.py +333 -0
- tallyfy/form_fields_management.py +582 -0
- tallyfy/models.py +1013 -0
- tallyfy/task_management.py +356 -0
- tallyfy/template_management.py +2580 -0
- tallyfy/user_management.py +235 -0
- tallyfy-1.0.0.dist-info/METADATA +680 -0
- tallyfy-1.0.0.dist-info/RECORD +13 -0
- tallyfy-1.0.0.dist-info/WHEEL +5 -0
- tallyfy-1.0.0.dist-info/licenses/LICENSE +21 -0
- tallyfy-1.0.0.dist-info/top_level.txt +1 -0
tallyfy/models.py
ADDED
|
@@ -0,0 +1,1013 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for Tallyfy API responses
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Optional, List
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TallyfyError(Exception):
|
|
10
|
+
"""Custom exception for Tallyfy API errors"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, message: str, status_code: Optional[int] = None, response_data: Optional[Any] = None):
|
|
13
|
+
super().__init__(message)
|
|
14
|
+
self.status_code = status_code
|
|
15
|
+
self.response_data = response_data
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Country:
|
|
20
|
+
"""Country data model"""
|
|
21
|
+
id: int
|
|
22
|
+
name: str
|
|
23
|
+
phone_code: str
|
|
24
|
+
iso2: str
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Country':
|
|
28
|
+
"""Create Country instance from dictionary"""
|
|
29
|
+
return cls(
|
|
30
|
+
id=data.get('id', 0),
|
|
31
|
+
name=data.get('name', ''),
|
|
32
|
+
phone_code=data.get('phone_code', ''),
|
|
33
|
+
iso2=data.get('iso2', '')
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class User:
|
|
39
|
+
"""User data model"""
|
|
40
|
+
id: int
|
|
41
|
+
email: str
|
|
42
|
+
username: str
|
|
43
|
+
first_name: str
|
|
44
|
+
last_name: str
|
|
45
|
+
full_name: str
|
|
46
|
+
profile_pic: Optional[str] = None
|
|
47
|
+
locale: Optional[str] = None
|
|
48
|
+
active: bool = True
|
|
49
|
+
is_suspended: bool = False
|
|
50
|
+
created_at: Optional[str] = None
|
|
51
|
+
last_updated: Optional[str] = None
|
|
52
|
+
last_login_at: Optional[str] = None
|
|
53
|
+
activated_at: Optional[str] = None
|
|
54
|
+
support_user: bool = False
|
|
55
|
+
country: Optional[Country] = None
|
|
56
|
+
phone: Optional[str] = None
|
|
57
|
+
job_title: Optional[str] = None
|
|
58
|
+
job_description: Optional[str] = None
|
|
59
|
+
team: Optional[str] = None
|
|
60
|
+
timezone: Optional[str] = None
|
|
61
|
+
UTC_offset: Optional[str] = None
|
|
62
|
+
last_accessed_at: Optional[str] = None
|
|
63
|
+
approved_at: Optional[str] = None
|
|
64
|
+
invited_by: Optional[int] = None
|
|
65
|
+
disabled_at: Optional[str] = None
|
|
66
|
+
disabled_by: Optional[int] = None
|
|
67
|
+
reactivated_at: Optional[str] = None
|
|
68
|
+
reactivated_by: Optional[int] = None
|
|
69
|
+
status: Optional[str] = None
|
|
70
|
+
date_format: Optional[str] = None
|
|
71
|
+
last_known_ip: Optional[str] = None
|
|
72
|
+
last_known_country: Optional[str] = None
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'User':
|
|
76
|
+
"""Create User instance from dictionary"""
|
|
77
|
+
country_data = data.get('country')
|
|
78
|
+
country = Country.from_dict(country_data) if country_data else None
|
|
79
|
+
|
|
80
|
+
return cls(
|
|
81
|
+
id=data.get('id', 0),
|
|
82
|
+
email=data.get('email', ''),
|
|
83
|
+
username=data.get('username', ''),
|
|
84
|
+
first_name=data.get('first_name', ''),
|
|
85
|
+
last_name=data.get('last_name', ''),
|
|
86
|
+
full_name=data.get('full_name', ''),
|
|
87
|
+
profile_pic=data.get('profile_pic'),
|
|
88
|
+
locale=data.get('locale'),
|
|
89
|
+
active=data.get('active', True),
|
|
90
|
+
is_suspended=data.get('is_suspended', False),
|
|
91
|
+
created_at=data.get('created_at'),
|
|
92
|
+
last_updated=data.get('last_updated'),
|
|
93
|
+
last_login_at=data.get('last_login_at'),
|
|
94
|
+
activated_at=data.get('activated_at'),
|
|
95
|
+
support_user=data.get('support_user', False),
|
|
96
|
+
country=country,
|
|
97
|
+
phone=data.get('phone'),
|
|
98
|
+
job_title=data.get('job_title'),
|
|
99
|
+
job_description=data.get('job_description'),
|
|
100
|
+
team=data.get('team'),
|
|
101
|
+
timezone=data.get('timezone'),
|
|
102
|
+
UTC_offset=data.get('UTC_offset'),
|
|
103
|
+
last_accessed_at=data.get('last_accessed_at'),
|
|
104
|
+
approved_at=data.get('approved_at'),
|
|
105
|
+
invited_by=data.get('invited_by'),
|
|
106
|
+
disabled_at=data.get('disabled_at'),
|
|
107
|
+
disabled_by=data.get('disabled_by'),
|
|
108
|
+
reactivated_at=data.get('reactivated_at'),
|
|
109
|
+
reactivated_by=data.get('reactivated_by'),
|
|
110
|
+
status=data.get('status'),
|
|
111
|
+
date_format=data.get('date_format'),
|
|
112
|
+
last_known_ip=data.get('last_known_ip'),
|
|
113
|
+
last_known_country=data.get('last_known_country')
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class GuestDetails:
|
|
119
|
+
"""Guest details data model"""
|
|
120
|
+
first_name: Optional[str] = None
|
|
121
|
+
last_name: Optional[str] = None
|
|
122
|
+
status: Optional[str] = None
|
|
123
|
+
phone_1: Optional[str] = None
|
|
124
|
+
phone_2: Optional[str] = None
|
|
125
|
+
image_url: Optional[str] = None
|
|
126
|
+
contact_url: Optional[str] = None
|
|
127
|
+
company_url: Optional[str] = None
|
|
128
|
+
opportunity_url: Optional[str] = None
|
|
129
|
+
company_name: Optional[str] = None
|
|
130
|
+
opportunity_name: Optional[str] = None
|
|
131
|
+
external_sync_source: Optional[str] = None
|
|
132
|
+
external_date_creation: Optional[str] = None
|
|
133
|
+
timezone: Optional[str] = None
|
|
134
|
+
reactivated_at: Optional[str] = None
|
|
135
|
+
reactivated_by: Optional[int] = None
|
|
136
|
+
disabled_on: Optional[str] = None
|
|
137
|
+
disabled_by: Optional[int] = None
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'GuestDetails':
|
|
141
|
+
"""Create GuestDetails instance from dictionary"""
|
|
142
|
+
return cls(
|
|
143
|
+
first_name=data.get('first_name'),
|
|
144
|
+
last_name=data.get('last_name'),
|
|
145
|
+
status=data.get('status'),
|
|
146
|
+
phone_1=data.get('phone_1'),
|
|
147
|
+
phone_2=data.get('phone_2'),
|
|
148
|
+
image_url=data.get('image_url'),
|
|
149
|
+
contact_url=data.get('contact_url'),
|
|
150
|
+
company_url=data.get('company_url'),
|
|
151
|
+
opportunity_url=data.get('opportunity_url'),
|
|
152
|
+
company_name=data.get('company_name'),
|
|
153
|
+
opportunity_name=data.get('opportunity_name'),
|
|
154
|
+
external_sync_source=data.get('external_sync_source'),
|
|
155
|
+
external_date_creation=data.get('external_date_creation'),
|
|
156
|
+
timezone=data.get('timezone'),
|
|
157
|
+
reactivated_at=data.get('reactivated_at'),
|
|
158
|
+
reactivated_by=data.get('reactivated_by'),
|
|
159
|
+
disabled_on=data.get('disabled_on'),
|
|
160
|
+
disabled_by=data.get('disabled_by')
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@dataclass
|
|
165
|
+
class Guest:
|
|
166
|
+
"""Guest data model"""
|
|
167
|
+
email: str
|
|
168
|
+
last_accessed_at: Optional[str] = None
|
|
169
|
+
last_known_ip: Optional[str] = None
|
|
170
|
+
last_known_country: Optional[str] = None
|
|
171
|
+
details: Optional[GuestDetails] = None
|
|
172
|
+
|
|
173
|
+
@classmethod
|
|
174
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Guest':
|
|
175
|
+
"""Create Guest instance from dictionary"""
|
|
176
|
+
details_data = data.get('details')
|
|
177
|
+
details = GuestDetails.from_dict(details_data) if details_data else None
|
|
178
|
+
|
|
179
|
+
return cls(
|
|
180
|
+
email=data.get('email', ''),
|
|
181
|
+
last_accessed_at=data.get('last_accessed_at'),
|
|
182
|
+
last_known_ip=data.get('last_known_ip'),
|
|
183
|
+
last_known_country=data.get('last_known_country'),
|
|
184
|
+
details=details
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass
|
|
189
|
+
class TaskOwners:
|
|
190
|
+
"""Task owners data model"""
|
|
191
|
+
users: List[int] = None
|
|
192
|
+
guests: List[str] = None
|
|
193
|
+
groups: List[int] = None
|
|
194
|
+
task_urls: List[str] = None
|
|
195
|
+
|
|
196
|
+
def __post_init__(self):
|
|
197
|
+
"""Initialize empty lists if None"""
|
|
198
|
+
if self.users is None:
|
|
199
|
+
self.users = []
|
|
200
|
+
if self.guests is None:
|
|
201
|
+
self.guests = []
|
|
202
|
+
if self.groups is None:
|
|
203
|
+
self.groups = []
|
|
204
|
+
if self.task_urls is None:
|
|
205
|
+
self.task_urls = []
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'TaskOwners':
|
|
209
|
+
"""Create TaskOwners instance from dictionary"""
|
|
210
|
+
return cls(
|
|
211
|
+
users=data.get('users', []),
|
|
212
|
+
guests=data.get('guests', []),
|
|
213
|
+
groups=data.get('groups', []),
|
|
214
|
+
task_urls=data.get('taskUrls', [])
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass
|
|
219
|
+
class Task:
|
|
220
|
+
"""Task data model"""
|
|
221
|
+
id: str
|
|
222
|
+
increment_id: int
|
|
223
|
+
title: str
|
|
224
|
+
original_title: str
|
|
225
|
+
allow_guest_owners: bool
|
|
226
|
+
run_id: Optional[str] = None
|
|
227
|
+
checklist_id: Optional[str] = None
|
|
228
|
+
linked_step_id: Optional[str] = None
|
|
229
|
+
step_id: Optional[str] = None
|
|
230
|
+
alias: Optional[str] = None
|
|
231
|
+
taskdata: Optional[Dict[str, Any]] = None
|
|
232
|
+
owners: Optional[TaskOwners] = None
|
|
233
|
+
is_completable: bool = True
|
|
234
|
+
status: str = "not-started"
|
|
235
|
+
status_label: str = "not-started"
|
|
236
|
+
task_type: str = "task"
|
|
237
|
+
is_approved: Optional[bool] = None
|
|
238
|
+
position: int = 0
|
|
239
|
+
started_at: Optional[str] = None
|
|
240
|
+
deadline: Optional[str] = None
|
|
241
|
+
created_at: Optional[str] = None
|
|
242
|
+
last_updated: Optional[str] = None
|
|
243
|
+
archived_at: Optional[str] = None
|
|
244
|
+
completed_at: Optional[str] = None
|
|
245
|
+
starter_id: Optional[int] = None
|
|
246
|
+
completer_id: Optional[int] = None
|
|
247
|
+
is_oneoff_task: bool = False
|
|
248
|
+
everyone_must_complete: bool = False
|
|
249
|
+
completion_progress: Optional[float] = None
|
|
250
|
+
has_deadline_dependent_child_tasks: bool = False
|
|
251
|
+
can_complete_only_assignees: bool = False
|
|
252
|
+
max_assignable: int = 0
|
|
253
|
+
webhook: Optional[str] = None
|
|
254
|
+
prevent_guest_comment: bool = False
|
|
255
|
+
stage_id: Optional[str] = None
|
|
256
|
+
problem: bool = False
|
|
257
|
+
blueprint_position: Optional[int] = None
|
|
258
|
+
is_soft_start_date: bool = False
|
|
259
|
+
send_chromeless: Optional[bool] = None
|
|
260
|
+
run_status: Optional[str] = None
|
|
261
|
+
completer_guest: Optional[str] = None
|
|
262
|
+
|
|
263
|
+
@classmethod
|
|
264
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Task':
|
|
265
|
+
"""Create Task instance from dictionary"""
|
|
266
|
+
owners_data = data.get('owners')
|
|
267
|
+
owners = TaskOwners.from_dict(owners_data) if owners_data else None
|
|
268
|
+
|
|
269
|
+
return cls(
|
|
270
|
+
id=data.get('id', ''),
|
|
271
|
+
increment_id=data.get('increment_id', 0),
|
|
272
|
+
title=data.get('title', ''),
|
|
273
|
+
original_title=data.get('original_title', ''),
|
|
274
|
+
allow_guest_owners=data.get('allow_guest_owners', False),
|
|
275
|
+
run_id=data.get('run_id'),
|
|
276
|
+
checklist_id=data.get('checklist_id'),
|
|
277
|
+
linked_step_id=data.get('linked_step_id'),
|
|
278
|
+
step_id=data.get('step_id'),
|
|
279
|
+
alias=data.get('alias'),
|
|
280
|
+
taskdata=data.get('taskdata', {}),
|
|
281
|
+
owners=owners,
|
|
282
|
+
is_completable=data.get('is_completable', True),
|
|
283
|
+
status=data.get('status', 'not-started'),
|
|
284
|
+
status_label=data.get('status_label', 'not-started'),
|
|
285
|
+
task_type=data.get('task_type', 'task'),
|
|
286
|
+
is_approved=data.get('is_approved'),
|
|
287
|
+
position=data.get('position', 0),
|
|
288
|
+
started_at=data.get('started_at'),
|
|
289
|
+
deadline=data.get('deadline'),
|
|
290
|
+
created_at=data.get('created_at'),
|
|
291
|
+
last_updated=data.get('last_updated'),
|
|
292
|
+
archived_at=data.get('archived_at'),
|
|
293
|
+
completed_at=data.get('completed_at'),
|
|
294
|
+
starter_id=data.get('starter_id'),
|
|
295
|
+
completer_id=data.get('completer_id'),
|
|
296
|
+
is_oneoff_task=data.get('is_oneoff_task', False),
|
|
297
|
+
everyone_must_complete=data.get('everyone_must_complete', False),
|
|
298
|
+
completion_progress=data.get('completion_progress'),
|
|
299
|
+
has_deadline_dependent_child_tasks=data.get('has_deadline_dependent_child_tasks', False),
|
|
300
|
+
can_complete_only_assignees=data.get('can_complete_only_assignees', False),
|
|
301
|
+
max_assignable=data.get('max_assignable', 0),
|
|
302
|
+
webhook=data.get('webhook'),
|
|
303
|
+
prevent_guest_comment=data.get('prevent_guest_comment', False),
|
|
304
|
+
stage_id=data.get('stage_id'),
|
|
305
|
+
problem=data.get('problem', False),
|
|
306
|
+
blueprint_position=data.get('blueprint_position'),
|
|
307
|
+
is_soft_start_date=data.get('is_soft_start_date', False),
|
|
308
|
+
send_chromeless=data.get('send_chromeless'),
|
|
309
|
+
run_status=data.get('run_status'),
|
|
310
|
+
completer_guest=data.get('completer_guest')
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@dataclass
|
|
315
|
+
class RunProgress:
|
|
316
|
+
"""Run progress data model"""
|
|
317
|
+
complete: int = 0
|
|
318
|
+
total: int = 0
|
|
319
|
+
percent: float = 0.0
|
|
320
|
+
|
|
321
|
+
@classmethod
|
|
322
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'RunProgress':
|
|
323
|
+
"""Create RunProgress instance from dictionary"""
|
|
324
|
+
return cls(
|
|
325
|
+
complete=data.get('complete', 0),
|
|
326
|
+
total=data.get('total', 0),
|
|
327
|
+
percent=data.get('percent', 0.0)
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@dataclass
|
|
332
|
+
class Run:
|
|
333
|
+
"""Run (Process) data model"""
|
|
334
|
+
id: str
|
|
335
|
+
increment_id: int
|
|
336
|
+
checklist_id: str
|
|
337
|
+
checklist_title: str
|
|
338
|
+
assign_someone_else: bool
|
|
339
|
+
name: str
|
|
340
|
+
summary: Optional[str] = None
|
|
341
|
+
status: str = "active"
|
|
342
|
+
progress: Optional[RunProgress] = None
|
|
343
|
+
whole_progress: Optional[RunProgress] = None
|
|
344
|
+
started_by: Optional[int] = None
|
|
345
|
+
prerun: Optional[Dict[str, Any]] = None
|
|
346
|
+
prerun_completed_at: Optional[str] = None
|
|
347
|
+
prerun_completed_by: Optional[int] = None
|
|
348
|
+
prerun_length: Optional[int] = None
|
|
349
|
+
starred: bool = False
|
|
350
|
+
created_at: Optional[str] = None
|
|
351
|
+
due_date: Optional[str] = None
|
|
352
|
+
owner_id: Optional[int] = None
|
|
353
|
+
started_at: Optional[str] = None
|
|
354
|
+
last_updated: Optional[str] = None
|
|
355
|
+
completed_at: Optional[str] = None
|
|
356
|
+
late_tasks: Optional[int] = None
|
|
357
|
+
archived_at: Optional[str] = None
|
|
358
|
+
due_date_passed: bool = False
|
|
359
|
+
collaborators: List[int] = None
|
|
360
|
+
due_soon: bool = False
|
|
361
|
+
max_task_deadline: Optional[str] = None
|
|
362
|
+
|
|
363
|
+
def __post_init__(self):
|
|
364
|
+
"""Initialize empty lists if None"""
|
|
365
|
+
if self.collaborators is None:
|
|
366
|
+
self.collaborators = []
|
|
367
|
+
|
|
368
|
+
@classmethod
|
|
369
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Run':
|
|
370
|
+
"""Create Run instance from dictionary"""
|
|
371
|
+
progress_data = data.get('progress')
|
|
372
|
+
progress = RunProgress.from_dict(progress_data) if progress_data else None
|
|
373
|
+
|
|
374
|
+
whole_progress_data = data.get('whole_progress')
|
|
375
|
+
whole_progress = RunProgress.from_dict(whole_progress_data) if whole_progress_data else None
|
|
376
|
+
|
|
377
|
+
return cls(
|
|
378
|
+
id=data.get('id', ''),
|
|
379
|
+
increment_id=data.get('increment_id', 0),
|
|
380
|
+
checklist_id=data.get('checklist_id', ''),
|
|
381
|
+
checklist_title=data.get('checklist_title', ''),
|
|
382
|
+
assign_someone_else=data.get('assign_someone_else', False),
|
|
383
|
+
name=data.get('name', ''),
|
|
384
|
+
summary=data.get('summary'),
|
|
385
|
+
status=data.get('status', 'active'),
|
|
386
|
+
progress=progress,
|
|
387
|
+
whole_progress=whole_progress,
|
|
388
|
+
started_by=data.get('started_by'),
|
|
389
|
+
prerun=data.get('prerun'),
|
|
390
|
+
prerun_completed_at=data.get('prerun_completed_at'),
|
|
391
|
+
prerun_completed_by=data.get('prerun_completed_by'),
|
|
392
|
+
prerun_length=data.get('prerun_length'),
|
|
393
|
+
starred=data.get('starred', False),
|
|
394
|
+
created_at=data.get('created_at'),
|
|
395
|
+
due_date=data.get('due_date'),
|
|
396
|
+
owner_id=data.get('owner_id'),
|
|
397
|
+
started_at=data.get('started_at'),
|
|
398
|
+
last_updated=data.get('last_updated'),
|
|
399
|
+
completed_at=data.get('completed_at'),
|
|
400
|
+
late_tasks=data.get('late_tasks'),
|
|
401
|
+
archived_at=data.get('archived_at'),
|
|
402
|
+
due_date_passed=data.get('due_date_passed', False),
|
|
403
|
+
collaborators=data.get('collaborators', []),
|
|
404
|
+
due_soon=data.get('due_soon', False),
|
|
405
|
+
max_task_deadline=data.get('max_task_deadline')
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@dataclass
|
|
410
|
+
class Folder:
|
|
411
|
+
"""Folder data model for search results"""
|
|
412
|
+
id: str
|
|
413
|
+
parent: Optional[str]
|
|
414
|
+
name: str
|
|
415
|
+
|
|
416
|
+
@classmethod
|
|
417
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Folder':
|
|
418
|
+
"""Create Folder instance from dictionary"""
|
|
419
|
+
return cls(
|
|
420
|
+
id=data.get('id', ''),
|
|
421
|
+
parent=data.get('parent'),
|
|
422
|
+
name=data.get('name', '')
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@dataclass
|
|
427
|
+
class Tag:
|
|
428
|
+
"""Tag data model for industry and topic tags"""
|
|
429
|
+
id: str
|
|
430
|
+
title: str
|
|
431
|
+
type: str
|
|
432
|
+
color: str
|
|
433
|
+
created_at: Optional[str] = None
|
|
434
|
+
deleted_at: Optional[str] = None
|
|
435
|
+
|
|
436
|
+
@classmethod
|
|
437
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Tag':
|
|
438
|
+
"""Create Tag instance from dictionary"""
|
|
439
|
+
return cls(
|
|
440
|
+
id=data.get('id', ''),
|
|
441
|
+
title=data.get('title', ''),
|
|
442
|
+
type=data.get('type', ''),
|
|
443
|
+
color=data.get('color', ''),
|
|
444
|
+
created_at=data.get('created_at'),
|
|
445
|
+
deleted_at=data.get('deleted_at')
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@dataclass
|
|
450
|
+
class PrerunField:
|
|
451
|
+
"""Prerun field data model"""
|
|
452
|
+
id: str
|
|
453
|
+
checklist_id: str
|
|
454
|
+
alias: str
|
|
455
|
+
field_type: str
|
|
456
|
+
guidance: Optional[str] = None
|
|
457
|
+
position: int = 1
|
|
458
|
+
required: bool = True
|
|
459
|
+
use_wysiwyg_editor: bool = True
|
|
460
|
+
collect_time: bool = True
|
|
461
|
+
options: List[Dict[str, Any]] = None
|
|
462
|
+
field_validation: List[str] = None
|
|
463
|
+
created_at: Optional[str] = None
|
|
464
|
+
last_updated: Optional[str] = None
|
|
465
|
+
|
|
466
|
+
def __post_init__(self):
|
|
467
|
+
"""Initialize empty lists if None"""
|
|
468
|
+
if self.options is None:
|
|
469
|
+
self.options = []
|
|
470
|
+
if self.field_validation is None:
|
|
471
|
+
self.field_validation = []
|
|
472
|
+
|
|
473
|
+
@classmethod
|
|
474
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'PrerunField':
|
|
475
|
+
"""Create PrerunField instance from dictionary"""
|
|
476
|
+
return cls(
|
|
477
|
+
id=data.get('id', ''),
|
|
478
|
+
checklist_id=data.get('checklist_id', ''),
|
|
479
|
+
alias=data.get('alias', ''),
|
|
480
|
+
field_type=data.get('field_type', 'text'),
|
|
481
|
+
guidance=data.get('guidance'),
|
|
482
|
+
position=data.get('position', 1),
|
|
483
|
+
required=data.get('required', True),
|
|
484
|
+
use_wysiwyg_editor=data.get('use_wysiwyg_editor', True),
|
|
485
|
+
collect_time=data.get('collect_time', True),
|
|
486
|
+
options=data.get('options', []),
|
|
487
|
+
field_validation=data.get('field_validation', []),
|
|
488
|
+
created_at=data.get('created_at'),
|
|
489
|
+
last_updated=data.get('last_updated')
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
@dataclass
|
|
494
|
+
class AutomationCondition:
|
|
495
|
+
"""Automation condition data model"""
|
|
496
|
+
id: str
|
|
497
|
+
conditionable_id: str
|
|
498
|
+
conditionable_type: str
|
|
499
|
+
operation: str
|
|
500
|
+
statement: str
|
|
501
|
+
logic: str
|
|
502
|
+
position: int
|
|
503
|
+
column_contains_name: Optional[str] = None
|
|
504
|
+
|
|
505
|
+
@classmethod
|
|
506
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'AutomationCondition':
|
|
507
|
+
"""Create AutomationCondition instance from dictionary"""
|
|
508
|
+
return cls(
|
|
509
|
+
id=data.get('id', ''),
|
|
510
|
+
conditionable_id=data.get('conditionable_id', ''),
|
|
511
|
+
conditionable_type=data.get('conditionable_type', ''),
|
|
512
|
+
operation=data.get('operation', ''),
|
|
513
|
+
statement=data.get('statement', ''),
|
|
514
|
+
logic=data.get('logic', ''),
|
|
515
|
+
position=data.get('position', 0),
|
|
516
|
+
column_contains_name=data.get('column_contains_name')
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
@dataclass
|
|
521
|
+
class AutomationDeadline:
|
|
522
|
+
"""Automation deadline data model"""
|
|
523
|
+
value: int
|
|
524
|
+
unit: str
|
|
525
|
+
|
|
526
|
+
@classmethod
|
|
527
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'AutomationDeadline':
|
|
528
|
+
"""Create AutomationDeadline instance from dictionary"""
|
|
529
|
+
return cls(
|
|
530
|
+
value=data.get('value', 0),
|
|
531
|
+
unit=data.get('unit', 'days')
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
@dataclass
|
|
536
|
+
class AutomationAssignees:
|
|
537
|
+
"""Automation assignees data model"""
|
|
538
|
+
users: List[int] = None
|
|
539
|
+
guests: List[str] = None
|
|
540
|
+
groups: List[str] = None
|
|
541
|
+
|
|
542
|
+
def __post_init__(self):
|
|
543
|
+
"""Initialize empty lists if None"""
|
|
544
|
+
if self.users is None:
|
|
545
|
+
self.users = []
|
|
546
|
+
if self.guests is None:
|
|
547
|
+
self.guests = []
|
|
548
|
+
if self.groups is None:
|
|
549
|
+
self.groups = []
|
|
550
|
+
|
|
551
|
+
@classmethod
|
|
552
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'AutomationAssignees':
|
|
553
|
+
"""Create AutomationAssignees instance from dictionary"""
|
|
554
|
+
return cls(
|
|
555
|
+
users=data.get('users', []),
|
|
556
|
+
guests=data.get('guests', []),
|
|
557
|
+
groups=data.get('groups', [])
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
@dataclass
|
|
562
|
+
class AutomationAction:
|
|
563
|
+
"""Automation action data model"""
|
|
564
|
+
id: str
|
|
565
|
+
action_type: str
|
|
566
|
+
action_verb: str
|
|
567
|
+
target_step_id: Optional[str] = None
|
|
568
|
+
position: int = 0
|
|
569
|
+
actionable_id: Optional[str] = None
|
|
570
|
+
actionable_type: Optional[str] = None
|
|
571
|
+
deadline: Optional[AutomationDeadline] = None
|
|
572
|
+
assignees: Optional[AutomationAssignees] = None
|
|
573
|
+
|
|
574
|
+
@classmethod
|
|
575
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'AutomationAction':
|
|
576
|
+
"""Create AutomationAction instance from dictionary"""
|
|
577
|
+
deadline_data = data.get('deadline')
|
|
578
|
+
deadline = AutomationDeadline.from_dict(deadline_data) if deadline_data else None
|
|
579
|
+
|
|
580
|
+
assignees_data = data.get('assignees')
|
|
581
|
+
assignees = AutomationAssignees.from_dict(assignees_data) if assignees_data else None
|
|
582
|
+
|
|
583
|
+
return cls(
|
|
584
|
+
id=data.get('id', ''),
|
|
585
|
+
action_type=data.get('action_type', ''),
|
|
586
|
+
action_verb=data.get('action_verb', ''),
|
|
587
|
+
target_step_id=data.get('target_step_id'),
|
|
588
|
+
position=data.get('position', 0),
|
|
589
|
+
actionable_id=data.get('actionable_id'),
|
|
590
|
+
actionable_type=data.get('actionable_type'),
|
|
591
|
+
deadline=deadline,
|
|
592
|
+
assignees=assignees
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
@dataclass
|
|
597
|
+
class AutomatedAction:
|
|
598
|
+
"""Automated action data model"""
|
|
599
|
+
id: str
|
|
600
|
+
automated_alias: str
|
|
601
|
+
conditions: List[AutomationCondition] = None
|
|
602
|
+
then_actions: List[AutomationAction] = None
|
|
603
|
+
created_at: Optional[str] = None
|
|
604
|
+
last_updated: Optional[str] = None
|
|
605
|
+
archived_at: Optional[str] = None
|
|
606
|
+
|
|
607
|
+
def __post_init__(self):
|
|
608
|
+
"""Initialize empty lists if None"""
|
|
609
|
+
if self.conditions is None:
|
|
610
|
+
self.conditions = []
|
|
611
|
+
if self.then_actions is None:
|
|
612
|
+
self.then_actions = []
|
|
613
|
+
|
|
614
|
+
@classmethod
|
|
615
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'AutomatedAction':
|
|
616
|
+
"""Create AutomatedAction instance from dictionary"""
|
|
617
|
+
conditions_data = data.get('conditions', [])
|
|
618
|
+
conditions = [AutomationCondition.from_dict(cond_data) for cond_data in conditions_data] if conditions_data else []
|
|
619
|
+
|
|
620
|
+
actions_data = data.get('then_actions', [])
|
|
621
|
+
then_actions = [AutomationAction.from_dict(action_data) for action_data in actions_data] if actions_data else []
|
|
622
|
+
|
|
623
|
+
return cls(
|
|
624
|
+
id=data.get('id', ''),
|
|
625
|
+
automated_alias=data.get('automated_alias', ''),
|
|
626
|
+
conditions=conditions,
|
|
627
|
+
then_actions=then_actions,
|
|
628
|
+
created_at=data.get('created_at'),
|
|
629
|
+
last_updated=data.get('last_updated'),
|
|
630
|
+
archived_at=data.get('archived_at')
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
@dataclass
|
|
635
|
+
class Template:
|
|
636
|
+
"""Template data model"""
|
|
637
|
+
id: str
|
|
638
|
+
title: str
|
|
639
|
+
summary: Optional[str] = None
|
|
640
|
+
starred: bool = True
|
|
641
|
+
webhook: Optional[str] = None
|
|
642
|
+
explanation_video: Optional[str] = None
|
|
643
|
+
guidance: Optional[str] = None
|
|
644
|
+
icon: Optional[str] = None
|
|
645
|
+
alias: Optional[str] = None
|
|
646
|
+
prerun: List[PrerunField] = None
|
|
647
|
+
automated_actions: List[AutomatedAction] = None
|
|
648
|
+
created_by: Optional[int] = None
|
|
649
|
+
owner_id: Optional[int] = None
|
|
650
|
+
started_processes: int = 0
|
|
651
|
+
kickoff_title: Optional[str] = None
|
|
652
|
+
kickoff_description: Optional[str] = None
|
|
653
|
+
created_at: Optional[str] = None
|
|
654
|
+
last_updated: Optional[str] = None
|
|
655
|
+
archived_at: Optional[str] = None
|
|
656
|
+
is_public: bool = True
|
|
657
|
+
is_featured: bool = True
|
|
658
|
+
users: List[int] = None
|
|
659
|
+
groups: List[str] = None
|
|
660
|
+
public_cover: Optional[str] = None
|
|
661
|
+
industry_tags: List[Tag] = None
|
|
662
|
+
topic_tags: List[Tag] = None
|
|
663
|
+
type: Optional[str] = None
|
|
664
|
+
default_process_name_format: Optional[str] = None
|
|
665
|
+
is_public_kickoff: bool = True
|
|
666
|
+
dual_version_enabled: bool = True
|
|
667
|
+
is_published_state: bool = True
|
|
668
|
+
auto_naming: bool = True
|
|
669
|
+
last_updated_by: Optional[str] = None
|
|
670
|
+
linked_tasks: List[Task] = None
|
|
671
|
+
folderize_process: bool = True
|
|
672
|
+
tag_process: bool = True
|
|
673
|
+
allow_launcher_change_name: bool = True
|
|
674
|
+
is_pinned: bool = True
|
|
675
|
+
ko_form_blueprint_id: Optional[str] = None
|
|
676
|
+
default_folder: Optional[str] = None
|
|
677
|
+
folder_changeable_by_launcher: bool = True
|
|
678
|
+
kickoff_sharing_user_id: Optional[int] = None
|
|
679
|
+
|
|
680
|
+
def __post_init__(self):
|
|
681
|
+
"""Initialize empty lists if None"""
|
|
682
|
+
if self.prerun is None:
|
|
683
|
+
self.prerun = []
|
|
684
|
+
if self.automated_actions is None:
|
|
685
|
+
self.automated_actions = []
|
|
686
|
+
if self.users is None:
|
|
687
|
+
self.users = []
|
|
688
|
+
if self.groups is None:
|
|
689
|
+
self.groups = []
|
|
690
|
+
if self.industry_tags is None:
|
|
691
|
+
self.industry_tags = []
|
|
692
|
+
if self.topic_tags is None:
|
|
693
|
+
self.topic_tags = []
|
|
694
|
+
if self.linked_tasks is None:
|
|
695
|
+
self.linked_tasks = []
|
|
696
|
+
|
|
697
|
+
@classmethod
|
|
698
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Template':
|
|
699
|
+
"""Create Template instance from dictionary"""
|
|
700
|
+
prerun_data = data.get('prerun', [])
|
|
701
|
+
prerun = [PrerunField.from_dict(field_data) for field_data in prerun_data] if prerun_data else []
|
|
702
|
+
|
|
703
|
+
automated_actions_data = data.get('automated_actions', [])
|
|
704
|
+
automated_actions = [AutomatedAction.from_dict(action_data) for action_data in automated_actions_data] if automated_actions_data else []
|
|
705
|
+
|
|
706
|
+
industry_tags_data = data.get('industry_tags', [])
|
|
707
|
+
industry_tags = [Tag.from_dict(tag_data) for tag_data in industry_tags_data] if industry_tags_data else []
|
|
708
|
+
|
|
709
|
+
topic_tags_data = data.get('topic_tags', [])
|
|
710
|
+
topic_tags = [Tag.from_dict(tag_data) for tag_data in topic_tags_data] if topic_tags_data else []
|
|
711
|
+
|
|
712
|
+
linked_tasks_data = data.get('linked_tasks', [])
|
|
713
|
+
linked_tasks = [Task.from_dict(task_data) for task_data in linked_tasks_data] if linked_tasks_data else []
|
|
714
|
+
|
|
715
|
+
return cls(
|
|
716
|
+
id=data.get('id', ''),
|
|
717
|
+
title=data.get('title', ''),
|
|
718
|
+
summary=data.get('summary'),
|
|
719
|
+
starred=data.get('starred', True),
|
|
720
|
+
webhook=data.get('webhook'),
|
|
721
|
+
explanation_video=data.get('explanation_video'),
|
|
722
|
+
guidance=data.get('guidance'),
|
|
723
|
+
icon=data.get('icon'),
|
|
724
|
+
alias=data.get('alias'),
|
|
725
|
+
prerun=prerun,
|
|
726
|
+
automated_actions=automated_actions,
|
|
727
|
+
created_by=data.get('created_by'),
|
|
728
|
+
owner_id=data.get('owner_id'),
|
|
729
|
+
started_processes=data.get('started_processes', 0),
|
|
730
|
+
kickoff_title=data.get('kickoff_title'),
|
|
731
|
+
kickoff_description=data.get('kickoff_description'),
|
|
732
|
+
created_at=data.get('created_at'),
|
|
733
|
+
last_updated=data.get('last_updated'),
|
|
734
|
+
archived_at=data.get('archived_at'),
|
|
735
|
+
is_public=data.get('is_public', True),
|
|
736
|
+
is_featured=data.get('is_featured', True),
|
|
737
|
+
users=data.get('users', []),
|
|
738
|
+
groups=data.get('groups', []),
|
|
739
|
+
public_cover=data.get('public_cover'),
|
|
740
|
+
industry_tags=industry_tags,
|
|
741
|
+
topic_tags=topic_tags,
|
|
742
|
+
type=data.get('type'),
|
|
743
|
+
default_process_name_format=data.get('default_process_name_format'),
|
|
744
|
+
is_public_kickoff=data.get('is_public_kickoff', True),
|
|
745
|
+
dual_version_enabled=data.get('dual_version_enabled', True),
|
|
746
|
+
is_published_state=data.get('is_published_state', True),
|
|
747
|
+
auto_naming=data.get('auto_naming', True),
|
|
748
|
+
last_updated_by=data.get('last_updated_by'),
|
|
749
|
+
linked_tasks=linked_tasks,
|
|
750
|
+
folderize_process=data.get('folderize_process', True),
|
|
751
|
+
tag_process=data.get('tag_process', True),
|
|
752
|
+
allow_launcher_change_name=data.get('allow_launcher_change_name', True),
|
|
753
|
+
is_pinned=data.get('is_pinned', True),
|
|
754
|
+
ko_form_blueprint_id=data.get('ko_form_blueprint_id'),
|
|
755
|
+
default_folder=data.get('default_folder'),
|
|
756
|
+
folder_changeable_by_launcher=data.get('folder_changeable_by_launcher', True),
|
|
757
|
+
kickoff_sharing_user_id=data.get('kickoff_sharing_user_id')
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
@dataclass
|
|
762
|
+
class StepStartDate:
|
|
763
|
+
"""Step start date data model"""
|
|
764
|
+
value: int = 0
|
|
765
|
+
unit: str = "days"
|
|
766
|
+
|
|
767
|
+
@classmethod
|
|
768
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'StepStartDate':
|
|
769
|
+
"""Create StepStartDate instance from dictionary"""
|
|
770
|
+
return cls(
|
|
771
|
+
value=data.get('value', 0),
|
|
772
|
+
unit=data.get('unit', 'days')
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
@dataclass
|
|
777
|
+
class StepDeadline:
|
|
778
|
+
"""Step deadline data model"""
|
|
779
|
+
value: int = 0
|
|
780
|
+
unit: str = "days"
|
|
781
|
+
option: str = "from"
|
|
782
|
+
step: str = "start_run"
|
|
783
|
+
|
|
784
|
+
@classmethod
|
|
785
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'StepDeadline':
|
|
786
|
+
"""Create StepDeadline instance from dictionary"""
|
|
787
|
+
return cls(
|
|
788
|
+
value=data.get('value', 0),
|
|
789
|
+
unit=data.get('unit', 'days'),
|
|
790
|
+
option=data.get('option', 'from'),
|
|
791
|
+
step=data.get('step', 'start_run')
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
@dataclass
|
|
796
|
+
class StepBpToLaunch:
|
|
797
|
+
"""Step blueprint to launch data model"""
|
|
798
|
+
id: Optional[str] = None
|
|
799
|
+
default_name_format: Optional[str] = None
|
|
800
|
+
tasks_within_process: bool = True
|
|
801
|
+
|
|
802
|
+
@classmethod
|
|
803
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'StepBpToLaunch':
|
|
804
|
+
"""Create StepBpToLaunch instance from dictionary"""
|
|
805
|
+
return cls(
|
|
806
|
+
id=data.get('id'),
|
|
807
|
+
default_name_format=data.get('default_name_format'),
|
|
808
|
+
tasks_within_process=data.get('tasks_within_process', True)
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
@dataclass
|
|
813
|
+
class Capture:
|
|
814
|
+
"""Capture (form field) data model"""
|
|
815
|
+
id: str
|
|
816
|
+
step_id: str
|
|
817
|
+
field_type: str
|
|
818
|
+
label: str
|
|
819
|
+
guidance: Optional[str] = None
|
|
820
|
+
position: int = 1
|
|
821
|
+
required: bool = True
|
|
822
|
+
options: List[Dict[str, Any]] = None
|
|
823
|
+
columns: List[Dict[str, Any]] = None
|
|
824
|
+
default_value_enabled: bool = False
|
|
825
|
+
default_value: Optional[str] = None
|
|
826
|
+
created_at: Optional[str] = None
|
|
827
|
+
last_updated: Optional[str] = None
|
|
828
|
+
|
|
829
|
+
def __post_init__(self):
|
|
830
|
+
"""Initialize empty lists if None"""
|
|
831
|
+
if self.options is None:
|
|
832
|
+
self.options = []
|
|
833
|
+
if self.columns is None:
|
|
834
|
+
self.columns = []
|
|
835
|
+
|
|
836
|
+
@classmethod
|
|
837
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Capture':
|
|
838
|
+
"""Create Capture instance from dictionary"""
|
|
839
|
+
return cls(
|
|
840
|
+
id=data.get('id', ''),
|
|
841
|
+
step_id=data.get('step_id', ''),
|
|
842
|
+
field_type=data.get('field_type', 'text'),
|
|
843
|
+
label=data.get('label', ''),
|
|
844
|
+
guidance=data.get('guidance'),
|
|
845
|
+
position=data.get('position', 1),
|
|
846
|
+
required=data.get('required', True),
|
|
847
|
+
options=data.get('options', []),
|
|
848
|
+
columns=data.get('columns', []),
|
|
849
|
+
default_value_enabled=data.get('default_value_enabled', False),
|
|
850
|
+
default_value=data.get('default_value'),
|
|
851
|
+
created_at=data.get('created_at'),
|
|
852
|
+
last_updated=data.get('last_updated')
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
@dataclass
|
|
857
|
+
class Step:
|
|
858
|
+
"""Step data model"""
|
|
859
|
+
id: str
|
|
860
|
+
checklist_id: str
|
|
861
|
+
title: str
|
|
862
|
+
alias: Optional[str] = None
|
|
863
|
+
summary: Optional[str] = None
|
|
864
|
+
step_type: Optional[str] = None
|
|
865
|
+
position: int = 1
|
|
866
|
+
allow_guest_owners: bool = False
|
|
867
|
+
max_assignable: int = 1
|
|
868
|
+
skip_start_process: bool = False
|
|
869
|
+
can_complete_only_assignees: bool = False
|
|
870
|
+
everyone_must_complete: bool = False
|
|
871
|
+
webhook: Optional[str] = None
|
|
872
|
+
start_date: Optional[StepStartDate] = None
|
|
873
|
+
is_soft_start_date: bool = False
|
|
874
|
+
deadline: Optional[StepDeadline] = None
|
|
875
|
+
bp_to_launch: Optional[StepBpToLaunch] = None
|
|
876
|
+
assignees: List[int] = None
|
|
877
|
+
guests: List[str] = None
|
|
878
|
+
captures: List[Capture] = None
|
|
879
|
+
prevent_guest_comment: bool = False
|
|
880
|
+
roles: List[str] = None
|
|
881
|
+
role_changes_every_time: bool = False
|
|
882
|
+
created_at: Optional[str] = None
|
|
883
|
+
last_updated: Optional[str] = None
|
|
884
|
+
archived_at: Optional[str] = None
|
|
885
|
+
|
|
886
|
+
def __post_init__(self):
|
|
887
|
+
"""Initialize empty lists if None"""
|
|
888
|
+
if self.assignees is None:
|
|
889
|
+
self.assignees = []
|
|
890
|
+
if self.guests is None:
|
|
891
|
+
self.guests = []
|
|
892
|
+
if self.captures is None:
|
|
893
|
+
self.captures = []
|
|
894
|
+
if self.roles is None:
|
|
895
|
+
self.roles = []
|
|
896
|
+
|
|
897
|
+
@classmethod
|
|
898
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'Step':
|
|
899
|
+
"""Create Step instance from dictionary"""
|
|
900
|
+
start_date_data = data.get('start_date')
|
|
901
|
+
start_date = StepStartDate.from_dict(start_date_data) if start_date_data else None
|
|
902
|
+
|
|
903
|
+
deadline_data = data.get('deadline')
|
|
904
|
+
deadline = StepDeadline.from_dict(deadline_data) if deadline_data else None
|
|
905
|
+
|
|
906
|
+
bp_to_launch_data = data.get('bp_to_launch')
|
|
907
|
+
bp_to_launch = StepBpToLaunch.from_dict(bp_to_launch_data) if bp_to_launch_data else None
|
|
908
|
+
|
|
909
|
+
captures_data = data.get('captures', [])
|
|
910
|
+
captures = [Capture.from_dict(capture_data) for capture_data in captures_data] if captures_data else []
|
|
911
|
+
|
|
912
|
+
return cls(
|
|
913
|
+
id=data.get('id', ''),
|
|
914
|
+
checklist_id=data.get('checklist_id', ''),
|
|
915
|
+
title=data.get('title', ''),
|
|
916
|
+
alias=data.get('alias'),
|
|
917
|
+
summary=data.get('summary'),
|
|
918
|
+
step_type=data.get('step_type'),
|
|
919
|
+
position=data.get('position', 1),
|
|
920
|
+
allow_guest_owners=data.get('allow_guest_owners', False),
|
|
921
|
+
max_assignable=data.get('max_assignable', 1),
|
|
922
|
+
skip_start_process=data.get('skip_start_process', False),
|
|
923
|
+
can_complete_only_assignees=data.get('can_complete_only_assignees', False),
|
|
924
|
+
everyone_must_complete=data.get('everyone_must_complete', False),
|
|
925
|
+
webhook=data.get('webhook'),
|
|
926
|
+
start_date=start_date,
|
|
927
|
+
is_soft_start_date=data.get('is_soft_start_date', False),
|
|
928
|
+
deadline=deadline,
|
|
929
|
+
bp_to_launch=bp_to_launch,
|
|
930
|
+
assignees=data.get('assignees', []),
|
|
931
|
+
guests=data.get('guests', []),
|
|
932
|
+
captures=captures,
|
|
933
|
+
prevent_guest_comment=data.get('prevent_guest_comment', False),
|
|
934
|
+
roles=data.get('roles', []),
|
|
935
|
+
role_changes_every_time=data.get('role_changes_every_time', False),
|
|
936
|
+
created_at=data.get('created_at'),
|
|
937
|
+
last_updated=data.get('last_updated'),
|
|
938
|
+
archived_at=data.get('archived_at')
|
|
939
|
+
)
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
@dataclass
|
|
943
|
+
class SearchResult:
|
|
944
|
+
"""Search result data model for templates, processes, and tasks"""
|
|
945
|
+
id: str
|
|
946
|
+
increment_id: int
|
|
947
|
+
search_type: str
|
|
948
|
+
|
|
949
|
+
# Common fields
|
|
950
|
+
name: Optional[str] = None
|
|
951
|
+
title: Optional[str] = None
|
|
952
|
+
status: Optional[str] = None
|
|
953
|
+
type: Optional[str] = None
|
|
954
|
+
|
|
955
|
+
# Template-specific fields
|
|
956
|
+
steps_count: Optional[int] = None
|
|
957
|
+
icon: Optional[str] = None
|
|
958
|
+
is_public: Optional[bool] = None
|
|
959
|
+
is_featured: Optional[bool] = None
|
|
960
|
+
organization_id: Optional[str] = None
|
|
961
|
+
folders: Optional[List[Folder]] = None
|
|
962
|
+
|
|
963
|
+
# Process-specific fields
|
|
964
|
+
due_date_passed: Optional[bool] = None
|
|
965
|
+
due_soon: Optional[bool] = None
|
|
966
|
+
|
|
967
|
+
# Task-specific fields
|
|
968
|
+
status_label: Optional[str] = None
|
|
969
|
+
position: Optional[int] = None
|
|
970
|
+
deadline: Optional[str] = None
|
|
971
|
+
created_at: Optional[str] = None
|
|
972
|
+
starter_id: Optional[int] = None
|
|
973
|
+
is_oneoff_task: Optional[bool] = None
|
|
974
|
+
owners: Optional[TaskOwners] = None
|
|
975
|
+
|
|
976
|
+
def __post_init__(self):
|
|
977
|
+
"""Initialize empty lists if None"""
|
|
978
|
+
if self.folders is None:
|
|
979
|
+
self.folders = []
|
|
980
|
+
|
|
981
|
+
@classmethod
|
|
982
|
+
def from_dict(cls, data: Dict[str, Any], search_type: str) -> 'SearchResult':
|
|
983
|
+
"""Create SearchResult instance from dictionary"""
|
|
984
|
+
folders_data = data.get('folders', [])
|
|
985
|
+
folders = [Folder.from_dict(folder_data) for folder_data in folders_data] if folders_data else []
|
|
986
|
+
|
|
987
|
+
owners_data = data.get('owners')
|
|
988
|
+
owners = TaskOwners.from_dict(owners_data) if owners_data else None
|
|
989
|
+
|
|
990
|
+
return cls(
|
|
991
|
+
id=data.get('id', ''),
|
|
992
|
+
increment_id=data.get('increment_id', 0),
|
|
993
|
+
search_type=search_type,
|
|
994
|
+
name=data.get('name'),
|
|
995
|
+
title=data.get('title'),
|
|
996
|
+
status=data.get('status'),
|
|
997
|
+
type=data.get('type'),
|
|
998
|
+
steps_count=data.get('steps_count'),
|
|
999
|
+
icon=data.get('icon'),
|
|
1000
|
+
is_public=data.get('is_public'),
|
|
1001
|
+
is_featured=data.get('is_featured'),
|
|
1002
|
+
organization_id=data.get('organization_id'),
|
|
1003
|
+
folders=folders,
|
|
1004
|
+
due_date_passed=data.get('due_date_passed'),
|
|
1005
|
+
due_soon=data.get('due_soon'),
|
|
1006
|
+
status_label=data.get('status_label'),
|
|
1007
|
+
position=data.get('position'),
|
|
1008
|
+
deadline=data.get('deadline'),
|
|
1009
|
+
created_at=data.get('created_at'),
|
|
1010
|
+
starter_id=data.get('starter_id'),
|
|
1011
|
+
is_oneoff_task=data.get('is_oneoff_task'),
|
|
1012
|
+
owners=owners
|
|
1013
|
+
)
|